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.
Files changed (178) hide show
  1. package/README.md +25 -3
  2. package/agent-agy/cordis.patch.yml +7 -4
  3. package/agent-agy/dist/agent.d.ts +12 -11
  4. package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
  5. package/agent-agy/dist/agy-auth-gateway.js +233 -0
  6. package/agent-agy/dist/agy-auth.d.ts +153 -0
  7. package/agent-agy/dist/agy-auth.js +179 -0
  8. package/agent-agy/dist/agy-cli-client.d.ts +39 -9
  9. package/agent-agy/dist/agy-cli-client.js +82 -18
  10. package/agent-agy/dist/agy-store.d.ts +8 -0
  11. package/agent-agy/dist/index.d.ts +46 -16
  12. package/agent-agy/dist/index.js +82 -56
  13. package/agent-agy/dist/models.d.ts +70 -0
  14. package/agent-agy/dist/models.js +102 -3
  15. package/agent-agy/package.json +7 -5
  16. package/agent-agy-sdk/cordis.patch.yml +58 -0
  17. package/agent-agy-sdk/dist/adapter.d.ts +15 -0
  18. package/agent-agy-sdk/dist/adapter.js +42 -0
  19. package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
  20. package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
  21. package/agent-agy-sdk/dist/agent.d.ts +138 -0
  22. package/agent-agy-sdk/dist/agent.js +494 -0
  23. package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
  24. package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
  25. package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
  26. package/agent-agy-sdk/dist/agy-sessions.js +107 -0
  27. package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
  28. package/agent-agy-sdk/dist/agy-store.js +83 -0
  29. package/agent-agy-sdk/dist/inbox.d.ts +26 -0
  30. package/agent-agy-sdk/dist/inbox.js +53 -0
  31. package/agent-agy-sdk/dist/index.d.ts +93 -0
  32. package/agent-agy-sdk/dist/index.js +476 -0
  33. package/agent-agy-sdk/dist/models.d.ts +41 -0
  34. package/agent-agy-sdk/dist/models.js +74 -0
  35. package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
  36. package/agent-agy-sdk/dist/sdk-venv.js +173 -0
  37. package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
  38. package/agent-agy-sdk/dist/world-plugin.js +82 -0
  39. package/agent-agy-sdk/package.json +64 -0
  40. package/agent-claude/dist/adapter.js +106 -31
  41. package/agent-claude/dist/agent.js +154 -17
  42. package/agent-claude/dist/claude-client.js +84 -43
  43. package/agent-claude/dist/claude-launch.js +101 -0
  44. package/agent-claude/dist/claude-native-config.js +169 -0
  45. package/agent-claude/dist/claude-provider-gateway.js +340 -0
  46. package/agent-claude/dist/claude-providers.js +391 -0
  47. package/agent-claude/dist/claude-route.js +152 -0
  48. package/agent-claude/dist/index.js +214 -37
  49. package/agent-claude/dist/models.js +14 -0
  50. package/agent-claude/dist/sdk-runtime.js +320 -0
  51. package/agent-claude/package.json +6 -4
  52. package/agent-codex/README.md +141 -36
  53. package/agent-codex/cordis.patch.yml +14 -10
  54. package/agent-codex/dist/adapter.js +64 -28
  55. package/agent-codex/dist/agent.js +255 -81
  56. package/agent-codex/dist/app-server-approvals.js +165 -0
  57. package/agent-codex/dist/app-server-client.js +723 -0
  58. package/agent-codex/dist/app-server-events.js +432 -0
  59. package/agent-codex/dist/app-server-protocol.js +95 -0
  60. package/agent-codex/dist/app-server-wire.js +299 -0
  61. package/agent-codex/dist/codex-launch.js +78 -0
  62. package/agent-codex/dist/codex-native-config.js +392 -0
  63. package/agent-codex/dist/codex-provider-gateway.js +402 -0
  64. package/agent-codex/dist/codex-providers.js +376 -0
  65. package/agent-codex/dist/codex-route.js +86 -0
  66. package/agent-codex/dist/index.js +256 -115
  67. package/agent-codex/dist/permission.js +75 -1
  68. package/agent-codex/package.json +1 -2
  69. package/agent-codex-sdk/README.md +79 -0
  70. package/agent-codex-sdk/cordis.patch.yml +81 -0
  71. package/agent-codex-sdk/dist/adapter.js +91 -0
  72. package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
  73. package/agent-codex-sdk/dist/agent.js +1085 -0
  74. package/agent-codex-sdk/dist/codex-native-config.js +392 -0
  75. package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
  76. package/agent-codex-sdk/dist/codex-providers.js +376 -0
  77. package/agent-codex-sdk/dist/codex-route.js +86 -0
  78. package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
  79. package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
  80. package/agent-codex-sdk/dist/codex-store.js +180 -0
  81. package/agent-codex-sdk/dist/inbox.js +53 -0
  82. package/agent-codex-sdk/dist/index.js +739 -0
  83. package/agent-codex-sdk/dist/permission.js +71 -0
  84. package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
  85. package/agent-codex-sdk/dist/session-map.js +167 -0
  86. package/agent-codex-sdk/dist/world-plugin.js +93 -0
  87. package/agent-codex-sdk/package.json +79 -0
  88. package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
  89. package/agent-omp/README.md +72 -55
  90. package/agent-omp/dist/agent.js +36 -11
  91. package/agent-omp/dist/index.js +92 -52
  92. package/agent-omp/dist/lazy-rpc.js +11 -4
  93. package/agent-omp/dist/omp-cli.js +114 -97
  94. package/agent-omp/dist/omp-store.js +12 -4
  95. package/agent-omp/dist/permission.js +70 -5
  96. package/agent-omp/dist/rpc-client.js +409 -0
  97. package/agent-omp/dist/world-plugin.js +36 -3
  98. package/agent-omp/package.json +2 -6
  99. package/agent-omp-sdk/README.md +103 -0
  100. package/agent-omp-sdk/cordis.patch.yml +108 -0
  101. package/agent-omp-sdk/dist/adapter.d.ts +21 -0
  102. package/agent-omp-sdk/dist/adapter.js +36 -0
  103. package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
  104. package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
  105. package/agent-omp-sdk/dist/agent.d.ts +64 -0
  106. package/agent-omp-sdk/dist/agent.js +1178 -0
  107. package/agent-omp-sdk/dist/discovery.d.ts +57 -0
  108. package/agent-omp-sdk/dist/discovery.js +116 -0
  109. package/agent-omp-sdk/dist/inbox.d.ts +28 -0
  110. package/agent-omp-sdk/dist/inbox.js +53 -0
  111. package/agent-omp-sdk/dist/index.d.ts +60 -0
  112. package/agent-omp-sdk/dist/index.js +632 -0
  113. package/agent-omp-sdk/dist/knobs.d.ts +34 -0
  114. package/agent-omp-sdk/dist/knobs.js +50 -0
  115. package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
  116. package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
  117. package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
  118. package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
  119. package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
  120. package/agent-omp-sdk/dist/mobile-boot.js +38 -0
  121. package/agent-omp-sdk/dist/models.d.ts +39 -0
  122. package/agent-omp-sdk/dist/models.js +299 -0
  123. package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
  124. package/agent-omp-sdk/dist/omp-cli.js +164 -0
  125. package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
  126. package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
  127. package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
  128. package/agent-omp-sdk/dist/omp-executable.js +30 -0
  129. package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
  130. package/agent-omp-sdk/dist/omp-store.js +526 -0
  131. package/agent-omp-sdk/dist/pairing.d.ts +7 -0
  132. package/agent-omp-sdk/dist/pairing.js +45 -0
  133. package/agent-omp-sdk/dist/permission.d.ts +138 -0
  134. package/agent-omp-sdk/dist/permission.js +161 -0
  135. package/agent-omp-sdk/dist/protocol.d.ts +189 -0
  136. package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
  137. package/agent-omp-sdk/dist/rpc-types.js +1 -0
  138. package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
  139. package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
  140. package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
  141. package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
  142. package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
  143. package/agent-omp-sdk/dist/sidecar-client.js +197 -0
  144. package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
  145. package/agent-omp-sdk/dist/store/adapter.js +47 -0
  146. package/agent-omp-sdk/dist/store/db.d.ts +70 -0
  147. package/agent-omp-sdk/dist/store/db.js +210 -0
  148. package/agent-omp-sdk/dist/store/index.d.ts +23 -0
  149. package/agent-omp-sdk/dist/store/index.js +59 -0
  150. package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
  151. package/agent-omp-sdk/dist/store/reconcile.js +51 -0
  152. package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
  153. package/agent-omp-sdk/dist/store/schema.js +40 -0
  154. package/agent-omp-sdk/dist/transport.d.ts +49 -0
  155. package/agent-omp-sdk/dist/transport.js +35 -0
  156. package/agent-omp-sdk/dist/world-home.d.ts +4 -0
  157. package/agent-omp-sdk/dist/world-home.js +23 -0
  158. package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
  159. package/agent-omp-sdk/dist/world-plugin.js +137 -0
  160. package/agent-omp-sdk/package.json +87 -0
  161. package/client/index.js +1204 -93
  162. package/cordis.patch.yml +64 -0
  163. package/package.json +17 -8
  164. package/superd-hub/dist/client/agent-settings.d.ts +210 -0
  165. package/superd-hub/dist/client/agent-settings.js +300 -0
  166. package/superd-hub/dist/client/roster.js +6 -0
  167. package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
  168. package/superd-hub/dist/client/runtime-settings-api.js +137 -0
  169. package/superd-hub/dist/client/switch.d.ts +165 -0
  170. package/superd-hub/dist/client/switch.js +295 -0
  171. package/superd-hub/lib/client/index.js +1204 -93
  172. package/agent-codex/dist/models.js +0 -152
  173. package/agent-omp/dist/sidecar-client.js +0 -195
  174. /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
  175. /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
  176. /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
  177. /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
  178. /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
+ };