@gajae-code/agent-core 0.15.5 → 0.16.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.d.ts +15 -3
- package/dist/types/types.d.ts +15 -1
- package/package.json +4 -4
- package/src/agent-loop.ts +7 -1
- package/src/agent.ts +63 -5
- package/src/types.ts +16 -1
package/CHANGELOG.md
CHANGED
package/dist/types/agent.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type AssistantMessage, type AssistantMessageEvent, type CursorExecHandl
|
|
|
5
5
|
import type { AppendOnlyContextManager } from "./append-only-context";
|
|
6
6
|
import type { AttemptRunHandle, AttemptScope } from "./attempt-scope";
|
|
7
7
|
import type { HarmonyAuditEvent } from "./harmony-leak";
|
|
8
|
-
import type { AgentEvent, AgentLoopConfig, AgentMessage, AgentState, AgentTool, AgentToolContext, ManagedLogicalRunId, RunCancellationDomainBridge, RunResourceLedger, RunTerminalRequest, StreamFn, ToolCallContext } from "./types";
|
|
8
|
+
import type { AgentEvent, AgentLoopConfig, AgentMessage, AgentMetadataResolverContext, AgentState, AgentTool, AgentToolContext, ManagedLogicalRunId, RunCancellationDomainBridge, RunResourceLedger, RunTerminalRequest, StreamFn, ToolCallContext } from "./types";
|
|
9
9
|
/**
|
|
10
10
|
* Whether persisted history ends at a point where a new model turn can resume.
|
|
11
11
|
* Assistant-ended histories require an in-memory queued message and are handled
|
|
@@ -271,7 +271,7 @@ export declare class Agent {
|
|
|
271
271
|
* only included for `"anthropic"` requests). Falls back to the static
|
|
272
272
|
* {@link metadata} value when no resolver is set.
|
|
273
273
|
*/
|
|
274
|
-
metadataForProvider(provider: string): Record<string, unknown> | undefined;
|
|
274
|
+
metadataForProvider(provider: string, model?: Model, transport?: AgentMetadataResolverContext["transport"]): Record<string, unknown> | undefined;
|
|
275
275
|
/**
|
|
276
276
|
* Install a function that resolves request metadata at call time. The
|
|
277
277
|
* resolver receives the target provider string and can gate provider-specific
|
|
@@ -280,7 +280,7 @@ export declare class Agent {
|
|
|
280
280
|
* credential. Pass `undefined` to clear and revert to the static
|
|
281
281
|
* {@link metadata} value.
|
|
282
282
|
*/
|
|
283
|
-
setMetadataResolver(resolver: ((
|
|
283
|
+
setMetadataResolver(resolver: ((context: AgentMetadataResolverContext) => Record<string, unknown> | undefined) | undefined): void;
|
|
284
284
|
/**
|
|
285
285
|
* Read the active OpenTelemetry configuration. Returns `undefined` when
|
|
286
286
|
* instrumentation is disabled. Callers spawning child runs (e.g. subagent
|
|
@@ -358,6 +358,18 @@ export declare class Agent {
|
|
|
358
358
|
setProvisionalAssistantMessageEventInterceptor(fn: ((message: AssistantMessage, event: AssistantMessageEvent) => void) | undefined): void;
|
|
359
359
|
setOnBeforeYield(fn: (() => Promise<void> | void) | undefined): void;
|
|
360
360
|
setShouldPause(fn: AgentLoopConfig["shouldPause"] | undefined): void;
|
|
361
|
+
/** The currently installed cooperative pause checkpoint, if any. */
|
|
362
|
+
get shouldPause(): AgentLoopConfig["shouldPause"] | undefined;
|
|
363
|
+
/**
|
|
364
|
+
* Fence old-turn steering admission.
|
|
365
|
+
*
|
|
366
|
+
* The loop polls steering UPSTREAM of its pause checkpoint (and again on the
|
|
367
|
+
* immediate-interrupt path), so a cooperative stop alone cannot prevent one
|
|
368
|
+
* more old-turn model call once a steering message has already been dequeued.
|
|
369
|
+
* While the fence returns true the poll yields no messages AND does not
|
|
370
|
+
* dequeue, so the queue survives intact for the next turn.
|
|
371
|
+
*/
|
|
372
|
+
setSteeringAdmissionFence(fn: (() => boolean) | undefined): void;
|
|
361
373
|
setMaintainContext(fn: AgentLoopConfig["maintainContext"] | undefined): void;
|
|
362
374
|
/**
|
|
363
375
|
* Publish an event produced OUTSIDE the agent loop (a provider that executed the tool
|
package/dist/types/types.d.ts
CHANGED
|
@@ -6,6 +6,20 @@ import type { AgentRunCoverage, AgentRunSummary } from "./run-collector";
|
|
|
6
6
|
import type { AgentTelemetryConfig } from "./telemetry";
|
|
7
7
|
/** Stream function - can return sync or Promise for async config lookup */
|
|
8
8
|
export type StreamFn = (...args: Parameters<typeof streamSimple>) => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
|
|
9
|
+
/**
|
|
10
|
+
* Request context supplied to provider-aware metadata resolvers.
|
|
11
|
+
*
|
|
12
|
+
* The model is the exact model selected for the concrete request (including
|
|
13
|
+
* fallback and ephemeral requests), while `transport` distinguishes the
|
|
14
|
+
* built-in stream path from a caller-supplied stream function. Metadata that
|
|
15
|
+
* carries provider identity must use both values to fail closed when routing
|
|
16
|
+
* is not the canonical provider path.
|
|
17
|
+
*/
|
|
18
|
+
export interface AgentMetadataResolverContext {
|
|
19
|
+
provider: string;
|
|
20
|
+
model?: Model;
|
|
21
|
+
transport?: "default" | "custom";
|
|
22
|
+
}
|
|
9
23
|
/** Stable identifier for a managed logical run, shared by all of its retry attempts. */
|
|
10
24
|
export type ManagedLogicalRunId = number;
|
|
11
25
|
/** A resource owned by a prompt run until its promise settles. */
|
|
@@ -233,7 +247,7 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
233
247
|
* current when `AgentLoopConfig` was first constructed). Overrides the static
|
|
234
248
|
* `metadata` field when present.
|
|
235
249
|
*/
|
|
236
|
-
metadataResolver?: (
|
|
250
|
+
metadataResolver?: (context: AgentMetadataResolverContext) => Record<string, unknown> | undefined;
|
|
237
251
|
/**
|
|
238
252
|
* Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.
|
|
239
253
|
*
|
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.16.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.16.0",
|
|
36
|
+
"@gajae-code/natives": "0.16.0",
|
|
37
|
+
"@gajae-code/utils": "0.16.0",
|
|
38
38
|
"@opentelemetry/api": "^1.9.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -4523,7 +4523,13 @@ async function streamAssistantResponse(
|
|
|
4523
4523
|
// reflects the credential actually used, not the snapshot from AgentLoopConfig construction.
|
|
4524
4524
|
const authCredentialType = config.getAuthCredentialType?.(config.model.provider);
|
|
4525
4525
|
|
|
4526
|
-
const resolvedMetadata = config.metadataResolver
|
|
4526
|
+
const resolvedMetadata = config.metadataResolver
|
|
4527
|
+
? config.metadataResolver({
|
|
4528
|
+
provider: config.model.provider,
|
|
4529
|
+
model: config.model,
|
|
4530
|
+
transport: streamFunction === streamSimple ? "default" : "custom",
|
|
4531
|
+
})
|
|
4532
|
+
: config.metadata;
|
|
4527
4533
|
|
|
4528
4534
|
// Synthetic recovery requests choose their tool mode explicitly below and
|
|
4529
4535
|
// must never consume a queued dynamic choice intended for an ordinary turn.
|
package/src/agent.ts
CHANGED
|
@@ -38,6 +38,7 @@ import type {
|
|
|
38
38
|
AgentEvent,
|
|
39
39
|
AgentLoopConfig,
|
|
40
40
|
AgentMessage,
|
|
41
|
+
AgentMetadataResolverContext,
|
|
41
42
|
AgentState,
|
|
42
43
|
AgentTool,
|
|
43
44
|
AgentToolContext,
|
|
@@ -116,6 +117,27 @@ function sanitizeAgentFailure(error: unknown, runtimeClassifiedCode?: string): {
|
|
|
116
117
|
return { code, message: "Agent run failed." };
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
/** Only runtime-authenticated built-in constructors may contribute a name. */
|
|
121
|
+
const TRUSTED_ERROR_CONSTRUCTORS = new Map<Function, string>([
|
|
122
|
+
[Error, "Error"],
|
|
123
|
+
[TypeError, "TypeError"],
|
|
124
|
+
[RangeError, "RangeError"],
|
|
125
|
+
[SyntaxError, "SyntaxError"],
|
|
126
|
+
[ReferenceError, "ReferenceError"],
|
|
127
|
+
[URIError, "URIError"],
|
|
128
|
+
[EvalError, "EvalError"],
|
|
129
|
+
[AggregateError, "AggregateError"],
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
function safeErrorName(error: unknown): string | undefined {
|
|
133
|
+
try {
|
|
134
|
+
if (!(error instanceof Error)) return undefined;
|
|
135
|
+
return TRUSTED_ERROR_CONSTRUCTORS.get(error.constructor);
|
|
136
|
+
} catch {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
119
141
|
/** Guarded HTTP-status extraction for untrusted provider errors: a throwing
|
|
120
142
|
* getter must never escape the failure handler and suppress terminalization
|
|
121
143
|
* (exact-head review P1). */
|
|
@@ -466,7 +488,7 @@ export class Agent {
|
|
|
466
488
|
#sessionId?: string;
|
|
467
489
|
#providerSessionId?: string;
|
|
468
490
|
#metadata?: Record<string, unknown>;
|
|
469
|
-
#metadataResolver?: (
|
|
491
|
+
#metadataResolver?: (context: AgentMetadataResolverContext) => Record<string, unknown> | undefined;
|
|
470
492
|
#providerSessionState?: Map<string, ProviderSessionState>;
|
|
471
493
|
#thinkingBudgets?: ThinkingBudgets;
|
|
472
494
|
#temperature?: number;
|
|
@@ -505,6 +527,8 @@ export class Agent {
|
|
|
505
527
|
#onHarmonyLeak?: (event: HarmonyAuditEvent) => void | Promise<void>;
|
|
506
528
|
#onBeforeYield?: () => Promise<void> | void;
|
|
507
529
|
#shouldPause?: AgentLoopConfig["shouldPause"];
|
|
530
|
+
/** While set and returning true, steering is neither admitted nor dequeued. */
|
|
531
|
+
#steeringAdmissionFence?: () => boolean;
|
|
508
532
|
#maintainContext?: AgentLoopConfig["maintainContext"];
|
|
509
533
|
#telemetry?: AgentLoopConfig["telemetry"];
|
|
510
534
|
#appendOnlyContext?: AppendOnlyContextManager;
|
|
@@ -669,8 +693,12 @@ export class Agent {
|
|
|
669
693
|
* only included for `"anthropic"` requests). Falls back to the static
|
|
670
694
|
* {@link metadata} value when no resolver is set.
|
|
671
695
|
*/
|
|
672
|
-
metadataForProvider(
|
|
673
|
-
|
|
696
|
+
metadataForProvider(
|
|
697
|
+
provider: string,
|
|
698
|
+
model?: Model,
|
|
699
|
+
transport?: AgentMetadataResolverContext["transport"],
|
|
700
|
+
): Record<string, unknown> | undefined {
|
|
701
|
+
if (this.#metadataResolver) return this.#metadataResolver({ provider, model, transport });
|
|
674
702
|
return this.#metadata;
|
|
675
703
|
}
|
|
676
704
|
|
|
@@ -682,7 +710,9 @@ export class Agent {
|
|
|
682
710
|
* credential. Pass `undefined` to clear and revert to the static
|
|
683
711
|
* {@link metadata} value.
|
|
684
712
|
*/
|
|
685
|
-
setMetadataResolver(
|
|
713
|
+
setMetadataResolver(
|
|
714
|
+
resolver: ((context: AgentMetadataResolverContext) => Record<string, unknown> | undefined) | undefined,
|
|
715
|
+
): void {
|
|
686
716
|
this.#metadataResolver = resolver;
|
|
687
717
|
}
|
|
688
718
|
|
|
@@ -891,6 +921,24 @@ export class Agent {
|
|
|
891
921
|
this.#shouldPause = fn;
|
|
892
922
|
}
|
|
893
923
|
|
|
924
|
+
/** The currently installed cooperative pause checkpoint, if any. */
|
|
925
|
+
get shouldPause(): AgentLoopConfig["shouldPause"] | undefined {
|
|
926
|
+
return this.#shouldPause;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Fence old-turn steering admission.
|
|
931
|
+
*
|
|
932
|
+
* The loop polls steering UPSTREAM of its pause checkpoint (and again on the
|
|
933
|
+
* immediate-interrupt path), so a cooperative stop alone cannot prevent one
|
|
934
|
+
* more old-turn model call once a steering message has already been dequeued.
|
|
935
|
+
* While the fence returns true the poll yields no messages AND does not
|
|
936
|
+
* dequeue, so the queue survives intact for the next turn.
|
|
937
|
+
*/
|
|
938
|
+
setSteeringAdmissionFence(fn: (() => boolean) | undefined): void {
|
|
939
|
+
this.#steeringAdmissionFence = fn;
|
|
940
|
+
}
|
|
941
|
+
|
|
894
942
|
setMaintainContext(fn: AgentLoopConfig["maintainContext"] | undefined): void {
|
|
895
943
|
this.#maintainContext = fn;
|
|
896
944
|
}
|
|
@@ -1911,6 +1959,12 @@ export class Agent {
|
|
|
1911
1959
|
skipInitialSteeringPoll = false;
|
|
1912
1960
|
return [];
|
|
1913
1961
|
}
|
|
1962
|
+
// Fenced: yield nothing and dequeue nothing, so a steer submitted while a
|
|
1963
|
+
// fold is being claimed is neither consumed by the run being wound down
|
|
1964
|
+
// nor lost.
|
|
1965
|
+
if (this.#steeringAdmissionFence?.() === true) {
|
|
1966
|
+
return [];
|
|
1967
|
+
}
|
|
1914
1968
|
const queued = this.#dequeueSteeringMessages();
|
|
1915
1969
|
if (this.#activeRunId !== runId) {
|
|
1916
1970
|
this.#steeringQueue = [...queued, ...this.#steeringQueue];
|
|
@@ -2095,6 +2149,8 @@ export class Agent {
|
|
|
2095
2149
|
const runtimeFailureCode = abortController.signal.aborted
|
|
2096
2150
|
? "aborted"
|
|
2097
2151
|
: (managedLocalErrorDiagnostic(err)?.errorKind ?? providerCode);
|
|
2152
|
+
const sanitized = sanitizeAgentFailure(err, runtimeFailureCode);
|
|
2153
|
+
const errorName = safeErrorName(err);
|
|
2098
2154
|
|
|
2099
2155
|
const errorMsg: AgentMessage = {
|
|
2100
2156
|
role: "assistant",
|
|
@@ -2111,7 +2167,9 @@ export class Agent {
|
|
|
2111
2167
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
2112
2168
|
},
|
|
2113
2169
|
stopReason: abortController.signal.aborted ? "aborted" : "error",
|
|
2114
|
-
errorMessage:
|
|
2170
|
+
errorMessage: sanitized.message,
|
|
2171
|
+
errorCode: sanitized.code,
|
|
2172
|
+
...(errorName ? { errorName } : {}),
|
|
2115
2173
|
errorStatus: safeErrorStatus(err),
|
|
2116
2174
|
// Local-diagnostic authority (`errorKind` + structured
|
|
2117
2175
|
// `bufferOverflow`) comes from ONE identity check: a foreign error
|
package/src/types.ts
CHANGED
|
@@ -28,6 +28,21 @@ export type StreamFn = (
|
|
|
28
28
|
...args: Parameters<typeof streamSimple>
|
|
29
29
|
) => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Request context supplied to provider-aware metadata resolvers.
|
|
33
|
+
*
|
|
34
|
+
* The model is the exact model selected for the concrete request (including
|
|
35
|
+
* fallback and ephemeral requests), while `transport` distinguishes the
|
|
36
|
+
* built-in stream path from a caller-supplied stream function. Metadata that
|
|
37
|
+
* carries provider identity must use both values to fail closed when routing
|
|
38
|
+
* is not the canonical provider path.
|
|
39
|
+
*/
|
|
40
|
+
export interface AgentMetadataResolverContext {
|
|
41
|
+
provider: string;
|
|
42
|
+
model?: Model;
|
|
43
|
+
transport?: "default" | "custom";
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
/** Stable identifier for a managed logical run, shared by all of its retry attempts. */
|
|
32
47
|
export type ManagedLogicalRunId = number;
|
|
33
48
|
/** A resource owned by a prompt run until its promise settles. */
|
|
@@ -257,7 +272,7 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
257
272
|
* current when `AgentLoopConfig` was first constructed). Overrides the static
|
|
258
273
|
* `metadata` field when present.
|
|
259
274
|
*/
|
|
260
|
-
metadataResolver?: (
|
|
275
|
+
metadataResolver?: (context: AgentMetadataResolverContext) => Record<string, unknown> | undefined;
|
|
261
276
|
|
|
262
277
|
/**
|
|
263
278
|
* Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.
|