super-dsh 0.1.9 → 0.1.10-e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/agent-agy/cordis.patch.yml +7 -4
- package/agent-agy/dist/agent.d.ts +12 -11
- package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
- package/agent-agy/dist/agy-auth-gateway.js +233 -0
- package/agent-agy/dist/agy-auth.d.ts +153 -0
- package/agent-agy/dist/agy-auth.js +179 -0
- package/agent-agy/dist/agy-cli-client.d.ts +39 -9
- package/agent-agy/dist/agy-cli-client.js +82 -18
- package/agent-agy/dist/agy-store.d.ts +8 -0
- package/agent-agy/dist/index.d.ts +46 -16
- package/agent-agy/dist/index.js +82 -56
- package/agent-agy/dist/models.d.ts +70 -0
- package/agent-agy/dist/models.js +102 -3
- package/agent-agy/package.json +7 -5
- package/agent-agy-sdk/cordis.patch.yml +58 -0
- package/agent-agy-sdk/dist/adapter.d.ts +15 -0
- package/agent-agy-sdk/dist/adapter.js +42 -0
- package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
- package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
- package/agent-agy-sdk/dist/agent.d.ts +138 -0
- package/agent-agy-sdk/dist/agent.js +494 -0
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
- package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
- package/agent-agy-sdk/dist/agy-sessions.js +107 -0
- package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
- package/agent-agy-sdk/dist/agy-store.js +83 -0
- package/agent-agy-sdk/dist/inbox.d.ts +26 -0
- package/agent-agy-sdk/dist/inbox.js +53 -0
- package/agent-agy-sdk/dist/index.d.ts +93 -0
- package/agent-agy-sdk/dist/index.js +476 -0
- package/agent-agy-sdk/dist/models.d.ts +41 -0
- package/agent-agy-sdk/dist/models.js +74 -0
- package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
- package/agent-agy-sdk/dist/sdk-venv.js +173 -0
- package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-agy-sdk/dist/world-plugin.js +82 -0
- package/agent-agy-sdk/package.json +64 -0
- package/agent-claude/dist/adapter.js +106 -31
- package/agent-claude/dist/agent.js +154 -17
- package/agent-claude/dist/claude-client.js +84 -43
- package/agent-claude/dist/claude-launch.js +101 -0
- package/agent-claude/dist/claude-native-config.js +169 -0
- package/agent-claude/dist/claude-provider-gateway.js +340 -0
- package/agent-claude/dist/claude-providers.js +391 -0
- package/agent-claude/dist/claude-route.js +152 -0
- package/agent-claude/dist/index.js +214 -37
- package/agent-claude/dist/models.js +14 -0
- package/agent-claude/dist/sdk-runtime.js +320 -0
- package/agent-claude/package.json +6 -4
- package/agent-codex/README.md +141 -36
- package/agent-codex/cordis.patch.yml +14 -10
- package/agent-codex/dist/adapter.js +64 -28
- package/agent-codex/dist/agent.js +255 -81
- package/agent-codex/dist/app-server-approvals.js +165 -0
- package/agent-codex/dist/app-server-client.js +723 -0
- package/agent-codex/dist/app-server-events.js +432 -0
- package/agent-codex/dist/app-server-protocol.js +95 -0
- package/agent-codex/dist/app-server-wire.js +299 -0
- package/agent-codex/dist/codex-launch.js +78 -0
- package/agent-codex/dist/codex-native-config.js +392 -0
- package/agent-codex/dist/codex-provider-gateway.js +402 -0
- package/agent-codex/dist/codex-providers.js +376 -0
- package/agent-codex/dist/codex-route.js +86 -0
- package/agent-codex/dist/index.js +256 -115
- package/agent-codex/dist/permission.js +75 -1
- package/agent-codex/package.json +1 -2
- package/agent-codex-sdk/README.md +79 -0
- package/agent-codex-sdk/cordis.patch.yml +81 -0
- package/agent-codex-sdk/dist/adapter.js +91 -0
- package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
- package/agent-codex-sdk/dist/agent.js +1085 -0
- package/agent-codex-sdk/dist/codex-native-config.js +392 -0
- package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
- package/agent-codex-sdk/dist/codex-providers.js +376 -0
- package/agent-codex-sdk/dist/codex-route.js +86 -0
- package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
- package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
- package/agent-codex-sdk/dist/codex-store.js +180 -0
- package/agent-codex-sdk/dist/inbox.js +53 -0
- package/agent-codex-sdk/dist/index.js +739 -0
- package/agent-codex-sdk/dist/permission.js +71 -0
- package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
- package/agent-codex-sdk/dist/session-map.js +167 -0
- package/agent-codex-sdk/dist/world-plugin.js +93 -0
- package/agent-codex-sdk/package.json +79 -0
- package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
- package/agent-omp/README.md +72 -55
- package/agent-omp/dist/agent.js +36 -11
- package/agent-omp/dist/index.js +92 -52
- package/agent-omp/dist/lazy-rpc.js +11 -4
- package/agent-omp/dist/omp-cli.js +114 -97
- package/agent-omp/dist/omp-store.js +12 -4
- package/agent-omp/dist/permission.js +70 -5
- package/agent-omp/dist/rpc-client.js +409 -0
- package/agent-omp/dist/world-plugin.js +36 -3
- package/agent-omp/package.json +2 -6
- package/agent-omp-sdk/README.md +103 -0
- package/agent-omp-sdk/cordis.patch.yml +108 -0
- package/agent-omp-sdk/dist/adapter.d.ts +21 -0
- package/agent-omp-sdk/dist/adapter.js +36 -0
- package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
- package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
- package/agent-omp-sdk/dist/agent.d.ts +64 -0
- package/agent-omp-sdk/dist/agent.js +1178 -0
- package/agent-omp-sdk/dist/discovery.d.ts +57 -0
- package/agent-omp-sdk/dist/discovery.js +116 -0
- package/agent-omp-sdk/dist/inbox.d.ts +28 -0
- package/agent-omp-sdk/dist/inbox.js +53 -0
- package/agent-omp-sdk/dist/index.d.ts +60 -0
- package/agent-omp-sdk/dist/index.js +632 -0
- package/agent-omp-sdk/dist/knobs.d.ts +34 -0
- package/agent-omp-sdk/dist/knobs.js +50 -0
- package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
- package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
- package/agent-omp-sdk/dist/mobile-boot.js +38 -0
- package/agent-omp-sdk/dist/models.d.ts +39 -0
- package/agent-omp-sdk/dist/models.js +299 -0
- package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
- package/agent-omp-sdk/dist/omp-cli.js +164 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
- package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
- package/agent-omp-sdk/dist/omp-executable.js +30 -0
- package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
- package/agent-omp-sdk/dist/omp-store.js +526 -0
- package/agent-omp-sdk/dist/pairing.d.ts +7 -0
- package/agent-omp-sdk/dist/pairing.js +45 -0
- package/agent-omp-sdk/dist/permission.d.ts +138 -0
- package/agent-omp-sdk/dist/permission.js +161 -0
- package/agent-omp-sdk/dist/protocol.d.ts +189 -0
- package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
- package/agent-omp-sdk/dist/rpc-types.js +1 -0
- package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
- package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
- package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
- package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
- package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
- package/agent-omp-sdk/dist/sidecar-client.js +197 -0
- package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
- package/agent-omp-sdk/dist/store/adapter.js +47 -0
- package/agent-omp-sdk/dist/store/db.d.ts +70 -0
- package/agent-omp-sdk/dist/store/db.js +210 -0
- package/agent-omp-sdk/dist/store/index.d.ts +23 -0
- package/agent-omp-sdk/dist/store/index.js +59 -0
- package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
- package/agent-omp-sdk/dist/store/reconcile.js +51 -0
- package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
- package/agent-omp-sdk/dist/store/schema.js +40 -0
- package/agent-omp-sdk/dist/transport.d.ts +49 -0
- package/agent-omp-sdk/dist/transport.js +35 -0
- package/agent-omp-sdk/dist/world-home.d.ts +4 -0
- package/agent-omp-sdk/dist/world-home.js +23 -0
- package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-omp-sdk/dist/world-plugin.js +137 -0
- package/agent-omp-sdk/package.json +87 -0
- package/client/index.js +1204 -93
- package/cordis.patch.yml +64 -0
- package/package.json +17 -8
- package/superd-hub/dist/client/agent-settings.d.ts +210 -0
- package/superd-hub/dist/client/agent-settings.js +300 -0
- package/superd-hub/dist/client/roster.js +6 -0
- package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
- package/superd-hub/dist/client/runtime-settings-api.js +137 -0
- package/superd-hub/dist/client/switch.d.ts +165 -0
- package/superd-hub/dist/client/switch.js +295 -0
- package/superd-hub/lib/client/index.js +1204 -93
- package/agent-codex/dist/models.js +0 -152
- package/agent-omp/dist/sidecar-client.js +0 -195
- /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
- /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
- /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
- /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
- /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex app-server notification → DSH wire vocabulary projection.
|
|
3
|
+
*
|
|
4
|
+
* The wire vocabulary is ported from `src/codex-events.ts` (the retired
|
|
5
|
+
* `@openai/codex-sdk` projector) and kept NAMES- AND SHAPES-IDENTICAL, because
|
|
6
|
+
* `src/agent.ts` consumes these events through the same switch it consumed on
|
|
7
|
+
* the SDK line — the event `type` STRINGS stay verbatim (`turn_start` /
|
|
8
|
+
* `message_start` / `message_update` / `message_end` / `tool_execution_start` /
|
|
9
|
+
* `tool_execution_end` / `todo_write` / `turn_end` / `text_delta` /
|
|
10
|
+
* `thinking_delta`).
|
|
11
|
+
*
|
|
12
|
+
* Projection mapping (as implemented; `agent_end` is NOT produced here — the
|
|
13
|
+
* client emits it when a run stream settles, mirroring the SDK sidecar):
|
|
14
|
+
*
|
|
15
|
+
* turn/started → turn_start
|
|
16
|
+
* item/started userMessage → message_start (user, text inputs)
|
|
17
|
+
* item/completed userMessage → null (the agent already appended or
|
|
18
|
+
* bridged the user message)
|
|
19
|
+
* item/started agentMessage → message_start (assistant, [])
|
|
20
|
+
* + message_update(text_delta) when
|
|
21
|
+
* the start already carries text
|
|
22
|
+
* item/agentMessage/delta → message_update(text_delta)
|
|
23
|
+
* item/completed agentMessage → message_end (assistant, [text])
|
|
24
|
+
* item/started reasoning → message_start (assistant, [thinking])
|
|
25
|
+
* item/reasoning/{textDelta,summaryTextDelta,summaryPartAdded}
|
|
26
|
+
* → message_update(thinking_delta)
|
|
27
|
+
* item/completed reasoning → message_end (assistant, [thinking])
|
|
28
|
+
* item/started commandExecution | fileChange | mcpToolCall |
|
|
29
|
+
* dynamicToolCall | webSearch | plan
|
|
30
|
+
* → tool_execution_start
|
|
31
|
+
* item/completed (same set) → tool_execution_end (+ the start
|
|
32
|
+
* first when the matching
|
|
33
|
+
* item/started never fired)
|
|
34
|
+
* item/completed plan → ALSO todo_write after the tool pair
|
|
35
|
+
* turn/plan/updated → tool_execution_start(update_plan)
|
|
36
|
+
* + todo_write (no end event)
|
|
37
|
+
* turn/completed → turn_end, data = { turn }
|
|
38
|
+
* thread/*, error, anything unrecognized or malformed → null
|
|
39
|
+
* (usage is the CLIENT's job: it
|
|
40
|
+
* records `error` and synthesizes
|
|
41
|
+
* the terminal assistant error, and
|
|
42
|
+
* it converts token usage)
|
|
43
|
+
*
|
|
44
|
+
* DESIGN CHOICE — thinking deltas: the SDK projector folded reasoning updates
|
|
45
|
+
* to `null` because the SDK only ever sent full-text snapshots. The app-server
|
|
46
|
+
* sends honest incremental deltas, and `agent.ts`'s `#handleUpdate` ignores
|
|
47
|
+
* unknown `assistantMessageEvent.type` values, so emitting `thinking_delta`
|
|
48
|
+
* frames is strictly more informative and stays wire-compatible. `delta` is
|
|
49
|
+
* OMITTED when the notification carries no string delta (e.g.
|
|
50
|
+
* `summaryPartAdded`): the vocabulary types the field as `delta?: string`, so a
|
|
51
|
+
* literal `null` would not type-check, and an absent key is equivalent for the
|
|
52
|
+
* consumer (`typeof delta.delta === "string"`). See `streamDelta` below.
|
|
53
|
+
*
|
|
54
|
+
* DESIGN CHOICE — tool item dedup: the app-server emits `item/started` when a
|
|
55
|
+
* tool call is dispatched and `item/completed` when it settles, but minimal or
|
|
56
|
+
* replayed chains can skip the start. A started-items watermark makes the start
|
|
57
|
+
* fire exactly once per item id: completed-after-started → end only;
|
|
58
|
+
* completed-without-started → full start+end pair. `agent.ts` appends one
|
|
59
|
+
* tool/call per start and one tool/result per end, so this keeps the DSH
|
|
60
|
+
* transcript free of duplicates.
|
|
61
|
+
*
|
|
62
|
+
* DESIGN CHOICE — no text watermark: unlike the SDK, whose `item.updated`
|
|
63
|
+
* snapshotted the FULL agent-message text (forcing the SDK projector to diff
|
|
64
|
+
* against a watermark), the app-server delivers explicit
|
|
65
|
+
* `item/agentMessage/delta` frames. A watermark would therefore be dead weight,
|
|
66
|
+
* so only `startedTools` is tracked.
|
|
67
|
+
*
|
|
68
|
+
* Never throws: any internal failure collapses to `null` (fail-soft), and the
|
|
69
|
+
* local helpers are equally defensive about malformed item shapes.
|
|
70
|
+
*/
|
|
71
|
+
import { SERVER_NOTIFICATION_METHODS } from "./app-server-protocol.js";
|
|
72
|
+
/** Items whose tool_execution_start already fired (item id → true). */
|
|
73
|
+
const startedTools = new Set();
|
|
74
|
+
/** Reset the projection watermarks (test / new-session hygiene). */
|
|
75
|
+
export function resetProjectionState() {
|
|
76
|
+
startedTools.clear();
|
|
77
|
+
}
|
|
78
|
+
function asRecord(value) {
|
|
79
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
80
|
+
return null;
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function asArray(value) {
|
|
84
|
+
return Array.isArray(value) ? value : [];
|
|
85
|
+
}
|
|
86
|
+
function asText(value) {
|
|
87
|
+
return typeof value === "string" ? value : "";
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* One `message_update` frame carrying an assistantMessageEvent delta. `delta`
|
|
91
|
+
* is set only when it is a string (see the header note on `thinking_delta`).
|
|
92
|
+
*/
|
|
93
|
+
function streamDelta(type, delta) {
|
|
94
|
+
const assistantMessageEvent = { type, contentIndex: 0 };
|
|
95
|
+
if (typeof delta === "string")
|
|
96
|
+
assistantMessageEvent.delta = delta;
|
|
97
|
+
return { type: "message_update", assistantMessageEvent };
|
|
98
|
+
}
|
|
99
|
+
/** Render a `PatchChangeKind` (tagged union) as a display string, defensively. */
|
|
100
|
+
function changeKindLabel(kind) {
|
|
101
|
+
if (typeof kind === "string")
|
|
102
|
+
return kind;
|
|
103
|
+
const record = asRecord(kind);
|
|
104
|
+
return record === null ? "" : asText(record.type);
|
|
105
|
+
}
|
|
106
|
+
/** All text inputs of a userMessage item, one block each (non-text inputs are dropped, as convertContent does). */
|
|
107
|
+
function userTextBlocks(content) {
|
|
108
|
+
const blocks = [];
|
|
109
|
+
for (const entry of asArray(content)) {
|
|
110
|
+
const record = asRecord(entry);
|
|
111
|
+
if (record === null || record.type !== "text")
|
|
112
|
+
continue;
|
|
113
|
+
if (typeof record.text !== "string")
|
|
114
|
+
continue;
|
|
115
|
+
blocks.push({ type: "text", text: record.text });
|
|
116
|
+
}
|
|
117
|
+
return blocks;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The final reasoning text of a completed `reasoning` item: the summary parts
|
|
121
|
+
* first, then the content parts, non-empty parts only, joined with newlines
|
|
122
|
+
* (upstream renders the summary as the readable trace).
|
|
123
|
+
*/
|
|
124
|
+
function reasoningText(summary, content) {
|
|
125
|
+
const parts = [];
|
|
126
|
+
for (const value of asArray(summary)) {
|
|
127
|
+
const text = asText(value);
|
|
128
|
+
if (text !== "")
|
|
129
|
+
parts.push(text);
|
|
130
|
+
}
|
|
131
|
+
for (const value of asArray(content)) {
|
|
132
|
+
const text = asText(value);
|
|
133
|
+
if (text !== "")
|
|
134
|
+
parts.push(text);
|
|
135
|
+
}
|
|
136
|
+
return parts.join("\n");
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Identity of an app-server tool item on the wire's tool_execution_* events:
|
|
140
|
+
* the display `args` object (omp parity) plus `argumentsJson` — the RAW JSON
|
|
141
|
+
* string the model produced, which is what the DSH `tool/call` payload wants.
|
|
142
|
+
* `mcpToolCall`/`dynamicToolCall` hand `arguments` over as unparsed JSON, so
|
|
143
|
+
* that rides through verbatim when it is a string; the other item shapes expose
|
|
144
|
+
* discrete fields, so their string is the faithful JSON rendering of exactly
|
|
145
|
+
* those fields.
|
|
146
|
+
*/
|
|
147
|
+
function toolIdentity(item) {
|
|
148
|
+
switch (item.type) {
|
|
149
|
+
case "commandExecution": {
|
|
150
|
+
const args = { command: asText(item.command) };
|
|
151
|
+
const cwd = asText(item.cwd);
|
|
152
|
+
if (cwd !== "")
|
|
153
|
+
args.cwd = cwd;
|
|
154
|
+
return { toolName: "command_execution", args, argsJson: JSON.stringify(args) };
|
|
155
|
+
}
|
|
156
|
+
case "fileChange": {
|
|
157
|
+
const args = { changes: asArray(item.changes) };
|
|
158
|
+
return { toolName: "file_change", args, argsJson: JSON.stringify(args) };
|
|
159
|
+
}
|
|
160
|
+
case "mcpToolCall": {
|
|
161
|
+
const args = asRecord(item.arguments) ?? {};
|
|
162
|
+
return {
|
|
163
|
+
toolName: asText(item.tool) || "mcp_tool_call",
|
|
164
|
+
args,
|
|
165
|
+
argsJson: typeof item.arguments === "string" ? item.arguments : JSON.stringify(args),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case "dynamicToolCall": {
|
|
169
|
+
const args = asRecord(item.arguments) ?? {};
|
|
170
|
+
return {
|
|
171
|
+
toolName: asText(item.tool) || "dynamic_tool_call",
|
|
172
|
+
args,
|
|
173
|
+
argsJson: typeof item.arguments === "string" ? item.arguments : JSON.stringify(args),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
case "webSearch": {
|
|
177
|
+
const args = { query: asText(item.query) };
|
|
178
|
+
return { toolName: "web_search", args, argsJson: JSON.stringify(args) };
|
|
179
|
+
}
|
|
180
|
+
case "plan": {
|
|
181
|
+
const args = { text: asText(item.text) };
|
|
182
|
+
return { toolName: "update_plan", args, argsJson: JSON.stringify(args) };
|
|
183
|
+
}
|
|
184
|
+
default:
|
|
185
|
+
return { toolName: asText(item.type) || "tool", args: {}, argsJson: "{}" };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Render a tool item's outcome as wire tool_execution_end result blocks. */
|
|
189
|
+
function toolResult(item) {
|
|
190
|
+
switch (item.type) {
|
|
191
|
+
case "commandExecution": {
|
|
192
|
+
return {
|
|
193
|
+
content: [{ type: "text", text: asText(item.aggregatedOutput) }],
|
|
194
|
+
isError: item.status === "failed",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
case "mcpToolCall": {
|
|
198
|
+
const result = asRecord(item.result);
|
|
199
|
+
const content = [];
|
|
200
|
+
const rawContent = result !== null ? asArray(result.content) : [];
|
|
201
|
+
for (const block of rawContent) {
|
|
202
|
+
const record = asRecord(block);
|
|
203
|
+
// Only MCP text blocks bridge (omp parity: images/unknown are dropped).
|
|
204
|
+
if (record !== null && record.type === "text" && typeof record.text === "string") {
|
|
205
|
+
content.push({ type: "text", text: record.text });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (content.length === 0) {
|
|
209
|
+
const error = asRecord(item.error);
|
|
210
|
+
if (error !== null && typeof error.message === "string")
|
|
211
|
+
content.push({ type: "text", text: error.message });
|
|
212
|
+
}
|
|
213
|
+
return { content, isError: item.status === "failed" };
|
|
214
|
+
}
|
|
215
|
+
case "dynamicToolCall": {
|
|
216
|
+
const content = [];
|
|
217
|
+
for (const entry of asArray(item.contentItems)) {
|
|
218
|
+
const record = asRecord(entry);
|
|
219
|
+
// The generated discriminant is `inputText`; accept a bare `text` too, defensively.
|
|
220
|
+
if (record === null)
|
|
221
|
+
continue;
|
|
222
|
+
if ((record.type === "inputText" || record.type === "text") && typeof record.text === "string") {
|
|
223
|
+
content.push({ type: "text", text: record.text });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { content, isError: item.success === false };
|
|
227
|
+
}
|
|
228
|
+
case "fileChange": {
|
|
229
|
+
const lines = [];
|
|
230
|
+
for (const change of asArray(item.changes)) {
|
|
231
|
+
const record = asRecord(change);
|
|
232
|
+
if (record !== null)
|
|
233
|
+
lines.push(`${changeKindLabel(record.kind) || "update"} ${asText(record.path)}`);
|
|
234
|
+
}
|
|
235
|
+
return { content: [{ type: "text", text: lines.join("\n") }], isError: item.status === "failed" };
|
|
236
|
+
}
|
|
237
|
+
case "webSearch":
|
|
238
|
+
return { content: [{ type: "text", text: asText(item.query) }], isError: false };
|
|
239
|
+
case "plan":
|
|
240
|
+
return { content: [{ type: "text", text: asText(item.text) }], isError: false };
|
|
241
|
+
default:
|
|
242
|
+
return { content: [], isError: false };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* `turn/plan/updated` steps → the DSH `todo/write` whole-list snapshot payload
|
|
247
|
+
* (`TodoItem { content, status }`; upstream packages/todo/tool-todo). The whole
|
|
248
|
+
* list is replaced on every write (latest-wins); a codex plan step is
|
|
249
|
+
* `"completed"` only when its status says so — `inProgress` and anything
|
|
250
|
+
* unrecognized map to `"pending"`. Blank steps are dropped, mirroring the SDK
|
|
251
|
+
* projector's todo snapshot.
|
|
252
|
+
*/
|
|
253
|
+
function planTodos(plan) {
|
|
254
|
+
const todos = [];
|
|
255
|
+
for (const entry of asArray(plan)) {
|
|
256
|
+
const record = asRecord(entry);
|
|
257
|
+
if (record === null)
|
|
258
|
+
continue;
|
|
259
|
+
const content = asText(record.step);
|
|
260
|
+
if (content === "")
|
|
261
|
+
continue;
|
|
262
|
+
todos.push({ content, status: record.status === "completed" ? "completed" : "pending" });
|
|
263
|
+
}
|
|
264
|
+
return todos;
|
|
265
|
+
}
|
|
266
|
+
function toolStart(item) {
|
|
267
|
+
const { toolName, args, argsJson } = toolIdentity(item);
|
|
268
|
+
const start = {
|
|
269
|
+
type: "tool_execution_start",
|
|
270
|
+
toolCallId: asText(item.id),
|
|
271
|
+
toolName,
|
|
272
|
+
args,
|
|
273
|
+
argumentsJson: argsJson,
|
|
274
|
+
};
|
|
275
|
+
if (item.type === "mcpToolCall")
|
|
276
|
+
start.server = asText(item.server);
|
|
277
|
+
return start;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Terminal tool event for a completed tool item. Emits the start first when
|
|
281
|
+
* this item's start was never projected (replayed/minimal chains), keeping the
|
|
282
|
+
* wire's start/end pairing intact without duplicate starts.
|
|
283
|
+
*/
|
|
284
|
+
function toolEnd(item) {
|
|
285
|
+
const id = asText(item.id);
|
|
286
|
+
const { toolName } = toolIdentity(item);
|
|
287
|
+
const result = toolResult(item);
|
|
288
|
+
const end = { type: "tool_execution_end", toolCallId: id, toolName, result, isError: result.isError };
|
|
289
|
+
if (item.type === "commandExecution") {
|
|
290
|
+
end.command = asText(item.command);
|
|
291
|
+
if (typeof item.exitCode === "number")
|
|
292
|
+
end.exit_code = item.exitCode;
|
|
293
|
+
}
|
|
294
|
+
if (!startedTools.has(id)) {
|
|
295
|
+
// start never fired for this id (replayed/minimal chain): emit the pair.
|
|
296
|
+
startedTools.add(id);
|
|
297
|
+
return [toolStart(item), end];
|
|
298
|
+
}
|
|
299
|
+
return end;
|
|
300
|
+
}
|
|
301
|
+
/** Tool item types that project as a tool_execution_start/end pair. */
|
|
302
|
+
const TOOL_ITEM_TYPES = new Set([
|
|
303
|
+
"commandExecution",
|
|
304
|
+
"fileChange",
|
|
305
|
+
"mcpToolCall",
|
|
306
|
+
"dynamicToolCall",
|
|
307
|
+
"webSearch",
|
|
308
|
+
"plan",
|
|
309
|
+
]);
|
|
310
|
+
function projectItem(item, phase) {
|
|
311
|
+
const record = asRecord(item);
|
|
312
|
+
if (record === null)
|
|
313
|
+
return null;
|
|
314
|
+
const id = asText(record.id);
|
|
315
|
+
const type = asText(record.type);
|
|
316
|
+
if (type === "")
|
|
317
|
+
return null;
|
|
318
|
+
if (type === "userMessage") {
|
|
319
|
+
// The completed twin is dropped: the agent already appended the local user
|
|
320
|
+
// message or bridged the remote one when the start arrived.
|
|
321
|
+
if (phase === "completed")
|
|
322
|
+
return null;
|
|
323
|
+
const message = { role: "user", content: userTextBlocks(record.content) };
|
|
324
|
+
return { type: "message_start", message };
|
|
325
|
+
}
|
|
326
|
+
if (type === "agentMessage") {
|
|
327
|
+
const text = asText(record.text);
|
|
328
|
+
if (phase === "started") {
|
|
329
|
+
const start = { type: "message_start", message: { role: "assistant", content: [] } };
|
|
330
|
+
return text === "" ? start : [start, streamDelta("text_delta", text)];
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
type: "message_end",
|
|
334
|
+
message: { role: "assistant", content: [{ type: "text", text }] },
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (type === "reasoning") {
|
|
338
|
+
if (phase === "started") {
|
|
339
|
+
return {
|
|
340
|
+
type: "message_start",
|
|
341
|
+
message: { role: "assistant", content: [{ type: "thinking", thinking: "" }] },
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
type: "message_end",
|
|
346
|
+
message: { role: "assistant", content: [{ type: "thinking", thinking: reasoningText(record.summary, record.content) }] },
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
if (!TOOL_ITEM_TYPES.has(type))
|
|
350
|
+
return null;
|
|
351
|
+
if (phase === "started") {
|
|
352
|
+
startedTools.add(id);
|
|
353
|
+
return toolStart(record);
|
|
354
|
+
}
|
|
355
|
+
const events = toolEnd(record);
|
|
356
|
+
if (type !== "plan")
|
|
357
|
+
return events;
|
|
358
|
+
// `item.plan` ALSO projects the DSH `todo/write` whole-list snapshot (log-only,
|
|
359
|
+
// latest-wins) after the tool pair — the tool pair stays. A plan item carries
|
|
360
|
+
// free text rather than steps, so it writes a single pending todo; an empty
|
|
361
|
+
// text would only clear the list, so nothing is written then.
|
|
362
|
+
const text = asText(record.text);
|
|
363
|
+
if (text === "")
|
|
364
|
+
return events;
|
|
365
|
+
const pair = Array.isArray(events) ? events : [events];
|
|
366
|
+
return [...pair, { type: "todo_write", todos: [{ content: text, status: "pending" }] }];
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* `turn/plan/updated` → a tool_execution_start (no end: the update is a
|
|
370
|
+
* snapshot, not a completion) plus the todo snapshot, in that order.
|
|
371
|
+
*/
|
|
372
|
+
function projectPlanUpdate(params) {
|
|
373
|
+
const args = { steps: asArray(params.plan) };
|
|
374
|
+
const start = {
|
|
375
|
+
type: "tool_execution_start",
|
|
376
|
+
toolCallId: asText(params.turnId),
|
|
377
|
+
toolName: "update_plan",
|
|
378
|
+
args,
|
|
379
|
+
argumentsJson: JSON.stringify(args),
|
|
380
|
+
};
|
|
381
|
+
return [start, { type: "todo_write", todos: planTodos(params.plan) }];
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Project ONE app-server notification ({ method, params }) onto the wire
|
|
385
|
+
* vocabulary. Returns one event, an ordered array, or null for
|
|
386
|
+
* ignorable/malformed input. Never throws.
|
|
387
|
+
*/
|
|
388
|
+
export function projectAppServerNotification(method, params) {
|
|
389
|
+
try {
|
|
390
|
+
if (typeof method !== "string")
|
|
391
|
+
return null;
|
|
392
|
+
const payload = asRecord(params) ?? {};
|
|
393
|
+
switch (method) {
|
|
394
|
+
case SERVER_NOTIFICATION_METHODS.turnStarted:
|
|
395
|
+
return { type: "turn_start" };
|
|
396
|
+
case SERVER_NOTIFICATION_METHODS.itemStarted:
|
|
397
|
+
return projectItem(payload.item, "started");
|
|
398
|
+
case SERVER_NOTIFICATION_METHODS.itemCompleted:
|
|
399
|
+
return projectItem(payload.item, "completed");
|
|
400
|
+
case SERVER_NOTIFICATION_METHODS.agentMessageDelta:
|
|
401
|
+
return streamDelta("text_delta", payload.delta);
|
|
402
|
+
case SERVER_NOTIFICATION_METHODS.reasoningTextDelta:
|
|
403
|
+
case SERVER_NOTIFICATION_METHODS.reasoningSummaryTextDelta:
|
|
404
|
+
case SERVER_NOTIFICATION_METHODS.reasoningSummaryPartAdded:
|
|
405
|
+
// `summaryPartAdded` carries no delta; streamDelta drops the key then.
|
|
406
|
+
return streamDelta("thinking_delta", payload.delta);
|
|
407
|
+
case SERVER_NOTIFICATION_METHODS.turnPlanUpdated:
|
|
408
|
+
return projectPlanUpdate(payload);
|
|
409
|
+
case SERVER_NOTIFICATION_METHODS.turnCompleted:
|
|
410
|
+
// The client reads `data.turn.status` / `data.turn.error` and owns
|
|
411
|
+
// `agent_end`; only the turn_end frame is projected here. A malformed
|
|
412
|
+
// payload (no turn object at all) is ignorable input, not an empty turn.
|
|
413
|
+
return asRecord(payload.turn) === null ? null : { type: "turn_end", data: { turn: payload.turn } };
|
|
414
|
+
case SERVER_NOTIFICATION_METHODS.error:
|
|
415
|
+
// The client records the error and synthesizes the terminal assistant
|
|
416
|
+
// error message; the wire stream stays silent (fail-soft invariant).
|
|
417
|
+
return null;
|
|
418
|
+
case SERVER_NOTIFICATION_METHODS.threadStarted:
|
|
419
|
+
case SERVER_NOTIFICATION_METHODS.threadStatusChanged:
|
|
420
|
+
case SERVER_NOTIFICATION_METHODS.threadTokenUsageUpdated:
|
|
421
|
+
// Thread lifecycle and token usage are the CLIENT's business: usage is
|
|
422
|
+
// converted from codex's snake_case shape by the client and attached to
|
|
423
|
+
// the next turn_end. Nothing is projected.
|
|
424
|
+
return null;
|
|
425
|
+
default:
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow hand-written types for the codex `app-server` JSONL JSON-RPC wire.
|
|
3
|
+
*
|
|
4
|
+
* DERIVED FROM: the `codex app-server generate-ts --out <dir>` output for the
|
|
5
|
+
* `@openai/codex` version this package pins (0.154.0). That tree is ~617 files;
|
|
6
|
+
* this module is a hand-written, deliberately narrow subset covering exactly
|
|
7
|
+
* the envelopes, requests, notifications and item shapes
|
|
8
|
+
* `src/app-server-client.ts` and `src/app-server-events.ts` consume. Each
|
|
9
|
+
* declaration names the generated file it was transcribed from; where a shape
|
|
10
|
+
* was simplified, the comment says so. Nothing is imported from `@openai/*`.
|
|
11
|
+
*
|
|
12
|
+
* ADVISORY: the app-server wire is EXPERIMENTAL. It is versioned by the CLI
|
|
13
|
+
* release, not by a compatibility promise, so a newer (or older) app-server may
|
|
14
|
+
* add, rename or drop fields without notice. These types describe what the
|
|
15
|
+
* pinned version emits. `InitializeCapabilities`, `Thread` and the JSON-RPC
|
|
16
|
+
* envelopes therefore stay superset-friendly (index signatures / optional
|
|
17
|
+
* members) so extra fields survive a decode untouched.
|
|
18
|
+
*
|
|
19
|
+
* FAIL-SOFT: callers MUST treat this wire as untrusted input. At runtime every
|
|
20
|
+
* field may be missing, null, or of the wrong type; narrow locally (see
|
|
21
|
+
* `src/app-server-events.ts`) instead of trusting a cast, and never let a
|
|
22
|
+
* malformed frame escape as an exception.
|
|
23
|
+
*
|
|
24
|
+
* The only runtime values in this module are the two JSON-RPC type guards and
|
|
25
|
+
* the `SERVER_REQUEST_METHODS` / `SERVER_NOTIFICATION_METHODS` method-name
|
|
26
|
+
* constant objects. Everything else is types only.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* True when the frame is a RESPONSE to a request we sent (`id` plus `result`
|
|
30
|
+
* or `error`). Accepts `unknown` because callers hold freshly parsed JSON.
|
|
31
|
+
*/
|
|
32
|
+
export function isJsonRpcResponse(message) {
|
|
33
|
+
if (message === null || typeof message !== "object")
|
|
34
|
+
return false;
|
|
35
|
+
const record = message;
|
|
36
|
+
if (typeof record.id !== "string" && typeof record.id !== "number")
|
|
37
|
+
return false;
|
|
38
|
+
return "result" in record || (typeof record.error === "object" && record.error !== null);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* True when the frame is a REQUEST that expects a reply from us — the shape of
|
|
42
|
+
* the server's approval requests (`item/commandExecution/requestApproval`,
|
|
43
|
+
* `item/fileChange/requestApproval`, the legacy `execCommandApproval` /
|
|
44
|
+
* `applyPatchApproval`). Accepts `unknown`.
|
|
45
|
+
*/
|
|
46
|
+
export function isJsonRpcRequest(message) {
|
|
47
|
+
if (message === null || typeof message !== "object")
|
|
48
|
+
return false;
|
|
49
|
+
const record = message;
|
|
50
|
+
return (typeof record.id === "string" || typeof record.id === "number") && typeof record.method === "string";
|
|
51
|
+
}
|
|
52
|
+
/* -------------------------------------------------------------------------- */
|
|
53
|
+
/* Method names (generated root `ClientRequest.ts` / `ClientNotification.ts` / */
|
|
54
|
+
/* `ServerRequest.ts` / `ServerNotification.ts`) */
|
|
55
|
+
/* -------------------------------------------------------------------------- */
|
|
56
|
+
/**
|
|
57
|
+
* Exact method strings the client sends (requests plus the `initialized`
|
|
58
|
+
* notification) and the server sends back as REQUESTS. `initialized` is a
|
|
59
|
+
* notification (generated root `ClientNotification.ts`), listed here because it
|
|
60
|
+
* belongs to the same handshake sequence.
|
|
61
|
+
*/
|
|
62
|
+
export const SERVER_REQUEST_METHODS = {
|
|
63
|
+
initialize: "initialize",
|
|
64
|
+
initialized: "initialized",
|
|
65
|
+
threadStart: "thread/start",
|
|
66
|
+
threadResume: "thread/resume",
|
|
67
|
+
turnStart: "turn/start",
|
|
68
|
+
turnSteer: "turn/steer",
|
|
69
|
+
turnInterrupt: "turn/interrupt",
|
|
70
|
+
modelList: "model/list",
|
|
71
|
+
commandExecutionRequestApproval: "item/commandExecution/requestApproval",
|
|
72
|
+
fileChangeRequestApproval: "item/fileChange/requestApproval",
|
|
73
|
+
execCommandApproval: "execCommandApproval",
|
|
74
|
+
applyPatchApproval: "applyPatchApproval",
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Exact method strings of the SERVER NOTIFICATIONS this adapter maps (see the
|
|
78
|
+
* projection table in `src/app-server-events.ts`). The remaining generated
|
|
79
|
+
* notifications are ignorable by design.
|
|
80
|
+
*/
|
|
81
|
+
export const SERVER_NOTIFICATION_METHODS = {
|
|
82
|
+
threadStarted: "thread/started",
|
|
83
|
+
threadStatusChanged: "thread/status/changed",
|
|
84
|
+
threadTokenUsageUpdated: "thread/tokenUsage/updated",
|
|
85
|
+
turnStarted: "turn/started",
|
|
86
|
+
turnCompleted: "turn/completed",
|
|
87
|
+
turnPlanUpdated: "turn/plan/updated",
|
|
88
|
+
itemStarted: "item/started",
|
|
89
|
+
itemCompleted: "item/completed",
|
|
90
|
+
agentMessageDelta: "item/agentMessage/delta",
|
|
91
|
+
reasoningTextDelta: "item/reasoning/textDelta",
|
|
92
|
+
reasoningSummaryTextDelta: "item/reasoning/summaryTextDelta",
|
|
93
|
+
reasoningSummaryPartAdded: "item/reasoning/summaryPartAdded",
|
|
94
|
+
error: "error",
|
|
95
|
+
};
|