@myagentroam/agent 0.9.173 → 0.9.175
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/agent-core/extensions/compaction.d.ts +1 -1
- package/dist/agent-core/extensions/model-request.js +7 -1
- package/dist/agent-core/extensions/model-stream.js +5 -1
- package/dist/agent-core/model/anthropic-messages.js +35 -25
- package/dist/agent-core/model/catalog-file.d.ts +0 -2
- package/dist/agent-core/model/catalog-file.js +7 -0
- package/dist/agent-core/model/configuration.d.ts +0 -1
- package/dist/agent-core/model/configuration.js +0 -1
- package/dist/agent-core/model/contracts.d.ts +13 -0
- package/dist/agent-core/model/openai-responses.js +57 -48
- package/dist/agent-core/model/sse.js +59 -9
- package/dist/agent-core/prompts/compact.d.ts +0 -1
- package/dist/agent-core/prompts/compact.js +0 -56
- package/dist/agent-core/prompts/index.d.ts +0 -1
- package/dist/agent-core/prompts/index.js +2 -9
- package/dist/agent-core/runtime/compaction/history.d.ts +1 -2
- package/dist/agent-core/runtime/compaction/history.js +4 -4
- package/dist/agent-core/runtime/events.d.ts +1 -1
- package/dist/agent-core/runtime/execution.js +35 -24
- package/dist/agent-core/runtime/model-generation-timing.d.ts +12 -0
- package/dist/agent-core/runtime/model-generation-timing.js +39 -0
- package/dist/agent-core/sdk/session.js +7 -11
- package/dist/agent-core/tools/agent/agent-start.js +3 -1
- package/dist/agent-core/tools/code-mode/index.js +10 -5
- package/dist/agent-core/tools/definition-projection.d.ts +7 -0
- package/dist/agent-core/tools/definition-projection.js +26 -0
- package/dist/agent-core/tools/mcp/tool-search.js +12 -4
- package/dist/cli/main.js +6 -4
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { observationModelParts } from '../tools/code-mode/observations.js';
|
|
2
|
+
import { stableToolSchema } from '../tools/definition-projection.js';
|
|
2
3
|
import { isDeepStrictEqual } from 'node:util';
|
|
3
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
5
|
import { describeMarAgentError, MarAgentError } from '../error.js';
|
|
@@ -89,6 +90,15 @@ export class OpenAiResponsesAdapter {
|
|
|
89
90
|
const attemptContinuation = useContinuation;
|
|
90
91
|
const attemptCredentialReason = credentialReason;
|
|
91
92
|
const attemptTransport = this.configuration.responsesTransport?.transport === 'WEBSOCKET' ? 'WEBSOCKET' : 'HTTP';
|
|
93
|
+
const requestPrefixDiagnostic = request.onAttemptDiagnostic
|
|
94
|
+
? {
|
|
95
|
+
requestInstructionsHash: hashResponsesValue(prepared.fullBody.instructions),
|
|
96
|
+
requestToolsHash: hashResponsesValue(prepared.fullBody.tools),
|
|
97
|
+
requestPropertiesHash: hashResponsesValue({ ...prepared.fullBody, input: undefined }),
|
|
98
|
+
requestInputHash: hashResponsesValue(prepared.fullBody.input),
|
|
99
|
+
requestInputItems: prepared.fullBody.input.length
|
|
100
|
+
}
|
|
101
|
+
: {};
|
|
92
102
|
const prelude = [];
|
|
93
103
|
let sawSemanticEvent = false;
|
|
94
104
|
let firstSemanticEventType;
|
|
@@ -100,11 +110,12 @@ export class OpenAiResponsesAdapter {
|
|
|
100
110
|
attempt: attemptNumber,
|
|
101
111
|
maxAttempts,
|
|
102
112
|
continuation: attemptContinuation,
|
|
103
|
-
credentialReason: attemptCredentialReason
|
|
113
|
+
credentialReason: attemptCredentialReason,
|
|
114
|
+
...requestPrefixDiagnostic
|
|
104
115
|
});
|
|
105
116
|
try {
|
|
106
117
|
for await (const event of this.startAttempt(request, signal, requestBody, useContinuation, credentialReason, turn)) {
|
|
107
|
-
if (event.type
|
|
118
|
+
if (event.type.startsWith('output.')) {
|
|
108
119
|
resetOutputTiming = true;
|
|
109
120
|
yield event;
|
|
110
121
|
continue;
|
|
@@ -130,6 +141,7 @@ export class OpenAiResponsesAdapter {
|
|
|
130
141
|
maxAttempts,
|
|
131
142
|
continuation: attemptContinuation,
|
|
132
143
|
credentialReason: attemptCredentialReason,
|
|
144
|
+
...requestPrefixDiagnostic,
|
|
133
145
|
semanticEventObserved: sawSemanticEvent,
|
|
134
146
|
...(firstSemanticEventType === undefined ? {} : { firstSemanticEventType }),
|
|
135
147
|
...turn.connectionDiagnostic(),
|
|
@@ -146,6 +158,7 @@ export class OpenAiResponsesAdapter {
|
|
|
146
158
|
maxAttempts,
|
|
147
159
|
continuation: attemptContinuation,
|
|
148
160
|
credentialReason: attemptCredentialReason,
|
|
161
|
+
...requestPrefixDiagnostic,
|
|
149
162
|
semanticEventObserved: sawSemanticEvent,
|
|
150
163
|
...(firstSemanticEventType === undefined ? {} : { firstSemanticEventType }),
|
|
151
164
|
...turn.connectionDiagnostic(),
|
|
@@ -297,10 +310,15 @@ export class OpenAiResponsesAdapter {
|
|
|
297
310
|
for await (const raw of rawEvents) {
|
|
298
311
|
const event = record(raw);
|
|
299
312
|
const type = text(event.type) ?? 'unknown';
|
|
313
|
+
if (type === 'response.output_item.added' &&
|
|
314
|
+
['reasoning', 'message', 'function_call', 'custom_tool_call'].includes(text(record(event.item).type) ?? ''))
|
|
315
|
+
yield observeModelStreamEvent({ type: 'output.started' }, modelStreamEventObservedAt(raw));
|
|
316
|
+
if (type === 'response.web_search_call.searching')
|
|
317
|
+
yield observeModelStreamEvent({ type: 'output.paused' }, modelStreamEventObservedAt(raw));
|
|
300
318
|
if (type === 'response.output_item.done') {
|
|
301
319
|
const item = responseItem(event.item);
|
|
302
320
|
if (item)
|
|
303
|
-
yield { type: 'response.item', provider: 'OPENAI_RESPONSES', item };
|
|
321
|
+
yield observeModelStreamEvent({ type: 'response.item', provider: 'OPENAI_RESPONSES', item }, modelStreamEventObservedAt(raw));
|
|
304
322
|
}
|
|
305
323
|
if (type === 'response.output_item.added' && text(record(event.item).type) === 'message') {
|
|
306
324
|
const item = record(event.item);
|
|
@@ -319,7 +337,7 @@ export class OpenAiResponsesAdapter {
|
|
|
319
337
|
if (completedText !== undefined && !emittedCompletedText.has(itemId)) {
|
|
320
338
|
emittedCompletedText.add(itemId);
|
|
321
339
|
pendingCompletedText.delete(itemId);
|
|
322
|
-
yield { type: 'text.completed', itemId, phase, text: completedText };
|
|
340
|
+
yield observeModelStreamEvent({ type: 'text.completed', itemId, phase, text: completedText }, modelStreamEventObservedAt(raw));
|
|
323
341
|
}
|
|
324
342
|
}
|
|
325
343
|
else if (type === 'response.output_item.added' &&
|
|
@@ -363,10 +381,10 @@ export class OpenAiResponsesAdapter {
|
|
|
363
381
|
yield observeModelStreamEvent({ type: 'output.delta' }, modelStreamEventObservedAt(raw));
|
|
364
382
|
}
|
|
365
383
|
else if (type === 'response.created')
|
|
366
|
-
yield {
|
|
384
|
+
yield observeModelStreamEvent({
|
|
367
385
|
type: 'response.started',
|
|
368
386
|
responseId: responseEventId(event) ?? 'unknown'
|
|
369
|
-
};
|
|
387
|
+
}, modelStreamEventObservedAt(raw));
|
|
370
388
|
else if (type === 'response.output_text.delta')
|
|
371
389
|
yield observeModelStreamEvent({
|
|
372
390
|
type: 'text.delta',
|
|
@@ -384,17 +402,17 @@ export class OpenAiResponsesAdapter {
|
|
|
384
402
|
continue;
|
|
385
403
|
if (phase) {
|
|
386
404
|
emittedCompletedText.add(itemId);
|
|
387
|
-
yield { type: 'text.completed', itemId, phase, text: completedText };
|
|
405
|
+
yield observeModelStreamEvent({ type: 'text.completed', itemId, phase, text: completedText }, modelStreamEventObservedAt(raw));
|
|
388
406
|
}
|
|
389
407
|
else
|
|
390
408
|
pendingCompletedText.set(itemId, completedText);
|
|
391
409
|
}
|
|
392
410
|
else if (type === 'response.reasoning_summary_text.done')
|
|
393
|
-
yield {
|
|
411
|
+
yield observeModelStreamEvent({
|
|
394
412
|
type: 'reasoning.summary',
|
|
395
413
|
itemId: text(event.item_id) ?? 'reasoning',
|
|
396
414
|
text: text(event.text) ?? ''
|
|
397
|
-
};
|
|
415
|
+
}, modelStreamEventObservedAt(raw));
|
|
398
416
|
else if (type === 'response.output_item.done' &&
|
|
399
417
|
text(record(event.item).type) === 'tool_search_call') {
|
|
400
418
|
const item = record(event.item);
|
|
@@ -410,13 +428,13 @@ export class OpenAiResponsesAdapter {
|
|
|
410
428
|
}
|
|
411
429
|
if (!emittedFunctionCalls.has(callId)) {
|
|
412
430
|
emittedFunctionCalls.add(callId);
|
|
413
|
-
yield {
|
|
431
|
+
yield observeModelStreamEvent({
|
|
414
432
|
type: 'client_tool.call',
|
|
415
433
|
callId,
|
|
416
434
|
name: 'tool_search',
|
|
417
435
|
arguments: arguments_,
|
|
418
436
|
...(argumentError ? { argumentError } : {})
|
|
419
|
-
};
|
|
437
|
+
}, modelStreamEventObservedAt(raw));
|
|
420
438
|
}
|
|
421
439
|
}
|
|
422
440
|
else if (type === 'response.function_call_arguments.done' ||
|
|
@@ -440,13 +458,13 @@ export class OpenAiResponsesAdapter {
|
|
|
440
458
|
'unknown';
|
|
441
459
|
if (!emittedFunctionCalls.has(callId)) {
|
|
442
460
|
emittedFunctionCalls.add(callId);
|
|
443
|
-
yield {
|
|
461
|
+
yield observeModelStreamEvent({
|
|
444
462
|
type: 'client_tool.call',
|
|
445
463
|
callId,
|
|
446
464
|
name: text(event.name) ?? text(item.name) ?? known?.name ?? 'unknown',
|
|
447
465
|
arguments: args,
|
|
448
466
|
...(argumentError ? { argumentError } : {})
|
|
449
|
-
};
|
|
467
|
+
}, modelStreamEventObservedAt(raw));
|
|
450
468
|
}
|
|
451
469
|
}
|
|
452
470
|
else if (type === 'response.custom_tool_call_input.done' ||
|
|
@@ -458,13 +476,13 @@ export class OpenAiResponsesAdapter {
|
|
|
458
476
|
const callId = text(event.call_id) ?? text(item.call_id) ?? known?.callId ?? itemId;
|
|
459
477
|
if (!emittedFunctionCalls.has(callId)) {
|
|
460
478
|
emittedFunctionCalls.add(callId);
|
|
461
|
-
yield {
|
|
479
|
+
yield observeModelStreamEvent({
|
|
462
480
|
type: 'client_tool.call',
|
|
463
481
|
callId,
|
|
464
482
|
name: text(event.name) ?? text(item.name) ?? known?.name ?? 'unknown',
|
|
465
483
|
arguments: text(event.input) ?? text(item.input) ?? known?.input ?? '',
|
|
466
484
|
inputMode: 'freeform'
|
|
467
|
-
};
|
|
485
|
+
}, modelStreamEventObservedAt(raw));
|
|
468
486
|
}
|
|
469
487
|
}
|
|
470
488
|
else if (type.includes('web_search_call') ||
|
|
@@ -489,7 +507,7 @@ export class OpenAiResponsesAdapter {
|
|
|
489
507
|
? {}
|
|
490
508
|
: { sourcesTruncated: summary.sourcesTruncated })
|
|
491
509
|
};
|
|
492
|
-
yield {
|
|
510
|
+
yield observeModelStreamEvent({
|
|
493
511
|
type: 'hosted_tool',
|
|
494
512
|
itemId,
|
|
495
513
|
kind,
|
|
@@ -503,13 +521,13 @@ export class OpenAiResponsesAdapter {
|
|
|
503
521
|
? 'COMPLETED'
|
|
504
522
|
: 'RUNNING',
|
|
505
523
|
summary
|
|
506
|
-
};
|
|
524
|
+
}, modelStreamEventObservedAt(raw));
|
|
507
525
|
}
|
|
508
526
|
else if (type === 'response.output_text.annotation.added') {
|
|
509
527
|
const annotation = record(event.annotation);
|
|
510
528
|
const url = text(annotation.url);
|
|
511
529
|
if (url)
|
|
512
|
-
yield {
|
|
530
|
+
yield observeModelStreamEvent({
|
|
513
531
|
type: 'citation',
|
|
514
532
|
citation: {
|
|
515
533
|
id: text(annotation.id) ??
|
|
@@ -517,9 +535,10 @@ export class OpenAiResponsesAdapter {
|
|
|
517
535
|
url,
|
|
518
536
|
...(text(annotation.title) ? { title: text(annotation.title) } : {})
|
|
519
537
|
}
|
|
520
|
-
};
|
|
538
|
+
}, modelStreamEventObservedAt(raw));
|
|
521
539
|
}
|
|
522
540
|
else if (type === 'response.completed' || type === 'response.incomplete') {
|
|
541
|
+
yield observeModelStreamEvent({ type: 'output.completed' }, modelStreamEventObservedAt(raw));
|
|
523
542
|
const responseData = record(event.response);
|
|
524
543
|
if (type === 'response.completed')
|
|
525
544
|
turn.captureCompletedResponseId(responseEventId(event));
|
|
@@ -527,21 +546,22 @@ export class OpenAiResponsesAdapter {
|
|
|
527
546
|
if (emittedCompletedText.has(itemId))
|
|
528
547
|
continue;
|
|
529
548
|
emittedCompletedText.add(itemId);
|
|
530
|
-
yield {
|
|
549
|
+
yield observeModelStreamEvent({
|
|
531
550
|
type: 'text.completed',
|
|
532
551
|
itemId,
|
|
533
552
|
phase: textPhases.get(itemId) ?? 'final_answer',
|
|
534
553
|
text: completedText
|
|
535
|
-
};
|
|
554
|
+
}, modelStreamEventObservedAt(raw));
|
|
536
555
|
}
|
|
537
556
|
pendingCompletedText.clear();
|
|
538
557
|
for (const citation of responseCitations(responseData))
|
|
539
|
-
yield { type: 'citation', citation };
|
|
558
|
+
yield observeModelStreamEvent({ type: 'citation', citation }, modelStreamEventObservedAt(raw));
|
|
540
559
|
const usage = record(responseData.usage);
|
|
541
560
|
const inputDetails = record(usage.input_tokens_details);
|
|
542
561
|
const rolloutBudgetUnits = optionalRolloutBudgetUnits(usage.codex_rollout_budget_units);
|
|
543
|
-
yield {
|
|
562
|
+
yield observeModelStreamEvent({
|
|
544
563
|
type: 'usage',
|
|
564
|
+
final: true,
|
|
545
565
|
usage: {
|
|
546
566
|
inputTokens: number(usage.input_tokens),
|
|
547
567
|
contextInputTokens: number(usage.input_tokens),
|
|
@@ -550,7 +570,7 @@ export class OpenAiResponsesAdapter {
|
|
|
550
570
|
cacheWriteTokens: number(inputDetails.cache_write_tokens),
|
|
551
571
|
...(rolloutBudgetUnits === undefined ? {} : { rolloutBudgetUnits })
|
|
552
572
|
}
|
|
553
|
-
};
|
|
573
|
+
}, modelStreamEventObservedAt(raw));
|
|
554
574
|
yield observeModelStreamEvent({
|
|
555
575
|
type: 'completed',
|
|
556
576
|
reason: type === 'response.incomplete' ? 'max_output' : 'completed'
|
|
@@ -586,7 +606,7 @@ export class OpenAiResponsesAdapter {
|
|
|
586
606
|
});
|
|
587
607
|
}
|
|
588
608
|
else if (!isNonSemanticOpenAiResponsesEvent(type))
|
|
589
|
-
yield { type: 'unknown', nativeType: type };
|
|
609
|
+
yield observeModelStreamEvent({ type: 'unknown', nativeType: type }, modelStreamEventObservedAt(raw));
|
|
590
610
|
}
|
|
591
611
|
}
|
|
592
612
|
catch (error) {
|
|
@@ -651,7 +671,7 @@ class OpenAiResponsesTurnSession {
|
|
|
651
671
|
if (event.type === 'response.started' && event.responseId !== 'unknown')
|
|
652
672
|
responseId = event.responseId;
|
|
653
673
|
else if (event.type === 'response.item')
|
|
654
|
-
outputItems.push(event.item);
|
|
674
|
+
outputItems.push(JSON.parse(JSON.stringify(event.item)));
|
|
655
675
|
else if (event.type === 'text.delta' ||
|
|
656
676
|
event.type === 'output.delta' ||
|
|
657
677
|
event.type === 'text.completed' ||
|
|
@@ -706,7 +726,8 @@ class OpenAiResponsesTurnSession {
|
|
|
706
726
|
invalidateResponsesWebSocket(this.state, false);
|
|
707
727
|
clearResponsesContinuation(this.state);
|
|
708
728
|
}
|
|
709
|
-
|
|
729
|
+
// The continuation baseline must not share mutable schemas or items with callers.
|
|
730
|
+
const fullBody = JSON.parse(JSON.stringify(responsesRequestBody(this.adapter.configuration, request, request.messages)));
|
|
710
731
|
if (responsesContinuationEnabled(this.adapter.configuration)) {
|
|
711
732
|
const incremental = incrementalResponsesInput(this.state, fullBody);
|
|
712
733
|
if (incremental !== undefined) {
|
|
@@ -875,7 +896,7 @@ function responsesRequestBody(configuration, request, messages) {
|
|
|
875
896
|
type: 'tool_search',
|
|
876
897
|
execution: 'client',
|
|
877
898
|
description: tool.description,
|
|
878
|
-
parameters: strictFunctionSchema(tool.inputSchema)
|
|
899
|
+
parameters: stableToolSchema(strictFunctionSchema(stableToolSchema(tool.inputSchema)))
|
|
879
900
|
}
|
|
880
901
|
: responsesFunctionTool(tool));
|
|
881
902
|
if (configuration.responsesToolNamespace && tools.length > 0) {
|
|
@@ -948,27 +969,13 @@ function incrementalResponsesInput(state, current) {
|
|
|
948
969
|
if (current.input.length < baseline.length)
|
|
949
970
|
return undefined;
|
|
950
971
|
for (let index = 0; index < baseline.length; index++)
|
|
951
|
-
if (
|
|
972
|
+
if (JSON.stringify(baseline[index]) !== JSON.stringify(current.input[index]))
|
|
952
973
|
return undefined;
|
|
953
974
|
return current.input.slice(baseline.length);
|
|
954
975
|
}
|
|
955
976
|
function responsesContinuationPropertiesMatch(previous, current) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
'service_tier',
|
|
959
|
-
'instructions',
|
|
960
|
-
'tools',
|
|
961
|
-
'tool_choice',
|
|
962
|
-
'max_output_tokens',
|
|
963
|
-
'parallel_tool_calls',
|
|
964
|
-
'reasoning',
|
|
965
|
-
'store',
|
|
966
|
-
'stream',
|
|
967
|
-
'include',
|
|
968
|
-
'prompt_cache_key',
|
|
969
|
-
'text'
|
|
970
|
-
];
|
|
971
|
-
return properties.every((property) => isDeepStrictEqual(previous[property], current[property]));
|
|
977
|
+
return (JSON.stringify({ ...previous, input: undefined }) ===
|
|
978
|
+
JSON.stringify({ ...current, input: undefined }));
|
|
972
979
|
}
|
|
973
980
|
function clearResponsesContinuation(state) {
|
|
974
981
|
delete state.lastRequest;
|
|
@@ -1387,6 +1394,8 @@ function webSocketReceiveError(cause) {
|
|
|
1387
1394
|
});
|
|
1388
1395
|
}
|
|
1389
1396
|
function isSemanticEvent(event) {
|
|
1397
|
+
if (event.type.startsWith('output.'))
|
|
1398
|
+
return false;
|
|
1390
1399
|
if (event.type === 'response.started' || event.type === 'usage' || event.type === 'response.item')
|
|
1391
1400
|
return false;
|
|
1392
1401
|
if (event.type === 'text.delta')
|
|
@@ -1508,11 +1517,11 @@ function responsesHeaders(credential, promptCacheKey, turnState) {
|
|
|
1508
1517
|
authorization: `Bearer ${credential.bearer.reveal()}`,
|
|
1509
1518
|
'content-type': 'application/json',
|
|
1510
1519
|
accept: 'text/event-stream',
|
|
1520
|
+
'x-client-request-id': randomUUID(),
|
|
1511
1521
|
...(promptCacheKey
|
|
1512
1522
|
? {
|
|
1513
1523
|
'session-id': promptCacheKey,
|
|
1514
|
-
'thread-id': promptCacheKey
|
|
1515
|
-
'x-client-request-id': promptCacheKey
|
|
1524
|
+
'thread-id': promptCacheKey
|
|
1516
1525
|
}
|
|
1517
1526
|
: {})
|
|
1518
1527
|
});
|
|
@@ -1927,7 +1936,7 @@ function responsesFunctionTool(tool) {
|
|
|
1927
1936
|
type: 'function',
|
|
1928
1937
|
name: tool.name,
|
|
1929
1938
|
description: tool.description,
|
|
1930
|
-
parameters: strict ? strictFunctionSchema(tool.inputSchema) : tool.inputSchema,
|
|
1939
|
+
parameters: stableToolSchema(strict ? strictFunctionSchema(stableToolSchema(tool.inputSchema)) : tool.inputSchema),
|
|
1931
1940
|
strict
|
|
1932
1941
|
};
|
|
1933
1942
|
}
|
|
@@ -38,18 +38,66 @@ export async function* parseSse(response, signal, maxBytes = 16 * 1024 * 1024, i
|
|
|
38
38
|
let buffer = '';
|
|
39
39
|
let total = 0;
|
|
40
40
|
let streamCompleted = false;
|
|
41
|
+
const receiverAbort = new AbortController();
|
|
42
|
+
const receiverSignal = AbortSignal.any([signal, receiverAbort.signal]);
|
|
43
|
+
const chunks = [];
|
|
44
|
+
let nextChunk = 0;
|
|
45
|
+
let receivedAll = false;
|
|
46
|
+
let receiverFailure;
|
|
47
|
+
let wake;
|
|
48
|
+
const notify = () => {
|
|
49
|
+
wake?.();
|
|
50
|
+
wake = undefined;
|
|
51
|
+
};
|
|
52
|
+
// Keep transport receipt independent of async SDK publication and persistence.
|
|
53
|
+
// The cumulative response byte limit also bounds the pending raw chunk queue.
|
|
54
|
+
const receive = (async () => {
|
|
55
|
+
try {
|
|
56
|
+
while (true) {
|
|
57
|
+
const { value, done } = await readStreamChunk(reader, receiverSignal, idleTimeoutMs);
|
|
58
|
+
const observedAt = performance.now();
|
|
59
|
+
if (done) {
|
|
60
|
+
streamCompleted = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
total += value.byteLength;
|
|
64
|
+
enforceResponseSize(total, maxBytes);
|
|
65
|
+
if (value.byteLength === 0)
|
|
66
|
+
continue;
|
|
67
|
+
chunks.push({ value, observedAt });
|
|
68
|
+
notify();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
receiverFailure = { error };
|
|
73
|
+
await reader.cancel(error).catch(() => undefined);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
receivedAll = true;
|
|
77
|
+
notify();
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
41
80
|
try {
|
|
42
81
|
while (true) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
82
|
+
signal.throwIfAborted();
|
|
83
|
+
const chunk = chunks[nextChunk];
|
|
84
|
+
if (chunk === undefined) {
|
|
85
|
+
if (receivedAll) {
|
|
86
|
+
if (receiverFailure !== undefined)
|
|
87
|
+
throw receiverFailure.error;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
await new Promise((resolve) => {
|
|
91
|
+
wake = resolve;
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
50
94
|
}
|
|
51
|
-
|
|
52
|
-
|
|
95
|
+
chunks[nextChunk++] = undefined;
|
|
96
|
+
if (nextChunk >= 1_024 && nextChunk * 2 >= chunks.length) {
|
|
97
|
+
chunks.splice(0, nextChunk);
|
|
98
|
+
nextChunk = 0;
|
|
99
|
+
}
|
|
100
|
+
const { value, observedAt } = chunk;
|
|
53
101
|
buffer += decoder.decode(value, { stream: true }).replaceAll('\r\n', '\n');
|
|
54
102
|
let boundary;
|
|
55
103
|
while ((boundary = buffer.indexOf('\n\n')) >= 0) {
|
|
@@ -75,6 +123,8 @@ export async function* parseSse(response, signal, maxBytes = 16 * 1024 * 1024, i
|
|
|
75
123
|
throw new MarAgentError('MAR_AGENT_MODEL_PROTOCOL_ERROR', 'Model SSE ended with an incomplete frame.');
|
|
76
124
|
}
|
|
77
125
|
finally {
|
|
126
|
+
receiverAbort.abort();
|
|
127
|
+
await receive;
|
|
78
128
|
await closeReader(reader, streamCompleted);
|
|
79
129
|
}
|
|
80
130
|
}
|
|
@@ -24,59 +24,3 @@ Remove aggressively:
|
|
|
24
24
|
|
|
25
25
|
Use compact Markdown with these headings when applicable: Objective, Instructions and constraints, Decisions and facts, Completed work, Verification and evidence, Active state, Open issues and risks, Next action. Omit empty headings. Prefer precise paths, IDs, error codes, and short evidence over narrative. Output only the handoff.`;
|
|
26
26
|
}
|
|
27
|
-
export function highDensityCompactionPrompt() {
|
|
28
|
-
return `Compile the visible conversation into Textual Continuation IR that lets the next agent continue correctly after the transcript is discarded. Transfer state; do not recap, address the user, call tools, advance the task, or invent facts.
|
|
29
|
-
|
|
30
|
-
Optimize for continuation fidelity first and density second. Prefer sufficient continuation state over brevity; never omit information required to resume correctly.
|
|
31
|
-
|
|
32
|
-
## Output contract
|
|
33
|
-
Use these English section names when applicable; omit empty sections:
|
|
34
|
-
1. Goal contract
|
|
35
|
-
2. Current state
|
|
36
|
-
3. Settled matters
|
|
37
|
-
4. Necessary evidence
|
|
38
|
-
5. Open loops
|
|
39
|
-
|
|
40
|
-
IR grammar and vocabulary are English: headings, keys, enums, state atoms, and control terms. Natural-language values may follow the task's dominant language. Preserve exact user-designated text and established domain terms; do not translate identifiers or invent localized IR terminology.
|
|
41
|
-
|
|
42
|
-
Prefer records over prose:
|
|
43
|
-
<object> | <key>=<value> | <key>=<value>
|
|
44
|
-
<event/condition> -> <object>.<key>=<value> | <effect>
|
|
45
|
-
|
|
46
|
-
Use | for parallel facts, = for current state, and -> only for causality, invalidation, or transition. Keep enums lowercase and semantic: required, forbidden, pending; never YES/NO. Omit empty or irrelevant unknown fields. Keep pending, not run, unverified, and failed distinct. Use a sentence only when records cannot express a multi-object cause, conflict, authorization, or safety boundary unambiguously.
|
|
47
|
-
|
|
48
|
-
Examples:
|
|
49
|
-
src/discount.js | cause=verified | edit=pending | tests=not run
|
|
50
|
-
patch(src/discount.js) -> prior_read=stale | next=re-evidence
|
|
51
|
-
authorization | action=<exact> | scope=<exact> | lifetime=<exact> | status=active
|
|
52
|
-
|
|
53
|
-
## Compiler
|
|
54
|
-
1. Resolve intent. The latest user correction overrides older intent. Extract the primary objective, current deliverable, completion criteria, non-goals, and boundaries.
|
|
55
|
-
2. Reduce time. Convert before -> event -> after into the latest evidenced state. Source mutation or base/branch change makes only affected evidence stale. History replacement invalidates Responses continuation, not factual evidence. Without an invalidating event, a verified claim remains current.
|
|
56
|
-
3. Build objects. Emit one current record per file, symbol, feature, migration, test, worktree axis, selected Skill, Todo/plan item, external decision, or external side effect. Deduplicate equivalent facts.
|
|
57
|
-
4. Separate status axes when needed:
|
|
58
|
-
- completion=pending|in_progress|completed|blocked
|
|
59
|
-
- epistemic=observed|verified|inferred|proposed|disproved
|
|
60
|
-
- freshness=current|stale|superseded
|
|
61
|
-
- scope=turn|worktree|base|branch|external
|
|
62
|
-
5. Bind evidence to the claim it proves. Prefer a verified conclusion over raw snapshots or intermediate hypotheses.
|
|
63
|
-
6. Select under budget. Protect, in order: objective, progress, boundaries and authorization, non-repeatable side effects, open loops, then necessary evidence. Remove provenance and recoverable detail only after those remain sufficient.
|
|
64
|
-
|
|
65
|
-
## Retention rules
|
|
66
|
-
- Preserve active caller/user instructions outside filesystem-loaded AGENTS.md, exact authorization boundaries, confirmed decisions and exclusions, completed edits and side effects, verification actually run, reproducible failures, failed approaches that constrain the next step, unresolved risks, and the smallest established next action.
|
|
67
|
-
- Authorization is valid only for its exact action, scope, and lifetime in the active request. Completed or superseded scopes cannot revive it. Object state, completion, tool availability, and convenience never imply permission.
|
|
68
|
-
- Preserve decisive values, or an exact artifact path/command that deterministically recovers them, when a result controls later work. Counts or schemas alone are insufficient.
|
|
69
|
-
- Preserve paths, symbols, APIs, tools, models, providers, JSON keys, protocol fields, commands, arguments, error codes, commit hashes, call IDs, sequences, versions, numbers, and user-designated text verbatim.
|
|
70
|
-
- Preserve selected Skill names with only continuation-critical constraints. Preserve Todo/plan revision, item/status, and current minimum step.
|
|
71
|
-
- Keep observed HEAD, validated base, target commit, and current worktree as separate axes. Evidence updates only what it directly proves; cross-axis, cross-base, and cross-branch inference remains unverified.
|
|
72
|
-
- If evidence conflicts, use state=conflict | candidates=<short values> | next=re-evidence. Never choose a side without evidence.
|
|
73
|
-
|
|
74
|
-
## Exclusions
|
|
75
|
-
Remove greetings, progress chatter, chronology that does not affect current state, duplicate facts, superseded hypotheses without remaining constraints, long logs, source bodies, verbose payloads, cheap-to-recover content, local-only integrity hashes, hidden reasoning, secrets, credentials, and unrelated content.
|
|
76
|
-
|
|
77
|
-
Remove user/project/directory AGENTS.md text and constraints derived only from those files; the next Execution reloads them. Remove retained process/Subagent inventory, IDs, status, commands, cwd, and reuse instructions; Runtime reloads them.
|
|
78
|
-
|
|
79
|
-
Before emitting, verify that objective, current progress, authorization/boundaries, completed versus unverified work, decisive evidence, open loops, and next action are explicit and non-contradictory.
|
|
80
|
-
|
|
81
|
-
Output only compact Markdown IR.`;
|
|
82
|
-
}
|
|
@@ -13,7 +13,6 @@ interface SystemPromptInput {
|
|
|
13
13
|
workspace: string;
|
|
14
14
|
tools: readonly string[];
|
|
15
15
|
directTools?: readonly string[];
|
|
16
|
-
highDensityCompaction?: boolean;
|
|
17
16
|
extension?: string | undefined;
|
|
18
17
|
}
|
|
19
18
|
export declare function buildSystemPrompt(input: SystemPromptInput): string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderPromptSections } from '../extensions/prompt-composition.js';
|
|
2
|
-
import { compactionPrompt
|
|
2
|
+
import { compactionPrompt } from './compact.js';
|
|
3
3
|
import { environmentPrompt, identityPrompt, instructionPriorityPrompt, workspaceDisciplinePrompt } from './core.js';
|
|
4
4
|
import { modePrompt } from './modes.js';
|
|
5
5
|
import { outputStylePrompt } from './output.js';
|
|
@@ -36,14 +36,7 @@ export function buildSystemPromptSections(input) {
|
|
|
36
36
|
questionAvailable: toolNames.has('question')
|
|
37
37
|
})
|
|
38
38
|
],
|
|
39
|
-
[
|
|
40
|
-
'compaction',
|
|
41
|
-
input.mode === 'compact'
|
|
42
|
-
? input.highDensityCompaction
|
|
43
|
-
? highDensityCompactionPrompt()
|
|
44
|
-
: compactionPrompt()
|
|
45
|
-
: ''
|
|
46
|
-
],
|
|
39
|
+
['compaction', input.mode === 'compact' ? compactionPrompt() : ''],
|
|
47
40
|
['subagent', toolNames.has('agent_start') ? subagentPrompt : ''],
|
|
48
41
|
[
|
|
49
42
|
'caller-instructions',
|
|
@@ -3,9 +3,8 @@ import type { CompactHistoryMessage, CompactUserInput } from '../../session/json
|
|
|
3
3
|
export declare const DEFAULT_COMPACTION_FOCUS = "Create a durable continuation state for the next model invocation.";
|
|
4
4
|
export declare function buildNativeCompactionMessages(messages: readonly ModelMessage[], input?: {
|
|
5
5
|
focus?: string;
|
|
6
|
-
highDensityCompaction?: boolean;
|
|
7
6
|
}): ModelMessage[];
|
|
8
|
-
export declare function buildCompactionMessages(evidence: string
|
|
7
|
+
export declare function buildCompactionMessages(evidence: string): ModelMessage[];
|
|
9
8
|
export declare function buildCompactionInput(messages: readonly ModelMessage[], input: {
|
|
10
9
|
focus?: string;
|
|
11
10
|
maxTokens: number;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { countModelTokens, truncateModelText } from '../token-budget.js';
|
|
2
|
-
import { compactionPrompt
|
|
2
|
+
import { compactionPrompt } from '../../prompts/compact.js';
|
|
3
3
|
export const DEFAULT_COMPACTION_FOCUS = 'Create a durable continuation state for the next model invocation.';
|
|
4
4
|
export function buildNativeCompactionMessages(messages, input = {}) {
|
|
5
5
|
const focus = input.focus?.trim();
|
|
6
|
-
const contract =
|
|
6
|
+
const contract = compactionPrompt();
|
|
7
7
|
return [
|
|
8
8
|
...messages
|
|
9
9
|
.filter((message) => message.contextKind !== 'private')
|
|
@@ -21,13 +21,13 @@ export function buildNativeCompactionMessages(messages, input = {}) {
|
|
|
21
21
|
}
|
|
22
22
|
];
|
|
23
23
|
}
|
|
24
|
-
export function buildCompactionMessages(evidence
|
|
24
|
+
export function buildCompactionMessages(evidence) {
|
|
25
25
|
return [
|
|
26
26
|
{ role: 'user', content: evidence, contextKind: 'contextual' },
|
|
27
27
|
{
|
|
28
28
|
role: 'user',
|
|
29
29
|
contextKind: 'compact_control',
|
|
30
|
-
content:
|
|
30
|
+
content: compactionPrompt()
|
|
31
31
|
}
|
|
32
32
|
];
|
|
33
33
|
}
|
|
@@ -26,7 +26,7 @@ export type MarAgentEvent = MarAgentEventBase & ({
|
|
|
26
26
|
modelName: string;
|
|
27
27
|
} | {
|
|
28
28
|
type: 'model.performance';
|
|
29
|
-
latencyMs
|
|
29
|
+
latencyMs?: number | undefined;
|
|
30
30
|
outputTokens?: number | undefined;
|
|
31
31
|
outputDurationMs?: number | undefined;
|
|
32
32
|
tokensPerSecond?: number | undefined;
|