@arnilo/prism 0.0.5 → 0.0.7
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 +39 -1
- package/dist/agent-loops.d.ts +1 -0
- package/dist/agent-loops.js +27 -16
- 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 +337 -46
- package/dist/contracts.d.ts +205 -3
- package/dist/contracts.js +4 -0
- package/dist/guardrails.d.ts +25 -0
- package/dist/guardrails.js +133 -0
- package/dist/ids.d.ts +2 -0
- package/dist/ids.js +6 -0
- package/dist/index.d.ts +17 -3
- package/dist/index.js +10 -3
- package/dist/input.js +2 -0
- package/dist/resources.js +2 -1
- 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/session-stores.js +2 -3
- package/dist/testing/persistence-schema.d.ts +45 -7
- package/dist/testing/persistence-schema.js +138 -24
- package/dist/thinking.d.ts +42 -0
- package/dist/thinking.js +92 -0
- package/dist/tools.d.ts +10 -2
- package/dist/tools.js +56 -7
- package/dist/use-case-model.d.ts +63 -0
- package/dist/use-case-model.js +52 -0
- package/docs/a2a.md +4 -2
- package/docs/agent-events.md +23 -16
- package/docs/agent-loops.md +19 -8
- package/docs/agent-session-runtime.md +33 -1
- package/docs/coding-agent-tools.md +33 -12
- package/docs/coding-security.md +2 -2
- package/docs/compaction-llm.md +17 -7
- package/docs/compaction-observational-memory.md +28 -4
- package/docs/credential-storage.md +58 -9
- package/docs/credentials-and-redaction.md +1 -1
- package/docs/database-persistence.md +8 -3
- package/docs/guardrails.md +75 -0
- package/docs/host-security.md +16 -8
- package/docs/index.md +26 -22
- package/docs/mcp-tools.md +32 -12
- package/docs/migration.md +164 -2
- package/docs/node-filesystem-config.md +1 -0
- package/docs/node-jsonl-session-store.md +5 -4
- package/docs/postgres-persistence.md +3 -3
- package/docs/provider-caching.md +16 -4
- package/docs/provider-conformance.md +39 -1
- package/docs/provider-packages.md +60 -3
- package/docs/providers/ai-sdk.md +36 -0
- package/docs/providers/kimi.md +124 -61
- package/docs/providers/neuralwatt.md +19 -13
- package/docs/providers/openai.md +56 -13
- package/docs/providers/opencode-go.md +118 -30
- package/docs/providers/openrouter.md +105 -35
- package/docs/providers/zai.md +94 -45
- package/docs/release-and-install.md +47 -49
- package/docs/review-coverage-2026-07-17-provider-validation.md +192 -0
- package/docs/runs-and-usage.md +30 -3
- package/docs/server.md +5 -2
- package/docs/sqlite-persistence.md +2 -2
- package/docs/structured-output.md +1 -1
- package/docs/thinking-and-reasoning.md +98 -0
- package/docs/tool-execution-primitives.md +3 -3
- package/docs/tools.md +21 -1
- package/docs/use-case-model-selection.md +109 -0
- package/docs/workflow-orchestration-primitives.md +1 -0
- package/docs/workflows.md +18 -10
- package/docs/working-and-semantic-memory.md +1 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,45 @@ 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
|
-
## [
|
|
8
|
+
## [0.0.7] - 2026-07-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- 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.
|
|
13
|
+
- Workflow tool nodes and MCP server tool registrations now route optional tool guardrails through shared `dispatchToolCall()`.
|
|
14
|
+
- `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`.
|
|
15
|
+
- 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.
|
|
16
|
+
- `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.
|
|
17
|
+
- `createAgentRunLifecycle()` adds explicit, ownership-scoped durable agent status/resume capability for selected server and MCP exposures; no lifecycle route/tool is enabled by default.
|
|
18
|
+
|
|
19
|
+
## [0.0.6] - 2026-07-19
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Caller-gated model discovery: `listOpenAIModels`, `listKimiModels`, `listZaiModels`, `listOpenRouterModels`, and `listOpenCodeGoModels`. Provider setup remains network-free; hosts explicitly fetch and register current models.
|
|
24
|
+
- Shared `ThinkingLevel` helpers and use-case model bindings. Background compaction and observational-memory jobs can use an explicit provider/model or a supplied session-model fallback.
|
|
25
|
+
- Opt-in sequential artifact-loop tools: `loop: { strategy: "generate-validate-revise", toolCalls: "bounded" }`. Tool rounds use existing authorization/redaction/ledger paths, share `maxToolRounds` across candidates, and fail with `artifact_failed` metadata `{ reason: "tool_round_limit" }` after exhaustion.
|
|
26
|
+
- Checksummed SQLite/PostgreSQL migration histories and catalog-shape verification, bounded JSON Schema compilation LRU, and public `assertFiniteVector` validation.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Provider packages now document and implement current cache, reasoning, streaming, and discovery behavior. OpenAI Responses replay/function-call/SSE argument handling is corrected; Kimi adds optional Moonshot support; Z.AI and OpenCode Go catalogs/routes were refreshed; OpenRouter discovery/reasoning and NeuralWatt thinking controls are hardened. AI SDK remains host-model-owned.
|
|
31
|
+
- Workflow definitions now require a non-empty `revision`; cancellation requires exact ownership and the current workflow definition. All workflow limits have finite hard caps.
|
|
32
|
+
- Coding tools now enforce bounded streamed reads, write/edit inputs, shell wall time, total output, and spill-file lifecycle. Custom coding operation interfaces now receive bounded read/stat/write/edit options and abort signals.
|
|
33
|
+
- Encrypted credential helpers `encryptBytes`, `decryptBytes`, and envelope rotation are asynchronous. Existing credential files must meet restrictive Unix permission requirements. Linux Secret Service/GNOME Keyring byte-array reads are accepted by the keychain store.
|
|
34
|
+
- MCP Streamable HTTP requires HTTPS and explicit `allowedOrigins`; loopback HTTP requires explicit opt-in. Discovery, schemas, results, and response bodies are bounded.
|
|
35
|
+
- Compaction and observational-memory workers now have finite turn/call/transcript/error budgets. A2A streaming uses strict incremental UTF-8 and LF/CRLF SSE parsing.
|
|
36
|
+
- Generated Prism, workflow, and evaluation IDs use cryptographic UUIDs; non-finite embedding vectors now fail before scoring or persistence.
|
|
37
|
+
|
|
38
|
+
### Security
|
|
39
|
+
|
|
40
|
+
- Fixed cross-owner workflow cancellation and duplicate active-run overwrite risks.
|
|
41
|
+
- Added fail-closed limits and validation at file, process, credential, MCP, migration, schema, vector, provider-worker, and A2A trust boundaries.
|
|
42
|
+
|
|
43
|
+
### Upgrade notes
|
|
44
|
+
|
|
45
|
+
- Finish or deliberately migrate pre-0.0.6 workflow runs/checkpoints before upgrading: their definition hashes lack the required revision.
|
|
46
|
+
- Update workflow definitions with `revision`, cancellation callers with `workflow` plus exact ownership, MCP HTTP configs with `allowedOrigins`, and custom coding/credential integrations for the changed interfaces above.
|
|
9
47
|
|
|
10
48
|
## [0.0.5] - 2026-07-16
|
|
11
49
|
|
package/dist/agent-loops.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare function generateValidateReviseLoop(opts: {
|
|
|
6
6
|
readonly parser?: ArtifactParser<unknown>;
|
|
7
7
|
readonly repairer?: ArtifactRepairer<unknown>;
|
|
8
8
|
readonly maxRevisions?: number;
|
|
9
|
+
readonly toolCalls?: "disabled" | "bounded";
|
|
9
10
|
}): AgentLoopStrategy;
|
|
10
11
|
export declare function resolveToolConcurrency(options: {
|
|
11
12
|
loop?: AgentLoopStrategy | AgentLoopOptions;
|
package/dist/agent-loops.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inputMessages } from "./input.js";
|
|
2
|
+
import { createId } from "./ids.js";
|
|
2
3
|
function throwIfAborted(signal) {
|
|
3
4
|
if (signal.aborted)
|
|
4
5
|
throw signal.reason instanceof Error ? signal.reason : new Error("Agent run aborted");
|
|
@@ -55,10 +56,8 @@ function defaultRepairer() {
|
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
// ponytail: GenerateValidateReviseLoop reuses LoopContext primitives only —
|
|
58
|
-
// no provider/retry/store/event re-implementation.
|
|
59
|
-
//
|
|
60
|
-
// generate→validate→revise; tool coupling deferred). Phase 28 fires
|
|
61
|
-
// artifact_* events at the marked seams (noop here).
|
|
59
|
+
// no provider/retry/store/event re-implementation. Bounded artifact tools use
|
|
60
|
+
// same dispatcher at concurrency one; add parallelism only with ordering need.
|
|
62
61
|
export function generateValidateReviseLoop(opts) {
|
|
63
62
|
const max = opts.maxRevisions ?? 3;
|
|
64
63
|
const repairer = opts.repairer ?? defaultRepairer();
|
|
@@ -68,12 +67,14 @@ export function generateValidateReviseLoop(opts) {
|
|
|
68
67
|
let usage;
|
|
69
68
|
let nextInput = ctx.input;
|
|
70
69
|
let pendingHistory = [];
|
|
71
|
-
|
|
70
|
+
let toolRounds = 0;
|
|
71
|
+
let attempts = 0;
|
|
72
|
+
for (let turn = 1; attempts <= max; turn += 1) {
|
|
72
73
|
throwIfAborted(ctx.signal);
|
|
73
74
|
ctx.emit({ type: "turn_started", sessionId: ctx.sessionId, runId: ctx.runId, turn });
|
|
74
75
|
const request = await ctx.assemble(nextInput, undefined, turn);
|
|
75
76
|
throwIfAborted(ctx.signal);
|
|
76
|
-
const { content, messageId, started, usage: turnUsage } = await ctx.generate(request);
|
|
77
|
+
const { content, calls, messageId, started, usage: turnUsage } = await ctx.generate(request);
|
|
77
78
|
usage = turnUsage ?? usage;
|
|
78
79
|
if (pendingHistory.length > 0) {
|
|
79
80
|
ctx.history.push(...pendingHistory);
|
|
@@ -88,10 +89,17 @@ export function generateValidateReviseLoop(opts) {
|
|
|
88
89
|
ctx.emit({ type: "message_finished", sessionId: ctx.sessionId, runId: ctx.runId, message });
|
|
89
90
|
}
|
|
90
91
|
ctx.emit({ type: "turn_finished", sessionId: ctx.sessionId, runId: ctx.runId, turn });
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
if (opts.toolCalls === "bounded" && calls.length > 0) {
|
|
93
|
+
if (toolRounds >= ctx.maxToolRounds) {
|
|
94
|
+
const result = { ok: false, errors: [{ message: "maximum tool rounds exceeded" }], metadata: { reason: "tool_round_limit" } };
|
|
95
|
+
ctx.emit({ type: "artifact_failed", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt: attempts + 1, result });
|
|
96
|
+
return usage;
|
|
97
|
+
}
|
|
98
|
+
toolRounds += 1;
|
|
99
|
+
await dispatchToolCallsInOrder(calls, { ...ctx, toolConcurrency: 1 });
|
|
100
|
+
nextInput = [];
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
95
103
|
const artifactCtx = {
|
|
96
104
|
sessionId: ctx.sessionId,
|
|
97
105
|
runId: ctx.runId,
|
|
@@ -99,13 +107,17 @@ export function generateValidateReviseLoop(opts) {
|
|
|
99
107
|
signal: ctx.signal,
|
|
100
108
|
metadata: ctx.metadata,
|
|
101
109
|
};
|
|
110
|
+
const text = content
|
|
111
|
+
.filter((b) => b.type === "text")
|
|
112
|
+
.map((b) => b.text)
|
|
113
|
+
.join("");
|
|
102
114
|
const parsed = opts.parser
|
|
103
115
|
? await opts.parser(text, artifactCtx)
|
|
104
116
|
: { ok: true, value: text };
|
|
105
117
|
// Parse failure ends the loop silently (terminal parse errors stay on `error`).
|
|
106
118
|
if (!parsed.ok || parsed.value === undefined)
|
|
107
119
|
return usage;
|
|
108
|
-
const attempt =
|
|
120
|
+
const attempt = ++attempts;
|
|
109
121
|
ctx.emit({ type: "artifact_validation_started", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt });
|
|
110
122
|
const result = await opts.validator(parsed.value, artifactCtx);
|
|
111
123
|
ctx.emit({ type: "artifact_validation_finished", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, result });
|
|
@@ -113,7 +125,7 @@ export function generateValidateReviseLoop(opts) {
|
|
|
113
125
|
ctx.emit({ type: "artifact_finished", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, result });
|
|
114
126
|
return usage;
|
|
115
127
|
}
|
|
116
|
-
if (
|
|
128
|
+
if (attempt > max) {
|
|
117
129
|
ctx.emit({ type: "artifact_failed", sessionId: ctx.sessionId, runId: ctx.runId, turn, attempt, result });
|
|
118
130
|
return usage;
|
|
119
131
|
}
|
|
@@ -124,7 +136,6 @@ export function generateValidateReviseLoop(opts) {
|
|
|
124
136
|
await ctx.appendMessage(message);
|
|
125
137
|
pendingHistory = repairMessages;
|
|
126
138
|
nextInput = repairMessages;
|
|
127
|
-
continue;
|
|
128
139
|
}
|
|
129
140
|
return usage;
|
|
130
141
|
},
|
|
@@ -148,6 +159,7 @@ export function resolveToolConcurrency(options, config) {
|
|
|
148
159
|
export async function dispatchToolCallsInOrder(calls, ctx) {
|
|
149
160
|
if (calls.length === 0)
|
|
150
161
|
return;
|
|
162
|
+
ctx.chargeToolRound?.(calls);
|
|
151
163
|
const concurrency = calls.some((call) => ctx.isToolCallExclusive?.(call))
|
|
152
164
|
? 1
|
|
153
165
|
: Math.max(1, Math.min(ctx.toolConcurrency, calls.length));
|
|
@@ -195,13 +207,12 @@ export function resolveLoop(options, config) {
|
|
|
195
207
|
parser: loop.parser,
|
|
196
208
|
repairer: loop.repairer,
|
|
197
209
|
maxRevisions: loop.maxRevisions,
|
|
210
|
+
toolCalls: loop.toolCalls,
|
|
198
211
|
});
|
|
199
212
|
}
|
|
200
213
|
throw new Error(`Unknown agent loop strategy: ${strategy}`);
|
|
201
214
|
}
|
|
202
215
|
return loop;
|
|
203
216
|
}
|
|
204
|
-
|
|
205
|
-
return `${prefix}_${globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2)}`;
|
|
206
|
-
}
|
|
217
|
+
const randomId = createId;
|
|
207
218
|
//# sourceMappingURL=agent-loops.js.map
|
|
@@ -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>;
|