@myagentroam/agent 0.9.82 → 0.9.84
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/contracts.d.ts +17 -1
- package/dist/model/openai-responses.d.ts +7 -4
- package/dist/model/openai-responses.js +111 -59
- package/dist/prompts/compact.js +5 -4
- package/dist/prompts/index.d.ts +1 -3
- package/dist/prompts/index.js +1 -5
- package/dist/prompts/resources.d.ts +2 -1
- package/dist/prompts/resources.js +21 -10
- package/dist/runtime/compact.js +3 -1
- package/dist/runtime/context-projection.js +1 -0
- package/dist/sdk/agent.js +207 -54
- package/dist/session/jsonl-store.js +4 -2
- package/dist/subagent/session-controller.js +8 -7
- package/dist/tools/agent-wait.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { MarAgentModelConfiguration, MarAgentReasoningEffort, ModelCredentialAcquireReason } from './configuration.js';
|
|
2
|
+
export interface ModelContinuationCheckpoint {
|
|
3
|
+
readonly version: 1;
|
|
4
|
+
readonly protocol: 'OPENAI_RESPONSES';
|
|
5
|
+
readonly configurationHash: string;
|
|
6
|
+
readonly responseId: string;
|
|
7
|
+
readonly requestPropertiesHash: string;
|
|
8
|
+
readonly inputPrefixLength: number;
|
|
9
|
+
readonly inputPrefixHash: string;
|
|
10
|
+
}
|
|
2
11
|
export interface ClientToolDefinition {
|
|
3
12
|
name: string;
|
|
4
13
|
description: string;
|
|
@@ -11,7 +20,7 @@ export interface ClientToolDefinition {
|
|
|
11
20
|
export interface ModelMessage {
|
|
12
21
|
role: 'user' | 'assistant' | 'tool' | 'tool_call' | 'provider';
|
|
13
22
|
content: string;
|
|
14
|
-
contextKind?: 'environment';
|
|
23
|
+
contextKind?: 'environment' | 'private';
|
|
15
24
|
callId?: string;
|
|
16
25
|
name?: string;
|
|
17
26
|
arguments?: unknown;
|
|
@@ -146,11 +155,18 @@ export interface ModelAdapter {
|
|
|
146
155
|
readonly configuration: MarAgentModelConfiguration;
|
|
147
156
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
148
157
|
createTurnSession?(): ModelTurnSession;
|
|
158
|
+
/**
|
|
159
|
+
* Prepares an equivalent model rebind without mutating live state.
|
|
160
|
+
* The returned commit callback must be synchronous and non-throwing.
|
|
161
|
+
*/
|
|
162
|
+
prepareReconfigure?(configuration: MarAgentModelConfiguration): (() => void) | undefined;
|
|
163
|
+
restoreContinuation?(checkpoint: ModelContinuationCheckpoint): boolean;
|
|
149
164
|
close?(): Promise<void>;
|
|
150
165
|
}
|
|
151
166
|
export interface ModelTurnSession {
|
|
152
167
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
153
168
|
/** Drops incremental response state after a local context replacement. */
|
|
154
169
|
resetContinuation?(): void;
|
|
170
|
+
continuationCheckpoint?(): ModelContinuationCheckpoint | undefined;
|
|
155
171
|
close(): Promise<void>;
|
|
156
172
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MarAgentError } from '../error.js';
|
|
2
2
|
import { type MarAgentModelConfiguration, type ModelCredentialAcquireReason } from './configuration.js';
|
|
3
3
|
import WebSocket from 'ws';
|
|
4
|
-
import type { ModelAdapter, ModelAttemptDiagnostic, ModelEvent, ModelRequest, ModelTurnSession } from './contracts.js';
|
|
4
|
+
import type { ModelAdapter, ModelAttemptDiagnostic, ModelContinuationCheckpoint, ModelEvent, ModelRequest, ModelTurnSession } from './contracts.js';
|
|
5
5
|
export declare const OPENAI_RESPONSES_EARLY_EOF_REASON = "OPENAI_RESPONSES_EARLY_EOF";
|
|
6
6
|
export declare const OPENAI_RESPONSES_WEBSOCKET_CONNECTION_LIMIT_REASON = "OPENAI_RESPONSES_WEBSOCKET_CONNECTION_LIMIT";
|
|
7
7
|
type ResponsesRequestBody = Readonly<Record<string, unknown>> & {
|
|
@@ -11,6 +11,7 @@ interface ResponsesSessionState {
|
|
|
11
11
|
socket?: WebSocket;
|
|
12
12
|
endpoint?: string;
|
|
13
13
|
credentialRevision?: number;
|
|
14
|
+
credentialFingerprint?: string;
|
|
14
15
|
connectionId?: string;
|
|
15
16
|
proxyType?: 'DIRECT' | 'SOCKS5';
|
|
16
17
|
lastRequest?: ResponsesRequestBody;
|
|
@@ -18,18 +19,20 @@ interface ResponsesSessionState {
|
|
|
18
19
|
readonly responseId: string;
|
|
19
20
|
readonly outputItems: readonly Record<string, unknown>[];
|
|
20
21
|
};
|
|
22
|
+
restoredContinuation?: ModelContinuationCheckpoint;
|
|
21
23
|
}
|
|
22
24
|
interface PreparedResponsesRequest {
|
|
23
25
|
readonly fullBody: ResponsesRequestBody;
|
|
24
26
|
readonly body: ResponsesRequestBody;
|
|
25
27
|
readonly continuation: boolean;
|
|
26
|
-
readonly requiresExistingSocket: boolean;
|
|
27
28
|
}
|
|
28
29
|
export declare class OpenAiResponsesAdapter implements ModelAdapter {
|
|
29
30
|
#private;
|
|
30
|
-
readonly configuration: MarAgentModelConfiguration;
|
|
31
31
|
readonly prefixIdentity: `${string}-${string}-${string}-${string}-${string}`;
|
|
32
32
|
constructor(configuration: MarAgentModelConfiguration);
|
|
33
|
+
get configuration(): MarAgentModelConfiguration;
|
|
34
|
+
prepareReconfigure(configuration: MarAgentModelConfiguration): (() => void) | undefined;
|
|
35
|
+
restoreContinuation(checkpoint: ModelContinuationCheckpoint): boolean;
|
|
33
36
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
34
37
|
createTurnSession(): ModelTurnSession;
|
|
35
38
|
close(): Promise<void>;
|
|
@@ -47,6 +50,7 @@ declare class OpenAiResponsesTurnSession implements ModelTurnSession {
|
|
|
47
50
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
48
51
|
close(): Promise<void>;
|
|
49
52
|
resetContinuation(): void;
|
|
53
|
+
continuationCheckpoint(): ModelContinuationCheckpoint | undefined;
|
|
50
54
|
prepareRequest(request: ModelRequest): PreparedResponsesRequest;
|
|
51
55
|
fullRequestBody(): ResponsesRequestBody;
|
|
52
56
|
currentTurnState(): string | undefined;
|
|
@@ -61,7 +65,6 @@ declare class OpenAiResponsesTurnSession implements ModelTurnSession {
|
|
|
61
65
|
readonly reason: ModelCredentialAcquireReason;
|
|
62
66
|
readonly signal: AbortSignal;
|
|
63
67
|
readonly continuation: boolean;
|
|
64
|
-
readonly requiresExistingSocket: boolean;
|
|
65
68
|
readonly credentialAcquired: (revision: number) => void;
|
|
66
69
|
}): Promise<WebSocket>;
|
|
67
70
|
private connectWebSocket;
|
|
@@ -13,13 +13,33 @@ const X_CODEX_TURN_STATE = 'x-codex-turn-state';
|
|
|
13
13
|
const RESPONSES_LITE_HEADER = 'x-openai-internal-codex-responses-lite';
|
|
14
14
|
const RESPONSES_LITE_METADATA = 'ws_request_header_x_openai_internal_codex_responses_lite';
|
|
15
15
|
export class OpenAiResponsesAdapter {
|
|
16
|
-
configuration;
|
|
17
16
|
prefixIdentity = randomUUID();
|
|
18
17
|
#cachedState = {};
|
|
19
18
|
#cacheLeased = false;
|
|
20
19
|
#closed = false;
|
|
20
|
+
#configuration;
|
|
21
21
|
constructor(configuration) {
|
|
22
|
-
this
|
|
22
|
+
this.#configuration = configuration;
|
|
23
|
+
}
|
|
24
|
+
get configuration() {
|
|
25
|
+
return this.#configuration;
|
|
26
|
+
}
|
|
27
|
+
prepareReconfigure(configuration) {
|
|
28
|
+
if (!sameResponsesAdapterConfiguration(this.#configuration, configuration))
|
|
29
|
+
return undefined;
|
|
30
|
+
return () => {
|
|
31
|
+
this.#configuration = configuration;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
restoreContinuation(checkpoint) {
|
|
35
|
+
if (this.#cacheLeased ||
|
|
36
|
+
this.#cachedState.lastRequest !== undefined ||
|
|
37
|
+
this.#cachedState.lastResponse !== undefined ||
|
|
38
|
+
checkpoint.protocol !== 'OPENAI_RESPONSES' ||
|
|
39
|
+
checkpoint.configurationHash !== responsesConfigurationHash(this.#configuration))
|
|
40
|
+
return false;
|
|
41
|
+
this.#cachedState.restoredContinuation = checkpoint;
|
|
42
|
+
return true;
|
|
23
43
|
}
|
|
24
44
|
async *start(request, signal) {
|
|
25
45
|
const turn = new OpenAiResponsesTurnSession(this, {}, false, true);
|
|
@@ -53,14 +73,13 @@ export class OpenAiResponsesAdapter {
|
|
|
53
73
|
return;
|
|
54
74
|
}
|
|
55
75
|
if (state.socket !== undefined && state.socket.readyState !== WebSocket.OPEN)
|
|
56
|
-
invalidateResponsesWebSocket(state,
|
|
76
|
+
invalidateResponsesWebSocket(state, false);
|
|
57
77
|
this.#cachedState = state;
|
|
58
78
|
this.#cacheLeased = false;
|
|
59
79
|
}
|
|
60
80
|
async *startTurn(request, signal, turn, prepared) {
|
|
61
81
|
let useContinuation = prepared.continuation;
|
|
62
82
|
let requestBody = prepared.body;
|
|
63
|
-
let requiresExistingSocket = prepared.requiresExistingSocket;
|
|
64
83
|
let credentialReason = 'REQUEST';
|
|
65
84
|
let unauthorizedRetryUsed = false;
|
|
66
85
|
let invalidContinuationFallbackUsed = false;
|
|
@@ -87,7 +106,7 @@ export class OpenAiResponsesAdapter {
|
|
|
87
106
|
credentialReason: attemptCredentialReason
|
|
88
107
|
});
|
|
89
108
|
try {
|
|
90
|
-
for await (const event of this.startAttempt(request, signal, requestBody, useContinuation, credentialReason,
|
|
109
|
+
for await (const event of this.startAttempt(request, signal, requestBody, useContinuation, credentialReason, turn)) {
|
|
91
110
|
if (!sawSemanticEvent && !isSemanticEvent(event)) {
|
|
92
111
|
prelude.push(event);
|
|
93
112
|
continue;
|
|
@@ -143,7 +162,6 @@ export class OpenAiResponsesAdapter {
|
|
|
143
162
|
invalidContinuationFallbackUsed = true;
|
|
144
163
|
useContinuation = false;
|
|
145
164
|
requestBody = prepared.fullBody;
|
|
146
|
-
requiresExistingSocket = false;
|
|
147
165
|
turn.invalidateWebSocket();
|
|
148
166
|
credentialReason = 'RECONNECT';
|
|
149
167
|
await reportFailure(true, {
|
|
@@ -192,14 +210,13 @@ export class OpenAiResponsesAdapter {
|
|
|
192
210
|
: 'REQUEST';
|
|
193
211
|
useContinuation = false;
|
|
194
212
|
requestBody = prepared.fullBody;
|
|
195
|
-
requiresExistingSocket = false;
|
|
196
213
|
if (error instanceof MarAgentError && error.details?.transport === 'WEBSOCKET')
|
|
197
214
|
turn.invalidateWebSocket();
|
|
198
215
|
await waitForModelRetry(retryAfter(error), attempt, signal, delayMs);
|
|
199
216
|
}
|
|
200
217
|
}
|
|
201
218
|
}
|
|
202
|
-
async *startAttempt(request, signal, body, continuation, credentialReason,
|
|
219
|
+
async *startAttempt(request, signal, body, continuation, credentialReason, turn) {
|
|
203
220
|
let latestCredentialRevision;
|
|
204
221
|
const fetchResponse = async (requestBody) => {
|
|
205
222
|
const serializedBody = JSON.stringify(requestBody);
|
|
@@ -233,7 +250,6 @@ export class OpenAiResponsesAdapter {
|
|
|
233
250
|
reason: credentialReason,
|
|
234
251
|
signal,
|
|
235
252
|
continuation,
|
|
236
|
-
requiresExistingSocket,
|
|
237
253
|
turn,
|
|
238
254
|
credentialAcquired: (revision) => (latestCredentialRevision = revision)
|
|
239
255
|
});
|
|
@@ -517,6 +533,15 @@ export class OpenAiResponsesAdapter {
|
|
|
517
533
|
});
|
|
518
534
|
}
|
|
519
535
|
}
|
|
536
|
+
function sameResponsesAdapterConfiguration(current, next) {
|
|
537
|
+
const { apiKey: currentApiKey, credentialProvider: currentCredentialProvider, imageGeneration: _currentImageGeneration, ...currentTransport } = current;
|
|
538
|
+
const { apiKey: nextApiKey, credentialProvider: nextCredentialProvider, imageGeneration: _nextImageGeneration, ...nextTransport } = next;
|
|
539
|
+
const credentialsAreRebindable = (currentCredentialProvider !== undefined && nextCredentialProvider !== undefined) ||
|
|
540
|
+
(currentApiKey !== undefined &&
|
|
541
|
+
nextApiKey !== undefined &&
|
|
542
|
+
currentApiKey.reveal() === nextApiKey.reveal());
|
|
543
|
+
return credentialsAreRebindable && isDeepStrictEqual(currentTransport, nextTransport);
|
|
544
|
+
}
|
|
520
545
|
class OpenAiResponsesTurnSession {
|
|
521
546
|
adapter;
|
|
522
547
|
state;
|
|
@@ -561,7 +586,7 @@ class OpenAiResponsesTurnSession {
|
|
|
561
586
|
for await (const event of this.adapter.startTurn({
|
|
562
587
|
...request,
|
|
563
588
|
onAttemptDiagnostic: async (diagnostic) => request.onAttemptDiagnostic?.({ ...diagnostic, prewarm: true })
|
|
564
|
-
}, signal, this, { fullBody: warmBody, body: warmBody, continuation: false
|
|
589
|
+
}, signal, this, { fullBody: warmBody, body: warmBody, continuation: false })) {
|
|
565
590
|
if (event.type === 'response.started' && event.responseId !== 'unknown')
|
|
566
591
|
warmId = event.responseId;
|
|
567
592
|
if (event.type === 'completed')
|
|
@@ -607,6 +632,7 @@ class OpenAiResponsesTurnSession {
|
|
|
607
632
|
(outputItems.length > 0 || !sawUnrepresentedOutput)) {
|
|
608
633
|
this.state.lastRequest = prepared.fullBody;
|
|
609
634
|
this.state.lastResponse = { responseId, outputItems };
|
|
635
|
+
delete this.state.restoredContinuation;
|
|
610
636
|
}
|
|
611
637
|
else
|
|
612
638
|
clearResponsesContinuation(this.state);
|
|
@@ -629,17 +655,31 @@ class OpenAiResponsesTurnSession {
|
|
|
629
655
|
resetContinuation() {
|
|
630
656
|
clearResponsesContinuation(this.state);
|
|
631
657
|
}
|
|
658
|
+
continuationCheckpoint() {
|
|
659
|
+
const request = this.state.lastRequest;
|
|
660
|
+
const response = this.state.lastResponse;
|
|
661
|
+
if (request === undefined || response === undefined)
|
|
662
|
+
return this.state.restoredContinuation;
|
|
663
|
+
const baseline = [...request.input, ...response.outputItems];
|
|
664
|
+
return {
|
|
665
|
+
version: 1,
|
|
666
|
+
protocol: 'OPENAI_RESPONSES',
|
|
667
|
+
configurationHash: responsesConfigurationHash(this.adapter.configuration),
|
|
668
|
+
responseId: response.responseId,
|
|
669
|
+
requestPropertiesHash: responsesRequestPropertiesHash(request),
|
|
670
|
+
inputPrefixLength: baseline.length,
|
|
671
|
+
inputPrefixHash: hashResponsesValue(baseline)
|
|
672
|
+
};
|
|
673
|
+
}
|
|
632
674
|
prepareRequest(request) {
|
|
633
675
|
if (this.adapter.configuration.responsesTransport?.transport === 'WEBSOCKET' &&
|
|
634
676
|
this.state.socket !== undefined &&
|
|
635
677
|
this.state.socket.readyState !== WebSocket.OPEN)
|
|
636
|
-
invalidateResponsesWebSocket(this.state,
|
|
678
|
+
invalidateResponsesWebSocket(this.state, false);
|
|
637
679
|
const fullBody = responsesRequestBody(this.adapter.configuration, request, request.messages, this.adapter.prefixIdentity);
|
|
638
680
|
if (this.adapter.configuration.responsesPreviousResponseId) {
|
|
639
681
|
const incremental = incrementalResponsesInput(this.state, fullBody);
|
|
640
|
-
if (incremental !== undefined
|
|
641
|
-
(this.adapter.configuration.responsesTransport?.transport !== 'WEBSOCKET' ||
|
|
642
|
-
this.state.socket?.readyState === WebSocket.OPEN)) {
|
|
682
|
+
if (incremental !== undefined) {
|
|
643
683
|
const responseId = this.state.lastResponse.responseId;
|
|
644
684
|
delete this.state.lastResponse;
|
|
645
685
|
return {
|
|
@@ -649,8 +689,7 @@ class OpenAiResponsesTurnSession {
|
|
|
649
689
|
input: incremental,
|
|
650
690
|
previous_response_id: responseId
|
|
651
691
|
},
|
|
652
|
-
continuation: true
|
|
653
|
-
requiresExistingSocket: this.adapter.configuration.responsesTransport?.transport === 'WEBSOCKET'
|
|
692
|
+
continuation: true
|
|
654
693
|
};
|
|
655
694
|
}
|
|
656
695
|
if (this.allowExternalContinuation &&
|
|
@@ -664,16 +703,14 @@ class OpenAiResponsesTurnSession {
|
|
|
664
703
|
input: deltaBody.input,
|
|
665
704
|
previous_response_id: request.continuation.previousResponseId
|
|
666
705
|
},
|
|
667
|
-
continuation: true
|
|
668
|
-
requiresExistingSocket: false
|
|
706
|
+
continuation: true
|
|
669
707
|
};
|
|
670
708
|
}
|
|
671
709
|
}
|
|
672
710
|
return {
|
|
673
711
|
fullBody,
|
|
674
712
|
body: fullBody,
|
|
675
|
-
continuation: false
|
|
676
|
-
requiresExistingSocket: false
|
|
713
|
+
continuation: false
|
|
677
714
|
};
|
|
678
715
|
}
|
|
679
716
|
fullRequestBody() {
|
|
@@ -714,14 +751,9 @@ class OpenAiResponsesTurnSession {
|
|
|
714
751
|
async webSocketForRequest(input) {
|
|
715
752
|
const endpoint = webSocketEndpoint(input.configuration.baseUrl);
|
|
716
753
|
const existingSocket = this.state.socket;
|
|
717
|
-
if (input.requiresExistingSocket &&
|
|
718
|
-
(existingSocket?.readyState !== WebSocket.OPEN || this.state.endpoint !== endpoint)) {
|
|
719
|
-
invalidateResponsesWebSocket(this.state, true);
|
|
720
|
-
throw unavailableContinuationConnection();
|
|
721
|
-
}
|
|
722
754
|
if (existingSocket !== undefined &&
|
|
723
755
|
(existingSocket.readyState !== WebSocket.OPEN || this.state.endpoint !== endpoint))
|
|
724
|
-
invalidateResponsesWebSocket(this.state,
|
|
756
|
+
invalidateResponsesWebSocket(this.state, false);
|
|
725
757
|
if (this.state.socket === undefined) {
|
|
726
758
|
const handshakeCredential = await acquireResponsesCredential(input.configuration, input.reason);
|
|
727
759
|
input.credentialAcquired(handshakeCredential.revision);
|
|
@@ -729,10 +761,9 @@ class OpenAiResponsesTurnSession {
|
|
|
729
761
|
}
|
|
730
762
|
const requestCredential = await acquireResponsesCredential(input.configuration, input.reason);
|
|
731
763
|
input.credentialAcquired(requestCredential.revision);
|
|
732
|
-
if (requestCredential.revision !== this.state.credentialRevision
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
throw unavailableContinuationConnection();
|
|
764
|
+
if (requestCredential.revision !== this.state.credentialRevision ||
|
|
765
|
+
responsesCredentialFingerprint(requestCredential) !== this.state.credentialFingerprint) {
|
|
766
|
+
invalidateResponsesWebSocket(this.state, false);
|
|
736
767
|
await this.connectWebSocket(endpoint, requestCredential, input.promptCacheKey, input.signal);
|
|
737
768
|
}
|
|
738
769
|
if (input.signal.aborted) {
|
|
@@ -787,6 +818,7 @@ class OpenAiResponsesTurnSession {
|
|
|
787
818
|
this.state.socket = socket;
|
|
788
819
|
this.state.endpoint = endpoint;
|
|
789
820
|
this.state.credentialRevision = credential.revision;
|
|
821
|
+
this.state.credentialFingerprint = responsesCredentialFingerprint(credential);
|
|
790
822
|
this.state.connectionId = connectionId;
|
|
791
823
|
this.state.proxyType = proxyType;
|
|
792
824
|
}
|
|
@@ -864,9 +896,18 @@ function uuidV5(namespace, value) {
|
|
|
864
896
|
function incrementalResponsesInput(state, current) {
|
|
865
897
|
const previous = state.lastRequest;
|
|
866
898
|
const completion = state.lastResponse;
|
|
867
|
-
if (previous === undefined ||
|
|
868
|
-
|
|
869
|
-
|
|
899
|
+
if (previous === undefined || completion === undefined) {
|
|
900
|
+
const restored = state.restoredContinuation;
|
|
901
|
+
if (restored === undefined ||
|
|
902
|
+
restored.requestPropertiesHash !== responsesRequestPropertiesHash(current) ||
|
|
903
|
+
current.input.length < restored.inputPrefixLength ||
|
|
904
|
+
restored.inputPrefixHash !==
|
|
905
|
+
hashResponsesValue(current.input.slice(0, restored.inputPrefixLength)))
|
|
906
|
+
return undefined;
|
|
907
|
+
state.lastResponse = { responseId: restored.responseId, outputItems: [] };
|
|
908
|
+
return current.input.slice(restored.inputPrefixLength);
|
|
909
|
+
}
|
|
910
|
+
if (!responsesRequestPropertiesMatch(previous, current))
|
|
870
911
|
return undefined;
|
|
871
912
|
const baseline = [...previous.input, ...completion.outputItems];
|
|
872
913
|
if (current.input.length < baseline.length)
|
|
@@ -877,31 +918,53 @@ function incrementalResponsesInput(state, current) {
|
|
|
877
918
|
return current.input.slice(baseline.length);
|
|
878
919
|
}
|
|
879
920
|
function responsesRequestPropertiesMatch(previous, current) {
|
|
880
|
-
|
|
881
|
-
'model',
|
|
882
|
-
'instructions',
|
|
883
|
-
'tools',
|
|
884
|
-
'tool_choice',
|
|
885
|
-
'parallel_tool_calls',
|
|
886
|
-
'reasoning',
|
|
887
|
-
'store',
|
|
888
|
-
'stream',
|
|
889
|
-
'include',
|
|
890
|
-
'prompt_cache_key',
|
|
891
|
-
'text',
|
|
892
|
-
'client_metadata'
|
|
893
|
-
];
|
|
894
|
-
return keys.every((key) => isDeepStrictEqual(previous[key], current[key]));
|
|
921
|
+
return RESPONSES_REQUEST_PROPERTY_KEYS.every((key) => isDeepStrictEqual(previous[key], current[key]));
|
|
895
922
|
}
|
|
896
923
|
function clearResponsesContinuation(state) {
|
|
897
924
|
delete state.lastRequest;
|
|
898
925
|
delete state.lastResponse;
|
|
926
|
+
delete state.restoredContinuation;
|
|
927
|
+
}
|
|
928
|
+
const RESPONSES_REQUEST_PROPERTY_KEYS = [
|
|
929
|
+
'model',
|
|
930
|
+
'instructions',
|
|
931
|
+
'tools',
|
|
932
|
+
'tool_choice',
|
|
933
|
+
'parallel_tool_calls',
|
|
934
|
+
'reasoning',
|
|
935
|
+
'store',
|
|
936
|
+
'stream',
|
|
937
|
+
'include',
|
|
938
|
+
'prompt_cache_key',
|
|
939
|
+
'text',
|
|
940
|
+
'client_metadata'
|
|
941
|
+
];
|
|
942
|
+
function responsesRequestPropertiesHash(request) {
|
|
943
|
+
return hashResponsesValue(Object.fromEntries(RESPONSES_REQUEST_PROPERTY_KEYS.map((key) => [key, request[key]])));
|
|
944
|
+
}
|
|
945
|
+
function hashResponsesValue(value) {
|
|
946
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
947
|
+
}
|
|
948
|
+
function responsesConfigurationHash(configuration) {
|
|
949
|
+
const { apiKey: _apiKey, credentialProvider: _credentialProvider, imageGeneration: _imageGeneration, ...identity } = configuration;
|
|
950
|
+
return hashResponsesValue(identity);
|
|
951
|
+
}
|
|
952
|
+
function responsesCredentialFingerprint(credential) {
|
|
953
|
+
return hashResponsesValue({
|
|
954
|
+
bearer: credential.bearer.reveal(),
|
|
955
|
+
headers: Object.fromEntries(Object.entries(credential.headers).map(([name, value]) => [
|
|
956
|
+
name,
|
|
957
|
+
typeof value === 'string' ? value : value.reveal()
|
|
958
|
+
])),
|
|
959
|
+
socks5Url: credential.socks5Url?.reveal()
|
|
960
|
+
});
|
|
899
961
|
}
|
|
900
962
|
function invalidateResponsesWebSocket(state, clearContinuation) {
|
|
901
963
|
const socket = state.socket;
|
|
902
964
|
delete state.socket;
|
|
903
965
|
delete state.endpoint;
|
|
904
966
|
delete state.credentialRevision;
|
|
967
|
+
delete state.credentialFingerprint;
|
|
905
968
|
delete state.connectionId;
|
|
906
969
|
delete state.proxyType;
|
|
907
970
|
if (clearContinuation)
|
|
@@ -911,16 +974,6 @@ function invalidateResponsesWebSocket(state, clearContinuation) {
|
|
|
911
974
|
socket.readyState !== WebSocket.CLOSING)
|
|
912
975
|
terminateWebSocket(socket);
|
|
913
976
|
}
|
|
914
|
-
function unavailableContinuationConnection() {
|
|
915
|
-
return new MarAgentError('previous_response_not_found', 'The previous response is unavailable on the current WebSocket connection.', {
|
|
916
|
-
details: {
|
|
917
|
-
reason: 'OPENAI_RESPONSES_CONTINUATION_CONNECTION_UNAVAILABLE',
|
|
918
|
-
transport: 'WEBSOCKET',
|
|
919
|
-
continuation: true
|
|
920
|
-
},
|
|
921
|
-
retryable: true
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
977
|
function turnStateFromEvent(event) {
|
|
925
978
|
for (const headers of [
|
|
926
979
|
record(event.headers),
|
|
@@ -958,7 +1011,6 @@ async function* webSocketResponseEvents(input) {
|
|
|
958
1011
|
reason: input.reason,
|
|
959
1012
|
signal: input.signal,
|
|
960
1013
|
continuation: input.continuation,
|
|
961
|
-
requiresExistingSocket: input.requiresExistingSocket,
|
|
962
1014
|
credentialAcquired: input.credentialAcquired
|
|
963
1015
|
});
|
|
964
1016
|
const body = input.turn.withTurnStateMetadata(input.body);
|
package/dist/prompts/compact.js
CHANGED
|
@@ -9,7 +9,7 @@ Preserve, when present:
|
|
|
9
9
|
- confirmed decisions, constraints, interfaces, invariants, assumptions that still matter, and exact identifiers;
|
|
10
10
|
- completed actions and edits with exact file paths, symbols, commands, migrations, or external state changes;
|
|
11
11
|
- verification actually run and its result, important tool evidence, reproducible errors, failed approaches and what they proved;
|
|
12
|
-
- unresolved questions, blockers, risks, uncommitted changes, Todo/plan state, pending approvals
|
|
12
|
+
- unresolved questions, blockers, risks, uncommitted changes, Todo/plan state, and pending approvals;
|
|
13
13
|
- the next concrete action and the evidence still required before completion.
|
|
14
14
|
|
|
15
15
|
Distinguish clearly between completed, attempted, planned, inferred, and unverified work. Newer user instructions override older ones; retain superseded instructions only when needed to explain current state. Never invent completion, test results, file contents, permissions, or external effects.
|
|
@@ -17,6 +17,7 @@ Distinguish clearly between completed, attempted, planned, inferred, and unverif
|
|
|
17
17
|
Remove aggressively:
|
|
18
18
|
- greetings, acknowledgements, progress chatter, repeated instructions, repeated status, and conclusions already represented once;
|
|
19
19
|
- user-level, project-level, or directory-level AGENTS.md instructions and constraints derived only from those files, because the next Execution reloads and replaces them separately;
|
|
20
|
+
- retained process/Subagent inventory, IDs, status, commands, cwd, and reuse instructions, because Runtime reloads and replaces that state separately;
|
|
20
21
|
- raw long logs, large source excerpts, verbose tool payloads, duplicated search/read output, and implementation detail recoverable from named files or commands;
|
|
21
22
|
- abandoned hypotheses and failed attempts that do not constrain the next action;
|
|
22
23
|
- hidden reasoning, secrets, credentials, authorization values, and unrelated conversation.
|
|
@@ -52,7 +53,7 @@ branch changed -> inherited_evidence=unverified
|
|
|
52
53
|
- direct user/caller authorization、prohibition、exclusion 保留 exact action/scope,不概括成更宽泛边界。
|
|
53
54
|
|
|
54
55
|
2. 当前状态 / Current state
|
|
55
|
-
- 对象:file、symbol、feature、migration、test、worktree、
|
|
56
|
+
- 对象:file、symbol、feature、migration、test、worktree、selected Skill、Todo/plan、approval、external side effect。retained process/subagent inventory 由 Runtime 重建,不进入 IR。
|
|
56
57
|
- 分离 completion(pending / in_progress / completed / blocked)、epistemic(observed / verified / inferred / proposed / disproved)、freshness(current / stale / superseded)、scope(turn / worktree / base / branch / external system);仅在消歧时标注。
|
|
57
58
|
- 明确 completed、not started、unverified、active。相同精度下选择更短状态原子;中文、English 均可,如 根因已证、edit=pending、tests=not run。
|
|
58
59
|
- selected Skill | 保留仍适用的 skill name + 续作必需约束;Todo/plan | 保留 revision、item/status、当前最小 step。
|
|
@@ -70,7 +71,7 @@ branch changed -> inherited_evidence=unverified
|
|
|
70
71
|
- 下一 Agent 无法使用、仅供 local storage validation 的 integrity hash:删除。
|
|
71
72
|
|
|
72
73
|
5. 未闭环 / Open loops
|
|
73
|
-
- unresolved、unverified、blocker、pending approval
|
|
74
|
+
- unresolved、unverified、blocker、pending approval、Todo/plan current step、仍需 evidence。
|
|
74
75
|
- 只保留已确定的最小 next action + preconditions;不复制可重算的长 plan。
|
|
75
76
|
|
|
76
77
|
编译规则:
|
|
@@ -85,7 +86,7 @@ observed HEAD、validated base、target commit、current worktree 不得折叠
|
|
|
85
86
|
|
|
86
87
|
语言:标题/一般叙述跟随 latest active user instructions 和当前任务主语言;每条 record 按局部精度与密度选择中文、English 或 mixed phrasing。保留 established term/exact identifier,不为语言统一展开或翻译。English codebase 不等于 English handoff。
|
|
87
88
|
|
|
88
|
-
删除 greetings、progress chatter、过程叙事、重复事实、无剩余约束的 superseded hypothesis、长日志、source body、verbose payload、可从精确 path/command 廉价恢复的正文。删除 user/project/directory AGENTS.md 正文及仅由这些文件派生的 constraint,包括其中 exclusions;下一 Execution
|
|
89
|
+
删除 greetings、progress chatter、过程叙事、重复事实、无剩余约束的 superseded hypothesis、长日志、source body、verbose payload、可从精确 path/command 廉价恢复的正文。删除 user/project/directory AGENTS.md 正文及仅由这些文件派生的 constraint,包括其中 exclusions;下一 Execution 会重新加载并整体替换。删除 retained process/Subagent inventory、ID、status、command、cwd 和 reuse instruction;Runtime 会重新加载并替换。不得保留 hidden reasoning、secret、credential、无关内容。
|
|
89
90
|
|
|
90
91
|
只输出 compact Markdown IR。`;
|
|
91
92
|
}
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { ExecutionMode } from '../sdk/types.js';
|
|
2
2
|
import { type ExecutionBudgetPromptState } from './execution-budget.js';
|
|
3
|
-
import { type RetainedSessionResources } from './resources.js';
|
|
4
3
|
import { type CurrentAgentModel, type SubagentModelOption } from './subagent.js';
|
|
5
|
-
export declare const MAR_AGENT_PROMPT_VERSION = "1.
|
|
4
|
+
export declare const MAR_AGENT_PROMPT_VERSION = "1.50";
|
|
6
5
|
export declare function buildSystemPrompt(input: {
|
|
7
6
|
mode: ExecutionMode;
|
|
8
7
|
platform: string;
|
|
@@ -16,7 +15,6 @@ export declare function buildSystemPrompt(input: {
|
|
|
16
15
|
tools: readonly string[];
|
|
17
16
|
currentAgentModel?: CurrentAgentModel;
|
|
18
17
|
subagentModels?: readonly SubagentModelOption[];
|
|
19
|
-
retainedSessionResources?: RetainedSessionResources;
|
|
20
18
|
executionBudget?: ExecutionBudgetPromptState;
|
|
21
19
|
highDensityCompaction?: boolean;
|
|
22
20
|
extension?: string | undefined;
|
package/dist/prompts/index.js
CHANGED
|
@@ -3,10 +3,9 @@ import { environmentPrompt, identityPrompt, instructionPriorityPrompt, workspace
|
|
|
3
3
|
import { executionBudgetPrompt } from './execution-budget.js';
|
|
4
4
|
import { modePrompt } from './modes.js';
|
|
5
5
|
import { outputStylePrompt } from './output.js';
|
|
6
|
-
import { retainedSessionResourcesPrompt } from './resources.js';
|
|
7
6
|
import { subagentModelOptionsPrompt, subagentPrompt, currentAgentModelPrompt } from './subagent.js';
|
|
8
7
|
import { interactionPrompt, longRunningPrompt, safetyPrompt, toolUsagePrompt, workflowPrompt } from './workflow.js';
|
|
9
|
-
export const MAR_AGENT_PROMPT_VERSION = '1.
|
|
8
|
+
export const MAR_AGENT_PROMPT_VERSION = '1.50';
|
|
10
9
|
export function buildSystemPrompt(input) {
|
|
11
10
|
const toolNames = new Set(input.tools);
|
|
12
11
|
const hasLongRunningCapability = [
|
|
@@ -28,9 +27,6 @@ export function buildSystemPrompt(input) {
|
|
|
28
27
|
environmentPrompt(input),
|
|
29
28
|
input.tools.length > 0 ? toolUsagePrompt(input.tools) : '',
|
|
30
29
|
hasLongRunningCapability ? longRunningPrompt(input.tools) : '',
|
|
31
|
-
input.retainedSessionResources
|
|
32
|
-
? retainedSessionResourcesPrompt(input.retainedSessionResources)
|
|
33
|
-
: '',
|
|
34
30
|
input.executionBudget ? executionBudgetPrompt(input.executionBudget) : '',
|
|
35
31
|
modePrompt(input.mode, {
|
|
36
32
|
questionAvailable: toolNames.has('question'),
|
|
@@ -12,4 +12,5 @@ export interface RetainedSessionResources {
|
|
|
12
12
|
description: string;
|
|
13
13
|
}[];
|
|
14
14
|
}
|
|
15
|
-
export declare function
|
|
15
|
+
export declare function retainedSessionResourcesSnapshot(input: RetainedSessionResources): string | undefined;
|
|
16
|
+
export declare function retainedSessionResourcesUpdate(snapshot: string | undefined, previousSnapshot: string | undefined, previousMayContainResources?: boolean): string | undefined;
|
|
@@ -1,31 +1,42 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function retainedSessionResourcesSnapshot(input) {
|
|
2
2
|
if (input.processes.length === 0 && input.subagents.length === 0)
|
|
3
|
-
return
|
|
3
|
+
return undefined;
|
|
4
|
+
const processes = [...input.processes].sort((left, right) => left.processId.localeCompare(right.processId));
|
|
5
|
+
const subagents = [...input.subagents].sort((left, right) => left.agentId.localeCompare(right.agentId));
|
|
4
6
|
return [
|
|
5
7
|
'# Retained Session resources',
|
|
6
8
|
'These resources belong to this Session and survived the previous Execution. Reuse their stable IDs instead of starting duplicate processes or subagents.',
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
: 'Cancelling a managed process releases that process resource.',
|
|
10
|
-
input.subagents.length === 0
|
|
9
|
+
processes.length === 0 ? '' : 'Cancelling a managed process releases that process resource.',
|
|
10
|
+
subagents.length === 0
|
|
11
11
|
? ''
|
|
12
12
|
: 'agent_cancel stops only the current child turn and retains its Session for later messages or explicit deletion.',
|
|
13
13
|
input.idleTtlMs === undefined
|
|
14
14
|
? ''
|
|
15
15
|
: `When the Session becomes idle again, unclaimed resources are retained for up to ${input.idleTtlMs} ms.`,
|
|
16
|
-
|
|
16
|
+
processes.length === 0
|
|
17
17
|
? ''
|
|
18
18
|
: [
|
|
19
19
|
'Managed processes:',
|
|
20
|
-
...
|
|
20
|
+
...processes.map((process) => `- processId: ${process.processId}; status: ${process.status}; cwd: ${process.cwd}; command: ${process.command}`)
|
|
21
21
|
].join('\n'),
|
|
22
|
-
|
|
22
|
+
subagents.length === 0
|
|
23
23
|
? ''
|
|
24
24
|
: [
|
|
25
25
|
'Subagents:',
|
|
26
|
-
...
|
|
26
|
+
...subagents.map((task) => `- agentId: ${task.agentId}; status: ${task.status}; description: ${task.description}`)
|
|
27
27
|
].join('\n')
|
|
28
28
|
]
|
|
29
29
|
.filter(Boolean)
|
|
30
30
|
.join('\n');
|
|
31
31
|
}
|
|
32
|
+
export function retainedSessionResourcesUpdate(snapshot, previousSnapshot, previousMayContainResources = false) {
|
|
33
|
+
if (!previousMayContainResources && snapshot === previousSnapshot)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!snapshot)
|
|
36
|
+
return previousSnapshot || previousMayContainResources
|
|
37
|
+
? 'The previously provided retained Session resources no longer apply. No retained processes or subagents remain.'
|
|
38
|
+
: undefined;
|
|
39
|
+
return previousSnapshot || previousMayContainResources
|
|
40
|
+
? `This replaces all previously provided retained Session resources.\n\n${snapshot}`
|
|
41
|
+
: snapshot;
|
|
42
|
+
}
|
package/dist/runtime/compact.js
CHANGED
|
@@ -95,7 +95,9 @@ export function buildNativeCompactionMessages(messages, input = {}) {
|
|
|
95
95
|
const focus = input.focus?.trim();
|
|
96
96
|
const contract = input.highDensityCompaction ? highDensityCompactionPrompt() : compactionPrompt();
|
|
97
97
|
return [
|
|
98
|
-
...messages
|
|
98
|
+
...messages
|
|
99
|
+
.filter((message) => message.contextKind !== 'private')
|
|
100
|
+
.map((message) => {
|
|
99
101
|
if (!message.images?.length)
|
|
100
102
|
return message;
|
|
101
103
|
const textOnly = { ...message };
|
package/dist/sdk/agent.js
CHANGED
|
@@ -18,6 +18,7 @@ import { DEFAULT_COMPACTION_FOCUS, CompactionOperation, buildCompactionInput, bu
|
|
|
18
18
|
import { buildSystemPrompt, MAR_AGENT_PROMPT_VERSION } from '../prompts/index.js';
|
|
19
19
|
import { buildEnvironmentContext } from '../prompts/core.js';
|
|
20
20
|
import { modelOutputContinuationMessage } from '../prompts/output.js';
|
|
21
|
+
import { retainedSessionResourcesSnapshot, retainedSessionResourcesUpdate } from '../prompts/resources.js';
|
|
21
22
|
import { environmentContextUpdate, restoreEnvironmentState } from '../runtime/environment-context.js';
|
|
22
23
|
import { childSubagentInstruction } from '../prompts/subagent.js';
|
|
23
24
|
import { sessionTitlePrompt } from '../prompts/title.js';
|
|
@@ -54,6 +55,7 @@ export async function createMarAgent(options) {
|
|
|
54
55
|
throw new MarAgentError('MAR_AGENT_HOST_INCOMPATIBLE', 'Host mode does not match Agent mode.');
|
|
55
56
|
const store = await JsonlSessionStore.open(description.homeDirectory);
|
|
56
57
|
const active = new Map();
|
|
58
|
+
const activeExecutionAdapters = new Set();
|
|
57
59
|
const executionResults = new Map();
|
|
58
60
|
const sessionRuntimes = new Map();
|
|
59
61
|
let disposed = false;
|
|
@@ -249,22 +251,24 @@ export async function createMarAgent(options) {
|
|
|
249
251
|
const sessionState = await store.readExecutionState(sessionId);
|
|
250
252
|
const todo = TodoStore.restore(sessionState.todo);
|
|
251
253
|
const contextRecords = sessionState.contextRecords;
|
|
252
|
-
const
|
|
253
|
-
const
|
|
254
|
-
let
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
generation:
|
|
254
|
+
const contextualWorldStateGeneration = latestContextualWorldStateGeneration(contextRecords);
|
|
255
|
+
const restoredContextualWorldState = sessionRuntime.contextualWorldState;
|
|
256
|
+
let contextualWorldState;
|
|
257
|
+
if (restoredContextualWorldState === undefined ||
|
|
258
|
+
restoredContextualWorldState.generation !== contextualWorldStateGeneration) {
|
|
259
|
+
contextualWorldState = {
|
|
260
|
+
generation: contextualWorldStateGeneration,
|
|
259
261
|
initialized: false,
|
|
260
|
-
|
|
262
|
+
instructionSnapshot: undefined,
|
|
263
|
+
resourceSnapshot: undefined,
|
|
261
264
|
entries: []
|
|
262
265
|
};
|
|
263
|
-
sessionRuntime.
|
|
266
|
+
sessionRuntime.contextualWorldState = contextualWorldState;
|
|
264
267
|
}
|
|
265
268
|
else
|
|
266
|
-
|
|
269
|
+
contextualWorldState = restoredContextualWorldState;
|
|
267
270
|
const compactMayContainAgentInstructions = contextRecords.some((record) => compactSummaryFromCheckpoint(record) !== undefined);
|
|
271
|
+
const compactMayContainRetainedResources = compactHistoryMayContainRetainedResources(contextRecords);
|
|
268
272
|
const restored = await restoreMessages(store, sessionId, contextRecords, {
|
|
269
273
|
includeImages: mode !== 'compact'
|
|
270
274
|
});
|
|
@@ -351,6 +355,7 @@ export async function createMarAgent(options) {
|
|
|
351
355
|
const titleModel = [...executionModels.values()].find((model) => model.titleGeneration) ??
|
|
352
356
|
executionModels.get(input.modelId ?? executionDefaultModelId);
|
|
353
357
|
titleAdapter = createAdapter(titleModel.id, executionModels);
|
|
358
|
+
activeExecutionAdapters.add(titleAdapter);
|
|
354
359
|
titleTask = generateSessionTitle(titleAdapter, input.prompt, titleModel.defaultReasoningEffort, controller.signal, recordModelAttempt('TITLE')).catch(() => undefined);
|
|
355
360
|
}
|
|
356
361
|
}
|
|
@@ -367,12 +372,17 @@ export async function createMarAgent(options) {
|
|
|
367
372
|
turnId
|
|
368
373
|
});
|
|
369
374
|
selectedAdapter = sessionAdapter(input.modelId ?? executionDefaultModelId, executionModels);
|
|
375
|
+
const selectedModel = executionModels.get(input.modelId ?? executionDefaultModelId);
|
|
376
|
+
const restoredContinuation = latestModelContinuationCheckpoint(contextRecords, selectedModel.id);
|
|
377
|
+
if (restoredContinuation !== undefined)
|
|
378
|
+
selectedAdapter.restoreContinuation?.(restoredContinuation);
|
|
370
379
|
if (options.adapterFactory !== undefined &&
|
|
371
|
-
selectedAdapter.createTurnSession === undefined)
|
|
380
|
+
selectedAdapter.createTurnSession === undefined) {
|
|
372
381
|
executionScopedAdapters.add(selectedAdapter);
|
|
382
|
+
activeExecutionAdapters.add(selectedAdapter);
|
|
383
|
+
}
|
|
373
384
|
const selected = createModelTurnSession(selectedAdapter);
|
|
374
385
|
selectedTurn = selected;
|
|
375
|
-
const selectedModel = executionModels.get(input.modelId ?? executionDefaultModelId);
|
|
376
386
|
let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
|
|
377
387
|
const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
|
|
378
388
|
const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
|
|
@@ -462,29 +472,29 @@ export async function createMarAgent(options) {
|
|
|
462
472
|
agentInstructionInsertIndex &&
|
|
463
473
|
messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
|
|
464
474
|
agentInstructionInsertIndex--;
|
|
465
|
-
if (!contextualInstructions.initialized ||
|
|
466
|
-
contextualInstructions.snapshot !== currentInstructionSnapshot) {
|
|
467
|
-
const content = renderAgentInstructions(instructionEntries, compactMayContainAgentInstructions || contextualInstructions.entries.length > 0);
|
|
468
|
-
if (content !== undefined)
|
|
469
|
-
contextualInstructions.entries.push({
|
|
470
|
-
index: agentInstructionInsertIndex,
|
|
471
|
-
content
|
|
472
|
-
});
|
|
473
|
-
contextualInstructions.snapshot = currentInstructionSnapshot;
|
|
474
|
-
contextualInstructions.initialized = true;
|
|
475
|
-
}
|
|
476
475
|
const tools = withCodeModeResultTypes(availableTools.filter((tool) => tool.name !== 'skill' || (instructionContext?.skills.size ?? 0) > 0));
|
|
477
476
|
const executionTools = tools;
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
477
|
+
const loadRetainedSessionResources = async () => {
|
|
478
|
+
const hostResources = (await options.host.listSessionResources?.(sessionId)) ?? {
|
|
479
|
+
processes: []
|
|
480
|
+
};
|
|
481
|
+
return {
|
|
482
|
+
...(hostResources.idleTtlMs === undefined
|
|
483
|
+
? {}
|
|
484
|
+
: { idleTtlMs: hostResources.idleTtlMs }),
|
|
485
|
+
processes: hostResources.processes,
|
|
486
|
+
subagents: subagents?.listSessionResources() ?? []
|
|
487
|
+
};
|
|
487
488
|
};
|
|
489
|
+
let currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
|
|
490
|
+
contextualWorldState = updateContextualWorldState(contextualWorldState, agentInstructionInsertIndex, {
|
|
491
|
+
instructionSnapshot: currentInstructionSnapshot,
|
|
492
|
+
instructionContent: renderAgentInstructions(instructionEntries, compactMayContainAgentInstructions ||
|
|
493
|
+
contextualWorldState.instructionSnapshot !== undefined),
|
|
494
|
+
resourceSnapshot: currentResourceSnapshot,
|
|
495
|
+
previousMayContainResources: compactMayContainRetainedResources
|
|
496
|
+
});
|
|
497
|
+
sessionRuntime.contextualWorldState = contextualWorldState;
|
|
488
498
|
const buildExecutionSystemPrompt = (availableTools = executionTools, promptMode = mode) => buildSystemPrompt({
|
|
489
499
|
mode: promptMode,
|
|
490
500
|
platform: description.platform,
|
|
@@ -512,7 +522,6 @@ export async function createMarAgent(options) {
|
|
|
512
522
|
reasoningEfforts: configuration.reasoningEfforts
|
|
513
523
|
}))
|
|
514
524
|
: [],
|
|
515
|
-
retainedSessionResources,
|
|
516
525
|
highDensityCompaction: selectedModel.highDensityCompaction,
|
|
517
526
|
...(rolloutBudget?.active
|
|
518
527
|
? {
|
|
@@ -537,7 +546,9 @@ export async function createMarAgent(options) {
|
|
|
537
546
|
if (mode === 'compact') {
|
|
538
547
|
const compactSystemPrompt = initialSystemPrompt;
|
|
539
548
|
const compactHistory = [...messages];
|
|
540
|
-
const projectedHistory = normalizeMessagesForModel(
|
|
549
|
+
const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries, {
|
|
550
|
+
excludeResources: true
|
|
551
|
+
}), { supportsImages: false });
|
|
541
552
|
const nativeMessages = buildNativeCompactionMessages(projectedHistory, {
|
|
542
553
|
focus: input.prompt,
|
|
543
554
|
highDensityCompaction: selectedModel.highDensityCompaction
|
|
@@ -593,7 +604,9 @@ export async function createMarAgent(options) {
|
|
|
593
604
|
let compactSummary = '';
|
|
594
605
|
const compactSystemPrompt = buildExecutionSystemPrompt(executionTools, 'run');
|
|
595
606
|
const compactHistory = retainCurrent ? messages.slice(0, -1) : messages;
|
|
596
|
-
const projectedHistory = normalizeMessagesForModel(
|
|
607
|
+
const projectedHistory = normalizeMessagesForModel(insertContextualWorldState(compactHistory, contextualWorldState.entries, {
|
|
608
|
+
excludeResources: true
|
|
609
|
+
}), { supportsImages: false });
|
|
597
610
|
const nativeCompactMessages = buildNativeCompactionMessages(projectedHistory, {
|
|
598
611
|
highDensityCompaction: selectedModel.highDensityCompaction
|
|
599
612
|
});
|
|
@@ -686,8 +699,14 @@ export async function createMarAgent(options) {
|
|
|
686
699
|
.filter((message) => message.kind === 'user_input')
|
|
687
700
|
.map((message) => message.content);
|
|
688
701
|
agentInstructionInsertIndex = Math.max(0, messages.length - 1);
|
|
689
|
-
|
|
690
|
-
|
|
702
|
+
currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
|
|
703
|
+
contextualWorldState = resetContextualWorldState(compactRecord.recordId, agentInstructionInsertIndex, {
|
|
704
|
+
instructionSnapshot: currentInstructionSnapshot,
|
|
705
|
+
instructionContent: renderAgentInstructions(instructionEntries, true),
|
|
706
|
+
resourceSnapshot: currentResourceSnapshot,
|
|
707
|
+
previousMayContainResources: compactSummary.includes('# Retained Session resources')
|
|
708
|
+
});
|
|
709
|
+
sessionRuntime.contextualWorldState = contextualWorldState;
|
|
691
710
|
sessionRuntime.responsesChain = undefined;
|
|
692
711
|
lastServerContextTokens = undefined;
|
|
693
712
|
lastServerUsage = undefined;
|
|
@@ -726,7 +745,7 @@ export async function createMarAgent(options) {
|
|
|
726
745
|
.update(stableJson({
|
|
727
746
|
modelId: selectedModel.id,
|
|
728
747
|
systemPrompt,
|
|
729
|
-
|
|
748
|
+
contextualWorldState: contextualWorldState.entries,
|
|
730
749
|
tools: roundTools,
|
|
731
750
|
reasoningEffort
|
|
732
751
|
}))
|
|
@@ -745,7 +764,7 @@ export async function createMarAgent(options) {
|
|
|
745
764
|
: undefined;
|
|
746
765
|
const modelMessages = mode === 'compact'
|
|
747
766
|
? normalizeMessagesForModel(requestContextMessages, { supportsImages: false })
|
|
748
|
-
: normalizeMessagesForModel(
|
|
767
|
+
: normalizeMessagesForModel(insertContextualWorldState(requestContextMessages, contextualWorldState.entries), {
|
|
749
768
|
supportsImages: selectedModel.inputCapabilities.includes('IMAGE')
|
|
750
769
|
});
|
|
751
770
|
let sawModelSemanticEvent = false;
|
|
@@ -807,6 +826,9 @@ export async function createMarAgent(options) {
|
|
|
807
826
|
content: '',
|
|
808
827
|
provider: event.provider,
|
|
809
828
|
providerModelId: selectedModel.id,
|
|
829
|
+
...(event.provider === 'ANTHROPIC_MESSAGES'
|
|
830
|
+
? { contextKind: 'private' }
|
|
831
|
+
: {}),
|
|
810
832
|
item: event.item
|
|
811
833
|
};
|
|
812
834
|
messages.push(message);
|
|
@@ -979,6 +1001,17 @@ export async function createMarAgent(options) {
|
|
|
979
1001
|
};
|
|
980
1002
|
else if (selectedModel.responsesPreviousResponseId)
|
|
981
1003
|
sessionRuntime.responsesChain = undefined;
|
|
1004
|
+
const continuationCheckpoint = selected.continuationCheckpoint?.();
|
|
1005
|
+
if (mode !== 'compact' && continuationCheckpoint !== undefined)
|
|
1006
|
+
await store.append(sessionId, {
|
|
1007
|
+
type: 'responses.continuation',
|
|
1008
|
+
payload: {
|
|
1009
|
+
modelId: selectedModel.id,
|
|
1010
|
+
checkpoint: continuationCheckpoint
|
|
1011
|
+
},
|
|
1012
|
+
executionId,
|
|
1013
|
+
turnId
|
|
1014
|
+
});
|
|
982
1015
|
if (pendingTools.length === 0 && mailbox.length > 0) {
|
|
983
1016
|
await appendMailboxMessages();
|
|
984
1017
|
finalAnswer = '';
|
|
@@ -1167,8 +1200,14 @@ export async function createMarAgent(options) {
|
|
|
1167
1200
|
if (agentInstructionInsertIndex &&
|
|
1168
1201
|
messages[agentInstructionInsertIndex - 1]?.contextKind === 'environment')
|
|
1169
1202
|
agentInstructionInsertIndex--;
|
|
1170
|
-
|
|
1171
|
-
|
|
1203
|
+
currentResourceSnapshot = retainedSessionResourcesSnapshot(await loadRetainedSessionResources());
|
|
1204
|
+
contextualWorldState = resetContextualWorldState(latestContextualWorldStateGeneration(committed.records), agentInstructionInsertIndex, {
|
|
1205
|
+
instructionSnapshot: currentInstructionSnapshot,
|
|
1206
|
+
instructionContent: renderAgentInstructions(instructionEntries, committed.records.some((record) => compactSummaryFromCheckpoint(record) !== undefined)),
|
|
1207
|
+
resourceSnapshot: currentResourceSnapshot,
|
|
1208
|
+
previousMayContainResources: compactHistoryMayContainRetainedResources(committed.records)
|
|
1209
|
+
});
|
|
1210
|
+
sessionRuntime.contextualWorldState = contextualWorldState;
|
|
1172
1211
|
}
|
|
1173
1212
|
}
|
|
1174
1213
|
if (controller.signal.aborted) {
|
|
@@ -1252,7 +1291,16 @@ export async function createMarAgent(options) {
|
|
|
1252
1291
|
await selectedTurn?.close().catch(() => undefined);
|
|
1253
1292
|
if (titleAdapter !== undefined && titleAdapter !== selectedAdapter)
|
|
1254
1293
|
executionScopedAdapters.add(titleAdapter);
|
|
1255
|
-
|
|
1294
|
+
try {
|
|
1295
|
+
await closeModelAdapters(executionScopedAdapters);
|
|
1296
|
+
}
|
|
1297
|
+
catch {
|
|
1298
|
+
// Execution cleanup is best effort.
|
|
1299
|
+
}
|
|
1300
|
+
finally {
|
|
1301
|
+
for (const adapter of executionScopedAdapters)
|
|
1302
|
+
activeExecutionAdapters.delete(adapter);
|
|
1303
|
+
}
|
|
1256
1304
|
subagents?.endParentExecution(executionId);
|
|
1257
1305
|
await releaseSession?.();
|
|
1258
1306
|
active.delete(`${sessionId}:${executionId}`);
|
|
@@ -1328,30 +1376,60 @@ export async function createMarAgent(options) {
|
|
|
1328
1376
|
const activeSessions = activeSessionIds();
|
|
1329
1377
|
if ([...activeSessions].some((sessionId) => sessionRuntimes.get(sessionId)?.sourceType !== 'subagent'))
|
|
1330
1378
|
throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Models cannot be reconfigured while a main execution is active.');
|
|
1331
|
-
const activeAdapters = new Set();
|
|
1379
|
+
const activeAdapters = new Set(activeExecutionAdapters);
|
|
1332
1380
|
for (const [sessionId, runtime] of sessionRuntimes)
|
|
1333
1381
|
if (activeSessions.has(sessionId))
|
|
1334
1382
|
for (const adapter of runtime.adapters.values())
|
|
1335
1383
|
activeAdapters.add(adapter);
|
|
1336
1384
|
const inactiveRuntimes = [...sessionRuntimes].flatMap(([sessionId, runtime]) => activeSessions.has(sessionId) ? [] : [runtime]);
|
|
1337
|
-
const adapters = inactiveRuntimes
|
|
1338
|
-
.flatMap((runtime) => [...runtime.adapters.values()])
|
|
1339
|
-
.filter((adapter) => !activeAdapters.has(adapter));
|
|
1340
1385
|
reconfiguring = true;
|
|
1341
1386
|
try {
|
|
1387
|
+
const reconfigurationCommits = new Map();
|
|
1388
|
+
for (const runtime of inactiveRuntimes)
|
|
1389
|
+
for (const [modelId, adapter] of runtime.adapters) {
|
|
1390
|
+
if (activeAdapters.has(adapter))
|
|
1391
|
+
continue;
|
|
1392
|
+
const configuration = next.models.get(modelId);
|
|
1393
|
+
const commit = configuration === undefined ? undefined : adapter.prepareReconfigure?.(configuration);
|
|
1394
|
+
if (commit !== undefined)
|
|
1395
|
+
reconfigurationCommits.set(adapter, commit);
|
|
1396
|
+
}
|
|
1397
|
+
const retainedAdapters = new Set(reconfigurationCommits.keys());
|
|
1398
|
+
const adapters = [
|
|
1399
|
+
...new Set(inactiveRuntimes.flatMap((runtime) => [...runtime.adapters.values()]))
|
|
1400
|
+
].filter((adapter) => !activeAdapters.has(adapter) && !retainedAdapters.has(adapter));
|
|
1401
|
+
for (const runtime of inactiveRuntimes) {
|
|
1402
|
+
for (const [modelId, adapter] of runtime.adapters)
|
|
1403
|
+
if (!retainedAdapters.has(adapter))
|
|
1404
|
+
runtime.adapters.delete(modelId);
|
|
1405
|
+
if (runtime.responsesChain !== undefined &&
|
|
1406
|
+
!retainedAdapters.has(runtime.adapters.get(runtime.responsesChain.modelId)))
|
|
1407
|
+
runtime.responsesChain = undefined;
|
|
1408
|
+
}
|
|
1342
1409
|
await closeModelAdapters(adapters);
|
|
1343
1410
|
if (disposed)
|
|
1344
1411
|
throw new MarAgentError('MAR_AGENT_DISPOSED', 'Agent is disposed.');
|
|
1412
|
+
for (const commit of reconfigurationCommits.values())
|
|
1413
|
+
commit();
|
|
1345
1414
|
models = next.models;
|
|
1346
1415
|
defaultModelId = next.defaultModelId;
|
|
1347
1416
|
modelGeneration++;
|
|
1348
1417
|
for (const provider of next.credentialProviders)
|
|
1349
1418
|
credentialProviders.add(provider);
|
|
1350
1419
|
for (const runtime of inactiveRuntimes) {
|
|
1351
|
-
runtime.responsesChain = undefined;
|
|
1352
|
-
runtime.adapters.clear();
|
|
1353
1420
|
runtime.adapterGeneration = modelGeneration;
|
|
1354
1421
|
}
|
|
1422
|
+
const liveProviders = new Set(next.credentialProviders);
|
|
1423
|
+
for (const adapter of activeExecutionAdapters)
|
|
1424
|
+
addModelCredentialProviders(liveProviders, adapter.configuration);
|
|
1425
|
+
for (const runtime of sessionRuntimes.values())
|
|
1426
|
+
for (const adapter of runtime.adapters.values())
|
|
1427
|
+
addModelCredentialProviders(liveProviders, adapter.configuration);
|
|
1428
|
+
const obsoleteProviders = [...credentialProviders].filter((provider) => !liveProviders.has(provider));
|
|
1429
|
+
const providerCloseResults = await Promise.allSettled(obsoleteProviders.map((provider) => provider.close()));
|
|
1430
|
+
for (const [index, result] of providerCloseResults.entries())
|
|
1431
|
+
if (result.status === 'fulfilled')
|
|
1432
|
+
credentialProviders.delete(obsoleteProviders[index]);
|
|
1355
1433
|
}
|
|
1356
1434
|
finally {
|
|
1357
1435
|
reconfiguring = false;
|
|
@@ -1494,9 +1572,46 @@ function createModelTurnSession(adapter) {
|
|
|
1494
1572
|
}
|
|
1495
1573
|
},
|
|
1496
1574
|
resetContinuation: () => turn.resetContinuation?.(),
|
|
1575
|
+
continuationCheckpoint: () => turn.continuationCheckpoint?.(),
|
|
1497
1576
|
close: () => turn.close()
|
|
1498
1577
|
};
|
|
1499
1578
|
}
|
|
1579
|
+
function latestModelContinuationCheckpoint(records, modelId) {
|
|
1580
|
+
let checkpoint;
|
|
1581
|
+
for (const record of records) {
|
|
1582
|
+
if (record.type === 'compact.completed' || record.type === 'context.gc.completed') {
|
|
1583
|
+
checkpoint = undefined;
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
if (record.type !== 'responses.continuation')
|
|
1587
|
+
continue;
|
|
1588
|
+
const payload = record.payload;
|
|
1589
|
+
if (payload === null || typeof payload !== 'object' || Array.isArray(payload))
|
|
1590
|
+
continue;
|
|
1591
|
+
const candidateModelId = payload.modelId;
|
|
1592
|
+
const candidate = payload.checkpoint;
|
|
1593
|
+
if (candidateModelId !== modelId || !isModelContinuationCheckpoint(candidate))
|
|
1594
|
+
continue;
|
|
1595
|
+
checkpoint = candidate;
|
|
1596
|
+
}
|
|
1597
|
+
return checkpoint;
|
|
1598
|
+
}
|
|
1599
|
+
function isModelContinuationCheckpoint(value) {
|
|
1600
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
1601
|
+
return false;
|
|
1602
|
+
const checkpoint = value;
|
|
1603
|
+
return (checkpoint.version === 1 &&
|
|
1604
|
+
checkpoint.protocol === 'OPENAI_RESPONSES' &&
|
|
1605
|
+
isBoundedString(checkpoint.configurationHash, 64) &&
|
|
1606
|
+
isBoundedString(checkpoint.responseId, 1_024) &&
|
|
1607
|
+
isBoundedString(checkpoint.requestPropertiesHash, 64) &&
|
|
1608
|
+
Number.isSafeInteger(checkpoint.inputPrefixLength) &&
|
|
1609
|
+
checkpoint.inputPrefixLength >= 0 &&
|
|
1610
|
+
isBoundedString(checkpoint.inputPrefixHash, 64));
|
|
1611
|
+
}
|
|
1612
|
+
function isBoundedString(value, maximumLength) {
|
|
1613
|
+
return typeof value === 'string' && value.length > 0 && value.length <= maximumLength;
|
|
1614
|
+
}
|
|
1500
1615
|
function modelAttemptFailureDiagnostic(diagnostic, summary) {
|
|
1501
1616
|
if (summary === undefined)
|
|
1502
1617
|
return diagnostic;
|
|
@@ -1508,7 +1623,16 @@ function modelAttemptFailureDiagnostic(diagnostic, summary) {
|
|
|
1508
1623
|
};
|
|
1509
1624
|
}
|
|
1510
1625
|
async function closeModelAdapters(adapters) {
|
|
1511
|
-
await Promise.
|
|
1626
|
+
const results = await Promise.allSettled([...new Set(adapters)].flatMap((adapter) => adapter.close === undefined ? [] : [adapter.close()]));
|
|
1627
|
+
const failure = results.find((result) => result.status === 'rejected');
|
|
1628
|
+
if (failure !== undefined)
|
|
1629
|
+
throw failure.reason;
|
|
1630
|
+
}
|
|
1631
|
+
function addModelCredentialProviders(target, configuration) {
|
|
1632
|
+
if (configuration.credentialProvider !== undefined)
|
|
1633
|
+
target.add(configuration.credentialProvider);
|
|
1634
|
+
if (configuration.imageGeneration !== undefined)
|
|
1635
|
+
target.add(configuration.imageGeneration.credentialProvider);
|
|
1512
1636
|
}
|
|
1513
1637
|
function truncateUtf8(value, maximumBytes) {
|
|
1514
1638
|
const bytes = Buffer.from(value);
|
|
@@ -1764,17 +1888,19 @@ function modelIdFromPayload(payload, key = 'modelId') {
|
|
|
1764
1888
|
const value = payload[key];
|
|
1765
1889
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
1766
1890
|
}
|
|
1767
|
-
function
|
|
1891
|
+
function insertContextualWorldState(messages, entries, options = {}) {
|
|
1768
1892
|
const result = [...messages];
|
|
1769
1893
|
let offset = 0;
|
|
1770
1894
|
for (const entry of entries) {
|
|
1895
|
+
if (options.excludeResources && entry.kind === 'resources')
|
|
1896
|
+
continue;
|
|
1771
1897
|
const insertionIndex = Math.max(0, Math.min(entry.index + offset, result.length));
|
|
1772
1898
|
result.splice(insertionIndex, 0, { role: 'user', content: entry.content });
|
|
1773
1899
|
offset++;
|
|
1774
1900
|
}
|
|
1775
1901
|
return result;
|
|
1776
1902
|
}
|
|
1777
|
-
function
|
|
1903
|
+
function latestContextualWorldStateGeneration(records) {
|
|
1778
1904
|
let generation;
|
|
1779
1905
|
for (const record of records)
|
|
1780
1906
|
if (record.type === 'context.gc.completed' ||
|
|
@@ -1782,12 +1908,39 @@ function latestContextualInstructionGeneration(records) {
|
|
|
1782
1908
|
generation = record.recordId;
|
|
1783
1909
|
return generation;
|
|
1784
1910
|
}
|
|
1785
|
-
function
|
|
1911
|
+
function compactHistoryMayContainRetainedResources(records) {
|
|
1912
|
+
return records.some((record) => compactSummaryFromCheckpoint(record)?.includes('# Retained Session resources'));
|
|
1913
|
+
}
|
|
1914
|
+
function updateContextualWorldState(state, index, current) {
|
|
1915
|
+
const entries = [...state.entries];
|
|
1916
|
+
if (!state.initialized || state.instructionSnapshot !== current.instructionSnapshot) {
|
|
1917
|
+
if (current.instructionContent !== undefined)
|
|
1918
|
+
entries.push({ index, kind: 'instructions', content: current.instructionContent });
|
|
1919
|
+
}
|
|
1920
|
+
const resourceContent = retainedSessionResourcesUpdate(current.resourceSnapshot, state.initialized ? state.resourceSnapshot : undefined, !state.initialized && current.previousMayContainResources);
|
|
1921
|
+
if (resourceContent !== undefined)
|
|
1922
|
+
entries.push({ index, kind: 'resources', content: resourceContent });
|
|
1923
|
+
return {
|
|
1924
|
+
generation: state.generation,
|
|
1925
|
+
initialized: true,
|
|
1926
|
+
instructionSnapshot: current.instructionSnapshot,
|
|
1927
|
+
resourceSnapshot: current.resourceSnapshot,
|
|
1928
|
+
entries
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
function resetContextualWorldState(generation, index, current) {
|
|
1932
|
+
const entries = [];
|
|
1933
|
+
if (current.instructionContent !== undefined)
|
|
1934
|
+
entries.push({ index, kind: 'instructions', content: current.instructionContent });
|
|
1935
|
+
const resourceContent = retainedSessionResourcesUpdate(current.resourceSnapshot, undefined, current.previousMayContainResources);
|
|
1936
|
+
if (resourceContent !== undefined)
|
|
1937
|
+
entries.push({ index, kind: 'resources', content: resourceContent });
|
|
1786
1938
|
return {
|
|
1787
1939
|
generation,
|
|
1788
1940
|
initialized: true,
|
|
1789
|
-
|
|
1790
|
-
|
|
1941
|
+
instructionSnapshot: current.instructionSnapshot,
|
|
1942
|
+
resourceSnapshot: current.resourceSnapshot,
|
|
1943
|
+
entries
|
|
1791
1944
|
};
|
|
1792
1945
|
}
|
|
1793
1946
|
function contextualInstructionInsertIndex(messages) {
|
|
@@ -302,7 +302,7 @@ export class JsonlSessionStore {
|
|
|
302
302
|
});
|
|
303
303
|
const retained = input.boundaryTurnId === null ? [] : records.slice(1, boundaryIndex + 1);
|
|
304
304
|
for (const record of retained) {
|
|
305
|
-
if (record.type === 'context.gc.completed')
|
|
305
|
+
if (record.type === 'context.gc.completed' || record.type === 'responses.continuation')
|
|
306
306
|
continue;
|
|
307
307
|
await this.append(target.id, {
|
|
308
308
|
type: record.type,
|
|
@@ -368,7 +368,9 @@ export class JsonlSessionStore {
|
|
|
368
368
|
: records.findLastIndex((record) => record.turnId === input.boundaryTurnId);
|
|
369
369
|
if (input.boundaryTurnId !== null && boundaryIndex < 0)
|
|
370
370
|
throw new MarAgentError('MAR_AGENT_ROLLBACK_TARGET_INVALID', 'Session rollback target was not found.');
|
|
371
|
-
const retained = records
|
|
371
|
+
const retained = records
|
|
372
|
+
.slice(0, boundaryIndex + 1)
|
|
373
|
+
.filter((record) => record.type !== 'responses.continuation');
|
|
372
374
|
const staged = await stageRollbackSegments(this.sessionDirectory(sessionId), retained);
|
|
373
375
|
const transactionId = randomUUID();
|
|
374
376
|
let generatedTrash = [];
|
|
@@ -160,9 +160,9 @@ export class SubagentSessionController {
|
|
|
160
160
|
if (waitFor === 'completion') {
|
|
161
161
|
const completed = await this.#waitForCompletion(state, waitMs, options.signal);
|
|
162
162
|
options.signal?.throwIfAborted();
|
|
163
|
-
const includeLatestMessage =
|
|
163
|
+
const includeLatestMessage = this.#hasUnreadMessage(state);
|
|
164
164
|
const includeTerminalDetails = completed ? this.#observeTerminal(state) : false;
|
|
165
|
-
if (
|
|
165
|
+
if (includeLatestMessage)
|
|
166
166
|
this.#observeLatestMessage(state);
|
|
167
167
|
return {
|
|
168
168
|
...this.#snapshot(state, { includeLatestMessage, includeTerminalDetails }),
|
|
@@ -244,12 +244,13 @@ export class SubagentSessionController {
|
|
|
244
244
|
}
|
|
245
245
|
const completed = await this.#waitForAnyCompletion(live, waitMs, options.signal);
|
|
246
246
|
options.signal?.throwIfAborted();
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
const state = completed ?? this.#oldestUnreadMessage(live) ?? live.at(-1);
|
|
248
|
+
const includeLatestMessage = this.#hasUnreadMessage(state);
|
|
249
|
+
const includeTerminalDetails = completed ? this.#observeTerminal(state) : false;
|
|
250
|
+
if (includeLatestMessage)
|
|
251
|
+
this.#observeLatestMessage(state);
|
|
251
252
|
return {
|
|
252
|
-
...this.#snapshot(
|
|
253
|
+
...this.#snapshot(state, {
|
|
253
254
|
includeLatestMessage,
|
|
254
255
|
includeTerminalDetails
|
|
255
256
|
}),
|
package/dist/tools/agent-wait.js
CHANGED
|
@@ -3,7 +3,7 @@ import { TOOL_EXECUTION_LIMITS } from './execution-limits.js';
|
|
|
3
3
|
export const agentWaitToolDefinition = {
|
|
4
4
|
name: 'agent_wait',
|
|
5
5
|
parallelSafety: 'serial',
|
|
6
|
-
description: 'Read or wait a bounded time for a child agent result. waitFor defaults to "completion"; use "message" only when a fresh public commentary/final message would change the next decision. Omit agentId to wait for whichever live child returns first, matching Codex-style untargeted waiting and avoiding UUID transcription; provide the exact agentId returned by agent_start only when observing a specific child. If no child is live, untargeted waiting reads the most recently created child. For an exec processId, use exec with action:"poll" instead. Omitting waitMs waits up to 30 seconds by default; waitMs=0 returns the current snapshot immediately. Positive waits range up to 5 minutes, and positive values below 10 seconds are raised to 10 seconds. When blocked on a live child result, prefer one 300000 ms completion wait rather than repeated short waits. Every result includes waitOutcome as snapshot, message, completion, or timeout plus the real agentId and description. A message or first terminal completion result includes the newly observed latest public commentary/final message;
|
|
6
|
+
description: 'Read or wait a bounded time for a child agent result. waitFor defaults to "completion"; use "message" only when a fresh public commentary/final message would change the next decision. Omit agentId to wait for whichever live child returns first, matching Codex-style untargeted waiting and avoiding UUID transcription; provide the exact agentId returned by agent_start only when observing a specific child. If no child is live, untargeted waiting reads the most recently created child. For an exec processId, use exec with action:"poll" instead. Omitting waitMs waits up to 30 seconds by default; waitMs=0 returns the current snapshot immediately. Positive waits range up to 5 minutes, and positive values below 10 seconds are raised to 10 seconds. When blocked on a live child result, prefer one 300000 ms completion wait rather than repeated short waits. Every result includes waitOutcome as snapshot, message, completion, or timeout plus the real agentId and description. A message, timeout with unread progress, or first terminal completion result includes the newly observed latest public commentary/final message; snapshots and repeated observations omit previously observed message text. latestActivity can still identify a public message or bounded tool started/completed/failed status. A queued or running result is not completion. A message wait consumes only the latest unread public message in the current Session runtime; tool activity does not wake it, and parent cancellation preserves unread progress for a later Execution. Do not use message waits as heartbeat checks or request progress merely to confirm that a child is still running. First terminal results include bounded summary, changedFiles, verification, and read evidence with path/line ranges; use that evidence to avoid repeating the full exploration, while independently checking consequential edits and claims.',
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: 'object',
|
|
9
9
|
properties: {
|