@myagentroam/agent 0.9.140 → 0.9.141
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.
- package/dist/model/anthropic-messages.js +24 -7
- package/dist/model/contracts.d.ts +4 -0
- package/dist/model/openai-responses.js +28 -10
- package/dist/model/sse.d.ts +2 -0
- package/dist/model/sse.js +11 -1
- package/dist/sdk/agent.js +32 -8
- package/dist/tools/code-mode/quickjs-engine.js +12 -2
- package/dist/tools/code-mode.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { MarAgentError } from '../error.js';
|
|
3
3
|
import { resolveModelDefaultReasoningEffort } from './configuration.js';
|
|
4
|
-
import { isRetryableUpstreamOverloadCode, number, parseSse, record, text } from './sse.js';
|
|
4
|
+
import { isRetryableUpstreamOverloadCode, number, parseSse, record, text, observeModelStreamEvent, modelStreamEventObservedAt } from './sse.js';
|
|
5
5
|
import { fetchModelResponse, modelRetryMaxAttempts, isRetryableModelRequestError, isRetryableModelTransportError, waitForModelRetry } from './http.js';
|
|
6
6
|
import { anthropicThinkingBlock, assertAnthropicThinkingBudget } from './anthropic-thinking.js';
|
|
7
7
|
export class AnthropicMessagesAdapter {
|
|
@@ -12,13 +12,22 @@ export class AnthropicMessagesAdapter {
|
|
|
12
12
|
async *start(request, signal) {
|
|
13
13
|
const retryState = request.retryState ?? { attempts: 0 };
|
|
14
14
|
const maxAttempts = modelRetryMaxAttempts(retryState);
|
|
15
|
+
let resetOutputTiming = false;
|
|
15
16
|
while (retryState.attempts < maxAttempts) {
|
|
17
|
+
if (resetOutputTiming)
|
|
18
|
+
yield { type: 'output.reset' };
|
|
19
|
+
resetOutputTiming = false;
|
|
16
20
|
const attempt = retryState.attempts++;
|
|
17
21
|
// Buffer non-semantic events until a semantic event makes this attempt non-retryable.
|
|
18
22
|
const prelude = [];
|
|
19
23
|
let sawSemanticEvent = false;
|
|
20
24
|
try {
|
|
21
25
|
for await (const event of this.startAttempt(request, signal)) {
|
|
26
|
+
if (event.type === 'output.delta') {
|
|
27
|
+
resetOutputTiming = true;
|
|
28
|
+
yield event;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
22
31
|
if (!sawSemanticEvent && !isSemanticEvent(event)) {
|
|
23
32
|
prelude.push(event);
|
|
24
33
|
continue;
|
|
@@ -193,17 +202,25 @@ export class AnthropicMessagesAdapter {
|
|
|
193
202
|
if (deltaType === 'text_delta') {
|
|
194
203
|
const value = text(delta.text) ?? '';
|
|
195
204
|
block.text += value;
|
|
196
|
-
yield { type: 'text.delta', itemId: block.id, phase: 'commentary', delta: value };
|
|
205
|
+
yield observeModelStreamEvent({ type: 'text.delta', itemId: block.id, phase: 'commentary', delta: value }, modelStreamEventObservedAt(raw));
|
|
206
|
+
}
|
|
207
|
+
else if (deltaType === 'thinking_delta') {
|
|
208
|
+
const value = text(delta.thinking) ?? '';
|
|
209
|
+
block.text += value;
|
|
210
|
+
if (value.length > 0)
|
|
211
|
+
yield observeModelStreamEvent({ type: 'output.delta' }, modelStreamEventObservedAt(raw));
|
|
197
212
|
}
|
|
198
|
-
else if (deltaType === 'thinking_delta')
|
|
199
|
-
block.text += text(delta.thinking) ?? '';
|
|
200
213
|
else if (deltaType === 'signature_delta') {
|
|
201
214
|
if (block.type !== 'thinking' || typeof delta.signature !== 'string')
|
|
202
215
|
throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Anthropic returned an invalid thinking signature delta.');
|
|
203
216
|
block.signature += delta.signature;
|
|
204
217
|
}
|
|
205
|
-
else if (deltaType === 'input_json_delta')
|
|
206
|
-
|
|
218
|
+
else if (deltaType === 'input_json_delta') {
|
|
219
|
+
const value = text(delta.partial_json) ?? '';
|
|
220
|
+
block.json += value;
|
|
221
|
+
if (value.length > 0)
|
|
222
|
+
yield observeModelStreamEvent({ type: 'output.delta' }, modelStreamEventObservedAt(raw));
|
|
223
|
+
}
|
|
207
224
|
else if (deltaType === 'citations_delta') {
|
|
208
225
|
const citation = record(delta.citation);
|
|
209
226
|
const url = text(citation.url);
|
|
@@ -307,7 +324,7 @@ export class AnthropicMessagesAdapter {
|
|
|
307
324
|
};
|
|
308
325
|
}
|
|
309
326
|
else if (type === 'message_stop') {
|
|
310
|
-
yield { type: 'completed', reason: terminal ?? 'completed' };
|
|
327
|
+
yield observeModelStreamEvent({ type: 'completed', reason: terminal ?? 'completed' }, modelStreamEventObservedAt(raw));
|
|
311
328
|
}
|
|
312
329
|
else if (type === 'error') {
|
|
313
330
|
const upstream = record(event.error);
|
|
@@ -95,6 +95,10 @@ export type ModelStopReason = 'completed' | 'tool_use' | 'pause' | 'max_output'
|
|
|
95
95
|
export type ModelEvent = {
|
|
96
96
|
type: 'response.started';
|
|
97
97
|
responseId: string;
|
|
98
|
+
} | {
|
|
99
|
+
type: 'output.delta';
|
|
100
|
+
} | {
|
|
101
|
+
type: 'output.reset';
|
|
98
102
|
} | {
|
|
99
103
|
type: 'response.item';
|
|
100
104
|
provider: 'OPENAI_RESPONSES' | 'ANTHROPIC_MESSAGES';
|
|
@@ -3,7 +3,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { describeMarAgentError, MarAgentError } from '../error.js';
|
|
4
4
|
import { marAgentReasoningEffortSchema, resolveModelDefaultReasoningEffort, usesResponsesConfigurationUpdates } from './configuration.js';
|
|
5
5
|
import WebSocket from 'ws';
|
|
6
|
-
import { isRetryableUpstreamOverloadCode, number, parseSse, record, text } from './sse.js';
|
|
6
|
+
import { isRetryableUpstreamOverloadCode, number, parseSse, record, text, observeModelStreamEvent, modelStreamEventObservedAt } from './sse.js';
|
|
7
7
|
import { connectSocks5Target, fetchModelResponse, isRetryableModelHttpStatus, isRetryableModelTransportError, modelRetryMaxAttempts, isRetryableModelRequestError, modelRetryDelay, waitForModelRetry } from './http.js';
|
|
8
8
|
import { acquireModelCredential, appendModelCredentialHeaders } from './runtime-credential.js';
|
|
9
9
|
const WEBSOCKET_IDLE_TIMEOUT_MS = 300_000;
|
|
@@ -77,7 +77,11 @@ export class OpenAiResponsesAdapter {
|
|
|
77
77
|
let invalidContinuationFallbackUsed = false;
|
|
78
78
|
const retryState = request.retryState ?? { attempts: 0 };
|
|
79
79
|
const maxAttempts = modelRetryMaxAttempts(retryState);
|
|
80
|
+
let resetOutputTiming = false;
|
|
80
81
|
while (retryState.attempts < maxAttempts) {
|
|
82
|
+
if (resetOutputTiming)
|
|
83
|
+
yield { type: 'output.reset' };
|
|
84
|
+
resetOutputTiming = false;
|
|
81
85
|
const attempt = retryState.attempts++;
|
|
82
86
|
const attemptNumber = attempt + 1;
|
|
83
87
|
const attemptStartedAt = Date.now();
|
|
@@ -99,6 +103,11 @@ export class OpenAiResponsesAdapter {
|
|
|
99
103
|
});
|
|
100
104
|
try {
|
|
101
105
|
for await (const event of this.startAttempt(request, signal, requestBody, useContinuation, credentialReason, turn)) {
|
|
106
|
+
if (event.type === 'output.delta') {
|
|
107
|
+
resetOutputTiming = true;
|
|
108
|
+
yield event;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
102
111
|
if (!sawSemanticEvent && !isSemanticEvent(event)) {
|
|
103
112
|
prelude.push(event);
|
|
104
113
|
continue;
|
|
@@ -341,8 +350,16 @@ export class OpenAiResponsesAdapter {
|
|
|
341
350
|
else if (type === 'response.custom_tool_call_input.delta') {
|
|
342
351
|
const itemId = text(event.item_id) ?? '';
|
|
343
352
|
const known = customCalls.get(itemId);
|
|
353
|
+
const delta = text(event.delta) ?? '';
|
|
344
354
|
if (known)
|
|
345
|
-
known.input +=
|
|
355
|
+
known.input += delta;
|
|
356
|
+
if (delta.length > 0)
|
|
357
|
+
yield observeModelStreamEvent({ type: 'output.delta' }, modelStreamEventObservedAt(raw));
|
|
358
|
+
}
|
|
359
|
+
else if (type === 'response.function_call_arguments.delta' ||
|
|
360
|
+
type === 'response.reasoning_summary_text.delta') {
|
|
361
|
+
if ((text(event.delta)?.length ?? 0) > 0)
|
|
362
|
+
yield observeModelStreamEvent({ type: 'output.delta' }, modelStreamEventObservedAt(raw));
|
|
346
363
|
}
|
|
347
364
|
else if (type === 'response.created')
|
|
348
365
|
yield {
|
|
@@ -350,14 +367,14 @@ export class OpenAiResponsesAdapter {
|
|
|
350
367
|
responseId: responseEventId(event) ?? 'unknown'
|
|
351
368
|
};
|
|
352
369
|
else if (type === 'response.output_text.delta')
|
|
353
|
-
yield {
|
|
370
|
+
yield observeModelStreamEvent({
|
|
354
371
|
type: 'text.delta',
|
|
355
372
|
itemId: text(event.item_id) ?? 'text',
|
|
356
373
|
phase: messagePhase(event.phase) ??
|
|
357
374
|
textPhases.get(text(event.item_id) ?? '') ??
|
|
358
375
|
'commentary',
|
|
359
376
|
delta: text(event.delta) ?? ''
|
|
360
|
-
};
|
|
377
|
+
}, modelStreamEventObservedAt(raw));
|
|
361
378
|
else if (type === 'response.output_text.done') {
|
|
362
379
|
const itemId = text(event.item_id) ?? 'text';
|
|
363
380
|
const completedText = text(event.text) ?? '';
|
|
@@ -519,10 +536,10 @@ export class OpenAiResponsesAdapter {
|
|
|
519
536
|
...(rolloutBudgetUnits === undefined ? {} : { rolloutBudgetUnits })
|
|
520
537
|
}
|
|
521
538
|
};
|
|
522
|
-
yield {
|
|
539
|
+
yield observeModelStreamEvent({
|
|
523
540
|
type: 'completed',
|
|
524
541
|
reason: type === 'response.incomplete' ? 'max_output' : 'completed'
|
|
525
|
-
};
|
|
542
|
+
}, modelStreamEventObservedAt(raw));
|
|
526
543
|
completed = true;
|
|
527
544
|
}
|
|
528
545
|
else if (type === 'error' || type === 'response.failed') {
|
|
@@ -634,6 +651,7 @@ class OpenAiResponsesTurnSession {
|
|
|
634
651
|
if (event.type === 'completed')
|
|
635
652
|
warmCompleted = event.reason === 'completed';
|
|
636
653
|
if (event.type === 'client_tool.call' ||
|
|
654
|
+
event.type === 'output.delta' ||
|
|
637
655
|
event.type === 'text.delta' ||
|
|
638
656
|
event.type === 'text.completed' ||
|
|
639
657
|
event.type === 'response.item' ||
|
|
@@ -660,6 +678,7 @@ class OpenAiResponsesTurnSession {
|
|
|
660
678
|
else if (event.type === 'response.item')
|
|
661
679
|
outputItems.push(event.item);
|
|
662
680
|
else if (event.type === 'text.delta' ||
|
|
681
|
+
event.type === 'output.delta' ||
|
|
663
682
|
event.type === 'text.completed' ||
|
|
664
683
|
event.type === 'reasoning.summary' ||
|
|
665
684
|
event.type === 'client_tool.call' ||
|
|
@@ -1179,7 +1198,8 @@ async function* webSocketMessages(socket, signal) {
|
|
|
1179
1198
|
};
|
|
1180
1199
|
const message = (value) => push({
|
|
1181
1200
|
type: 'message',
|
|
1182
|
-
value
|
|
1201
|
+
value,
|
|
1202
|
+
observedAt: performance.now()
|
|
1183
1203
|
});
|
|
1184
1204
|
const close = () => push({ type: 'closed' });
|
|
1185
1205
|
const error = (cause) => push({
|
|
@@ -1228,7 +1248,7 @@ async function* webSocketMessages(socket, signal) {
|
|
|
1228
1248
|
const event = JSON.parse(text);
|
|
1229
1249
|
if (!isWebSocketNonProgressEvent(event))
|
|
1230
1250
|
idleDeadline = Date.now() + WEBSOCKET_IDLE_TIMEOUT_MS;
|
|
1231
|
-
yield event;
|
|
1251
|
+
yield observeModelStreamEvent(event, item.observedAt);
|
|
1232
1252
|
}
|
|
1233
1253
|
catch (cause) {
|
|
1234
1254
|
throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model WebSocket contained invalid JSON.', { cause });
|
|
@@ -1336,13 +1356,11 @@ const NON_SEMANTIC_OPENAI_RESPONSES_EVENTS = new Set([
|
|
|
1336
1356
|
'codex.response.metadata',
|
|
1337
1357
|
'response.content_part.added',
|
|
1338
1358
|
'response.content_part.done',
|
|
1339
|
-
'response.function_call_arguments.delta',
|
|
1340
1359
|
'response.in_progress',
|
|
1341
1360
|
'response.metadata',
|
|
1342
1361
|
'response.queued',
|
|
1343
1362
|
'response.reasoning_summary_part.added',
|
|
1344
1363
|
'response.reasoning_summary_part.done',
|
|
1345
|
-
'response.reasoning_summary_text.delta',
|
|
1346
1364
|
'responsesapi.websocket_timing'
|
|
1347
1365
|
]);
|
|
1348
1366
|
function isRetryableResponsesUpstreamError(code, message) {
|
package/dist/model/sse.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export declare function observeModelStreamEvent<const T>(value: T, observedAt: number | undefined): T;
|
|
2
|
+
export declare function modelStreamEventObservedAt(value: unknown): number | undefined;
|
|
1
3
|
export declare function parseSse(response: Response, signal: AbortSignal, maxBytes?: number, idleTimeoutMs?: number): AsyncIterable<unknown>;
|
|
2
4
|
export declare function isRetryableUpstreamOverloadCode(code: string | undefined): boolean;
|
|
3
5
|
export declare function record(value: unknown): Record<string, unknown>;
|
package/dist/model/sse.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { MarAgentError } from '../error.js';
|
|
2
2
|
import { isRetryableModelHttpStatus } from './http.js';
|
|
3
3
|
const DEFAULT_MODEL_STREAM_IDLE_TIMEOUT_MS = 300_000;
|
|
4
|
+
const streamEventObservedAt = new WeakMap();
|
|
5
|
+
export function observeModelStreamEvent(value, observedAt) {
|
|
6
|
+
if (typeof value === 'object' && value !== null && observedAt !== undefined)
|
|
7
|
+
streamEventObservedAt.set(value, observedAt);
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
export function modelStreamEventObservedAt(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null ? streamEventObservedAt.get(value) : undefined;
|
|
12
|
+
}
|
|
4
13
|
export async function* parseSse(response, signal, maxBytes = 16 * 1024 * 1024, idleTimeoutMs = DEFAULT_MODEL_STREAM_IDLE_TIMEOUT_MS) {
|
|
5
14
|
if (!response.ok) {
|
|
6
15
|
const body = await readResponseText(response, signal, maxBytes, idleTimeoutMs);
|
|
@@ -34,6 +43,7 @@ export async function* parseSse(response, signal, maxBytes = 16 * 1024 * 1024, i
|
|
|
34
43
|
if (signal.aborted)
|
|
35
44
|
throw signal.reason;
|
|
36
45
|
const { value, done } = await readStreamChunk(reader, signal, idleTimeoutMs);
|
|
46
|
+
const observedAt = performance.now();
|
|
37
47
|
if (done) {
|
|
38
48
|
streamCompleted = true;
|
|
39
49
|
break;
|
|
@@ -53,7 +63,7 @@ export async function* parseSse(response, signal, maxBytes = 16 * 1024 * 1024, i
|
|
|
53
63
|
if (data.length === 0 || data === '[DONE]')
|
|
54
64
|
continue;
|
|
55
65
|
try {
|
|
56
|
-
yield JSON.parse(data);
|
|
66
|
+
yield observeModelStreamEvent(JSON.parse(data), observedAt);
|
|
57
67
|
}
|
|
58
68
|
catch (cause) {
|
|
59
69
|
throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model SSE contained invalid JSON.', { cause });
|
package/dist/sdk/agent.js
CHANGED
|
@@ -37,6 +37,7 @@ import { createCodeToolDispatch } from '../tools/code-mode/dispatch.js';
|
|
|
37
37
|
import { createSessionToolScheduler, scheduleToolCallBatch } from '../tools/scheduler.js';
|
|
38
38
|
import { projectCodeModeSearchResult, projectCodeModeTools } from '../tools/code-mode.js';
|
|
39
39
|
import { allowsDirectTool, allowsToolCaller, toolCallers } from '../tools/callers.js';
|
|
40
|
+
import { modelStreamEventObservedAt } from '../model/sse.js';
|
|
40
41
|
import { SubagentSessionController } from '../subagent/session-controller.js';
|
|
41
42
|
/** Retry only classified recoverable failures within the attempt budget; never retry cancellation. */
|
|
42
43
|
async function recoverModelStream(error, retryState, signal) {
|
|
@@ -905,6 +906,8 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
905
906
|
let sawModelSemanticEvent = false;
|
|
906
907
|
let requestStartedAt;
|
|
907
908
|
let firstModelResponseAt;
|
|
909
|
+
let firstOutputDeltaAt;
|
|
910
|
+
let lastOutputDeltaAt;
|
|
908
911
|
let requestCompletedAt;
|
|
909
912
|
let requestOutputTokens;
|
|
910
913
|
const roundMessageCount = messages.length;
|
|
@@ -917,18 +920,24 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
917
920
|
});
|
|
918
921
|
};
|
|
919
922
|
const emitFinalPerformance = async (completedAt) => {
|
|
920
|
-
if (mode === 'compact' ||
|
|
923
|
+
if (mode === 'compact' ||
|
|
924
|
+
firstModelResponseAt === undefined ||
|
|
925
|
+
firstOutputDeltaAt === undefined ||
|
|
926
|
+
lastOutputDeltaAt === undefined ||
|
|
927
|
+
lastOutputDeltaAt - firstOutputDeltaAt < 1)
|
|
921
928
|
return;
|
|
922
|
-
const
|
|
929
|
+
const outputDuration = completedAt - firstOutputDeltaAt;
|
|
923
930
|
const outputTokens = requestOutputTokens;
|
|
924
|
-
if (outputTokens === undefined ||
|
|
931
|
+
if (outputTokens === undefined ||
|
|
932
|
+
outputDuration < 1 ||
|
|
933
|
+
completedAt < lastOutputDeltaAt)
|
|
925
934
|
return;
|
|
926
935
|
await emit({
|
|
927
936
|
type: 'model.performance',
|
|
928
937
|
latencyMs: Math.max(0, Math.round(firstModelResponseAt - requestStartedAt)),
|
|
929
938
|
outputTokens,
|
|
930
|
-
outputDurationMs,
|
|
931
|
-
tokensPerSecond: Number(((outputTokens * 1000) /
|
|
939
|
+
outputDurationMs: Math.round(outputDuration),
|
|
940
|
+
tokensPerSecond: Number(((outputTokens * 1000) / outputDuration).toFixed(2))
|
|
932
941
|
});
|
|
933
942
|
};
|
|
934
943
|
const persistToolCalls = async (calls) => {
|
|
@@ -976,11 +985,25 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
976
985
|
...(modelInputImages?.length ? { images: modelInputImages } : {}),
|
|
977
986
|
onAttemptDiagnostic: recordModelAttempt(mode === 'compact' ? 'COMPACTION' : 'EXECUTION')
|
|
978
987
|
})) {
|
|
979
|
-
|
|
988
|
+
if (event.type === 'output.reset') {
|
|
989
|
+
firstOutputDeltaAt = undefined;
|
|
990
|
+
lastOutputDeltaAt = undefined;
|
|
991
|
+
requestOutputTokens = undefined;
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const modelResponseAt = mode !== 'compact' && isModelResponseEvent(event)
|
|
995
|
+
? Math.max(requestStartedAt, modelStreamEventObservedAt(event) ?? performance.now())
|
|
996
|
+
: undefined;
|
|
980
997
|
const isFirstModelResponse = modelResponseAt !== undefined && firstModelResponseAt === undefined;
|
|
981
998
|
if (modelResponseAt !== undefined) {
|
|
982
|
-
|
|
999
|
+
if (event.type !== 'output.delta')
|
|
1000
|
+
sawModelSemanticEvent = true;
|
|
983
1001
|
firstModelResponseAt ??= modelResponseAt;
|
|
1002
|
+
if (event.type === 'output.delta' ||
|
|
1003
|
+
(event.type === 'text.delta' && event.delta.length > 0)) {
|
|
1004
|
+
firstOutputDeltaAt ??= modelResponseAt;
|
|
1005
|
+
lastOutputDeltaAt = modelResponseAt;
|
|
1006
|
+
}
|
|
984
1007
|
}
|
|
985
1008
|
if (event.type === 'response.item') {
|
|
986
1009
|
// Compaction keeps only its successful checkpoint, never intermediate native output.
|
|
@@ -1117,7 +1140,7 @@ export async function createMarAgentWithAdapter(options) {
|
|
|
1117
1140
|
}
|
|
1118
1141
|
else if (event.type === 'completed') {
|
|
1119
1142
|
stop = event.reason;
|
|
1120
|
-
requestCompletedAt = performance.now();
|
|
1143
|
+
requestCompletedAt = modelStreamEventObservedAt(event) ?? performance.now();
|
|
1121
1144
|
}
|
|
1122
1145
|
if (isFirstModelResponse)
|
|
1123
1146
|
await emitLatency();
|
|
@@ -2459,6 +2482,7 @@ function isModelResponseEvent(event) {
|
|
|
2459
2482
|
if (event.type === 'text.completed' || event.type === 'reasoning.summary')
|
|
2460
2483
|
return event.text.length > 0;
|
|
2461
2484
|
return (event.type === 'response.item' ||
|
|
2485
|
+
event.type === 'output.delta' ||
|
|
2462
2486
|
event.type === 'client_tool.call' ||
|
|
2463
2487
|
event.type === 'hosted_tool' ||
|
|
2464
2488
|
event.type === 'citation');
|
|
@@ -527,13 +527,23 @@ function streamText(value, emit) {
|
|
|
527
527
|
else {
|
|
528
528
|
emit('{');
|
|
529
529
|
let first = true;
|
|
530
|
-
|
|
530
|
+
const names = Object.keys(item);
|
|
531
|
+
// Special-case view_image and native MCP image objects: text omits Base64; out.image keeps pixels.
|
|
532
|
+
const imagePayload = names.includes('mimeType') &&
|
|
533
|
+
typeof item.mimeType === 'string' &&
|
|
534
|
+
item.mimeType.startsWith('image/');
|
|
535
|
+
for (const name of names) {
|
|
531
536
|
if (!first)
|
|
532
537
|
emit(',');
|
|
533
538
|
first = false;
|
|
534
539
|
chunks(name, true);
|
|
535
540
|
emit(':');
|
|
536
|
-
|
|
541
|
+
const field = item[name];
|
|
542
|
+
write(imagePayload &&
|
|
543
|
+
(name === 'dataBase64' || (name === 'data' && item.type === 'image')) &&
|
|
544
|
+
typeof field === 'string'
|
|
545
|
+
? '[image payload omitted; use out.image]'
|
|
546
|
+
: field, name);
|
|
537
547
|
}
|
|
538
548
|
emit('}');
|
|
539
549
|
}
|
package/dist/tools/code-mode.js
CHANGED
|
@@ -30,7 +30,7 @@ Tool calls:
|
|
|
30
30
|
- skill, tool_search, question, code_exec, code_exec_wait and direct-only tools cannot be called here. Load Skills directly and read their instructions before the next tool call. Use direct tool_search before a later code cell for deferred tools; the current cell's tools do not change.
|
|
31
31
|
|
|
32
32
|
Built-in namespaces:
|
|
33
|
-
- out.text(value): void emits text. Strings and scalar values are stringified; JSON-compatible objects/arrays are serialized as compact JSON. Excess ordinary text is truncated with omission metadata, without stopping later code or tools. Cyclic or unsupported object values fail. Only explicitly emitted output reaches the model.
|
|
33
|
+
- out.text(value): void emits text. Strings and scalar values are stringified; JSON-compatible objects/arrays are serialized as compact JSON. Structured image objects (image/* mimeType and string dataBase64, or native MCP type:image with string data) are recursively summarized without their Base64 payload; originals remain unchanged. Ordinary strings remain unchanged. This does not emit pixels: use out.image(result.images[0]) for images. Prefer result.data or result.content over the whole image result. Excess ordinary text is truncated with omission metadata, without stopping later code or tools. Cyclic or unsupported object values fail. Only explicitly emitted output reaches the model.
|
|
34
34
|
- out.image(value: {mimeType:string,dataBase64:string}): void emits one returned image; for example out.image(result.images[0]) when that image exists and the model supports images. It does not accept a path, URL, or result.content.
|
|
35
35
|
- state.get(key), state.has(key), state.set(key, value), state.delete(key) access bounded session JSON state. Mutations commit atomically only when the cell succeeds; failed or cancelled cells discard them.
|
|
36
36
|
- catalog.tools() returns a read-only snapshot of the tools callable in this cell, including name, description, inputSchema and inputMode. It does not discover or authorize additional tools.
|