@arnilo/prism 0.0.23 → 0.0.25
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 -3
- package/dist/agent-event-source.d.ts +11 -0
- package/dist/agent-event-source.js +512 -0
- package/dist/agent-loops.js +37 -5
- package/dist/agent-run-state.d.ts +27 -1
- package/dist/agent-run-state.js +86 -5
- package/dist/agents.js +890 -78
- package/dist/contracts.d.ts +338 -4
- package/dist/contracts.js +53 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +5 -2
- package/dist/testing/agent-event-source-conformance.d.ts +4 -0
- package/dist/testing/agent-event-source-conformance.js +54 -0
- package/dist/testing/persistence-schema.d.ts +2 -2
- package/dist/testing/persistence-schema.js +58 -21
- package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
- package/dist/testing/tool-effect-store-conformance.js +85 -0
- package/dist/tool-effects.d.ts +15 -0
- package/dist/tool-effects.js +338 -0
- package/dist/tools.d.ts +4 -1
- package/dist/tools.js +219 -9
- package/docs/0.1.0-readiness.md +10 -9
- package/docs/a2a.md +6 -2
- package/docs/ag-ui-adoption.md +77 -0
- package/docs/ag-ui.md +77 -42
- package/docs/agent-events.md +5 -1
- package/docs/agent-loops.md +9 -1
- package/docs/agent-session-runtime.md +9 -2
- package/docs/browser-automation.md +2 -0
- package/docs/coding-agent-tools.md +2 -0
- package/docs/coding-security.md +1 -1
- package/docs/database-persistence.md +2 -0
- package/docs/enterprise-postgres-state.md +5 -1
- package/docs/host-security.md +8 -1
- package/docs/index.md +13 -11
- package/docs/mcp-tools.md +19 -2
- package/docs/migration.md +46 -0
- package/docs/performance.md +25 -0
- package/docs/postgres-persistence.md +5 -2
- package/docs/public-contracts.md +2 -0
- package/docs/release-and-install.md +70 -690
- package/docs/server.md +10 -6
- package/docs/sqlite-persistence.md +10 -2
- package/docs/supervisors.md +6 -0
- package/docs/tool-effects.md +95 -0
- package/docs/tools.md +4 -0
- package/docs/work-tools.md +4 -0
- package/docs/workflows.md +1 -1
- package/package.json +11 -3
|
@@ -1,19 +1,44 @@
|
|
|
1
|
-
import type { Agent, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, AgentRunStatusResult, CheckpointRecord, CheckpointStore, Message, ModelConfig, OwnershipScope, RunLimitCounters, ToolCallContent } from "./contracts.js";
|
|
1
|
+
import type { Agent, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, AgentRunStatusResult, CheckpointRecord, CheckpointStore, JsonValue, Message, ModelConfig, NestedRunRef, OwnershipScope, RunDecision, RunLimitCounters, StickyDecision, ToolCallContent } from "./contracts.js";
|
|
2
2
|
import type { SecretRedactor } from "./redaction.js";
|
|
3
3
|
export declare const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
|
|
4
4
|
export declare const AGENT_RUN_STATE_SCHEMA_VERSION: 1;
|
|
5
5
|
export declare const DEFAULT_MAX_AGENT_RUN_STATE_BYTES: number;
|
|
6
6
|
export declare const HARD_MAX_AGENT_RUN_STATE_BYTES: number;
|
|
7
|
+
/** One gated tool call awaiting or holding a decision inside a suspended durable run. */
|
|
8
|
+
export interface PendingToolCall {
|
|
9
|
+
readonly call: ToolCallContent;
|
|
10
|
+
readonly status: "ready" | "dispatched";
|
|
11
|
+
readonly approvalId: string;
|
|
12
|
+
/** Decision persisted by a partial batch; applied when the run finally resumes. */
|
|
13
|
+
readonly decision?: RunDecision;
|
|
14
|
+
}
|
|
7
15
|
export interface StoredAgentRunState extends AgentRunState {
|
|
8
16
|
readonly input?: readonly Message[];
|
|
17
|
+
/** Legacy single gated call (pre-0.0.25 checkpoints). New states write `pendingCalls`. */
|
|
9
18
|
readonly pending?: {
|
|
10
19
|
readonly call: ToolCallContent;
|
|
11
20
|
readonly status: "ready" | "dispatched";
|
|
12
21
|
};
|
|
22
|
+
/** Gated calls of the current suspension, in provider-turn order. */
|
|
23
|
+
readonly pendingCalls?: readonly PendingToolCall[];
|
|
24
|
+
/** Suspended nested runs (supervisor children) whose pending decisions surface at this root. */
|
|
25
|
+
readonly nestedRuns?: readonly NestedRunRef[];
|
|
26
|
+
/** Run-scoped sticky decisions; exact scope match, dropped at any terminal status. */
|
|
27
|
+
readonly stickyDecisions?: readonly StickyDecision[];
|
|
13
28
|
readonly interruptBeforeTool?: boolean;
|
|
14
29
|
readonly counters: RunLimitCounters;
|
|
15
30
|
readonly deadlineAt: string;
|
|
31
|
+
/** Loop-local durable state captured by the strategy's snapshot hook at suspension. */
|
|
32
|
+
readonly loopState?: {
|
|
33
|
+
readonly name: string;
|
|
34
|
+
readonly revision: string;
|
|
35
|
+
readonly snapshot: JsonValue;
|
|
36
|
+
};
|
|
16
37
|
}
|
|
38
|
+
/** Revision stamps of the built-in loops; custom strategies declare their own `revision`. */
|
|
39
|
+
export declare const BUILT_IN_LOOP_REVISIONS: Readonly<Record<string, string>>;
|
|
40
|
+
/** Validate a strategy snapshot as JSON-compatible and package it for the durable envelope. */
|
|
41
|
+
export declare function boundedLoopSnapshot(name: string, revision: string, snapshot: JsonValue): StoredAgentRunState["loopState"];
|
|
17
42
|
export declare function agentFingerprint(agent: Agent, revision: string): string;
|
|
18
43
|
export declare function agentId(agent: Agent): string;
|
|
19
44
|
export declare function validateRunStateOptions(options: AgentRunStateOptions): void;
|
|
@@ -48,6 +73,7 @@ export declare function initialAgentRunState(input: {
|
|
|
48
73
|
readonly interruption?: AgentRunInterruption;
|
|
49
74
|
readonly messages?: readonly Message[];
|
|
50
75
|
readonly pending?: StoredAgentRunState["pending"];
|
|
76
|
+
readonly pendingCalls?: StoredAgentRunState["pendingCalls"];
|
|
51
77
|
readonly interruptBeforeTool?: boolean;
|
|
52
78
|
}): StoredAgentRunState;
|
|
53
79
|
export declare function parseAgentRunState(value: unknown, version?: number): StoredAgentRunState;
|
package/dist/agent-run-state.js
CHANGED
|
@@ -1,11 +1,50 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { AgentRunStateError } from "./contracts.js";
|
|
2
|
+
import { AgentLoopStateError, AgentRunStateError } from "./contracts.js";
|
|
3
3
|
export const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
|
|
4
4
|
export const AGENT_RUN_STATE_SCHEMA_VERSION = 1;
|
|
5
5
|
export const DEFAULT_MAX_AGENT_RUN_STATE_BYTES = 256 * 1024;
|
|
6
6
|
export const HARD_MAX_AGENT_RUN_STATE_BYTES = 1024 * 1024;
|
|
7
7
|
const MAX_DEPTH = 32;
|
|
8
8
|
const MAX_PROPERTIES = 256;
|
|
9
|
+
/** Revision stamps of the built-in loops; custom strategies declare their own `revision`. */
|
|
10
|
+
export const BUILT_IN_LOOP_REVISIONS = {
|
|
11
|
+
"single-shot": "1",
|
|
12
|
+
"generate-validate-revise": "1",
|
|
13
|
+
};
|
|
14
|
+
/** Validate a strategy snapshot as JSON-compatible and package it for the durable envelope. */
|
|
15
|
+
export function boundedLoopSnapshot(name, revision, snapshot) {
|
|
16
|
+
try {
|
|
17
|
+
assertJsonValue(snapshot, 0);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
if (error instanceof AgentLoopStateError)
|
|
21
|
+
throw error;
|
|
22
|
+
throw new AgentLoopStateError("ERR_PRISM_LOOP_SNAPSHOT", "Loop snapshot must be JSON-compatible", { cause: error });
|
|
23
|
+
}
|
|
24
|
+
return { name, revision, snapshot };
|
|
25
|
+
}
|
|
26
|
+
function assertJsonValue(value, depth) {
|
|
27
|
+
if (depth > MAX_DEPTH)
|
|
28
|
+
throw new AgentLoopStateError("ERR_PRISM_LOOP_SNAPSHOT", `Loop snapshot exceeds depth ${MAX_DEPTH}`);
|
|
29
|
+
switch (typeof value) {
|
|
30
|
+
case "string":
|
|
31
|
+
case "boolean":
|
|
32
|
+
return;
|
|
33
|
+
case "number":
|
|
34
|
+
if (!Number.isFinite(value))
|
|
35
|
+
throw new AgentLoopStateError("ERR_PRISM_LOOP_SNAPSHOT", "Loop snapshot numbers must be finite");
|
|
36
|
+
return;
|
|
37
|
+
case "object": {
|
|
38
|
+
if (value === null)
|
|
39
|
+
return;
|
|
40
|
+
for (const item of Object.values(value))
|
|
41
|
+
assertJsonValue(item, depth + 1);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
default:
|
|
45
|
+
throw new AgentLoopStateError("ERR_PRISM_LOOP_SNAPSHOT", "Loop snapshot must be JSON-compatible");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
9
48
|
export function agentFingerprint(agent, revision) {
|
|
10
49
|
const config = agent.config;
|
|
11
50
|
const tools = !config.tools ? [] : "list" in config.tools ? config.tools.list() : config.tools;
|
|
@@ -28,11 +67,17 @@ export function agentFingerprint(agent, revision) {
|
|
|
28
67
|
instructions: config.instructions ?? null,
|
|
29
68
|
systemPrompt,
|
|
30
69
|
skills: skills.map((skill) => ({ name: skill.name, instructions: skill.instructions, toolNames: skill.toolNames })),
|
|
31
|
-
tools: tools.map((tool) => ({
|
|
70
|
+
tools: tools.map((tool) => ({
|
|
71
|
+
name: tool.name,
|
|
72
|
+
parameters: tool.parameters,
|
|
73
|
+
exclusive: tool.exclusive,
|
|
74
|
+
effect: typeof tool.effect === "function" ? "classifier" : tool.effect,
|
|
75
|
+
})),
|
|
32
76
|
guardrails: guardrails.map((guardrail) => ({ name: guardrail.name, stage: guardrail.stage, revision: guardrail.revision })),
|
|
77
|
+
// Loop revision participates so a loop change without a definitionRevision bump fails closed.
|
|
33
78
|
loop: typeof config.loop === "object" && config.loop && "strategy" in config.loop
|
|
34
|
-
? config.loop.strategy
|
|
35
|
-
:
|
|
79
|
+
? { name: config.loop.strategy, revision: BUILT_IN_LOOP_REVISIONS[config.loop.strategy] ?? null }
|
|
80
|
+
: { name: config.loop?.name ?? "single-shot", revision: config.loop?.revision ?? BUILT_IN_LOOP_REVISIONS["single-shot"] },
|
|
36
81
|
});
|
|
37
82
|
return createHash("sha256").update(value).digest("hex");
|
|
38
83
|
}
|
|
@@ -80,7 +125,7 @@ export function statusFromState(state, version) {
|
|
|
80
125
|
return { state: publicState({ ...state, version }), version };
|
|
81
126
|
}
|
|
82
127
|
export function publicState(state) {
|
|
83
|
-
const { input: _input, pending: _pending, interruptBeforeTool: _interruptBeforeTool, counters: _counters, deadlineAt: _deadlineAt, ...publicValue } = state;
|
|
128
|
+
const { input: _input, pending: _pending, pendingCalls: _pendingCalls, nestedRuns: _nestedRuns, interruptBeforeTool: _interruptBeforeTool, counters: _counters, deadlineAt: _deadlineAt, ...publicValue } = state;
|
|
84
129
|
return publicValue;
|
|
85
130
|
}
|
|
86
131
|
export function initialAgentRunState(input) {
|
|
@@ -98,6 +143,7 @@ export function initialAgentRunState(input) {
|
|
|
98
143
|
interruption: input.interruption,
|
|
99
144
|
input: input.messages,
|
|
100
145
|
pending: input.pending,
|
|
146
|
+
pendingCalls: input.pendingCalls,
|
|
101
147
|
interruptBeforeTool: input.interruptBeforeTool,
|
|
102
148
|
counters: input.counters,
|
|
103
149
|
deadlineAt: input.deadlineAt,
|
|
@@ -120,6 +166,41 @@ export function parseAgentRunState(value, version) {
|
|
|
120
166
|
!state.deadlineAt) {
|
|
121
167
|
throw new AgentRunStateError("Malformed agent run state");
|
|
122
168
|
}
|
|
169
|
+
if (state.pendingCalls !== undefined &&
|
|
170
|
+
(!Array.isArray(state.pendingCalls) ||
|
|
171
|
+
state.pendingCalls.some((entry) => !entry ||
|
|
172
|
+
typeof entry !== "object" ||
|
|
173
|
+
!entry.call ||
|
|
174
|
+
typeof entry.approvalId !== "string" ||
|
|
175
|
+
(entry.status !== "ready" && entry.status !== "dispatched")))) {
|
|
176
|
+
throw new AgentRunStateError("Malformed agent run pending calls");
|
|
177
|
+
}
|
|
178
|
+
if (state.stickyDecisions !== undefined &&
|
|
179
|
+
(!Array.isArray(state.stickyDecisions) ||
|
|
180
|
+
state.stickyDecisions.some((entry) => !entry ||
|
|
181
|
+
typeof entry !== "object" ||
|
|
182
|
+
!entry.scope ||
|
|
183
|
+
(entry.outcome !== "allow_for_run" && entry.outcome !== "reject_for_run")))) {
|
|
184
|
+
throw new AgentRunStateError("Malformed agent run sticky decisions");
|
|
185
|
+
}
|
|
186
|
+
if (state.nestedRuns !== undefined &&
|
|
187
|
+
(!Array.isArray(state.nestedRuns) ||
|
|
188
|
+
state.nestedRuns.some((entry) => !entry ||
|
|
189
|
+
typeof entry !== "object" ||
|
|
190
|
+
typeof entry.runId !== "string" ||
|
|
191
|
+
typeof entry.toolCallId !== "string" ||
|
|
192
|
+
!Array.isArray(entry.path) ||
|
|
193
|
+
!Array.isArray(entry.approvals) ||
|
|
194
|
+
entry.approvals.some((approval) => typeof approval?.id !== "string" || typeof approval?.childApprovalId !== "string")))) {
|
|
195
|
+
throw new AgentRunStateError("Malformed agent run nested runs");
|
|
196
|
+
}
|
|
197
|
+
if (state.loopState !== undefined &&
|
|
198
|
+
(typeof state.loopState !== "object" ||
|
|
199
|
+
typeof state.loopState.name !== "string" ||
|
|
200
|
+
typeof state.loopState.revision !== "string" ||
|
|
201
|
+
!("snapshot" in state.loopState))) {
|
|
202
|
+
throw new AgentRunStateError("Malformed agent run loop state");
|
|
203
|
+
}
|
|
123
204
|
// Load bounds against the hard cap, not the default: the configured maxStateBytes is a
|
|
124
205
|
// save-side policy knob, while the load-side bound is only a DoS ceiling. States saved
|
|
125
206
|
// with a raised maxStateBytes must remain resumable.
|