maka-agent 0.2.0-dev.44.20260920 → 0.2.0-dev.45.20260921

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 (61) hide show
  1. package/dist/runtime-host-service-manager.js +1 -6
  2. package/dist/runtime-host-update-command.js +39 -27
  3. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  4. package/node_modules/@maka/core/dist/artifacts.js +1 -0
  5. package/node_modules/@maka/core/dist/model-metadata.js +1 -2
  6. package/node_modules/@maka/core/dist/provider-auth.js +1 -7
  7. package/node_modules/@maka/core/dist/provider-registry.js +13 -7
  8. package/node_modules/@maka/core/dist/recall.js +5 -1
  9. package/node_modules/@maka/core/dist/session-start-mode.js +2 -5
  10. package/node_modules/@maka/core/dist/session.js +0 -1
  11. package/node_modules/@maka/core/dist/settings.js +17 -0
  12. package/node_modules/@maka/core/dist/tool-quiet-preview.js +2 -16
  13. package/node_modules/@maka/core/dist/transcript-search.js +46 -0
  14. package/node_modules/@maka/core/package.json +1 -4
  15. package/node_modules/@maka/runtime/dist/model-factory.js +0 -16
  16. package/node_modules/@maka/runtime/dist/model-fetcher.js +1 -4
  17. package/node_modules/@maka/runtime/dist/model-runtime.js +0 -8
  18. package/node_modules/@maka/runtime/dist/recall-tools.js +4 -0
  19. package/node_modules/@maka/runtime/dist/session-manager.js +4 -13
  20. package/node_modules/@maka/runtime/dist/subagent-tools.js +69 -11
  21. package/node_modules/@maka/runtime/dist/test-connection.js +1 -48
  22. package/node_modules/@maka/runtime/package.json +0 -1
  23. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +25 -18
  24. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +3 -1
  25. package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +0 -108
  26. package/node_modules/@maka/runtime-host/dist/protocol/index.js +6 -1
  27. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +4 -5
  28. package/node_modules/@maka/runtime-host/dist/protocol/recall.js +270 -0
  29. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -7
  30. package/node_modules/@maka/runtime-host/dist/protocol/usage-screen.js +12 -4
  31. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
  32. package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +1 -57
  33. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +11 -33
  34. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +3 -20
  35. package/node_modules/@maka/runtime-host/dist/server/recall-coordinator.js +117 -0
  36. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +0 -4
  37. package/node_modules/@maka/storage/dist/artifact-metadata-codec.js +4 -3
  38. package/node_modules/@maka/storage/dist/artifact-store.js +1 -11
  39. package/node_modules/@maka/storage/dist/process-lifetime-file-update-lock.js +0 -41
  40. package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +0 -28
  41. package/node_modules/@maka/storage/dist/runtime-policy-stores.js +0 -2
  42. package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +29 -5
  43. package/node_modules/@maka/storage/dist/sqlite-workflow-schema.js +2 -0
  44. package/node_modules/@maka/storage/dist/storage-writer-composition.js +0 -3
  45. package/node_modules/@maka/storage/dist/telemetry-file-schema.js +5 -3
  46. package/node_modules/@maka/storage/dist/usage-screen.js +5 -6
  47. package/node_modules/@maka/storage/dist/usage-stores.js +14 -4
  48. package/node_modules/@maka/storage/package.json +0 -2
  49. package/package.json +1 -1
  50. package/node_modules/@maka/core/dist/connection-usage.js +0 -27
  51. package/node_modules/@maka/core/dist/deep-research-client-progress.js +0 -132
  52. package/node_modules/@maka/core/dist/deep-research-run.js +0 -625
  53. package/node_modules/@maka/core/dist/deep-research.js +0 -208
  54. package/node_modules/@maka/core/dist/thread-search.js +0 -498
  55. package/node_modules/@maka/runtime/dist/commandcode-cli-language-model.js +0 -837
  56. package/node_modules/@maka/runtime/dist/connection-usage.js +0 -232
  57. package/node_modules/@maka/runtime/dist/deep-research-tools.js +0 -732
  58. package/node_modules/@maka/runtime-host/dist/protocol/deep-research.js +0 -233
  59. package/node_modules/@maka/runtime-host/dist/server/deep-research-coordinator.js +0 -116
  60. package/node_modules/@maka/storage/dist/deep-research-authority.js +0 -129
  61. package/node_modules/@maka/storage/dist/deep-research-store.js +0 -343
@@ -1,837 +0,0 @@
1
- /*
2
- * Licensed to the Apache Software Foundation (ASF) under one
3
- * or more contributor license agreements. See the NOTICE file
4
- * distributed with this work for additional information
5
- * regarding copyright ownership. The ASF licenses this file
6
- * to you under the Apache License, Version 2.0 (the
7
- * "License"); you may not use this file except in compliance
8
- * with the License. You may obtain a copy of the License at
9
- *
10
- * http://www.apache.org/licenses/LICENSE-2.0
11
- *
12
- * Unless required by applicable law or agreed to in writing,
13
- * software distributed under the License is distributed on an
14
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
- * KIND, either express or implied. See the License for the
16
- * specific language governing permissions and limitations
17
- * under the License.
18
- */
19
- /**
20
- * Command Code CLI transport: the `/alpha/generate` wire, exposed as an AI SDK
21
- * language model. The Go plan's keys are answered by
22
- * `/provider/v1/chat/completions` with `403 upgrade_required`, so this is the
23
- * wire that serves them.
24
- *
25
- * Shape (see also the MIT-licensed `pi-commandcode-provider` and
26
- * `dsh-commandcode-provider`):
27
- * POST {apiBase}/alpha/generate
28
- * body { config, memory, taste, skills, params: { model, messages, tools,
29
- * system, max_tokens, temperature, stream, reasoning_effort? }, threadId }
30
- * events SSE `data:` lines carrying AI SDK stream parts: `text-delta`,
31
- * `reasoning-start|delta|end`, `tool-call`, `finish`, `error`.
32
- * errors 403 `upgrade_required` is a plan gate, not a bad key.
33
- */
34
- import { randomUUID } from 'node:crypto';
35
- import { APICallError, } from '@ai-sdk/provider';
36
- /** The official CLI release whose wire this mirrors; sent as its version header. */
37
- export const COMMANDCODE_CLI_VERSION = '1.54.0';
38
- const DEFAULT_MAX_TOKENS = 64_000;
39
- const DEFAULT_TEMPERATURE = 0.3;
40
- /** The gateway rejects `call_id` values longer than this. */
41
- const MAX_WIRE_TOOL_CALL_ID_LENGTH = 64;
42
- const SCHEMA_NORMALIZE_MAX_DEPTH = 8;
43
- export function commandCodeCliGenerateUrl(apiBase) {
44
- return `${apiBase.replace(/\/+$/u, '')}/alpha/generate`;
45
- }
46
- /** The identity headers the official CLI sends; the gateway keys plan gating on them. */
47
- export function commandCodeCliHeaders(apiKey, workingDir) {
48
- return {
49
- 'content-type': 'application/json',
50
- authorization: `Bearer ${apiKey}`,
51
- 'x-command-code-version': COMMANDCODE_CLI_VERSION,
52
- 'x-cli-environment': 'production',
53
- 'x-project-slug': projectSlugFromPath(workingDir),
54
- 'x-taste-learning': 'true',
55
- 'x-co-flag': 'false',
56
- };
57
- }
58
- export function projectSlugFromPath(pathName) {
59
- const slug = pathName
60
- .toLowerCase()
61
- .replace(/^[a-z]:/iu, '')
62
- .replace(/[^a-z0-9]+/gu, '-')
63
- .replace(/^-+|(?<!-)-+$/gu, '');
64
- return slug || 'project';
65
- }
66
- export class CommandCodeCliLanguageModel {
67
- specificationVersion = 'v4';
68
- provider = 'commandcode-cli';
69
- modelId;
70
- supportedUrls = {};
71
- #config;
72
- constructor(config) {
73
- this.#config = config;
74
- this.modelId = config.modelId;
75
- }
76
- async doGenerate(options) {
77
- const { stream, request, response } = await this.doStream(options);
78
- const content = [];
79
- const warnings = [];
80
- let finishReason = { unified: 'other', raw: undefined };
81
- let usage = emptyUsage();
82
- let openText;
83
- let openReasoning;
84
- const reader = stream.getReader();
85
- for (;;) {
86
- const { done, value: part } = await reader.read();
87
- if (done)
88
- break;
89
- switch (part.type) {
90
- case 'stream-start':
91
- warnings.push(...part.warnings);
92
- break;
93
- case 'text-start':
94
- openText = { id: part.id, text: '' };
95
- break;
96
- case 'text-delta':
97
- if (openText)
98
- openText.text += part.delta;
99
- break;
100
- case 'text-end':
101
- if (openText)
102
- content.push({ type: 'text', text: openText.text });
103
- openText = undefined;
104
- break;
105
- case 'reasoning-start':
106
- openReasoning = { id: part.id, text: '' };
107
- break;
108
- case 'reasoning-delta':
109
- if (openReasoning)
110
- openReasoning.text += part.delta;
111
- break;
112
- case 'reasoning-end':
113
- if (openReasoning)
114
- content.push({ type: 'reasoning', text: openReasoning.text });
115
- openReasoning = undefined;
116
- break;
117
- case 'tool-call':
118
- content.push(part);
119
- break;
120
- case 'finish':
121
- finishReason = part.finishReason;
122
- usage = part.usage;
123
- break;
124
- case 'error':
125
- throw part.error;
126
- default:
127
- break;
128
- }
129
- }
130
- return {
131
- content,
132
- finishReason,
133
- usage,
134
- warnings,
135
- request,
136
- response: { headers: response?.headers },
137
- };
138
- }
139
- async doStream(options) {
140
- const { body, warnings } = buildCommandCodeCliRequest(options, this.#config);
141
- const url = commandCodeCliGenerateUrl(this.#config.apiBase);
142
- const fetchFn = this.#config.fetch ?? globalThis.fetch;
143
- const headers = {
144
- ...commandCodeCliHeaders(this.#config.apiKey, this.#config.workingDir ?? DEFAULT_WORKING_DIR),
145
- ...definedHeaders(options.headers),
146
- };
147
- let response;
148
- try {
149
- response = await fetchFn(url, {
150
- method: 'POST',
151
- headers,
152
- body: JSON.stringify(body),
153
- signal: options.abortSignal,
154
- });
155
- }
156
- catch (error) {
157
- if (options.abortSignal?.aborted)
158
- throw error;
159
- throw new APICallError({
160
- message: `Command Code GO request failed: ${errorMessage(error)}`,
161
- url,
162
- requestBodyValues: body,
163
- cause: error,
164
- isRetryable: true,
165
- });
166
- }
167
- const responseHeaders = headersToRecord(response.headers);
168
- if (!response.ok) {
169
- const responseBody = await response.text().catch(() => '');
170
- throw new APICallError({
171
- message: `Command Code GO rejected the request (${response.status}): ${responseBody.slice(0, 500)}`,
172
- url,
173
- requestBodyValues: body,
174
- statusCode: response.status,
175
- responseHeaders,
176
- responseBody,
177
- isRetryable: response.status === 429 || response.status >= 500,
178
- });
179
- }
180
- if (!response.body) {
181
- throw new APICallError({
182
- message: 'Command Code GO returned no body',
183
- url,
184
- requestBodyValues: body,
185
- statusCode: response.status,
186
- responseHeaders,
187
- isRetryable: true,
188
- });
189
- }
190
- const stream = response.body
191
- .pipeThrough(new TextDecoderStream())
192
- .pipeThrough(sseDataLines())
193
- .pipeThrough(cliEventsToStreamParts({ warnings, url, body, modelId: this.modelId }));
194
- return { stream, request: { body }, response: { headers: responseHeaders } };
195
- }
196
- }
197
- const DEFAULT_WORKING_DIR = 'maka';
198
- // ---------------------------------------------------------------------------
199
- // Request
200
- // ---------------------------------------------------------------------------
201
- export function buildCommandCodeCliRequest(options, config) {
202
- const warnings = [];
203
- const unsupported = [
204
- ['topP', options.topP],
205
- ['topK', options.topK],
206
- ['presencePenalty', options.presencePenalty],
207
- ['frequencyPenalty', options.frequencyPenalty],
208
- ['stopSequences', options.stopSequences],
209
- ['seed', options.seed],
210
- ];
211
- for (const [setting, value] of unsupported) {
212
- if (value !== undefined)
213
- warnings.push({ type: 'unsupported', feature: setting });
214
- }
215
- if (options.responseFormat && options.responseFormat.type !== 'text') {
216
- warnings.push({ type: 'unsupported', feature: 'responseFormat' });
217
- }
218
- if (options.toolChoice && options.toolChoice.type !== 'auto') {
219
- warnings.push({
220
- type: 'unsupported',
221
- feature: 'toolChoice',
222
- details: 'Command Code GO always lets the model choose',
223
- });
224
- }
225
- const { system, messages } = convertPrompt(options.prompt, warnings);
226
- const tools = (options.tools ?? []).flatMap((tool) => {
227
- if (tool.type !== 'function') {
228
- warnings.push({ type: 'unsupported', feature: `provider tool ${tool.name}` });
229
- return [];
230
- }
231
- return [
232
- {
233
- type: 'function',
234
- name: tool.name,
235
- description: tool.description,
236
- input_schema: toolParametersSchema(tool.inputSchema),
237
- },
238
- ];
239
- });
240
- const reasoningEffort = resolveReasoningEffort(options);
241
- const now = config.now?.() ?? new Date();
242
- const body = {
243
- config: {
244
- workingDir: config.workingDir ?? DEFAULT_WORKING_DIR,
245
- date: now.toISOString().split('T')[0],
246
- environment: `${process.platform}-${process.arch}, Node.js ${process.version}`,
247
- structure: [],
248
- isGitRepo: false,
249
- currentBranch: '',
250
- mainBranch: '',
251
- gitStatus: '',
252
- recentCommits: [],
253
- },
254
- memory: null,
255
- taste: null,
256
- skills: null,
257
- params: {
258
- model: config.modelId,
259
- messages,
260
- tools,
261
- system,
262
- max_tokens: options.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
263
- temperature: options.temperature ?? DEFAULT_TEMPERATURE,
264
- stream: true,
265
- ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
266
- },
267
- threadId: config.threadId?.() ?? randomUUID(),
268
- };
269
- return { body, warnings };
270
- }
271
- function resolveReasoningEffort(options) {
272
- const override = options.providerOptions?.['commandcode-cli']?.reasoningEffort;
273
- if (typeof override === 'string' && override !== '')
274
- return override;
275
- const level = options.reasoning;
276
- if (level === undefined || level === 'provider-default' || level === 'none')
277
- return undefined;
278
- return level;
279
- }
280
- /**
281
- * The CLI wire's message shape, converted from the AI SDK prompt. System
282
- * messages fold into `params.system`; only tool calls with a paired result
283
- * are replayed; assistant reasoning IS replayed (the gateway rebuilds the
284
- * upstream request from these blocks and DeepSeek thinking mode rejects a
285
- * tool loop whose history lacks it).
286
- */
287
- function convertPrompt(prompt, warnings) {
288
- const systemParts = [];
289
- const callIds = new Set();
290
- const callNames = new Map();
291
- const resultIds = new Set();
292
- for (const message of prompt) {
293
- if (message.role === 'assistant') {
294
- for (const part of message.content) {
295
- if (part.type === 'tool-call') {
296
- callIds.add(part.toolCallId);
297
- callNames.set(part.toolCallId, part.toolName);
298
- }
299
- }
300
- }
301
- if (message.role === 'tool') {
302
- for (const part of message.content) {
303
- if (part.type === 'tool-result')
304
- resultIds.add(part.toolCallId);
305
- }
306
- }
307
- }
308
- const paired = new Set([...callIds].filter((id) => resultIds.has(id)));
309
- const wireIds = wireToolCallIds(paired);
310
- const messages = [];
311
- for (const message of prompt) {
312
- switch (message.role) {
313
- case 'system':
314
- systemParts.push(message.content);
315
- break;
316
- case 'user': {
317
- const parts = [];
318
- for (const part of message.content) {
319
- if (part.type === 'text') {
320
- parts.push({ type: 'text', text: part.text });
321
- continue;
322
- }
323
- const image = imagePart(part, warnings);
324
- if (image)
325
- parts.push(image);
326
- }
327
- if (parts.length > 0)
328
- messages.push({ role: 'user', content: parts });
329
- break;
330
- }
331
- case 'assistant': {
332
- const parts = [];
333
- for (const part of message.content) {
334
- if (part.type === 'text')
335
- parts.push({ type: 'text', text: part.text });
336
- else if (part.type === 'reasoning')
337
- parts.push({ type: 'reasoning', text: part.text });
338
- else if (part.type === 'tool-call' && paired.has(part.toolCallId)) {
339
- parts.push({
340
- type: 'tool-call',
341
- toolCallId: wireIds.get(part.toolCallId) ?? part.toolCallId,
342
- toolName: part.toolName,
343
- input: recordOrEmpty(part.input),
344
- });
345
- }
346
- }
347
- if (parts.length > 0)
348
- messages.push({ role: 'assistant', content: parts });
349
- break;
350
- }
351
- case 'tool': {
352
- for (const part of message.content) {
353
- if (part.type !== 'tool-result' || !paired.has(part.toolCallId))
354
- continue;
355
- const { text, isError } = toolResultText(part.output, warnings);
356
- messages.push({
357
- role: 'tool',
358
- content: [
359
- {
360
- type: 'tool-result',
361
- toolCallId: wireIds.get(part.toolCallId) ?? part.toolCallId,
362
- toolName: callNames.get(part.toolCallId) || part.toolName || 'unknown',
363
- output: isError
364
- ? { type: 'error-text', value: text }
365
- : { type: 'text', value: text },
366
- },
367
- ],
368
- });
369
- }
370
- break;
371
- }
372
- default:
373
- break;
374
- }
375
- }
376
- return { system: systemParts.join('\n\n'), messages };
377
- }
378
- function imagePart(part, warnings) {
379
- if (!part.mediaType.startsWith('image/')) {
380
- warnings.push({ type: 'unsupported', feature: `file input ${part.mediaType}` });
381
- return undefined;
382
- }
383
- if (part.data.type !== 'data') {
384
- warnings.push({
385
- type: 'unsupported',
386
- feature: `image ${part.data.type} input`,
387
- details: 'Command Code GO carries images inline only',
388
- });
389
- return undefined;
390
- }
391
- const encoded = typeof part.data.data === 'string'
392
- ? part.data.data
393
- : Buffer.from(part.data.data).toString('base64');
394
- // The pinned CLI serializes an image block in the AI SDK message shape it
395
- // builds its request from — a data URL under `image`, beside `mimeType` —
396
- // not Anthropic's `source` object. The rest of this wire is AI SDK shaped
397
- // too (`toolCallId`, `toolName`, `input`), and for an unpublished endpoint
398
- // the pinned CLI is the protocol authority.
399
- return {
400
- type: 'image',
401
- image: encoded.startsWith('data:') ? encoded : `data:${part.mediaType};base64,${encoded}`,
402
- mimeType: part.mediaType,
403
- };
404
- }
405
- function toolResultText(output, warnings) {
406
- switch (output.type) {
407
- case 'text':
408
- return { text: output.value, isError: false };
409
- case 'error-text':
410
- return { text: output.value, isError: true };
411
- case 'json':
412
- return { text: JSON.stringify(output.value), isError: false };
413
- case 'error-json':
414
- return { text: JSON.stringify(output.value), isError: true };
415
- case 'execution-denied':
416
- return { text: output.reason ?? 'Tool execution was denied.', isError: true };
417
- case 'content': {
418
- const texts = [];
419
- for (const item of output.value) {
420
- if (item.type === 'text')
421
- texts.push(item.text);
422
- else
423
- warnings.push({ type: 'unsupported', feature: `tool result ${item.type}` });
424
- }
425
- return { text: texts.join('\n'), isError: false };
426
- }
427
- default:
428
- return { text: '', isError: false };
429
- }
430
- }
431
- /** Overlong paired ids get a per-request `cc-<n>` alias; the pair resolves through one map. */
432
- export function wireToolCallIds(paired) {
433
- const wire = new Map();
434
- const taken = new Set();
435
- for (const id of paired) {
436
- if (id.length <= MAX_WIRE_TOOL_CALL_ID_LENGTH) {
437
- wire.set(id, id);
438
- taken.add(id);
439
- }
440
- }
441
- let seq = 1;
442
- for (const id of paired) {
443
- if (wire.has(id))
444
- continue;
445
- let alias = `cc-${seq++}`;
446
- while (taken.has(alias))
447
- alias = `cc-${seq++}`;
448
- wire.set(id, alias);
449
- taken.add(alias);
450
- }
451
- return wire;
452
- }
453
- /**
454
- * The gateway validates a tool schema's root `type` against the literal
455
- * string "object". Schemas generators emit as `["object","null"]`, a bare
456
- * `$ref`, or a combinator get the declared type they mean.
457
- */
458
- export function toolParametersSchema(parameters, depth = 0) {
459
- if (!isRecord(parameters))
460
- return { type: 'object', properties: {}, additionalProperties: true };
461
- if (parameters.type === 'object')
462
- return parameters;
463
- if (Array.isArray(parameters.type) && parameters.type.includes('object')) {
464
- return { ...parameters, type: 'object' };
465
- }
466
- if (parameters.type === undefined || parameters.type === null) {
467
- if (typeof parameters.$ref === 'string' && depth < SCHEMA_NORMALIZE_MAX_DEPTH) {
468
- const target = resolveLocalRef(parameters, parameters.$ref);
469
- if (target !== undefined) {
470
- const { $ref: _ref, ...rest } = parameters;
471
- return toolParametersSchema({ ...target, ...rest }, depth + 1);
472
- }
473
- }
474
- if (isRecord(parameters.properties) || parameters.additionalProperties !== undefined) {
475
- return { ...parameters, type: 'object' };
476
- }
477
- for (const key of ['allOf', 'anyOf', 'oneOf']) {
478
- const branches = parameters[key];
479
- if (Array.isArray(branches) && depth < SCHEMA_NORMALIZE_MAX_DEPTH) {
480
- const merged = branches.map((branch) => toolParametersSchema(branch, depth + 1));
481
- const properties = Object.assign({}, ...merged.map((b) => b.properties ?? {}));
482
- const required = [
483
- ...new Set(merged.flatMap((b) => (Array.isArray(b.required) ? b.required : []))),
484
- ];
485
- return {
486
- type: 'object',
487
- properties,
488
- ...(required.length > 0 ? { required } : {}),
489
- additionalProperties: true,
490
- };
491
- }
492
- }
493
- if (typeof parameters.$ref === 'string')
494
- return { ...parameters, type: 'object' };
495
- }
496
- return { type: 'object', properties: {}, additionalProperties: true };
497
- }
498
- function resolveLocalRef(root, ref) {
499
- if (!ref.startsWith('#/'))
500
- return undefined;
501
- let current = root;
502
- for (const segment of ref.slice(2).split('/')) {
503
- if (!isRecord(current))
504
- return undefined;
505
- current = current[segment.replace(/~1/gu, '/').replace(/~0/gu, '~')];
506
- }
507
- return isRecord(current) ? current : undefined;
508
- }
509
- // ---------------------------------------------------------------------------
510
- // Response
511
- // ---------------------------------------------------------------------------
512
- /** Splits SSE text into the JSON payload of each `data:` line, dropping comments and `[DONE]`. */
513
- /** One SSE line's event payload, or undefined when the line carries none. */
514
- export function parseCommandCodeCliEventLine(line) {
515
- let trimmed = line.trim();
516
- if (!trimmed || trimmed.startsWith(':') || trimmed.startsWith('event:'))
517
- return undefined;
518
- if (trimmed.startsWith('data:'))
519
- trimmed = trimmed.slice(5).trim();
520
- if (!trimmed || trimmed === '[DONE]')
521
- return undefined;
522
- try {
523
- return JSON.parse(trimmed);
524
- }
525
- catch {
526
- // A non-JSON line is not an event on this wire.
527
- return undefined;
528
- }
529
- }
530
- /**
531
- * Reads one complete CLI stream body the way {@link CommandCodeCliLanguageModel}
532
- * reads it incrementally. HTTP 200 is only the handshake on this wire, so a
533
- * caller that stops at the status (the connection probe) would accept a body
534
- * whose first event is a rejection.
535
- */
536
- export function summarizeCommandCodeCliStream(body) {
537
- for (const line of body.split('\n')) {
538
- const event = parseCommandCodeCliEventLine(line);
539
- if (!isRecord(event))
540
- continue;
541
- if (event.type === 'error') {
542
- const { message, statusCode } = streamErrorFacts(event);
543
- return {
544
- finished: false,
545
- error: { message, ...(statusCode === undefined ? {} : { statusCode }) },
546
- };
547
- }
548
- if (event.type === 'finish')
549
- return { finished: true };
550
- }
551
- return { finished: false };
552
- }
553
- function sseDataLines() {
554
- let buffer = '';
555
- const emit = (line, controller) => {
556
- const event = parseCommandCodeCliEventLine(line);
557
- if (event !== undefined)
558
- controller.enqueue(event);
559
- };
560
- return new TransformStream({
561
- transform(chunk, controller) {
562
- buffer += chunk;
563
- const lines = buffer.split('\n');
564
- buffer = lines.pop() ?? '';
565
- for (const line of lines)
566
- emit(line, controller);
567
- },
568
- flush(controller) {
569
- if (buffer)
570
- emit(buffer, controller);
571
- },
572
- });
573
- }
574
- function cliEventsToStreamParts(input) {
575
- let textId;
576
- let reasoningId;
577
- let nextId = 0;
578
- let finished = false;
579
- // The wire splits one call's usage across two events: `finish-step` carries
580
- // the provider's OWN body (`prompt_tokens`, `prompt_cache_hit_tokens`, …)
581
- // under `usage.raw`, and `finish` carries only the normalized totals. The
582
- // provider keys are what telemetry's cache accounting reads, so keep the
583
- // body here until the finish event can carry it out.
584
- let providerUsageRaw;
585
- const closeText = (controller) => {
586
- if (textId === undefined)
587
- return;
588
- controller.enqueue({ type: 'text-end', id: textId });
589
- textId = undefined;
590
- };
591
- const closeReasoning = (controller) => {
592
- if (reasoningId === undefined)
593
- return;
594
- controller.enqueue({ type: 'reasoning-end', id: reasoningId });
595
- reasoningId = undefined;
596
- };
597
- return new TransformStream({
598
- start(controller) {
599
- controller.enqueue({ type: 'stream-start', warnings: input.warnings });
600
- controller.enqueue({ type: 'response-metadata', modelId: input.modelId });
601
- },
602
- transform(event, controller) {
603
- if (!isRecord(event) || finished)
604
- return;
605
- switch (event.type) {
606
- case 'text-delta': {
607
- closeReasoning(controller);
608
- if (textId === undefined) {
609
- textId = `text-${nextId++}`;
610
- controller.enqueue({ type: 'text-start', id: textId });
611
- }
612
- controller.enqueue({
613
- type: 'text-delta',
614
- id: textId,
615
- delta: stringValue(event.text) ?? '',
616
- });
617
- break;
618
- }
619
- case 'text-end':
620
- closeText(controller);
621
- break;
622
- case 'reasoning-start':
623
- closeText(controller);
624
- break;
625
- case 'reasoning-delta': {
626
- closeText(controller);
627
- if (reasoningId === undefined) {
628
- reasoningId = `reasoning-${nextId++}`;
629
- controller.enqueue({ type: 'reasoning-start', id: reasoningId });
630
- }
631
- controller.enqueue({
632
- type: 'reasoning-delta',
633
- id: reasoningId,
634
- delta: stringValue(event.text) ?? '',
635
- });
636
- break;
637
- }
638
- case 'reasoning-end':
639
- closeReasoning(controller);
640
- break;
641
- case 'tool-call': {
642
- closeText(controller);
643
- closeReasoning(controller);
644
- const id = stringValue(event.toolCallId) ?? randomUUID();
645
- const toolName = stringValue(event.toolName) ?? '';
646
- const args = JSON.stringify(recordOrEmpty(event.input ?? event.args ?? event.arguments));
647
- controller.enqueue({ type: 'tool-input-start', id, toolName });
648
- controller.enqueue({ type: 'tool-input-delta', id, delta: args });
649
- controller.enqueue({ type: 'tool-input-end', id });
650
- controller.enqueue({ type: 'tool-call', toolCallId: id, toolName, input: args });
651
- break;
652
- }
653
- case 'finish-step': {
654
- // Carries the provider's own usage body; `finish` normalizes it away.
655
- const usage = isRecord(event.usage) ? event.usage : undefined;
656
- if (isRecord(usage?.raw))
657
- providerUsageRaw = usage.raw;
658
- break;
659
- }
660
- case 'finish': {
661
- closeText(controller);
662
- closeReasoning(controller);
663
- finished = true;
664
- controller.enqueue({
665
- type: 'finish',
666
- usage: usageFromEvent(event.totalUsage, providerUsageRaw),
667
- finishReason: mapFinishReason(event.finishReason),
668
- });
669
- break;
670
- }
671
- case 'error': {
672
- closeText(controller);
673
- closeReasoning(controller);
674
- finished = true;
675
- controller.enqueue({ type: 'error', error: streamErrorToApiCallError(event, input) });
676
- break;
677
- }
678
- default:
679
- break;
680
- }
681
- },
682
- flush(controller) {
683
- closeText(controller);
684
- closeReasoning(controller);
685
- if (!finished) {
686
- // The connection closed before the wire said it was done: report the
687
- // truncation instead of a clean stop so the caller can retry.
688
- controller.enqueue({
689
- type: 'error',
690
- error: new APICallError({
691
- message: 'Command Code GO stream ended without a finish event',
692
- url: input.url,
693
- requestBodyValues: input.body,
694
- isRetryable: true,
695
- }),
696
- });
697
- }
698
- },
699
- });
700
- }
701
- function usageFromEvent(value, providerRaw) {
702
- if (!isRecord(value))
703
- return emptyUsage();
704
- const inputDetails = isRecord(value.inputTokenDetails) ? value.inputTokenDetails : undefined;
705
- const outputDetails = isRecord(value.outputTokenDetails) ? value.outputTokenDetails : undefined;
706
- const total = numberValue(value.inputTokens);
707
- const cacheRead = numberValue(inputDetails?.cacheReadTokens);
708
- const cacheWrite = numberValue(inputDetails?.cacheWriteTokens);
709
- const noCache = numberValue(inputDetails?.noCacheTokens) ??
710
- (total === undefined ? undefined : Math.max(0, total - (cacheRead ?? 0) - (cacheWrite ?? 0)));
711
- return {
712
- inputTokens: { total, noCache, cacheRead, cacheWrite },
713
- outputTokens: {
714
- total: numberValue(value.outputTokens),
715
- text: undefined,
716
- reasoning: numberValue(outputDetails?.reasoningTokens),
717
- },
718
- // `raw` must stay the PROVIDER's own usage body, which is where the
719
- // OpenAI/Anthropic cache keys live (`prompt_tokens`,
720
- // `prompt_tokens_details.cached_tokens`, `cache_read_input_tokens`, …).
721
- // Only `finish-step` carries that body; `finish.totalUsage` is already
722
- // normalized, so carrying the envelope here left telemetry's strict reader
723
- // looking for `prompt_tokens` on an object that had only `inputTokens` —
724
- // every attempt settled as `usageBasis: 'missing'` and the composer's
725
- // context gauge never moved.
726
- //
727
- // The fallback keeps a body-less event readable on the normalized keys
728
- // rather than handing telemetry `undefined`.
729
- raw: (providerRaw ?? value),
730
- };
731
- }
732
- function emptyUsage() {
733
- return {
734
- inputTokens: {
735
- total: undefined,
736
- noCache: undefined,
737
- cacheRead: undefined,
738
- cacheWrite: undefined,
739
- },
740
- outputTokens: { total: undefined, text: undefined, reasoning: undefined },
741
- };
742
- }
743
- export function mapFinishReason(reason) {
744
- const raw = stringValue(reason);
745
- if (raw === 'tool-calls' || raw === 'tool_calls')
746
- return { unified: 'tool-calls', raw };
747
- if (raw === 'length' ||
748
- raw === 'max_tokens' ||
749
- raw === 'max-tokens' ||
750
- raw === 'max_output_tokens') {
751
- return { unified: 'length', raw };
752
- }
753
- if (raw === 'content-filter' || raw === 'content_filter')
754
- return { unified: 'content-filter', raw };
755
- if (raw === 'error')
756
- return { unified: 'error', raw };
757
- return { unified: 'stop', raw };
758
- }
759
- /**
760
- * An in-band `error` event becomes the same error shape a rejected HTTP
761
- * response produces, so the runtime's provider-error classification reads
762
- * status and structured code from one place.
763
- */
764
- /** What one in-band `error` event states, shared by the stream and the probe. */
765
- function streamErrorFacts(event) {
766
- const detail = isRecord(event.error) ? event.error : undefined;
767
- const message = stringValue(detail?.message) ??
768
- stringValue(event.message) ??
769
- (detail ? JSON.stringify(detail) : stringValue(event.error)) ??
770
- 'Stream error';
771
- const statusCode = numberValue(detail?.statusCode) ?? numberValue(detail?.status);
772
- return {
773
- message,
774
- ...(statusCode !== undefined ? { statusCode } : {}),
775
- ...(detail !== undefined ? { detail } : {}),
776
- ...(typeof detail?.isRetryable === 'boolean' ? { explicitRetryable: detail.isRetryable } : {}),
777
- };
778
- }
779
- function streamErrorToApiCallError(event, input) {
780
- const { message, statusCode, detail, explicitRetryable } = streamErrorFacts(event);
781
- const isRetryable = explicitRetryable ??
782
- (statusCode !== undefined ? statusCode === 429 || statusCode >= 500 : false);
783
- return new APICallError({
784
- message: `Command Code GO stream error: ${message}`,
785
- url: input.url,
786
- requestBodyValues: input.body,
787
- ...(statusCode !== undefined ? { statusCode } : {}),
788
- responseBody: JSON.stringify({ error: detail ?? { message } }),
789
- isRetryable,
790
- data: detail,
791
- });
792
- }
793
- // ---------------------------------------------------------------------------
794
- // Small helpers
795
- // ---------------------------------------------------------------------------
796
- function isRecord(value) {
797
- return typeof value === 'object' && value !== null && !Array.isArray(value);
798
- }
799
- function stringValue(value) {
800
- return typeof value === 'string' ? value : undefined;
801
- }
802
- function numberValue(value) {
803
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
804
- }
805
- function recordOrEmpty(value) {
806
- if (isRecord(value))
807
- return value;
808
- if (typeof value === 'string') {
809
- try {
810
- const parsed = JSON.parse(value);
811
- if (isRecord(parsed))
812
- return parsed;
813
- }
814
- catch {
815
- // Not JSON: the model produced no usable arguments.
816
- }
817
- }
818
- return {};
819
- }
820
- function definedHeaders(headers) {
821
- const out = {};
822
- for (const [key, value] of Object.entries(headers ?? {})) {
823
- if (value !== undefined)
824
- out[key] = value;
825
- }
826
- return out;
827
- }
828
- function headersToRecord(headers) {
829
- const out = {};
830
- headers.forEach((value, key) => {
831
- out[key] = value;
832
- });
833
- return out;
834
- }
835
- function errorMessage(error) {
836
- return error instanceof Error ? error.message : String(error);
837
- }