lark-remote 0.1.6 → 0.1.7

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 (188) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.en.md +9 -1
  3. package/README.md +14 -2
  4. package/dist/bridge/approval-coordinator.d.ts +57 -34
  5. package/dist/bridge/approval-coordinator.js +191 -40
  6. package/dist/bridge/inbound-media.d.ts +62 -0
  7. package/dist/bridge/inbound-media.js +282 -0
  8. package/dist/bridge/index.d.ts +78 -7
  9. package/dist/bridge/index.js +202 -79
  10. package/dist/bridge/queue-manager.d.ts +2 -1
  11. package/dist/bridge/queue-manager.js +1 -0
  12. package/dist/card/approval-render.js +152 -36
  13. package/dist/card/bash-card-session.d.ts +2 -24
  14. package/dist/card/bash-card-session.js +6 -73
  15. package/dist/card/bash-renderer.js +2 -3
  16. package/dist/card/card-budget.js +29 -75
  17. package/dist/card/card-session.d.ts +48 -0
  18. package/dist/card/card-session.js +86 -0
  19. package/dist/card/card-shared.d.ts +1 -12
  20. package/dist/card/card-shared.js +1 -26
  21. package/dist/card/run-card-session.d.ts +2 -55
  22. package/dist/card/run-card-session.js +6 -104
  23. package/dist/card/run-renderer.js +99 -232
  24. package/dist/card/run-state.d.ts +13 -0
  25. package/dist/card/run-state.js +25 -16
  26. package/dist/card/text-truncate.d.ts +13 -12
  27. package/dist/card/text-truncate.js +13 -63
  28. package/dist/common/fs.d.ts +8 -0
  29. package/dist/common/fs.js +17 -0
  30. package/dist/common/guards.d.ts +0 -20
  31. package/dist/common/guards.js +0 -43
  32. package/dist/common/sleep.d.ts +2 -0
  33. package/dist/common/sleep.js +5 -0
  34. package/dist/common/truncate.d.ts +39 -0
  35. package/dist/common/truncate.js +92 -0
  36. package/dist/config/codex-config.d.ts +1 -1
  37. package/dist/config/codex-config.js +1 -1
  38. package/dist/config/index.d.ts +88 -23
  39. package/dist/config/index.js +105 -34
  40. package/dist/connector/index.d.ts +69 -2
  41. package/dist/connector/index.js +107 -1
  42. package/dist/index.js +123 -105
  43. package/dist/instance-lock.js +4 -12
  44. package/dist/order/alias-resolve.d.ts +13 -0
  45. package/dist/order/alias-resolve.js +22 -0
  46. package/dist/order/alias-store.d.ts +38 -0
  47. package/dist/order/alias-store.js +106 -0
  48. package/dist/persistence/atomic-write.d.ts +8 -2
  49. package/dist/persistence/atomic-write.js +23 -7
  50. package/dist/restart.js +2 -1
  51. package/dist/router/card-helpers.d.ts +63 -0
  52. package/dist/router/card-helpers.js +146 -0
  53. package/dist/router/config/claude.js +25 -1
  54. package/dist/router/config/codex.js +29 -50
  55. package/dist/router/config/common/model-patch.d.ts +22 -0
  56. package/dist/router/config/common/model-patch.js +26 -0
  57. package/dist/router/config/common/render.js +5 -4
  58. package/dist/router/config/index.d.ts +12 -0
  59. package/dist/router/config/index.js +25 -1
  60. package/dist/router/config/kimi.d.ts +4 -1
  61. package/dist/router/config/kimi.js +27 -0
  62. package/dist/router/config/opencode.js +18 -5
  63. package/dist/router/config/pi.js +4 -5
  64. package/dist/router/config/types.d.ts +11 -2
  65. package/dist/router/index.d.ts +56 -21
  66. package/dist/router/index.js +373 -548
  67. package/dist/router/utils.d.ts +2 -0
  68. package/dist/router/utils.js +4 -0
  69. package/dist/runner/agent-choices-common.d.ts +16 -0
  70. package/dist/runner/agent-choices-common.js +31 -0
  71. package/dist/runner/claude/runner.d.ts +47 -10
  72. package/dist/runner/claude/runner.js +117 -46
  73. package/dist/runner/claude/session.d.ts +166 -0
  74. package/dist/runner/claude/session.js +749 -0
  75. package/dist/runner/codex/app-server/protocol-types.d.ts +31 -39
  76. package/dist/runner/codex/app-server/protocol-types.js +1 -12
  77. package/dist/runner/codex/app-server/runner.d.ts +42 -59
  78. package/dist/runner/codex/app-server/runner.js +160 -337
  79. package/dist/runner/codex/app-server/translator.d.ts +1 -21
  80. package/dist/runner/codex/app-server/translator.js +0 -18
  81. package/dist/runner/codex/index.d.ts +0 -2
  82. package/dist/runner/codex/index.js +0 -2
  83. package/dist/runner/common/acp/protocol-helpers.d.ts +14 -0
  84. package/dist/runner/common/acp/protocol-helpers.js +21 -0
  85. package/dist/runner/common/acp/protocol-types.d.ts +238 -0
  86. package/dist/runner/common/acp/protocol-types.js +75 -0
  87. package/dist/runner/common/connection-based-runner.d.ts +111 -0
  88. package/dist/runner/common/connection-based-runner.js +261 -0
  89. package/dist/runner/{codex/app-server → common/jsonrpc}/client.d.ts +14 -7
  90. package/dist/runner/{codex/app-server → common/jsonrpc}/client.js +27 -18
  91. package/dist/runner/common/jsonrpc/connection-manager.d.ts +125 -0
  92. package/dist/runner/{codex/app-server → common/jsonrpc}/connection-manager.js +63 -28
  93. package/dist/runner/{codex/app-server → common/jsonrpc}/transport.d.ts +5 -4
  94. package/dist/runner/{codex/app-server → common/jsonrpc}/transport.js +18 -19
  95. package/dist/runner/common/jsonrpc/types.d.ts +32 -0
  96. package/dist/runner/common/jsonrpc/types.js +9 -0
  97. package/dist/runner/common/process-stopper.d.ts +2 -1
  98. package/dist/runner/common/process-stopper.js +1 -0
  99. package/dist/runner/common/runner-utils.d.ts +6 -13
  100. package/dist/runner/common/runner-utils.js +6 -47
  101. package/dist/runner/common/spawning-runner.d.ts +24 -70
  102. package/dist/runner/common/spawning-runner.js +98 -152
  103. package/dist/runner/index.d.ts +4 -4
  104. package/dist/runner/index.js +4 -4
  105. package/dist/runner/kimi/acp/runner.d.ts +106 -0
  106. package/dist/runner/kimi/acp/runner.js +448 -0
  107. package/dist/runner/kimi/acp/translator.d.ts +124 -0
  108. package/dist/runner/kimi/acp/translator.js +336 -0
  109. package/dist/runner/kimi/index.d.ts +1 -1
  110. package/dist/runner/kimi/index.js +1 -1
  111. package/dist/runner/opencode/acp/index.d.ts +1 -0
  112. package/dist/runner/opencode/acp/index.js +2 -0
  113. package/dist/runner/opencode/acp/runner.d.ts +121 -0
  114. package/dist/runner/opencode/acp/runner.js +429 -0
  115. package/dist/runner/opencode/acp/translator.d.ts +119 -0
  116. package/dist/runner/opencode/acp/translator.js +345 -0
  117. package/dist/runner/opencode/index.d.ts +1 -1
  118. package/dist/runner/opencode/index.js +1 -1
  119. package/dist/runner/pi/index.d.ts +1 -1
  120. package/dist/runner/pi/index.js +1 -1
  121. package/dist/runner/pi/rpc/client.d.ts +52 -0
  122. package/dist/runner/pi/rpc/client.js +114 -0
  123. package/dist/runner/pi/rpc/index.d.ts +4 -0
  124. package/dist/runner/pi/rpc/index.js +4 -0
  125. package/dist/runner/pi/rpc/protocol-types.d.ts +127 -0
  126. package/dist/runner/pi/rpc/protocol-types.js +21 -0
  127. package/dist/runner/pi/rpc/runner.d.ts +80 -0
  128. package/dist/runner/pi/rpc/runner.js +199 -0
  129. package/dist/runner/pi/rpc/translator.d.ts +55 -0
  130. package/dist/runner/pi/rpc/translator.js +258 -0
  131. package/dist/runner/registry.d.ts +7 -8
  132. package/dist/runner/registry.js +3 -4
  133. package/dist/runner/resolve-agent-choices.js +18 -59
  134. package/dist/runner/sync-agent-choices.js +16 -67
  135. package/dist/runner/types.d.ts +43 -13
  136. package/dist/session/claude/session-index.js +2 -3
  137. package/dist/session/claude/session-reader.js +3 -6
  138. package/dist/session/claude/sessions.js +6 -13
  139. package/dist/session/codex/rollout-reader.d.ts +2 -2
  140. package/dist/session/codex/rollout-reader.js +14 -17
  141. package/dist/session/codex/sessions.d.ts +1 -1
  142. package/dist/session/codex/sessions.js +2 -2
  143. package/dist/session/common/content-blocks.d.ts +7 -0
  144. package/dist/session/common/content-blocks.js +13 -9
  145. package/dist/session/common/jsonl.d.ts +8 -25
  146. package/dist/session/common/jsonl.js +126 -261
  147. package/dist/session/common/pagination.d.ts +28 -0
  148. package/dist/session/common/pagination.js +33 -0
  149. package/dist/session/kimi/sessions.d.ts +34 -1
  150. package/dist/session/kimi/sessions.js +208 -98
  151. package/dist/session/opencode/sessions.d.ts +17 -3
  152. package/dist/session/opencode/sessions.js +29 -29
  153. package/dist/session/pi/sessions.d.ts +5 -1
  154. package/dist/session/pi/sessions.js +12 -16
  155. package/dist/session/session-store.d.ts +11 -1
  156. package/dist/session/session-store.js +75 -178
  157. package/dist/update/index.d.ts +2 -2
  158. package/dist/update/index.js +2 -2
  159. package/dist/update/install.d.ts +20 -2
  160. package/dist/update/install.js +51 -7
  161. package/package.json +1 -1
  162. package/dist/runner/codex/app-server/connection-manager.d.ts +0 -56
  163. package/dist/runner/codex/argv.d.ts +0 -33
  164. package/dist/runner/codex/argv.js +0 -40
  165. package/dist/runner/codex/factory.d.ts +0 -29
  166. package/dist/runner/codex/factory.js +0 -39
  167. package/dist/runner/codex/jsonl.d.ts +0 -37
  168. package/dist/runner/codex/jsonl.js +0 -221
  169. package/dist/runner/codex/runner.d.ts +0 -66
  170. package/dist/runner/codex/runner.js +0 -129
  171. package/dist/runner/common/exec-translator.d.ts +0 -73
  172. package/dist/runner/common/exec-translator.js +0 -105
  173. package/dist/runner/kimi/jsonl.d.ts +0 -28
  174. package/dist/runner/kimi/jsonl.js +0 -143
  175. package/dist/runner/kimi/runner.d.ts +0 -49
  176. package/dist/runner/kimi/runner.js +0 -105
  177. package/dist/runner/opencode/argv.d.ts +0 -32
  178. package/dist/runner/opencode/argv.js +0 -35
  179. package/dist/runner/opencode/jsonl.d.ts +0 -68
  180. package/dist/runner/opencode/jsonl.js +0 -295
  181. package/dist/runner/opencode/runner.d.ts +0 -61
  182. package/dist/runner/opencode/runner.js +0 -93
  183. package/dist/runner/pi/jsonl.d.ts +0 -128
  184. package/dist/runner/pi/jsonl.js +0 -249
  185. package/dist/runner/pi/runner.d.ts +0 -36
  186. package/dist/runner/pi/runner.js +0 -76
  187. package/dist/session/common/catch-up.d.ts +0 -12
  188. package/dist/session/common/catch-up.js +0 -31
@@ -1,295 +0,0 @@
1
- /**
2
- * Translates opencode `run --format json` ndjson output into lark-remote `AgentEvent`s.
3
- *
4
- * Key design decisions:
5
- * - opencode uses step-level events (step_start/step_finish), not turn-level like codex.
6
- * - Tool is a single-part state machine: state.status evolves pending→running→completed/error,
7
- * input and output are in the same part.
8
- * - Fail-fast: unknown types and missing fields return [] (no event emitted),
9
- * never throw, never write back to opencode (root cause ② tolerance).
10
- * - Stream ending before a terminal event records a terminal error via `finish()`
11
- * (root cause ③ fail-fast); the runner's `buildResultEvent` emits the result.
12
- * - Each `run()` must create a new translator instance (stateful: sessionId/terminal/startedTools).
13
- *
14
- * Terminal state (sessionId, lastUsage, terminalErrorMessage) is stored on
15
- * the instance and exposed via getters; `OpencodeExecRunner.run()` folds
16
- * this state into `SpawningRunner.buildResultEvent(...)` so result-event
17
- * semantics are uniform across all 5 agents.
18
- *
19
- * Event mapping:
20
- * | opencode ndjson | lark-remote AgentEvent |
21
- * |--------------------------|------------------------------------------------------|
22
- * | step_start | SystemInitEvent (first event only) |
23
- * | text | AssistantEvent(text) |
24
- * | reasoning | AssistantEvent(thinking) |
25
- * | tool/tool_use (first) | AssistantEvent(tool_use) |
26
- * | tool (status=completed) | UserEvent(tool_result, is_error=false) |
27
- * | tool (status=error) | UserEvent(tool_result, is_error=true) |
28
- * | patch | FileChangeEvent |
29
- * | step_finish(reason=stop) | [] (terminal — usage stashed for runner) |
30
- * | step_finish(reason=*) | [] (non-terminal, continues) |
31
- * | error | [] (terminal - error message stashed for runner) |
32
- * | unknown type | [] (no event) |
33
- */
34
- import { getLogger } from '../../logger/index.js';
35
- import { recordValue, stringValue, numberValue, extractErrorMessage } from '../../common/guards.js';
36
- import { ExecTranslator } from '../common/exec-translator.js';
37
- export class OpencodeExecTranslator extends ExecTranslator {
38
- cwd;
39
- /** Accumulate usage across all steps (not just terminal step). */
40
- accumulatedUsage = {
41
- input_tokens: 0,
42
- output_tokens: 0,
43
- cache_read_tokens: 0,
44
- cache_creation_tokens: 0,
45
- };
46
- /** Track which tool results have already been emitted (idempotency). */
47
- emittedResults = new Set();
48
- logTag = '[opencode-exec-translator]';
49
- streamEndedMessage() {
50
- return 'opencode stream ended before a terminal step';
51
- }
52
- /**
53
- * @param opts.cwd The session's real directory. Emitted on the synthesized
54
- * SystemInitEvent so the bridge persists the correct cwd (was hardcoded '').
55
- * Post-PWD-sync-fix this equals the runner's opts.cwd == opencode's directory.
56
- */
57
- constructor(opts = {}) {
58
- super();
59
- this.cwd = opts.cwd ?? '';
60
- }
61
- /** Get accumulated usage across all steps (for diagnostics). */
62
- getAccumulatedUsage() {
63
- return { ...this.accumulatedUsage };
64
- }
65
- // --- translateEvent: opencode-specific dispatch ---
66
- translateEvent(raw) {
67
- // Stash sessionId from the very first event (every event carries it)
68
- const sid = stringValue(raw.sessionID ?? raw.sessionId);
69
- if (sid && !this.sessionId) {
70
- this.sessionId = sid;
71
- }
72
- switch (raw.type) {
73
- case 'step_start':
74
- return this.onStepStart(raw);
75
- case 'text':
76
- return this.onText(raw);
77
- case 'reasoning':
78
- return this.onReasoning(raw);
79
- case 'tool':
80
- case 'tool_use':
81
- case 'tool_output':
82
- return this.onTool(raw);
83
- case 'patch':
84
- return this.onPatch(raw);
85
- case 'step_finish':
86
- return this.onStepFinish(raw);
87
- case 'error':
88
- return this.onError(raw);
89
- default:
90
- return this.recordUnknownEvent(raw.type);
91
- }
92
- }
93
- // --- Private handlers ---
94
- /** SystemInitEvent: synthesize from first event with sessionId. */
95
- synthesizeInit() {
96
- return {
97
- type: 'system',
98
- subtype: 'init',
99
- session_id: this.sessionId ?? '',
100
- cwd: this.cwd,
101
- model: '',
102
- timestamp: new Date().toISOString(),
103
- };
104
- }
105
- onStepStart(_raw) {
106
- // Internal marker, no lark-remote event
107
- // But this is the first event, so synthesize SystemInitEvent
108
- return this.sessionId && !this.terminal ? [this.synthesizeInit()] : [];
109
- }
110
- onText(raw) {
111
- const part = recordValue(raw.part);
112
- if (!part) {
113
- return [];
114
- }
115
- const text = stringValue(part.text);
116
- if (!text) {
117
- return [];
118
- }
119
- return [
120
- {
121
- type: 'assistant',
122
- message: {
123
- content: [{ type: 'text', text }],
124
- },
125
- timestamp: new Date().toISOString(),
126
- },
127
- ];
128
- }
129
- onReasoning(raw) {
130
- const part = recordValue(raw.part);
131
- if (!part) {
132
- return [];
133
- }
134
- const text = stringValue(part.text);
135
- if (!text) {
136
- return [];
137
- }
138
- return [
139
- {
140
- type: 'assistant',
141
- message: {
142
- content: [{ type: 'thinking', thinking: text }],
143
- },
144
- timestamp: new Date().toISOString(),
145
- },
146
- ];
147
- }
148
- onTool(raw) {
149
- const part = recordValue(raw.part);
150
- if (!part) {
151
- return [];
152
- }
153
- const tool = stringValue(part.tool);
154
- const callId = stringValue(part.callID ?? part.callId);
155
- const state = recordValue(part.state);
156
- if (!callId) {
157
- return [];
158
- }
159
- const events = [];
160
- // First time seeing this tool call: emit tool_use
161
- if (!this.startedTools.has(callId)) {
162
- this.startedTools.add(callId);
163
- const input = state ? recordValue(state.input) : undefined;
164
- const toolUseContent = {
165
- type: 'tool_use',
166
- id: callId,
167
- name: tool ?? '',
168
- input: input ?? {},
169
- };
170
- events.push({
171
- type: 'assistant',
172
- message: {
173
- content: [toolUseContent],
174
- },
175
- timestamp: new Date().toISOString(),
176
- });
177
- }
178
- // Check for completion or error status
179
- if (state) {
180
- const status = stringValue(state.status);
181
- // completed: emit tool_result with output
182
- if (status === 'completed' && !this.emittedResults.has(callId)) {
183
- this.emittedResults.add(callId);
184
- const output = stringValue(state.output) ?? '';
185
- events.push({
186
- type: 'user',
187
- message: {
188
- content: [
189
- {
190
- type: 'tool_result',
191
- tool_use_id: callId,
192
- content: output,
193
- is_error: false,
194
- },
195
- ],
196
- },
197
- timestamp: new Date().toISOString(),
198
- });
199
- }
200
- // error: emit tool_result with error
201
- if (status === 'error' && !this.emittedResults.has(callId)) {
202
- this.emittedResults.add(callId);
203
- const error = stringValue(state.error) ?? stringValue(state.output) ?? 'unknown error';
204
- events.push({
205
- type: 'user',
206
- message: {
207
- content: [
208
- {
209
- type: 'tool_result',
210
- tool_use_id: callId,
211
- content: error,
212
- is_error: true,
213
- },
214
- ],
215
- },
216
- timestamp: new Date().toISOString(),
217
- });
218
- }
219
- }
220
- return events;
221
- }
222
- onPatch(raw) {
223
- const part = recordValue(raw.part);
224
- if (!part) {
225
- return [];
226
- }
227
- const files = part.files;
228
- if (!Array.isArray(files) || files.length === 0) {
229
- return [];
230
- }
231
- // Emit a FileChangeEvent for each file
232
- const events = [];
233
- for (const file of files) {
234
- const path = stringValue(file.path ?? file.file_path);
235
- if (!path)
236
- continue;
237
- events.push({
238
- type: 'file_change',
239
- path,
240
- operation: 'edit', // patch implies edit
241
- timestamp: new Date().toISOString(),
242
- });
243
- }
244
- return events;
245
- }
246
- onStepFinish(raw) {
247
- const part = recordValue(raw.part);
248
- if (!part) {
249
- return [];
250
- }
251
- const reason = stringValue(part.reason);
252
- // Extract tokens from this step (both terminal and non-terminal steps contribute usage).
253
- const tokens = recordValue(part.tokens);
254
- if (tokens) {
255
- const cache = recordValue(tokens.cache);
256
- // Accumulate usage across ALL steps (not just terminal step).
257
- // This fixes the systematic undercount for multi-step runs.
258
- this.accumulatedUsage.input_tokens += numberValue(tokens.input) ?? 0;
259
- this.accumulatedUsage.output_tokens += numberValue(tokens.output) ?? 0;
260
- this.accumulatedUsage.cache_read_tokens += cache ? (numberValue(cache.read) ?? 0) : 0;
261
- this.accumulatedUsage.cache_creation_tokens += cache ? (numberValue(cache.write) ?? 0) : 0;
262
- // Only reason='stop' is terminal
263
- if (reason === 'stop') {
264
- // Mark terminal and stash accumulated usage; the runner emits the result event
265
- // via buildResultEvent({...usage}) so success/error is uniform across agents.
266
- this.terminal = true;
267
- // Use accumulated usage (all steps) for the final result
268
- const total = numberValue(tokens.total);
269
- this.lastUsage = {
270
- input_tokens: this.accumulatedUsage.input_tokens,
271
- output_tokens: this.accumulatedUsage.output_tokens,
272
- cache_read_tokens: this.accumulatedUsage.cache_read_tokens,
273
- cache_creation_tokens: this.accumulatedUsage.cache_creation_tokens,
274
- ...(total !== undefined ? { total_tokens: total } : {}),
275
- };
276
- }
277
- }
278
- return [];
279
- }
280
- onError(raw) {
281
- // opencode error events are terminal in practice: opencode exits with
282
- // code=1 immediately after emitting one, without a step_finish(reason=stop).
283
- // Mark terminal so the real error message reaches the user via
284
- // buildResultEvent({translatorError}) instead of the generic
285
- // "opencode exited code=1" fallback.
286
- const message = extractErrorMessage(raw, 'opencode error');
287
- getLogger().warn(`[opencode-exec-translator] error: ${message.slice(0, 500)}`);
288
- getLogger().warn(`[opencode-exec-translator] RAW ERROR EVENT: ${JSON.stringify(raw).slice(0, 2000)}`);
289
- this.terminal = true;
290
- this.terminalErrorFromAgent = true;
291
- this.terminalErrorMessage = message;
292
- return [];
293
- }
294
- }
295
- //# sourceMappingURL=jsonl.js.map
@@ -1,61 +0,0 @@
1
- /**
2
- * OpencodeExecRunner: spawn-per-message runner using `opencode run --format json --auto`.
3
- *
4
- * Architecture:
5
- * - Each `run()` spawns a fresh `opencode run` process, writes prompt to stdin,
6
- * reads ndjson from stdout, translates to AgentEvent via OpencodeExecTranslator.
7
- * - No singleton, no long-lived process, no HTTP server, no approval handshake.
8
- * - Structurally identical to other exec runners (spawn-per-message + ProcessStopper + SpawnHeartbeat).
9
- *
10
- * The entire spawn + completion + cleanup orchestration is inherited
11
- * from SpawningRunner. This subclass overrides only the 5 hooks:
12
- * - getStdio() → ['pipe','pipe','pipe'] (writes prompt to stdin)
13
- * - getSpawnEnv(opts) → sync PWD=opts.cwd (opencode reads PWD, not cwd)
14
- * - setupStdin(proc, msg) → proc.stdin.end(msg, 'utf-8')
15
- * - handleBadStreamLine() → empty override (silently skip non-JSON lines)
16
- * - createTranslator(opts) → new OpencodeExecTranslator({ cwd: opts.cwd })
17
- *
18
- * Key invariants:
19
- * 1. argv includes `--auto` (root cause ①).
20
- * 2. stdin is written once via `stdin.end(prompt)` and never written again (root cause ②).
21
- * 3. Translator parse failures → return [] (no event), never throw, never write back (root cause ②).
22
- * 4. Stream ending early → finish() yields terminal event, never leaves consumer waiting (root cause ③).
23
- *
24
- * Verified (2026-07-13): opencode run reads prompt from stdin.
25
- * Command: `echo 'msg' | opencode run --format json --auto`
26
- * Result: stdout outputs ndjson with step_start → text → step_finish
27
- */
28
- import type { ChildProcess } from 'node:child_process';
29
- import type { AgentKind, AgentSessionReader, AgentRunner, AgentStatusInfo } from '../types.js';
30
- import type { SpawnOptions } from '../types.js';
31
- import { SpawningRunner, type RunnerTranslator } from '../common/spawning-runner.js';
32
- interface OpencodeExecRunnerOptions {
33
- /** Model override in provider/model format (e.g. 'anthropic/claude-sonnet-4-20250514'). Omitted → opencode config default. */
34
- model?: string;
35
- /** Grace period (ms) for SIGTERM→SIGKILL. Default: 5000. */
36
- stopGraceMs?: number;
37
- /** Directory for pid files. Default: `~/.lark-remote`. */
38
- pidDir?: string;
39
- /** Workspace identifier, scoped into the pid file name so concurrent
40
- * workspaces do not share one `<prefix>.pid` (P1-9). */
41
- workspace: string;
42
- /** Spawn heartbeat interval (ms). Default: 30_000. */
43
- spawnHeartbeatMs?: number;
44
- /** Injected session reader (CLI-based OpencodeSessionReader). */
45
- sessionReader: AgentSessionReader;
46
- }
47
- export declare class OpencodeExecRunner extends SpawningRunner implements AgentRunner {
48
- readonly kind: AgentKind;
49
- readonly sessionReader: AgentSessionReader;
50
- private readonly defaultModel?;
51
- constructor(opts: OpencodeExecRunnerOptions);
52
- getStatusInfo(): AgentStatusInfo;
53
- protected buildArgv(opts: SpawnOptions): string[];
54
- protected getStdio(): ('ignore' | 'pipe')[];
55
- protected getSpawnEnv(opts: SpawnOptions): NodeJS.ProcessEnv;
56
- protected setupStdin(proc: ChildProcess, message: string): void;
57
- /** Silently skip non-JSON lines from opencode stdout (e.g. warnings). */
58
- protected handleBadStreamLine(_line: string): void;
59
- protected createTranslator(opts: SpawnOptions): RunnerTranslator;
60
- }
61
- export {};
@@ -1,93 +0,0 @@
1
- /**
2
- * OpencodeExecRunner: spawn-per-message runner using `opencode run --format json --auto`.
3
- *
4
- * Architecture:
5
- * - Each `run()` spawns a fresh `opencode run` process, writes prompt to stdin,
6
- * reads ndjson from stdout, translates to AgentEvent via OpencodeExecTranslator.
7
- * - No singleton, no long-lived process, no HTTP server, no approval handshake.
8
- * - Structurally identical to other exec runners (spawn-per-message + ProcessStopper + SpawnHeartbeat).
9
- *
10
- * The entire spawn + completion + cleanup orchestration is inherited
11
- * from SpawningRunner. This subclass overrides only the 5 hooks:
12
- * - getStdio() → ['pipe','pipe','pipe'] (writes prompt to stdin)
13
- * - getSpawnEnv(opts) → sync PWD=opts.cwd (opencode reads PWD, not cwd)
14
- * - setupStdin(proc, msg) → proc.stdin.end(msg, 'utf-8')
15
- * - handleBadStreamLine() → empty override (silently skip non-JSON lines)
16
- * - createTranslator(opts) → new OpencodeExecTranslator({ cwd: opts.cwd })
17
- *
18
- * Key invariants:
19
- * 1. argv includes `--auto` (root cause ①).
20
- * 2. stdin is written once via `stdin.end(prompt)` and never written again (root cause ②).
21
- * 3. Translator parse failures → return [] (no event), never throw, never write back (root cause ②).
22
- * 4. Stream ending early → finish() yields terminal event, never leaves consumer waiting (root cause ③).
23
- *
24
- * Verified (2026-07-13): opencode run reads prompt from stdin.
25
- * Command: `echo 'msg' | opencode run --format json --auto`
26
- * Result: stdout outputs ndjson with step_start → text → step_finish
27
- */
28
- import { SpawningRunner } from '../common/spawning-runner.js';
29
- import { pipeAllStdio, endStdinWithPrompt } from '../common/runner-utils.js';
30
- import { buildOpencodeRunArgs } from './argv.js';
31
- import { OpencodeExecTranslator } from './jsonl.js';
32
- export class OpencodeExecRunner extends SpawningRunner {
33
- kind = 'opencode';
34
- sessionReader;
35
- defaultModel;
36
- constructor(opts) {
37
- super({
38
- pidDir: opts.pidDir,
39
- workspace: opts.workspace,
40
- stopGraceMs: opts.stopGraceMs,
41
- spawnHeartbeatMs: opts.spawnHeartbeatMs,
42
- pidFilePrefix: 'opencode',
43
- logTag: 'opencode-exec-runner',
44
- });
45
- this.binary = 'opencode';
46
- this.defaultModel = opts.model;
47
- this.sessionReader = opts.sessionReader;
48
- }
49
- getStatusInfo() {
50
- return {
51
- kind: 'opencode',
52
- model: this.defaultModel ?? '(未配置)',
53
- provider: this.defaultModel?.split('/')[0],
54
- };
55
- }
56
- buildArgv(opts) {
57
- const model = opts.model ?? this.defaultModel;
58
- return buildOpencodeRunArgs({
59
- model,
60
- sessionId: opts.sessionId,
61
- });
62
- }
63
- // --- Hooks ---
64
- getStdio() {
65
- // ['pipe','pipe','pipe'] so we can write the prompt to stdin via setupStdin.
66
- return pipeAllStdio();
67
- }
68
- getSpawnEnv(opts) {
69
- // Sync PWD env to opts.cwd: Node spawn's `cwd` option only chdir's the
70
- // child, it does NOT update the PWD env var (a shell convention).
71
- // opencode `run` reads PWD (not process.cwd()) for project/directory
72
- // detection in its 2nd instance phase, so an inherited PWD (e.g. bridge
73
- // started from "/") would orphan sessions under directory="/" /
74
- // project=global, making /resume unable to find them.
75
- const spawnEnv = { ...process.env };
76
- if (opts.cwd)
77
- spawnEnv.PWD = opts.cwd;
78
- return spawnEnv;
79
- }
80
- setupStdin(proc, message) {
81
- // Write prompt once and close (invariant: write once, never again).
82
- endStdinWithPrompt(proc, message);
83
- }
84
- /** Silently skip non-JSON lines from opencode stdout (e.g. warnings). */
85
- handleBadStreamLine(_line) { }
86
- createTranslator(opts) {
87
- // Fresh translator per run. Pass opts.cwd so the synthesized
88
- // SystemInitEvent carries the session's real directory (the bridge
89
- // persists this into last-session.json).
90
- return new OpencodeExecTranslator({ cwd: opts.cwd });
91
- }
92
- }
93
- //# sourceMappingURL=runner.js.map
@@ -1,128 +0,0 @@
1
- import type { RunnerTranslator } from '../common/spawning-runner.js';
2
- import type { AgentEvent } from '../types.js';
3
- interface PiSessionEvent {
4
- type: 'session';
5
- id: string;
6
- cwd: string;
7
- model?: string;
8
- }
9
- interface PiMessageStartEvent {
10
- type: 'message_start';
11
- message: {
12
- role: 'user' | 'assistant' | 'toolResult';
13
- content?: unknown[];
14
- model?: string;
15
- usage?: PiUsage;
16
- };
17
- }
18
- interface PiMessageUpdateEvent {
19
- type: 'message_update';
20
- assistantMessageEvent: {
21
- type: 'text_start' | 'text_delta' | 'text_end' | 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'toolcall_start' | 'toolcall_delta' | 'toolcall_end';
22
- contentIndex?: number;
23
- delta?: string;
24
- content?: string;
25
- toolCall?: {
26
- id: string;
27
- name: string;
28
- arguments: Record<string, unknown>;
29
- };
30
- };
31
- }
32
- interface PiMessageEndEvent {
33
- type: 'message_end';
34
- message: {
35
- role: 'user' | 'assistant' | 'toolResult';
36
- content?: unknown[];
37
- usage?: PiUsage;
38
- toolCallId?: string;
39
- toolName?: string;
40
- isError?: boolean;
41
- /** pi reports provider/LLM failures via stopReason="error" + errorMessage
42
- * (content empty, usage zeros). e.g. "Connection error." on transient
43
- * provider outage. */
44
- stopReason?: string;
45
- errorMessage?: string;
46
- };
47
- }
48
- interface PiTurnEndEvent {
49
- type: 'turn_end';
50
- message?: {
51
- usage?: PiUsage;
52
- };
53
- }
54
- interface PiAgentEndEvent {
55
- type: 'agent_end';
56
- messages?: unknown[];
57
- }
58
- interface PiUsage {
59
- input?: number;
60
- output?: number;
61
- cacheRead?: number;
62
- cacheWrite?: number;
63
- totalTokens?: number;
64
- /** Reasoning tokens (display-only, not added to total). */
65
- reasoning?: number;
66
- }
67
- type PiEvent = PiSessionEvent | PiMessageStartEvent | PiMessageUpdateEvent | PiMessageEndEvent | PiTurnEndEvent | PiAgentEndEvent | {
68
- type: string;
69
- };
70
- /** ccusage-aligned usage: pi's `input` is non-cached; cacheRead/cacheWrite map to cache read/creation. */
71
- type PiResultUsage = {
72
- input_tokens: number;
73
- output_tokens: number;
74
- cache_read_tokens: number;
75
- cache_creation_tokens: number;
76
- total_tokens?: number;
77
- };
78
- /**
79
- * Accumulates pi's incremental message_update events and emits normalized
80
- * AgentEvents. Content mapping: text→TextContent, thinking→ThinkingContent,
81
- * toolCall→ToolUseContent (input:arguments), toolResult→ToolResultContent.
82
- *
83
- * Implements RunnerTranslator so the base SpawningRunner can drive it
84
- * uniformly. pi has no agent-reported terminal error (no equivalent of codex
85
- * `turn.failed`), so isTerminal/finish/getTerminalError/hasAgentTerminalError
86
- * are no-ops — pi relies entirely on the base class's signal/code-based
87
- * result event classification.
88
- */
89
- export declare class PiEventAccumulator implements RunnerTranslator {
90
- private content;
91
- private currentContentIndex;
92
- private sessionId;
93
- /**
94
- * Accumulated (summed) token usage across all assistant messages in this run.
95
- * pi emits per-message (non-cumulative) usage on each assistant `message_end`;
96
- * summing yields the run's total consumption, aligned with ccusage (which sums
97
- * per-message entries).
98
- * `totalTokens` is NOT summed: it tracks the last message's total as the
99
- * context-length water level (summing would overcount - design.md §usage:
100
- * "N turn 累加得 N×context 虚假巨值").
101
- */
102
- private accInput;
103
- private accOutput;
104
- private accCacheRead;
105
- private accCacheCreation;
106
- private lastTotalTokens;
107
- /** stopReason of the most recent assistant message_end. "error" indicates a
108
- * provider/LLM failure (pi retries internally; a later message_end may
109
- * overwrite this with "stop" on eventual success). */
110
- private lastAssistantStopReason;
111
- /** errorMessage paired with the last assistant message_end whose
112
- * stopReason==="error". Surfaces the provider's failure text (e.g.
113
- * "Connection error.") to the user via the result event. */
114
- private lastAssistantErrorMessage;
115
- translate(raw: unknown): AgentEvent | null;
116
- isTerminal(): boolean;
117
- finish(_reason: 'failed' | 'interrupted' | 'timeout'): void;
118
- getTerminalError(): string | undefined;
119
- hasAgentTerminalError(): boolean;
120
- getSessionId(): string;
121
- getLastUsage(): PiResultUsage | undefined;
122
- /** Accumulate a per-message usage record into the run's cumulative totals. */
123
- private addUsage;
124
- normalize(raw: PiEvent): AgentEvent | null;
125
- private applyUpdate;
126
- private normalizeToolResult;
127
- }
128
- export {};