@arnilo/prism 0.3.2 → 0.5.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 +50 -1
- package/README.md +42 -62
- package/dist/agent-run-lifecycle.js +4 -0
- package/dist/agent-run-state.d.ts +5 -2
- package/dist/agent-run-state.js +18 -8
- package/dist/agent-session/session/assemble.d.ts +6 -0
- package/dist/agent-session/session/assemble.js +391 -0
- package/dist/agent-session/session/persist.d.ts +28 -0
- package/dist/agent-session/session/persist.js +166 -0
- package/dist/agent-session/session/provider-round.d.ts +6 -0
- package/dist/agent-session/session/provider-round.js +231 -0
- package/dist/agent-session/session/tool-round.d.ts +31 -0
- package/dist/agent-session/session/tool-round.js +473 -0
- package/dist/agent-session/session/types.d.ts +115 -0
- package/dist/agent-session/session/types.js +5 -0
- package/dist/agent-session/session.d.ts +54 -41
- package/dist/agent-session/session.js +23 -1132
- package/dist/capture.d.ts +63 -0
- package/dist/capture.js +67 -0
- package/dist/cli-dev.d.ts +29 -0
- package/dist/cli-dev.js +52 -0
- package/dist/cli-init.d.ts +34 -3
- package/dist/cli-init.js +192 -24
- package/dist/cli-runner.d.ts +6 -2
- package/dist/cli-runner.js +57 -10
- package/dist/content.d.ts +3 -3
- package/dist/content.js +3 -1
- package/dist/contracts-core/agent.d.ts +8 -0
- package/dist/contracts-core/batch.d.ts +97 -0
- package/dist/contracts-core/batch.js +65 -0
- package/dist/contracts-core/content.d.ts +72 -1
- package/dist/contracts-core/embeddings.d.ts +30 -0
- package/dist/contracts-core/embeddings.js +17 -0
- package/dist/contracts-core/images.d.ts +60 -0
- package/dist/contracts-core/images.js +17 -0
- package/dist/contracts-core/moderation.d.ts +46 -0
- package/dist/contracts-core/moderation.js +34 -0
- package/dist/contracts-core/speech.d.ts +39 -0
- package/dist/contracts-core/speech.js +17 -0
- package/dist/contracts-core/transcription.d.ts +48 -0
- package/dist/contracts-core/transcription.js +17 -0
- package/dist/contracts-core/video.d.ts +61 -0
- package/dist/contracts-core/video.js +17 -0
- package/dist/contracts-core.d.ts +7 -0
- package/dist/contracts-core.js +7 -0
- package/dist/contracts-protocol.d.ts +18 -0
- package/dist/contracts-run-state.d.ts +1 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -3
- package/dist/input.d.ts +8 -0
- package/dist/input.js +4 -0
- package/dist/node/agent-definitions.d.ts +1 -8
- package/dist/node/agent-definitions.js +0 -34
- package/dist/node/settings.d.ts +0 -1
- package/dist/node/settings.js +0 -5
- package/dist/pinned-fetch.js +29 -3
- package/dist/provider-events.js +3 -4
- package/dist/providers/media.d.ts +1 -2
- package/dist/providers/media.js +1 -4
- package/dist/rpc.d.ts +1 -1
- package/dist/rpc.js +4 -4
- package/dist/testing/persistence-schema.d.ts +1 -1
- package/dist/testing/persistence-schema.js +32 -28
- package/dist/testing/provider-conformance.d.ts +114 -5
- package/dist/testing/provider-conformance.js +342 -0
- package/dist/testing/tool-conformance.d.ts +25 -0
- package/dist/testing/tool-conformance.js +128 -1
- package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
- package/dist/testing/tool-effect-store-conformance.js +0 -3
- package/dist/thinking.d.ts +48 -9
- package/dist/thinking.js +134 -8
- package/dist/tool-search.d.ts +76 -0
- package/dist/tool-search.js +199 -0
- package/docs/0.1.0-readiness.md +3 -3
- package/docs/a2a.md +2 -2
- package/docs/acp-agent.md +1 -1
- package/docs/acp.md +3 -3
- package/docs/ag-ui-adoption.md +1 -1
- package/docs/ag-ui.md +1 -2
- package/docs/agent-definitions.md +1 -1
- package/docs/agent-events.md +5 -5
- package/docs/agent-identity.md +13 -2
- package/docs/audit-export.md +3 -3
- package/docs/batch-jobs.md +120 -0
- package/docs/browser-automation.md +5 -5
- package/docs/caveman.md +2 -2
- package/docs/cli-rpc.md +43 -9
- package/docs/coding-agent-tools.md +19 -19
- package/docs/coding-review-and-diagnostics.md +2 -2
- package/docs/coding-security.md +5 -5
- package/docs/coding-tools.md +82 -0
- package/docs/coding-workspaces.md +2 -2
- package/docs/compaction-and-retry.md +2 -2
- package/docs/compaction-llm.md +4 -4
- package/docs/compaction-observational-memory.md +3 -3
- package/docs/computer-use-linux.md +13 -2
- package/docs/context-and-skills.md +3 -1
- package/docs/conversations.md +4 -4
- package/docs/core.md +85 -0
- package/docs/credential-storage.md +12 -8
- package/docs/credentials-and-redaction.md +1 -1
- package/docs/data-classification.md +1 -1
- package/docs/database-persistence.md +7 -3
- package/docs/dev-inspector.md +103 -0
- package/docs/device-adapters.md +2 -2
- package/docs/diagrams.md +247 -0
- package/docs/document-reader.md +6 -6
- package/docs/documents.md +214 -0
- package/docs/embeddings.md +112 -0
- package/docs/enterprise-postgres-state.md +7 -7
- package/docs/evaluations.md +41 -7
- package/docs/extensions.md +3 -3
- package/docs/forge-integration.md +3 -3
- package/docs/graft.md +5 -5
- package/docs/guardrails.md +2 -2
- package/docs/host-security.md +16 -15
- package/docs/image-generation.md +129 -0
- package/docs/impeccable.md +7 -5
- package/docs/index.md +84 -46
- package/docs/indexed-code-search.md +2 -2
- package/docs/language-intelligence.md +4 -4
- package/docs/live-testing.md +126 -0
- package/docs/mcp-tools.md +44 -13
- package/docs/middleware-hooks.md +1 -1
- package/docs/migrate-to-0.4.md +312 -0
- package/docs/migrate-to-0.5.md +122 -0
- package/docs/migration.md +51 -1
- package/docs/model-registry.md +38 -0
- package/docs/model-routing.md +6 -6
- package/docs/moderation.md +117 -0
- package/docs/multi-agent-patterns.md +177 -0
- package/docs/multimodal-content.md +27 -3
- package/docs/obscura.md +12 -12
- package/docs/observability.md +32 -7
- package/docs/openapi-tools.md +14 -4
- package/docs/operations.md +11 -0
- package/docs/performance.md +30 -10
- package/docs/persistence-credentials-multimodality-primitives.md +7 -7
- package/docs/policy-and-audit.md +18 -8
- package/docs/ponytail.md +3 -3
- package/docs/postgres-persistence.md +5 -5
- package/docs/process-sessions.md +2 -2
- package/docs/prompt-registry.md +106 -0
- package/docs/provider-caching.md +36 -32
- package/docs/provider-conformance.md +24 -2
- package/docs/provider-packages.md +58 -22
- package/docs/provider-primitives.md +5 -5
- package/docs/provider-request-policies.md +1 -1
- package/docs/providers/ai-sdk.md +18 -6
- package/docs/providers/alibaba.md +10 -6
- package/docs/providers/anthropic.md +10 -6
- package/docs/providers/azure.md +20 -4
- package/docs/providers/bedrock.md +18 -3
- package/docs/providers/clinepass.md +7 -3
- package/docs/providers/commandcode.md +253 -0
- package/docs/providers/deepseek.md +7 -3
- package/docs/providers/google.md +8 -4
- package/docs/providers/hyper.md +284 -0
- package/docs/providers/kimi.md +7 -3
- package/docs/providers/neuralwatt.md +12 -8
- package/docs/providers/ollama.md +18 -3
- package/docs/providers/openai-compatible.md +5 -1
- package/docs/providers/openai.md +9 -5
- package/docs/providers/opencode-go.md +8 -4
- package/docs/providers/openrouter.md +8 -4
- package/docs/providers/vertex.md +21 -5
- package/docs/providers/xai.md +7 -3
- package/docs/providers/zai.md +7 -3
- package/docs/rag.md +31 -9
- package/docs/release-and-install.md +181 -76
- package/docs/resource-loading.md +1 -1
- package/docs/runs-and-usage.md +28 -3
- package/docs/server.md +94 -5
- package/docs/settings-auth-trust-security.md +7 -5
- package/docs/sheets.md +229 -0
- package/docs/speech.md +126 -0
- package/docs/sqlite-persistence.md +4 -4
- package/docs/supervisors.md +4 -3
- package/docs/thinking-and-reasoning.md +93 -60
- package/docs/tool-conformance.md +28 -3
- package/docs/tool-execution-primitives.md +8 -8
- package/docs/tools.md +32 -5
- package/docs/web-tools.md +3 -3
- package/docs/wiki.md +7 -7
- package/docs/work-artifacts-and-review.md +17 -6
- package/docs/work-connectors.md +4 -4
- package/docs/work-tools.md +5 -5
- package/docs/workflow-orchestration-primitives.md +35 -11
- package/docs/workflows.md +74 -13
- package/docs/working-and-semantic-memory.md +53 -5
- package/package.json +14 -31
- package/templates/README.md +23 -0
- package/templates/deep-research/README.md.tmpl +47 -0
- package/templates/deep-research/env.example.tmpl +12 -0
- package/templates/deep-research/gitignore.tmpl +7 -0
- package/templates/deep-research/manifest.json +12 -0
- package/templates/deep-research/package.json.tmpl +23 -0
- package/templates/deep-research/src/agent.ts.tmpl +81 -0
- package/templates/deep-research/src/index.ts.tmpl +53 -0
- package/templates/deep-research/src/tests/research.test.ts.tmpl +114 -0
- package/templates/deep-research/src/tools.ts.tmpl +86 -0
- package/templates/deep-research/src/types.ts.tmpl +45 -0
- package/templates/deep-research/src/workflow.ts.tmpl +156 -0
- package/templates/deep-research/tsconfig.json.tmpl +15 -0
- package/templates/init/manifest.json +5 -0
- package/templates/init/package.json.tmpl +2 -1
- package/templates/init/providers.json +40 -24
- package/docs/antigravity-agent.md +0 -207
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
export const SEARCH_TOOLS_TOOL_NAME = "search_tools";
|
|
2
|
+
export const DEFAULT_TOOLS_SEARCH_TOP_K = 16;
|
|
3
|
+
export const HARD_MAX_TOOLS_SEARCH_TOP_K = 64;
|
|
4
|
+
/** Frozen index cap; larger registries fail closed to full disclosure. */
|
|
5
|
+
export const HARD_MAX_TOOLS_INDEX = 1024;
|
|
6
|
+
export const DEFAULT_MAX_TOOLS_SEARCH_QUERY_BYTES = 4_096;
|
|
7
|
+
export const HARD_MAX_TOOLS_SEARCH_QUERY_BYTES = 65_536;
|
|
8
|
+
export const TOOL_DISCLOSURE_ERROR_CODE = "tool_disclosure_exceeded";
|
|
9
|
+
export class ToolDisclosureError extends Error {
|
|
10
|
+
code = TOOL_DISCLOSURE_ERROR_CODE;
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "ToolDisclosureError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function isToolDisclosureError(error) {
|
|
17
|
+
return error instanceof Error && error.code === TOOL_DISCLOSURE_ERROR_CODE;
|
|
18
|
+
}
|
|
19
|
+
/** Run options win over agent config (mirrors resolveSkillsDisclosure); default "all". */
|
|
20
|
+
export function resolveToolsDisclosure(run, agent) {
|
|
21
|
+
return run ?? agent ?? "all";
|
|
22
|
+
}
|
|
23
|
+
export function createActiveToolSet() {
|
|
24
|
+
const names = new Set();
|
|
25
|
+
return {
|
|
26
|
+
has(name) {
|
|
27
|
+
return names.has(name);
|
|
28
|
+
},
|
|
29
|
+
add(name) {
|
|
30
|
+
names.add(name);
|
|
31
|
+
},
|
|
32
|
+
list() {
|
|
33
|
+
return [...names];
|
|
34
|
+
},
|
|
35
|
+
clear() {
|
|
36
|
+
names.clear();
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Fixed tokenization: lowercase alphanumeric runs. No user-controlled regex anywhere. */
|
|
41
|
+
const WORD_SPLIT = /[^a-z0-9]+/;
|
|
42
|
+
function tokenize(text) {
|
|
43
|
+
return text
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.split(WORD_SPLIT)
|
|
46
|
+
.filter((token) => token.length > 0);
|
|
47
|
+
}
|
|
48
|
+
/** ponytail: O(n·d) lexical index, rebuilt on registry change by the caller — embedder-backed
|
|
49
|
+
* scoring via the @arnilo/prism-memory/rag seam if accuracy fixtures fall short (plan 041). */
|
|
50
|
+
export function createToolSearchIndex(tools, skip) {
|
|
51
|
+
if (tools.length > HARD_MAX_TOOLS_INDEX) {
|
|
52
|
+
throw new ToolDisclosureError(`Tool index exceeds hard cap (${HARD_MAX_TOOLS_INDEX} tools)`);
|
|
53
|
+
}
|
|
54
|
+
const postings = new Map();
|
|
55
|
+
for (const [toolIndex, tool] of tools.entries()) {
|
|
56
|
+
if (skip?.(tool))
|
|
57
|
+
continue;
|
|
58
|
+
// Name tokens weigh ×3 so an exact name match outranks description-only matches.
|
|
59
|
+
const seen = new Map();
|
|
60
|
+
for (const term of [...tokenize(tool.name), ...tokenize(tool.name), ...tokenize(tool.name), ...tokenize(tool.description ?? "")]) {
|
|
61
|
+
seen.set(term, (seen.get(term) ?? 0) + 1);
|
|
62
|
+
}
|
|
63
|
+
for (const [term, tf] of seen) {
|
|
64
|
+
const list = postings.get(term) ?? [];
|
|
65
|
+
list.push({ toolIndex, tf });
|
|
66
|
+
postings.set(term, list);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { tools, postings };
|
|
70
|
+
}
|
|
71
|
+
/** Bounded lexical scoring: BM25-lite (tf × IDF, registry-derived DF). Ties keep registry order. */
|
|
72
|
+
export function scoreTools(index, query, k, queryByteCap = DEFAULT_MAX_TOOLS_SEARCH_QUERY_BYTES) {
|
|
73
|
+
const bounded = query.slice(0, Math.trunc(queryByteCap));
|
|
74
|
+
const terms = tokenize(bounded);
|
|
75
|
+
if (terms.length === 0 || k <= 0 || index.tools.length === 0)
|
|
76
|
+
return [];
|
|
77
|
+
const limit = Math.min(Math.trunc(k), index.tools.length);
|
|
78
|
+
const total = index.tools.filter((tool) => tool.name !== SEARCH_TOOLS_TOOL_NAME).length;
|
|
79
|
+
const scores = new Array(index.tools.length).fill(0);
|
|
80
|
+
const matchedTerms = new Map();
|
|
81
|
+
for (const term of new Set(terms)) {
|
|
82
|
+
const list = index.postings.get(term);
|
|
83
|
+
if (!list)
|
|
84
|
+
continue;
|
|
85
|
+
const idf = 1 + Math.log(total / list.length);
|
|
86
|
+
for (const entry of list) {
|
|
87
|
+
scores[entry.toolIndex] += entry.tf * idf;
|
|
88
|
+
const names = matchedTerms.get(entry.toolIndex) ?? [];
|
|
89
|
+
if (names.length < 8)
|
|
90
|
+
names.push(term);
|
|
91
|
+
matchedTerms.set(entry.toolIndex, names);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const ranked = scores
|
|
95
|
+
.map((score, toolIndex) => ({ score, toolIndex }))
|
|
96
|
+
.filter((entry) => entry.score > 0)
|
|
97
|
+
.sort((a, b) => b.score - a.score || a.toolIndex - b.toolIndex)
|
|
98
|
+
.slice(0, limit);
|
|
99
|
+
return ranked.map(({ toolIndex }) => ({
|
|
100
|
+
name: index.tools[toolIndex].name,
|
|
101
|
+
description: index.tools[toolIndex].description,
|
|
102
|
+
matched: matchedTerms.get(toolIndex) ?? [],
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
export function createToolSearchState(options) {
|
|
106
|
+
const requested = options.search?.topK;
|
|
107
|
+
const topK = requested === undefined ? DEFAULT_TOOLS_SEARCH_TOP_K : Math.min(Math.max(1, Math.trunc(requested)), HARD_MAX_TOOLS_SEARCH_TOP_K);
|
|
108
|
+
// ponytail: index built once per run; mid-run registry mutation is not observed — rebuild by starting a new run.
|
|
109
|
+
const index = createToolSearchIndex(options.tools, (tool) => tool.name === SEARCH_TOOLS_TOOL_NAME);
|
|
110
|
+
return { index, activated: options.activated, topK };
|
|
111
|
+
}
|
|
112
|
+
/** Provider-facing narrowing: activated tools ∪ top-k for the turn. Fails closed — any
|
|
113
|
+
* scoring/index error discloses the full input list (never zero, never wider than input). */
|
|
114
|
+
export function selectDisclosedTools(options) {
|
|
115
|
+
if (options.tools.length === 0)
|
|
116
|
+
return options.tools;
|
|
117
|
+
try {
|
|
118
|
+
const keep = new Set();
|
|
119
|
+
for (const tool of options.tools)
|
|
120
|
+
if (tool.name === SEARCH_TOOLS_TOOL_NAME || options.activated?.has(tool.name) === true)
|
|
121
|
+
keep.add(tool.name);
|
|
122
|
+
const index = createToolSearchIndex(options.tools, (tool) => tool.name === SEARCH_TOOLS_TOOL_NAME);
|
|
123
|
+
const topK = Math.min(Math.max(1, Math.trunc(options.search?.topK ?? DEFAULT_TOOLS_SEARCH_TOP_K)), HARD_MAX_TOOLS_SEARCH_TOP_K);
|
|
124
|
+
for (const match of scoreTools(index, inputText(options.input), topK, options.queryByteCap ?? DEFAULT_MAX_TOOLS_SEARCH_QUERY_BYTES))
|
|
125
|
+
keep.add(match.name);
|
|
126
|
+
const disclosed = options.tools.filter((tool) => keep.has(tool.name));
|
|
127
|
+
if (disclosed.length > 0)
|
|
128
|
+
return disclosed;
|
|
129
|
+
// Nothing scored or activated and no always-on tools in the list: bounded deterministic
|
|
130
|
+
// prefix instead of zero tools (the wired session always keeps `search_tools`, so the
|
|
131
|
+
// un-wired caller is the only one that reaches this).
|
|
132
|
+
return options.tools.slice(0, Math.min(Math.max(1, Math.trunc(options.search?.topK ?? DEFAULT_TOOLS_SEARCH_TOP_K)) + 1, options.tools.length));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return options.tools;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function inputText(input) {
|
|
139
|
+
if (typeof input === "string")
|
|
140
|
+
return input;
|
|
141
|
+
const messages = input === null || input === undefined || Array.isArray(input) === false ? [input] : input;
|
|
142
|
+
let text = "";
|
|
143
|
+
for (const message of messages) {
|
|
144
|
+
for (const block of message.content) {
|
|
145
|
+
if (block.type === "text") {
|
|
146
|
+
text += `${block.text}\n`;
|
|
147
|
+
if (text.length > HARD_MAX_TOOLS_SEARCH_QUERY_BYTES)
|
|
148
|
+
return text;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return text;
|
|
153
|
+
}
|
|
154
|
+
/** Model-facing activation tool, generated only in search mode. Results are inert
|
|
155
|
+
* name+description lines — no schemas or bodies — and activation re-checks allow/deny
|
|
156
|
+
* at dispatch time (blocked-reason matrix unchanged). */
|
|
157
|
+
export function createSearchToolsTool(state, queryByteCap = DEFAULT_MAX_TOOLS_SEARCH_QUERY_BYTES) {
|
|
158
|
+
return {
|
|
159
|
+
name: SEARCH_TOOLS_TOOL_NAME,
|
|
160
|
+
description: `Search available tools by relevance. Returns up to k tool names with short descriptions and marks them active so their full definitions appear on the next turn. Try queries made of tool keywords.`,
|
|
161
|
+
parameters: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: { query: { type: "string" }, k: { type: "integer", minimum: 1 } },
|
|
164
|
+
required: ["query"],
|
|
165
|
+
},
|
|
166
|
+
execute(args, context) {
|
|
167
|
+
const fail = (message) => ({
|
|
168
|
+
toolCallId: context.toolCallId,
|
|
169
|
+
name: SEARCH_TOOLS_TOOL_NAME,
|
|
170
|
+
error: { code: "ERR_PRISM_TOOL_SEARCH_INVALID", message },
|
|
171
|
+
});
|
|
172
|
+
if (typeof args.query !== "string" || args.query.trim().length === 0)
|
|
173
|
+
return fail("query must be a non-empty string");
|
|
174
|
+
if (Buffer.byteLength(args.query, "utf8") > HARD_MAX_TOOLS_SEARCH_QUERY_BYTES)
|
|
175
|
+
return fail(`query exceeds ${HARD_MAX_TOOLS_SEARCH_QUERY_BYTES} bytes`);
|
|
176
|
+
const k = args.k === undefined ? state.topK : typeof args.k === "number" && Number.isInteger(args.k) ? args.k : NaN;
|
|
177
|
+
if (Number.isNaN(k) || k < 1)
|
|
178
|
+
return fail("k must be an integer >= 1");
|
|
179
|
+
const matches = scoreTools(state.index, args.query, k, queryByteCap);
|
|
180
|
+
for (const match of matches)
|
|
181
|
+
state.activated.add(match.name);
|
|
182
|
+
return {
|
|
183
|
+
toolCallId: context.toolCallId,
|
|
184
|
+
name: SEARCH_TOOLS_TOOL_NAME,
|
|
185
|
+
content: [
|
|
186
|
+
{
|
|
187
|
+
type: "text",
|
|
188
|
+
text: matches.length === 0
|
|
189
|
+
? `No tools matched ${JSON.stringify(args.query.slice(0, 64))}. Try different keywords.`
|
|
190
|
+
: matches
|
|
191
|
+
.map((match) => `- ${match.name}${match.description ? `: ${match.description.slice(0, 512)}` : ""} [matched: ${match.matched.join(", ")}]`)
|
|
192
|
+
.join("\n"),
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=tool-search.js.map
|
package/docs/0.1.0-readiness.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0.1.0 / 1.0 Readiness Gates
|
|
2
2
|
|
|
3
|
-
Status: **0.
|
|
3
|
+
Status: **0.5.0** is the current release line (dead-export removal cut: 10 active packages, family subpaths, `^0.5.0` peers); **0.3.3** was the terminal 0.3.x cut; **0.1.7** was the terminal 0.1.x baseline; **1.0** readiness remains operator-gated, not automatic.
|
|
4
4
|
|
|
5
5
|
This page distills runnable readiness gates into one command-per-gate table.
|
|
6
6
|
The **Last evidence** column records the 0.1.0-tree snapshot (plan 012 Tasks
|
|
@@ -20,7 +20,7 @@ Historical release lines (0.0.16 floor → 0.0.27 Phase 10 ACP interop → 0.1.0
|
|
|
20
20
|
keep their per-phase evidence in the pages above; this page records the 0.2.6
|
|
21
21
|
snapshot (plan 026) with the 0.1.x tables below as the historical record.
|
|
22
22
|
|
|
23
|
-
## Current line (0.
|
|
23
|
+
## Current line (0.5.0)
|
|
24
24
|
|
|
25
25
|
| Item | Status |
|
|
26
26
|
|---|---|
|
|
@@ -66,7 +66,7 @@ snapshot (plan 026) with the 0.1.x tables below as the historical record.
|
|
|
66
66
|
| Publish order + tarball validation | `node scripts/release.mjs publish --version 0.1.0 --dry-run --allow-dirty --allow-untagged` | 49/49 packages `dry-run` twice with byte-identical reports, deterministic dependency order, no failures (Task 7) | Operator (dry-run), CI |
|
|
67
67
|
| Node 20 compatibility | CI `node20-compat` (build + public-import smoke) | all 21 root exports import cleanly on Node 20.20.2 | CI |
|
|
68
68
|
| PostgreSQL suite | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres` | 0.1.0: Phase 7 conformance + Phase 12 restart-recovery + 74 workspace checks green against PostgreSQL 16 (Task 4 recording, re-run green at 0.1.0 on 2026-08-09); operator-gated | Operator |
|
|
69
|
-
| Keychain suite | `PRISM_TEST_KEYCHAIN=1 npm test --workspace @arnilo/prism-credentials
|
|
69
|
+
| Keychain suite | `PRISM_TEST_KEYCHAIN=1 npm test --workspace @arnilo/prism-core/credentials/node` (protected) | 28/28 green incl. native keychain round-trip against the OS secret-service backend (gnome-keyring, 2026-08-09) | Operator host |
|
|
70
70
|
| Live-provider suites | `npm run test:live` (protected) | **operator-gated** (requires credentials; `live-canaries.yml` blocked gate, `canary-report.json` retained) | Operator |
|
|
71
71
|
| SAST | GitHub CodeQL | **operator-gated** (runs in CI workflow) | CI |
|
|
72
72
|
| Signed, provenance publication | `npm run release:publish` (clean tagged tree, OIDC) | **operator-gated** (see "Remaining for 1.0") | Operator |
|
package/docs/a2a.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-supervisor` implements bounded A2A 1.0 over the JSON-RPC/HTTPS binding. Supported operations: `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask`, push-notification-config create/get/list/delete, and `GetExtendedAgentCard`. `client.streamMessage()` additionally exposes verified rich task/message events for frontend adapters while legacy `stream()` remains text-compatible. Agent Cards retain explicit ES256 verification. gRPC, HTTP+JSON, discovery registries, automatic JWK/OAuth fetching, and an internal task worker/store are absent.
|
|
5
|
+
`@arnilo/prism-core/runtime/supervisor` implements bounded A2A 1.0 over the JSON-RPC/HTTPS binding. Supported operations: `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask`, push-notification-config create/get/list/delete, and `GetExtendedAgentCard`. `client.streamMessage()` additionally exposes verified rich task/message events for frontend adapters while legacy `stream()` remains text-compatible. Agent Cards retain explicit ES256 verification. gRPC, HTTP+JSON, discovery registries, automatic JWK/OAuth fetching, and an internal task worker/store are absent.
|
|
6
6
|
|
|
7
7
|
## When to use it
|
|
8
8
|
|
|
@@ -41,7 +41,7 @@ Parts, messages, artifacts, histories, metadata, and aggregate responses are unt
|
|
|
41
41
|
|
|
42
42
|
## AG-UI server-side exposure (Task 13, 0.0.26)
|
|
43
43
|
|
|
44
|
-
`createAgUiA2AServer()` in `@arnilo/prism-ag-ui` fronts one host-selected **local AG-UI agent** as an A2A 1.0 server, the reverse direction of `createAgUiA2AAdapter()`: remote A2A clients start and stream local runs through the same AG-UI input allow-list and event mapper as the AG-UI SSE path (same projection, redaction, and byte caps). It reuses this package's `createA2AHandler` transport/lifecycle; it creates no second runtime, task store, or worker. Requires the optional `@arnilo/prism-supervisor` peer (imported lazily; plain `@arnilo/prism-ag-ui` imports keep working without it).
|
|
44
|
+
`createAgUiA2AServer()` in `@arnilo/prism-ag-ui` fronts one host-selected **local AG-UI agent** as an A2A 1.0 server, the reverse direction of `createAgUiA2AAdapter()`: remote A2A clients start and stream local runs through the same AG-UI input allow-list and event mapper as the AG-UI SSE path (same projection, redaction, and byte caps). It reuses this package's `createA2AHandler` transport/lifecycle; it creates no second runtime, task store, or worker. Requires the optional `@arnilo/prism-core/runtime/supervisor` peer (imported lazily; plain `@arnilo/prism-ag-ui` imports keep working without it).
|
|
45
45
|
|
|
46
46
|
```ts
|
|
47
47
|
import { createAgentEventSourceAgUiReplay, createAgUiA2AServer } from "@arnilo/prism-ag-ui";
|
package/docs/acp-agent.md
CHANGED
|
@@ -56,7 +56,7 @@ The binary is pure wiring (~200 lines) — no protocol code lives here. It build
|
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
58
|
import { createSpawnableAgent, loadConfig } from "@arnilo/prism-acp-agent";
|
|
59
|
-
import { createOpenAIResponsesProvider } from "@arnilo/prism-
|
|
59
|
+
import { createOpenAIResponsesProvider } from "@arnilo/prism-providers/openai";
|
|
60
60
|
|
|
61
61
|
const agent = createSpawnableAgent({
|
|
62
62
|
config: loadConfig("prism-acp-agent.json"),
|
package/docs/acp.md
CHANGED
|
@@ -112,7 +112,7 @@ const agent = createPrismAcpAgent({
|
|
|
112
112
|
|
|
113
113
|
- **Seam = capability.** Wiring `sessions.load` advertises `loadSession`; removing it withdraws the method. There is no separate capability flag to keep in sync — the freeze manifest's advertise-when matrix is enforced by construction and asserted by `scripts/phase10-conformance.test.mjs`.
|
|
114
114
|
- **Transcript replay (F2).** When `sessions.transcript` is wired, `session/load` and `session/resume` replay `user_message_chunk`/`agent_message_chunk` text chunks (from `SessionEntry`s with `kind: "message"` and a user/assistant role, text blocks only) before returning `sessionState`. Each chunk passes the shared redactor and is truncated at `maxTextBytes`; replay stops at `maxReplayEvents` chunks and counts against the stream event/byte caps (an oversized transcript fails the load/resume request closed). Absent seam = no replay, behavior unchanged.
|
|
115
|
-
- **Client fs/terminal are adapters, not a second implementation.** `AcpClientFilesystem` / `AcpClientTerminals` wrap the client's `fs/*` and `terminal/*` methods behind the Phase 9 `ProcessSession`-flavored interfaces; the agent pre-generates the session id so terminal requests can carry it. `createAcpFilesystemOperations` from `@arnilo/prism-coding-agent` maps that filesystem seam onto the coding tools' `read`/`write`/`edit` operations. This editor-buffer mode is intentionally hybrid: `repo_list`, `repo_search`, `glob`, `delete`, and `move` remain disk-backed unless the host supplies separate operations; binary/image/document handling never falls back to local disk. Host repo operations remain default when the client fs is absent.
|
|
115
|
+
- **Client fs/terminal are adapters, not a second implementation.** `AcpClientFilesystem` / `AcpClientTerminals` wrap the client's `fs/*` and `terminal/*` methods behind the Phase 9 `ProcessSession`-flavored interfaces; the agent pre-generates the session id so terminal requests can carry it. `createAcpFilesystemOperations` from `@arnilo/prism-coding-tools/agent` maps that filesystem seam onto the coding tools' `read`/`write`/`edit` operations. This editor-buffer mode is intentionally hybrid: `repo_list`, `repo_search`, `glob`, `delete`, and `move` remain disk-backed unless the host supplies separate operations; binary/image/document handling never falls back to local disk. Host repo operations remain default when the client fs is absent.
|
|
116
116
|
- **Spawnable ACP coding registry (Task 6).** `@arnilo/prism-acp-agent` wires `createAcpClientFilesystem` and creates a separate coding tool registry per ACP session when the client advertises `fs/read_text_file` or `fs/write_text_file`. That session's `read`/`write`/`edit` operations use editor buffers; without fs advertisement, the existing disk registry is used. `shell`, repository search/list/glob, `delete`, and `move` remain disk-backed in this hybrid mode. Durable approvals resolve the same per-session agent, so one session cannot resume through another session's buffer adapter.
|
|
117
117
|
- **Modes and config options are a pure host overlay.** The agent stores only a thin per-session registry; `apply`/`onChange` hooks narrow the host's own behavior. Mode switches can narrow or host-authorized widen — never a parallel policy evaluator, never a client-enabled tool.
|
|
118
118
|
- **Lifecycle wiring.** Pass your `createCodingLifecycleEmitter()` as `coding.lifecycle`; `file_changed` etc. then flow to streaming sessions. `configuration_changed` broadcasts `config_option_update` (agent-message fallback if the SDK rejects the kind).
|
|
@@ -152,14 +152,14 @@ const agent = createPrismAcpAgent({
|
|
|
152
152
|
- **Deny-closed by default.** Unknown mode ids, unadvertised methods, unprojected lifecycle events, oversize diffs/locations/media, thrown projection hooks, and failed elicitation all fail closed. Raw tool arguments/results are never sent unless a projection allow-list says otherwise.
|
|
153
153
|
- **Slash commands (F9).** `commands.list` is a host-owned slash-command list (not derived from the tool registry). The agent emits `available_commands_update` on session start (`session/new`, `session/load`, `session/resume`). Mid-session refresh is not in this release — re-list by starting a session. Names, descriptions, and input hints pass the shared redactor; the list is sliced at `acpCommandsPerUpdate`. Absent seam or a thrown list ⇒ no update.
|
|
154
154
|
- **Projected images (F8).** `AgUiProjection.toolResult` may return `{ type: "image", data, mimeType }` (return-type widening — existing string returns stay valid). The mapper emits `{ type: "content", content: { type: "image", data, mimeType } }` (SDK v1 `ToolCallContent` has no top-level image variant). `data` is the host-supplied base64; it is not redacted and not truncated — payloads over `acpImageBytes` are dropped. Default (no hook / non-image return) emits no image.
|
|
155
|
-
- **Coding-tool projection (F7).** `createCodingToolProjection({ maxDiffBytes? })` is an opt-in `AgUiProjection` for first-party `@arnilo/prism-coding-agent` results: `edit` → `toolDiff` (`path` + unified `patch` as `newText`) and `toolLocations` (`path` + `firstChangedLine`); `write` and `delete` → `toolLocations` (`path` only); `move` → destination `toolLocations` (`metadata.to`, with `from` fallback). No delete/move diff is fabricated. Pass as `projection: createCodingToolProjection()` on the agent/mapper. Mapper still redacts and enforces `acpDiffBytes` / `acpLocationsPerUpdate`; optional `maxDiffBytes` pre-truncates the patch so a slightly-oversize edit is shortened instead of dropped. Without the factory, behavior is unchanged (deny-by-default).
|
|
155
|
+
- **Coding-tool projection (F7).** `createCodingToolProjection({ maxDiffBytes? })` is an opt-in `AgUiProjection` for first-party `@arnilo/prism-coding-tools/agent` results: `edit` → `toolDiff` (`path` + unified `patch` as `newText`) and `toolLocations` (`path` + `firstChangedLine`); `write` and `delete` → `toolLocations` (`path` only); `move` → destination `toolLocations` (`metadata.to`, with `from` fallback). No delete/move diff is fabricated. Pass as `projection: createCodingToolProjection()` on the agent/mapper. Mapper still redacts and enforces `acpDiffBytes` / `acpLocationsPerUpdate`; optional `maxDiffBytes` pre-truncates the patch so a slightly-oversize edit is shortened instead of dropped. Without the factory, behavior is unchanged (deny-by-default).
|
|
156
156
|
- **No secrets.** Updates carry no raw file bodies, terminal output is capped by the Phase 9 chunk budget, and the shared redactor is applied before anything leaves the host. `permission_denied` never includes raw args.
|
|
157
157
|
- **Performance.** The adapter is O(1) per update with no unbounded buffering; p95 targets (fs round trip 250 ms, mode switch 250 ms, terminal chunk ack 1000 ms, prompt first update 2000 ms, prompt end 30 s) are recorded by `scripts/benchmark-0.0.27.mjs` and gated in `scripts/budgets.json` `phase10`.
|
|
158
158
|
|
|
159
159
|
## Related APIs
|
|
160
160
|
|
|
161
161
|
- [AG-UI](ag-ui.md): sibling frontend protocol; shared projection/redaction/caps and the same pending-decision model. This page is the full ACP reference.
|
|
162
|
-
- [Coding agent tools](coding-agent-tools.md): the `CodingLifecycleEvent` source mapped here; `@arnilo/prism-coding-agent` `process.outputChunkBytes` caps terminal chunks.
|
|
162
|
+
- [Coding agent tools](coding-agent-tools.md): the `CodingLifecycleEvent` source mapped here; `@arnilo/prism-coding-tools/agent` `process.outputChunkBytes` caps terminal chunks.
|
|
163
163
|
- [Agent events](agent-events.md): the durable `AgentEventSource`/replay story behind `session/load` and `session/resume`.
|
|
164
164
|
- [Host security guide](host-security.md): fail-closed checklist rows for ACP boundaries (authorize, ownership, redaction, untrusted MCP).
|
|
165
165
|
- [Migration guide](migration.md): 0.0.26 → 0.0.27 advertise/surface changes for hosts that parsed the old `initialize`.
|
package/docs/ag-ui-adoption.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
This page records Prism's compatibility review against official AG-UI `@ag-ui/core` **0.0.
|
|
5
|
+
This page records Prism's compatibility review against official AG-UI `@ag-ui/core` **0.0.59** and the official repository at commit [`a40b5c0`](https://github.com/ag-ui-protocol/ag-ui/commit/a40b5c0824564eb2f9ab9edf2be43f355f42a3b8). It separates shipped transport/replay support from remaining work needed to claim full AG-UI support, including AG-UI fronting MCP and A2A agents.
|
|
6
6
|
|
|
7
7
|
Official material reviewed:
|
|
8
8
|
|
package/docs/ag-ui.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`@arnilo/prism-ag-ui` is an optional, framework-free protocol adapter over Prism's existing redacted `AgentEvent`, session, durable-run, and persistence seams.
|
|
6
6
|
|
|
7
|
-
- Root export maps Prism events to AG-UI `@ag-ui/core` **0.0.
|
|
7
|
+
- Root export maps Prism events to AG-UI `@ag-ui/core` **0.0.59** events and offers `createAgUiHandler()` (`Request` → SSE `Response`), compatible `createPersistenceAgUiReplay()` pages, distributed `createAgentEventSourceAgUiReplay()` follow, and explicit `createAgUiMcpAdapter()` / `createAgUiMcpAppHandler()` / `createAgUiA2AAdapter()` protocol handshakes.
|
|
8
8
|
- `@arnilo/prism-ag-ui/acp` is the stable ACP **v1** sibling: `createAcpEventMapper()` and `createPrismAcpAgent()` over `@agentclientprotocol/sdk` **1.3.0** root exports. ACP is a protocol adapter — sessions, modes, MCP, fs/terminal, lifecycle mapping, and caps live on the host seams. See [ACP coding-host interop](acp.md) for the full reference; this page covers AG-UI only.
|
|
9
9
|
- Core remains protocol-free. `resumeAgentRunStream()` / `AgentRunLifecycle.resumeStream()` are generic durable-resume streams shared by adapters.
|
|
10
10
|
|
|
@@ -225,5 +225,4 @@ Defaults / hard caps: request 64 KiB / 1 MiB; input 128 / 1024 messages, 32 / 25
|
|
|
225
225
|
- [MCP bridge/server](mcp-tools.md): `mcpApps` negotiation, bounded resources, and remote tool trust.
|
|
226
226
|
- [A2A interoperability](a2a.md): verified rich task client and remote task lifecycle.
|
|
227
227
|
- [Host security guide](host-security.md): authorization, ownership, redaction, and credential boundaries.
|
|
228
|
-
- [Antigravity delegated agent](antigravity-agent.md): delegated Antigravity CLI execution with timeline step projection.
|
|
229
228
|
- [Work artifacts and review](work-artifacts-and-review.md): durable artifact service that produces the co-work approval/progress/download-link events projected here.
|
|
@@ -224,7 +224,7 @@ Use `activateAllCapabilities: true` only while migrating old configs. It intenti
|
|
|
224
224
|
- `ExtensionAPI.registerAgent(agent)` contributes an inert `AgentDefinition` programmatically; its `create()` (if present) is only invoked when the host runs it through `resolveAgentDefinition`. See [Extensions](extensions.md).
|
|
225
225
|
- Bundle resolution is config over code: every seam lives on `AgentDefinition`, `AgentDefinitionResolutionContext`, or `ResolveAgentBundleOptions`. `systemPrompt` and `loop` are passed via `context.overrides` / `create()` rather than frontmatter.
|
|
226
226
|
- Migration note: before Phase 38, a definition that omitted `tools` but had a tool scope could receive every scoped tool. Now omitted `tools`/`skills` activates none. Add explicit names to `tools` / `skills`; use `activateAllCapabilities: true` only while migrating old configs.
|
|
227
|
-
- `parseAgentFile(text, path)` (re-exported from `@arnilo/prism`) is the stdlib-only frontmatter parser for `AGENT.md`.
|
|
227
|
+
- `parseAgentFile(text, path)` (re-exported from `@arnilo/prism`) is the stdlib-only frontmatter parser for `AGENT.md`. Colocated `CONTEXT.md` / tool-descriptor parsing is host-owned (the thin `parseContextFile`/`parseToolFile` helpers were removed in 0.5.0 — see `docs/migrate-to-0.5.md`).
|
|
228
228
|
- Repo contributions (`<workspaceRoot>/.agents/{skills,tools}/`) are scanned by `discoverContributions` and passed via `repoContributions`. Repo `.agents/` is preserved as a shared contribution surface across every agent that operates on the same repository; multiple agents from different apps can work the same repo, and all share its repo-level skills.
|
|
229
229
|
|
|
230
230
|
## Security and performance notes
|
package/docs/agent-events.md
CHANGED
|
@@ -24,10 +24,10 @@ Event records preserve emission order within a run because the runtime drains pe
|
|
|
24
24
|
|
|
25
25
|
### Placement (FR-7 answer, 0.0.26)
|
|
26
26
|
|
|
27
|
-
The durable `AgentEventSource` **stays in `@arnilo/prism-
|
|
27
|
+
The durable `AgentEventSource` **stays in `@arnilo/prism-core/sessions/postgres`** for the 0.0.26 line and is importable from the package root (FR-6):
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
|
-
import { createPostgresAgentEventSource } from "@arnilo/prism-
|
|
30
|
+
import { createPostgresAgentEventSource } from "@arnilo/prism-core/sessions/postgres";
|
|
31
31
|
const source = createPostgresAgentEventSource({ pool, schema: "prism", cursorSecret });
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -35,11 +35,11 @@ PostgreSQL `LISTEN`/`NOTIFY` remains the **reference durable implementation**; `
|
|
|
35
35
|
|
|
36
36
|
### NATS JetStream adapter (FR-5)
|
|
37
37
|
|
|
38
|
-
`@arnilo/prism-
|
|
38
|
+
`@arnilo/prism-core/sessions/nats` ships a sibling durable `AgentEventSource` over NATS JetStream for JetStream backbones (Postgres remains the reference implementation):
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
41
|
import { connect } from "@nats-io/transport-node";
|
|
42
|
-
import { createNatsAgentEventSource, createNatsJetStream } from "@arnilo/prism-
|
|
42
|
+
import { createNatsAgentEventSource, createNatsJetStream } from "@arnilo/prism-core/sessions/nats";
|
|
43
43
|
|
|
44
44
|
const nc = await connect({ servers: process.env.NATS_URL });
|
|
45
45
|
const source = createNatsAgentEventSource({ connection: await createNatsJetStream(nc), stream: "prism_agent_events" });
|
|
@@ -249,4 +249,4 @@ for await (const event of session.stream("draft", { loop: { strategy: "generate-
|
|
|
249
249
|
- [Observability](observability.md): `ProviderTurnMetadata`; optional adapter builds one parented GenAI span tree from metadata-only lifecycle events and ignores message/progress deltas.
|
|
250
250
|
- [Tools](tools.md): `tool_execution_*` variants.
|
|
251
251
|
- [Compaction and retry policies](compaction-and-retry.md): `compaction_*` and `retry_scheduled` variants.
|
|
252
|
-
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional redacted mapping of this stream; durable replay is ledger-backed and at-least-once, never a live-subscriber substitute. [ACP coding-host interop](acp.md) additionally maps `CodingLifecycleEvent`s from `@arnilo/prism-coding-agent` (`file_changed`, `worktree_changed`, `permission_denied`, `configuration_changed`, `plan_changed`, `plan_removed`; process events reuse `CodingProcessEvent`) into ACP session updates — locations/diff blocks only through projection allow-lists, terminal chunks under `process.outputChunkBytes`, plan updates only to clients that advertised the UNSTABLE `plan` capability.
|
|
252
|
+
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): optional redacted mapping of this stream; durable replay is ledger-backed and at-least-once, never a live-subscriber substitute. [ACP coding-host interop](acp.md) additionally maps `CodingLifecycleEvent`s from `@arnilo/prism-coding-tools/agent` (`file_changed`, `worktree_changed`, `permission_denied`, `configuration_changed`, `plan_changed`, `plan_removed`; process events reuse `CodingProcessEvent`) into ACP session updates — locations/diff blocks only through projection allow-lists, terminal chunks under `process.outputChunkBytes`, plan updates only to clients that advertised the UNSTABLE `plan` capability.
|
package/docs/agent-identity.md
CHANGED
|
@@ -87,7 +87,7 @@ await agent.createSession().run("Summarize inbox", {
|
|
|
87
87
|
|
|
88
88
|
Server / MCP / A2A authorize callbacks may include the same `identity` beside `ownership`. Handlers assert activity and ownership match before admitting work.
|
|
89
89
|
|
|
90
|
-
## OIDC/JWKS verifier adapter (`@arnilo/prism-credentials
|
|
90
|
+
## OIDC/JWKS verifier adapter (`@arnilo/prism-core/credentials/node/oidc`)
|
|
91
91
|
|
|
92
92
|
Optional `createOidcIdentityVerifier` turns a pinned issuer/audience and pinned JWKS URL into a core `IdentityVerifier` — one bounded reference adapter for hosts that already authenticate callers with OIDC JWTs (Entra, Keycloak, Auth0, …). Native `fetch` + WebCrypto only; no JOSE dependency.
|
|
93
93
|
|
|
@@ -102,7 +102,7 @@ Optional `createOidcIdentityVerifier` turns a pinned issuer/audience and pinned
|
|
|
102
102
|
| `limits` | Bounded JWKS/claims knobs; `identity` reuses core identity caps |
|
|
103
103
|
|
|
104
104
|
```ts
|
|
105
|
-
import { createOidcIdentityVerifier } from "@arnilo/prism-credentials
|
|
105
|
+
import { createOidcIdentityVerifier } from "@arnilo/prism-core/credentials/node/oidc";
|
|
106
106
|
|
|
107
107
|
const verifier = createOidcIdentityVerifier({
|
|
108
108
|
issuer: "https://id.example.com/tenant",
|
|
@@ -132,6 +132,17 @@ Identity is optional. Hosts that only set `ownership` keep prior behavior. When
|
|
|
132
132
|
- Checks are O(fields) and network-free in core; remote auth stays in the host verifier.
|
|
133
133
|
- Raising hard caps requires updating `docs/_evidence/review-coverage-2026-07-23-phase-8.md`, tests, and docs.
|
|
134
134
|
|
|
135
|
+
## Live probe (plans/064 Task 9)
|
|
136
|
+
|
|
137
|
+
The OIDC/JWKS identity verifier has an operator-gated live probe against a real issuer:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
PRISM_TEST_OIDC_ISSUER=https://id.example.com/tenant PRISM_TEST_OIDC_AUDIENCE=prism-api \
|
|
141
|
+
PRISM_TEST_OIDC_TOKEN=<real bearer token> npm test -w @arnilo/prism-core -- oidc-live
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Set `PRISM_TEST_OIDC_JWKS_URL` to pin a non-default JWKS URL (default `<issuer>/.well-known/jwks.json`). Probes: a valid token verifies against the live JWKS (1 fetch), a tampered token fails closed with `ERR_PRISM_OIDC_SIGNATURE` (error text never echoes the token), and a garbage token fails closed without JWKS traffic. Bounded to 1 real request. Registered in `scripts/live-matrix.json` as `core/oidc-live`.
|
|
145
|
+
|
|
135
146
|
## Related APIs
|
|
136
147
|
|
|
137
148
|
- [Policy and audit](policy-and-audit.md)
|
package/docs/audit-export.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-policy` exports tenant-scoped audit records as signed, hash-chained
|
|
5
|
+
`@arnilo/prism-core/governance/policy` exports tenant-scoped audit records as signed, hash-chained
|
|
6
6
|
batches: each record envelope is canonicalized (RFC 8785 semantics), hashed with
|
|
7
7
|
SHA-256 including the prior digest, so records form a tamper-evident chain. A
|
|
8
8
|
batch of chained records is wrapped in a manifest that a host-provided
|
|
@@ -69,7 +69,7 @@ canonical `record` payload. `verifyAuditBatch` returns `{ ok, errors, batch }`.
|
|
|
69
69
|
## Request/response example
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
|
-
import { createAuditExporter, createMemoryAuditCursorStore } from "@arnilo/prism-policy";
|
|
72
|
+
import { createAuditExporter, createMemoryAuditCursorStore } from "@arnilo/prism-core/governance/policy";
|
|
73
73
|
|
|
74
74
|
const exporter = createAuditExporter({
|
|
75
75
|
source, // host: tenant-scoped record pages
|
|
@@ -87,7 +87,7 @@ const result = await exporter.exportNext({ tenantId: "acme", maxRecords: 1000 })
|
|
|
87
87
|
|
|
88
88
|
```ts
|
|
89
89
|
import { readFileSync } from "node:fs";
|
|
90
|
-
import { verifyAuditBatch } from "@arnilo/prism-policy";
|
|
90
|
+
import { verifyAuditBatch } from "@arnilo/prism-core/governance/policy";
|
|
91
91
|
|
|
92
92
|
const artifactBytes = new Uint8Array(readFileSync("./acme-000001.json"));
|
|
93
93
|
const verified = verifyAuditBatch({
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Batch jobs
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`BatchJobsProvider` is the provider-neutral async batch contract (plan 061
|
|
6
|
+
Task 7): `submit` (requests + metadata → opaque job id), `status`, `cancel`, and
|
|
7
|
+
paged `results`, with a typed job-state union and a `pollBatch` backoff utility
|
|
8
|
+
exported standalone — core never loops or awaits completion for you. The
|
|
9
|
+
first-party adapter is [`createOpenAIBatchJobsProvider`](providers/openai.md)
|
|
10
|
+
(Files API JSONL upload + `/v1/batches` lifecycle). Offline conformance runs via
|
|
11
|
+
`runBatchJobsConformance` from `@arnilo/prism/testing/provider-conformance`.
|
|
12
|
+
|
|
13
|
+
## When to use it
|
|
14
|
+
|
|
15
|
+
Use it for large asynchronous workloads that tolerate 24-hour completion
|
|
16
|
+
windows — offline scoring, backfills, bulk classification. Do not use it as a
|
|
17
|
+
scheduler or an orchestration saga: the contract is standalone by decision
|
|
18
|
+
(integration with the workflows saga is deferred — see plan 061 Further
|
|
19
|
+
Actions). Interactive low-latency requests belong on the normal provider path.
|
|
20
|
+
|
|
21
|
+
## Inputs / request
|
|
22
|
+
|
|
23
|
+
| Call | Inputs | Meaning |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `submit` | `{ model, requests, metadata? }` | `requests` are provider-native `{ customId?, body: JsonObject }` items; bodies are opaque to the contract and inherit provider caps. |
|
|
26
|
+
| `status` | opaque job id | Current `BatchJob` snapshot (state union + provider counts + `raw`). |
|
|
27
|
+
| `cancel` | opaque job id | Best-effort cancellation; returns the transitioning job (`cancelling`). |
|
|
28
|
+
| `results` | job id + `{ cursor?, pageSize? }` | Page of per-request outcomes; cursor is an opaque continuation token. |
|
|
29
|
+
| `pollBatch` | provider + job id + `{ intervalMs?, backoffMultiplier?, maxIntervalMs?, maxAttempts? }` | Utility only — resolves on `completed`, throws typed on `failed`/`cancelled`/`expired`. |
|
|
30
|
+
|
|
31
|
+
Adapter options: `apiKey` (`CredentialValueSource` — resolved per call,
|
|
32
|
+
redacted from errors), `baseUrl`, `fetch` (fake transport for offline tests),
|
|
33
|
+
`headers`, `endpoint` (defaults to `/v1/chat/completions`),
|
|
34
|
+
`completionWindow` (defaults `24h`).
|
|
35
|
+
|
|
36
|
+
## Outputs / response / events
|
|
37
|
+
|
|
38
|
+
| Field | Type | Meaning |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `job.state` | `"queued" \| "running" \| "cancelling" \| "completed" \| "failed" \| "cancelled" \| "expired"` | Neutral union; adapters map provider states (OpenAI `validating`→`queued`, `in_progress`/`finalizing`→`running`, …). `isBatchJobTerminal` classifies. |
|
|
41
|
+
| `job.id` | `string` | Opaque provider id — the contract never parses or scopes it. |
|
|
42
|
+
| `job.requestCounts` | `{ total, completed, failed }?` | Provider progress counts. |
|
|
43
|
+
| `job.raw` | `JsonObject?` | Provider-native job fields, unmodified, for audits. |
|
|
44
|
+
| `result.items[n]` | `{ customId, response?, error?, raw? }` | Per-request outcome; per-item failures ride alongside a `completed` job. |
|
|
45
|
+
|
|
46
|
+
Failures throw `BatchJobsError` with a stable `code`: `empty_requests`,
|
|
47
|
+
`too_many_requests` (adapter cap `OPENAI_BATCH_MAX_REQUESTS` = 50,000, OpenAI's
|
|
48
|
+
documented limit), `unsupported_model` (via `assertBatchJobsSupported` when the
|
|
49
|
+
host checks `ModelCapabilities.batchJobs`), `job_not_found`, `invalid_cursor`,
|
|
50
|
+
`request_failed` (non-2xx, secret-redacted), `response_malformed`, and — from
|
|
51
|
+
`pollBatch` on terminal states — `job_failed`, `job_cancelled`, `job_expired`.
|
|
52
|
+
|
|
53
|
+
## Request/response example
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{ "input_file_id": "file-X123", "endpoint": "/v1/chat/completions", "completion_window": "24h" }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Implementation example
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { createOpenAIBatchJobsProvider } from "@arnilo/prism-providers/openai";
|
|
63
|
+
import { pollBatch, runBatchJobsConformance } from "@arnilo/prism";
|
|
64
|
+
import { runBatchJobsConformance } from "@arnilo/prism/testing/provider-conformance";
|
|
65
|
+
|
|
66
|
+
const batch = createOpenAIBatchJobsProvider({ apiKey: process.env.OPENAI_API_KEY });
|
|
67
|
+
const job = await batch.submit({
|
|
68
|
+
model: "gpt-4o-mini",
|
|
69
|
+
requests: [{ customId: "doc-1", body: { messages: [{ role: "user", content: "summarize" }] } }],
|
|
70
|
+
});
|
|
71
|
+
// pollBatch is a plain utility — host owns scheduling and persistence:
|
|
72
|
+
const done = await pollBatch(batch, job.id, { intervalMs: 30_000, backoffMultiplier: 1.5, maxIntervalMs: 300_000 });
|
|
73
|
+
let cursor: string | null | undefined = null;
|
|
74
|
+
do {
|
|
75
|
+
const page = await batch.results(done.id, { cursor });
|
|
76
|
+
for (const item of page.items) { /* host owns per-item handling */ }
|
|
77
|
+
cursor = page.nextCursor ?? null;
|
|
78
|
+
} while (cursor !== null);
|
|
79
|
+
|
|
80
|
+
// Offline conformance (fake transport, no network):
|
|
81
|
+
await runBatchJobsConformance({
|
|
82
|
+
provider: createOpenAIBatchJobsProvider({ apiKey: "sk-test", fetch: fakeFetch }),
|
|
83
|
+
maxRequests: 50_000,
|
|
84
|
+
sample: { model: "gpt-4o-mini", requests: [{ body: { messages: [] } }] },
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Extension and configuration notes
|
|
89
|
+
|
|
90
|
+
- Implement `BatchJobsProvider` for other vendors; the contract is structural —
|
|
91
|
+
no base class, no registry. Map your provider's states onto the neutral union
|
|
92
|
+
and surface your raw job payload on `job.raw`.
|
|
93
|
+
- Models declare support with `capabilities.batchJobs`; hosts gate with
|
|
94
|
+
`modelSupportsBatchJobs` / `assertBatchJobsSupported`, mirroring the
|
|
95
|
+
embeddings/speech/image/video/moderation guard pattern.
|
|
96
|
+
- Workflow-saga integration (auto-submission from run failure recovery) is
|
|
97
|
+
intentionally out of scope for v1 — see plan 061 Further Actions.
|
|
98
|
+
|
|
99
|
+
## Security and performance notes
|
|
100
|
+
|
|
101
|
+
- Job ids are opaque strings; results cursors are opaque continuation tokens
|
|
102
|
+
(adapter: line offsets — never parsed as authorization).
|
|
103
|
+
- API keys resolve through the existing `CredentialValueSource` seam and are
|
|
104
|
+
redacted from every thrown error; no new secret paths.
|
|
105
|
+
- All responses read through the bounded readers
|
|
106
|
+
(`OPENAI_BATCH_MAX_RESPONSE_BYTES`, 256 MiB for JSONL downloads — batches are
|
|
107
|
+
large by design); oversized payloads reject instead of buffering.
|
|
108
|
+
- One provider request per call; paging is client-side over the downloaded
|
|
109
|
+
output/error file — no network per page boundary.
|
|
110
|
+
- Inputs, payloads, and raw files are never logged by core; error messages
|
|
111
|
+
carry status and a redacted body only.
|
|
112
|
+
|
|
113
|
+
## Related APIs
|
|
114
|
+
|
|
115
|
+
- [Provider conformance](provider-conformance.md): `runBatchJobsConformance`
|
|
116
|
+
and the offline conformance matrix.
|
|
117
|
+
- [Provider packages](provider-packages.md): subpath import rules for
|
|
118
|
+
`@arnilo/prism-providers/openai`.
|
|
119
|
+
- [Multimodal content](multimodal-content.md): sibling one-shot modality
|
|
120
|
+
contracts sharing the same capability-flag and guard pattern.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-browser` exposes six exclusive model-facing tools—`browser_open`, `browser_snapshot`, `browser_act`, `browser_close`, `browser_evaluate`, and `browser_observe`—over a host-supplied Playwright `Browser`. Prism creates one non-persistent `BrowserContext` per run, serializes actions, returns bounded AI-mode accessibility snapshots with snapshot-scoped refs, enforces egress/side-effect/upload/download/screenshot policy, and closes context/pages/listeners/quarantined downloads on close, abort, or manager disposal. Since 0.1.4 the package also rides playwright-core's existing CDP transport for bounded page evaluation, console/network observation, and network/emulation control on Chromium hosts — zero new dependencies, Prism still never launches or downloads browsers.
|
|
5
|
+
The `@arnilo/prism-web-tools/browser` subpath exposes six exclusive model-facing tools—`browser_open`, `browser_snapshot`, `browser_act`, `browser_close`, `browser_evaluate`, and `browser_observe`—over a host-supplied Playwright `Browser`. Prism creates one non-persistent `BrowserContext` per run, serializes actions, returns bounded AI-mode accessibility snapshots with snapshot-scoped refs, enforces egress/side-effect/upload/download/screenshot policy, and closes context/pages/listeners/quarantined downloads on close, abort, or manager disposal. Since 0.1.4 the package also rides playwright-core's existing CDP transport for bounded page evaluation, console/network observation, and network/emulation control on Chromium hosts — zero new dependencies, Prism still never launches or downloads browsers.
|
|
6
6
|
|
|
7
7
|
## When to use it
|
|
8
8
|
|
|
@@ -65,8 +65,8 @@ import {
|
|
|
65
65
|
createBrowserManager,
|
|
66
66
|
createBrowserTools,
|
|
67
67
|
createSharedSandboxBrowserOptions,
|
|
68
|
-
} from "@arnilo/prism-browser";
|
|
69
|
-
import { assertBrowserSandboxNetwork } from "@arnilo/prism-coding-security";
|
|
68
|
+
} from "@arnilo/prism-web-tools/browser";
|
|
69
|
+
import { assertBrowserSandboxNetwork } from "@arnilo/prism-coding-tools/security";
|
|
70
70
|
|
|
71
71
|
assertBrowserSandboxNetwork({
|
|
72
72
|
mode: "custom",
|
|
@@ -106,7 +106,7 @@ await browser.close();
|
|
|
106
106
|
- `networkPolicy` defaults to `requireContainedProxy: true` (fail closed). Hosts must supply `containedProxyAttestation: { proxyEndpoint, denyDirectEgress: true }`. Private/loopback/link-local, `file`/`data`/`blob`/`javascript`/`devtools` schemes are denied by default. Playwright routing is defense in depth — production DNS/private egress is a host firewall/proxy.
|
|
107
107
|
- Uploads require absolute paths under `uploads.roots` (realpath-contained; symlink escapes rejected). Downloads stream into `downloads.quarantine` with SHA-256/MIME/name metadata; `download_release` requires host `approveRelease`. Screenshots return bounded `ImageContent`.
|
|
108
108
|
- Observation (`snapshot`, `wait`, open-without-url, `close`) vs mutation/high-impact (`navigate`, click/form, dialog accept, upload, download release, popup select) is classified for `ExecutionPolicy` / `beforeSideEffect`.
|
|
109
|
-
- `createSharedSandboxBrowserOptions()` aligns browser uploads/downloads with Task 1 sandbox `/workspace` and `/downloads`. `assertBrowserSandboxNetwork()` in `@arnilo/prism-coding-security` fails closed for custom Docker networks without browser egress attestation.
|
|
109
|
+
- `createSharedSandboxBrowserOptions()` aligns browser uploads/downloads with Task 1 sandbox `/workspace` and `/downloads`. `assertBrowserSandboxNetwork()` in `@arnilo/prism-coding-tools/security` fails closed closed for custom Docker networks without browser egress attestation.
|
|
110
110
|
- Raw CSS/XPath: since 0.1.4 `{ css }` / `{ xpath }` targets resolve via Playwright's selector engine (`locator(css)` / `locator("xpath=…")`); ref resolution keeps the built-in `aria-ref=` selector with a package-owned snapshot ref table for staleness checks.
|
|
111
111
|
- CDP capabilities (0.1.4): `browser_evaluate` (bounded `Runtime.evaluate`), `browser_observe` (Runtime console/exception + Network request/response/failed events in a bounded ring with drain-on-read), and `block_urls`/`unblock_urls` (`Network.setBlockedURLs`), `throttle` (`Network.emulateNetworkConditions`), `emulate` (`Emulation.setDeviceMetricsOverride` + optional `setUserAgentOverride`). All CDP sessions are per-page via `context.newCDPSession(page)` and are detached on run close — network/emulation changes are run-scoped and reset with `browser_close`. `BrowserCdpOptions.mode` (`auto` | `on` | `off`, default `auto`) gates the surface: non-Chromium hosts or mode `off` return `ERR_PRISM_BROWSER_CDP_UNAVAILABLE` without affecting Playwright-only tools. Domains are limited to the Runtime/Network/Emulation allowlist — cookies, tracing, performance profiles, IndexedDB, and worker debugging are not exposed. CDP is not an egress bypass: page network still routes through the run's routing/blocking and `networkPolicy`.
|
|
112
112
|
- CDP bounds (0.1.4): evaluate expression ≤ `maxActionInputBytes` (64 KiB default / 256 KiB hard) and result capped at `maxEvaluateResultBytes` (64 KiB / 256 KiB) with truncation marking; `browser_observe` rings capped at `maxConsoleEntries` (200/500) and `maxNetworkRequests`; `block_urls` patterns ≤ `maxBlockedUrlPatterns` (32/128); throttle latency ≤ 120 s and throughput ≤ 1 Gbps; emulate dimensions ≤ 16 384 and scale ≤ 10, user agent ≤ 2 KiB. Evaluate is classified high-impact (arbitrary page-context code execution): `ExecutionPolicy` approval and the `beforeSideEffect` hook are mandatory, it charges the action budget, and results are marked `untrusted_external`. `browser_observe` is observation-only (no side-effect hook, no action charge) and **never captures request/response bodies, cookies, or auth headers** — only bounded URL/method/status/error-text/arg previews.
|
|
@@ -118,7 +118,7 @@ Observation tools declare `kind: none`; mutations are `external_mutation`/`unsup
|
|
|
118
118
|
|
|
119
119
|
Import is inert. Construction fails clearly when neither `browser` nor `manager` is supplied. Browser installation, launch, version, and control endpoint are host-owned. Prism never exposes init scripts, extensions, persistent profiles, or model-supplied Playwright launch options; CDP exposure is limited to the allowlisted Runtime/Network/Emulation surface above (evaluate is policy-gated arbitrary code execution — treat results as untrusted). Secrets and storage state must not appear in snapshots, tool results, logs, or checkpoints. Finite caps charge before context/page/action/queue/snapshot/network/artifact retention; snapshots retain no unbounded DOM, console, request, response, or trace history. Unreleased downloads are deleted on context close.
|
|
120
120
|
|
|
121
|
-
Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PLAYWRIGHT=1` (or `PRISM_TEST_PLAYWRIGHT=1`) `npm run test:live -w @arnilo/prism-
|
|
121
|
+
Default tests use fake Playwright APIs only. Protected live gate: `PRISM_LIVE_PLAYWRIGHT=1` (or `PRISM_TEST_PLAYWRIGHT=1`) `npm run test:live -w @arnilo/prism-web-tools` exercises a local loopback hostile HTML fixture for snapshot refs, stale-ref rejection, css/xpath targets, private/file deny, upload containment, screenshot bounds, download quarantine/release, and the CDP leg (real evaluate, observe, and emulate). Missing browser binaries fail closed when the gate is enabled. The protected coding journey (0.2.6, plan 026 Task 7) additionally runs a real browser inspection leg (local loopback fixture page, snapshot text assertion, run-owned context closed before the host browser) inside the packed consumer as part of scripts/phase26-coding-journey.test.mjs, gated by PRISM_LIVE_PLAYWRIGHT with the pinned playwright-core installed into the consumer; browser storage never appears in the retained report. Adversarial network-free fixtures live in `eval-fixtures.test.ts`; see [Evaluations](evaluations.md) and `examples/coding-browser-evaluation.ts`.
|
|
122
122
|
|
|
123
123
|
## Related APIs
|
|
124
124
|
|
package/docs/caveman.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-caveman` is an optional package that wires [juliusbrussee/caveman](https://github.com/juliusbrussee/caveman) into Prism contribution contracts.
|
|
5
|
+
`@arnilo/prism-coding-tools/caveman` is an optional package that wires [juliusbrussee/caveman](https://github.com/juliusbrussee/caveman) into Prism contribution contracts.
|
|
6
6
|
|
|
7
7
|
It registers upstream skills and commands, injects active level prompt slices via `InstructionInjector`, and persists level as session custom `caveman-level` entries. Import and extension `setup` without a resolvable upstream path fail closed with a bounded redacted error and register zero contributions.
|
|
8
8
|
|
|
@@ -66,7 +66,7 @@ Deactivation phrases `stop caveman` and `normal mode` clear active injection wit
|
|
|
66
66
|
## Implementation example
|
|
67
67
|
|
|
68
68
|
```ts
|
|
69
|
-
import { createCavemanExtension } from "@arnilo/prism-caveman";
|
|
69
|
+
import { createCavemanExtension } from "@arnilo/prism-coding-tools/caveman";
|
|
70
70
|
import {
|
|
71
71
|
createExtensionKernel,
|
|
72
72
|
createLoadSkillTool,
|