@yu_robotics/remote-cli 1.6.30 → 1.6.34

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 (77) hide show
  1. package/dist/client/MessageHandler.d.ts +5 -3
  2. package/dist/client/MessageHandler.d.ts.map +1 -1
  3. package/dist/client/MessageHandler.js +26 -16
  4. package/dist/client/MessageHandler.js.map +1 -1
  5. package/dist/commands/start.d.ts +7 -0
  6. package/dist/commands/start.d.ts.map +1 -1
  7. package/dist/commands/start.js +21 -5
  8. package/dist/commands/start.js.map +1 -1
  9. package/dist/config/ConfigManager.d.ts +2 -0
  10. package/dist/config/ConfigManager.d.ts.map +1 -1
  11. package/dist/config/ConfigManager.js +34 -2
  12. package/dist/config/ConfigManager.js.map +1 -1
  13. package/dist/executor/AcpExecutor.d.ts +79 -0
  14. package/dist/executor/AcpExecutor.d.ts.map +1 -0
  15. package/dist/executor/AcpExecutor.js +532 -0
  16. package/dist/executor/AcpExecutor.js.map +1 -0
  17. package/dist/executor/ClaudePersistentExecutor.d.ts.map +1 -1
  18. package/dist/executor/ClaudePersistentExecutor.js +2 -1
  19. package/dist/executor/ClaudePersistentExecutor.js.map +1 -1
  20. package/dist/executor/KimiExecutor.d.ts +2 -2
  21. package/dist/executor/KimiExecutor.d.ts.map +1 -1
  22. package/dist/executor/KimiExecutor.js +2 -2
  23. package/dist/executor/KimiExecutor.js.map +1 -1
  24. package/dist/executor/OpenCodeExecutor.d.ts +4 -65
  25. package/dist/executor/OpenCodeExecutor.d.ts.map +1 -1
  26. package/dist/executor/OpenCodeExecutor.js +19 -504
  27. package/dist/executor/OpenCodeExecutor.js.map +1 -1
  28. package/dist/executor/ZCodeExecutor.d.ts +20 -0
  29. package/dist/executor/ZCodeExecutor.d.ts.map +1 -0
  30. package/dist/executor/ZCodeExecutor.js +37 -0
  31. package/dist/executor/ZCodeExecutor.js.map +1 -0
  32. package/dist/executor/acp/AcpClient.d.ts +3 -0
  33. package/dist/executor/acp/AcpClient.d.ts.map +1 -1
  34. package/dist/executor/acp/AcpClient.js.map +1 -1
  35. package/dist/executor/index.d.ts +6 -9
  36. package/dist/executor/index.d.ts.map +1 -1
  37. package/dist/executor/index.js +26 -54
  38. package/dist/executor/index.js.map +1 -1
  39. package/dist/executor/zcode/ZCodeClient.d.ts +73 -0
  40. package/dist/executor/zcode/ZCodeClient.d.ts.map +1 -0
  41. package/dist/executor/zcode/ZCodeClient.js +681 -0
  42. package/dist/executor/zcode/ZCodeClient.js.map +1 -0
  43. package/dist/executor/zcode/ZCodeCommand.d.ts +9 -0
  44. package/dist/executor/zcode/ZCodeCommand.d.ts.map +1 -0
  45. package/dist/executor/zcode/ZCodeCommand.js +122 -0
  46. package/dist/executor/zcode/ZCodeCommand.js.map +1 -0
  47. package/dist/thread/ThreadExecutorPool.js +2 -2
  48. package/dist/thread/ThreadExecutorPool.js.map +1 -1
  49. package/dist/types/config.d.ts +13 -5
  50. package/dist/types/config.d.ts.map +1 -1
  51. package/dist/types/config.js +2 -0
  52. package/dist/types/config.js.map +1 -1
  53. package/package.json +2 -2
  54. package/dist/executor/ClaudeExecutor.d.ts +0 -93
  55. package/dist/executor/ClaudeExecutor.d.ts.map +0 -1
  56. package/dist/executor/ClaudeExecutor.js +0 -380
  57. package/dist/executor/ClaudeExecutor.js.map +0 -1
  58. package/dist/executor/CodexExecutor.d.ts +0 -140
  59. package/dist/executor/CodexExecutor.d.ts.map +0 -1
  60. package/dist/executor/CodexExecutor.js +0 -789
  61. package/dist/executor/CodexExecutor.js.map +0 -1
  62. package/dist/executor/GeminiExecutor.d.ts +0 -93
  63. package/dist/executor/GeminiExecutor.d.ts.map +0 -1
  64. package/dist/executor/GeminiExecutor.js +0 -415
  65. package/dist/executor/GeminiExecutor.js.map +0 -1
  66. package/dist/executor/acp/SessionManager.d.ts +0 -41
  67. package/dist/executor/acp/SessionManager.d.ts.map +0 -1
  68. package/dist/executor/acp/SessionManager.js +0 -152
  69. package/dist/executor/acp/SessionManager.js.map +0 -1
  70. package/dist/utils/processUtils.d.ts +0 -16
  71. package/dist/utils/processUtils.d.ts.map +0 -1
  72. package/dist/utils/processUtils.js +0 -37
  73. package/dist/utils/processUtils.js.map +0 -1
  74. package/dist/worktree/WorktreeManager.d.ts +0 -119
  75. package/dist/worktree/WorktreeManager.d.ts.map +0 -1
  76. package/dist/worktree/WorktreeManager.js +0 -451
  77. package/dist/worktree/WorktreeManager.js.map +0 -1
@@ -0,0 +1,681 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ZCodeClient = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const readline = __importStar(require("readline"));
41
+ const fs_1 = require("fs");
42
+ const ZCodeCommand_1 = require("./ZCodeCommand");
43
+ const KILL_GRACE_MS = 3_000;
44
+ const REQUEST_TIMEOUT_MS = 30_000;
45
+ /** Direct client for the official ZCode app-server line-delimited JSON protocol. */
46
+ class ZCodeClient {
47
+ child;
48
+ callbacks;
49
+ cwd;
50
+ autoApprove;
51
+ requestTimeoutMs;
52
+ compactPollIntervalMs;
53
+ pending = new Map();
54
+ prompts = new Map();
55
+ sessions = new Map();
56
+ interactions = new Map();
57
+ sequence = new Map();
58
+ reader;
59
+ nextId = 1;
60
+ destroyed = false;
61
+ closed = false;
62
+ killTimer = null;
63
+ constructor(options, callbacks) {
64
+ this.callbacks = callbacks;
65
+ this.cwd = options.cwd;
66
+ this.autoApprove = options.autoApprove ?? true;
67
+ this.requestTimeoutMs = options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;
68
+ this.compactPollIntervalMs = options.compactPollIntervalMs ?? 2_000;
69
+ const launch = options.launch ?? (0, ZCodeCommand_1.resolveZCodeLaunch)(options.command);
70
+ this.child = (0, child_process_1.spawn)(launch.command, launch.args, {
71
+ cwd: options.cwd,
72
+ stdio: ['pipe', 'pipe', 'pipe'],
73
+ env: launch.env,
74
+ detached: process.platform !== 'win32',
75
+ });
76
+ this.child.stdin?.on('error', () => { });
77
+ this.child.stdout?.on('error', () => { });
78
+ this.child.stderr?.on('data', (chunk) => {
79
+ const message = chunk.toString().trimEnd();
80
+ if (message)
81
+ console.error(`[ZCodeClient stderr] ${message}`);
82
+ });
83
+ this.reader = readline.createInterface({ input: this.child.stdout });
84
+ this.reader.on('line', (line) => this.handleLine(line));
85
+ this.child.on('error', (error) => this.rejectAll(error));
86
+ this.child.on('close', (code, signal) => {
87
+ this.closed = true;
88
+ this.clearKillTimer();
89
+ if (!this.destroyed)
90
+ this.rejectAll(new Error(`ZCode app-server exited: code=${code} signal=${signal}`));
91
+ });
92
+ }
93
+ async initialize() {
94
+ return {};
95
+ }
96
+ async newSession(cwd) {
97
+ const result = await this.request('session/create', {
98
+ workspace: { workspacePath: cwd, workspaceKey: cwd },
99
+ mode: this.autoApprove ? 'yolo' : 'build',
100
+ });
101
+ const sessionId = result?.session?.sessionId;
102
+ if (typeof sessionId !== 'string' || !sessionId)
103
+ throw new Error('ZCode did not return a session ID');
104
+ return this.captureSession(sessionId, result);
105
+ }
106
+ async loadSession(sessionId, cwd) {
107
+ const result = await this.request('session/resume', {
108
+ sessionId,
109
+ workspace: { workspacePath: cwd, workspaceKey: cwd },
110
+ });
111
+ return this.captureSession(sessionId, result);
112
+ }
113
+ async prompt(sessionId, blocks) {
114
+ if (this.prompts.has(sessionId))
115
+ throw new Error('ZCode session is already running a prompt');
116
+ const text = blocks.flatMap((block) => block.type === 'text' && 'text' in block ? [block.text] : []).join('\n');
117
+ const images = blocks.flatMap((block) => block.type === 'image'
118
+ && typeof block.data === 'string'
119
+ && typeof block.mimeType === 'string'
120
+ ? [{ data: block.data, mimeType: block.mimeType }]
121
+ : []);
122
+ const attachments = images.map((block, index) => ({
123
+ kind: 'image',
124
+ filename: `image-${index + 1}.${this.extensionForMime(block.mimeType)}`,
125
+ mimeType: block.mimeType,
126
+ dataBase64: block.data,
127
+ sizeBytes: Math.floor(block.data.length * 3 / 4),
128
+ }));
129
+ if (!text && attachments.length === 0)
130
+ throw new Error('Empty prompt');
131
+ const subscribe = () => this.request('session/subscribe', {
132
+ sessionId,
133
+ deliveryKind: 'desktop-continuous',
134
+ includeSnapshot: false,
135
+ afterSeq: this.sequence.get(sessionId) ?? 0,
136
+ });
137
+ try {
138
+ await subscribe();
139
+ }
140
+ catch (error) {
141
+ if (!/session is not active/i.test(error instanceof Error ? error.message : String(error)))
142
+ throw error;
143
+ await this.loadSession(sessionId, this.cwd);
144
+ await subscribe();
145
+ }
146
+ return new Promise((resolve, reject) => {
147
+ this.prompts.set(sessionId, { resolve, reject, tools: new Map() });
148
+ void this.request('session/send', {
149
+ sessionId,
150
+ content: text,
151
+ ...(attachments.length > 0 ? { attachments } : {}),
152
+ }, 15_000).then((result) => {
153
+ if (!result?.accepted)
154
+ this.failPrompt(sessionId, new Error('ZCode did not accept the prompt'));
155
+ }).catch((error) => this.failPrompt(sessionId, error));
156
+ });
157
+ }
158
+ async setConfigOption(sessionId, configId, value) {
159
+ const state = this.sessions.get(sessionId) ?? { models: [] };
160
+ if (configId === 'model') {
161
+ const model = this.findModel(state, value);
162
+ if (!model?.ref?.modelId || !model.ref.providerId)
163
+ throw new Error(`Unknown ZCode model: ${value}`);
164
+ const selected = { ...model.ref };
165
+ const defaultLevel = model.reasoning?.defaultLevel;
166
+ if (defaultLevel)
167
+ selected.options = { reasoningLevel: defaultLevel };
168
+ await this.request('session/setModel', {
169
+ sessionId,
170
+ model: selected,
171
+ persistAsWorkspaceLastUsed: false,
172
+ }, 15_000);
173
+ state.currentModel = { ...model.ref };
174
+ state.currentThought = defaultLevel ?? state.currentThought;
175
+ }
176
+ else if (configId === 'thought') {
177
+ await this.request('session/setThoughtLevel', { sessionId, thoughtLevel: value }, 15_000);
178
+ state.currentThought = value;
179
+ }
180
+ else {
181
+ throw new Error(`Unsupported ZCode config option: ${configId}`);
182
+ }
183
+ this.sessions.set(sessionId, state);
184
+ const configOptions = this.configOptions(state);
185
+ this.callbacks.onConfigOptions?.(configOptions);
186
+ return { sessionId, configOptions };
187
+ }
188
+ async compactSession(sessionId) {
189
+ await this.request('session/compact', { sessionId }, 30_000);
190
+ await this.waitForInternalTurn(sessionId, 5 * 60 * 1000);
191
+ return { stopReason: 'end_turn' };
192
+ }
193
+ async deleteSession(sessionId) {
194
+ // ZCode exposes session/close for releasing the resident runtime. The
195
+ // persisted conversation remains in ZCode's own history, while remote-cli
196
+ // removes its pointer in AcpExecutor.deleteThreadData().
197
+ this.notify('session/close', { sessionId });
198
+ this.sessions.delete(sessionId);
199
+ this.sequence.delete(sessionId);
200
+ }
201
+ sendCancel(sessionId) {
202
+ this.notify('session/stop', { sessionId });
203
+ const prompt = this.prompts.get(sessionId);
204
+ if (prompt) {
205
+ this.prompts.delete(sessionId);
206
+ prompt.resolve({ stopReason: 'cancelled' });
207
+ }
208
+ }
209
+ destroy() {
210
+ if (this.destroyed)
211
+ return;
212
+ this.destroyed = true;
213
+ this.rejectAll(new Error('ZCode client destroyed'));
214
+ this.reader.close();
215
+ try {
216
+ this.child.stdin?.end();
217
+ }
218
+ catch { }
219
+ if (!this.child.killed) {
220
+ if (process.platform !== 'win32' && this.child.pid) {
221
+ try {
222
+ process.kill(-this.child.pid, 'SIGTERM');
223
+ }
224
+ catch {
225
+ this.child.kill('SIGTERM');
226
+ }
227
+ }
228
+ else {
229
+ this.child.kill('SIGTERM');
230
+ }
231
+ this.killTimer = setTimeout(() => {
232
+ if (this.closed)
233
+ return;
234
+ if (process.platform !== 'win32' && this.child.pid) {
235
+ try {
236
+ process.kill(-this.child.pid, 'SIGKILL');
237
+ }
238
+ catch {
239
+ this.child.kill('SIGKILL');
240
+ }
241
+ }
242
+ else {
243
+ this.child.kill('SIGKILL');
244
+ }
245
+ }, KILL_GRACE_MS);
246
+ this.killTimer.unref?.();
247
+ }
248
+ }
249
+ request(method, params, timeoutMs = this.requestTimeoutMs) {
250
+ return new Promise((resolve, reject) => {
251
+ const id = this.nextId++;
252
+ const timer = setTimeout(() => {
253
+ this.pending.delete(id);
254
+ reject(new Error(`ZCode request timed out: ${method}`));
255
+ }, timeoutMs);
256
+ timer.unref?.();
257
+ this.pending.set(id, { resolve, reject, timer });
258
+ this.write({ id, method, params });
259
+ });
260
+ }
261
+ notify(method, params) {
262
+ this.write({ method, params });
263
+ }
264
+ respond(id, result) {
265
+ this.write({ id, result });
266
+ }
267
+ respondError(id, message) {
268
+ this.write({ id, error: { code: -32601, message } });
269
+ }
270
+ write(message) {
271
+ if (this.destroyed || !this.child.stdin)
272
+ return;
273
+ try {
274
+ this.child.stdin.write(`${JSON.stringify(message)}\n`);
275
+ }
276
+ catch { }
277
+ }
278
+ handleLine(line) {
279
+ let message;
280
+ try {
281
+ message = JSON.parse(line);
282
+ }
283
+ catch {
284
+ return;
285
+ }
286
+ const id = message.id;
287
+ const method = message.method;
288
+ if (id !== undefined && method === undefined) {
289
+ const pending = this.pending.get(Number(id));
290
+ if (!pending)
291
+ return;
292
+ this.pending.delete(Number(id));
293
+ clearTimeout(pending.timer);
294
+ if (message.error)
295
+ pending.reject(new Error(this.formatError(message.error)));
296
+ else
297
+ pending.resolve(message.result);
298
+ return;
299
+ }
300
+ if (id !== undefined && typeof method === 'string') {
301
+ void this.handleServerRequest(id, method, message.params ?? {});
302
+ return;
303
+ }
304
+ if (method === 'session/event')
305
+ this.handleSessionEvent(message.params ?? {});
306
+ else if (method === 'state.updated')
307
+ this.handleStateUpdate(message.params ?? {});
308
+ }
309
+ async handleServerRequest(id, method, params) {
310
+ if (method === 'session/requestRuntimePreferences') {
311
+ this.respond(id, {
312
+ nativeSearchEnhancementsEnabled: false,
313
+ memoryEnabled: false,
314
+ askUserQuestionAutoResolutionEnabled: false,
315
+ });
316
+ return;
317
+ }
318
+ if (method === 'interaction/requestProviderRuntimeHeaders') {
319
+ const apiKey = this.codingPlanApiKey(params.modelSelection?.providerId ?? params.providerId);
320
+ this.respond(id, apiKey
321
+ ? { headersApplied: true, requestAuth: { apiKey } }
322
+ : { headersApplied: false, errorMessage: 'The selected ZCode plan requires desktop-only runtime authentication.' });
323
+ return;
324
+ }
325
+ if (method === 'interaction/requestPermission') {
326
+ await this.handleInteraction(id, params, async () => {
327
+ const options = this.permissionOptions(params.options);
328
+ const selected = await this.askPermission(this.permissionTitle(params), options);
329
+ const option = selected >= 0 ? options[selected] : undefined;
330
+ return option?.kind.startsWith('allow')
331
+ ? { decision: 'allow' }
332
+ : { decision: 'deny', reason: selected < 0 ? 'cancelled by user' : 'rejected by user' };
333
+ });
334
+ return;
335
+ }
336
+ if (method === 'interaction/requestUserInput') {
337
+ await this.handleInteraction(id, params, () => this.handleUserInput(params));
338
+ return;
339
+ }
340
+ this.respondError(id, `Unsupported ZCode request: ${method}`);
341
+ }
342
+ handleSessionEvent(event) {
343
+ const sessionId = String(event.sessionId ?? '');
344
+ if (!sessionId)
345
+ return;
346
+ if (typeof event.seq === 'number')
347
+ this.sequence.set(sessionId, Math.max(event.seq, this.sequence.get(sessionId) ?? 0));
348
+ const prompt = this.prompts.get(sessionId);
349
+ if (!prompt)
350
+ return;
351
+ const payload = event.payload ?? {};
352
+ if (event.type === 'turn.started') {
353
+ prompt.turnId = typeof payload.turnId === 'string' ? payload.turnId : prompt.turnId;
354
+ return;
355
+ }
356
+ if (event.type === 'model.streaming') {
357
+ this.handleStreaming(prompt, payload);
358
+ return;
359
+ }
360
+ if (event.type === 'tool.updated') {
361
+ this.handleToolUpdate(prompt, payload);
362
+ return;
363
+ }
364
+ if (event.type === 'session.updated' && Array.isArray(payload.todos)) {
365
+ this.callbacks.onPlan?.(payload.todos);
366
+ return;
367
+ }
368
+ if (event.type === 'turn.completed' || event.type === 'turn.failed' || event.type === 'turn.terminal') {
369
+ if (prompt.turnId && payload.turnId && prompt.turnId !== payload.turnId)
370
+ return;
371
+ this.prompts.delete(sessionId);
372
+ if (event.type === 'turn.failed')
373
+ prompt.reject(new Error(this.formatError(payload.error ?? payload)));
374
+ else
375
+ prompt.resolve({ stopReason: payload.resultType === 'cancelled' ? 'cancelled' : 'end_turn' });
376
+ }
377
+ }
378
+ handleStreaming(prompt, payload) {
379
+ if (payload.kind === 'text_delta' && typeof payload.delta === 'string') {
380
+ this.callbacks.onTextChunk?.({ type: 'text', text: payload.delta });
381
+ }
382
+ else if (payload.kind === 'reasoning_delta' && typeof payload.delta === 'string') {
383
+ this.callbacks.onThoughtChunk?.({ type: 'text', text: payload.delta });
384
+ }
385
+ else if (payload.kind === 'tool_call') {
386
+ const id = String(payload.toolCallId ?? '');
387
+ if (!id)
388
+ return;
389
+ prompt.tools.set(id, {
390
+ toolCallId: id,
391
+ title: String(payload.toolName ?? 'Tool'),
392
+ kind: this.toolKind(payload.toolName),
393
+ rawInput: payload.input,
394
+ });
395
+ }
396
+ }
397
+ handleToolUpdate(prompt, payload) {
398
+ const id = String(payload.toolCallId ?? '');
399
+ if (!id)
400
+ return;
401
+ const previous = prompt.tools.get(id);
402
+ const tool = {
403
+ ...previous,
404
+ toolCallId: id,
405
+ title: String(payload.toolName ?? previous?.title ?? 'Tool'),
406
+ kind: this.toolKind(payload.toolName ?? previous?.title),
407
+ rawInput: payload.input ?? previous?.rawInput,
408
+ };
409
+ prompt.tools.set(id, tool);
410
+ if (payload.kind === 'scheduled' || payload.kind === 'started') {
411
+ this.callbacks.onToolCall?.({ ...tool, status: payload.kind === 'started' ? 'in_progress' : 'pending' });
412
+ }
413
+ else if (payload.kind === 'result' || payload.kind === 'error') {
414
+ this.callbacks.onToolResult?.({
415
+ ...tool,
416
+ status: payload.kind === 'error' ? 'failed' : 'completed',
417
+ rawOutput: payload.kind === 'error' ? payload.error : payload.result,
418
+ });
419
+ prompt.tools.delete(id);
420
+ }
421
+ }
422
+ handleStateUpdate(params) {
423
+ const sessionId = String(params.sessionId ?? '');
424
+ const state = this.sessions.get(sessionId);
425
+ if (!state)
426
+ return;
427
+ const patch = params.patch ?? {};
428
+ if (patch.model?.current)
429
+ state.currentModel = patch.model.current;
430
+ if (typeof patch.thoughtLevel?.current === 'string')
431
+ state.currentThought = patch.thoughtLevel.current;
432
+ const options = this.configOptions(state);
433
+ this.callbacks.onConfigOptions?.(options);
434
+ }
435
+ captureSession(sessionId, result) {
436
+ const settings = result.settings ?? result.session?.settings ?? {};
437
+ const state = {
438
+ currentModel: settings.model?.current,
439
+ currentThought: settings.thoughtLevel?.current,
440
+ models: Array.isArray(settings.model?.available) ? settings.model.available : [],
441
+ };
442
+ this.sessions.set(sessionId, state);
443
+ const configOptions = this.configOptions(state);
444
+ this.callbacks.onConfigOptions?.(configOptions);
445
+ return { sessionId, configOptions };
446
+ }
447
+ configOptions(state) {
448
+ const counts = new Map();
449
+ for (const model of state.models) {
450
+ const id = model.ref?.modelId;
451
+ if (id)
452
+ counts.set(id, (counts.get(id) ?? 0) + 1);
453
+ }
454
+ const valueFor = (model) => {
455
+ const provider = model.ref?.providerId ?? '';
456
+ const id = model.ref?.modelId ?? '';
457
+ return (counts.get(id) ?? 0) > 1 ? `${provider}/${id}` : id;
458
+ };
459
+ const current = state.models.find((model) => model.ref?.providerId === state.currentModel?.providerId
460
+ && model.ref?.modelId === state.currentModel?.modelId);
461
+ const levels = current?.reasoning?.levels?.map((level) => level.value).filter((value) => Boolean(value)) ?? [];
462
+ const options = [{
463
+ id: 'model',
464
+ name: 'Model',
465
+ type: 'select',
466
+ currentValue: current ? valueFor(current) : state.currentModel?.modelId,
467
+ options: state.models.flatMap((model) => model.ref?.modelId ? [{
468
+ value: valueFor(model),
469
+ name: model.ref.modelId,
470
+ description: model.ref.providerId,
471
+ }] : []),
472
+ }];
473
+ if (levels.length > 0) {
474
+ options.push({
475
+ id: 'thought',
476
+ name: 'Reasoning effort',
477
+ type: 'select',
478
+ currentValue: state.currentThought ?? current?.reasoning?.defaultLevel,
479
+ options: levels.map((value) => ({ value, name: value })),
480
+ });
481
+ }
482
+ return options;
483
+ }
484
+ findModel(state, value) {
485
+ const exact = state.models.filter((model) => model.ref?.modelId === value);
486
+ if (exact.length === 1)
487
+ return exact[0];
488
+ return state.models.find((model) => `${model.ref?.providerId}/${model.ref?.modelId}` === value
489
+ || `${model.ref?.providerId}\\${model.ref?.modelId}` === value);
490
+ }
491
+ permissionOptions(raw) {
492
+ const values = Array.isArray(raw) ? raw : [];
493
+ const options = values.map((entry) => ({
494
+ optionId: String(entry.optionId ?? entry.kind ?? ''),
495
+ name: String(entry.name ?? entry.optionId ?? entry.kind ?? ''),
496
+ kind: this.permissionKind(entry.kind),
497
+ }));
498
+ return options.length > 0 ? options : [
499
+ { optionId: 'allow_once', name: 'Allow once', kind: 'allow_once' },
500
+ { optionId: 'reject_once', name: 'Reject', kind: 'reject_once' },
501
+ ];
502
+ }
503
+ permissionKind(kind) {
504
+ if (kind === 'allow_once' || kind === 'allow_always' || kind === 'reject_once' || kind === 'reject_always')
505
+ return kind;
506
+ if (kind === 'allow' || kind === 'allow_project')
507
+ return 'allow_always';
508
+ return 'reject_once';
509
+ }
510
+ async askPermission(title, options) {
511
+ if (!this.callbacks.onPermissionRequest)
512
+ return this.autoApprove ? options.findIndex((option) => option.kind.startsWith('allow')) : -1;
513
+ return this.callbacks.onPermissionRequest(title, options);
514
+ }
515
+ async handleUserInput(params) {
516
+ if (params.schema?.interaction === 'plan_approval') {
517
+ const options = [
518
+ { optionId: 'q0_opt_0', name: 'Approve plan', kind: 'allow_once' },
519
+ { optionId: 'q0_skip', name: 'Reject plan', kind: 'reject_once' },
520
+ ];
521
+ const selected = await this.askPermission('Approve the proposed plan?', options);
522
+ return selected === 0 ? { action: 'accept', content: { answer_0: 'approve' } } : { action: 'decline', reason: 'plan rejected' };
523
+ }
524
+ const questions = params.questions ?? params.input?.questions ?? [];
525
+ const answers = {};
526
+ for (let index = 0; index < questions.length; index++) {
527
+ const question = questions[index];
528
+ const choices = Array.isArray(question.options) ? question.options : [];
529
+ if (question.multiSelect) {
530
+ const picked = [];
531
+ for (let choiceIndex = 0; choiceIndex < choices.length; choiceIndex++) {
532
+ const choice = choices[choiceIndex];
533
+ const label = String(choice.label ?? choice.value ?? `Option ${choiceIndex + 1}`);
534
+ const selected = await this.askPermission(`${String(question.question ?? params.prompt ?? 'ZCode question')}\nInclude "${label}"?`, [
535
+ { optionId: `q${index}_opt_${choiceIndex}_yes`, name: 'Include', kind: 'allow_once' },
536
+ { optionId: `q${index}_opt_${choiceIndex}_no`, name: 'Skip', kind: 'reject_once' },
537
+ ]);
538
+ if (selected < 0)
539
+ return { action: 'decline', reason: 'question cancelled' };
540
+ if (selected === 0)
541
+ picked.push(String(choice.value ?? choice.label ?? label));
542
+ }
543
+ answers[String(question.question ?? `question_${index}`)] = picked.join(', ');
544
+ continue;
545
+ }
546
+ const options = choices.map((choice, optionIndex) => ({
547
+ optionId: `q${index}_opt_${optionIndex}`,
548
+ name: String(choice.label ?? choice.value ?? `Option ${optionIndex + 1}`),
549
+ kind: 'allow_once',
550
+ }));
551
+ options.push({ optionId: `q${index}_skip`, name: 'Skip', kind: 'reject_once' });
552
+ const selected = await this.askPermission(String(question.question ?? params.prompt ?? 'ZCode question'), options);
553
+ if (selected < 0 || selected >= choices.length)
554
+ return { action: 'decline', reason: 'question skipped or cancelled' };
555
+ const choice = choices[selected];
556
+ answers[String(question.question ?? `question_${index}`)] = String(choice.value ?? choice.label ?? options[selected].name);
557
+ }
558
+ return { action: 'accept', content: { answers } };
559
+ }
560
+ async handleInteraction(id, params, resolve) {
561
+ const key = String(params.requestId ?? params.toolCallId ?? id);
562
+ const existing = this.interactions.get(key);
563
+ if (existing) {
564
+ if (existing.result)
565
+ this.respond(id, existing.result);
566
+ else
567
+ existing.ids.push(id);
568
+ return;
569
+ }
570
+ const interaction = { ids: [id] };
571
+ this.interactions.set(key, interaction);
572
+ let result;
573
+ try {
574
+ result = await resolve();
575
+ }
576
+ catch (error) {
577
+ result = { action: 'decline', reason: error instanceof Error ? error.message : 'interaction failed' };
578
+ }
579
+ interaction.result = result;
580
+ for (const requestId of interaction.ids)
581
+ this.respond(requestId, result);
582
+ const timer = setTimeout(() => this.interactions.delete(key), 5 * 60 * 1000);
583
+ timer.unref?.();
584
+ }
585
+ permissionTitle(params) {
586
+ const tool = String(params.toolName ?? 'Tool');
587
+ const input = params.input;
588
+ if (!input || typeof input !== 'object')
589
+ return tool;
590
+ const detail = input.command ?? input.file_path ?? input.path ?? input.url;
591
+ return detail ? `${tool}: ${String(detail).split('\n')[0].slice(0, 120)}` : tool;
592
+ }
593
+ codingPlanApiKey(providerId) {
594
+ if (typeof providerId !== 'string')
595
+ return null;
596
+ const match = /^account:([a-z0-9]+)-individual-coding-plan$/.exec(providerId);
597
+ if (!match)
598
+ return null;
599
+ try {
600
+ const configPath = path.join(process.env.ZCODE_HOME ?? path.join(os.homedir(), '.zcode'), 'v2', 'config.json');
601
+ const config = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
602
+ const provider = config.provider?.[`builtin:${match[1]}-coding-plan`];
603
+ const apiKey = provider?.options?.apiKey;
604
+ return typeof apiKey === 'string' && apiKey.trim() ? apiKey.trim() : null;
605
+ }
606
+ catch {
607
+ return null;
608
+ }
609
+ }
610
+ toolKind(name) {
611
+ const normalized = String(name ?? '').toLowerCase();
612
+ if (normalized.includes('read'))
613
+ return 'read';
614
+ if (normalized.includes('write') || normalized.includes('edit'))
615
+ return 'edit';
616
+ if (normalized.includes('glob') || normalized.includes('search') || normalized.includes('list'))
617
+ return 'search';
618
+ return 'execute';
619
+ }
620
+ extensionForMime(mime) {
621
+ return { 'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/webp': 'webp', 'image/bmp': 'bmp', 'image/svg+xml': 'svg' }[mime] ?? 'png';
622
+ }
623
+ async waitForInternalTurn(sessionId, timeoutMs) {
624
+ const startedAt = Date.now();
625
+ let lockSeen = false;
626
+ while (Date.now() - startedAt < timeoutMs) {
627
+ try {
628
+ await this.request('session/goal', { sessionId, action: 'show' }, 10_000);
629
+ if (lockSeen || Date.now() - startedAt >= 30_000)
630
+ return;
631
+ }
632
+ catch (error) {
633
+ const message = error instanceof Error ? error.message : String(error);
634
+ if (/prompt is running/i.test(message)) {
635
+ lockSeen = true;
636
+ }
637
+ else if (lockSeen) {
638
+ return;
639
+ }
640
+ else if (/backend reader exited|app-server exited/i.test(message)) {
641
+ throw error;
642
+ }
643
+ else if (Date.now() - startedAt >= 30_000) {
644
+ return;
645
+ }
646
+ }
647
+ await new Promise((resolve) => setTimeout(resolve, this.compactPollIntervalMs));
648
+ }
649
+ throw new Error('ZCode compaction did not finish within 5 minutes');
650
+ }
651
+ failPrompt(sessionId, error) {
652
+ const prompt = this.prompts.get(sessionId);
653
+ if (!prompt)
654
+ return;
655
+ this.prompts.delete(sessionId);
656
+ prompt.reject(error instanceof Error ? error : new Error(String(error)));
657
+ }
658
+ rejectAll(error) {
659
+ for (const request of this.pending.values()) {
660
+ clearTimeout(request.timer);
661
+ request.reject(error);
662
+ }
663
+ this.pending.clear();
664
+ for (const prompt of this.prompts.values())
665
+ prompt.reject(error);
666
+ this.prompts.clear();
667
+ }
668
+ formatError(error) {
669
+ if (typeof error === 'string')
670
+ return error;
671
+ const code = error?.code === undefined ? '' : ` ${error.code}`;
672
+ return `ZCode error${code}: ${error?.message ?? error?.type ?? JSON.stringify(error)}`;
673
+ }
674
+ clearKillTimer() {
675
+ if (this.killTimer)
676
+ clearTimeout(this.killTimer);
677
+ this.killTimer = null;
678
+ }
679
+ }
680
+ exports.ZCodeClient = ZCodeClient;
681
+ //# sourceMappingURL=ZCodeClient.js.map