mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-21 05:02:17 +00:00
chore: update docs and schemas
This commit is contained in:
parent
4083baa1c1
commit
79bb441287
7 changed files with 720 additions and 919 deletions
|
|
@ -34,8 +34,10 @@ Universal schema guidance:
|
||||||
- Do not make breaking changes to API endpoints.
|
- Do not make breaking changes to API endpoints.
|
||||||
- When changing API routes, ensure the HTTP/SSE test suite has full coverage of every route.
|
- When changing API routes, ensure the HTTP/SSE test suite has full coverage of every route.
|
||||||
- When agent schema changes, ensure API tests cover the new schema and event shapes end-to-end.
|
- When agent schema changes, ensure API tests cover the new schema and event shapes end-to-end.
|
||||||
|
- Update `docs/conversion.md` whenever agent-native schema terms, synthetic events, identifier mappings, or conversion logic change.
|
||||||
- Never use synthetic data or mocked responses in tests.
|
- Never use synthetic data or mocked responses in tests.
|
||||||
- Never manually write agent types; always use generated types in `resources/agent-schemas/`. If types are broken, fix the generated types.
|
- Never manually write agent types; always use generated types in `resources/agent-schemas/`. If types are broken, fix the generated types.
|
||||||
|
- The universal schema must provide consistent behavior across providers; avoid requiring frontend/client logic to special-case agents.
|
||||||
|
|
||||||
### CLI ⇄ HTTP endpoint map (keep in sync)
|
### CLI ⇄ HTTP endpoint map (keep in sync)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,3 +93,9 @@ TODO
|
||||||
- more difficult to interact with, harder to analyze, doesn't support inspector for debugging
|
- more difficult to interact with, harder to analyze, doesn't support inspector for debugging
|
||||||
- may add at some point
|
- may add at some point
|
||||||
- codex does this. claude sort of does this.
|
- codex does this. claude sort of does this.
|
||||||
|
|
||||||
|
**Why not OpenCode?**
|
||||||
|
|
||||||
|
- the harnesses do a lot of heavy lifting
|
||||||
|
- the difference between opencode, claude, and codex is vast & vastly opinionated
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,14 @@
|
||||||
- **Vercel AI SDK Compatibility**: Works with existing AI SDK tooling, like `useChat`
|
- **Vercel AI SDK Compatibility**: Works with existing AI SDK tooling, like `useChat`
|
||||||
- **Auto-configure MCP & Skills**: Auto-load MCP servers & skills for your agents
|
- **Auto-configure MCP & Skills**: Auto-load MCP servers & skills for your agents
|
||||||
- **Process & logs manager**: Manage processes, logs, and ports for your agents to run background processes
|
- **Process & logs manager**: Manage processes, logs, and ports for your agents to run background processes
|
||||||
|
- **Codex app-server concurrency**: Run a single shared Codex app-server with multiple threads in parallel (like OpenCode), with file-write safety
|
||||||
|
|
||||||
## later
|
## later
|
||||||
|
|
||||||
|
- guides:
|
||||||
|
- ralph
|
||||||
|
- swarms
|
||||||
|
- opencode compatible api
|
||||||
- review all flags available on coding agents clis
|
- review all flags available on coding agents clis
|
||||||
- set up agent to check diffs in versions to recommend updates
|
- set up agent to check diffs in versions to recommend updates
|
||||||
- auto-updating for long running job
|
- auto-updating for long running job
|
||||||
|
|
|
||||||
72
docs/conversion.md
Normal file
72
docs/conversion.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Universal ↔ Agent Term Mapping
|
||||||
|
|
||||||
|
Source of truth: generated agent schemas in `resources/agent-schemas/artifacts/json-schema/`.
|
||||||
|
|
||||||
|
Identifiers
|
||||||
|
|
||||||
|
+----------------------+------------------------+------------------------------------------+-----------------------------+------------------------+
|
||||||
|
| Universal term | Claude | Codex (app-server) | OpenCode | Amp |
|
||||||
|
+----------------------+------------------------+------------------------------------------+-----------------------------+------------------------+
|
||||||
|
| session_id | n/a (daemon-only) | n/a (daemon-only) | n/a (daemon-only) | n/a (daemon-only) |
|
||||||
|
| native_session_id | none | threadId | sessionID | none |
|
||||||
|
| item_id | synthetic | ThreadItem.id | Message.id | StreamJSONMessage.id |
|
||||||
|
| native_item_id | none | ThreadItem.id | Message.id | StreamJSONMessage.id |
|
||||||
|
+----------------------+------------------------+------------------------------------------+-----------------------------+------------------------+
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- When a provider does not supply IDs (Claude), we synthesize item_id values and keep native_item_id null.
|
||||||
|
- native_session_id is the only provider session identifier. It is intentionally used for thread/session/run ids.
|
||||||
|
- native_item_id preserves the agent-native item/message id when present.
|
||||||
|
|
||||||
|
Events / Message Flow
|
||||||
|
|
||||||
|
+------------------------+------------------------------+--------------------------------------------+-----------------------------------------+----------------------------------+
|
||||||
|
| Universal term | Claude | Codex (app-server) | OpenCode | Amp |
|
||||||
|
+------------------------+------------------------------+--------------------------------------------+-----------------------------------------+----------------------------------+
|
||||||
|
| session.started | none | method=thread/started | type=session.created | none |
|
||||||
|
| session.ended | SDKMessage.type=result | no explicit session end (turn/completed) | no explicit session end (session.deleted)| type=done |
|
||||||
|
| message (user) | SDKMessage.type=user | item/completed (ThreadItem.type=userMessage)| message.updated (Message.role=user) | type=message |
|
||||||
|
| message (assistant) | SDKMessage.type=assistant | item/completed (ThreadItem.type=agentMessage)| message.updated (Message.role=assistant)| type=message |
|
||||||
|
| message.delta | synthetic | method=item/agentMessage/delta | type=message.part.updated (delta) | synthetic |
|
||||||
|
| tool call | synthetic from tool usage | method=item/mcpToolCall/progress | message.part.updated (part.type=tool) | type=tool_call |
|
||||||
|
| tool result | synthetic from tool usage | item/completed (tool result ThreadItem variants) | message.part.updated (part.type=tool, state=completed) | type=tool_result |
|
||||||
|
| permission.requested | none | none | type=permission.asked | none |
|
||||||
|
| permission.resolved | none | none | type=permission.replied | none |
|
||||||
|
| question.requested | ExitPlanMode tool (synthetic)| experimental request_user_input (payload) | type=question.asked | none |
|
||||||
|
| question.resolved | ExitPlanMode reply (synthetic)| experimental request_user_input (payload) | type=question.replied / question.rejected | none |
|
||||||
|
| error | SDKResultMessage.error | method=error | type=session.error (or message error) | type=error |
|
||||||
|
+------------------------+------------------------------+--------------------------------------------+-----------------------------------------+----------------------------------+
|
||||||
|
|
||||||
|
Synthetics
|
||||||
|
|
||||||
|
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||||
|
| Synthetic element | When it appears | Stored as | Notes |
|
||||||
|
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||||
|
| session.started | When agent emits no explicit start | session.started event | Mark origin=daemon |
|
||||||
|
| session.ended | When agent emits no explicit end | session.ended event | Mark origin=daemon; reason may be inferred |
|
||||||
|
| item_id (Claude) | Claude provides no item IDs | item_id | Maintain provider_item_id map when possible |
|
||||||
|
| user message (Claude) | Claude emits only assistant output | item.completed | Mark origin=daemon; preserve raw input in event metadata |
|
||||||
|
| question events (Claude) | Plan mode ExitPlanMode tool usage | question.requested/resolved | Synthetic mapping from tool call/result |
|
||||||
|
| native_session_id (Codex) | Codex uses threadId | native_session_id | Intentionally merged threadId into native_session_id |
|
||||||
|
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||||
|
| message.delta (Claude/Amp) | No native deltas | item.delta | Synthetic delta with full message content; origin=daemon |
|
||||||
|
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||||
|
| message.delta (OpenCode) | part delta before message | item.delta | If part arrives first, create item.started stub then delta |
|
||||||
|
+------------------------------+------------------------+--------------------------+--------------------------------------------------------------+
|
||||||
|
|
||||||
|
Delta handling
|
||||||
|
|
||||||
|
- Codex emits agent message and other deltas (e.g., item/agentMessage/delta).
|
||||||
|
- OpenCode emits part deltas via message.part.updated with a delta string.
|
||||||
|
- Claude and Amp do not emit deltas in their schemas.
|
||||||
|
|
||||||
|
Policy:
|
||||||
|
- Always emit item.delta across all providers.
|
||||||
|
- For providers without native deltas, emit a single synthetic delta containing the full content prior to item.completed.
|
||||||
|
- For providers with native deltas, forward as-is; also emit item.completed when final content is known.
|
||||||
|
|
||||||
|
Message normalization notes
|
||||||
|
|
||||||
|
- user vs assistant: normalized via role in the universal item; provider role fields or item types determine role.
|
||||||
|
- OpenCode unrolling: message.updated creates/updates the parent message item; tool-related parts emit separate tool item events (item.started/ item.completed) with parent_id pointing to the message item.
|
||||||
|
- If a message.part.updated arrives before message.updated, we create a stub item.started (origin=daemon) so deltas have a parent.
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2099,6 +2099,47 @@
|
||||||
"properties"
|
"properties"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Event.file.watcher.updated": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "file.watcher.updated"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"const": "add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"const": "change"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"const": "unlink"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"file",
|
||||||
|
"event"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"properties"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Todo": {
|
"Todo": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2157,47 +2198,6 @@
|
||||||
"properties"
|
"properties"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Event.file.watcher.updated": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"const": "file.watcher.updated"
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"file": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"event": {
|
|
||||||
"anyOf": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"const": "add"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"const": "change"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"const": "unlink"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"file",
|
|
||||||
"event"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"type",
|
|
||||||
"properties"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Event.tui.prompt.append": {
|
"Event.tui.prompt.append": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -3006,10 +3006,10 @@
|
||||||
"$ref": "#/definitions/Event.session.compacted"
|
"$ref": "#/definitions/Event.session.compacted"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/Event.todo.updated"
|
"$ref": "#/definitions/Event.file.watcher.updated"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/Event.file.watcher.updated"
|
"$ref": "#/definitions/Event.todo.updated"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/Event.tui.prompt.append"
|
"$ref": "#/definitions/Event.tui.prompt.append"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,26 @@ import { join } from "path";
|
||||||
import { createNormalizedSchema, type NormalizedSchema } from "./normalize.js";
|
import { createNormalizedSchema, type NormalizedSchema } from "./normalize.js";
|
||||||
import type { JSONSchema7 } from "json-schema";
|
import type { JSONSchema7 } from "json-schema";
|
||||||
|
|
||||||
|
function normalizeCodexRefs(value: JSONSchema7): JSONSchema7 {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((item) => normalizeCodexRefs(item as JSONSchema7)) as JSONSchema7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && typeof value === "object") {
|
||||||
|
const next: Record<string, JSONSchema7> = {};
|
||||||
|
for (const [key, child] of Object.entries(value)) {
|
||||||
|
if (key === "$ref" && typeof child === "string") {
|
||||||
|
next[key] = child.replace("#/definitions/v2/", "#/definitions/") as JSONSchema7;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
next[key] = normalizeCodexRefs(child as JSONSchema7);
|
||||||
|
}
|
||||||
|
return next as JSONSchema7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
export async function extractCodexSchema(): Promise<NormalizedSchema> {
|
export async function extractCodexSchema(): Promise<NormalizedSchema> {
|
||||||
console.log("Extracting Codex schema via CLI...");
|
console.log("Extracting Codex schema via CLI...");
|
||||||
|
|
||||||
|
|
@ -33,14 +53,14 @@ export async function extractCodexSchema(): Promise<NormalizedSchema> {
|
||||||
|
|
||||||
if (schema.definitions) {
|
if (schema.definitions) {
|
||||||
for (const [defName, def] of Object.entries(schema.definitions)) {
|
for (const [defName, def] of Object.entries(schema.definitions)) {
|
||||||
definitions[defName] = def as JSONSchema7;
|
definitions[defName] = normalizeCodexRefs(def as JSONSchema7);
|
||||||
}
|
}
|
||||||
} else if (schema.$defs) {
|
} else if (schema.$defs) {
|
||||||
for (const [defName, def] of Object.entries(schema.$defs)) {
|
for (const [defName, def] of Object.entries(schema.$defs)) {
|
||||||
definitions[defName] = def as JSONSchema7;
|
definitions[defName] = normalizeCodexRefs(def as JSONSchema7);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
definitions[name] = schema as JSONSchema7;
|
definitions[name] = normalizeCodexRefs(schema as JSONSchema7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue