@gajae-code/agent-core 0.16.3 → 0.16.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/compaction/branch-summarization.d.ts +2 -0
- package/dist/types/compaction/compaction.d.ts +4 -0
- package/dist/types/types.d.ts +7 -5
- package/package.json +4 -4
- package/src/agent-loop.ts +16 -5
- package/src/agent.ts +8 -5
- package/src/compaction/branch-summarization.ts +13 -1
- package/src/compaction/compaction.ts +8 -0
- package/src/types.ts +10 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.16.4] - 2026-09-05
|
|
6
|
+
|
|
7
|
+
- Provider calls now carry the agent-owned opaque provider conversation identity separately from generic session/cache affinity, including compaction, handoff, turn-prefix summary, and branch-summary maintenance calls. This lets provider-specific conversation headers remain stable without treating prompt-derived gateway cache keys as conversation authority (#5295).
|
|
8
|
+
|
|
9
|
+
- Failed cooperative context maintenance now terminalizes the active run with its concrete maintenance error instead of advertising a continuation over unchanged context. Only committed prune, compaction, or promotion outcomes remain resumable.
|
|
10
|
+
|
|
5
11
|
## [0.16.3] - 2026-09-04
|
|
6
12
|
|
|
7
13
|
## [0.16.2] - 2026-09-04
|
|
@@ -62,6 +62,8 @@ export interface GenerateBranchSummaryOptions {
|
|
|
62
62
|
* reuses the live turn's provider/WebSocket session.
|
|
63
63
|
*/
|
|
64
64
|
sessionId?: string;
|
|
65
|
+
/** Opaque provider conversation identity; never derived from branch-summary content. */
|
|
66
|
+
providerSessionId?: string;
|
|
65
67
|
/** Shared provider state map so the branch summary call reuses session-scoped transport/session caches. */
|
|
66
68
|
providerSessionState?: Map<string, ProviderSessionState>;
|
|
67
69
|
/** Hint that websocket transport should be preferred when supported by the provider implementation. */
|
|
@@ -209,6 +209,8 @@ export interface SummaryOptions {
|
|
|
209
209
|
* `providerSessionId ?? sessionId` the agent loop sends for normal turns).
|
|
210
210
|
*/
|
|
211
211
|
sessionId?: string;
|
|
212
|
+
/** Opaque provider conversation identity; never derived from compaction content. */
|
|
213
|
+
providerSessionId?: string;
|
|
212
214
|
/** Shared provider state map so maintenance calls reuse session-scoped transport/session caches. */
|
|
213
215
|
providerSessionState?: Map<string, ProviderSessionState>;
|
|
214
216
|
/** Hint that websocket transport should be preferred when supported by the provider implementation. */
|
|
@@ -262,6 +264,8 @@ export interface HandoffOptions {
|
|
|
262
264
|
* reuses the live turn's provider/WebSocket session.
|
|
263
265
|
*/
|
|
264
266
|
sessionId?: string;
|
|
267
|
+
/** Opaque provider conversation identity; never derived from handoff content. */
|
|
268
|
+
providerSessionId?: string;
|
|
265
269
|
/** Shared provider state map so the handoff call reuses session-scoped transport/session caches. */
|
|
266
270
|
providerSessionState?: Map<string, ProviderSessionState>;
|
|
267
271
|
/** Hint that websocket transport should be preferred when supported by the provider implementation. */
|
package/dist/types/types.d.ts
CHANGED
|
@@ -195,14 +195,16 @@ export type ManagedAttemptOutcomeHandler = (outcome: ManagedAttemptOutcome) => M
|
|
|
195
195
|
* Outcome of a cooperative mid-run context-maintenance checkpoint (see
|
|
196
196
|
* {@link AgentLoopConfig.maintainContext}). Any value other than "not-needed"
|
|
197
197
|
* means the checkpoint mutated (or attempted to mutate) durable context, so the
|
|
198
|
-
* loop ends the current run without
|
|
199
|
-
*
|
|
198
|
+
* loop ends the current run without lossy finalization. Committed maintenance
|
|
199
|
+
* resumes on rewritten context; failed or aborted maintenance terminalizes.
|
|
200
200
|
*/
|
|
201
201
|
export type MidRunMaintenanceOutcome = "not-needed" | "pruned" | "compacted" | "promoted" | "failed" | "aborted";
|
|
202
202
|
export interface ContextMaintenanceResult {
|
|
203
203
|
outcome: MidRunMaintenanceOutcome;
|
|
204
204
|
releaseCurrentContext?: boolean;
|
|
205
|
+
errorMessage?: string;
|
|
205
206
|
}
|
|
207
|
+
export declare function isContinuingMidRunMaintenanceOutcome(outcome: unknown): boolean;
|
|
206
208
|
/**
|
|
207
209
|
* Configuration for the agent loop.
|
|
208
210
|
*/
|
|
@@ -407,9 +409,9 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
407
409
|
* non-optional loop signal, and `awaitEventDrain(invocationSignal)` waits for
|
|
408
410
|
* prior event consumer bodies with loop and invocation cancellation composed.
|
|
409
411
|
* Any outcome other than "not-needed" ends the current run with
|
|
410
|
-
* `agent_end.stopReason === "maintenance"
|
|
411
|
-
*
|
|
412
|
-
*
|
|
412
|
+
* `agent_end.stopReason === "maintenance"`. Successful maintenance resumes
|
|
413
|
+
* on rewritten context; `failed` and `aborted` are terminal and never resend
|
|
414
|
+
* the unchanged context.
|
|
413
415
|
*/
|
|
414
416
|
maintainContext?: (context: AgentContext, lifecycle: {
|
|
415
417
|
signal: AbortSignal;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/agent-core",
|
|
4
|
-
"version": "0.16.
|
|
4
|
+
"version": "0.16.4",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"fmt": "biome format --write ."
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@gajae-code/ai": "0.16.
|
|
36
|
-
"@gajae-code/natives": "0.16.
|
|
37
|
-
"@gajae-code/utils": "0.16.
|
|
35
|
+
"@gajae-code/ai": "0.16.4",
|
|
36
|
+
"@gajae-code/natives": "0.16.4",
|
|
37
|
+
"@gajae-code/utils": "0.16.4",
|
|
38
38
|
"@opentelemetry/api": "^1.9.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -89,6 +89,7 @@ import {
|
|
|
89
89
|
type AgentTool,
|
|
90
90
|
type AgentToolContext,
|
|
91
91
|
type AgentToolResult,
|
|
92
|
+
isContinuingMidRunMaintenanceOutcome,
|
|
92
93
|
type ManagedAttemptOutcome,
|
|
93
94
|
type StandaloneRunOwnership,
|
|
94
95
|
type StreamFn,
|
|
@@ -1260,13 +1261,12 @@ function publishAgentEnd(
|
|
|
1260
1261
|
event: Extract<AgentEvent, { type: "agent_end" }>,
|
|
1261
1262
|
scope?: AttemptScope,
|
|
1262
1263
|
): void {
|
|
1263
|
-
//
|
|
1264
|
-
//
|
|
1265
|
-
// stopReason so AgentSession can still report the aborted maintenance
|
|
1266
|
-
// settlement to its consumers.
|
|
1264
|
+
// Only maintenance that committed a context mutation can continue. Failed and
|
|
1265
|
+
// aborted maintenance are terminal so the unchanged context is never resent.
|
|
1267
1266
|
const publishedEvent = scope ? { ...event, scope } : event;
|
|
1268
1267
|
const maintenanceContinues =
|
|
1269
|
-
publishedEvent.stopReason === "maintenance" &&
|
|
1268
|
+
publishedEvent.stopReason === "maintenance" &&
|
|
1269
|
+
isContinuingMidRunMaintenanceOutcome(publishedEvent.maintenanceOutcome);
|
|
1270
1270
|
stream.push(publishedEvent);
|
|
1271
1271
|
const standalone = config.standaloneRunOwnership
|
|
1272
1272
|
? standaloneOwnershipStates.get(config.standaloneRunOwnership)
|
|
@@ -3739,6 +3739,16 @@ async function runLoopBody(
|
|
|
3739
3739
|
}
|
|
3740
3740
|
|
|
3741
3741
|
if (outcome !== "not-needed") {
|
|
3742
|
+
if (outcome === "failed") {
|
|
3743
|
+
const errorMessage =
|
|
3744
|
+
maintenance.errorMessage ??
|
|
3745
|
+
"Context maintenance failed before the next model request; the unchanged context was not resubmitted.";
|
|
3746
|
+
const message = managedFailureMessage(new Error(errorMessage), config);
|
|
3747
|
+
stream.push({ type: "message_start", message, scope: attemptScope });
|
|
3748
|
+
stream.push({ type: "message_end", message, scope: attemptScope });
|
|
3749
|
+
currentContext.messages.push(message);
|
|
3750
|
+
newMessages.push(message);
|
|
3751
|
+
}
|
|
3742
3752
|
publishAgentEnd(
|
|
3743
3753
|
stream,
|
|
3744
3754
|
config,
|
|
@@ -4639,6 +4649,7 @@ async function streamAssistantResponse(
|
|
|
4639
4649
|
authCredentialType,
|
|
4640
4650
|
metadata: resolvedMetadata,
|
|
4641
4651
|
sessionId: config.providerSessionId ?? config.sessionId,
|
|
4652
|
+
providerSessionId: config.providerSessionId,
|
|
4642
4653
|
toolChoice: effectiveToolChoice,
|
|
4643
4654
|
reasoning: effectiveReasoning,
|
|
4644
4655
|
temperature: effectiveTemperature,
|
package/src/agent.ts
CHANGED
|
@@ -54,7 +54,7 @@ import type {
|
|
|
54
54
|
StreamFn,
|
|
55
55
|
ToolCallContext,
|
|
56
56
|
} from "./types";
|
|
57
|
-
import { setAgentTerminalOwnerContext } from "./types";
|
|
57
|
+
import { isContinuingMidRunMaintenanceOutcome, setAgentTerminalOwnerContext } from "./types";
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Closed runtime allowlist of failure-classifier codes. The public diagnostic
|
|
@@ -118,7 +118,7 @@ function sanitizeAgentFailure(error: unknown, runtimeClassifiedCode?: string): {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** Only runtime-authenticated built-in constructors may contribute a name. */
|
|
121
|
-
const TRUSTED_ERROR_CONSTRUCTORS = new Map<
|
|
121
|
+
const TRUSTED_ERROR_CONSTRUCTORS = new Map<object, string>([
|
|
122
122
|
[Error, "Error"],
|
|
123
123
|
[TypeError, "TypeError"],
|
|
124
124
|
[RangeError, "RangeError"],
|
|
@@ -2147,12 +2147,15 @@ export class Agent {
|
|
|
2147
2147
|
}
|
|
2148
2148
|
this.#state.isStreaming = false;
|
|
2149
2149
|
this.#state.streamMessage = null;
|
|
2150
|
-
// A maintenance checkpoint is only non-terminal while a
|
|
2151
|
-
//
|
|
2150
|
+
// A maintenance checkpoint is only non-terminal while a committed rewrite
|
|
2151
|
+
// will continue. Failed or aborted maintenance yields none. Because the loop runs with
|
|
2152
2152
|
// `resourceSealOwner: "caller"` it deliberately leaves sealing to us, so
|
|
2153
2153
|
// treating it as a checkpoint here would leave the run open forever and make
|
|
2154
2154
|
// every cancel report `run_not_sealed`.
|
|
2155
|
-
if (
|
|
2155
|
+
if (
|
|
2156
|
+
event.stopReason === "maintenance" &&
|
|
2157
|
+
isContinuingMidRunMaintenanceOutcome(event.maintenanceOutcome)
|
|
2158
|
+
) {
|
|
2156
2159
|
this.#managedLogicalRunOwner ??= managedLogicalRunOwner ?? runId;
|
|
2157
2160
|
maintenanceInterrupted = true;
|
|
2158
2161
|
this.#emit(event);
|
|
@@ -91,6 +91,8 @@ export interface GenerateBranchSummaryOptions {
|
|
|
91
91
|
* reuses the live turn's provider/WebSocket session.
|
|
92
92
|
*/
|
|
93
93
|
sessionId?: string;
|
|
94
|
+
/** Opaque provider conversation identity; never derived from branch-summary content. */
|
|
95
|
+
providerSessionId?: string;
|
|
94
96
|
/** Shared provider state map so the branch summary call reuses session-scoped transport/session caches. */
|
|
95
97
|
providerSessionState?: Map<string, ProviderSessionState>;
|
|
96
98
|
/** Hint that websocket transport should be preferred when supported by the provider implementation. */
|
|
@@ -291,6 +293,7 @@ export async function generateBranchSummary(
|
|
|
291
293
|
reserveTokens = 16384,
|
|
292
294
|
metadata,
|
|
293
295
|
sessionId,
|
|
296
|
+
providerSessionId,
|
|
294
297
|
providerSessionState,
|
|
295
298
|
preferWebsockets,
|
|
296
299
|
} = options;
|
|
@@ -326,7 +329,16 @@ export async function generateBranchSummary(
|
|
|
326
329
|
const response = await instrumentedCompleteSimple(
|
|
327
330
|
model,
|
|
328
331
|
{ systemPrompt: [SUMMARIZATION_SYSTEM_PROMPT], messages: summarizationMessages },
|
|
329
|
-
{
|
|
332
|
+
{
|
|
333
|
+
apiKey,
|
|
334
|
+
signal,
|
|
335
|
+
maxTokens: 2048,
|
|
336
|
+
metadata,
|
|
337
|
+
sessionId,
|
|
338
|
+
providerSessionId,
|
|
339
|
+
providerSessionState,
|
|
340
|
+
preferWebsockets,
|
|
341
|
+
},
|
|
330
342
|
{ telemetry: options.telemetry, oneshotKind: "branch_summary" },
|
|
331
343
|
);
|
|
332
344
|
|
|
@@ -882,6 +882,8 @@ export interface SummaryOptions {
|
|
|
882
882
|
* `providerSessionId ?? sessionId` the agent loop sends for normal turns).
|
|
883
883
|
*/
|
|
884
884
|
sessionId?: string;
|
|
885
|
+
/** Opaque provider conversation identity; never derived from compaction content. */
|
|
886
|
+
providerSessionId?: string;
|
|
885
887
|
/** Shared provider state map so maintenance calls reuse session-scoped transport/session caches. */
|
|
886
888
|
providerSessionState?: Map<string, ProviderSessionState>;
|
|
887
889
|
/** Hint that websocket transport should be preferred when supported by the provider implementation. */
|
|
@@ -1025,6 +1027,7 @@ export async function generateSummary(
|
|
|
1025
1027
|
initiatorOverride: options?.initiatorOverride,
|
|
1026
1028
|
metadata: options?.metadata,
|
|
1027
1029
|
sessionId: options?.sessionId,
|
|
1030
|
+
providerSessionId: options?.providerSessionId,
|
|
1028
1031
|
providerSessionState: options?.providerSessionState,
|
|
1029
1032
|
preferWebsockets: options?.preferWebsockets,
|
|
1030
1033
|
},
|
|
@@ -1073,6 +1076,8 @@ export interface HandoffOptions {
|
|
|
1073
1076
|
* reuses the live turn's provider/WebSocket session.
|
|
1074
1077
|
*/
|
|
1075
1078
|
sessionId?: string;
|
|
1079
|
+
/** Opaque provider conversation identity; never derived from handoff content. */
|
|
1080
|
+
providerSessionId?: string;
|
|
1076
1081
|
/** Shared provider state map so the handoff call reuses session-scoped transport/session caches. */
|
|
1077
1082
|
providerSessionState?: Map<string, ProviderSessionState>;
|
|
1078
1083
|
/** Hint that websocket transport should be preferred when supported by the provider implementation. */
|
|
@@ -1120,6 +1125,7 @@ export async function generateHandoff(
|
|
|
1120
1125
|
initiatorOverride: options.initiatorOverride,
|
|
1121
1126
|
metadata: options.metadata,
|
|
1122
1127
|
sessionId: options.sessionId,
|
|
1128
|
+
providerSessionId: options.providerSessionId,
|
|
1123
1129
|
providerSessionState: options.providerSessionState,
|
|
1124
1130
|
preferWebsockets: options.preferWebsockets,
|
|
1125
1131
|
},
|
|
@@ -1378,6 +1384,7 @@ export async function compact(
|
|
|
1378
1384
|
convertToLlm: options?.convertToLlm,
|
|
1379
1385
|
telemetry: options?.telemetry,
|
|
1380
1386
|
sessionId: options?.sessionId,
|
|
1387
|
+
providerSessionId: options?.providerSessionId,
|
|
1381
1388
|
providerSessionState: options?.providerSessionState,
|
|
1382
1389
|
preferWebsockets: options?.preferWebsockets,
|
|
1383
1390
|
remoteCompactionFallbackHealth: options?.remoteCompactionFallbackHealth,
|
|
@@ -1552,6 +1559,7 @@ async function generateTurnPrefixSummary(
|
|
|
1552
1559
|
initiatorOverride: options?.initiatorOverride,
|
|
1553
1560
|
metadata: options?.metadata,
|
|
1554
1561
|
sessionId: options?.sessionId,
|
|
1562
|
+
providerSessionId: options?.providerSessionId,
|
|
1555
1563
|
providerSessionState: options?.providerSessionState,
|
|
1556
1564
|
preferWebsockets: options?.preferWebsockets,
|
|
1557
1565
|
},
|
package/src/types.ts
CHANGED
|
@@ -214,14 +214,19 @@ export type ManagedAttemptOutcomeHandler = (
|
|
|
214
214
|
* Outcome of a cooperative mid-run context-maintenance checkpoint (see
|
|
215
215
|
* {@link AgentLoopConfig.maintainContext}). Any value other than "not-needed"
|
|
216
216
|
* means the checkpoint mutated (or attempted to mutate) durable context, so the
|
|
217
|
-
* loop ends the current run without
|
|
218
|
-
*
|
|
217
|
+
* loop ends the current run without lossy finalization. Committed maintenance
|
|
218
|
+
* resumes on rewritten context; failed or aborted maintenance terminalizes.
|
|
219
219
|
*/
|
|
220
220
|
export type MidRunMaintenanceOutcome = "not-needed" | "pruned" | "compacted" | "promoted" | "failed" | "aborted";
|
|
221
221
|
|
|
222
222
|
export interface ContextMaintenanceResult {
|
|
223
223
|
outcome: MidRunMaintenanceOutcome;
|
|
224
224
|
releaseCurrentContext?: boolean;
|
|
225
|
+
errorMessage?: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function isContinuingMidRunMaintenanceOutcome(outcome: unknown): boolean {
|
|
229
|
+
return outcome === "pruned" || outcome === "compacted" || outcome === "promoted";
|
|
225
230
|
}
|
|
226
231
|
|
|
227
232
|
/**
|
|
@@ -438,9 +443,9 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
438
443
|
* non-optional loop signal, and `awaitEventDrain(invocationSignal)` waits for
|
|
439
444
|
* prior event consumer bodies with loop and invocation cancellation composed.
|
|
440
445
|
* Any outcome other than "not-needed" ends the current run with
|
|
441
|
-
* `agent_end.stopReason === "maintenance"
|
|
442
|
-
*
|
|
443
|
-
*
|
|
446
|
+
* `agent_end.stopReason === "maintenance"`. Successful maintenance resumes
|
|
447
|
+
* on rewritten context; `failed` and `aborted` are terminal and never resend
|
|
448
|
+
* the unchanged context.
|
|
444
449
|
*/
|
|
445
450
|
maintainContext?: (
|
|
446
451
|
context: AgentContext,
|