@arnilo/prism 0.0.14 → 0.0.16
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 +22 -2
- package/README.md +5 -4
- package/dist/agent-definitions.js +2 -3
- package/dist/agent-loops.d.ts +4 -0
- package/dist/agent-loops.js +28 -10
- package/dist/agent-run-lifecycle.d.ts +1 -2
- package/dist/agent-run-lifecycle.js +1 -1
- package/dist/agent-run-state.js +16 -3
- package/dist/agents.d.ts +1 -1
- package/dist/agents.js +121 -51
- package/dist/cache-helpers.js +18 -9
- package/dist/checkpoints.js +5 -9
- package/dist/cli-init.js +3 -7
- package/dist/cli-runner.d.ts +1 -1
- package/dist/cli-runner.js +74 -13
- package/dist/compaction.js +5 -4
- package/dist/config.js +7 -4
- package/dist/content.js +26 -24
- package/dist/context-budget.js +12 -8
- package/dist/contracts.d.ts +79 -3
- package/dist/contracts.js +4 -1
- package/dist/contribution-parsing.js +6 -2
- package/dist/conversations.js +2 -1
- package/dist/credentials.d.ts +1 -1
- package/dist/credentials.js +3 -1
- package/dist/event-multiplexer.js +1 -3
- package/dist/feedback.js +11 -9
- package/dist/guardrails.d.ts +1 -1
- package/dist/guardrails.js +17 -14
- package/dist/identity.js +10 -2
- package/dist/index.d.ts +83 -84
- package/dist/index.js +42 -42
- package/dist/input.d.ts +2 -2
- package/dist/input.js +40 -24
- package/dist/instruction-injection.d.ts +1 -1
- package/dist/node/agent-definitions.js +16 -8
- package/dist/node/contribution-discovery.d.ts +1 -2
- package/dist/node/contribution-discovery.js +3 -3
- package/dist/node/session-store-jsonl.js +10 -7
- package/dist/node/settings.d.ts +1 -1
- package/dist/node/settings.js +1 -1
- package/dist/node/system-project-prompts.js +2 -4
- package/dist/node/trust.js +1 -1
- package/dist/persistence-lifecycle.js +1 -3
- package/dist/provider-events.d.ts +1 -0
- package/dist/provider-events.js +6 -1
- package/dist/provider-request-policy.js +3 -4
- package/dist/providers/media.d.ts +1 -1
- package/dist/providers/openai-compatible.js +3 -4
- package/dist/providers/openai-primitives.js +7 -7
- package/dist/redaction.d.ts +1 -0
- package/dist/redaction.js +5 -2
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +2 -2
- package/dist/rpc.js +42 -9
- package/dist/run-ledger.js +13 -4
- package/dist/run-limits.js +49 -10
- package/dist/secure-agent.js +1 -1
- package/dist/security.js +7 -2
- package/dist/session-stores.d.ts +1 -1
- package/dist/session-stores.js +13 -13
- package/dist/structured-output.js +2 -2
- package/dist/system-prompts.js +7 -2
- package/dist/testing/compaction-conformance.js +5 -1
- package/dist/testing/extension-conformance.js +15 -3
- package/dist/testing/feedback.d.ts +1 -3
- package/dist/testing/feedback.js +1 -1
- package/dist/testing/persistence-schema.js +206 -37
- package/dist/testing/provider-conformance.js +3 -3
- package/dist/testing/run-ledger-conformance.js +1 -1
- package/dist/testing/session-store-conformance.js +1 -1
- package/dist/testing/tool-conformance.js +30 -5
- package/dist/thinking.js +4 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +24 -5
- package/docs/0.1.0-readiness.md +139 -0
- package/docs/host-security.md +4 -1
- package/docs/index.md +14 -11
- package/docs/migration.md +58 -1
- package/docs/multimodal-content.md +8 -5
- package/docs/performance.md +67 -0
- package/docs/provider-caching.md +8 -0
- package/docs/provider-conformance.md +29 -5
- package/docs/provider-packages.md +22 -1
- package/docs/providers/ai-sdk.md +23 -7
- package/docs/providers/openai.md +22 -3
- package/docs/public-contracts.md +1 -1
- package/docs/rag.md +41 -12
- package/docs/release-and-install.md +153 -16
- package/docs/resource-loading.md +3 -0
- package/docs/working-and-semantic-memory.md +22 -4
- package/package.json +14 -6
- package/docs/review-coverage-2026-07-14.md +0 -260
- package/docs/review-coverage-2026-07-15.md +0 -193
- package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
- package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
- package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
- package/docs/review-coverage-2026-07-21-phase-5.md +0 -172
- package/docs/review-coverage-2026-07-22-phase-6.md +0 -209
- package/docs/review-coverage-2026-07-22-phase-7.md +0 -173
- package/docs/review-coverage-2026-07-23-phase-8.md +0 -245
- package/docs/review-coverage-2026-07-25-phase-9.md +0 -256
package/dist/session-stores.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, SESSION_APPEND_CONFLICT_CODE, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SessionAppendConflictError, SessionSearchUnsupportedError,
|
|
1
|
+
import { DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, resolveSessionSearchQuery, SESSION_APPEND_CONFLICT_CODE, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SessionAppendConflictError, SessionSearchUnsupportedError, } from "./contracts.js";
|
|
2
2
|
import { createId } from "./ids.js";
|
|
3
3
|
export function createSessionEntry(options) {
|
|
4
4
|
const { createId, now, ...entry } = options;
|
|
@@ -13,7 +13,9 @@ export function createSessionEntry(options) {
|
|
|
13
13
|
// this if a legitimate branch exceeds it.
|
|
14
14
|
const MAX_BRANCH_PAGES = 64;
|
|
15
15
|
export function getSessionBranchEntries(input, options = {}) {
|
|
16
|
-
return typeof input === "function"
|
|
16
|
+
return typeof input === "function"
|
|
17
|
+
? readBranchFromReader(input, options)
|
|
18
|
+
: getSessionBranchEntriesCore(input, options);
|
|
17
19
|
}
|
|
18
20
|
async function readBranchFromReader(reader, query) {
|
|
19
21
|
const items = [];
|
|
@@ -66,13 +68,15 @@ async function rebuildSessionContextFromReader(reader, query) {
|
|
|
66
68
|
}
|
|
67
69
|
function rebuildSessionContextCore(entries, options = {}) {
|
|
68
70
|
const branch = getSessionBranchEntriesCore(entries, options);
|
|
69
|
-
const compaction = [...branch]
|
|
71
|
+
const compaction = [...branch]
|
|
72
|
+
.reverse()
|
|
73
|
+
.find((entry) => entry.kind === "compaction" && entry.summary && isCompactionEntryData(entry.data));
|
|
70
74
|
if (!compaction || !isCompactionEntryData(compaction.data)) {
|
|
71
75
|
return {
|
|
72
76
|
leafId: branch.at(-1)?.id,
|
|
73
77
|
entries: branch,
|
|
74
|
-
messages: branch.flatMap((entry) => entry.kind === "message" && entry.message ? [entry.message] : []),
|
|
75
|
-
summaries: branch.flatMap((entry) => entry.kind === "summary" && entry.summary ? [entry.summary] : []),
|
|
78
|
+
messages: branch.flatMap((entry) => (entry.kind === "message" && entry.message ? [entry.message] : [])),
|
|
79
|
+
summaries: branch.flatMap((entry) => (entry.kind === "summary" && entry.summary ? [entry.summary] : [])),
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
const keepIds = new Set(compaction.data.keepEntryIds ?? []);
|
|
@@ -257,16 +261,12 @@ function searchMemorySessionsLinear(bySession, leafBySession, query) {
|
|
|
257
261
|
}
|
|
258
262
|
matches.sort((a, b) => compareSearchHit(a, b, q.order));
|
|
259
263
|
const afterCursor = q.cursor ? decodeSearchCursor(q.cursor) : undefined;
|
|
260
|
-
const filtered = afterCursor
|
|
261
|
-
? matches.filter((hit) => isAfterSearchCursor(hit, afterCursor, q.order))
|
|
262
|
-
: matches;
|
|
264
|
+
const filtered = afterCursor ? matches.filter((hit) => isAfterSearchCursor(hit, afterCursor, q.order)) : matches;
|
|
263
265
|
const page = filtered.slice(0, q.limit);
|
|
264
266
|
const last = page.at(-1);
|
|
265
267
|
return {
|
|
266
268
|
items: page,
|
|
267
|
-
nextCursor: filtered.length > q.limit && last?.updatedAt
|
|
268
|
-
? encodeSearchCursor(last.updatedAt, last.sessionId)
|
|
269
|
-
: undefined,
|
|
269
|
+
nextCursor: filtered.length > q.limit && last?.updatedAt ? encodeSearchCursor(last.updatedAt, last.sessionId) : undefined,
|
|
270
270
|
};
|
|
271
271
|
}
|
|
272
272
|
function entrySearchText(entry) {
|
|
@@ -323,8 +323,8 @@ function isCompactionEntryData(value) {
|
|
|
323
323
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
324
324
|
return false;
|
|
325
325
|
const data = value;
|
|
326
|
-
return (data.throughEntryId === undefined || typeof data.throughEntryId === "string")
|
|
327
|
-
|
|
326
|
+
return ((data.throughEntryId === undefined || typeof data.throughEntryId === "string") &&
|
|
327
|
+
(data.keepEntryIds === undefined || (Array.isArray(data.keepEntryIds) && data.keepEntryIds.every((item) => typeof item === "string"))));
|
|
328
328
|
}
|
|
329
329
|
function indexEntries(entries) {
|
|
330
330
|
const byId = new Map();
|
|
@@ -38,8 +38,8 @@ export function assertStructuredOutputRequestSupported(model, options) {
|
|
|
38
38
|
return;
|
|
39
39
|
validateStructuredOutputOptions(options.structuredOutput);
|
|
40
40
|
if (!modelSupportsStructuredOutput(model.capabilities)) {
|
|
41
|
-
throw new StructuredOutputError("unsupported_model", `Model ${model.provider}/${model.model} does not declare structuredOutput capability; `
|
|
42
|
-
|
|
41
|
+
throw new StructuredOutputError("unsupported_model", `Model ${model.provider}/${model.model} does not declare structuredOutput capability; ` +
|
|
42
|
+
`set loop.structuredOutputMode to "artifact-loop" or choose a capable model`);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function isGenerateValidateReviseLoopOptions(loop) {
|
package/dist/system-prompts.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// ponytail: Phase 31 — `source: "user"` is the global base layer; `source: "app"` sits above package.
|
|
2
2
|
// Behavioral change from Phase 14: `source: "user"` is now the global base (rank 0), not a high-priority caller override.
|
|
3
3
|
// Unknown custom sources sort after package but before app/run so they cannot override host/run layers.
|
|
4
|
-
const sourceRank = new Map([
|
|
4
|
+
const sourceRank = new Map([
|
|
5
|
+
["user", 0],
|
|
6
|
+
["package", 1],
|
|
7
|
+
["app", 2],
|
|
8
|
+
["run", 3],
|
|
9
|
+
]);
|
|
5
10
|
const unknownSourceRank = 1.5;
|
|
6
11
|
export function composeSystemPrompt(contributions = [], options = {}) {
|
|
7
12
|
const parts = baseParts(options.base);
|
|
@@ -40,7 +45,7 @@ function isContributionArray(value) {
|
|
|
40
45
|
return Array.isArray(value);
|
|
41
46
|
}
|
|
42
47
|
function baseParts(base) {
|
|
43
|
-
return (typeof base === "string" ? [base] : base ?? []).filter((text) => text.length > 0);
|
|
48
|
+
return (typeof base === "string" ? [base] : (base ?? [])).filter((text) => text.length > 0);
|
|
44
49
|
}
|
|
45
50
|
function rank(layer) {
|
|
46
51
|
return sourceRank.get(layer.source ?? "") ?? unknownSourceRank;
|
|
@@ -16,7 +16,11 @@ import { createSessionEntry } from "../session-stores.js";
|
|
|
16
16
|
export async function assertCompactionStrategyConforms(strategy, options = {}) {
|
|
17
17
|
const secrets = options.secrets ?? [];
|
|
18
18
|
const entries = [
|
|
19
|
-
createSessionEntry({
|
|
19
|
+
createSessionEntry({
|
|
20
|
+
sessionId: "s",
|
|
21
|
+
kind: "message",
|
|
22
|
+
message: { role: "user", content: [{ type: "text", text: `old ${secrets[0] ?? "history"}` }] },
|
|
23
|
+
}),
|
|
20
24
|
createSessionEntry({ sessionId: "s", kind: "message", message: { role: "assistant", content: [{ type: "text", text: "recent" }] } }),
|
|
21
25
|
];
|
|
22
26
|
const context = {
|
|
@@ -23,7 +23,12 @@ export async function assertExtensionConforms(extension, options = {}) {
|
|
|
23
23
|
// provider/tool/skill capabilities until host code resolves and calls them).
|
|
24
24
|
if (options.expectThrow) {
|
|
25
25
|
// A second extension that throws must rethrow under errorPolicy: "throw".
|
|
26
|
-
const failing = {
|
|
26
|
+
const failing = {
|
|
27
|
+
name: "conformance-failing",
|
|
28
|
+
setup: () => {
|
|
29
|
+
throw new Error("conformance setup failed");
|
|
30
|
+
},
|
|
31
|
+
};
|
|
27
32
|
let threw = false;
|
|
28
33
|
try {
|
|
29
34
|
await kernel.load([failing]);
|
|
@@ -37,9 +42,16 @@ export async function assertExtensionConforms(extension, options = {}) {
|
|
|
37
42
|
}
|
|
38
43
|
if (options.secrets && options.secrets.length > 0) {
|
|
39
44
|
const secret = options.secrets[0];
|
|
40
|
-
const failing = {
|
|
45
|
+
const failing = {
|
|
46
|
+
name: "conformance-failing",
|
|
47
|
+
setup: () => {
|
|
48
|
+
throw new Error(`boom ${secret}`);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
41
51
|
const errors = [];
|
|
42
|
-
kernel.events.on("extension_error", (event) => {
|
|
52
|
+
kernel.events.on("extension_error", (event) => {
|
|
53
|
+
errors.push(event.error ?? {});
|
|
54
|
+
});
|
|
43
55
|
await kernel.load([failing]);
|
|
44
56
|
if (errors.length === 0)
|
|
45
57
|
throw new Error("Failing extension setup did not emit an extension_error event");
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { RunFeedbackStore } from "../contracts.js";
|
|
2
|
-
export
|
|
3
|
-
(): RunFeedbackStore | Promise<RunFeedbackStore>;
|
|
4
|
-
}
|
|
2
|
+
export type RunFeedbackConformanceFactory = () => RunFeedbackStore | Promise<RunFeedbackStore>;
|
|
5
3
|
/** Shared minimum behavior for memory and production feedback stores. */
|
|
6
4
|
export declare function runFeedbackConformance(factory: RunFeedbackConformanceFactory): Promise<void>;
|
package/dist/testing/feedback.js
CHANGED
|
@@ -31,7 +31,7 @@ export async function runFeedbackConformance(factory) {
|
|
|
31
31
|
if (await store.delete({ id: "feedback-1", tenantId: "feedback-tenant", userId: "other" })) {
|
|
32
32
|
throw new Error("cross-owner feedback deletion succeeded");
|
|
33
33
|
}
|
|
34
|
-
if (!await store.delete({ id: "feedback-1", ...owner }))
|
|
34
|
+
if (!(await store.delete({ id: "feedback-1", ...owner })))
|
|
35
35
|
throw new Error("owned feedback deletion failed");
|
|
36
36
|
}
|
|
37
37
|
//# sourceMappingURL=feedback.js.map
|
|
@@ -301,34 +301,160 @@ export function createPersistenceSchemaModel() {
|
|
|
301
301
|
},
|
|
302
302
|
],
|
|
303
303
|
indexes: [
|
|
304
|
-
{
|
|
304
|
+
{
|
|
305
|
+
name: "prism_sessions_tenant_created_idx",
|
|
306
|
+
table: "prism_sessions",
|
|
307
|
+
columns: ["tenant_id", "account_id", "user_id", "created_at"],
|
|
308
|
+
purpose: "tenant-scoped session listing",
|
|
309
|
+
},
|
|
305
310
|
{ name: "prism_sessions_expires_idx", table: "prism_sessions", columns: ["expires_at"], purpose: "retention expiry scans" },
|
|
306
311
|
{ name: "prism_branches_session_name_idx", table: "prism_branches", columns: ["session_id", "name"], purpose: "named branch lookup" },
|
|
307
312
|
{ name: "prism_branches_leaf_idx", table: "prism_branches", columns: ["leaf_entry_id"], purpose: "leaf-to-branch resolution" },
|
|
308
|
-
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
{
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
{
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
313
|
+
{
|
|
314
|
+
name: "prism_session_entries_session_parent_idx",
|
|
315
|
+
table: "prism_session_entries",
|
|
316
|
+
columns: ["session_id", "parent_id"],
|
|
317
|
+
purpose: "parent existence checks and child lookups",
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "prism_session_entries_session_kind_ts_idx",
|
|
321
|
+
table: "prism_session_entries",
|
|
322
|
+
columns: ["session_id", "kind", "timestamp"],
|
|
323
|
+
purpose: "kind-filtered entry listing",
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: "prism_session_entries_session_run_ts_idx",
|
|
327
|
+
table: "prism_session_entries",
|
|
328
|
+
columns: ["session_id", "run_id", "timestamp"],
|
|
329
|
+
purpose: "run-scoped entry listing",
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "prism_session_entries_session_ts_id_idx",
|
|
333
|
+
table: "prism_session_entries",
|
|
334
|
+
columns: ["session_id", "timestamp", "id"],
|
|
335
|
+
purpose: "cursor pagination without full scans",
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: "prism_session_entries_session_id_idx",
|
|
339
|
+
table: "prism_session_entries",
|
|
340
|
+
columns: ["session_id", "id"],
|
|
341
|
+
purpose: "append parent validation and recursive branch reads",
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "prism_session_append_idempotency_unique",
|
|
345
|
+
table: "prism_session_append_idempotency",
|
|
346
|
+
columns: ["session_id", "expected_parent_id", "idempotency_key"],
|
|
347
|
+
purpose: "append retry deduplication (primary key enforces uniqueness)",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: "prism_runs_session_started_idx",
|
|
351
|
+
table: "prism_runs",
|
|
352
|
+
columns: ["session_id", "started_at", "id"],
|
|
353
|
+
purpose: "run history pagination",
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: "prism_runs_branch_started_idx",
|
|
357
|
+
table: "prism_runs",
|
|
358
|
+
columns: ["branch_id", "started_at", "id"],
|
|
359
|
+
purpose: "branch-scoped runs",
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: "prism_runs_tenant_idempotency_unique",
|
|
363
|
+
table: "prism_runs",
|
|
364
|
+
columns: ["tenant_id", "idempotency_key"],
|
|
365
|
+
unique: true,
|
|
366
|
+
purpose: "run-level idempotency deduplication per tenant",
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
name: "prism_agent_events_run_sequence_idx",
|
|
370
|
+
table: "prism_agent_events",
|
|
371
|
+
columns: ["run_id", "sequence"],
|
|
372
|
+
purpose: "stable per-run event timeline pagination",
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "prism_agent_events_session_ts_id_idx",
|
|
376
|
+
table: "prism_agent_events",
|
|
377
|
+
columns: ["session_id", "timestamp", "id"],
|
|
378
|
+
purpose: "event stream pagination",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "prism_tool_calls_session_name_started_idx",
|
|
382
|
+
table: "prism_tool_calls",
|
|
383
|
+
columns: ["session_id", "name", "started_at"],
|
|
384
|
+
purpose: "tool usage by name",
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "prism_tool_calls_run_started_idx",
|
|
388
|
+
table: "prism_tool_calls",
|
|
389
|
+
columns: ["run_id", "started_at"],
|
|
390
|
+
purpose: "run tool-call listing",
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
name: "prism_usage_run_recorded_idx",
|
|
394
|
+
table: "prism_usage",
|
|
395
|
+
columns: ["run_id", "recorded_at", "id"],
|
|
396
|
+
purpose: "run usage pagination",
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
name: "prism_usage_session_recorded_idx",
|
|
400
|
+
table: "prism_usage",
|
|
401
|
+
columns: ["session_id", "recorded_at"],
|
|
402
|
+
purpose: "usage aggregation",
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
name: "prism_usage_session_scope_recorded_idx",
|
|
406
|
+
table: "prism_usage",
|
|
407
|
+
columns: ["session_id", "scope", "recorded_at"],
|
|
408
|
+
purpose: "scope-safe usage aggregation",
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "prism_run_feedback_owner_created_idx",
|
|
412
|
+
table: "prism_run_feedback",
|
|
413
|
+
columns: ["tenant_id", "account_id", "user_id", "created_at", "id"],
|
|
414
|
+
purpose: "ownership-scoped feedback pagination",
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "prism_run_feedback_run_created_idx",
|
|
418
|
+
table: "prism_run_feedback",
|
|
419
|
+
columns: ["run_id", "created_at", "id"],
|
|
420
|
+
purpose: "run feedback lookup",
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: "prism_run_feedback_trace_created_idx",
|
|
424
|
+
table: "prism_run_feedback",
|
|
425
|
+
columns: ["trace_id", "created_at", "id"],
|
|
426
|
+
purpose: "trace feedback lookup",
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: "prism_agent_definitions_name_version_idx",
|
|
430
|
+
table: "prism_agent_definitions",
|
|
431
|
+
columns: ["name", "version"],
|
|
432
|
+
purpose: "definition lookup",
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "prism_legal_holds_owner_resource_idx",
|
|
436
|
+
table: "prism_legal_holds",
|
|
437
|
+
columns: ["tenant_id", "account_id", "user_id", "resource_kind", "resource_id"],
|
|
438
|
+
purpose: "hold lookup by owned resource",
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: "prism_legal_holds_created_id_idx",
|
|
442
|
+
table: "prism_legal_holds",
|
|
443
|
+
columns: ["created_at", "id"],
|
|
444
|
+
purpose: "hold export pagination",
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "prism_tenant_quotas_owner_kind_idx",
|
|
448
|
+
table: "prism_tenant_quotas",
|
|
449
|
+
columns: ["tenant_id", "account_id", "user_id", "resource_kind"],
|
|
450
|
+
purpose: "tenant quota lookup",
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: "prism_migrations_name_version_idx",
|
|
454
|
+
table: "prism_migrations",
|
|
455
|
+
columns: ["name", "version"],
|
|
456
|
+
purpose: "applied-migration lookup (table constraint enforces uniqueness)",
|
|
457
|
+
},
|
|
332
458
|
],
|
|
333
459
|
};
|
|
334
460
|
}
|
|
@@ -346,13 +472,25 @@ function migrationStep(version, name, description) {
|
|
|
346
472
|
: version === 2
|
|
347
473
|
? { table: "prism_usage", columns: ["scope", "turn", "attempt"], indexes: ["prism_usage_session_scope_recorded_idx"] }
|
|
348
474
|
: version === 3
|
|
349
|
-
? {
|
|
475
|
+
? {
|
|
476
|
+
tables: ["prism_run_feedback"],
|
|
477
|
+
indexes: model.indexes.filter((index) => index.name.startsWith("prism_run_feedback_")).map((index) => index.name),
|
|
478
|
+
}
|
|
350
479
|
: version === 4
|
|
351
|
-
// Adapter-local FTS objects (SQLite FTS5 / Postgres tsvector) map to this canonical name.
|
|
352
|
-
|
|
480
|
+
? // Adapter-local FTS objects (SQLite FTS5 / Postgres tsvector) map to this canonical name.
|
|
481
|
+
{ search: ["prism_session_search"], indexes: ["prism_sessions_updated_id_idx"] }
|
|
353
482
|
: version === 5
|
|
354
|
-
? {
|
|
355
|
-
|
|
483
|
+
? {
|
|
484
|
+
tables: ["prism_legal_holds", "prism_tenant_quotas"],
|
|
485
|
+
indexes: [
|
|
486
|
+
"prism_legal_holds_owner_resource_idx",
|
|
487
|
+
"prism_legal_holds_created_id_idx",
|
|
488
|
+
"prism_tenant_quotas_owner_kind_idx",
|
|
489
|
+
],
|
|
490
|
+
}
|
|
491
|
+
: (() => {
|
|
492
|
+
throw new Error(`Unknown migration version ${version}`);
|
|
493
|
+
})();
|
|
356
494
|
return {
|
|
357
495
|
version,
|
|
358
496
|
name,
|
|
@@ -379,13 +517,33 @@ export function createPersistenceMigrationContract() {
|
|
|
379
517
|
/** Indexed cursor columns adapters must support for paginated reads. */
|
|
380
518
|
export function getPersistencePaginationCursors() {
|
|
381
519
|
return [
|
|
382
|
-
{
|
|
520
|
+
{
|
|
521
|
+
table: "prism_session_entries",
|
|
522
|
+
columns: ["session_id", "timestamp", "id"],
|
|
523
|
+
supportsOrder: ["asc", "desc"],
|
|
524
|
+
purpose: "entry listing and branch paging",
|
|
525
|
+
},
|
|
383
526
|
{ table: "prism_runs", columns: ["session_id", "started_at", "id"], supportsOrder: ["asc", "desc"], purpose: "run history" },
|
|
384
|
-
{
|
|
385
|
-
|
|
527
|
+
{
|
|
528
|
+
table: "prism_agent_events",
|
|
529
|
+
columns: ["run_id", "sequence"],
|
|
530
|
+
supportsOrder: ["asc", "desc"],
|
|
531
|
+
purpose: "stable per-run event timeline",
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
table: "prism_agent_events",
|
|
535
|
+
columns: ["session_id", "timestamp", "id"],
|
|
536
|
+
supportsOrder: ["asc", "desc"],
|
|
537
|
+
purpose: "session event stream",
|
|
538
|
+
},
|
|
386
539
|
{ table: "prism_usage", columns: ["run_id", "recorded_at", "id"], supportsOrder: ["asc", "desc"], purpose: "run usage totals" },
|
|
387
540
|
{ table: "prism_tool_calls", columns: ["run_id", "started_at"], supportsOrder: ["asc", "desc"], purpose: "run tool-call listing" },
|
|
388
|
-
{
|
|
541
|
+
{
|
|
542
|
+
table: "prism_run_feedback",
|
|
543
|
+
columns: ["tenant_id", "account_id", "user_id", "created_at", "id"],
|
|
544
|
+
supportsOrder: ["asc", "desc"],
|
|
545
|
+
purpose: "owned feedback listing",
|
|
546
|
+
},
|
|
389
547
|
];
|
|
390
548
|
}
|
|
391
549
|
/** Build a tenant-scoped unique key column list for adapter DDL. */
|
|
@@ -418,7 +576,13 @@ export function assertPersistenceSchemaModel(model) {
|
|
|
418
576
|
throw new Error("Runs table must include tenant-scoped tenant_id");
|
|
419
577
|
}
|
|
420
578
|
const indexTables = new Set(model.indexes.map((index) => index.table));
|
|
421
|
-
for (const requiredIndex of [
|
|
579
|
+
for (const requiredIndex of [
|
|
580
|
+
"prism_session_append_idempotency",
|
|
581
|
+
"prism_session_entries",
|
|
582
|
+
"prism_agent_events",
|
|
583
|
+
"prism_runs",
|
|
584
|
+
"prism_run_feedback",
|
|
585
|
+
]) {
|
|
422
586
|
if (!indexTables.has(requiredIndex)) {
|
|
423
587
|
throw new Error(`Persistence schema missing indexes for ${requiredIndex}`);
|
|
424
588
|
}
|
|
@@ -462,7 +626,10 @@ function schemaKey(columns) {
|
|
|
462
626
|
return columns.join("\u0000");
|
|
463
627
|
}
|
|
464
628
|
function normalizedDefault(value) {
|
|
465
|
-
return value
|
|
629
|
+
return value
|
|
630
|
+
?.trim()
|
|
631
|
+
.toLowerCase()
|
|
632
|
+
.replace(/::[a-z_ ]+$/, "");
|
|
466
633
|
}
|
|
467
634
|
function compatibleColumnType(dialect, actual, expected) {
|
|
468
635
|
const type = actual.trim().toUpperCase();
|
|
@@ -527,7 +694,9 @@ export function assertPersistenceSchemaShape(shape, dialect, model = createPersi
|
|
|
527
694
|
const actual = actualIndexes.get(expected.name);
|
|
528
695
|
if (!actual)
|
|
529
696
|
throw new Error(`Persistence schema missing required index ${expected.name}`);
|
|
530
|
-
if (actual.table !== expected.table ||
|
|
697
|
+
if (actual.table !== expected.table ||
|
|
698
|
+
schemaKey(actual.columns) !== schemaKey(expected.columns) ||
|
|
699
|
+
actual.unique !== (expected.unique === true)) {
|
|
531
700
|
throw new Error(`Persistence schema index ${expected.name} has incompatible definition`);
|
|
532
701
|
}
|
|
533
702
|
}
|
|
@@ -73,7 +73,7 @@ export function assertProviderOwnedHeadersWin(captured, options) {
|
|
|
73
73
|
throw new Error(`Caller header overrode provider-owned "${name}": expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
|
|
74
74
|
}
|
|
75
75
|
for (const [name, callerValue] of Object.entries(options.caller)) {
|
|
76
|
-
if (Object.
|
|
76
|
+
if (Object.hasOwn(ownedLower, name.toLowerCase()))
|
|
77
77
|
continue;
|
|
78
78
|
const actual = captured.get(name);
|
|
79
79
|
if (actual !== callerValue)
|
|
@@ -90,7 +90,7 @@ export function assertNoSecretLeak(events, secrets) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
export function assertUsageAccounting(events, expected) {
|
|
93
|
-
const usage = [...events].reverse().find((event) => event.type === "done" && event.usage || event.type === "usage");
|
|
93
|
+
const usage = [...events].reverse().find((event) => (event.type === "done" && event.usage) || event.type === "usage");
|
|
94
94
|
const actual = usage?.type === "usage" ? usage.usage : usage?.usage;
|
|
95
95
|
if (!actual)
|
|
96
96
|
throw new Error("Provider stream did not include usage");
|
|
@@ -155,6 +155,6 @@ function jsonPrimitives(value) {
|
|
|
155
155
|
return primitives;
|
|
156
156
|
}
|
|
157
157
|
function textFrom(events) {
|
|
158
|
-
return events.map((event) => event.type === "content_delta" && event.content.type === "text" ? event.content.text : "").join("");
|
|
158
|
+
return events.map((event) => (event.type === "content_delta" && event.content.type === "text" ? event.content.text : "")).join("");
|
|
159
159
|
}
|
|
160
160
|
//# sourceMappingURL=provider-conformance.js.map
|
|
@@ -138,7 +138,7 @@ export async function assertRunLedgerConforms(fixture, options = {}) {
|
|
|
138
138
|
await fixture.ledger.appendRun(otherRun);
|
|
139
139
|
const runs = await fixture.readRuns();
|
|
140
140
|
const stored = runs.find((row) => row.id === "run-tenant-b");
|
|
141
|
-
if (
|
|
141
|
+
if (stored?.tenantId !== "tenant-b") {
|
|
142
142
|
throw new Error("RunLedger must persist tenant_id on records for tenant-scoped isolation");
|
|
143
143
|
}
|
|
144
144
|
const scoped = runs.filter((row) => row.tenantId === tenantId);
|
|
@@ -155,7 +155,7 @@ async function assertSessionStoreBranchIsolation(store, options) {
|
|
|
155
155
|
throw new Error("SessionStore leaked entries across session ids");
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
-
async function assertConcurrentParentAppendAllowed(store, sessionId,
|
|
158
|
+
async function assertConcurrentParentAppendAllowed(store, sessionId, _now, make) {
|
|
159
159
|
const forkRoot = make("fork-root");
|
|
160
160
|
await store.append(forkRoot);
|
|
161
161
|
const childA = make("fork-a", forkRoot.id);
|
|
@@ -26,15 +26,38 @@ export async function assertToolDispatchConforms(registry, options) {
|
|
|
26
26
|
// 1. unknown tool → blocked "unknown_tool".
|
|
27
27
|
await assertToolBlocked({ call: call("does-not-exist", {}), context: baseContext, ...shared, ...pickPolicy(options) }, "unknown_tool");
|
|
28
28
|
// 2. denied tool (filter) → blocked "tool_denied".
|
|
29
|
-
await assertToolBlocked({
|
|
29
|
+
await assertToolBlocked({
|
|
30
|
+
call: call(options.tool.name, options.validArgs),
|
|
31
|
+
context: baseContext,
|
|
32
|
+
...shared,
|
|
33
|
+
...pickPolicy(options),
|
|
34
|
+
filter: { deny: [options.tool.name] },
|
|
35
|
+
}, "tool_denied");
|
|
30
36
|
// 3. invalid (non-object) arguments → blocked "invalid_arguments".
|
|
31
37
|
await assertToolBlocked({ call: call(options.tool.name, "not-an-object"), context: baseContext, ...shared, ...pickPolicy(options) }, "invalid_arguments");
|
|
32
38
|
// 4. permission denial → blocked "permission_denied".
|
|
33
|
-
await assertToolBlocked({
|
|
39
|
+
await assertToolBlocked({
|
|
40
|
+
call: call(options.tool.name, options.validArgs),
|
|
41
|
+
context: baseContext,
|
|
42
|
+
...shared,
|
|
43
|
+
...pickPolicy(options),
|
|
44
|
+
permission: denyAllPermission,
|
|
45
|
+
}, "permission_denied");
|
|
34
46
|
// 5. validator failure → blocked "validation_failed".
|
|
35
|
-
await assertToolBlocked({
|
|
47
|
+
await assertToolBlocked({
|
|
48
|
+
call: call(options.tool.name, options.validArgs),
|
|
49
|
+
context: baseContext,
|
|
50
|
+
...shared,
|
|
51
|
+
...pickPolicy(options),
|
|
52
|
+
validate: alwaysInvalidValidator,
|
|
53
|
+
}, "validation_failed");
|
|
36
54
|
// 6. valid call → executes (tool_execution_started), no error, no blocked event.
|
|
37
|
-
const probe = await dispatchAndCollect({
|
|
55
|
+
const probe = await dispatchAndCollect({
|
|
56
|
+
call: call(options.tool.name, options.validArgs),
|
|
57
|
+
context: baseContext,
|
|
58
|
+
...shared,
|
|
59
|
+
...pickPolicy(options),
|
|
60
|
+
});
|
|
38
61
|
if (probe.result.error)
|
|
39
62
|
throw new Error(`Valid tool call was not executed: ${probe.result.error.message}`);
|
|
40
63
|
if (!probe.events.some((event) => event.type === "tool_execution_started")) {
|
|
@@ -69,7 +92,9 @@ export async function dispatchAndCollect(probe) {
|
|
|
69
92
|
permission: probe.permission,
|
|
70
93
|
validate: probe.validate,
|
|
71
94
|
secrets: probe.secrets,
|
|
72
|
-
emit: (event) => {
|
|
95
|
+
emit: (event) => {
|
|
96
|
+
events.push(event);
|
|
97
|
+
},
|
|
73
98
|
});
|
|
74
99
|
return { result, events };
|
|
75
100
|
}
|
package/dist/thinking.js
CHANGED
|
@@ -47,7 +47,10 @@ export function applyThinkingLevel(options, level, family = "reasoning_effort")
|
|
|
47
47
|
if (!normalized || family === "noop")
|
|
48
48
|
return options ?? {};
|
|
49
49
|
const patch = thinkingCompatFor(family, normalized);
|
|
50
|
-
if (family === "openai_reasoning" &&
|
|
50
|
+
if (family === "openai_reasoning" &&
|
|
51
|
+
options?.compat?.reasoning &&
|
|
52
|
+
typeof options.compat.reasoning === "object" &&
|
|
53
|
+
!Array.isArray(options.compat.reasoning)) {
|
|
51
54
|
return mergeProviderRequestOptions(options, {
|
|
52
55
|
compat: {
|
|
53
56
|
reasoning: {
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import type { AgentEvent, ErrorInfo, Guardrails, JsonObject, OwnershipScope, RunLedger, ToolCallContent, ToolDefinition, ToolExecutionContext, ToolRegistry, ToolResult } from "./contracts.js";
|
|
2
|
-
import type { RunLimitTracker } from "./run-limits.js";
|
|
3
2
|
import type { MiddlewareRegistry } from "./middleware.js";
|
|
4
3
|
import { type SecretRedactor } from "./redaction.js";
|
|
5
4
|
import { type DuplicateRegistrationOptions } from "./registry-options.js";
|
|
5
|
+
import type { RunLimitTracker } from "./run-limits.js";
|
|
6
6
|
import { type PermissionPolicy, type TrustPolicy } from "./security.js";
|
|
7
7
|
export interface ToolFilter {
|
|
8
8
|
readonly allow?: readonly string[];
|
|
9
9
|
readonly deny?: readonly string[];
|
|
10
10
|
}
|
|
11
11
|
export type ToolFilterInput = ToolFilter | readonly ToolFilter[];
|
|
12
|
-
export type ToolValidator = (tool: ToolDefinition, args: JsonObject, context: ToolExecutionContext) =>
|
|
12
|
+
export type ToolValidator = (tool: ToolDefinition, args: JsonObject, context: ToolExecutionContext) => undefined | string | ErrorInfo | Promise<undefined | string | ErrorInfo>;
|
|
13
13
|
export interface ToolArgumentValidationError {
|
|
14
14
|
readonly path?: string;
|
|
15
15
|
readonly message: string;
|