@monoes/monomindcli 2.9.20 → 2.9.22

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 (67) hide show
  1. package/.claude/helpers/control-start.cjs +7 -1
  2. package/.claude/skills/mastermind-createorg/SKILL.md +26 -3
  3. package/dist/src/commands/events.d.ts +9 -0
  4. package/dist/src/commands/events.d.ts.map +1 -0
  5. package/dist/src/commands/events.js +89 -0
  6. package/dist/src/commands/events.js.map +1 -0
  7. package/dist/src/commands/index.d.ts +1 -1
  8. package/dist/src/commands/index.d.ts.map +1 -1
  9. package/dist/src/commands/index.js +2 -1
  10. package/dist/src/commands/index.js.map +1 -1
  11. package/dist/src/commands/org-observe.d.ts +21 -0
  12. package/dist/src/commands/org-observe.d.ts.map +1 -1
  13. package/dist/src/commands/org-observe.js +88 -6
  14. package/dist/src/commands/org-observe.js.map +1 -1
  15. package/dist/src/commands/org.d.ts.map +1 -1
  16. package/dist/src/commands/org.js +20 -0
  17. package/dist/src/commands/org.js.map +1 -1
  18. package/dist/src/orgrt/approvals.d.ts +16 -1
  19. package/dist/src/orgrt/approvals.d.ts.map +1 -1
  20. package/dist/src/orgrt/approvals.js +44 -2
  21. package/dist/src/orgrt/approvals.js.map +1 -1
  22. package/dist/src/orgrt/copilot-runner.d.ts +14 -0
  23. package/dist/src/orgrt/copilot-runner.d.ts.map +1 -0
  24. package/dist/src/orgrt/copilot-runner.js +250 -0
  25. package/dist/src/orgrt/copilot-runner.js.map +1 -0
  26. package/dist/src/orgrt/crush-runner.d.ts +24 -0
  27. package/dist/src/orgrt/crush-runner.d.ts.map +1 -0
  28. package/dist/src/orgrt/crush-runner.js +251 -0
  29. package/dist/src/orgrt/crush-runner.js.map +1 -0
  30. package/dist/src/orgrt/daemon.d.ts +40 -1
  31. package/dist/src/orgrt/daemon.d.ts.map +1 -1
  32. package/dist/src/orgrt/daemon.js +68 -4
  33. package/dist/src/orgrt/daemon.js.map +1 -1
  34. package/dist/src/orgrt/grok-runner.d.ts +18 -0
  35. package/dist/src/orgrt/grok-runner.d.ts.map +1 -0
  36. package/dist/src/orgrt/grok-runner.js +323 -0
  37. package/dist/src/orgrt/grok-runner.js.map +1 -0
  38. package/dist/src/orgrt/pi-rpc-runner.d.ts +68 -0
  39. package/dist/src/orgrt/pi-rpc-runner.d.ts.map +1 -0
  40. package/dist/src/orgrt/pi-rpc-runner.js +400 -0
  41. package/dist/src/orgrt/pi-rpc-runner.js.map +1 -0
  42. package/dist/src/orgrt/pi-runner.d.ts +16 -0
  43. package/dist/src/orgrt/pi-runner.d.ts.map +1 -0
  44. package/dist/src/orgrt/pi-runner.js +264 -0
  45. package/dist/src/orgrt/pi-runner.js.map +1 -0
  46. package/dist/src/orgrt/qwen-runner.d.ts +18 -0
  47. package/dist/src/orgrt/qwen-runner.d.ts.map +1 -0
  48. package/dist/src/orgrt/qwen-runner.js +267 -0
  49. package/dist/src/orgrt/qwen-runner.js.map +1 -0
  50. package/dist/src/orgrt/session.js +1 -1
  51. package/dist/src/orgrt/session.js.map +1 -1
  52. package/dist/src/orgrt/types.d.ts +21 -0
  53. package/dist/src/orgrt/types.d.ts.map +1 -1
  54. package/dist/src/orgrt/types.js +9 -2
  55. package/dist/src/orgrt/types.js.map +1 -1
  56. package/dist/src/orgrt/usage-proxy.d.ts +32 -0
  57. package/dist/src/orgrt/usage-proxy.d.ts.map +1 -0
  58. package/dist/src/orgrt/usage-proxy.js +226 -0
  59. package/dist/src/orgrt/usage-proxy.js.map +1 -0
  60. package/dist/src/ui/dashboard.html +63 -0
  61. package/dist/src/ui/orgs.html +8 -1
  62. package/dist/src/ui/routes-org.mjs +26 -0
  63. package/dist/src/utils/resource-governor.d.ts.map +1 -1
  64. package/dist/src/utils/resource-governor.js +11 -2
  65. package/dist/src/utils/resource-governor.js.map +1 -1
  66. package/dist/tsconfig.tsbuildinfo +1 -1
  67. package/package.json +1 -1
@@ -0,0 +1,264 @@
1
+ // packages/@monomind/cli/src/orgrt/pi-runner.ts
2
+ /**
3
+ * PiAgentRunner — AgentRunner impl backed by the Pi coding agent CLI
4
+ * (`pi`, https://github.com/badlogic/pi-mono, package @mariozechner/pi-coding-agent).
5
+ *
6
+ * Architectural pattern: SAME as the other subprocess runners — spawn the
7
+ * CLI, parse its output, normalize to AgentMessage.
8
+ *
9
+ * Auth: pi's own provider login (per-provider API key or subscription,
10
+ * configured via `pi` itself). No env vars set here.
11
+ *
12
+ * Subprocess protocol — per public docs (pi-mono's rpc.md + README), NOT
13
+ * byte-verified against a running binary. `pi --mode json` shares its event
14
+ * vocabulary with pi's RPC mode:
15
+ * - Invocation: `pi --mode json --approve --session-dir <dir> "<prompt>"`.
16
+ * The prompt is POSITIONAL (matching pi's own interactive-mode CLI shape
17
+ * and the same convention codex uses) — an earlier revision of this
18
+ * runner passed it via a `-p` flag, which a second-source cross-check
19
+ * against another public agentic-CLI wrapper's tool table indicated was
20
+ * wrong; corrected here. `--approve` accepts the cwd as a trusted
21
+ * project so pi doesn't stop to ask (pi has no single "yolo" flag; tool
22
+ * auto-approval for individual actions is a separate, not-yet-wired
23
+ * concern for this runner — org tool calls go through canUseTool
24
+ * regardless, since they use the tool-fence protocol, not pi's native
25
+ * tool surface).
26
+ * (`--session-dir` also gives turn-to-turn continuity: pi persists
27
+ * sessions in that directory and resumes the latest one by default —
28
+ * there is no confirmed explicit `--resume <id>` flag for headless use,
29
+ * so this runner points every turn at the SAME per-run session dir
30
+ * rather than tracking a session id, and disclaims true cross-process
31
+ * resume as best-effort).
32
+ * - Event types seen: `agent_start` (ignored), `message_update` (partial;
33
+ * carries a cumulative `usage` object — kept as running totals but
34
+ * superseded by `message_end`), `message_end` (final `content` array
35
+ * with `{type:'text', text}` / `{type:'toolCall', ...}` items — only
36
+ * `text` items are surfaced to the bus), `tool_execution_*` (ignored;
37
+ * org tool calls use the shared tool-fence protocol, not pi's native
38
+ * tool-call surface).
39
+ * - Usage field names differ from the other CLIs: `usage.input` /
40
+ * `usage.output` (not `input_tokens`/`output_tokens`).
41
+ *
42
+ * Org tools — FENCE PROTOCOL: same approach as the other subprocess runners.
43
+ */
44
+ import { spawn } from 'node:child_process';
45
+ import { join } from 'node:path';
46
+ import { buildToolProtocol, parseToolCalls, executeToolCall, formatToolResults, MAX_TOOL_ROUNDS, TOOL_CALL_RE } from './tool-fence.js';
47
+ const TURN_TIMEOUT_MS = 2 * 60 * 60 * 1000;
48
+ /** Distinct from TURN_TIMEOUT_MS: catches a hung first-run interactive prompt
49
+ * fast instead of waiting out the full turn timeout. Any output at all
50
+ * disarms it. Per pi's own docs, `-p`/`--mode json`/`--mode rpc` do NOT show
51
+ * a trust prompt (see this file's --approve comment above), so this is a
52
+ * defensive backstop rather than a known risk the way it is for copilot. */
53
+ const STARTUP_GRACE_MS = 45_000;
54
+ /** Pure JSON-event parser — exported for unit testing against fixture lines
55
+ * (pi-runner.test.ts). */
56
+ export function parsePiEvents(lines) {
57
+ const rawTexts = [];
58
+ let inputTokens = 0;
59
+ let outputTokens = 0;
60
+ for (const line of lines) {
61
+ const trimmed = line.trim();
62
+ if (!trimmed || !trimmed.startsWith('{'))
63
+ continue;
64
+ let ev;
65
+ try {
66
+ ev = JSON.parse(trimmed);
67
+ }
68
+ catch {
69
+ continue;
70
+ }
71
+ if (ev.type === 'message_end' && ev.message?.content) {
72
+ const text = ev.message.content
73
+ .filter((b) => b.type === 'text' && typeof b.text === 'string')
74
+ .map((b) => b.text)
75
+ .join('\n');
76
+ if (text)
77
+ rawTexts.push(text);
78
+ }
79
+ const usage = ev.message?.usage ?? ev.usage;
80
+ if (usage && (typeof usage.input === 'number' || typeof usage.output === 'number')) {
81
+ inputTokens = usage.input ?? inputTokens;
82
+ outputTokens = usage.output ?? outputTokens;
83
+ }
84
+ }
85
+ const texts = rawTexts.map((t) => t.replace(TOOL_CALL_RE, '').trim());
86
+ return { texts, rawTexts, inputTokens, outputTokens };
87
+ }
88
+ export class PiAgentRunner {
89
+ piBin;
90
+ constructor(piBin) {
91
+ this.piBin = piBin;
92
+ }
93
+ async *run(args) {
94
+ const bin = this.piBin || process.env.PI_CLI_BIN || 'pi';
95
+ // Stable per-run session directory — see file header on why this
96
+ // substitutes for an explicit resume-by-id flag.
97
+ const sessionDir = join(args.cwd, '.monomind-pi-session');
98
+ try {
99
+ let first = true;
100
+ for await (const p of args.prompt) {
101
+ const text = typeof p === 'string' ? p : (p?.message?.content ?? String(p ?? ''));
102
+ let nextPrompt = first ? `${args.systemPrompt}${buildToolProtocol(args.tools)}\n\n---\n\n${text}` : text;
103
+ first = false;
104
+ let turnInputTokens = 0;
105
+ let turnOutputTokens = 0;
106
+ for (let round = 0; round <= MAX_TOOL_ROUNDS; round++) {
107
+ const outcome = await this.runTurn(bin, nextPrompt, sessionDir, args);
108
+ if (outcome.hangSuspected) {
109
+ throw new Error(`PiAgentRunner: pi produced no output within ${STARTUP_GRACE_MS / 1000}s and was killed. ` +
110
+ 'This usually means it is stuck on a prompt headless mode has no way to answer — pi\'s own ' +
111
+ 'docs say --mode json should not show a trust prompt, so this is unexpected. Run `pi` once ' +
112
+ `manually in a real terminal in this project to check, then retry.${outcome.stderrTail ? `\nstderr: ${outcome.stderrTail.slice(-500)}` : ''}`);
113
+ }
114
+ if (outcome.exitCode !== 0) {
115
+ throw new Error(`PiAgentRunner: pi failed (exit ${outcome.exitCode})` +
116
+ (outcome.timedOut ? ` — killed after exceeding the ${TURN_TIMEOUT_MS / 3_600_000}h turn timeout` : '') +
117
+ (outcome.stderrTail ? `\nstderr: ${outcome.stderrTail.slice(-500)}` : ''));
118
+ }
119
+ for (const t of outcome.texts) {
120
+ if (t.trim())
121
+ yield { type: 'assistant', text: t };
122
+ }
123
+ turnInputTokens += outcome.inputTokens;
124
+ turnOutputTokens += outcome.outputTokens;
125
+ const malformed = [];
126
+ const calls = parseToolCalls(outcome.rawTexts, (raw, err) => malformed.push(`[monomind] ignored malformed tool_call fence (${err}): ${raw.slice(0, 200)}`));
127
+ for (const note of malformed)
128
+ yield { type: 'assistant', text: note };
129
+ if (calls.length === 0)
130
+ break;
131
+ if (round === MAX_TOOL_ROUNDS) {
132
+ yield { type: 'assistant', text: `[monomind] tool-call round cap (${MAX_TOOL_ROUNDS}) reached — dropping ${calls.length} pending tool call(s)` };
133
+ break;
134
+ }
135
+ const results = [];
136
+ for (const call of calls)
137
+ results.push(await executeToolCall(args.tools, call, args.canUseTool));
138
+ nextPrompt = formatToolResults(calls, results);
139
+ }
140
+ yield {
141
+ type: 'result',
142
+ subtype: 'success',
143
+ input_tokens: turnInputTokens,
144
+ output_tokens: turnOutputTokens,
145
+ };
146
+ }
147
+ }
148
+ catch (err) {
149
+ if (err.code === 'ENOENT') {
150
+ throw new Error('PiAgentRunner requires the Pi coding agent CLI (pi) on PATH. ' +
151
+ 'Install it: npm install -g @mariozechner/pi-coding-agent, then configure a ' +
152
+ 'provider. Or unset the runtime to use Claude.');
153
+ }
154
+ throw err;
155
+ }
156
+ }
157
+ runTurn(bin, prompt, sessionDir, args) {
158
+ return new Promise((resolve, reject) => {
159
+ // Prompt is positional (see file header) — always LAST so no later flag
160
+ // is mistaken for part of it.
161
+ const cliArgs = ['--mode', 'json', '--approve', '--session-dir', sessionDir];
162
+ if (args.model)
163
+ cliArgs.push('--model', args.model);
164
+ cliArgs.push(prompt);
165
+ const child = spawn(bin, cliArgs, {
166
+ cwd: args.cwd,
167
+ // PI_TELEMETRY/PI_SKIP_VERSION_CHECK: confirmed via pi's own docs —
168
+ // suppresses install/update telemetry and version-check network calls
169
+ // that otherwise add latency/flakiness to every headless turn.
170
+ env: { ...process.env, PI_TELEMETRY: '0', PI_SKIP_VERSION_CHECK: '1', ...args.env },
171
+ stdio: ['ignore', 'pipe', 'pipe'],
172
+ });
173
+ let stderrTail = '';
174
+ child.stderr?.on('data', (c) => { stderrTail = (stderrTail + c.toString()).slice(-4000); });
175
+ let sawOutput = false;
176
+ let timedOut = false;
177
+ let hangSuspected = false;
178
+ const KILL_GRACE_MS = 5000;
179
+ let killTimer;
180
+ const timer = setTimeout(() => {
181
+ timedOut = true;
182
+ child.kill('SIGTERM');
183
+ killTimer = setTimeout(() => { try {
184
+ child.kill('SIGKILL');
185
+ }
186
+ catch { /* already gone */ } }, KILL_GRACE_MS);
187
+ killTimer.unref?.();
188
+ }, TURN_TIMEOUT_MS);
189
+ // See STARTUP_GRACE_MS — disarmed by the first stdout chunk below.
190
+ let hangTimer = setTimeout(() => {
191
+ hangSuspected = true;
192
+ child.kill('SIGTERM');
193
+ killTimer = setTimeout(() => { try {
194
+ child.kill('SIGKILL');
195
+ }
196
+ catch { /* already gone */ } }, KILL_GRACE_MS);
197
+ killTimer.unref?.();
198
+ }, STARTUP_GRACE_MS);
199
+ const exitPromise = new Promise((res, rej) => {
200
+ child.on('error', rej);
201
+ child.on('close', (code) => res(code ?? 1));
202
+ });
203
+ exitPromise.catch(() => { });
204
+ const readLines = (async () => {
205
+ const lines = [];
206
+ let buf = '';
207
+ for await (const chunk of child.stdout) {
208
+ if (!sawOutput) {
209
+ sawOutput = true;
210
+ if (hangTimer) {
211
+ clearTimeout(hangTimer);
212
+ hangTimer = undefined;
213
+ }
214
+ }
215
+ buf += chunk.toString();
216
+ const parts = buf.split('\n');
217
+ buf = parts.pop() ?? '';
218
+ lines.push(...parts);
219
+ }
220
+ if (buf.trim())
221
+ lines.push(buf);
222
+ return lines;
223
+ })();
224
+ // Timer cleanup lives in a top-level .finally() (not nested inside a
225
+ // success-path .then()) so it runs on EITHER path — a stdout stream
226
+ // error would otherwise skip straight to reject() and leave the
227
+ // TURN_TIMEOUT_MS/hangTimer/killTimer timers running past the
228
+ // process's actual lifetime.
229
+ Promise.all([readLines, exitPromise])
230
+ .then(([lines, exitCode]) => {
231
+ const parsed = parsePiEvents(lines);
232
+ resolve({
233
+ texts: parsed.texts,
234
+ rawTexts: parsed.rawTexts,
235
+ exitCode,
236
+ stderrTail,
237
+ timedOut,
238
+ hangSuspected,
239
+ inputTokens: parsed.inputTokens,
240
+ outputTokens: parsed.outputTokens,
241
+ });
242
+ }, (err) => {
243
+ // A stdout stream error (the reject path) means the process is
244
+ // still ALIVE and unmanaged — none of the timeout/hang timers
245
+ // would have fired to kill it. Without this, that error would
246
+ // orphan the child. child.kill() on an already-dead process is a
247
+ // documented no-op, so this is safe on every path.
248
+ try {
249
+ child.kill('SIGTERM');
250
+ }
251
+ catch { /* already gone */ }
252
+ reject(err);
253
+ })
254
+ .finally(() => {
255
+ clearTimeout(timer);
256
+ if (hangTimer)
257
+ clearTimeout(hangTimer);
258
+ if (killTimer)
259
+ clearTimeout(killTimer);
260
+ });
261
+ });
262
+ }
263
+ }
264
+ //# sourceMappingURL=pi-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-runner.js","sourceRoot":"","sources":["../../../src/orgrt/pi-runner.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACvI,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC3C;;;;6EAI6E;AAC7E,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAUhC;2BAC2B;AAC3B,MAAM,UAAU,aAAa,CAAC,KAAe;IAG3C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACnD,IAAI,EAAW,CAAC;QAChB,IAAI,CAAC;YAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QAErD,IAAI,EAAE,CAAC,IAAI,KAAK,aAAa,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;YACrD,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO;iBAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;iBAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAc,CAAC;iBAC5B,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,IAAI,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC;QAC5C,IAAI,KAAK,IAAI,CAAC,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,EAAE,CAAC;YACnF,WAAW,GAAG,KAAK,CAAC,KAAK,IAAI,WAAW,CAAC;YACzC,YAAY,GAAG,KAAK,CAAC,MAAM,IAAI,YAAY,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACtE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AACxD,CAAC;AAeD,MAAM,OAAO,aAAa;IACJ,KAAK;IAAzB,YAAoB,KAAc;qBAAd,KAAK;IAAY,CAAC;IAEtC,KAAK,CAAC,CAAC,GAAG,CAAC,IAAkB;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC;QACzD,iEAAiE;QACjE,iDAAiD;QACjD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;QAE1D,IAAI,CAAC;YACH,IAAI,KAAK,GAAG,IAAI,CAAC;YACjB,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAClF,IAAI,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;gBACzG,KAAK,GAAG,KAAK,CAAC;gBACd,IAAI,eAAe,GAAG,CAAC,CAAC;gBACxB,IAAI,gBAAgB,GAAG,CAAC,CAAC;gBAEzB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,eAAe,EAAE,KAAK,EAAE,EAAE,CAAC;oBACtD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;oBAEtE,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;wBAC1B,MAAM,IAAI,KAAK,CACb,+CAA+C,gBAAgB,GAAG,IAAI,oBAAoB;4BAC1F,4FAA4F;4BAC5F,4FAA4F;4BAC5F,oEAAoE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9I,CAAC;oBACJ,CAAC;oBACD,IAAI,OAAO,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;wBAC3B,MAAM,IAAI,KAAK,CACb,kCAAkC,OAAO,CAAC,QAAQ,GAAG;4BACrD,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,iCAAiC,eAAe,GAAG,SAAS,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;4BACtG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC1E,CAAC;oBACJ,CAAC;oBAED,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;wBAC9B,IAAI,CAAC,CAAC,IAAI,EAAE;4BAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;oBACrD,CAAC;oBACD,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC;oBACvC,gBAAgB,IAAI,OAAO,CAAC,YAAY,CAAC;oBAEzC,MAAM,SAAS,GAAa,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CACzE,iDAAiD,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC9E,CAAC,CAAC;oBACH,KAAK,MAAM,IAAI,IAAI,SAAS;wBAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;oBACtE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;wBAAE,MAAM;oBAE9B,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;wBAC9B,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,mCAAmC,eAAe,wBAAwB,KAAK,CAAC,MAAM,uBAAuB,EAAE,CAAC;wBACjJ,MAAM;oBACR,CAAC;oBAED,MAAM,OAAO,GAAa,EAAE,CAAC;oBAC7B,KAAK,MAAM,IAAI,IAAI,KAAK;wBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;oBACjG,UAAU,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBACjD,CAAC;gBAED,MAAM;oBACJ,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,SAAS;oBAClB,YAAY,EAAE,eAAe;oBAC7B,aAAa,EAAE,gBAAgB;iBAChC,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,MAAM,IAAI,KAAK,CACb,+DAA+D;oBAC/D,6EAA6E;oBAC7E,+CAA+C,CAChD,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,GAAW,EAAE,MAAc,EAAE,UAAkB,EAAE,IAAkB;QACjF,OAAO,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAClD,wEAAwE;YACxE,8BAA8B;YAC9B,MAAM,OAAO,GAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;YACvF,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACpD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAErB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE;gBAChC,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,oEAAoE;gBACpE,sEAAsE;gBACtE,+DAA+D;gBAC/D,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,qBAAqB,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;gBACnF,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;aAClC,CAAC,CAAC;YAEH,IAAI,UAAU,GAAG,EAAE,CAAC;YACpB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,GAAG,UAAU,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAEpG,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,IAAI,aAAa,GAAG,KAAK,CAAC;YAC1B,MAAM,aAAa,GAAG,IAAI,CAAC;YAC3B,IAAI,SAAoD,CAAC;YACzD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,QAAQ,GAAG,IAAI,CAAC;gBAChB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;oBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC7G,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;YACtB,CAAC,EAAE,eAAe,CAAC,CAAC;YAEpB,mEAAmE;YACnE,IAAI,SAAS,GAA8C,UAAU,CAAC,GAAG,EAAE;gBACzE,aAAa,GAAG,IAAI,CAAC;gBACrB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;oBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC7G,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;YACtB,CAAC,EAAE,gBAAgB,CAAC,CAAC;YAErB,MAAM,WAAW,GAAG,IAAI,OAAO,CAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACnD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBACvB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;YACH,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAE5B,MAAM,SAAS,GAAG,CAAC,KAAK,IAAI,EAAE;gBAC5B,MAAM,KAAK,GAAa,EAAE,CAAC;gBAC3B,IAAI,GAAG,GAAG,EAAE,CAAC;gBACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,CAAC,MAA+B,EAAE,CAAC;oBAChE,IAAI,CAAC,SAAS,EAAE,CAAC;wBAAC,SAAS,GAAG,IAAI,CAAC;wBAAC,IAAI,SAAS,EAAE,CAAC;4BAAC,YAAY,CAAC,SAAS,CAAC,CAAC;4BAAC,SAAS,GAAG,SAAS,CAAC;wBAAC,CAAC;oBAAC,CAAC;oBACxG,GAAG,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACxB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC9B,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;oBACxB,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;gBACvB,CAAC;gBACD,IAAI,GAAG,CAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAChC,OAAO,KAAK,CAAC;YACf,CAAC,CAAC,EAAE,CAAC;YAEL,qEAAqE;YACrE,oEAAoE;YACpE,gEAAgE;YAChE,8DAA8D;YAC9D,6BAA6B;YAC7B,OAAO,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;iBAClC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE;gBAC1B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBACpC,OAAO,CAAC;oBACN,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,QAAQ;oBACR,UAAU;oBACV,QAAQ;oBACR,aAAa;oBACb,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;iBAClC,CAAC,CAAC;YACL,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE;gBACT,+DAA+D;gBAC/D,8DAA8D;gBAC9D,8DAA8D;gBAC9D,iEAAiE;gBACjE,mDAAmD;gBACnD,IAAI,CAAC;oBAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;gBAC3D,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,SAAS;oBAAE,YAAY,CAAC,SAAS,CAAC,CAAC;gBACvC,IAAI,SAAS;oBAAE,YAAY,CAAC,SAAS,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import type { AgentRunner, AgentRunArgs, AgentMessage } from './agent-runner.js';
2
+ /** Pure stream-json parser — exported for unit testing against fixture lines
3
+ * built from Qwen Code's documented event schema (qwen-runner.test.ts). */
4
+ export declare function parseQwenEvents(lines: string[]): {
5
+ texts: string[];
6
+ rawTexts: string[];
7
+ sessionId?: string;
8
+ inputTokens: number;
9
+ outputTokens: number;
10
+ error?: string;
11
+ };
12
+ export declare class QwenAgentRunner implements AgentRunner {
13
+ private qwenBin?;
14
+ constructor(qwenBin?: string | undefined);
15
+ run(args: AgentRunArgs): AsyncIterable<AgentMessage>;
16
+ private runTurn;
17
+ }
18
+ //# sourceMappingURL=qwen-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qwen-runner.d.ts","sourceRoot":"","sources":["../../../src/orgrt/qwen-runner.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAqCjF;4EAC4E;AAC5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG;IAChD,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACpH,CAqCA;AAED,qBAAa,eAAgB,YAAW,WAAW;IACrC,OAAO,CAAC,OAAO,CAAC;IAA5B,YAAoB,OAAO,CAAC,EAAE,MAAM,YAAA,EAAI;IAEjC,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,CAsF1D;IAED,OAAO,CAAC,OAAO;CAgGhB"}
@@ -0,0 +1,267 @@
1
+ // packages/@monomind/cli/src/orgrt/qwen-runner.ts
2
+ /**
3
+ * QwenAgentRunner — AgentRunner impl backed by the Qwen Code CLI (`qwen`,
4
+ * https://qwenlm.github.io/qwen-code-docs/).
5
+ *
6
+ * Architectural pattern: SAME as CodexAgentRunner/GrokAgentRunner — spawn the
7
+ * vendor's CLI binary, parse its stream-json output, normalize to
8
+ * AgentMessage. No SDK dependency.
9
+ *
10
+ * Auth: inherited from the CLI's own login flow. No env vars set here.
11
+ *
12
+ * Org tools — FENCE PROTOCOL: same approach as the other subprocess runners
13
+ * (see tool-fence.ts).
14
+ *
15
+ * Subprocess protocol — per Qwen Code's public "Headless Mode" docs, NOT
16
+ * byte-verified against a running binary:
17
+ * - Invocation: `qwen -p "<prompt>" --output-format stream-json --yolo
18
+ * [-m <model>] [--resume <sessionId> | --continue]`
19
+ * - stream-json emits one JSON object per line; documented shape:
20
+ * { type: 'system'|'assistant'|'result', subtype, uuid, session_id,
21
+ * role: 'assistant',
22
+ * message: { content: [{type:'text', text}], usage: { tokens: { input, output, total } } } }
23
+ * - Session continuity: `--resume [sessionId]` resumes a specific session,
24
+ * `--continue` resumes the most recent one; `session_id` is carried on
25
+ * every event.
26
+ * - `--yolo` auto-approves tool actions (org roles gate tool execution
27
+ * themselves via canUseTool/tool-fence, so CLI-level approval prompts
28
+ * would otherwise hang a non-interactive run).
29
+ */
30
+ import { spawn } from 'node:child_process';
31
+ import { buildToolProtocol, parseToolCalls, executeToolCall, formatToolResults, MAX_TOOL_ROUNDS, TOOL_CALL_RE } from './tool-fence.js';
32
+ const TURN_TIMEOUT_MS = 2 * 60 * 60 * 1000;
33
+ /** Distinct from TURN_TIMEOUT_MS: catches a hung first-run interactive prompt
34
+ * (trust/telemetry gate) fast instead of waiting out the full turn timeout.
35
+ * Fires only if the process has produced ZERO stdout by this point — any
36
+ * output at all disarms it, since a slow model response is not a hang. */
37
+ const STARTUP_GRACE_MS = 45_000;
38
+ /** Pure stream-json parser — exported for unit testing against fixture lines
39
+ * built from Qwen Code's documented event schema (qwen-runner.test.ts). */
40
+ export function parseQwenEvents(lines) {
41
+ const rawTexts = [];
42
+ let sessionId;
43
+ let inputTokens = 0;
44
+ let outputTokens = 0;
45
+ let error;
46
+ for (const line of lines) {
47
+ const trimmed = line.trim();
48
+ if (!trimmed || !trimmed.startsWith('{'))
49
+ continue;
50
+ let ev;
51
+ try {
52
+ ev = JSON.parse(trimmed);
53
+ }
54
+ catch {
55
+ continue;
56
+ }
57
+ if (ev.session_id)
58
+ sessionId = ev.session_id;
59
+ if (ev.type === 'assistant' && ev.message?.content) {
60
+ const text = ev.message.content
61
+ .filter((b) => b.type === 'text' && typeof b.text === 'string')
62
+ .map((b) => b.text)
63
+ .join('\n');
64
+ if (text)
65
+ rawTexts.push(text);
66
+ }
67
+ if (ev.type === 'result') {
68
+ const tokens = ev.message?.usage?.tokens;
69
+ if (tokens) {
70
+ inputTokens = tokens.input ?? 0;
71
+ outputTokens = tokens.output ?? 0;
72
+ }
73
+ if (ev.subtype === 'error') {
74
+ error = typeof ev.error === 'string' ? ev.error : ev.error?.message ?? 'qwen result: error';
75
+ }
76
+ }
77
+ }
78
+ const texts = rawTexts.map((t) => t.replace(TOOL_CALL_RE, '').trim());
79
+ return { texts, rawTexts, sessionId, inputTokens, outputTokens, error };
80
+ }
81
+ export class QwenAgentRunner {
82
+ qwenBin;
83
+ constructor(qwenBin) {
84
+ this.qwenBin = qwenBin;
85
+ }
86
+ async *run(args) {
87
+ const bin = this.qwenBin || process.env.QWEN_CLI_BIN || 'qwen';
88
+ let sessionId = args.resume;
89
+ try {
90
+ for await (const p of args.prompt) {
91
+ const text = typeof p === 'string' ? p : (p?.message?.content ?? String(p ?? ''));
92
+ let nextPrompt = text;
93
+ let turnInputTokens = 0;
94
+ let turnOutputTokens = 0;
95
+ for (let round = 0; round <= MAX_TOOL_ROUNDS; round++) {
96
+ // Gated on !sessionId alone, NOT `round === 0 && !sessionId` — if
97
+ // a session id never gets parsed out of qwen's output, every
98
+ // round after the first would otherwise spawn a completely
99
+ // fresh, contextless qwen invocation carrying only the tool-
100
+ // result text. Once a session id IS captured, --resume takes
101
+ // over and this stops re-sending the system prompt.
102
+ const promptWithSystem = !sessionId
103
+ ? `${args.systemPrompt}${buildToolProtocol(args.tools)}\n\n---\n\n${nextPrompt}`
104
+ : nextPrompt;
105
+ const outcome = await this.runTurn(bin, promptWithSystem, sessionId, args);
106
+ if (outcome.sessionId)
107
+ sessionId = outcome.sessionId;
108
+ if (outcome.hangSuspected) {
109
+ throw new Error(`QwenAgentRunner: qwen produced no output within ${STARTUP_GRACE_MS / 1000}s and was killed. ` +
110
+ 'This usually means it is stuck on a first-run interactive prompt (trust/telemetry gate) ' +
111
+ 'that headless mode has no way to answer. Run `qwen` once manually in a real terminal in ' +
112
+ `this project to accept any prompts, then retry.${outcome.stderrTail ? `\nstderr: ${outcome.stderrTail.slice(-500)}` : ''}`);
113
+ }
114
+ if (outcome.exitCode !== 0 || outcome.error) {
115
+ throw new Error(`QwenAgentRunner: qwen failed (exit ${outcome.exitCode})` +
116
+ (outcome.timedOut ? ` — killed after exceeding the ${TURN_TIMEOUT_MS / 3_600_000}h turn timeout` : '') +
117
+ (outcome.error ? `: ${outcome.error}` : '') +
118
+ (outcome.stderrTail ? `\nstderr: ${outcome.stderrTail.slice(-500)}` : ''));
119
+ }
120
+ for (const t of outcome.texts) {
121
+ if (t.trim())
122
+ yield { type: 'assistant', session_id: sessionId, text: t };
123
+ }
124
+ turnInputTokens += outcome.inputTokens;
125
+ turnOutputTokens += outcome.outputTokens;
126
+ const malformed = [];
127
+ const calls = parseToolCalls(outcome.rawTexts, (raw, err) => malformed.push(`[monomind] ignored malformed tool_call fence (${err}): ${raw.slice(0, 200)}`));
128
+ for (const note of malformed)
129
+ yield { type: 'assistant', session_id: sessionId, text: note };
130
+ if (calls.length === 0)
131
+ break;
132
+ if (round === MAX_TOOL_ROUNDS) {
133
+ yield {
134
+ type: 'assistant', session_id: sessionId,
135
+ text: `[monomind] tool-call round cap (${MAX_TOOL_ROUNDS}) reached — dropping ${calls.length} pending tool call(s)`,
136
+ };
137
+ break;
138
+ }
139
+ const results = [];
140
+ for (const call of calls)
141
+ results.push(await executeToolCall(args.tools, call, args.canUseTool));
142
+ nextPrompt = formatToolResults(calls, results);
143
+ }
144
+ yield {
145
+ type: 'result',
146
+ session_id: sessionId,
147
+ subtype: 'success',
148
+ input_tokens: turnInputTokens,
149
+ output_tokens: turnOutputTokens,
150
+ };
151
+ }
152
+ }
153
+ catch (err) {
154
+ if (err.code === 'ENOENT') {
155
+ throw new Error('QwenAgentRunner requires the Qwen Code CLI (qwen) on PATH. ' +
156
+ 'Install it: npm install -g @qwen-code/qwen-code, then run `qwen` once ' +
157
+ 'to authenticate. Or unset the runtime to use Claude.');
158
+ }
159
+ throw err;
160
+ }
161
+ }
162
+ runTurn(bin, prompt, sessionId, args) {
163
+ return new Promise((resolve, reject) => {
164
+ const cliArgs = ['-p', prompt, '--output-format', 'stream-json', '--yolo'];
165
+ if (args.model)
166
+ cliArgs.push('-m', args.model);
167
+ if (sessionId)
168
+ cliArgs.push('--resume', sessionId);
169
+ const child = spawn(bin, cliArgs, {
170
+ cwd: args.cwd,
171
+ env: { ...process.env, ...args.env },
172
+ stdio: ['ignore', 'pipe', 'pipe'],
173
+ });
174
+ let stderrTail = '';
175
+ child.stderr?.on('data', (c) => { stderrTail = (stderrTail + c.toString()).slice(-4000); });
176
+ let sawOutput = false;
177
+ let timedOut = false;
178
+ let hangSuspected = false;
179
+ const KILL_GRACE_MS = 5000;
180
+ let killTimer;
181
+ const timer = setTimeout(() => {
182
+ timedOut = true;
183
+ child.kill('SIGTERM');
184
+ killTimer = setTimeout(() => { try {
185
+ child.kill('SIGKILL');
186
+ }
187
+ catch { /* already gone */ } }, KILL_GRACE_MS);
188
+ killTimer.unref?.();
189
+ }, TURN_TIMEOUT_MS);
190
+ // See STARTUP_GRACE_MS — disarmed by the first stdout chunk below.
191
+ let hangTimer = setTimeout(() => {
192
+ hangSuspected = true;
193
+ child.kill('SIGTERM');
194
+ killTimer = setTimeout(() => { try {
195
+ child.kill('SIGKILL');
196
+ }
197
+ catch { /* already gone */ } }, KILL_GRACE_MS);
198
+ killTimer.unref?.();
199
+ }, STARTUP_GRACE_MS);
200
+ const exitPromise = new Promise((res, rej) => {
201
+ child.on('error', rej);
202
+ child.on('close', (code) => res(code ?? 1));
203
+ });
204
+ exitPromise.catch(() => { });
205
+ const readLines = (async () => {
206
+ const lines = [];
207
+ let buf = '';
208
+ for await (const chunk of child.stdout) {
209
+ if (!sawOutput) {
210
+ sawOutput = true;
211
+ if (hangTimer) {
212
+ clearTimeout(hangTimer);
213
+ hangTimer = undefined;
214
+ }
215
+ }
216
+ buf += chunk.toString();
217
+ const parts = buf.split('\n');
218
+ buf = parts.pop() ?? '';
219
+ lines.push(...parts);
220
+ }
221
+ if (buf.trim())
222
+ lines.push(buf);
223
+ return lines;
224
+ })();
225
+ // Timer cleanup lives in a top-level .finally() (not nested inside a
226
+ // success-path .then()) so it runs on EITHER path — a stdout stream
227
+ // error would otherwise skip straight to reject() and leave the
228
+ // TURN_TIMEOUT_MS/hangTimer/killTimer timers running past the
229
+ // process's actual lifetime.
230
+ Promise.all([readLines, exitPromise])
231
+ .then(([lines, exitCode]) => {
232
+ const parsed = parseQwenEvents(lines);
233
+ resolve({
234
+ texts: parsed.texts,
235
+ rawTexts: parsed.rawTexts,
236
+ sessionId: parsed.sessionId ?? sessionId,
237
+ exitCode,
238
+ stderrTail,
239
+ timedOut,
240
+ hangSuspected,
241
+ inputTokens: parsed.inputTokens,
242
+ outputTokens: parsed.outputTokens,
243
+ error: parsed.error,
244
+ });
245
+ }, (err) => {
246
+ // A stdout stream error (the reject path) means the process is
247
+ // still ALIVE and unmanaged — none of the timeout/hang timers
248
+ // would have fired to kill it. Without this, that error would
249
+ // orphan the child. child.kill() on an already-dead process is a
250
+ // documented no-op, so this is safe on every path.
251
+ try {
252
+ child.kill('SIGTERM');
253
+ }
254
+ catch { /* already gone */ }
255
+ reject(err);
256
+ })
257
+ .finally(() => {
258
+ clearTimeout(timer);
259
+ if (hangTimer)
260
+ clearTimeout(hangTimer);
261
+ if (killTimer)
262
+ clearTimeout(killTimer);
263
+ });
264
+ });
265
+ }
266
+ }
267
+ //# sourceMappingURL=qwen-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qwen-runner.js","sourceRoot":"","sources":["../../../src/orgrt/qwen-runner.ts"],"names":[],"mappings":"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"}
@@ -480,7 +480,7 @@ export function buildOrgTools(opts) {
480
480
  if (opts.onComplete) {
481
481
  tools.push({
482
482
  name: 'org_complete',
483
- description: 'Record the outcome of this run. Call exactly once, when the goal is achieved or clearly cannot be. This ends the run: check org_tasks first — siblings with in-progress work only get a short drain window to finish before being cut off, so do not call this while others are still mid-build or mid-edit unless the run genuinely cannot continue. The outcome and summary are persisted to the org run history and briefed to the next run.',
483
+ description: '⚠️ Ends the run — every agent session shuts down after this. Call exactly once, and only against the org\'s actual, full stated goal (see your briefing), never against just the current batch of dispatched tasks. "achieved" means the WHOLE goal is done, not "everyone assigned so far finished their piece" — a multi-phase or open-ended goal is very rarely achieved in a single run. If the current task batch is clearly done but the goal has more scope left: do NOT call this — use org_task/createTask to dispatch the next phase\'s work instead, so the org keeps making progress instead of stopping short. Use outcome "partial" only when you are ending the run with real scope still remaining (e.g. a genuine external stopping point, not merely "out of tasks right now"); "achieved" only when the full goal is met; "failed" only when it clearly cannot be. Before calling, check org_tasks — siblings with in-progress work only get a short drain window to finish before being cut off, so do not call this while others are still mid-build or mid-edit unless the run genuinely cannot continue. The outcome and summary are persisted to the org run history and briefed to the next run.',
484
484
  schema: { outcome: z.enum(['achieved', 'partial', 'failed']), summary: z.string() },
485
485
  handler: async (args) => {
486
486
  opts.onComplete(role.id, args.outcome, args.summary);