@myagentroam/agent 0.9.83 → 0.9.85
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/configuration.js +5 -0
- package/dist/model/contracts.d.ts +16 -1
- package/dist/model/openai-responses.d.ts +8 -6
- package/dist/model/openai-responses.js +106 -83
- package/dist/runtime/compact.js +3 -1
- package/dist/runtime/context-projection.js +1 -0
- package/dist/sdk/agent.js +115 -10
- package/dist/session/jsonl-store.js +4 -2
- package/dist/tools/agent-message.js +7 -2
- package/dist/tools/agent-start.js +1 -0
- package/dist/tools/agent-wait.js +3 -0
- package/package.json +2 -1
|
@@ -80,6 +80,11 @@ const rawModelSchema = z
|
|
|
80
80
|
});
|
|
81
81
|
if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesPreviousResponseId)
|
|
82
82
|
context.addIssue({ code: 'custom', message: 'Responses continuation requires Responses.' });
|
|
83
|
+
if (value.responsesPreviousResponseId && value.responsesTransport?.transport !== 'WEBSOCKET')
|
|
84
|
+
context.addIssue({
|
|
85
|
+
code: 'custom',
|
|
86
|
+
message: 'Responses continuation requires WebSocket transport.'
|
|
87
|
+
});
|
|
83
88
|
if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesTransport !== undefined)
|
|
84
89
|
context.addIssue({ code: 'custom', message: 'Responses transport requires Responses.' });
|
|
85
90
|
if (value.protocol !== 'OPENAI_RESPONSES' && value.responsesEncoding !== undefined)
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { MarAgentModelConfiguration, MarAgentReasoningEffort, ModelCredentialAcquireReason } from './configuration.js';
|
|
2
|
+
export interface ModelContinuationCheckpoint {
|
|
3
|
+
readonly version: 2;
|
|
4
|
+
readonly protocol: 'OPENAI_RESPONSES';
|
|
5
|
+
readonly configurationHash: string;
|
|
6
|
+
readonly responseId: string;
|
|
7
|
+
readonly inputPrefixLength: number;
|
|
8
|
+
readonly inputPrefixHash: string;
|
|
9
|
+
}
|
|
2
10
|
export interface ClientToolDefinition {
|
|
3
11
|
name: string;
|
|
4
12
|
description: string;
|
|
@@ -11,7 +19,7 @@ export interface ClientToolDefinition {
|
|
|
11
19
|
export interface ModelMessage {
|
|
12
20
|
role: 'user' | 'assistant' | 'tool' | 'tool_call' | 'provider';
|
|
13
21
|
content: string;
|
|
14
|
-
contextKind?: 'environment';
|
|
22
|
+
contextKind?: 'environment' | 'private';
|
|
15
23
|
callId?: string;
|
|
16
24
|
name?: string;
|
|
17
25
|
arguments?: unknown;
|
|
@@ -146,11 +154,18 @@ export interface ModelAdapter {
|
|
|
146
154
|
readonly configuration: MarAgentModelConfiguration;
|
|
147
155
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
148
156
|
createTurnSession?(): ModelTurnSession;
|
|
157
|
+
/**
|
|
158
|
+
* Prepares an equivalent model rebind without mutating live state.
|
|
159
|
+
* The returned commit callback must be synchronous and non-throwing.
|
|
160
|
+
*/
|
|
161
|
+
prepareReconfigure?(configuration: MarAgentModelConfiguration): (() => void) | undefined;
|
|
162
|
+
restoreContinuation?(checkpoint: ModelContinuationCheckpoint): boolean;
|
|
149
163
|
close?(): Promise<void>;
|
|
150
164
|
}
|
|
151
165
|
export interface ModelTurnSession {
|
|
152
166
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
153
167
|
/** Drops incremental response state after a local context replacement. */
|
|
154
168
|
resetContinuation?(): void;
|
|
169
|
+
continuationCheckpoint?(): ModelContinuationCheckpoint | undefined;
|
|
155
170
|
close(): Promise<void>;
|
|
156
171
|
}
|
|
@@ -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>;
|
|
@@ -42,11 +45,11 @@ declare class OpenAiResponsesTurnSession implements ModelTurnSession {
|
|
|
42
45
|
private readonly adapter;
|
|
43
46
|
private readonly state;
|
|
44
47
|
private readonly cacheLease;
|
|
45
|
-
|
|
46
|
-
constructor(adapter: OpenAiResponsesAdapter, state: ResponsesSessionState, cacheLease: boolean, allowExternalContinuation: boolean);
|
|
48
|
+
constructor(adapter: OpenAiResponsesAdapter, state: ResponsesSessionState, cacheLease: boolean);
|
|
47
49
|
start(request: ModelRequest, signal: AbortSignal): AsyncIterable<ModelEvent>;
|
|
48
50
|
close(): Promise<void>;
|
|
49
51
|
resetContinuation(): void;
|
|
52
|
+
continuationCheckpoint(): ModelContinuationCheckpoint | undefined;
|
|
50
53
|
prepareRequest(request: ModelRequest): PreparedResponsesRequest;
|
|
51
54
|
fullRequestBody(): ResponsesRequestBody;
|
|
52
55
|
currentTurnState(): string | undefined;
|
|
@@ -61,7 +64,6 @@ declare class OpenAiResponsesTurnSession implements ModelTurnSession {
|
|
|
61
64
|
readonly reason: ModelCredentialAcquireReason;
|
|
62
65
|
readonly signal: AbortSignal;
|
|
63
66
|
readonly continuation: boolean;
|
|
64
|
-
readonly requiresExistingSocket: boolean;
|
|
65
67
|
readonly credentialAcquired: (revision: number) => void;
|
|
66
68
|
}): Promise<WebSocket>;
|
|
67
69
|
private connectWebSocket;
|
|
@@ -13,16 +13,38 @@ 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
|
+
!responsesContinuationEnabled(this.#configuration) ||
|
|
39
|
+
checkpoint.version !== 2 ||
|
|
40
|
+
checkpoint.protocol !== 'OPENAI_RESPONSES' ||
|
|
41
|
+
checkpoint.configurationHash !== responsesContinuationIdentityHash(this.#configuration))
|
|
42
|
+
return false;
|
|
43
|
+
this.#cachedState.restoredContinuation = checkpoint;
|
|
44
|
+
return true;
|
|
23
45
|
}
|
|
24
46
|
async *start(request, signal) {
|
|
25
|
-
const turn = new OpenAiResponsesTurnSession(this, {}, false
|
|
47
|
+
const turn = new OpenAiResponsesTurnSession(this, {}, false);
|
|
26
48
|
try {
|
|
27
49
|
yield* turn.start(request, signal);
|
|
28
50
|
}
|
|
@@ -39,7 +61,7 @@ export class OpenAiResponsesAdapter {
|
|
|
39
61
|
this.#cachedState = {};
|
|
40
62
|
this.#cacheLeased = true;
|
|
41
63
|
}
|
|
42
|
-
return new OpenAiResponsesTurnSession(this, state, cacheLease
|
|
64
|
+
return new OpenAiResponsesTurnSession(this, state, cacheLease);
|
|
43
65
|
}
|
|
44
66
|
async close() {
|
|
45
67
|
if (this.#closed)
|
|
@@ -53,14 +75,13 @@ export class OpenAiResponsesAdapter {
|
|
|
53
75
|
return;
|
|
54
76
|
}
|
|
55
77
|
if (state.socket !== undefined && state.socket.readyState !== WebSocket.OPEN)
|
|
56
|
-
invalidateResponsesWebSocket(state,
|
|
78
|
+
invalidateResponsesWebSocket(state, false);
|
|
57
79
|
this.#cachedState = state;
|
|
58
80
|
this.#cacheLeased = false;
|
|
59
81
|
}
|
|
60
82
|
async *startTurn(request, signal, turn, prepared) {
|
|
61
83
|
let useContinuation = prepared.continuation;
|
|
62
84
|
let requestBody = prepared.body;
|
|
63
|
-
let requiresExistingSocket = prepared.requiresExistingSocket;
|
|
64
85
|
let credentialReason = 'REQUEST';
|
|
65
86
|
let unauthorizedRetryUsed = false;
|
|
66
87
|
let invalidContinuationFallbackUsed = false;
|
|
@@ -87,7 +108,7 @@ export class OpenAiResponsesAdapter {
|
|
|
87
108
|
credentialReason: attemptCredentialReason
|
|
88
109
|
});
|
|
89
110
|
try {
|
|
90
|
-
for await (const event of this.startAttempt(request, signal, requestBody, useContinuation, credentialReason,
|
|
111
|
+
for await (const event of this.startAttempt(request, signal, requestBody, useContinuation, credentialReason, turn)) {
|
|
91
112
|
if (!sawSemanticEvent && !isSemanticEvent(event)) {
|
|
92
113
|
prelude.push(event);
|
|
93
114
|
continue;
|
|
@@ -143,7 +164,6 @@ export class OpenAiResponsesAdapter {
|
|
|
143
164
|
invalidContinuationFallbackUsed = true;
|
|
144
165
|
useContinuation = false;
|
|
145
166
|
requestBody = prepared.fullBody;
|
|
146
|
-
requiresExistingSocket = false;
|
|
147
167
|
turn.invalidateWebSocket();
|
|
148
168
|
credentialReason = 'RECONNECT';
|
|
149
169
|
await reportFailure(true, {
|
|
@@ -192,14 +212,13 @@ export class OpenAiResponsesAdapter {
|
|
|
192
212
|
: 'REQUEST';
|
|
193
213
|
useContinuation = false;
|
|
194
214
|
requestBody = prepared.fullBody;
|
|
195
|
-
requiresExistingSocket = false;
|
|
196
215
|
if (error instanceof MarAgentError && error.details?.transport === 'WEBSOCKET')
|
|
197
216
|
turn.invalidateWebSocket();
|
|
198
217
|
await waitForModelRetry(retryAfter(error), attempt, signal, delayMs);
|
|
199
218
|
}
|
|
200
219
|
}
|
|
201
220
|
}
|
|
202
|
-
async *startAttempt(request, signal, body, continuation, credentialReason,
|
|
221
|
+
async *startAttempt(request, signal, body, continuation, credentialReason, turn) {
|
|
203
222
|
let latestCredentialRevision;
|
|
204
223
|
const fetchResponse = async (requestBody) => {
|
|
205
224
|
const serializedBody = JSON.stringify(requestBody);
|
|
@@ -233,7 +252,6 @@ export class OpenAiResponsesAdapter {
|
|
|
233
252
|
reason: credentialReason,
|
|
234
253
|
signal,
|
|
235
254
|
continuation,
|
|
236
|
-
requiresExistingSocket,
|
|
237
255
|
turn,
|
|
238
256
|
credentialAcquired: (revision) => (latestCredentialRevision = revision)
|
|
239
257
|
});
|
|
@@ -517,21 +535,28 @@ export class OpenAiResponsesAdapter {
|
|
|
517
535
|
});
|
|
518
536
|
}
|
|
519
537
|
}
|
|
538
|
+
function sameResponsesAdapterConfiguration(current, next) {
|
|
539
|
+
const { apiKey: currentApiKey, credentialProvider: currentCredentialProvider, imageGeneration: _currentImageGeneration, ...currentTransport } = current;
|
|
540
|
+
const { apiKey: nextApiKey, credentialProvider: nextCredentialProvider, imageGeneration: _nextImageGeneration, ...nextTransport } = next;
|
|
541
|
+
const credentialsAreRebindable = (currentCredentialProvider !== undefined && nextCredentialProvider !== undefined) ||
|
|
542
|
+
(currentApiKey !== undefined &&
|
|
543
|
+
nextApiKey !== undefined &&
|
|
544
|
+
currentApiKey.reveal() === nextApiKey.reveal());
|
|
545
|
+
return credentialsAreRebindable && isDeepStrictEqual(currentTransport, nextTransport);
|
|
546
|
+
}
|
|
520
547
|
class OpenAiResponsesTurnSession {
|
|
521
548
|
adapter;
|
|
522
549
|
state;
|
|
523
550
|
cacheLease;
|
|
524
|
-
allowExternalContinuation;
|
|
525
551
|
#active = false;
|
|
526
552
|
#closed = false;
|
|
527
553
|
#turnState;
|
|
528
554
|
#fullBody;
|
|
529
555
|
#connectionDiagnostic;
|
|
530
|
-
constructor(adapter, state, cacheLease
|
|
556
|
+
constructor(adapter, state, cacheLease) {
|
|
531
557
|
this.adapter = adapter;
|
|
532
558
|
this.state = state;
|
|
533
559
|
this.cacheLease = cacheLease;
|
|
534
|
-
this.allowExternalContinuation = allowExternalContinuation;
|
|
535
560
|
}
|
|
536
561
|
async *start(request, signal) {
|
|
537
562
|
if (this.#closed)
|
|
@@ -548,7 +573,7 @@ class OpenAiResponsesTurnSession {
|
|
|
548
573
|
this.#fullBody = prepared.fullBody;
|
|
549
574
|
if (this.adapter.configuration.responsesEncoding === 'LITE' &&
|
|
550
575
|
this.adapter.configuration.responsesTransport?.transport === 'WEBSOCKET' &&
|
|
551
|
-
this.adapter.configuration
|
|
576
|
+
responsesContinuationEnabled(this.adapter.configuration) &&
|
|
552
577
|
this.state.lastRequest === undefined &&
|
|
553
578
|
request.allowTools !== false &&
|
|
554
579
|
request.toolChoice !== 'none' &&
|
|
@@ -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')
|
|
@@ -601,12 +626,13 @@ class OpenAiResponsesTurnSession {
|
|
|
601
626
|
completed = event.reason === 'completed' || event.reason === 'tool_use';
|
|
602
627
|
yield event;
|
|
603
628
|
}
|
|
604
|
-
if (this.adapter.configuration
|
|
629
|
+
if (responsesContinuationEnabled(this.adapter.configuration) &&
|
|
605
630
|
completed &&
|
|
606
631
|
responseId !== undefined &&
|
|
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,32 @@ class OpenAiResponsesTurnSession {
|
|
|
629
655
|
resetContinuation() {
|
|
630
656
|
clearResponsesContinuation(this.state);
|
|
631
657
|
}
|
|
658
|
+
continuationCheckpoint() {
|
|
659
|
+
if (!responsesContinuationEnabled(this.adapter.configuration))
|
|
660
|
+
return undefined;
|
|
661
|
+
const request = this.state.lastRequest;
|
|
662
|
+
const response = this.state.lastResponse;
|
|
663
|
+
if (request === undefined || response === undefined)
|
|
664
|
+
return this.state.restoredContinuation;
|
|
665
|
+
const baseline = [...request.input, ...response.outputItems];
|
|
666
|
+
return {
|
|
667
|
+
version: 2,
|
|
668
|
+
protocol: 'OPENAI_RESPONSES',
|
|
669
|
+
configurationHash: responsesContinuationIdentityHash(this.adapter.configuration),
|
|
670
|
+
responseId: response.responseId,
|
|
671
|
+
inputPrefixLength: baseline.length,
|
|
672
|
+
inputPrefixHash: hashResponsesValue(baseline)
|
|
673
|
+
};
|
|
674
|
+
}
|
|
632
675
|
prepareRequest(request) {
|
|
633
676
|
if (this.adapter.configuration.responsesTransport?.transport === 'WEBSOCKET' &&
|
|
634
677
|
this.state.socket !== undefined &&
|
|
635
678
|
this.state.socket.readyState !== WebSocket.OPEN)
|
|
636
|
-
invalidateResponsesWebSocket(this.state,
|
|
679
|
+
invalidateResponsesWebSocket(this.state, false);
|
|
637
680
|
const fullBody = responsesRequestBody(this.adapter.configuration, request, request.messages, this.adapter.prefixIdentity);
|
|
638
|
-
if (this.adapter.configuration
|
|
681
|
+
if (responsesContinuationEnabled(this.adapter.configuration)) {
|
|
639
682
|
const incremental = incrementalResponsesInput(this.state, fullBody);
|
|
640
|
-
if (incremental !== undefined
|
|
641
|
-
(this.adapter.configuration.responsesTransport?.transport !== 'WEBSOCKET' ||
|
|
642
|
-
this.state.socket?.readyState === WebSocket.OPEN)) {
|
|
683
|
+
if (incremental !== undefined) {
|
|
643
684
|
const responseId = this.state.lastResponse.responseId;
|
|
644
685
|
delete this.state.lastResponse;
|
|
645
686
|
return {
|
|
@@ -649,31 +690,14 @@ class OpenAiResponsesTurnSession {
|
|
|
649
690
|
input: incremental,
|
|
650
691
|
previous_response_id: responseId
|
|
651
692
|
},
|
|
652
|
-
continuation: true
|
|
653
|
-
requiresExistingSocket: this.adapter.configuration.responsesTransport?.transport === 'WEBSOCKET'
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
if (this.allowExternalContinuation &&
|
|
657
|
-
request.continuation !== undefined &&
|
|
658
|
-
this.adapter.configuration.responsesTransport?.transport !== 'WEBSOCKET') {
|
|
659
|
-
const deltaBody = responsesRequestBody(this.adapter.configuration, request, request.continuation.deltaMessages);
|
|
660
|
-
return {
|
|
661
|
-
fullBody,
|
|
662
|
-
body: {
|
|
663
|
-
...fullBody,
|
|
664
|
-
input: deltaBody.input,
|
|
665
|
-
previous_response_id: request.continuation.previousResponseId
|
|
666
|
-
},
|
|
667
|
-
continuation: true,
|
|
668
|
-
requiresExistingSocket: false
|
|
693
|
+
continuation: true
|
|
669
694
|
};
|
|
670
695
|
}
|
|
671
696
|
}
|
|
672
697
|
return {
|
|
673
698
|
fullBody,
|
|
674
699
|
body: fullBody,
|
|
675
|
-
continuation: false
|
|
676
|
-
requiresExistingSocket: false
|
|
700
|
+
continuation: false
|
|
677
701
|
};
|
|
678
702
|
}
|
|
679
703
|
fullRequestBody() {
|
|
@@ -714,14 +738,9 @@ class OpenAiResponsesTurnSession {
|
|
|
714
738
|
async webSocketForRequest(input) {
|
|
715
739
|
const endpoint = webSocketEndpoint(input.configuration.baseUrl);
|
|
716
740
|
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
741
|
if (existingSocket !== undefined &&
|
|
723
742
|
(existingSocket.readyState !== WebSocket.OPEN || this.state.endpoint !== endpoint))
|
|
724
|
-
invalidateResponsesWebSocket(this.state,
|
|
743
|
+
invalidateResponsesWebSocket(this.state, false);
|
|
725
744
|
if (this.state.socket === undefined) {
|
|
726
745
|
const handshakeCredential = await acquireResponsesCredential(input.configuration, input.reason);
|
|
727
746
|
input.credentialAcquired(handshakeCredential.revision);
|
|
@@ -729,10 +748,9 @@ class OpenAiResponsesTurnSession {
|
|
|
729
748
|
}
|
|
730
749
|
const requestCredential = await acquireResponsesCredential(input.configuration, input.reason);
|
|
731
750
|
input.credentialAcquired(requestCredential.revision);
|
|
732
|
-
if (requestCredential.revision !== this.state.credentialRevision
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
throw unavailableContinuationConnection();
|
|
751
|
+
if (requestCredential.revision !== this.state.credentialRevision ||
|
|
752
|
+
responsesCredentialFingerprint(requestCredential) !== this.state.credentialFingerprint) {
|
|
753
|
+
invalidateResponsesWebSocket(this.state, false);
|
|
736
754
|
await this.connectWebSocket(endpoint, requestCredential, input.promptCacheKey, input.signal);
|
|
737
755
|
}
|
|
738
756
|
if (input.signal.aborted) {
|
|
@@ -787,6 +805,7 @@ class OpenAiResponsesTurnSession {
|
|
|
787
805
|
this.state.socket = socket;
|
|
788
806
|
this.state.endpoint = endpoint;
|
|
789
807
|
this.state.credentialRevision = credential.revision;
|
|
808
|
+
this.state.credentialFingerprint = responsesCredentialFingerprint(credential);
|
|
790
809
|
this.state.connectionId = connectionId;
|
|
791
810
|
this.state.proxyType = proxyType;
|
|
792
811
|
}
|
|
@@ -864,10 +883,16 @@ function uuidV5(namespace, value) {
|
|
|
864
883
|
function incrementalResponsesInput(state, current) {
|
|
865
884
|
const previous = state.lastRequest;
|
|
866
885
|
const completion = state.lastResponse;
|
|
867
|
-
if (previous === undefined ||
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
886
|
+
if (previous === undefined || completion === undefined) {
|
|
887
|
+
const restored = state.restoredContinuation;
|
|
888
|
+
if (restored === undefined ||
|
|
889
|
+
current.input.length < restored.inputPrefixLength ||
|
|
890
|
+
restored.inputPrefixHash !==
|
|
891
|
+
hashResponsesValue(current.input.slice(0, restored.inputPrefixLength)))
|
|
892
|
+
return undefined;
|
|
893
|
+
state.lastResponse = { responseId: restored.responseId, outputItems: [] };
|
|
894
|
+
return current.input.slice(restored.inputPrefixLength);
|
|
895
|
+
}
|
|
871
896
|
const baseline = [...previous.input, ...completion.outputItems];
|
|
872
897
|
if (current.input.length < baseline.length)
|
|
873
898
|
return undefined;
|
|
@@ -876,32 +901,41 @@ function incrementalResponsesInput(state, current) {
|
|
|
876
901
|
return undefined;
|
|
877
902
|
return current.input.slice(baseline.length);
|
|
878
903
|
}
|
|
879
|
-
function responsesRequestPropertiesMatch(previous, current) {
|
|
880
|
-
const keys = [
|
|
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]));
|
|
895
|
-
}
|
|
896
904
|
function clearResponsesContinuation(state) {
|
|
897
905
|
delete state.lastRequest;
|
|
898
906
|
delete state.lastResponse;
|
|
907
|
+
delete state.restoredContinuation;
|
|
908
|
+
}
|
|
909
|
+
function hashResponsesValue(value) {
|
|
910
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
911
|
+
}
|
|
912
|
+
function responsesContinuationIdentityHash(configuration) {
|
|
913
|
+
return hashResponsesValue({
|
|
914
|
+
endpoint: new URL(responsesEndpoint(configuration.baseUrl)).toString(),
|
|
915
|
+
modelId: configuration.modelId,
|
|
916
|
+
responsesEncoding: configuration.responsesEncoding ?? 'STANDARD'
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
function responsesContinuationEnabled(configuration) {
|
|
920
|
+
return (configuration.responsesPreviousResponseId === true &&
|
|
921
|
+
configuration.responsesTransport?.transport === 'WEBSOCKET');
|
|
922
|
+
}
|
|
923
|
+
function responsesCredentialFingerprint(credential) {
|
|
924
|
+
return hashResponsesValue({
|
|
925
|
+
bearer: credential.bearer.reveal(),
|
|
926
|
+
headers: Object.fromEntries(Object.entries(credential.headers).map(([name, value]) => [
|
|
927
|
+
name,
|
|
928
|
+
typeof value === 'string' ? value : value.reveal()
|
|
929
|
+
])),
|
|
930
|
+
socks5Url: credential.socks5Url?.reveal()
|
|
931
|
+
});
|
|
899
932
|
}
|
|
900
933
|
function invalidateResponsesWebSocket(state, clearContinuation) {
|
|
901
934
|
const socket = state.socket;
|
|
902
935
|
delete state.socket;
|
|
903
936
|
delete state.endpoint;
|
|
904
937
|
delete state.credentialRevision;
|
|
938
|
+
delete state.credentialFingerprint;
|
|
905
939
|
delete state.connectionId;
|
|
906
940
|
delete state.proxyType;
|
|
907
941
|
if (clearContinuation)
|
|
@@ -911,16 +945,6 @@ function invalidateResponsesWebSocket(state, clearContinuation) {
|
|
|
911
945
|
socket.readyState !== WebSocket.CLOSING)
|
|
912
946
|
terminateWebSocket(socket);
|
|
913
947
|
}
|
|
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
948
|
function turnStateFromEvent(event) {
|
|
925
949
|
for (const headers of [
|
|
926
950
|
record(event.headers),
|
|
@@ -958,7 +982,6 @@ async function* webSocketResponseEvents(input) {
|
|
|
958
982
|
reason: input.reason,
|
|
959
983
|
signal: input.signal,
|
|
960
984
|
continuation: input.continuation,
|
|
961
|
-
requiresExistingSocket: input.requiresExistingSocket,
|
|
962
985
|
credentialAcquired: input.credentialAcquired
|
|
963
986
|
});
|
|
964
987
|
const body = input.turn.withTurnStateMetadata(input.body);
|
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
|
@@ -55,6 +55,7 @@ export async function createMarAgent(options) {
|
|
|
55
55
|
throw new MarAgentError('MAR_AGENT_HOST_INCOMPATIBLE', 'Host mode does not match Agent mode.');
|
|
56
56
|
const store = await JsonlSessionStore.open(description.homeDirectory);
|
|
57
57
|
const active = new Map();
|
|
58
|
+
const activeExecutionAdapters = new Set();
|
|
58
59
|
const executionResults = new Map();
|
|
59
60
|
const sessionRuntimes = new Map();
|
|
60
61
|
let disposed = false;
|
|
@@ -354,6 +355,7 @@ export async function createMarAgent(options) {
|
|
|
354
355
|
const titleModel = [...executionModels.values()].find((model) => model.titleGeneration) ??
|
|
355
356
|
executionModels.get(input.modelId ?? executionDefaultModelId);
|
|
356
357
|
titleAdapter = createAdapter(titleModel.id, executionModels);
|
|
358
|
+
activeExecutionAdapters.add(titleAdapter);
|
|
357
359
|
titleTask = generateSessionTitle(titleAdapter, input.prompt, titleModel.defaultReasoningEffort, controller.signal, recordModelAttempt('TITLE')).catch(() => undefined);
|
|
358
360
|
}
|
|
359
361
|
}
|
|
@@ -370,12 +372,17 @@ export async function createMarAgent(options) {
|
|
|
370
372
|
turnId
|
|
371
373
|
});
|
|
372
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);
|
|
373
379
|
if (options.adapterFactory !== undefined &&
|
|
374
|
-
selectedAdapter.createTurnSession === undefined)
|
|
380
|
+
selectedAdapter.createTurnSession === undefined) {
|
|
375
381
|
executionScopedAdapters.add(selectedAdapter);
|
|
382
|
+
activeExecutionAdapters.add(selectedAdapter);
|
|
383
|
+
}
|
|
376
384
|
const selected = createModelTurnSession(selectedAdapter);
|
|
377
385
|
selectedTurn = selected;
|
|
378
|
-
const selectedModel = executionModels.get(input.modelId ?? executionDefaultModelId);
|
|
379
386
|
let lastServerUsage = latestModelUsage(contextRecords, selectedModel.id);
|
|
380
387
|
const compactUserTokenLimit = Math.min(20_000, Math.floor(selectedModel.contextWindowTokens / 8));
|
|
381
388
|
const reasoningEffort = input.reasoningEffort ?? selectedModel.defaultReasoningEffort;
|
|
@@ -819,6 +826,9 @@ export async function createMarAgent(options) {
|
|
|
819
826
|
content: '',
|
|
820
827
|
provider: event.provider,
|
|
821
828
|
providerModelId: selectedModel.id,
|
|
829
|
+
...(event.provider === 'ANTHROPIC_MESSAGES'
|
|
830
|
+
? { contextKind: 'private' }
|
|
831
|
+
: {}),
|
|
822
832
|
item: event.item
|
|
823
833
|
};
|
|
824
834
|
messages.push(message);
|
|
@@ -991,6 +1001,17 @@ export async function createMarAgent(options) {
|
|
|
991
1001
|
};
|
|
992
1002
|
else if (selectedModel.responsesPreviousResponseId)
|
|
993
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
|
+
});
|
|
994
1015
|
if (pendingTools.length === 0 && mailbox.length > 0) {
|
|
995
1016
|
await appendMailboxMessages();
|
|
996
1017
|
finalAnswer = '';
|
|
@@ -1270,7 +1291,16 @@ export async function createMarAgent(options) {
|
|
|
1270
1291
|
await selectedTurn?.close().catch(() => undefined);
|
|
1271
1292
|
if (titleAdapter !== undefined && titleAdapter !== selectedAdapter)
|
|
1272
1293
|
executionScopedAdapters.add(titleAdapter);
|
|
1273
|
-
|
|
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
|
+
}
|
|
1274
1304
|
subagents?.endParentExecution(executionId);
|
|
1275
1305
|
await releaseSession?.();
|
|
1276
1306
|
active.delete(`${sessionId}:${executionId}`);
|
|
@@ -1346,30 +1376,60 @@ export async function createMarAgent(options) {
|
|
|
1346
1376
|
const activeSessions = activeSessionIds();
|
|
1347
1377
|
if ([...activeSessions].some((sessionId) => sessionRuntimes.get(sessionId)?.sourceType !== 'subagent'))
|
|
1348
1378
|
throw new MarAgentError('MAR_AGENT_EXECUTION_ACTIVE', 'Models cannot be reconfigured while a main execution is active.');
|
|
1349
|
-
const activeAdapters = new Set();
|
|
1379
|
+
const activeAdapters = new Set(activeExecutionAdapters);
|
|
1350
1380
|
for (const [sessionId, runtime] of sessionRuntimes)
|
|
1351
1381
|
if (activeSessions.has(sessionId))
|
|
1352
1382
|
for (const adapter of runtime.adapters.values())
|
|
1353
1383
|
activeAdapters.add(adapter);
|
|
1354
1384
|
const inactiveRuntimes = [...sessionRuntimes].flatMap(([sessionId, runtime]) => activeSessions.has(sessionId) ? [] : [runtime]);
|
|
1355
|
-
const adapters = inactiveRuntimes
|
|
1356
|
-
.flatMap((runtime) => [...runtime.adapters.values()])
|
|
1357
|
-
.filter((adapter) => !activeAdapters.has(adapter));
|
|
1358
1385
|
reconfiguring = true;
|
|
1359
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
|
+
}
|
|
1360
1409
|
await closeModelAdapters(adapters);
|
|
1361
1410
|
if (disposed)
|
|
1362
1411
|
throw new MarAgentError('MAR_AGENT_DISPOSED', 'Agent is disposed.');
|
|
1412
|
+
for (const commit of reconfigurationCommits.values())
|
|
1413
|
+
commit();
|
|
1363
1414
|
models = next.models;
|
|
1364
1415
|
defaultModelId = next.defaultModelId;
|
|
1365
1416
|
modelGeneration++;
|
|
1366
1417
|
for (const provider of next.credentialProviders)
|
|
1367
1418
|
credentialProviders.add(provider);
|
|
1368
1419
|
for (const runtime of inactiveRuntimes) {
|
|
1369
|
-
runtime.responsesChain = undefined;
|
|
1370
|
-
runtime.adapters.clear();
|
|
1371
1420
|
runtime.adapterGeneration = modelGeneration;
|
|
1372
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]);
|
|
1373
1433
|
}
|
|
1374
1434
|
finally {
|
|
1375
1435
|
reconfiguring = false;
|
|
@@ -1512,9 +1572,45 @@ function createModelTurnSession(adapter) {
|
|
|
1512
1572
|
}
|
|
1513
1573
|
},
|
|
1514
1574
|
resetContinuation: () => turn.resetContinuation?.(),
|
|
1575
|
+
continuationCheckpoint: () => turn.continuationCheckpoint?.(),
|
|
1515
1576
|
close: () => turn.close()
|
|
1516
1577
|
};
|
|
1517
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 === 2 &&
|
|
1604
|
+
checkpoint.protocol === 'OPENAI_RESPONSES' &&
|
|
1605
|
+
isBoundedString(checkpoint.configurationHash, 64) &&
|
|
1606
|
+
isBoundedString(checkpoint.responseId, 1_024) &&
|
|
1607
|
+
Number.isSafeInteger(checkpoint.inputPrefixLength) &&
|
|
1608
|
+
checkpoint.inputPrefixLength >= 0 &&
|
|
1609
|
+
isBoundedString(checkpoint.inputPrefixHash, 64));
|
|
1610
|
+
}
|
|
1611
|
+
function isBoundedString(value, maximumLength) {
|
|
1612
|
+
return typeof value === 'string' && value.length > 0 && value.length <= maximumLength;
|
|
1613
|
+
}
|
|
1518
1614
|
function modelAttemptFailureDiagnostic(diagnostic, summary) {
|
|
1519
1615
|
if (summary === undefined)
|
|
1520
1616
|
return diagnostic;
|
|
@@ -1526,7 +1622,16 @@ function modelAttemptFailureDiagnostic(diagnostic, summary) {
|
|
|
1526
1622
|
};
|
|
1527
1623
|
}
|
|
1528
1624
|
async function closeModelAdapters(adapters) {
|
|
1529
|
-
await Promise.
|
|
1625
|
+
const results = await Promise.allSettled([...new Set(adapters)].flatMap((adapter) => adapter.close === undefined ? [] : [adapter.close()]));
|
|
1626
|
+
const failure = results.find((result) => result.status === 'rejected');
|
|
1627
|
+
if (failure !== undefined)
|
|
1628
|
+
throw failure.reason;
|
|
1629
|
+
}
|
|
1630
|
+
function addModelCredentialProviders(target, configuration) {
|
|
1631
|
+
if (configuration.credentialProvider !== undefined)
|
|
1632
|
+
target.add(configuration.credentialProvider);
|
|
1633
|
+
if (configuration.imageGeneration !== undefined)
|
|
1634
|
+
target.add(configuration.imageGeneration.credentialProvider);
|
|
1530
1635
|
}
|
|
1531
1636
|
function truncateUtf8(value, maximumBytes) {
|
|
1532
1637
|
const bytes = Buffer.from(value);
|
|
@@ -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 = [];
|
|
@@ -33,7 +33,10 @@ function parseAgentMessageInput(value) {
|
|
|
33
33
|
return invalid();
|
|
34
34
|
const input = value;
|
|
35
35
|
if (Object.keys(input).some((key) => !['agentId', 'message', 'delivery'].includes(key)) ||
|
|
36
|
-
(input.delivery !== undefined &&
|
|
36
|
+
(input.delivery !== undefined &&
|
|
37
|
+
input.delivery !== null &&
|
|
38
|
+
input.delivery !== 'append' &&
|
|
39
|
+
input.delivery !== 'replace') ||
|
|
37
40
|
typeof input.agentId !== 'string' ||
|
|
38
41
|
input.agentId.trim().length === 0 ||
|
|
39
42
|
typeof input.message !== 'string' ||
|
|
@@ -43,7 +46,9 @@ function parseAgentMessageInput(value) {
|
|
|
43
46
|
return {
|
|
44
47
|
agentId: input.agentId.trim(),
|
|
45
48
|
message: input.message,
|
|
46
|
-
...(input.delivery ===
|
|
49
|
+
...(input.delivery === 'append' || input.delivery === 'replace'
|
|
50
|
+
? { delivery: input.delivery }
|
|
51
|
+
: {})
|
|
47
52
|
};
|
|
48
53
|
}
|
|
49
54
|
function invalid() {
|
|
@@ -62,6 +62,7 @@ function parseAgentStartInput(value) {
|
|
|
62
62
|
input.modelId !== null &&
|
|
63
63
|
(typeof input.modelId !== 'string' || input.modelId.trim().length === 0)) ||
|
|
64
64
|
(input.reasoningEffort !== undefined &&
|
|
65
|
+
input.reasoningEffort !== null &&
|
|
65
66
|
(typeof input.reasoningEffort !== 'string' ||
|
|
66
67
|
!marAgentReasoningEffortSchema.safeParse(input.reasoningEffort.trim()).success)) ||
|
|
67
68
|
(input.background !== undefined &&
|
package/dist/tools/agent-wait.js
CHANGED
|
@@ -46,11 +46,14 @@ function parseAgentWaitInput(value) {
|
|
|
46
46
|
const input = value;
|
|
47
47
|
if (Object.keys(input).some((key) => !['agentId', 'waitFor', 'waitMs'].includes(key)) ||
|
|
48
48
|
(input.agentId !== undefined &&
|
|
49
|
+
input.agentId !== null &&
|
|
49
50
|
(typeof input.agentId !== 'string' || input.agentId.trim().length === 0)) ||
|
|
50
51
|
(input.waitFor !== undefined &&
|
|
52
|
+
input.waitFor !== null &&
|
|
51
53
|
input.waitFor !== 'completion' &&
|
|
52
54
|
input.waitFor !== 'message') ||
|
|
53
55
|
(input.waitMs !== undefined &&
|
|
56
|
+
input.waitMs !== null &&
|
|
54
57
|
(!Number.isInteger(input.waitMs) ||
|
|
55
58
|
input.waitMs < TOOL_EXECUTION_LIMITS.agentWaitMinWaitMs ||
|
|
56
59
|
input.waitMs > TOOL_EXECUTION_LIMITS.agentWaitMaxWaitMs)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myagentroam/agent",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.85",
|
|
4
4
|
"description": "Embeddable MAR coding agent SDK and CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"build": "node ../scripts/clean-build-output.mjs dist && tsc -p tsconfig.json",
|
|
43
43
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
44
44
|
"test:unit": "vitest run --config vitest.config.ts test/unit",
|
|
45
|
+
"analyze:session": "node test/tools/session-analyzer.mjs",
|
|
45
46
|
"benchmark:compaction": "node test/e2e/compaction-benchmark.mjs",
|
|
46
47
|
"test:performance": "vitest run --config vitest.performance.config.ts",
|
|
47
48
|
"test:memory": "node --expose-gc ./node_modules/vitest/vitest.mjs run --config vitest.memory.config.ts",
|