@arnilo/prism 0.5.5 → 0.6.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 +46 -0
- package/README.md +10 -10
- package/dist/agent-approval.js +7 -6
- package/dist/agent-loops.js +51 -12
- package/dist/agent-session/session.d.ts +1 -0
- package/dist/agent-session/session.js +20 -2
- package/dist/agent-tool-dispatch.js +5 -4
- package/dist/cli-runner.d.ts +8 -1
- package/dist/cli-runner.js +97 -7
- package/dist/content.d.ts +3 -16
- package/dist/content.js +9 -99
- package/dist/context-budget.d.ts +12 -1
- package/dist/context-budget.js +42 -19
- package/dist/contracts-core/agent.d.ts +11 -0
- package/dist/contracts-core/agent.js +4 -1
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +10 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +4 -4
- package/dist/input.d.ts +6 -0
- package/dist/input.js +12 -1
- package/dist/media-types.d.ts +34 -0
- package/dist/media-types.js +158 -0
- package/dist/pinned-fetch.d.ts +2 -2
- package/dist/pinned-fetch.js +11 -12
- package/dist/redaction.js +74 -1
- package/dist/session-stores.d.ts +11 -0
- package/dist/session-stores.js +23 -8
- package/docs/acp.md +1 -1
- package/docs/ag-ui.md +4 -2
- package/docs/agent-events.md +2 -0
- package/docs/agent-loops.md +1 -1
- package/docs/agent-session-runtime.md +3 -1
- package/docs/browser-automation.md +5 -2
- package/docs/cli-rpc.md +15 -1
- package/docs/contributing.md +37 -0
- package/docs/core.md +2 -0
- package/docs/document-reader.md +2 -0
- package/docs/documents.md +1 -1
- package/docs/extension-authoring.md +8 -9
- package/docs/extensions.md +13 -1
- package/docs/graft.md +29 -5
- package/docs/history/release-handoffs.md +33 -0
- package/docs/host-security.md +2 -2
- package/docs/index.md +33 -17
- package/docs/input-and-prompt-assembly.md +4 -4
- package/docs/language-intelligence.md +1 -1
- package/docs/migrate-to-0.5.md +7 -2
- package/docs/migrate-to-0.6.md +89 -0
- package/docs/migration.md +30 -0
- package/docs/model-registry.md +1 -1
- package/docs/multimodal-content.md +1 -1
- package/docs/obscura.md +3 -1
- package/docs/options-index.md +286 -0
- package/docs/peer-dependencies.md +94 -0
- package/docs/performance.md +34 -2
- package/docs/ponytail.md +2 -0
- package/docs/postgres-persistence.md +3 -1
- package/docs/provider-conformance.md +1 -1
- package/docs/provider-packages.md +21 -21
- package/docs/provider-primitives.md +2 -1
- package/docs/providers/ai-sdk.md +5 -2
- package/docs/public-contracts.md +2 -2
- package/docs/release-and-install.md +75 -55
- package/docs/server.md +1 -1
- package/docs/session-stores.md +3 -1
- package/docs/sqlite-persistence.md +2 -0
- package/docs/testing.md +38 -0
- package/docs/tools.md +1 -1
- package/docs/wiki.md +47 -3
- package/package.json +5 -5
package/dist/content.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2
2
|
import { isIP } from "node:net";
|
|
3
|
+
import { assertSsrfAllowedUrl, isAllowedByCidr, isBlockedIp, MediaContentError, normalizeHostname, } from "./media-types.js";
|
|
3
4
|
import { pinnedFetch } from "./pinned-fetch.js";
|
|
4
5
|
import { assertPermission } from "./security.js";
|
|
6
|
+
// SSRF policy, host/address types, `MediaContentError`, and the URL gate live in the leaf
|
|
7
|
+
// module shared with `pinned-fetch.ts` (plan 070 Task 10); re-exported here so the
|
|
8
|
+
// `@arnilo/prism` surface and the error-class identity are unchanged. `normalizeHostname`
|
|
9
|
+
// is imported for internal use only — it is public through `pinned-fetch.ts`, not here.
|
|
10
|
+
export { assertSsrfAllowedUrl, MediaContentError } from "./media-types.js";
|
|
5
11
|
/** Known model input capability tags for `ModelCapabilities.input`. */
|
|
6
12
|
export const MODEL_INPUT_CAPABILITIES = ["text", "image", "audio", "file", "document", "video"];
|
|
7
13
|
/** Default per-item media byte ceiling (10 MB; aligns with coding-agent image bounds). */
|
|
@@ -26,14 +32,6 @@ export class UnsupportedModalityError extends Error {
|
|
|
26
32
|
this.model = model.model;
|
|
27
33
|
}
|
|
28
34
|
}
|
|
29
|
-
export class MediaContentError extends Error {
|
|
30
|
-
code;
|
|
31
|
-
constructor(code, message, options) {
|
|
32
|
-
super(message, options);
|
|
33
|
-
this.name = "MediaContentError";
|
|
34
|
-
this.code = code;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
35
|
export function contentBlockInputModality(block) {
|
|
38
36
|
switch (block.type) {
|
|
39
37
|
case "image":
|
|
@@ -90,41 +88,6 @@ export function assertMediaBlocksWithinBounds(blocks, bounds = {}) {
|
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
|
-
export function assertSsrfAllowedUrl(url, policy = {}) {
|
|
94
|
-
let parsed;
|
|
95
|
-
try {
|
|
96
|
-
parsed = new URL(url);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
throw new MediaContentError("ssrf_denied", "Media URL is not a valid absolute URL");
|
|
100
|
-
}
|
|
101
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
102
|
-
throw new MediaContentError("unsupported_url_scheme", `Media URL scheme ${parsed.protocol} is not allowed`);
|
|
103
|
-
}
|
|
104
|
-
if (parsed.username || parsed.password) {
|
|
105
|
-
throw new MediaContentError("ssrf_denied", "Media URL must not embed credentials");
|
|
106
|
-
}
|
|
107
|
-
const hostname = normalizeHostname(parsed.hostname);
|
|
108
|
-
if (policy.allowedHostnames?.length) {
|
|
109
|
-
if (!policy.allowedHostnames.some((allowed) => hostname === normalizeHostname(allowed))) {
|
|
110
|
-
throw new MediaContentError("ssrf_denied", `Media URL host ${hostname} is not allow-listed`);
|
|
111
|
-
}
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (policy.denyPrivateHosts === false)
|
|
115
|
-
return;
|
|
116
|
-
if (hostname === "localhost" ||
|
|
117
|
-
hostname.endsWith(".localhost") ||
|
|
118
|
-
hostname.endsWith(".local") ||
|
|
119
|
-
hostname === "metadata" ||
|
|
120
|
-
hostname === "metadata.google.internal" ||
|
|
121
|
-
hostname === "instance-data") {
|
|
122
|
-
throw new MediaContentError("ssrf_denied", `Media URL host ${hostname} is not allowed`);
|
|
123
|
-
}
|
|
124
|
-
if (isBlockedIp(hostname)) {
|
|
125
|
-
throw new MediaContentError("ssrf_denied", `Media URL host ${hostname} is not allowed`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
91
|
export function sniffMediaMimeType(bytes) {
|
|
129
92
|
if (startsWith(bytes, [0x25, 0x50, 0x44, 0x46, 0x2d]))
|
|
130
93
|
return "application/pdf";
|
|
@@ -364,7 +327,9 @@ async function resolvePublicAddress(hostname, resolver, signal, policy) {
|
|
|
364
327
|
if (addresses.length > 32)
|
|
365
328
|
throw new MediaContentError("fetch_failed", "Media hostname resolved to too many addresses");
|
|
366
329
|
if (policy?.denyPrivateHosts !== false && !policy?.allowedHostnames?.length) {
|
|
367
|
-
|
|
330
|
+
// An allow-listed CIDR covers resolved answers too, but only the private-IP block.
|
|
331
|
+
const blocked = addresses.some(({ address }) => isBlockedIp(normalizeHostname(address)) && !isAllowedByCidr(address, policy?.allowedCidrs));
|
|
332
|
+
if (blocked) {
|
|
368
333
|
throw new MediaContentError("ssrf_denied", `Media URL host ${hostname} resolved to a private address`);
|
|
369
334
|
}
|
|
370
335
|
}
|
|
@@ -450,61 +415,6 @@ function mediaTypesCompatible(declared, sniffed) {
|
|
|
450
415
|
return true;
|
|
451
416
|
return false;
|
|
452
417
|
}
|
|
453
|
-
function normalizeHostname(hostname) {
|
|
454
|
-
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
455
|
-
return normalized.endsWith(".") ? normalized.slice(0, -1) : normalized;
|
|
456
|
-
}
|
|
457
|
-
function isBlockedIp(hostname) {
|
|
458
|
-
const normalized = normalizeHostname(hostname);
|
|
459
|
-
const family = isIP(normalized);
|
|
460
|
-
if (family === 4)
|
|
461
|
-
return isBlockedIpv4(normalized);
|
|
462
|
-
if (family === 6)
|
|
463
|
-
return isBlockedIpv6(normalized);
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
function isBlockedIpv4(address) {
|
|
467
|
-
const [a, b] = address.split(".").map(Number);
|
|
468
|
-
return (a === 0 ||
|
|
469
|
-
a === 10 ||
|
|
470
|
-
a === 127 ||
|
|
471
|
-
(a === 100 && b >= 64 && b <= 127) ||
|
|
472
|
-
(a === 169 && b === 254) ||
|
|
473
|
-
(a === 172 && b >= 16 && b <= 31) ||
|
|
474
|
-
(a === 192 && (b === 0 || b === 168)) ||
|
|
475
|
-
(a === 198 && (b === 18 || b === 19 || b === 51)) ||
|
|
476
|
-
(a === 203 && b === 0) ||
|
|
477
|
-
a >= 224);
|
|
478
|
-
}
|
|
479
|
-
function isBlockedIpv6(address) {
|
|
480
|
-
const words = parseIpv6Words(address);
|
|
481
|
-
if (!words)
|
|
482
|
-
return true;
|
|
483
|
-
if (words.every((word) => word === 0) || (words.slice(0, 7).every((word) => word === 0) && words[7] === 1))
|
|
484
|
-
return true;
|
|
485
|
-
if ((words[0] & 0xfe00) === 0xfc00)
|
|
486
|
-
return true;
|
|
487
|
-
if ((words[0] & 0xffc0) === 0xfe80 || (words[0] & 0xffc0) === 0xfec0)
|
|
488
|
-
return true;
|
|
489
|
-
if ((words[0] & 0xff00) === 0xff00)
|
|
490
|
-
return true;
|
|
491
|
-
if (words[0] === 0x2001 && words[1] === 0x0db8)
|
|
492
|
-
return true;
|
|
493
|
-
const mapped = words.slice(0, 5).every((word) => word === 0) && (words[5] === 0 || words[5] === 0xffff);
|
|
494
|
-
return mapped && isBlockedIpv4(`${words[6] >> 8}.${words[6] & 0xff}.${words[7] >> 8}.${words[7] & 0xff}`);
|
|
495
|
-
}
|
|
496
|
-
function parseIpv6Words(address) {
|
|
497
|
-
const parts = address.split("::");
|
|
498
|
-
if (parts.length > 2)
|
|
499
|
-
return undefined;
|
|
500
|
-
const left = parts[0] ? parts[0].split(":") : [];
|
|
501
|
-
const right = parts[1] ? parts[1].split(":") : [];
|
|
502
|
-
const missing = 8 - left.length - right.length;
|
|
503
|
-
if (missing < 0 || (parts.length === 1 && missing !== 0))
|
|
504
|
-
return undefined;
|
|
505
|
-
const words = [...left, ...Array.from({ length: missing }, () => "0"), ...right].map((part) => Number.parseInt(part, 16));
|
|
506
|
-
return words.length === 8 && words.every((word) => Number.isInteger(word) && word >= 0 && word <= 0xffff) ? words : undefined;
|
|
507
|
-
}
|
|
508
418
|
function startsWith(bytes, prefix) {
|
|
509
419
|
if (bytes.length < prefix.length)
|
|
510
420
|
return false;
|
package/dist/context-budget.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import type { ContextBlock, InputAssemblyLayout, Message, ProviderRequest, Skill, ToolDefinition } from "./contracts.js";
|
|
2
2
|
import { type LoadedSkillSet, type SkillsDisclosure } from "./skill-disclosure.js";
|
|
3
|
+
/**
|
|
4
|
+
* Host-supplied token estimator. Budget-only: it never reaches billing, provider
|
|
5
|
+
* usage, or the wire — it decides what the assembler evicts and nothing else.
|
|
6
|
+
*/
|
|
7
|
+
export type TokenEstimator = (text: string) => number;
|
|
3
8
|
/** Assembler-time input budget. At least one max required when present. */
|
|
4
9
|
export interface ContextBudget {
|
|
5
10
|
readonly maxInputTokens?: number;
|
|
6
11
|
readonly maxInputBytes?: number;
|
|
7
12
|
readonly reportOmissions?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Overrides the built-in UTF-16/4 heuristic for eviction accounting. Must return a
|
|
15
|
+
* non-negative finite token count (a NaN/negative/absent return fails the assembly
|
|
16
|
+
* closed with a `TypeError`). Byte caps are estimator-independent and always enforced.
|
|
17
|
+
*/
|
|
18
|
+
readonly tokenEstimator?: TokenEstimator;
|
|
8
19
|
}
|
|
9
20
|
export type ContextBudgetOmissionKind = "skills" | "skill_body" | "context" | "history" | "tool_results" | "summaries" | "attachments" | "tools";
|
|
10
21
|
export interface ContextBudgetOmission {
|
|
@@ -43,7 +54,7 @@ export declare function isContextBudgetError(error: unknown): error is ContextBu
|
|
|
43
54
|
/** UTF-16 code units / 4. Estimate only — not billing. */
|
|
44
55
|
export declare function estimateTextTokens(text: string): number;
|
|
45
56
|
export declare function estimateTextBytes(text: string): number;
|
|
46
|
-
export declare function estimateMessageTokens(message: Message): number;
|
|
57
|
+
export declare function estimateMessageTokens(message: Message, estimateTokens?: TokenEstimator): number;
|
|
47
58
|
export declare function estimateMessageBytes(message: Message): number;
|
|
48
59
|
export declare function estimateAssemblyTokens(messages: readonly Message[]): number;
|
|
49
60
|
export declare function resolveContextBudget(budget: ContextBudget): Required<Pick<ContextBudget, "reportOmissions">> & ContextBudget;
|
package/dist/context-budget.js
CHANGED
|
@@ -22,8 +22,8 @@ export function estimateTextTokens(text) {
|
|
|
22
22
|
export function estimateTextBytes(text) {
|
|
23
23
|
return Buffer.byteLength(text, "utf8");
|
|
24
24
|
}
|
|
25
|
-
export function estimateMessageTokens(message) {
|
|
26
|
-
return
|
|
25
|
+
export function estimateMessageTokens(message, estimateTokens = estimateTextTokens) {
|
|
26
|
+
return estimateTokens(messageText(message));
|
|
27
27
|
}
|
|
28
28
|
export function estimateMessageBytes(message) {
|
|
29
29
|
return estimateTextBytes(messageText(message));
|
|
@@ -41,6 +41,9 @@ export function resolveContextBudget(budget) {
|
|
|
41
41
|
assertPositiveCap(budget.maxInputTokens, "maxInputTokens", HARD_MAX_CONTEXT_BUDGET_TOKENS);
|
|
42
42
|
if (hasBytes)
|
|
43
43
|
assertPositiveCap(budget.maxInputBytes, "maxInputBytes", HARD_MAX_CONTEXT_BUDGET_BYTES);
|
|
44
|
+
if (budget.tokenEstimator !== undefined && typeof budget.tokenEstimator !== "function") {
|
|
45
|
+
throw new TypeError("contextBudget.tokenEstimator must be a function");
|
|
46
|
+
}
|
|
44
47
|
return { ...budget, reportOmissions: budget.reportOmissions === true };
|
|
45
48
|
}
|
|
46
49
|
export function getContextBudgetReport(request) {
|
|
@@ -49,6 +52,7 @@ export function getContextBudgetReport(request) {
|
|
|
49
52
|
}
|
|
50
53
|
export function applyContextBudget(options) {
|
|
51
54
|
const budget = resolveContextBudget(options.budget);
|
|
55
|
+
const estimateTokens = resolveTokenEstimator(budget);
|
|
52
56
|
const layout = options.layout ?? "cache_aware";
|
|
53
57
|
const groups = {
|
|
54
58
|
instructions: [...options.groups.instructions],
|
|
@@ -67,9 +71,9 @@ export function applyContextBudget(options) {
|
|
|
67
71
|
const historyCursor = { index: 0 };
|
|
68
72
|
// Measure once, then subtract each dropped item's own estimate (dropNext computes it
|
|
69
73
|
// with the same estimators) — avoids an O(n²) re-scan of the full keep-set per drop.
|
|
70
|
-
const kept = measureAll(groups, context, skills, tools, skillContext, demotedBodies);
|
|
74
|
+
const kept = measureAll(groups, context, skills, tools, skillContext, demotedBodies, estimateTokens);
|
|
71
75
|
while (overBudget(kept, budget)) {
|
|
72
|
-
const drop = dropNext(groups, context, skills, layout, skillContext, demotedBodies, historyCursor);
|
|
76
|
+
const drop = dropNext(groups, context, skills, layout, skillContext, demotedBodies, historyCursor, estimateTokens);
|
|
73
77
|
if (!drop) {
|
|
74
78
|
throw new ContextBudgetError();
|
|
75
79
|
}
|
|
@@ -97,7 +101,7 @@ export function applyContextBudget(options) {
|
|
|
97
101
|
},
|
|
98
102
|
};
|
|
99
103
|
}
|
|
100
|
-
function dropNext(groups, context, skills, layout, skillContext, demotedBodies, historyCursor) {
|
|
104
|
+
function dropNext(groups, context, skills, layout, skillContext, demotedBodies, historyCursor, estimateTokens) {
|
|
101
105
|
// ponytail: drop droppable groups in layout order; within history, advance a cursor and slice once.
|
|
102
106
|
// cache_aware keeps attachments longer so stable prefix stays intact while budget still allows it.
|
|
103
107
|
const order = layout === "cache_aware"
|
|
@@ -106,19 +110,19 @@ function dropNext(groups, context, skills, layout, skillContext, demotedBodies,
|
|
|
106
110
|
for (const kind of order) {
|
|
107
111
|
if (kind === "tool_results" && groups.toolResults.length > 0) {
|
|
108
112
|
const message = groups.toolResults.pop();
|
|
109
|
-
return omission("tool_results", message.id ?? toolResultId(message), message);
|
|
113
|
+
return omission("tool_results", message.id ?? toolResultId(message), message, estimateTokens);
|
|
110
114
|
}
|
|
111
115
|
if (kind === "history" && historyCursor.index < groups.history.length) {
|
|
112
116
|
const message = groups.history[historyCursor.index++];
|
|
113
|
-
return omission("history", message.id, message);
|
|
117
|
+
return omission("history", message.id, message, estimateTokens);
|
|
114
118
|
}
|
|
115
119
|
if (kind === "summaries" && groups.summaries.length > 0) {
|
|
116
120
|
const message = groups.summaries.pop();
|
|
117
|
-
return omission("summaries", message.id, message);
|
|
121
|
+
return omission("summaries", message.id, message, estimateTokens);
|
|
118
122
|
}
|
|
119
123
|
if (kind === "attachments" && groups.attachments.length > 0) {
|
|
120
124
|
const message = groups.attachments.pop();
|
|
121
|
-
return omission("attachments", message.id, message);
|
|
125
|
+
return omission("attachments", message.id, message, estimateTokens);
|
|
122
126
|
}
|
|
123
127
|
if (kind === "context" && context.length > 0) {
|
|
124
128
|
const index = pickVictimIndex(context, (block) => block.priority ?? 0);
|
|
@@ -127,7 +131,7 @@ function dropNext(groups, context, skills, layout, skillContext, demotedBodies,
|
|
|
127
131
|
return {
|
|
128
132
|
kind: "context",
|
|
129
133
|
id: block.id ?? block.title,
|
|
130
|
-
tokenEstimate:
|
|
134
|
+
tokenEstimate: estimateTokens(text),
|
|
131
135
|
byteLength: estimateTextBytes(text),
|
|
132
136
|
};
|
|
133
137
|
}
|
|
@@ -142,7 +146,7 @@ function dropNext(groups, context, skills, layout, skillContext, demotedBodies,
|
|
|
142
146
|
return {
|
|
143
147
|
kind: "skill_body",
|
|
144
148
|
id: skill.name,
|
|
145
|
-
tokenEstimate:
|
|
149
|
+
tokenEstimate: estimateTokens(beforeText) - estimateTokens(afterText),
|
|
146
150
|
byteLength: estimateTextBytes(beforeText) - estimateTextBytes(afterText),
|
|
147
151
|
};
|
|
148
152
|
}
|
|
@@ -152,31 +156,50 @@ function dropNext(groups, context, skills, layout, skillContext, demotedBodies,
|
|
|
152
156
|
return {
|
|
153
157
|
kind: "skills",
|
|
154
158
|
id: skill.name,
|
|
155
|
-
tokenEstimate:
|
|
159
|
+
tokenEstimate: estimateTokens(text),
|
|
156
160
|
byteLength: estimateTextBytes(text),
|
|
157
161
|
};
|
|
158
162
|
}
|
|
159
163
|
}
|
|
160
164
|
return undefined;
|
|
161
165
|
}
|
|
162
|
-
function omission(kind, id, message) {
|
|
166
|
+
function omission(kind, id, message, estimateTokens) {
|
|
163
167
|
return {
|
|
164
168
|
kind,
|
|
165
169
|
id,
|
|
166
|
-
tokenEstimate: estimateMessageTokens(message),
|
|
170
|
+
tokenEstimate: estimateMessageTokens(message, estimateTokens),
|
|
167
171
|
byteLength: estimateMessageBytes(message),
|
|
168
172
|
};
|
|
169
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Resolves the budget's estimator, validating each return value: a host estimator that
|
|
176
|
+
* yields NaN/negative/non-finite tokens would make every eviction decision unsound, so it
|
|
177
|
+
* fails the assembly closed instead of silently keeping or dropping the wrong content.
|
|
178
|
+
*/
|
|
179
|
+
function resolveTokenEstimator(budget) {
|
|
180
|
+
const estimator = budget.tokenEstimator;
|
|
181
|
+
if (estimator === undefined)
|
|
182
|
+
return estimateTextTokens;
|
|
183
|
+
if (typeof estimator !== "function")
|
|
184
|
+
throw new TypeError("contextBudget.tokenEstimator must be a function");
|
|
185
|
+
return (text) => {
|
|
186
|
+
const tokens = estimator(text);
|
|
187
|
+
if (typeof tokens !== "number" || !Number.isFinite(tokens) || tokens < 0) {
|
|
188
|
+
throw new TypeError("contextBudget.tokenEstimator must return a non-negative finite number of tokens");
|
|
189
|
+
}
|
|
190
|
+
return tokens;
|
|
191
|
+
};
|
|
192
|
+
}
|
|
170
193
|
function toolResultId(message) {
|
|
171
194
|
const block = message.content.find((part) => part.type === "tool_result");
|
|
172
195
|
return block && block.type === "tool_result" ? block.toolCallId : undefined;
|
|
173
196
|
}
|
|
174
|
-
function measureAll(groups, context, skills, tools, skillContext, demotedBodies) {
|
|
197
|
+
function measureAll(groups, context, skills, tools, skillContext, demotedBodies, estimateTokens) {
|
|
175
198
|
const renderContext = withDemoted(skillContext, demotedBodies);
|
|
176
199
|
let tokens = 0;
|
|
177
200
|
let bytes = 0;
|
|
178
201
|
const addMessage = (message) => {
|
|
179
|
-
tokens += estimateMessageTokens(message);
|
|
202
|
+
tokens += estimateMessageTokens(message, estimateTokens);
|
|
180
203
|
bytes += estimateMessageBytes(message);
|
|
181
204
|
};
|
|
182
205
|
for (const message of groups.instructions)
|
|
@@ -193,17 +216,17 @@ function measureAll(groups, context, skills, tools, skillContext, demotedBodies)
|
|
|
193
216
|
addMessage(message);
|
|
194
217
|
for (const block of context) {
|
|
195
218
|
const text = `${block.title ? `${block.title}:\n` : "Context:\n"}${contextBlockText(block)}`;
|
|
196
|
-
tokens +=
|
|
219
|
+
tokens += estimateTokens(text);
|
|
197
220
|
bytes += estimateTextBytes(text);
|
|
198
221
|
}
|
|
199
222
|
for (const skill of skills) {
|
|
200
223
|
const text = skillPromptText(skill, renderContext) ?? "";
|
|
201
|
-
tokens +=
|
|
224
|
+
tokens += estimateTokens(text);
|
|
202
225
|
bytes += estimateTextBytes(text);
|
|
203
226
|
}
|
|
204
227
|
if (tools?.length) {
|
|
205
228
|
const text = `Available tools:\n${tools.map((tool) => `- ${tool.name}${tool.description ? `: ${tool.description}` : ""}`).join("\n")}`;
|
|
206
|
-
tokens +=
|
|
229
|
+
tokens += estimateTokens(text);
|
|
207
230
|
bytes += estimateTextBytes(text);
|
|
208
231
|
}
|
|
209
232
|
return { tokens, bytes };
|
|
@@ -127,7 +127,18 @@ export interface AgentSessionConfig {
|
|
|
127
127
|
readonly store?: SessionStore;
|
|
128
128
|
readonly leafId?: string;
|
|
129
129
|
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
130
|
+
/**
|
|
131
|
+
* TTL of the in-memory `session.snapshot()` branch cache in milliseconds.
|
|
132
|
+
* Default `DEFAULT_SNAPSHOT_CACHE_TTL_MS`; `0` disables the cache (every snapshot read
|
|
133
|
+
* rebuilds from the store); at most `HARD_MAX_SNAPSHOT_CACHE_TTL_MS`. The cache is always
|
|
134
|
+
* invalidated by a new leaf or a mutation, so the TTL only bounds staleness-free reuse.
|
|
135
|
+
*/
|
|
136
|
+
readonly snapshotCacheTtlMs?: number;
|
|
130
137
|
}
|
|
138
|
+
/** Default `session.snapshot()` branch-cache TTL (milliseconds). */
|
|
139
|
+
export declare const DEFAULT_SNAPSHOT_CACHE_TTL_MS = 1000;
|
|
140
|
+
/** Upper bound for `AgentSessionConfig.snapshotCacheTtlMs` (milliseconds). */
|
|
141
|
+
export declare const HARD_MAX_SNAPSHOT_CACHE_TTL_MS = 30000;
|
|
131
142
|
export interface AgentSessionForkOptions {
|
|
132
143
|
readonly leafId?: string;
|
|
133
144
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/** Contracts-core agent family (0.2.5 plan 025 Task 1 split).
|
|
2
2
|
* Moved verbatim from contracts-core.ts; public surface unchanged behind the barrel. */
|
|
3
|
-
|
|
3
|
+
/** Default `session.snapshot()` branch-cache TTL (milliseconds). */
|
|
4
|
+
export const DEFAULT_SNAPSHOT_CACHE_TTL_MS = 1_000;
|
|
5
|
+
/** Upper bound for `AgentSessionConfig.snapshotCacheTtlMs` (milliseconds). */
|
|
6
|
+
export const HARD_MAX_SNAPSHOT_CACHE_TTL_MS = 30_000;
|
|
4
7
|
/** Directory-name spelling for discovered contribution kinds. Maps to a
|
|
5
8
|
* {@link ManifestContributionDeclaration} kind for non-skill kinds:
|
|
6
9
|
* `context` → `contextProvider`, `instructions` → `systemPromptContribution`. */
|
package/dist/extensions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Extension, ExtensionEvent, ExtensionLifecycleEventName } from "./contracts.js";
|
|
1
|
+
import type { CommandDefinition, ContextProvider, Extension, ExtensionEvent, ExtensionLifecycleEventName, InstructionInjector, Skill, ToolDefinition } from "./contracts.js";
|
|
2
2
|
import { type ContributionRegistries } from "./contributions.js";
|
|
3
3
|
import { type MiddlewareRegistry } from "./middleware.js";
|
|
4
4
|
import { type PermissionPolicy } from "./security.js";
|
|
@@ -40,3 +40,20 @@ export interface ExtensionKernel {
|
|
|
40
40
|
}
|
|
41
41
|
export declare function createExtensionEventBus(options?: Pick<ExtensionKernelOptions, "errorPolicy" | "secrets">): ExtensionEventBus;
|
|
42
42
|
export declare function createExtensionKernel(options?: ExtensionKernelOptions): ExtensionKernel;
|
|
43
|
+
/** Host-owned activation: copy contributed entries into the `createAgent()`
|
|
44
|
+
* fields that accept plain arrays. Contributions stay inert until the host
|
|
45
|
+
* passes the returned fields into runtime config. Array slots only —
|
|
46
|
+
* single-slot builders (`inputBuilder`/`promptBuilder`), `compaction`,
|
|
47
|
+
* `retry`, provider/model selection, and skill activation remain host-owned
|
|
48
|
+
* decisions; `commands` are for host RPC surfaces, not an `AgentConfig` field. */
|
|
49
|
+
export interface ActivatedKernelConfig {
|
|
50
|
+
readonly tools: readonly ToolDefinition[];
|
|
51
|
+
readonly skills: readonly Skill[];
|
|
52
|
+
readonly instructionInjectors: readonly InstructionInjector[];
|
|
53
|
+
readonly context: readonly ContextProvider[];
|
|
54
|
+
/** For host command surfaces (CLI/RPC/UI); not part of `AgentConfig`. */
|
|
55
|
+
readonly commands: readonly CommandDefinition[];
|
|
56
|
+
/** The kernel middleware registry itself; runs only when passed to runtime config. */
|
|
57
|
+
readonly middleware: MiddlewareRegistry;
|
|
58
|
+
}
|
|
59
|
+
export declare function activateKernel(kernel: ExtensionKernel): ActivatedKernelConfig;
|
package/dist/extensions.js
CHANGED
|
@@ -190,6 +190,16 @@ export function createExtensionKernel(options = {}) {
|
|
|
190
190
|
},
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
|
+
export function activateKernel(kernel) {
|
|
194
|
+
return {
|
|
195
|
+
tools: kernel.registries.tools.list(),
|
|
196
|
+
skills: kernel.registries.skills.list(),
|
|
197
|
+
instructionInjectors: kernel.registries.instructionInjectors.list(),
|
|
198
|
+
context: kernel.registries.contextProviders.list(),
|
|
199
|
+
commands: kernel.registries.commands.list(),
|
|
200
|
+
middleware: kernel.middleware,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
193
203
|
async function assertExtensionLoadPolicy(policy, extension) {
|
|
194
204
|
if (!policy)
|
|
195
205
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,11 +23,11 @@ export type { ConfigLayer, ConfigLoadContext, ConfigProvider } from "./config.js
|
|
|
23
23
|
export { assertJsonObject, isJsonObject, loadConfigLayers, mergeConfigLayers } from "./config.js";
|
|
24
24
|
export type { AudioContent, DocumentContent, FileContent, MediaContentBlock, MediaContentBounds, MediaHostAddress, MediaHostnameResolver, MediaMimePolicy, MediaUrlRequest, MediaUrlRequester, ModelInputCapability, ResolvedMediaContent, ResolveMediaContentOptions, SsrfPolicy, } from "./content.js";
|
|
25
25
|
export { assertDeclaredMediaTypeMatches, assertMediaBlocksWithinBounds, assertMessagesSupportModelCapabilities, assertModelSupportsContentBlocks, assertSsrfAllowedUrl, collectMessageContentBlocks, contentBlockInputModality, DEFAULT_MAX_AUDIO_DURATION_MS, DEFAULT_MAX_MEDIA_ITEM_BYTES, DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST, DEFAULT_MAX_MEDIA_REQUEST_BYTES, DEFAULT_MEDIA_FETCH_TIMEOUT_MS, loadBoundedBinaryResource, MediaContentError, MODEL_INPUT_CAPABILITIES, resolveMediaContentBlock, resolveMediaContentBlocks, sniffMediaMimeType, UnsupportedModalityError, } from "./content.js";
|
|
26
|
-
export type { ContextBudget, ContextBudgetMessageGroups, ContextBudgetOmission, ContextBudgetOmissionKind, ContextBudgetReport, } from "./context-budget.js";
|
|
26
|
+
export type { ContextBudget, ContextBudgetMessageGroups, ContextBudgetOmission, ContextBudgetOmissionKind, ContextBudgetReport, TokenEstimator, } from "./context-budget.js";
|
|
27
27
|
export { applyContextBudget, CONTEXT_BUDGET_ERROR_CODE, CONTEXT_BUDGET_REPORT_METADATA_KEY, ContextBudgetError, DEFAULT_MAX_CONTEXT_BUDGET_OMISSIONS, estimateAssemblyTokens, estimateMessageBytes, estimateMessageTokens, estimateTextBytes, estimateTextTokens, getContextBudgetReport, HARD_MAX_CONTEXT_BUDGET_BYTES, HARD_MAX_CONTEXT_BUDGET_OMISSIONS, HARD_MAX_CONTEXT_BUDGET_TOKENS, isContextBudgetError, resolveContextBudget, } from "./context-budget.js";
|
|
28
28
|
export type * from "./contracts.js";
|
|
29
29
|
export type { ApprovalOutcome, DecisionScope, NestedRunApproval, NestedRunOutcome, NestedRunRef, PendingDecision, PendingDecisionKind, ProviderResolver, RealtimeCaps, RealtimeEvent, RealtimeSession, RealtimeSessionFactory, RealtimeSessionOptions, ResumeNestedRun, RunDecision, RunLimitCounters, RunLimitName, SecureAgentOptions, StickyDecision, ToolCallAuthority, ToolEffectClassifier, ToolEffectDeclaration, ToolEffectIdempotency, ToolEffectKey, ToolEffectKind, ToolEffectRecord, ToolEffectStatus, ToolEffectStore, ToolEffectTransition, ToolElicitationRequest, } from "./contracts.js";
|
|
30
|
-
export { AgentDecisionError, AgentDelegationSuspendedError, AgentLoopStateError, AgentRunError, AgentRunStateError, assertBatchJobsSupported, assertEmbeddingsSupported, assertImageGenerationSupported, assertModerationSupported, assertSessionMetadataKey, assertSpeechSupported, assertTranscriptionSupported, assertVideoGenerationSupported, BatchJobsError, DEFAULT_MAX_PENDING_DECISIONS, DEFAULT_MAX_PENDING_STEER_BYTES, DEFAULT_MAX_PENDING_STEERS, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_STICKY_DECISIONS, DEFAULT_SESSION_SEARCH_LIMIT, EmbeddingsError, HARD_MAX_ACTION_CONSTRAINT_BYTES, HARD_MAX_ACTION_CONSTRAINTS, HARD_MAX_DECISION_REASON_BYTES, HARD_MAX_ELICITATION_BYTES, HARD_MAX_PENDING_DECISIONS, HARD_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEERS, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_STICKY_DECISIONS, ImageGenerationError, isBatchJobTerminal, isSessionAppendConflict, isSessionEntryKind, isSessionMetadataConflict, isSessionSearchUnsupported, MAX_ACTION_CONSTRAINT_BYTES, MAX_ACTION_CONSTRAINTS, MAX_ATTRIBUTION_DEPTH, MAX_DECISION_REASON_BYTES, MAX_ELICITATION_BYTES, ModerationError, modelSupportsBatchJobs, modelSupportsEmbeddings, modelSupportsImageGeneration, modelSupportsModeration, modelSupportsSpeech, modelSupportsTranscription, modelSupportsVideoGeneration, pollBatch, resolveSessionSearchQuery, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SESSION_METADATA_CONFLICT_CODE, SESSION_SEARCH_UNSUPPORTED_CODE, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SessionAppendConflictError, SessionMetadataConflictError, SessionSearchUnsupportedError, SpeechError, TranscriptionError, VideoGenerationError, } from "./contracts.js";
|
|
30
|
+
export { AgentDecisionError, AgentDelegationSuspendedError, AgentLoopStateError, AgentRunError, AgentRunStateError, assertBatchJobsSupported, assertEmbeddingsSupported, assertImageGenerationSupported, assertModerationSupported, assertSessionMetadataKey, assertSpeechSupported, assertTranscriptionSupported, assertVideoGenerationSupported, BatchJobsError, DEFAULT_MAX_PENDING_DECISIONS, DEFAULT_MAX_PENDING_STEER_BYTES, DEFAULT_MAX_PENDING_STEERS, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_STICKY_DECISIONS, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_SNAPSHOT_CACHE_TTL_MS, EmbeddingsError, HARD_MAX_ACTION_CONSTRAINT_BYTES, HARD_MAX_ACTION_CONSTRAINTS, HARD_MAX_DECISION_REASON_BYTES, HARD_MAX_ELICITATION_BYTES, HARD_MAX_PENDING_DECISIONS, HARD_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEERS, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_SNAPSHOT_CACHE_TTL_MS, HARD_MAX_STICKY_DECISIONS, ImageGenerationError, isBatchJobTerminal, isSessionAppendConflict, isSessionEntryKind, isSessionMetadataConflict, isSessionSearchUnsupported, MAX_ACTION_CONSTRAINT_BYTES, MAX_ACTION_CONSTRAINTS, MAX_ATTRIBUTION_DEPTH, MAX_DECISION_REASON_BYTES, MAX_ELICITATION_BYTES, ModerationError, modelSupportsBatchJobs, modelSupportsEmbeddings, modelSupportsImageGeneration, modelSupportsModeration, modelSupportsSpeech, modelSupportsTranscription, modelSupportsVideoGeneration, pollBatch, resolveSessionSearchQuery, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SESSION_METADATA_CONFLICT_CODE, SESSION_SEARCH_UNSUPPORTED_CODE, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SessionAppendConflictError, SessionMetadataConflictError, SessionSearchUnsupportedError, SpeechError, TranscriptionError, VideoGenerationError, } from "./contracts.js";
|
|
31
31
|
export { parseAgentFile, parseSkillFile } from "./contribution-parsing.js";
|
|
32
32
|
export type { ContributionRegistries, ContributionRegistriesOptions, ContributionRegistry, ContributionRegistryOptions, } from "./contributions.js";
|
|
33
33
|
export { createContributionRegistries, createContributionRegistry, registerDiscoveredContributions } from "./contributions.js";
|
|
@@ -43,8 +43,8 @@ export type { EventMultiplexer, EventMultiplexerOptions, EventOverflowInfo, Even
|
|
|
43
43
|
export { createEventMultiplexer, EVENT_MULTIPLEXER_SINGLE_CONSUMER_CODE, EventMultiplexerError } from "./event-multiplexer.js";
|
|
44
44
|
export type { ExecutionAction, ExecutionDecision, ExecutionPolicy, ExecutionRisk } from "./execution-policy.js";
|
|
45
45
|
export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
|
|
46
|
-
export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions, ExtensionLoadPolicy, LoadedExtension, } from "./extensions.js";
|
|
47
|
-
export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
46
|
+
export type { ActivatedKernelConfig, ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions, ExtensionLoadPolicy, LoadedExtension, } from "./extensions.js";
|
|
47
|
+
export { activateKernel, createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
48
48
|
export type { MemoryRunFeedbackStoreOptions, PrepareRunFeedbackOptions, RunFeedbackLimits, RunFeedbackRun, RunFeedbackRunResolver, } from "./feedback.js";
|
|
49
49
|
export { createMemoryRunFeedbackStore, prepareRunFeedback, RunFeedbackError, requireRunFeedbackOwnership, runFeedbackPageLimit, } from "./feedback.js";
|
|
50
50
|
export type { ApplyFieldPolicyOptions, AuditFieldRedaction, AuditFieldRedactorLike, AuditFieldRedactorOptions, FieldPolicy, FieldPolicyAction, FieldPolicyDecision, FieldPolicyInput, ProtectedFieldPolicyOptions, } from "./field-policy.js";
|
|
@@ -54,7 +54,7 @@ export { assertGuardrailsAllowed, GuardrailError, MAX_GUARDRAIL_CONCURRENCY, run
|
|
|
54
54
|
export type { AgentIdentity, AssertIdentityActiveOptions, IdentityLimits, IdentityVerifier, NarrowIdentityOptions, Principal, ResolvedIdentityLimits, } from "./identity.js";
|
|
55
55
|
export { assertIdentityActive, assertIdentityMatchesOwnership, assertIdentityPropagation, DEFAULT_IDENTITY_LIMITS, HARD_IDENTITY_LIMITS, IdentityError, identityTelemetryAttributes, narrowIdentity, ownershipFromIdentity, resolveIdentityLimits, resolveRunIdentity, } from "./identity.js";
|
|
56
56
|
export type { AgentInput, AssembleProviderInputOptions, DefaultInputBuildContext, DefaultInputBuilder, DefaultPromptBuilder, InputAttachment, PromptInstruction, PromptTemplateOptions, ResolveContextOptions, } from "./input.js";
|
|
57
|
-
export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBuilder, renderPromptTemplate, resolveContextProviders, } from "./input.js";
|
|
57
|
+
export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBuilder, EMPTY_TOOL_RESULT_TEXT, renderPromptTemplate, resolveContextProviders, } from "./input.js";
|
|
58
58
|
export type { ResolveInstructionInjectorsOptions } from "./instruction-injection.js";
|
|
59
59
|
export { resolveInstructionInjectors, runInstructionInjectors } from "./instruction-injection.js";
|
|
60
60
|
export { createMemoryLeaseStore, LEASE_CONFLICT_CODE, LeaseConflictError } from "./leases.js";
|
|
@@ -119,5 +119,5 @@ export { trimTrailingSlashes } from "./trim-trailing-slashes.js";
|
|
|
119
119
|
export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
|
|
120
120
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
121
121
|
export declare const name = "prism";
|
|
122
|
-
export declare const version = "0.
|
|
122
|
+
export declare const version = "0.6.0";
|
|
123
123
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ export { createDefaultCompactionStrategy, isCompactionEntryData } from "./compac
|
|
|
13
13
|
export { assertJsonObject, isJsonObject, loadConfigLayers, mergeConfigLayers } from "./config.js";
|
|
14
14
|
export { assertDeclaredMediaTypeMatches, assertMediaBlocksWithinBounds, assertMessagesSupportModelCapabilities, assertModelSupportsContentBlocks, assertSsrfAllowedUrl, collectMessageContentBlocks, contentBlockInputModality, DEFAULT_MAX_AUDIO_DURATION_MS, DEFAULT_MAX_MEDIA_ITEM_BYTES, DEFAULT_MAX_MEDIA_ITEMS_PER_REQUEST, DEFAULT_MAX_MEDIA_REQUEST_BYTES, DEFAULT_MEDIA_FETCH_TIMEOUT_MS, loadBoundedBinaryResource, MediaContentError, MODEL_INPUT_CAPABILITIES, resolveMediaContentBlock, resolveMediaContentBlocks, sniffMediaMimeType, UnsupportedModalityError, } from "./content.js";
|
|
15
15
|
export { applyContextBudget, CONTEXT_BUDGET_ERROR_CODE, CONTEXT_BUDGET_REPORT_METADATA_KEY, ContextBudgetError, DEFAULT_MAX_CONTEXT_BUDGET_OMISSIONS, estimateAssemblyTokens, estimateMessageBytes, estimateMessageTokens, estimateTextBytes, estimateTextTokens, getContextBudgetReport, HARD_MAX_CONTEXT_BUDGET_BYTES, HARD_MAX_CONTEXT_BUDGET_OMISSIONS, HARD_MAX_CONTEXT_BUDGET_TOKENS, isContextBudgetError, resolveContextBudget, } from "./context-budget.js";
|
|
16
|
-
export { AgentDecisionError, AgentDelegationSuspendedError, AgentLoopStateError, AgentRunError, AgentRunStateError, assertBatchJobsSupported, assertEmbeddingsSupported, assertImageGenerationSupported, assertModerationSupported, assertSessionMetadataKey, assertSpeechSupported, assertTranscriptionSupported, assertVideoGenerationSupported, BatchJobsError, DEFAULT_MAX_PENDING_DECISIONS, DEFAULT_MAX_PENDING_STEER_BYTES, DEFAULT_MAX_PENDING_STEERS, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_STICKY_DECISIONS, DEFAULT_SESSION_SEARCH_LIMIT, EmbeddingsError, HARD_MAX_ACTION_CONSTRAINT_BYTES, HARD_MAX_ACTION_CONSTRAINTS, HARD_MAX_DECISION_REASON_BYTES, HARD_MAX_ELICITATION_BYTES, HARD_MAX_PENDING_DECISIONS, HARD_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEERS, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_STICKY_DECISIONS, ImageGenerationError, isBatchJobTerminal, isSessionAppendConflict, isSessionEntryKind, isSessionMetadataConflict, isSessionSearchUnsupported, MAX_ACTION_CONSTRAINT_BYTES, MAX_ACTION_CONSTRAINTS, MAX_ATTRIBUTION_DEPTH, MAX_DECISION_REASON_BYTES, MAX_ELICITATION_BYTES, ModerationError, modelSupportsBatchJobs, modelSupportsEmbeddings, modelSupportsImageGeneration, modelSupportsModeration, modelSupportsSpeech, modelSupportsTranscription, modelSupportsVideoGeneration, pollBatch, resolveSessionSearchQuery, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SESSION_METADATA_CONFLICT_CODE, SESSION_SEARCH_UNSUPPORTED_CODE, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SessionAppendConflictError, SessionMetadataConflictError, SessionSearchUnsupportedError, SpeechError, TranscriptionError, VideoGenerationError, } from "./contracts.js";
|
|
16
|
+
export { AgentDecisionError, AgentDelegationSuspendedError, AgentLoopStateError, AgentRunError, AgentRunStateError, assertBatchJobsSupported, assertEmbeddingsSupported, assertImageGenerationSupported, assertModerationSupported, assertSessionMetadataKey, assertSpeechSupported, assertTranscriptionSupported, assertVideoGenerationSupported, BatchJobsError, DEFAULT_MAX_PENDING_DECISIONS, DEFAULT_MAX_PENDING_STEER_BYTES, DEFAULT_MAX_PENDING_STEERS, DEFAULT_MAX_SESSION_SEARCH_CURSOR_BYTES, DEFAULT_MAX_SESSION_SEARCH_FTS_CANDIDATES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_BYTES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_ENTRIES, DEFAULT_MAX_SESSION_SEARCH_LINEAR_SESSIONS, DEFAULT_MAX_SESSION_SEARCH_QUERY_BYTES, DEFAULT_MAX_SESSION_SEARCH_SNIPPET_BYTES, DEFAULT_MAX_STICKY_DECISIONS, DEFAULT_SESSION_SEARCH_LIMIT, DEFAULT_SNAPSHOT_CACHE_TTL_MS, EmbeddingsError, HARD_MAX_ACTION_CONSTRAINT_BYTES, HARD_MAX_ACTION_CONSTRAINTS, HARD_MAX_DECISION_REASON_BYTES, HARD_MAX_ELICITATION_BYTES, HARD_MAX_PENDING_DECISIONS, HARD_MAX_PENDING_STEER_BYTES, HARD_MAX_PENDING_STEERS, HARD_MAX_SESSION_SEARCH_CURSOR_BYTES, HARD_MAX_SESSION_SEARCH_FTS_CANDIDATES, HARD_MAX_SESSION_SEARCH_LIMIT, HARD_MAX_SESSION_SEARCH_LINEAR_BYTES, HARD_MAX_SESSION_SEARCH_LINEAR_ENTRIES, HARD_MAX_SESSION_SEARCH_LINEAR_SESSIONS, HARD_MAX_SESSION_SEARCH_QUERY_BYTES, HARD_MAX_SESSION_SEARCH_SNIPPET_BYTES, HARD_MAX_SNAPSHOT_CACHE_TTL_MS, HARD_MAX_STICKY_DECISIONS, ImageGenerationError, isBatchJobTerminal, isSessionAppendConflict, isSessionEntryKind, isSessionMetadataConflict, isSessionSearchUnsupported, MAX_ACTION_CONSTRAINT_BYTES, MAX_ACTION_CONSTRAINTS, MAX_ATTRIBUTION_DEPTH, MAX_DECISION_REASON_BYTES, MAX_ELICITATION_BYTES, ModerationError, modelSupportsBatchJobs, modelSupportsEmbeddings, modelSupportsImageGeneration, modelSupportsModeration, modelSupportsSpeech, modelSupportsTranscription, modelSupportsVideoGeneration, pollBatch, resolveSessionSearchQuery, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_KINDS, SESSION_ENTRY_SCHEMA_VERSION, SESSION_METADATA_CONFLICT_CODE, SESSION_SEARCH_UNSUPPORTED_CODE, SESSION_SEARCH_WORKSPACE_METADATA_KEY, SessionAppendConflictError, SessionMetadataConflictError, SessionSearchUnsupportedError, SpeechError, TranscriptionError, VideoGenerationError, } from "./contracts.js";
|
|
17
17
|
export { parseAgentFile, parseSkillFile } from "./contribution-parsing.js";
|
|
18
18
|
export { createContributionRegistries, createContributionRegistry, registerDiscoveredContributions } from "./contributions.js";
|
|
19
19
|
export { CONVERSATION_METADATA_KEY, ConversationError, conversationMarkerMetadata, conversationThreadFromRecord, DEFAULT_MAX_CONVERSATION_CURSOR_BYTES, decodeConversationReplayCursor, encodeConversationReplayCursor, HARD_MAX_CONVERSATION_CURSOR_BYTES, } from "./conversations.js";
|
|
@@ -22,12 +22,12 @@ export { createDelegatedAgentStep, DelegatedAgentStepError, MAX_DELEGATED_AGENT_
|
|
|
22
22
|
export { acceptDeviceChunk, assertDeviceAdmit, DEFAULT_DEVICE_MAX_CHUNK_BYTES, DEFAULT_DEVICE_MAX_CONCURRENT_SESSIONS, DevicePolicyError, HARD_DEVICE_MAX_CHUNK_BYTES, HARD_DEVICE_MAX_CONCURRENT_SESSIONS, redactDeviceTelemetry, resolveDevicePolicy, runDevicePolicyConformance, } from "./devices.js";
|
|
23
23
|
export { createEventMultiplexer, EVENT_MULTIPLEXER_SINGLE_CONSUMER_CODE, EventMultiplexerError } from "./event-multiplexer.js";
|
|
24
24
|
export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
|
|
25
|
-
export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
25
|
+
export { activateKernel, createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
26
26
|
export { createMemoryRunFeedbackStore, prepareRunFeedback, RunFeedbackError, requireRunFeedbackOwnership, runFeedbackPageLimit, } from "./feedback.js";
|
|
27
27
|
export { ALLOW_FIELD_POLICY, applyFieldPolicy, createAuditFieldRedactor, createProtectedFieldPolicy, FIELD_POLICY_LIMITS, FieldPolicyError, } from "./field-policy.js";
|
|
28
28
|
export { assertGuardrailsAllowed, GuardrailError, MAX_GUARDRAIL_CONCURRENCY, runGuardrails } from "./guardrails.js";
|
|
29
29
|
export { assertIdentityActive, assertIdentityMatchesOwnership, assertIdentityPropagation, DEFAULT_IDENTITY_LIMITS, HARD_IDENTITY_LIMITS, IdentityError, identityTelemetryAttributes, narrowIdentity, ownershipFromIdentity, resolveIdentityLimits, resolveRunIdentity, } from "./identity.js";
|
|
30
|
-
export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBuilder, renderPromptTemplate, resolveContextProviders, } from "./input.js";
|
|
30
|
+
export { assembleProviderInput, createDefaultInputBuilder, createDefaultPromptBuilder, EMPTY_TOOL_RESULT_TEXT, renderPromptTemplate, resolveContextProviders, } from "./input.js";
|
|
31
31
|
export { resolveInstructionInjectors, runInstructionInjectors } from "./instruction-injection.js";
|
|
32
32
|
export { createMemoryLeaseStore, LEASE_CONFLICT_CODE, LeaseConflictError } from "./leases.js";
|
|
33
33
|
export { definePrismManifest, parsePrismManifest } from "./manifests.js";
|
|
@@ -66,6 +66,6 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
|
|
|
66
66
|
export { trimTrailingSlashes } from "./trim-trailing-slashes.js";
|
|
67
67
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
68
68
|
export const name = "prism";
|
|
69
|
-
export const version = "0.
|
|
69
|
+
export const version = "0.6.0";
|
|
70
70
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
71
71
|
//# sourceMappingURL=index.js.map
|
package/dist/input.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ import { type LoadedSkillSet, type SkillsDisclosure } from "./skill-disclosure.j
|
|
|
6
6
|
import { type ResolvedToolResultFoldOptions } from "./tool-result-fold.js";
|
|
7
7
|
import { type ToolsDisclosure, type ToolsSearchOptions } from "./tool-search.js";
|
|
8
8
|
export type AgentInput = string | Message | readonly Message[];
|
|
9
|
+
/**
|
|
10
|
+
* Wire payload for a tool result that carries no `value`, no `type:text` content, and no error.
|
|
11
|
+
* Every provider route serializes a tool result from this block, so the empty case must be a
|
|
12
|
+
* constant non-empty string instead of an absent payload (strict providers reject empty results).
|
|
13
|
+
*/
|
|
14
|
+
export declare const EMPTY_TOOL_RESULT_TEXT = "(tool completed with no output)";
|
|
9
15
|
export interface PromptInstruction {
|
|
10
16
|
readonly text: string;
|
|
11
17
|
readonly label?: string;
|
package/dist/input.js
CHANGED
|
@@ -8,6 +8,12 @@ import { skillMessages as buildSkillMessages } from "./skill-disclosure.js";
|
|
|
8
8
|
import { composeSystemPrompt } from "./system-prompts.js";
|
|
9
9
|
import { foldToolResultHistory, foldToolResults } from "./tool-result-fold.js";
|
|
10
10
|
import { selectDisclosedTools } from "./tool-search.js";
|
|
11
|
+
/**
|
|
12
|
+
* Wire payload for a tool result that carries no `value`, no `type:text` content, and no error.
|
|
13
|
+
* Every provider route serializes a tool result from this block, so the empty case must be a
|
|
14
|
+
* constant non-empty string instead of an absent payload (strict providers reject empty results).
|
|
15
|
+
*/
|
|
16
|
+
export const EMPTY_TOOL_RESULT_TEXT = "(tool completed with no output)";
|
|
11
17
|
export function createDefaultInputBuilder() {
|
|
12
18
|
return {
|
|
13
19
|
name: "default-input",
|
|
@@ -328,7 +334,12 @@ function toolResultPayload(result) {
|
|
|
328
334
|
.map((block) => block.text)
|
|
329
335
|
.filter(Boolean)
|
|
330
336
|
.join("\n");
|
|
331
|
-
|
|
337
|
+
if (text.length > 0)
|
|
338
|
+
return text;
|
|
339
|
+
// An error already carries the outcome; without one, send the constant sentinel so no route
|
|
340
|
+
// (JSON string content, a content string, a function response, or a typed output part) emits
|
|
341
|
+
// an empty or absent payload.
|
|
342
|
+
return result.error === undefined || result.error === null ? EMPTY_TOOL_RESULT_TEXT : undefined;
|
|
332
343
|
}
|
|
333
344
|
function toolResultMessages(results) {
|
|
334
345
|
return (results ?? []).map(toToolResultMessage);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface SsrfPolicy {
|
|
2
|
+
/** When true (default), deny private/link-local/metadata hostnames and IPs. */
|
|
3
|
+
readonly denyPrivateHosts?: boolean;
|
|
4
|
+
/** Optional hostname allow-list. When set, only listed hosts are permitted. */
|
|
5
|
+
readonly allowedHostnames?: readonly string[];
|
|
6
|
+
/**
|
|
7
|
+
* Optional IP-literal CIDR allow-list (IPv4 + IPv6, e.g. `"10.0.0.0/8"`). Checked
|
|
8
|
+
* after the hostname allow-list and the denied-name list, and applied to both URL
|
|
9
|
+
* literals and resolved DNS candidates. Membership bypasses **only** the private-IP
|
|
10
|
+
* block: metadata-style hostnames (`metadata.google.internal`), loopback names, and
|
|
11
|
+
* embedded credentials stay denied, and a hostname in the list can never match.
|
|
12
|
+
* An unparseable entry fails the check closed. Explicit host trust override — see
|
|
13
|
+
* `docs/multimodal-content.md` / `docs/host-security.md`.
|
|
14
|
+
*/
|
|
15
|
+
readonly allowedCidrs?: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export interface MediaHostAddress {
|
|
18
|
+
readonly address: string;
|
|
19
|
+
readonly family: 4 | 6;
|
|
20
|
+
}
|
|
21
|
+
export type MediaHostnameResolver = (hostname: string, signal: AbortSignal) => Promise<readonly MediaHostAddress[]>;
|
|
22
|
+
export declare class MediaContentError extends Error {
|
|
23
|
+
readonly code: "ambiguous_source" | "missing_source" | "item_too_large" | "request_too_large" | "too_many_items" | "audio_too_long" | "invalid_base64" | "ssrf_denied" | "redirect" | "fetch_failed" | "fetch_timeout" | "resource_required" | "mime_mismatch" | "unsupported_url_scheme";
|
|
24
|
+
constructor(code: MediaContentError["code"], message: string, options?: ErrorOptions);
|
|
25
|
+
}
|
|
26
|
+
export declare function assertSsrfAllowedUrl(url: string, policy?: SsrfPolicy): void;
|
|
27
|
+
export declare function normalizeHostname(value: string): string;
|
|
28
|
+
export declare function isBlockedIp(hostname: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Membership test for `SsrfPolicy.allowedCidrs`. Non-IP hostnames can never match; an
|
|
31
|
+
* entry that does not parse as `address/prefix` throws `ssrf_denied` (fail closed,
|
|
32
|
+
* including entries of the other address family than the one being tested).
|
|
33
|
+
*/
|
|
34
|
+
export declare function isAllowedByCidr(hostname: string, allowedCidrs: readonly string[] | undefined): boolean;
|