@gajae-code/agent-core 0.2.2 → 0.2.4
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/CHANGELOG.md +6 -0
- package/dist/types/agent-loop.d.ts +1 -0
- package/dist/types/agent.d.ts +13 -0
- package/dist/types/append-only-context.d.ts +10 -0
- package/dist/types/types.d.ts +6 -0
- package/package.json +4 -4
- package/src/agent-loop.ts +4 -3
- package/src/agent.ts +43 -0
- package/src/append-only-context.ts +83 -8
- package/src/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -51,5 +51,6 @@ export declare function agentLoopContinueDetailed(context: AgentContext, config:
|
|
|
51
51
|
readonly stream: EventStream<AgentEvent, AgentMessage[]>;
|
|
52
52
|
readonly detailed: () => Promise<AgentLoopDetailedResult>;
|
|
53
53
|
};
|
|
54
|
+
export declare function normalizeMessagesForProvider(messages: Context["messages"], model: AgentLoopConfig["model"]): Context["messages"];
|
|
54
55
|
export declare const INTENT_FIELD = "_i";
|
|
55
56
|
export declare function normalizeTools(tools: AgentContext["tools"], injectIntent: boolean): Context["tools"];
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ export interface AgentOptions {
|
|
|
49
49
|
* Used by providers that support session-based caching (e.g., OpenAI code provider).
|
|
50
50
|
*/
|
|
51
51
|
sessionId?: string;
|
|
52
|
+
/** Provider-facing cache/session affinity identifier. */
|
|
53
|
+
providerSessionId?: string;
|
|
52
54
|
/**
|
|
53
55
|
* Shared provider state map for session-scoped transport/session caches.
|
|
54
56
|
*/
|
|
@@ -108,6 +110,10 @@ export interface AgentOptions {
|
|
|
108
110
|
* Default: 60000 (60 seconds). Set to 0 to disable the cap.
|
|
109
111
|
*/
|
|
110
112
|
maxRetryDelayMs?: number;
|
|
113
|
+
/** Provider request retry budget. Counts retries, not the initial attempt. */
|
|
114
|
+
requestMaxRetries?: number;
|
|
115
|
+
/** Provider stream replay retry budget. Counts retries, not the initial attempt. */
|
|
116
|
+
streamMaxRetries?: number;
|
|
111
117
|
/**
|
|
112
118
|
* Provides tool execution context, resolved per tool call.
|
|
113
119
|
* Use for late-bound UI or session state access.
|
|
@@ -157,6 +163,7 @@ export interface AgentPromptOptions {
|
|
|
157
163
|
}
|
|
158
164
|
export declare class Agent {
|
|
159
165
|
#private;
|
|
166
|
+
get intentTracing(): boolean;
|
|
160
167
|
streamFn: StreamFn;
|
|
161
168
|
getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
|
|
162
169
|
getAuthCredentialType?: (provider: string) => "api_key" | "oauth" | undefined;
|
|
@@ -180,6 +187,8 @@ export declare class Agent {
|
|
|
180
187
|
* Call this when switching sessions (new session, branch, resume).
|
|
181
188
|
*/
|
|
182
189
|
set sessionId(value: string | undefined);
|
|
190
|
+
get providerSessionId(): string | undefined;
|
|
191
|
+
set providerSessionId(value: string | undefined);
|
|
183
192
|
/**
|
|
184
193
|
* Static metadata forwarded to every API request when no resolver is installed
|
|
185
194
|
* (e.g. `metadata.user_id` for Anthropic session attribution). Setting this
|
|
@@ -268,6 +277,10 @@ export declare class Agent {
|
|
|
268
277
|
* Set to 0 to disable the cap.
|
|
269
278
|
*/
|
|
270
279
|
set maxRetryDelayMs(value: number | undefined);
|
|
280
|
+
get requestMaxRetries(): number | undefined;
|
|
281
|
+
set requestMaxRetries(value: number | undefined);
|
|
282
|
+
get streamMaxRetries(): number | undefined;
|
|
283
|
+
set streamMaxRetries(value: number | undefined);
|
|
271
284
|
get state(): AgentState;
|
|
272
285
|
get appendOnlyContext(): AppendOnlyContextManager | undefined;
|
|
273
286
|
setAppendOnlyContext(manager?: AppendOnlyContextManager): void;
|
|
@@ -39,6 +39,8 @@ export declare class StablePrefix {
|
|
|
39
39
|
get fingerprint(): string;
|
|
40
40
|
get version(): number;
|
|
41
41
|
get built(): boolean;
|
|
42
|
+
exportSnapshot(): StablePrefixSnapshot | null;
|
|
43
|
+
importSnapshot(snapshot: StablePrefixSnapshot, options: BuildOptions): void;
|
|
42
44
|
/**
|
|
43
45
|
* Build or rebuild from live context.
|
|
44
46
|
* Returns `true` if the prefix actually changed (cache miss imminent).
|
|
@@ -94,6 +96,11 @@ export declare class AppendOnlyContextManager {
|
|
|
94
96
|
#private;
|
|
95
97
|
readonly prefix: StablePrefix;
|
|
96
98
|
readonly log: AppendOnlyLog;
|
|
99
|
+
static forkFromSeed(args: {
|
|
100
|
+
prefixSnapshot?: StablePrefixSnapshot;
|
|
101
|
+
messages?: readonly Message[];
|
|
102
|
+
options: BuildOptions;
|
|
103
|
+
}): AppendOnlyContextManager;
|
|
97
104
|
build(context: AgentContext, options: BuildOptions): Context;
|
|
98
105
|
/**
|
|
99
106
|
* Sync normalized (provider-level) messages into the append-only log.
|
|
@@ -102,6 +109,9 @@ export declare class AppendOnlyContextManager {
|
|
|
102
109
|
* (same length, changed content via a rolling digest).
|
|
103
110
|
*/
|
|
104
111
|
syncMessages(normalizedMessages: any[]): void;
|
|
112
|
+
seedNormalizedMessages(messages: readonly Message[], options?: {
|
|
113
|
+
reset?: boolean;
|
|
114
|
+
}): void;
|
|
105
115
|
/** Reset prefix + log for a model/provider switch while mode stays active. */
|
|
106
116
|
invalidateForModelChange(): void;
|
|
107
117
|
/** Reset the sync cursor AND clear the log. */
|
package/dist/types/types.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
21
21
|
* Used by providers that support session-based caching (e.g., OpenAI code provider).
|
|
22
22
|
*/
|
|
23
23
|
sessionId?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional provider-facing cache/session affinity identifier. When set, this
|
|
26
|
+
* is forwarded to providers as StreamOptions.sessionId while `sessionId`
|
|
27
|
+
* remains the logical agent conversation id for telemetry/metadata.
|
|
28
|
+
*/
|
|
29
|
+
providerSessionId?: string;
|
|
24
30
|
/**
|
|
25
31
|
* Optional resolver called per LLM request to produce request metadata.
|
|
26
32
|
* When set, the agent loop evaluates it **after** `getApiKey` resolves the
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/agent-core",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.4",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"fmt": "biome format --write ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gajae-code/ai": "0.2.
|
|
39
|
-
"@gajae-code/natives": "0.2.
|
|
40
|
-
"@gajae-code/utils": "0.2.
|
|
38
|
+
"@gajae-code/ai": "0.2.4",
|
|
39
|
+
"@gajae-code/natives": "0.2.4",
|
|
40
|
+
"@gajae-code/utils": "0.2.4",
|
|
41
41
|
"@opentelemetry/api": "^1.9.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -300,7 +300,7 @@ function createDetailedCapture(config: AgentLoopConfig): {
|
|
|
300
300
|
};
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
function normalizeMessagesForProvider(
|
|
303
|
+
export function normalizeMessagesForProvider(
|
|
304
304
|
messages: Context["messages"],
|
|
305
305
|
model: AgentLoopConfig["model"],
|
|
306
306
|
): Context["messages"] {
|
|
@@ -735,6 +735,7 @@ async function streamAssistantResponse(
|
|
|
735
735
|
apiKey: resolvedApiKey,
|
|
736
736
|
authCredentialType,
|
|
737
737
|
metadata: resolvedMetadata,
|
|
738
|
+
sessionId: config.providerSessionId ?? config.sessionId,
|
|
738
739
|
toolChoice: effectiveToolChoice,
|
|
739
740
|
reasoning: effectiveReasoning,
|
|
740
741
|
temperature: effectiveTemperature,
|
|
@@ -857,7 +858,7 @@ async function streamAssistantResponse(
|
|
|
857
858
|
}
|
|
858
859
|
|
|
859
860
|
function emitAbortedAssistantMessage(
|
|
860
|
-
|
|
861
|
+
partialMessage: AssistantMessage | null,
|
|
861
862
|
addedPartial: boolean,
|
|
862
863
|
context: AgentContext,
|
|
863
864
|
config: AgentLoopConfig,
|
|
@@ -867,7 +868,7 @@ function emitAbortedAssistantMessage(
|
|
|
867
868
|
const now = Date.now();
|
|
868
869
|
const abortedMessage: AssistantMessage = {
|
|
869
870
|
role: "assistant",
|
|
870
|
-
content: [],
|
|
871
|
+
content: partialMessage ? structuredClone(partialMessage.content) : [],
|
|
871
872
|
api: config.model.api,
|
|
872
873
|
provider: config.model.provider,
|
|
873
874
|
model: config.model.id,
|
package/src/agent.ts
CHANGED
|
@@ -118,6 +118,8 @@ export interface AgentOptions {
|
|
|
118
118
|
* Used by providers that support session-based caching (e.g., OpenAI code provider).
|
|
119
119
|
*/
|
|
120
120
|
sessionId?: string;
|
|
121
|
+
/** Provider-facing cache/session affinity identifier. */
|
|
122
|
+
providerSessionId?: string;
|
|
121
123
|
/**
|
|
122
124
|
* Shared provider state map for session-scoped transport/session caches.
|
|
123
125
|
*/
|
|
@@ -183,6 +185,10 @@ export interface AgentOptions {
|
|
|
183
185
|
* Default: 60000 (60 seconds). Set to 0 to disable the cap.
|
|
184
186
|
*/
|
|
185
187
|
maxRetryDelayMs?: number;
|
|
188
|
+
/** Provider request retry budget. Counts retries, not the initial attempt. */
|
|
189
|
+
requestMaxRetries?: number;
|
|
190
|
+
/** Provider stream replay retry budget. Counts retries, not the initial attempt. */
|
|
191
|
+
streamMaxRetries?: number;
|
|
186
192
|
|
|
187
193
|
/**
|
|
188
194
|
* Provides tool execution context, resolved per tool call.
|
|
@@ -269,6 +275,7 @@ export class Agent {
|
|
|
269
275
|
#followUpMode: "all" | "one-at-a-time";
|
|
270
276
|
#interruptMode: "immediate" | "wait";
|
|
271
277
|
#sessionId?: string;
|
|
278
|
+
#providerSessionId?: string;
|
|
272
279
|
#metadata?: Record<string, unknown>;
|
|
273
280
|
#metadataResolver?: (provider: string) => Record<string, unknown> | undefined;
|
|
274
281
|
#providerSessionState?: Map<string, ProviderSessionState>;
|
|
@@ -282,6 +289,8 @@ export class Agent {
|
|
|
282
289
|
#serviceTier?: ServiceTier;
|
|
283
290
|
#hideThinkingSummary?: boolean;
|
|
284
291
|
#maxRetryDelayMs?: number;
|
|
292
|
+
#requestMaxRetries?: number;
|
|
293
|
+
#streamMaxRetries?: number;
|
|
285
294
|
#getToolContext?: (toolCall?: ToolCallContext) => AgentToolContext | undefined;
|
|
286
295
|
#cursorExecHandlers?: CursorExecHandlers;
|
|
287
296
|
#cursorOnToolResult?: CursorToolResultHandler;
|
|
@@ -303,6 +312,10 @@ export class Agent {
|
|
|
303
312
|
#telemetry?: AgentLoopConfig["telemetry"];
|
|
304
313
|
#appendOnlyContext?: AppendOnlyContextManager;
|
|
305
314
|
|
|
315
|
+
get intentTracing(): boolean {
|
|
316
|
+
return this.#intentTracing;
|
|
317
|
+
}
|
|
318
|
+
|
|
306
319
|
/** Buffered Cursor tool results with text length at time of call (for correct ordering) */
|
|
307
320
|
#cursorToolResultBuffer: CursorToolResultEntry[] = [];
|
|
308
321
|
|
|
@@ -329,6 +342,7 @@ export class Agent {
|
|
|
329
342
|
this.#interruptMode = opts.interruptMode || "immediate";
|
|
330
343
|
this.streamFn = opts.streamFn || streamSimple;
|
|
331
344
|
this.#sessionId = opts.sessionId;
|
|
345
|
+
this.#providerSessionId = opts.providerSessionId;
|
|
332
346
|
this.#providerSessionState = opts.providerSessionState;
|
|
333
347
|
this.#thinkingBudgets = opts.thinkingBudgets;
|
|
334
348
|
this.#temperature = opts.temperature;
|
|
@@ -340,6 +354,8 @@ export class Agent {
|
|
|
340
354
|
this.#serviceTier = opts.serviceTier;
|
|
341
355
|
this.#hideThinkingSummary = opts.hideThinkingSummary;
|
|
342
356
|
this.#maxRetryDelayMs = opts.maxRetryDelayMs;
|
|
357
|
+
this.#requestMaxRetries = opts.requestMaxRetries;
|
|
358
|
+
this.#streamMaxRetries = opts.streamMaxRetries;
|
|
343
359
|
this.getApiKey = opts.getApiKey;
|
|
344
360
|
this.getAuthCredentialType = opts.getAuthCredentialType;
|
|
345
361
|
this.#onPayload = opts.onPayload;
|
|
@@ -376,6 +392,14 @@ export class Agent {
|
|
|
376
392
|
this.#sessionId = value;
|
|
377
393
|
}
|
|
378
394
|
|
|
395
|
+
get providerSessionId(): string | undefined {
|
|
396
|
+
return this.#providerSessionId;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
set providerSessionId(value: string | undefined) {
|
|
400
|
+
this.#providerSessionId = value;
|
|
401
|
+
}
|
|
402
|
+
|
|
379
403
|
/**
|
|
380
404
|
* Static metadata forwarded to every API request when no resolver is installed
|
|
381
405
|
* (e.g. `metadata.user_id` for Anthropic session attribution). Setting this
|
|
@@ -550,6 +574,22 @@ export class Agent {
|
|
|
550
574
|
this.#maxRetryDelayMs = value;
|
|
551
575
|
}
|
|
552
576
|
|
|
577
|
+
get requestMaxRetries(): number | undefined {
|
|
578
|
+
return this.#requestMaxRetries;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
set requestMaxRetries(value: number | undefined) {
|
|
582
|
+
this.#requestMaxRetries = value;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
get streamMaxRetries(): number | undefined {
|
|
586
|
+
return this.#streamMaxRetries;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
set streamMaxRetries(value: number | undefined) {
|
|
590
|
+
this.#streamMaxRetries = value;
|
|
591
|
+
}
|
|
592
|
+
|
|
553
593
|
get state(): AgentState {
|
|
554
594
|
return this.#state;
|
|
555
595
|
}
|
|
@@ -1070,11 +1110,14 @@ export class Agent {
|
|
|
1070
1110
|
hideThinkingSummary: this.#hideThinkingSummary,
|
|
1071
1111
|
interruptMode: this.#interruptMode,
|
|
1072
1112
|
sessionId: this.#sessionId,
|
|
1113
|
+
providerSessionId: this.#providerSessionId,
|
|
1073
1114
|
metadata: this.#metadataResolver ? undefined : this.#metadata,
|
|
1074
1115
|
metadataResolver: this.#metadataResolver,
|
|
1075
1116
|
providerSessionState: this.#providerSessionState,
|
|
1076
1117
|
thinkingBudgets: this.#thinkingBudgets,
|
|
1077
1118
|
maxRetryDelayMs: this.#maxRetryDelayMs,
|
|
1119
|
+
requestMaxRetries: this.#requestMaxRetries,
|
|
1120
|
+
streamMaxRetries: this.#streamMaxRetries,
|
|
1078
1121
|
kimiApiFormat: this.#kimiApiFormat,
|
|
1079
1122
|
preferWebsockets: this.#preferWebsockets,
|
|
1080
1123
|
convertToLlm: this.#convertToLlm,
|
|
@@ -57,6 +57,23 @@ export class StablePrefix {
|
|
|
57
57
|
return this.#snapshot !== null;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
exportSnapshot(): StablePrefixSnapshot | null {
|
|
61
|
+
return this.#snapshot ? cloneJson(this.#snapshot) : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
importSnapshot(snapshot: StablePrefixSnapshot, options: BuildOptions): void {
|
|
65
|
+
const systemPrompt = cloneJson(snapshot.systemPrompt);
|
|
66
|
+
const tools = normalizeImportedTools(snapshot.tools, options);
|
|
67
|
+
const fingerprint = computeFingerprint(systemPrompt, tools, options);
|
|
68
|
+
if (fingerprint !== snapshot.fingerprint) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`StablePrefix.importSnapshot() fingerprint mismatch: expected ${fingerprint}, received ${snapshot.fingerprint}`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
this.#snapshot = { systemPrompt, tools, fingerprint };
|
|
74
|
+
this.#version++;
|
|
75
|
+
}
|
|
76
|
+
|
|
60
77
|
/**
|
|
61
78
|
* Build or rebuild from live context.
|
|
62
79
|
* Returns `true` if the prefix actually changed (cache miss imminent).
|
|
@@ -83,7 +100,7 @@ export class StablePrefix {
|
|
|
83
100
|
toContext(): { systemPrompt: string[]; tools: Tool[] } {
|
|
84
101
|
const s = this.#snapshot;
|
|
85
102
|
if (!s) throw new Error("StablePrefix.toContext() called before build()");
|
|
86
|
-
return { systemPrompt: s.systemPrompt, tools: s.tools };
|
|
103
|
+
return { systemPrompt: cloneJson(s.systemPrompt), tools: cloneJson(s.tools) };
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
|
|
@@ -160,6 +177,23 @@ export class AppendOnlyContextManager {
|
|
|
160
177
|
#lastSyncCount = 0;
|
|
161
178
|
/** Rolling digest of synced message content — detects in-place rewrites. */
|
|
162
179
|
#syncedDigest = 0;
|
|
180
|
+
/** Number of provider-normalized messages that were seeded before child-local messages. */
|
|
181
|
+
#seededPrefixCount = 0;
|
|
182
|
+
|
|
183
|
+
static forkFromSeed(args: {
|
|
184
|
+
prefixSnapshot?: StablePrefixSnapshot;
|
|
185
|
+
messages?: readonly Message[];
|
|
186
|
+
options: BuildOptions;
|
|
187
|
+
}): AppendOnlyContextManager {
|
|
188
|
+
const manager = new AppendOnlyContextManager();
|
|
189
|
+
if (args.prefixSnapshot) {
|
|
190
|
+
manager.prefix.importSnapshot(args.prefixSnapshot, args.options);
|
|
191
|
+
}
|
|
192
|
+
if (args.messages) {
|
|
193
|
+
manager.seedNormalizedMessages(args.messages);
|
|
194
|
+
}
|
|
195
|
+
return manager;
|
|
196
|
+
}
|
|
163
197
|
|
|
164
198
|
build(context: AgentContext, options: BuildOptions): Context {
|
|
165
199
|
this.prefix.build(context, options);
|
|
@@ -174,29 +208,57 @@ export class AppendOnlyContextManager {
|
|
|
174
208
|
* (same length, changed content via a rolling digest).
|
|
175
209
|
*/
|
|
176
210
|
syncMessages(normalizedMessages: any[]): void {
|
|
211
|
+
const seededPrefix = this.#seededPrefixCount > 0 ? this.log.toMessages().slice(0, this.#seededPrefixCount) : [];
|
|
212
|
+
const includesSeedPrefix =
|
|
213
|
+
seededPrefix.length > 0 &&
|
|
214
|
+
normalizedMessages.length >= seededPrefix.length &&
|
|
215
|
+
this.#computeDigest(normalizedMessages.slice(0, seededPrefix.length)) === this.#computeDigest(seededPrefix);
|
|
216
|
+
const messagesToSync =
|
|
217
|
+
seededPrefix.length > 0 && !includesSeedPrefix ? [...seededPrefix, ...normalizedMessages] : normalizedMessages;
|
|
218
|
+
|
|
177
219
|
// Detect in-place rewrites of already-synced messages.
|
|
178
220
|
if (
|
|
179
221
|
this.#lastSyncCount > 0 &&
|
|
180
|
-
this.#lastSyncCount <=
|
|
181
|
-
this.#computeDigest(
|
|
222
|
+
this.#lastSyncCount <= messagesToSync.length &&
|
|
223
|
+
this.#computeDigest(messagesToSync.slice(0, this.#lastSyncCount)) !== this.#syncedDigest
|
|
182
224
|
) {
|
|
225
|
+
if (this.#seededPrefixCount > 0) {
|
|
226
|
+
throw new Error("AppendOnlyContextManager.syncMessages() seed prefix changed");
|
|
227
|
+
}
|
|
183
228
|
this.log.clear();
|
|
184
229
|
this.#lastSyncCount = 0;
|
|
185
230
|
}
|
|
186
231
|
|
|
187
|
-
// Compaction — array shrunk.
|
|
188
|
-
|
|
232
|
+
// Compaction — array shrunk. Seeded forks preserve the inherited prefix
|
|
233
|
+
// and append child-local deltas, so a shorter child message array is not a
|
|
234
|
+
// compaction signal while a seed prefix is active.
|
|
235
|
+
if (messagesToSync.length < this.#lastSyncCount) {
|
|
236
|
+
if (this.#seededPrefixCount > 0) {
|
|
237
|
+
throw new Error("AppendOnlyContextManager.syncMessages() cannot compact a seeded fork without reset");
|
|
238
|
+
}
|
|
189
239
|
this.log.clear();
|
|
190
240
|
this.#lastSyncCount = 0;
|
|
191
241
|
}
|
|
192
242
|
|
|
193
|
-
const newMsgs =
|
|
243
|
+
const newMsgs = messagesToSync.slice(this.#lastSyncCount);
|
|
194
244
|
for (const msg of newMsgs) {
|
|
195
245
|
this.log.append(msg);
|
|
196
246
|
}
|
|
197
247
|
|
|
198
|
-
this.#lastSyncCount =
|
|
199
|
-
this.#syncedDigest = this.#computeDigest(
|
|
248
|
+
this.#lastSyncCount = messagesToSync.length;
|
|
249
|
+
this.#syncedDigest = this.#computeDigest(messagesToSync);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
seedNormalizedMessages(messages: readonly Message[], options?: { reset?: boolean }): void {
|
|
253
|
+
if (this.log.length > 0 && options?.reset !== true) {
|
|
254
|
+
throw new Error("AppendOnlyContextManager.seedNormalizedMessages() cannot seed a non-empty log without reset");
|
|
255
|
+
}
|
|
256
|
+
const clonedMessages = cloneJson([...messages]);
|
|
257
|
+
this.log.clear();
|
|
258
|
+
this.log.extend(clonedMessages);
|
|
259
|
+
this.#lastSyncCount = clonedMessages.length;
|
|
260
|
+
this.#syncedDigest = this.#computeDigest(clonedMessages);
|
|
261
|
+
this.#seededPrefixCount = clonedMessages.length;
|
|
200
262
|
}
|
|
201
263
|
|
|
202
264
|
/** Reset prefix + log for a model/provider switch while mode stays active. */
|
|
@@ -205,6 +267,7 @@ export class AppendOnlyContextManager {
|
|
|
205
267
|
this.log.clear();
|
|
206
268
|
this.#lastSyncCount = 0;
|
|
207
269
|
this.#syncedDigest = 0;
|
|
270
|
+
this.#seededPrefixCount = 0;
|
|
208
271
|
}
|
|
209
272
|
|
|
210
273
|
/** Reset the sync cursor AND clear the log. */
|
|
@@ -212,6 +275,7 @@ export class AppendOnlyContextManager {
|
|
|
212
275
|
this.log.clear();
|
|
213
276
|
this.#lastSyncCount = 0;
|
|
214
277
|
this.#syncedDigest = 0;
|
|
278
|
+
this.#seededPrefixCount = 0;
|
|
215
279
|
}
|
|
216
280
|
|
|
217
281
|
appendMessage(message: any): void {
|
|
@@ -231,6 +295,7 @@ export class AppendOnlyContextManager {
|
|
|
231
295
|
this.log.clear();
|
|
232
296
|
this.#lastSyncCount = 0;
|
|
233
297
|
this.#syncedDigest = 0;
|
|
298
|
+
this.#seededPrefixCount = 0;
|
|
234
299
|
this.prefix.build(context, options);
|
|
235
300
|
}
|
|
236
301
|
|
|
@@ -276,6 +341,16 @@ function takeSnapshot(context: AgentContext, options: BuildOptions): StablePrefi
|
|
|
276
341
|
};
|
|
277
342
|
}
|
|
278
343
|
|
|
344
|
+
function normalizeImportedTools(tools: readonly Tool[], options: BuildOptions): Tool[] {
|
|
345
|
+
const clonedTools = cloneJson(tools);
|
|
346
|
+
const normalizedTools = normalizeTools(clonedTools as AgentContext["tools"], options.intentTracing) ?? [];
|
|
347
|
+
return cloneJson(normalizedTools);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function cloneJson<T>(value: T): T {
|
|
351
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
352
|
+
}
|
|
353
|
+
|
|
279
354
|
function computeFingerprint(systemPrompt: string[], tools: Tool[], options: BuildOptions): string {
|
|
280
355
|
const payload = JSON.stringify({
|
|
281
356
|
s: systemPrompt,
|
package/src/types.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
43
43
|
* Used by providers that support session-based caching (e.g., OpenAI code provider).
|
|
44
44
|
*/
|
|
45
45
|
sessionId?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Optional provider-facing cache/session affinity identifier. When set, this
|
|
48
|
+
* is forwarded to providers as StreamOptions.sessionId while `sessionId`
|
|
49
|
+
* remains the logical agent conversation id for telemetry/metadata.
|
|
50
|
+
*/
|
|
51
|
+
providerSessionId?: string;
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
54
|
* Optional resolver called per LLM request to produce request metadata.
|