@gajae-code/agent-core 0.11.1 → 0.11.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 +7 -0
- package/dist/types/agent-loop.d.ts +44 -0
- package/dist/types/agent.d.ts +8 -0
- package/dist/types/compaction/compaction.d.ts +6 -2
- package/dist/types/compaction/pruning.d.ts +6 -2
- package/dist/types/proxy.d.ts +11 -0
- package/dist/types/types.d.ts +8 -2
- package/package.json +4 -4
- package/src/agent-loop.ts +504 -60
- package/src/agent.ts +36 -3
- package/src/compaction/compaction.ts +45 -89
- package/src/compaction/pruning.ts +144 -11
- package/src/proxy.ts +82 -2
- package/src/types.ts +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.11.3] - 2026-07-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Pre-compaction pruning now preserves bounded, actionable error evidence instead of discarding it, while enforcing exact positive-savings admission and accounting so a prune is only applied when it demonstrably reduces context cost (#2635).
|
|
9
|
+
|
|
5
10
|
## [0.11.1] - 2026-07-16
|
|
6
11
|
|
|
7
12
|
### Fixed
|
|
8
13
|
|
|
14
|
+
- Hardened the managed fallback attempt snapshot: staged agent events and assistant partials were cloned with a bare `structuredClone`, so a single non-cloneable value in a staged payload (e.g. a live `Headers` inside `transportFailure`) threw `DataCloneError` ("The object can not be cloned."), masked the real provider outcome, and deterministically failed every attempt until the fallback chain exhausted. The snapshot now degrades to a cycle-aware sanitizing deep clone that always returns a detached, JSON-serializable value (unsupported leaves become placeholders), so event-time replay semantics are preserved and no local snapshot failure can masquerade as a provider attempt failure. Byte accounting in the provisional buffer measures the raw event before the snapshot duplicates it (over-limit payloads are rejected pre-clone), re-measures degraded snapshots so the retained sanitized form is what gets accounted, and uses the sanitized detached form as the cycle-safe estimator for cyclic payloads.
|
|
15
|
+
- Enforced the managed fallback authority boundary for local staging failures: `ManagedAttemptBufferOverflowError` no longer carries a synthetic provider-like `503` status, so exceeding the provisional event buffer limit (like any other local snapshot failure) is non-retryable, never converts into `transportFailure { kind: "transport", status: 503 }` evidence, and never rotates or consumes the model fallback chain — it surfaces as an explicit local error instead. Only original typed provider transport facts may authorize provider fallback.
|
|
9
16
|
- Added a bounded, neutralize-only `invalid_prompt` circuit breaker to the agent loop (#2282). A poisoned-history rejection (`Request blocked (code=invalid_prompt)`) is a deterministic content fault: re-sending the same history re-triggers it, so uncontrolled session auto-retry would burn its budget re-poisoning the model. On the first `invalid_prompt` of a run, leaked reserved control tokens are neutralized in place across history (no item is ever dropped). If that changes the outgoing bytes, the turn is resent exactly once with the repaired history; if neutralization cannot change anything, the run fails fast immediately with no resend. The repaired history is persisted for a clean resume, the breaker fires at most once per run (budget = one repaired resend), and it is scoped to the non-managed session path since managed fallback owns its own retry policy.
|
|
10
17
|
|
|
11
18
|
## [0.10.2] - 2026-07-14
|
|
@@ -27,6 +27,50 @@ export declare function agentLoop(prompts: AgentMessage[], context: AgentContext
|
|
|
27
27
|
* This cannot be validated here since `convertToLlm` is only called once per turn.
|
|
28
28
|
*/
|
|
29
29
|
export declare function agentLoopContinue(context: AgentContext, config: AgentLoopConfig, signal?: AbortSignal, streamFn?: StreamFn, emitManagedAgentStart?: boolean): EventStream<AgentEvent, AgentMessage[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Hard work budget for one degraded snapshot: every visited node AND every
|
|
32
|
+
* enumerated own key is debited against this budget before it is processed
|
|
33
|
+
* (accessor keys and re-visits of shared objects included), and any remainder
|
|
34
|
+
* collapses to the deterministic `"[truncated]"` placeholder. Well above
|
|
35
|
+
* ordinary streamed events; it only bounds hostile graphs.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MANAGED_SNAPSHOT_MAX_NODES = 100000;
|
|
38
|
+
/**
|
|
39
|
+
* Cycle-aware deep clone that always returns a detached, JSON-serializable
|
|
40
|
+
* value. Used whenever a detached snapshot cannot be safely obtained or
|
|
41
|
+
* measured: after `structuredClone` fails, and again when a (successfully
|
|
42
|
+
* cloned) snapshot cannot be serialized for byte accounting.
|
|
43
|
+
*
|
|
44
|
+
* Totality rules — the walk must never dispatch through payload-controlled
|
|
45
|
+
* code, throw, or do unbounded work:
|
|
46
|
+
* - proxies (revoked or live) are collapsed to `"[unserializable]"` BEFORE
|
|
47
|
+
* any reflective operation, so `ownKeys`/descriptor traps are never
|
|
48
|
+
* dispatched (`util.types.isProxy` identifies proxies without touching
|
|
49
|
+
* their handlers);
|
|
50
|
+
* - only intrinsics are used on the remaining ordinary objects (no
|
|
51
|
+
* `input.map`, no `input.getTime()`, no `input.length` reads);
|
|
52
|
+
* - arrays are enumerated through their own present keys, never their
|
|
53
|
+
* declared length, so a sparse array cannot force a dense allocation
|
|
54
|
+
* proportional to `length`; sparse/exotic arrays degrade to a null-proto
|
|
55
|
+
* record of their present indices, and the dense-shape decision verifies
|
|
56
|
+
* every index against its ordinal;
|
|
57
|
+
* - the walk debits `maxNodes` budget per visited node and per enumerated
|
|
58
|
+
* key before processing it; anything beyond the budget becomes
|
|
59
|
+
* `"[truncated]"` (the one linear primitive per visited node is a single
|
|
60
|
+
* `Object.keys` call on a non-proxy object the process already holds);
|
|
61
|
+
* - property values are read via own-property descriptors, so accessors are
|
|
62
|
+
* never invoked (a snapshot must not cause observable side effects) and are
|
|
63
|
+
* replaced with `"[accessor]"`;
|
|
64
|
+
* - functions/symbols and any property that cannot be read safely become
|
|
65
|
+
* short placeholders, `bigint` becomes its decimal string, and references
|
|
66
|
+
* back into the current path collapse to `"[Circular]"`;
|
|
67
|
+
* - records are built on a null prototype so a `__proto__` key cannot mutate
|
|
68
|
+
* the clone's prototype chain.
|
|
69
|
+
*
|
|
70
|
+
* Exported for direct regression coverage of the budget accounting; runtime
|
|
71
|
+
* callers use the default budget via {@link managedAttemptSnapshot}.
|
|
72
|
+
*/
|
|
73
|
+
export declare function sanitizedDetachedClone<T>(value: T, maxNodes?: number): T;
|
|
30
74
|
/**
|
|
31
75
|
* Detailed-result handle returned by {@link agentLoopDetailed}. Adds the
|
|
32
76
|
* run-level telemetry/coverage rollup to the existing `AgentMessage[]`
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -184,6 +184,10 @@ export interface AgentPromptOptions {
|
|
|
184
184
|
/** Receives a discarded managed attempt without exposing assistant lifecycle events. */
|
|
185
185
|
onManagedAttemptOutcome?: AgentLoopConfig["onManagedAttemptOutcome"];
|
|
186
186
|
}
|
|
187
|
+
export type AgentQueueSnapshot = {
|
|
188
|
+
steering: AgentMessage[];
|
|
189
|
+
followUp: AgentMessage[];
|
|
190
|
+
};
|
|
187
191
|
export declare class Agent {
|
|
188
192
|
#private;
|
|
189
193
|
get intentTracing(): boolean;
|
|
@@ -376,6 +380,10 @@ export declare class Agent {
|
|
|
376
380
|
snapshotFollowUp(): AgentMessage[];
|
|
377
381
|
/** Restore previously snapshotted follow-up messages ahead of any newly queued ones. */
|
|
378
382
|
restoreFollowUp(messages: AgentMessage[]): void;
|
|
383
|
+
/** Snapshot both executable queues as one atomic session-level view. */
|
|
384
|
+
snapshotQueues(): AgentQueueSnapshot;
|
|
385
|
+
/** Replace both executable queues with a prior snapshot. */
|
|
386
|
+
restoreQueues(snapshot: AgentQueueSnapshot): void;
|
|
379
387
|
/**
|
|
380
388
|
* Remove and return the last steering message from the queue (LIFO).
|
|
381
389
|
* Used by dequeue keybinding.
|
|
@@ -192,8 +192,7 @@ export interface SummaryOptions {
|
|
|
192
192
|
/**
|
|
193
193
|
* Optional telemetry handle. When provided, every LLM call emitted during
|
|
194
194
|
* compaction is wrapped in an OTEL chat span tagged with
|
|
195
|
-
* `pi.gen_ai.oneshot.kind` (`compaction_summary
|
|
196
|
-
* or `compaction_turn_prefix`). `undefined` keeps the call paths zero-cost.
|
|
195
|
+
* `pi.gen_ai.oneshot.kind` (`compaction_summary` or `compaction_turn_prefix`).
|
|
197
196
|
*/
|
|
198
197
|
telemetry?: AgentTelemetry;
|
|
199
198
|
authCredentialType?: "api_key" | "oauth";
|
|
@@ -301,6 +300,11 @@ export interface PrepareCompactionOptions {
|
|
|
301
300
|
* (the confounded raw promptTokens/estimatedTokens quotient is never used).
|
|
302
301
|
*/
|
|
303
302
|
tokenCorrectionRatio?: number;
|
|
303
|
+
/**
|
|
304
|
+
* Model context-window size. Windows below 66k retain the legacy fixed
|
|
305
|
+
* keepRecentTokens behavior; larger windows scale the keep window to 30%.
|
|
306
|
+
*/
|
|
307
|
+
contextWindow?: number;
|
|
304
308
|
}
|
|
305
309
|
export declare function prepareCompaction(pathEntries: SessionEntry[], settings: CompactionSettings, options?: PrepareCompactionOptions): CompactionPreparation | undefined;
|
|
306
310
|
/**
|
|
@@ -49,7 +49,7 @@ export declare function pruneAssistantToolArguments(entries: SessionEntry[], con
|
|
|
49
49
|
* mutating any entry. Returns 0 savings when below the configured minimum so the
|
|
50
50
|
* caller sees the same gate the real prune enforces.
|
|
51
51
|
*/
|
|
52
|
-
export declare function estimateToolOutputPruneSavings(entries: SessionEntry[], config?: PruneConfig): {
|
|
52
|
+
export declare function estimateToolOutputPruneSavings(entries: SessionEntry[], config?: PruneConfig, options?: PruneToolOutputsOptions): {
|
|
53
53
|
prunableCount: number;
|
|
54
54
|
tokensSaved: number;
|
|
55
55
|
};
|
|
@@ -66,4 +66,8 @@ export declare function shouldRunMaintenancePrune(args: {
|
|
|
66
66
|
minSavings: number;
|
|
67
67
|
cacheEpochResetCost: number;
|
|
68
68
|
}): boolean;
|
|
69
|
-
export
|
|
69
|
+
export interface PruneToolOutputsOptions {
|
|
70
|
+
/** Lower the usual minimum only when the caller is already over its compaction threshold. */
|
|
71
|
+
relaxedMinimum?: number;
|
|
72
|
+
}
|
|
73
|
+
export declare function pruneToolOutputs(entries: SessionEntry[], config?: PruneConfig, options?: PruneToolOutputsOptions): PruneResult;
|
package/dist/types/proxy.d.ts
CHANGED
|
@@ -33,6 +33,17 @@ export type ProxyAssistantMessageEvent = {
|
|
|
33
33
|
type: "thinking_end";
|
|
34
34
|
contentIndex: number;
|
|
35
35
|
contentSignature?: string;
|
|
36
|
+
} | {
|
|
37
|
+
type: "reasoning_summary_start";
|
|
38
|
+
contentIndex: number;
|
|
39
|
+
} | {
|
|
40
|
+
type: "reasoning_summary_delta";
|
|
41
|
+
contentIndex: number;
|
|
42
|
+
delta: string;
|
|
43
|
+
} | {
|
|
44
|
+
type: "reasoning_summary_end";
|
|
45
|
+
contentIndex: number;
|
|
46
|
+
content?: string;
|
|
36
47
|
} | {
|
|
37
48
|
type: "toolcall_start";
|
|
38
49
|
contentIndex: number;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessage, AssistantMessageEvent, AssistantMessageEventStream, Effort, ImageContent, Message, Model, SimpleStreamOptions, Static, streamSimple, TextContent, Tool, ToolChoice, ToolResultMessage, TSchema } from "@gajae-code/ai";
|
|
1
|
+
import type { AssistantMessage, AssistantMessageEvent, AssistantMessageEventStream, Effort, ImageContent, Message, Model, SimpleStreamOptions, Static, streamSimple, TextContent, Tool, ToolChoice, ToolResultMessage, TransportFailureFacts, TSchema } from "@gajae-code/ai";
|
|
2
2
|
import type { AppendOnlyContextManager } from "./append-only-context";
|
|
3
3
|
import type { HarmonyAuditEvent } from "./harmony-leak";
|
|
4
4
|
import type { AgentRunCoverage, AgentRunSummary } from "./run-collector";
|
|
@@ -35,6 +35,9 @@ export type ManagedAttemptContinuation = (ownership: ManagedAttemptContinuationO
|
|
|
35
35
|
export type ManagedAttemptDecision = {
|
|
36
36
|
type: "retry";
|
|
37
37
|
continuation: ManagedAttemptContinuation;
|
|
38
|
+
} | {
|
|
39
|
+
type: "maintenance";
|
|
40
|
+
continuation: ManagedAttemptContinuation;
|
|
38
41
|
} | {
|
|
39
42
|
type: "terminal";
|
|
40
43
|
terminal: RunTerminalRequest;
|
|
@@ -45,8 +48,11 @@ export type ManagedAttemptOutcome = {
|
|
|
45
48
|
failure: {
|
|
46
49
|
message: AssistantMessage;
|
|
47
50
|
/** Exact provider transport facts, including retry headers, for fallback policy. */
|
|
48
|
-
transportFailure?:
|
|
51
|
+
transportFailure?: TransportFailureFacts;
|
|
49
52
|
};
|
|
53
|
+
} | {
|
|
54
|
+
type: "context_overflow_discarded";
|
|
55
|
+
message: AssistantMessage;
|
|
50
56
|
} | {
|
|
51
57
|
type: "run_terminal";
|
|
52
58
|
reason: "cancelled" | "error" | "exhausted";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/agent-core",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.3",
|
|
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.11.
|
|
36
|
-
"@gajae-code/natives": "0.11.
|
|
37
|
-
"@gajae-code/utils": "0.11.
|
|
35
|
+
"@gajae-code/ai": "0.11.3",
|
|
36
|
+
"@gajae-code/natives": "0.11.3",
|
|
37
|
+
"@gajae-code/utils": "0.11.3",
|
|
38
38
|
"@opentelemetry/api": "^1.9.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|