@gajae-code/agent-core 0.2.1 → 0.2.3
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 +5 -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 +22 -23
- package/src/agent.ts +17 -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
|
*/
|
|
@@ -157,6 +159,7 @@ export interface AgentPromptOptions {
|
|
|
157
159
|
}
|
|
158
160
|
export declare class Agent {
|
|
159
161
|
#private;
|
|
162
|
+
get intentTracing(): boolean;
|
|
160
163
|
streamFn: StreamFn;
|
|
161
164
|
getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
|
|
162
165
|
getAuthCredentialType?: (provider: string) => "api_key" | "oauth" | undefined;
|
|
@@ -180,6 +183,8 @@ export declare class Agent {
|
|
|
180
183
|
* Call this when switching sessions (new session, branch, resume).
|
|
181
184
|
*/
|
|
182
185
|
set sessionId(value: string | undefined);
|
|
186
|
+
get providerSessionId(): string | undefined;
|
|
187
|
+
set providerSessionId(value: string | undefined);
|
|
183
188
|
/**
|
|
184
189
|
* Static metadata forwarded to every API request when no resolver is installed
|
|
185
190
|
* (e.g. `metadata.user_id` for Anthropic session attribution). Setting this
|
|
@@ -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.3",
|
|
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.3",
|
|
39
|
+
"@gajae-code/natives": "0.2.3",
|
|
40
|
+
"@gajae-code/utils": "0.2.3",
|
|
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,
|
|
@@ -864,30 +865,28 @@ function emitAbortedAssistantMessage(
|
|
|
864
865
|
stream: EventStream<AgentEvent, AgentMessage[]>,
|
|
865
866
|
): AssistantMessage {
|
|
866
867
|
const errorMessage = "Request was aborted";
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
:
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
};
|
|
868
|
+
const now = Date.now();
|
|
869
|
+
const abortedMessage: AssistantMessage = {
|
|
870
|
+
role: "assistant",
|
|
871
|
+
content: partialMessage ? structuredClone(partialMessage.content) : [],
|
|
872
|
+
api: config.model.api,
|
|
873
|
+
provider: config.model.provider,
|
|
874
|
+
model: config.model.id,
|
|
875
|
+
usage: {
|
|
876
|
+
input: 0,
|
|
877
|
+
output: 0,
|
|
878
|
+
cacheRead: 0,
|
|
879
|
+
cacheWrite: 0,
|
|
880
|
+
totalTokens: 0,
|
|
881
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
882
|
+
},
|
|
883
|
+
stopReason: "aborted",
|
|
884
|
+
errorMessage,
|
|
885
|
+
timestamp: now,
|
|
886
|
+
};
|
|
887
887
|
if (addedPartial) {
|
|
888
|
-
context.messages
|
|
888
|
+
context.messages.pop();
|
|
889
889
|
} else {
|
|
890
|
-
context.messages.push(abortedMessage);
|
|
891
890
|
stream.push({ type: "message_start", message: { ...abortedMessage } });
|
|
892
891
|
}
|
|
893
892
|
stream.push({ type: "message_end", message: abortedMessage });
|
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
|
*/
|
|
@@ -269,6 +271,7 @@ export class Agent {
|
|
|
269
271
|
#followUpMode: "all" | "one-at-a-time";
|
|
270
272
|
#interruptMode: "immediate" | "wait";
|
|
271
273
|
#sessionId?: string;
|
|
274
|
+
#providerSessionId?: string;
|
|
272
275
|
#metadata?: Record<string, unknown>;
|
|
273
276
|
#metadataResolver?: (provider: string) => Record<string, unknown> | undefined;
|
|
274
277
|
#providerSessionState?: Map<string, ProviderSessionState>;
|
|
@@ -303,6 +306,10 @@ export class Agent {
|
|
|
303
306
|
#telemetry?: AgentLoopConfig["telemetry"];
|
|
304
307
|
#appendOnlyContext?: AppendOnlyContextManager;
|
|
305
308
|
|
|
309
|
+
get intentTracing(): boolean {
|
|
310
|
+
return this.#intentTracing;
|
|
311
|
+
}
|
|
312
|
+
|
|
306
313
|
/** Buffered Cursor tool results with text length at time of call (for correct ordering) */
|
|
307
314
|
#cursorToolResultBuffer: CursorToolResultEntry[] = [];
|
|
308
315
|
|
|
@@ -329,6 +336,7 @@ export class Agent {
|
|
|
329
336
|
this.#interruptMode = opts.interruptMode || "immediate";
|
|
330
337
|
this.streamFn = opts.streamFn || streamSimple;
|
|
331
338
|
this.#sessionId = opts.sessionId;
|
|
339
|
+
this.#providerSessionId = opts.providerSessionId;
|
|
332
340
|
this.#providerSessionState = opts.providerSessionState;
|
|
333
341
|
this.#thinkingBudgets = opts.thinkingBudgets;
|
|
334
342
|
this.#temperature = opts.temperature;
|
|
@@ -376,6 +384,14 @@ export class Agent {
|
|
|
376
384
|
this.#sessionId = value;
|
|
377
385
|
}
|
|
378
386
|
|
|
387
|
+
get providerSessionId(): string | undefined {
|
|
388
|
+
return this.#providerSessionId;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
set providerSessionId(value: string | undefined) {
|
|
392
|
+
this.#providerSessionId = value;
|
|
393
|
+
}
|
|
394
|
+
|
|
379
395
|
/**
|
|
380
396
|
* Static metadata forwarded to every API request when no resolver is installed
|
|
381
397
|
* (e.g. `metadata.user_id` for Anthropic session attribution). Setting this
|
|
@@ -1070,6 +1086,7 @@ export class Agent {
|
|
|
1070
1086
|
hideThinkingSummary: this.#hideThinkingSummary,
|
|
1071
1087
|
interruptMode: this.#interruptMode,
|
|
1072
1088
|
sessionId: this.#sessionId,
|
|
1089
|
+
providerSessionId: this.#providerSessionId,
|
|
1073
1090
|
metadata: this.#metadataResolver ? undefined : this.#metadata,
|
|
1074
1091
|
metadataResolver: this.#metadataResolver,
|
|
1075
1092
|
providerSessionState: this.#providerSessionState,
|
|
@@ -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.
|