@arnilo/prism 0.0.6 → 0.0.8
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 +32 -0
- package/README.md +3 -1
- package/dist/agent-loops.js +8 -5
- package/dist/agent-run-lifecycle.d.ts +28 -0
- package/dist/agent-run-lifecycle.js +33 -0
- package/dist/agent-run-state.d.ts +53 -0
- package/dist/agent-run-state.js +127 -0
- package/dist/agents.d.ts +3 -1
- package/dist/agents.js +356 -45
- package/dist/contracts.d.ts +218 -3
- package/dist/contracts.js +4 -0
- package/dist/guardrails.d.ts +25 -0
- package/dist/guardrails.js +133 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +9 -3
- package/dist/input.js +2 -0
- package/dist/resources.js +2 -1
- package/dist/run-ledger.d.ts +21 -0
- package/dist/run-ledger.js +115 -0
- package/dist/run-limits.d.ts +34 -0
- package/dist/run-limits.js +163 -0
- package/dist/secure-agent.d.ts +3 -0
- package/dist/secure-agent.js +63 -0
- package/dist/tools.d.ts +10 -2
- package/dist/tools.js +54 -4
- package/docs/a2a.md +61 -42
- package/docs/agent-events.md +15 -3
- package/docs/agent-loops.md +12 -4
- package/docs/agent-session-runtime.md +34 -1
- package/docs/credential-storage.md +9 -0
- package/docs/database-persistence.md +1 -1
- package/docs/evaluations.md +26 -3
- package/docs/guardrails.md +75 -0
- package/docs/host-security.md +31 -4
- package/docs/index.md +17 -14
- package/docs/mcp-tools.md +36 -8
- package/docs/migration.md +54 -0
- package/docs/observability.md +26 -14
- package/docs/performance.md +25 -0
- package/docs/postgres-persistence.md +1 -0
- package/docs/providers/kimi.md +16 -2
- package/docs/providers/opencode-go.md +43 -2
- package/docs/release-and-install.md +73 -62
- package/docs/resource-loading.md +4 -0
- package/docs/review-coverage-2026-07-19-phase-3.md +174 -0
- package/docs/run-ledger-conformance.md +1 -0
- package/docs/runs-and-usage.md +46 -4
- package/docs/server.md +5 -2
- package/docs/sqlite-persistence.md +1 -0
- package/docs/supervisors.md +2 -2
- package/docs/tools.md +8 -2
- package/docs/web-tools.md +78 -0
- package/docs/workflows.md +2 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## [0.0.8] - 2026-07-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added OpenTelemetry GenAI agent/provider/tool hierarchy, context propagation, delegation/guardrail spans, bounded trace references, and evaluation linkage.
|
|
15
|
+
- Added bounded evaluation trace resolution, host model judges, deterministic pairwise reports, serialized artifacts, and CI threshold assertions.
|
|
16
|
+
- Added MCP resources/prompts/roots/sampling/elicitation plus principal-bound Streamable HTTP sessions on pinned SDK 1.29.0, and full A2A 1.0 durable task/rich-part/reconnect/push interoperability.
|
|
17
|
+
- Added immutable-revision CodeQL/dependency/SBOM/license/secret/attestation release gates, weekly dependency updates, and protected bounded provider/MCP/A2A/web live canaries.
|
|
18
|
+
- Added optional `@arnilo/prism-web-tools` with bounded host-selected Brave/Exa search, Firecrawl Markdown/schema extraction, stable citations, late credentials, and explicit untrusted-content results.
|
|
19
|
+
- Added optional `createBatchedRunLedger()` with bounded FIFO/backpressure, explicit durability/flush status, terminal acknowledgement, and documented buffered crash-loss semantics.
|
|
20
|
+
- Added one-leaf, one-second runtime session snapshot caching with mutation/checkout/resume invalidation and reproducible network-free 0.0.8 performance evidence.
|
|
21
|
+
- Versioned all 31 first-party manifests and exact internal ranges to 0.0.8; no tag or publication was created.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `generateValidateReviseLoop` routes artifact parse failures through the revision budget (`metadata.reason: "parse_error"`, repairer receives `value: undefined`) instead of returning silently after one provider turn.
|
|
26
|
+
- `@arnilo/prism-provider-opencode-go` Anthropic route sends provider-owned `x-api-key` and `anthropic-version: 2023-06-01` headers alongside Bearer, fixing HTTP 401 on MiniMax/Qwen models; `structuredOutput: "json_schema"` is no longer inferred from OpenAI routing alone (verified models only), fixing HTTP 400 on `deepseek-v4-pro`; both stream parsers require protocol completion evidence and fail truncated streams with a terminal `error` instead of a false `done`.
|
|
27
|
+
- `@arnilo/prism-provider-kimi` aligns with official contracts: featured Coding `k3` defaults `reasoning_effort: "high"`, 256K-class context windows use the exact `262_144`, the featured Moonshot catalog adds `kimi-k2.7-code-highspeed`/`kimi-k2.6`/`kimi-k2.5`, routing keys (`route`, `preserve_thinking`) no longer leak into wire bodies, the Coding route sends provider-owned `x-api-key`/`anthropic-version` headers, and both stream parsers fail truncated streams instead of emitting `done`.
|
|
28
|
+
|
|
29
|
+
## [0.0.7] - 2026-07-19
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Typed `Guardrails` for input, provider output, tool input, and tool output. Guardrail decisions are bounded/redacted `guardrail_decision` events; provider output is buffered before exposure when output checks are configured.
|
|
34
|
+
- Workflow tool nodes and MCP server tool registrations now route optional tool guardrails through shared `dispatchToolCall()`.
|
|
35
|
+
- `RunLimits` adds validated, narrowing-only budgets for turns, provider attempts, tool rounds/calls, wall time, request/response bytes, token usage, and optional single-currency cost. Breaches emit one `run_limit_exceeded` event and return `AgentRunError.result.limit`.
|
|
36
|
+
- Opt-in durable built-in agent runs can suspend before a tool side effect and resume through versioned, bounded, redacted checkpoint state with CAS approval, ownership/fingerprint checks, and no automatic replay of an ambiguous dispatched tool.
|
|
37
|
+
- `createSecureAgent()` composes strict tool schemas/validation, trust and permission gates, redaction, finite limits, exact ownership, and durable pre-tool approval without changing low-level `createAgent()` defaults.
|
|
38
|
+
- `createAgentRunLifecycle()` adds explicit, ownership-scoped durable agent status/resume capability for selected server and MCP exposures; no lifecycle route/tool is enabled by default.
|
|
39
|
+
|
|
8
40
|
## [0.0.6] - 2026-07-19
|
|
9
41
|
|
|
10
42
|
### Added
|
package/README.md
CHANGED
|
@@ -53,6 +53,7 @@ npm install @arnilo/prism-sdk @arnilo/prism-provider-openai # application profi
|
|
|
53
53
|
npm install @arnilo/prism-all # every first-party package
|
|
54
54
|
npm install @arnilo/prism-server @arnilo/prism-workflows # optional Web API boundary
|
|
55
55
|
npm install @arnilo/prism-supervisor # optional local delegation + A2A 1.0
|
|
56
|
+
npm install @arnilo/prism-web-tools # optional bounded Brave/Exa/Firecrawl research
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
See [docs/release-and-install.md](docs/release-and-install.md) for install
|
|
@@ -157,6 +158,7 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
|
|
|
157
158
|
| `@arnilo/prism-mcp` | MCP client/tool bridge |
|
|
158
159
|
| `@arnilo/prism-workflows` | bounded DAG workflows, durable suspend/resume, schedules/background runs, composition/state/replay, and multi-process coordination |
|
|
159
160
|
| `@arnilo/prism-supervisor` | bounded local child delegation and A2A 1.0 interoperability |
|
|
161
|
+
| `@arnilo/prism-web-tools` | host-selected bounded Brave/Exa search and Firecrawl Markdown/schema extraction |
|
|
160
162
|
| `@arnilo/prism-observability-opentelemetry` | optional OpenTelemetry adapter |
|
|
161
163
|
| `@arnilo/prism-credentials-node` | encrypted-file and keychain credentials |
|
|
162
164
|
| `@arnilo/prism-session-store-sqlite` | SQLite persistence/checkpoints/leases/owned run feedback |
|
|
@@ -166,7 +168,7 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
|
|
|
166
168
|
| `@arnilo/prism-base` | profile: core + compaction + JSON Schema validation |
|
|
167
169
|
| `@arnilo/prism-code` | profile: base + coding tools/security + MCP |
|
|
168
170
|
| `@arnilo/prism-sdk` | profile: base + workflows + MCP + credentials + OpenTelemetry |
|
|
169
|
-
| `@arnilo/prism-all` | every first-party package, including both persistence adapters |
|
|
171
|
+
| `@arnilo/prism-all` | every first-party package, including both persistence adapters and web tools |
|
|
170
172
|
|
|
171
173
|
## Scripts
|
|
172
174
|
|
package/dist/agent-loops.js
CHANGED
|
@@ -114,12 +114,14 @@ export function generateValidateReviseLoop(opts) {
|
|
|
114
114
|
const parsed = opts.parser
|
|
115
115
|
? await opts.parser(text, artifactCtx)
|
|
116
116
|
: { ok: true, value: text };
|
|
117
|
-
// Parse failure
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
// Parse failure consumes revision budget like a validation failure; the
|
|
118
|
+
// repairer receives `undefined` value plus a synthetic parse issue.
|
|
119
|
+
const parseFailure = !parsed.ok || parsed.value === undefined
|
|
120
|
+
? { ok: false, errors: [{ path: "$", message: parsed.error ?? "artifact parse failed" }], metadata: { reason: "parse_error" } }
|
|
121
|
+
: undefined;
|
|
120
122
|
const attempt = ++attempts;
|
|
121
123
|
ctx.emit({ type: "artifact_validation_started", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt });
|
|
122
|
-
const result = await opts.validator(parsed.value, artifactCtx);
|
|
124
|
+
const result = parseFailure ?? await opts.validator(parsed.value, artifactCtx);
|
|
123
125
|
ctx.emit({ type: "artifact_validation_finished", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, result });
|
|
124
126
|
if (result.ok) {
|
|
125
127
|
ctx.emit({ type: "artifact_finished", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, result });
|
|
@@ -130,7 +132,7 @@ export function generateValidateReviseLoop(opts) {
|
|
|
130
132
|
return usage;
|
|
131
133
|
}
|
|
132
134
|
ctx.emit({ type: "artifact_revision_started", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, failure: result });
|
|
133
|
-
const repair = await repairer(parsed.value, result, artifactCtx);
|
|
135
|
+
const repair = await repairer(parseFailure ? undefined : parsed.value, result, artifactCtx);
|
|
134
136
|
const repairMessages = inputMessages(repair).map((m) => ({ ...m, id: randomId("msg") }));
|
|
135
137
|
for (const message of repairMessages)
|
|
136
138
|
await ctx.appendMessage(message);
|
|
@@ -159,6 +161,7 @@ export function resolveToolConcurrency(options, config) {
|
|
|
159
161
|
export async function dispatchToolCallsInOrder(calls, ctx) {
|
|
160
162
|
if (calls.length === 0)
|
|
161
163
|
return;
|
|
164
|
+
ctx.chargeToolRound?.(calls);
|
|
162
165
|
const concurrency = calls.some((call) => ctx.isToolCallExclusive?.(call))
|
|
163
166
|
? 1
|
|
164
167
|
: Math.max(1, Math.min(ctx.toolConcurrency, calls.length));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Agent, AgentRunRef, AgentRunResult, AgentRunResume, AgentRunStatusResult, OwnershipScope } from "./contracts.js";
|
|
2
|
+
import type { CheckpointStore } from "./contracts.js";
|
|
3
|
+
export interface AgentRunLifecycleAgent {
|
|
4
|
+
readonly agent: Agent;
|
|
5
|
+
/** Current host-authored revision; it must match the stored revision. */
|
|
6
|
+
readonly definitionRevision: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AgentRunLifecycleOptions {
|
|
9
|
+
readonly checkpoints: CheckpointStore;
|
|
10
|
+
readonly resolveAgent: (input: {
|
|
11
|
+
readonly agentId: string;
|
|
12
|
+
readonly ownership?: OwnershipScope;
|
|
13
|
+
readonly signal?: AbortSignal;
|
|
14
|
+
}) => AgentRunLifecycleAgent | Promise<AgentRunLifecycleAgent>;
|
|
15
|
+
readonly fencingToken?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentRunLifecycleRequest {
|
|
18
|
+
readonly ownership?: OwnershipScope;
|
|
19
|
+
readonly signal?: AbortSignal;
|
|
20
|
+
/** Adapter-selected capability; stored runs for another agent are non-enumerable. */
|
|
21
|
+
readonly agentId?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AgentRunLifecycle {
|
|
24
|
+
status(ref: AgentRunRef, options?: AgentRunLifecycleRequest): Promise<AgentRunStatusResult>;
|
|
25
|
+
resume(ref: AgentRunRef, resume: AgentRunResume, options?: AgentRunLifecycleRequest): Promise<AgentRunResult>;
|
|
26
|
+
}
|
|
27
|
+
/** Host capability for durable agent status/resume. Adapters supply authorized ownership only. */
|
|
28
|
+
export declare function createAgentRunLifecycle(options: AgentRunLifecycleOptions): AgentRunLifecycle;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AgentRunStateError } from "./contracts.js";
|
|
2
|
+
import { loadAgentRunState, publicState } from "./agent-run-state.js";
|
|
3
|
+
import { resumeAgentRun } from "./agents.js";
|
|
4
|
+
function assertAgentId(actual, expected) {
|
|
5
|
+
if (expected !== undefined && actual !== expected)
|
|
6
|
+
throw new AgentRunStateError("Agent run capability mismatch");
|
|
7
|
+
}
|
|
8
|
+
/** Host capability for durable agent status/resume. Adapters supply authorized ownership only. */
|
|
9
|
+
export function createAgentRunLifecycle(options) {
|
|
10
|
+
return {
|
|
11
|
+
async status(ref, request = {}) {
|
|
12
|
+
request.signal?.throwIfAborted();
|
|
13
|
+
const { state, record } = await loadAgentRunState(options.checkpoints, ref, request.ownership);
|
|
14
|
+
assertAgentId(state.agentId, request.agentId);
|
|
15
|
+
request.signal?.throwIfAborted();
|
|
16
|
+
return { state: publicState({ ...state, version: record.version }), version: record.version };
|
|
17
|
+
},
|
|
18
|
+
async resume(ref, resume, request = {}) {
|
|
19
|
+
request.signal?.throwIfAborted();
|
|
20
|
+
const { state } = await loadAgentRunState(options.checkpoints, ref, request.ownership);
|
|
21
|
+
assertAgentId(state.agentId, request.agentId);
|
|
22
|
+
const resolved = await options.resolveAgent({ agentId: state.agentId, ownership: request.ownership, signal: request.signal });
|
|
23
|
+
request.signal?.throwIfAborted();
|
|
24
|
+
return resumeAgentRun(resolved.agent, ref, resume, {
|
|
25
|
+
checkpoints: options.checkpoints,
|
|
26
|
+
ownership: request.ownership,
|
|
27
|
+
fencingToken: options.fencingToken,
|
|
28
|
+
definitionRevision: resolved.definitionRevision,
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=agent-run-lifecycle.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Agent, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, AgentRunStatusResult, CheckpointRecord, CheckpointStore, Message, ModelConfig, OwnershipScope, RunLimitCounters, ToolCallContent } from "./contracts.js";
|
|
2
|
+
import type { SecretRedactor } from "./redaction.js";
|
|
3
|
+
export declare const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
|
|
4
|
+
export declare const AGENT_RUN_STATE_SCHEMA_VERSION: 1;
|
|
5
|
+
export declare const DEFAULT_MAX_AGENT_RUN_STATE_BYTES: number;
|
|
6
|
+
export declare const HARD_MAX_AGENT_RUN_STATE_BYTES: number;
|
|
7
|
+
export interface StoredAgentRunState extends AgentRunState {
|
|
8
|
+
readonly input?: readonly Message[];
|
|
9
|
+
readonly pending?: {
|
|
10
|
+
readonly call: ToolCallContent;
|
|
11
|
+
readonly status: "ready" | "dispatched";
|
|
12
|
+
};
|
|
13
|
+
readonly interruptBeforeTool?: boolean;
|
|
14
|
+
readonly counters: RunLimitCounters;
|
|
15
|
+
readonly deadlineAt: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function agentFingerprint(agent: Agent, revision: string): string;
|
|
18
|
+
export declare function agentId(agent: Agent): string;
|
|
19
|
+
export declare function validateRunStateOptions(options: AgentRunStateOptions): void;
|
|
20
|
+
export declare function loadAgentRunState(checkpoints: CheckpointStore, ref: AgentRunRef, ownership?: OwnershipScope): Promise<{
|
|
21
|
+
readonly record: CheckpointRecord;
|
|
22
|
+
readonly state: StoredAgentRunState;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function saveAgentRunState(input: {
|
|
25
|
+
readonly checkpoints: CheckpointStore;
|
|
26
|
+
readonly state: StoredAgentRunState;
|
|
27
|
+
readonly expectedVersion: number;
|
|
28
|
+
readonly ownership?: OwnershipScope;
|
|
29
|
+
readonly fencingToken?: number;
|
|
30
|
+
readonly redactor?: SecretRedactor;
|
|
31
|
+
readonly maxStateBytes?: number;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
readonly record: CheckpointRecord;
|
|
34
|
+
readonly state: StoredAgentRunState;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function statusFromState(state: StoredAgentRunState, version: number): AgentRunStatusResult;
|
|
37
|
+
export declare function publicState(state: StoredAgentRunState): AgentRunState;
|
|
38
|
+
export declare function initialAgentRunState(input: {
|
|
39
|
+
readonly agent: Agent;
|
|
40
|
+
readonly options: AgentRunStateOptions;
|
|
41
|
+
readonly runId: string;
|
|
42
|
+
readonly sessionId: string;
|
|
43
|
+
readonly leafId?: string;
|
|
44
|
+
readonly model: ModelConfig;
|
|
45
|
+
readonly counters: RunLimitCounters;
|
|
46
|
+
readonly deadlineAt: string;
|
|
47
|
+
readonly status: "suspended" | "running";
|
|
48
|
+
readonly interruption?: AgentRunInterruption;
|
|
49
|
+
readonly messages?: readonly Message[];
|
|
50
|
+
readonly pending?: StoredAgentRunState["pending"];
|
|
51
|
+
readonly interruptBeforeTool?: boolean;
|
|
52
|
+
}): StoredAgentRunState;
|
|
53
|
+
export declare function parseAgentRunState(value: unknown, version?: number): StoredAgentRunState;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { AgentRunStateError } from "./contracts.js";
|
|
3
|
+
export const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
|
|
4
|
+
export const AGENT_RUN_STATE_SCHEMA_VERSION = 1;
|
|
5
|
+
export const DEFAULT_MAX_AGENT_RUN_STATE_BYTES = 256 * 1024;
|
|
6
|
+
export const HARD_MAX_AGENT_RUN_STATE_BYTES = 1024 * 1024;
|
|
7
|
+
const MAX_DEPTH = 32;
|
|
8
|
+
const MAX_PROPERTIES = 256;
|
|
9
|
+
export function agentFingerprint(agent, revision) {
|
|
10
|
+
const config = agent.config;
|
|
11
|
+
const tools = !config.tools ? [] : "list" in config.tools ? config.tools.list() : config.tools;
|
|
12
|
+
const guardrails = [
|
|
13
|
+
...(config.guardrails?.input ?? []),
|
|
14
|
+
...(config.guardrails?.output ?? []),
|
|
15
|
+
...(config.guardrails?.toolInput ?? []),
|
|
16
|
+
...(config.guardrails?.toolOutput ?? []),
|
|
17
|
+
];
|
|
18
|
+
const value = JSON.stringify({
|
|
19
|
+
id: config.id ?? config.name ?? "agent",
|
|
20
|
+
revision,
|
|
21
|
+
model: config.model,
|
|
22
|
+
tools: tools.map((tool) => ({ name: tool.name, parameters: tool.parameters, exclusive: tool.exclusive })),
|
|
23
|
+
guardrails: guardrails.map((guardrail) => ({ name: guardrail.name, stage: guardrail.stage, revision: guardrail.revision })),
|
|
24
|
+
loop: typeof config.loop === "object" && config.loop && "strategy" in config.loop ? config.loop.strategy : config.loop?.name ?? "single-shot",
|
|
25
|
+
});
|
|
26
|
+
return createHash("sha256").update(value).digest("hex");
|
|
27
|
+
}
|
|
28
|
+
export function agentId(agent) {
|
|
29
|
+
const id = agent.config.id ?? agent.config.name;
|
|
30
|
+
if (!id?.trim())
|
|
31
|
+
throw new AgentRunStateError("Durable agent runs require AgentConfig.id or name");
|
|
32
|
+
return id;
|
|
33
|
+
}
|
|
34
|
+
export function validateRunStateOptions(options) {
|
|
35
|
+
if (!options.definitionRevision.trim())
|
|
36
|
+
throw new AgentRunStateError("Durable agent runs require definitionRevision");
|
|
37
|
+
const bytes = options.maxStateBytes ?? DEFAULT_MAX_AGENT_RUN_STATE_BYTES;
|
|
38
|
+
if (!Number.isSafeInteger(bytes) || bytes < 1 || bytes > HARD_MAX_AGENT_RUN_STATE_BYTES) {
|
|
39
|
+
throw new AgentRunStateError(`maxStateBytes must be a positive safe integer at most ${HARD_MAX_AGENT_RUN_STATE_BYTES}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export async function loadAgentRunState(checkpoints, ref, ownership) {
|
|
43
|
+
const record = await checkpoints.loadCheckpoint({ namespace: AGENT_RUN_STATE_NAMESPACE, key: ref.runId, ...ownership });
|
|
44
|
+
if (!record)
|
|
45
|
+
throw new AgentRunStateError(`No durable agent run ${ref.runId}`);
|
|
46
|
+
if (ref.sessionId && record.value && typeof record.value === "object" && record.value.sessionId !== ref.sessionId) {
|
|
47
|
+
throw new AgentRunStateError("Agent run session mismatch");
|
|
48
|
+
}
|
|
49
|
+
return { record, state: parseAgentRunState(record.value, record.version) };
|
|
50
|
+
}
|
|
51
|
+
export async function saveAgentRunState(input) {
|
|
52
|
+
const bounded = boundState(input.redactor?.redact(input.state) ?? input.state, input.maxStateBytes ?? DEFAULT_MAX_AGENT_RUN_STATE_BYTES);
|
|
53
|
+
const record = await input.checkpoints.saveCheckpoint({
|
|
54
|
+
namespace: AGENT_RUN_STATE_NAMESPACE,
|
|
55
|
+
key: bounded.runId,
|
|
56
|
+
version: input.expectedVersion + 1,
|
|
57
|
+
expectedVersion: input.expectedVersion,
|
|
58
|
+
fencingToken: input.fencingToken,
|
|
59
|
+
value: bounded,
|
|
60
|
+
category: "agent-run",
|
|
61
|
+
...input.ownership,
|
|
62
|
+
});
|
|
63
|
+
return { record, state: { ...bounded, version: record.version } };
|
|
64
|
+
}
|
|
65
|
+
export function statusFromState(state, version) {
|
|
66
|
+
return { state: publicState({ ...state, version }), version };
|
|
67
|
+
}
|
|
68
|
+
export function publicState(state) {
|
|
69
|
+
const { input: _input, pending: _pending, interruptBeforeTool: _interruptBeforeTool, counters: _counters, deadlineAt: _deadlineAt, ...publicValue } = state;
|
|
70
|
+
return publicValue;
|
|
71
|
+
}
|
|
72
|
+
export function initialAgentRunState(input) {
|
|
73
|
+
validateRunStateOptions(input.options);
|
|
74
|
+
return {
|
|
75
|
+
schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
|
|
76
|
+
agentId: agentId(input.agent),
|
|
77
|
+
definitionRevision: input.options.definitionRevision,
|
|
78
|
+
fingerprint: agentFingerprint(input.agent, input.options.definitionRevision),
|
|
79
|
+
runId: input.runId,
|
|
80
|
+
sessionId: input.sessionId,
|
|
81
|
+
...(input.leafId ? { leafId: input.leafId } : {}),
|
|
82
|
+
model: input.model,
|
|
83
|
+
status: input.status,
|
|
84
|
+
interruption: input.interruption,
|
|
85
|
+
input: input.messages,
|
|
86
|
+
pending: input.pending,
|
|
87
|
+
interruptBeforeTool: input.interruptBeforeTool,
|
|
88
|
+
counters: input.counters,
|
|
89
|
+
deadlineAt: input.deadlineAt,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export function parseAgentRunState(value, version) {
|
|
93
|
+
if (!value || typeof value !== "object")
|
|
94
|
+
throw new AgentRunStateError("Agent run state must be an object");
|
|
95
|
+
const state = value;
|
|
96
|
+
if (state.schemaVersion !== AGENT_RUN_STATE_SCHEMA_VERSION)
|
|
97
|
+
throw new AgentRunStateError(`Unsupported agent run state schemaVersion ${String(state.schemaVersion)}`);
|
|
98
|
+
if (!state.agentId || !state.definitionRevision || !state.fingerprint || !state.runId || !state.sessionId || !state.model || !state.status || !state.counters || !state.deadlineAt) {
|
|
99
|
+
throw new AgentRunStateError("Malformed agent run state");
|
|
100
|
+
}
|
|
101
|
+
return boundState({ ...state, version }, DEFAULT_MAX_AGENT_RUN_STATE_BYTES);
|
|
102
|
+
}
|
|
103
|
+
function boundState(state, maxBytes) {
|
|
104
|
+
checkShape(state, 0);
|
|
105
|
+
let text;
|
|
106
|
+
try {
|
|
107
|
+
text = JSON.stringify(state);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
throw new AgentRunStateError("Agent run state must be JSON serializable");
|
|
111
|
+
}
|
|
112
|
+
if (Buffer.byteLength(text) > maxBytes)
|
|
113
|
+
throw new AgentRunStateError(`Agent run state exceeds ${maxBytes} bytes`);
|
|
114
|
+
return JSON.parse(text);
|
|
115
|
+
}
|
|
116
|
+
function checkShape(value, depth) {
|
|
117
|
+
if (depth > MAX_DEPTH)
|
|
118
|
+
throw new AgentRunStateError(`Agent run state exceeds depth ${MAX_DEPTH}`);
|
|
119
|
+
if (!value || typeof value !== "object")
|
|
120
|
+
return;
|
|
121
|
+
const entries = Array.isArray(value) ? value : Object.values(value);
|
|
122
|
+
if (!Array.isArray(value) && entries.length > MAX_PROPERTIES)
|
|
123
|
+
throw new AgentRunStateError(`Agent run state exceeds ${MAX_PROPERTIES} properties`);
|
|
124
|
+
for (const item of entries)
|
|
125
|
+
checkShape(item, depth + 1);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=agent-run-state.js.map
|
package/dist/agents.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { Agent, AgentConfig, AgentSession, AgentSessionConfig } from "./contracts.js";
|
|
1
|
+
import type { Agent, AgentConfig, AgentRunResult, AgentRunResume, AgentRunResumeOptions, AgentRunRef, AgentSession, AgentSessionConfig } from "./contracts.js";
|
|
2
2
|
export declare function createAgent(config: AgentConfig): Agent;
|
|
3
3
|
export declare function createAgentSession(config: AgentSessionConfig & {
|
|
4
4
|
readonly agent: Agent;
|
|
5
5
|
}): AgentSession;
|
|
6
|
+
/** Resume a persisted built-in run. A claimed/dispatched tool is never replayed automatically. */
|
|
7
|
+
export declare function resumeAgentRun(agent: Agent, ref: AgentRunRef, resume: AgentRunResume, options: AgentRunResumeOptions): Promise<AgentRunResult>;
|