@gajae-code/agent-core 0.10.2 → 0.11.0
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 +4 -0
- package/dist/types/agent-loop.d.ts +10 -2
- package/dist/types/agent.d.ts +34 -2
- package/dist/types/compaction/entries.d.ts +11 -1
- package/dist/types/types.d.ts +92 -2
- package/package.json +4 -4
- package/src/agent-loop.ts +299 -17
- package/src/agent.ts +226 -21
- package/src/compaction/entries.ts +12 -0
- package/src/types.ts +102 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
- Extended the gpt-5.6 `Request blocked (code=invalid_prompt)` fix to the compaction paths that bypass the streaming transport. Remote OpenAI compaction (`/responses/compact`, `compaction.remoteEnabled` default on — the "remote compact task" in openai/codex#32028) built its native `input` from reasoning signatures, verbatim history items, and message/tool text without neutralizing leaked Harmony control-token markers (e.g. `<|channel|>analysis`), so gpt-5.6 rejected the compaction request and, on retry, could escalate to account-level blocking. `requestOpenAiRemoteCompaction` now neutralizes reserved control tokens across the whole outgoing `input`, and the generic `requestRemoteCompaction` prompt/systemPrompt are neutralized too. Local summarization was already covered by the streaming-transport request-boundary fix.
|
|
10
10
|
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- `AgentLoopConfig.maintainContext` now receives a required cancellation-aware lifecycle (`signal`, `awaitEventDrain(invocationSignal)`). Agent loops compose the run and maintenance-invocation signals and pass that single signal to EventStream's FIFO consumer-drain barrier, so cancellation removes the pending drain at its owner instead of racing an orphaned wait.
|
|
14
|
+
|
|
11
15
|
## [0.10.0] - 2026-07-12
|
|
12
16
|
|
|
13
17
|
### Fixed
|
|
@@ -5,11 +5,19 @@
|
|
|
5
5
|
import { type Context, EventStream } from "@gajae-code/ai";
|
|
6
6
|
import { type AgentRunCoverage, type AgentRunSummary } from "./run-collector";
|
|
7
7
|
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types";
|
|
8
|
+
/** Sentinel returned by the abort race in `streamAssistantResponse`. */
|
|
9
|
+
/**
|
|
10
|
+
* Defensive caps for a provisional managed attempt. These are intentionally
|
|
11
|
+
* well above ordinary streamed responses; they only bound memory when an
|
|
12
|
+
* upstream emits an unbounded event stream before the attempt can commit.
|
|
13
|
+
*/
|
|
14
|
+
export declare const MANAGED_ATTEMPT_MAX_STAGED_EVENTS = 10000;
|
|
15
|
+
export declare const MANAGED_ATTEMPT_MAX_STAGED_BYTES: number;
|
|
8
16
|
/**
|
|
9
17
|
* Start an agent loop with a new prompt message.
|
|
10
18
|
* The prompt is added to the context and events are emitted for it.
|
|
11
19
|
*/
|
|
12
|
-
export declare function agentLoop(prompts: AgentMessage[], context: AgentContext, config: AgentLoopConfig, signal?: AbortSignal, streamFn?: StreamFn): EventStream<AgentEvent, AgentMessage[]>;
|
|
20
|
+
export declare function agentLoop(prompts: AgentMessage[], context: AgentContext, config: AgentLoopConfig, signal?: AbortSignal, streamFn?: StreamFn, emitManagedAgentStart?: boolean): EventStream<AgentEvent, AgentMessage[]>;
|
|
13
21
|
/**
|
|
14
22
|
* Continue an agent loop from the current context without adding a new message.
|
|
15
23
|
* Used for retries - context already has user message or tool results.
|
|
@@ -18,7 +26,7 @@ export declare function agentLoop(prompts: AgentMessage[], context: AgentContext
|
|
|
18
26
|
* via `convertToLlm`. If it doesn't, the LLM provider will reject the request.
|
|
19
27
|
* This cannot be validated here since `convertToLlm` is only called once per turn.
|
|
20
28
|
*/
|
|
21
|
-
export declare function agentLoopContinue(context: AgentContext, config: AgentLoopConfig, signal?: AbortSignal, streamFn?: StreamFn): EventStream<AgentEvent, AgentMessage[]>;
|
|
29
|
+
export declare function agentLoopContinue(context: AgentContext, config: AgentLoopConfig, signal?: AbortSignal, streamFn?: StreamFn, emitManagedAgentStart?: boolean): EventStream<AgentEvent, AgentMessage[]>;
|
|
22
30
|
/**
|
|
23
31
|
* Detailed-result handle returned by {@link agentLoopDetailed}. Adds the
|
|
24
32
|
* run-level telemetry/coverage rollup to the existing `AgentMessage[]`
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
import { type AssistantMessage, type AssistantMessageEvent, type CursorExecHandlers, type CursorToolResultHandler, type Effort, type ImageContent, type Message, type Model, type ProviderSessionState, type ServiceTier, type SimpleStreamOptions, type ThinkingBudgets, type ToolChoice } from "@gajae-code/ai";
|
|
5
5
|
import type { AppendOnlyContextManager } from "./append-only-context";
|
|
6
6
|
import type { HarmonyAuditEvent } from "./harmony-leak";
|
|
7
|
-
import type { AgentEvent, AgentLoopConfig, AgentMessage, AgentState, AgentTool, AgentToolContext, StreamFn, ToolCallContext } from "./types";
|
|
7
|
+
import type { AgentEvent, AgentLoopConfig, AgentMessage, AgentState, AgentTool, AgentToolContext, ManagedLogicalRunId, RunTerminalRequest, StreamFn, ToolCallContext } from "./types";
|
|
8
8
|
/**
|
|
9
9
|
* Whether persisted history ends at a point where a new model turn can resume.
|
|
10
10
|
* Assistant-ended histories require an in-memory queued message and are handled
|
|
11
11
|
* separately by `Agent.continue()`.
|
|
12
12
|
*/
|
|
13
13
|
export declare function canContinuePersistedHistory(messages: readonly AgentMessage[]): boolean;
|
|
14
|
+
export declare class ManagedCursorInvariantError extends Error {
|
|
15
|
+
constructor(message?: string);
|
|
16
|
+
}
|
|
14
17
|
export declare class AgentBusyError extends Error {
|
|
15
18
|
constructor(message?: string);
|
|
16
19
|
}
|
|
@@ -170,6 +173,16 @@ export interface AgentOptions {
|
|
|
170
173
|
}
|
|
171
174
|
export interface AgentPromptOptions {
|
|
172
175
|
toolChoice?: ToolChoice;
|
|
176
|
+
/** Disable transport replay; fallback accounting is owned by the caller. */
|
|
177
|
+
fallbackManaged?: boolean;
|
|
178
|
+
/** Called synchronously after this invocation claims the agent run, before asynchronous provider work. */
|
|
179
|
+
onRunAccepted?: () => void;
|
|
180
|
+
/** Called once immediately before every managed upstream request. */
|
|
181
|
+
nextFallbackAttempt?: AgentLoopConfig["nextFallbackAttempt"];
|
|
182
|
+
/** Called after a managed upstream request is accepted and committed. */
|
|
183
|
+
onManagedAttemptAccepted?: AgentLoopConfig["onManagedAttemptAccepted"];
|
|
184
|
+
/** Receives a discarded managed attempt without exposing assistant lifecycle events. */
|
|
185
|
+
onManagedAttemptOutcome?: AgentLoopConfig["onManagedAttemptOutcome"];
|
|
173
186
|
}
|
|
174
187
|
export declare class Agent {
|
|
175
188
|
#private;
|
|
@@ -307,6 +320,7 @@ export declare class Agent {
|
|
|
307
320
|
setAssistantMessageEventInterceptor(fn: ((message: AssistantMessage, event: AssistantMessageEvent) => void) | undefined): void;
|
|
308
321
|
setOnBeforeYield(fn: (() => Promise<void> | void) | undefined): void;
|
|
309
322
|
setShouldPause(fn: AgentLoopConfig["shouldPause"] | undefined): void;
|
|
323
|
+
setMaintainContext(fn: AgentLoopConfig["maintainContext"] | undefined): void;
|
|
310
324
|
emitExternalEvent(event: AgentEvent): void;
|
|
311
325
|
createExternalEventEmitterForCurrentRun(): ((event: AgentEvent) => void) | undefined;
|
|
312
326
|
setSystemPrompt(v: string[]): void;
|
|
@@ -391,6 +405,24 @@ export declare class Agent {
|
|
|
391
405
|
*/
|
|
392
406
|
forceAbort(reason?: string): boolean;
|
|
393
407
|
waitForIdle(): Promise<void>;
|
|
408
|
+
/** The active per-attempt run identifier. */
|
|
409
|
+
get activeRunId(): number | undefined;
|
|
410
|
+
/**
|
|
411
|
+
* Stable identifier for the active managed logical run, shared by every retry
|
|
412
|
+
* attempt. Pass this value to requestRunTerminal(); never retain activeRunId
|
|
413
|
+
* for managed terminal completion.
|
|
414
|
+
*/
|
|
415
|
+
get currentManagedLogicalRunId(): ManagedLogicalRunId | undefined;
|
|
416
|
+
/**
|
|
417
|
+
* Request terminal completion through the single logical-run keyed finalizer.
|
|
418
|
+
*
|
|
419
|
+
* For managed runs, logicalRunId must be currentManagedLogicalRunId from any
|
|
420
|
+
* attempt in the retry chain. Non-managed runs use their activeRunId. Terminal
|
|
421
|
+
* requests with messages emit a committed message_start/message_end lifecycle
|
|
422
|
+
* for each diagnostic before agent_end. Requests without messages (such as
|
|
423
|
+
* cancellation) emit only agent_end.
|
|
424
|
+
*/
|
|
425
|
+
requestRunTerminal(logicalRunId: ManagedLogicalRunId, request: RunTerminalRequest): boolean;
|
|
394
426
|
reset(): void;
|
|
395
427
|
/** Send a prompt with an AgentMessage */
|
|
396
428
|
prompt(message: AgentMessage | AgentMessage[], options?: AgentPromptOptions): Promise<void>;
|
|
@@ -399,5 +431,5 @@ export declare class Agent {
|
|
|
399
431
|
/**
|
|
400
432
|
* Continue from current context (used for retries and resuming queued messages).
|
|
401
433
|
*/
|
|
402
|
-
continue(): Promise<void>;
|
|
434
|
+
continue(options?: AgentPromptOptions): Promise<void>;
|
|
403
435
|
}
|
|
@@ -100,9 +100,19 @@ export interface ModeChangeEntry extends SessionEntryBase {
|
|
|
100
100
|
/** Optional mode-specific data (e.g. plan file path) */
|
|
101
101
|
data?: Record<string, unknown>;
|
|
102
102
|
}
|
|
103
|
+
export interface ConfiguredModelChainEntry extends SessionEntryBase {
|
|
104
|
+
type: "configured_model_chain";
|
|
105
|
+
role: string;
|
|
106
|
+
entries: readonly string[];
|
|
107
|
+
origin: string;
|
|
108
|
+
identity?: string;
|
|
109
|
+
explicitHead: boolean;
|
|
110
|
+
/** Whether this entry removes the configured chain for its role. */
|
|
111
|
+
cleared?: boolean;
|
|
112
|
+
}
|
|
103
113
|
export interface CustomCompactionSessionEntries {
|
|
104
114
|
}
|
|
105
|
-
export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry | CustomCompactionSessionEntries[keyof CustomCompactionSessionEntries];
|
|
115
|
+
export type SessionEntry = SessionMessageEntry | ThinkingLevelChangeEntry | ModelChangeEntry | ServiceTierChangeEntry | CompactionEntry | BranchSummaryEntry | CustomEntry | CustomMessageEntry | LabelEntry | TtsrInjectionEntry | MCPToolSelectionEntry | SessionInitEntry | ModeChangeEntry | ConfiguredModelChainEntry | CustomCompactionSessionEntries[keyof CustomCompactionSessionEntries];
|
|
106
116
|
export interface ReadonlySessionManager {
|
|
107
117
|
getBranch(leafId?: string | null): SessionEntry[];
|
|
108
118
|
getEntry(id: string): SessionEntry | undefined;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -5,11 +5,76 @@ import type { AgentRunCoverage, AgentRunSummary } from "./run-collector";
|
|
|
5
5
|
import type { AgentTelemetryConfig } from "./telemetry";
|
|
6
6
|
/** Stream function - can return sync or Promise for async config lookup */
|
|
7
7
|
export type StreamFn = (...args: Parameters<typeof streamSimple>) => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
|
|
8
|
+
/** Stable identifier for a managed logical run, shared by all of its retry attempts. */
|
|
9
|
+
export type ManagedLogicalRunId = number;
|
|
10
|
+
/** Terminal completion requested for a logical run. */
|
|
11
|
+
export interface RunTerminalRequest {
|
|
12
|
+
stopReason: "cancelled" | "error" | "exhausted";
|
|
13
|
+
messages?: AgentMessage[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Ownership token supplied when Agent invokes a retry continuation.
|
|
17
|
+
*
|
|
18
|
+
* A continuation MUST verify `isCurrent()` immediately before starting a
|
|
19
|
+
* follow-up invocation and abandon the retry when it returns false. The token
|
|
20
|
+
* becomes invalid when its originating run is force-aborted or superseded.
|
|
21
|
+
* Coding-agent retry continuations must accept this argument and must not call
|
|
22
|
+
* `agent.continue()` after ownership has been lost.
|
|
23
|
+
*/
|
|
24
|
+
export interface ManagedAttemptContinuationOwnership {
|
|
25
|
+
/** Per-attempt run-loop id; use only for attempt-local ownership checks. */
|
|
26
|
+
readonly runId: number;
|
|
27
|
+
/** Stable managed logical-run id; use for all terminal completion requests. */
|
|
28
|
+
readonly logicalRunId: ManagedLogicalRunId;
|
|
29
|
+
readonly generation: number;
|
|
30
|
+
isCurrent(): boolean;
|
|
31
|
+
}
|
|
32
|
+
/** Runs after a discarded attempt is idle, only while its ownership token remains current. */
|
|
33
|
+
export type ManagedAttemptContinuation = (ownership: ManagedAttemptContinuationOwnership) => void | Promise<void>;
|
|
34
|
+
/** Decision returned by managed fallback policy for one provisional attempt. */
|
|
35
|
+
export type ManagedAttemptDecision = {
|
|
36
|
+
type: "retry";
|
|
37
|
+
continuation: ManagedAttemptContinuation;
|
|
38
|
+
} | {
|
|
39
|
+
type: "terminal";
|
|
40
|
+
terminal: RunTerminalRequest;
|
|
41
|
+
};
|
|
42
|
+
/** Structured result for one managed upstream invocation. */
|
|
43
|
+
export type ManagedAttemptOutcome = {
|
|
44
|
+
type: "retryable_discarded";
|
|
45
|
+
failure: {
|
|
46
|
+
message: AssistantMessage;
|
|
47
|
+
/** Exact provider transport facts, including retry headers, for fallback policy. */
|
|
48
|
+
transportFailure?: import("@gajae-code/ai").TransportFailureFacts;
|
|
49
|
+
};
|
|
50
|
+
} | {
|
|
51
|
+
type: "run_terminal";
|
|
52
|
+
reason: "cancelled" | "error" | "exhausted";
|
|
53
|
+
};
|
|
54
|
+
export type ManagedAttemptOutcomeHandler = (outcome: ManagedAttemptOutcome) => ManagedAttemptDecision | Promise<ManagedAttemptDecision>;
|
|
55
|
+
/**
|
|
56
|
+
* Outcome of a cooperative mid-run context-maintenance checkpoint (see
|
|
57
|
+
* {@link AgentLoopConfig.maintainContext}). Any value other than "not-needed"
|
|
58
|
+
* means the checkpoint mutated (or attempted to mutate) durable context, so the
|
|
59
|
+
* loop ends the current run without the lossy `agent_end` finalization and the
|
|
60
|
+
* maintenance owner resumes the run on the rewritten context.
|
|
61
|
+
*/
|
|
62
|
+
export type MidRunMaintenanceOutcome = "not-needed" | "pruned" | "compacted" | "promoted" | "failed" | "aborted";
|
|
8
63
|
/**
|
|
9
64
|
* Configuration for the agent loop.
|
|
10
65
|
*/
|
|
11
66
|
export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
12
67
|
model: Model;
|
|
68
|
+
/**
|
|
69
|
+
* Supplies a fresh opaque token at each concrete managed transport invocation.
|
|
70
|
+
* The callback runs at the stream boundary so controller accounting matches
|
|
71
|
+
* upstream request count, including multi-step tool turns.
|
|
72
|
+
*/
|
|
73
|
+
nextFallbackAttempt?: (model: Model) => SimpleStreamOptions["fallbackAttempt"];
|
|
74
|
+
/** Called after a managed upstream request is accepted and committed. */
|
|
75
|
+
onManagedAttemptAccepted?: () => void | Promise<void>;
|
|
76
|
+
/** Receives a managed invocation outcome without publishing provisional lifecycle events. */
|
|
77
|
+
onManagedAttemptOutcome?: ManagedAttemptOutcomeHandler;
|
|
13
78
|
/**
|
|
14
79
|
* When to interrupt tool execution for steering messages.
|
|
15
80
|
* - "immediate" = check after each tool call (default)
|
|
@@ -129,6 +194,29 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
129
194
|
* Use this when tool availability or the system prompt can change mid-turn.
|
|
130
195
|
*/
|
|
131
196
|
syncContextBeforeModelCall?: (context: AgentContext) => void | Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Cooperative mid-run context-maintenance checkpoint.
|
|
199
|
+
*
|
|
200
|
+
* Invoked at the top of every loop iteration AFTER pending tool-result /
|
|
201
|
+
* steering messages have been materialized into durable context and BEFORE
|
|
202
|
+
* {@link syncContextBeforeModelCall} and the model call. This is the only
|
|
203
|
+
* boundary where the full unsent context (tool results + dequeued steering)
|
|
204
|
+
* is already durable, so a long uninterrupted tool loop can be bounded here
|
|
205
|
+
* before it grows past the provider window.
|
|
206
|
+
*
|
|
207
|
+
* The callback owns the maintenance decision (prune / compact / promote) and
|
|
208
|
+
* receives the minimal cancellation-aware lifecycle: `signal` is the
|
|
209
|
+
* non-optional loop signal, and `awaitEventDrain(invocationSignal)` waits for
|
|
210
|
+
* prior event consumer bodies with loop and invocation cancellation composed.
|
|
211
|
+
* Any outcome other than "not-needed" ends the current run with
|
|
212
|
+
* `agent_end.stopReason === "maintenance"` (NOT the lossy pause / completed
|
|
213
|
+
* finalization); the callback's continuation owner resumes the run on the
|
|
214
|
+
* rewritten context.
|
|
215
|
+
*/
|
|
216
|
+
maintainContext?: (context: AgentContext, lifecycle: {
|
|
217
|
+
signal: AbortSignal;
|
|
218
|
+
awaitEventDrain: (invocationSignal: AbortSignal) => Promise<void>;
|
|
219
|
+
}) => Promise<MidRunMaintenanceOutcome> | MidRunMaintenanceOutcome;
|
|
132
220
|
/**
|
|
133
221
|
* Optional transform applied to tool call arguments before execution.
|
|
134
222
|
* Use for deobfuscating secrets or rewriting arguments.
|
|
@@ -388,8 +476,10 @@ export type AgentEvent = {
|
|
|
388
476
|
} | {
|
|
389
477
|
type: "agent_end";
|
|
390
478
|
messages: AgentMessage[];
|
|
391
|
-
/** Indicates whether the loop ended normally
|
|
392
|
-
stopReason?: "completed" | "paused";
|
|
479
|
+
/** Indicates whether the loop ended normally, suspended, cancelled, or entered maintenance. */
|
|
480
|
+
stopReason?: "completed" | "paused" | "cancelled" | "maintenance";
|
|
481
|
+
/** Present iff `stopReason === "maintenance"`; the maintenance outcome. */
|
|
482
|
+
maintenanceOutcome?: MidRunMaintenanceOutcome;
|
|
393
483
|
/** Present iff `AgentTelemetryConfig` was supplied on this run. */
|
|
394
484
|
telemetry?: AgentRunSummary;
|
|
395
485
|
coverage?: AgentRunCoverage;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/agent-core",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
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.
|
|
36
|
-
"@gajae-code/natives": "0.
|
|
37
|
-
"@gajae-code/utils": "0.
|
|
35
|
+
"@gajae-code/ai": "0.11.0",
|
|
36
|
+
"@gajae-code/natives": "0.11.0",
|
|
37
|
+
"@gajae-code/utils": "0.11.0",
|
|
38
38
|
"@opentelemetry/api": "^1.9.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|