@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
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
1
2
|
import { join } from "node:path";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { readdir, stat } from "node:fs/promises";
|
|
4
|
-
import { parseSkillFile, splitFrontmatter } from "../contribution-parsing.js";
|
|
5
3
|
import { resolveAgentDefinition } from "../agent-definitions.js";
|
|
4
|
+
import { parseSkillFile, splitFrontmatter } from "../contribution-parsing.js";
|
|
6
5
|
import { assertPermission, isTrusted } from "../security.js";
|
|
7
|
-
import { isPathInsideReal } from "./trust.js";
|
|
8
|
-
import { readOptionalFile } from "./contribution-discovery.js";
|
|
9
6
|
import { createSkillRegistry } from "../skills.js";
|
|
10
7
|
import { isNodeErrorCode } from "./config.js";
|
|
8
|
+
import { readOptionalFile } from "./contribution-discovery.js";
|
|
9
|
+
import { isPathInsideReal } from "./trust.js";
|
|
11
10
|
const defaultInclude = {
|
|
12
11
|
systemPrompt: true,
|
|
13
12
|
agentPrompt: true,
|
|
@@ -288,7 +287,13 @@ function resolveSkillContexts(skills, contextProviders) {
|
|
|
288
287
|
function collectMetadata(front) {
|
|
289
288
|
const metadata = {};
|
|
290
289
|
for (const [key, value] of front) {
|
|
291
|
-
if (key !== "name" &&
|
|
290
|
+
if (key !== "name" &&
|
|
291
|
+
key !== "description" &&
|
|
292
|
+
key !== "model" &&
|
|
293
|
+
key !== "tools" &&
|
|
294
|
+
key !== "skills" &&
|
|
295
|
+
key !== "context" &&
|
|
296
|
+
key !== "instructions") {
|
|
292
297
|
metadata[key] = value;
|
|
293
298
|
}
|
|
294
299
|
}
|
|
@@ -305,7 +310,10 @@ function getStringList(front, key) {
|
|
|
305
310
|
if (Array.isArray(value))
|
|
306
311
|
return value.map(String);
|
|
307
312
|
if (typeof value === "string")
|
|
308
|
-
return value
|
|
313
|
+
return value
|
|
314
|
+
.split(",")
|
|
315
|
+
.map((s) => s.trim())
|
|
316
|
+
.filter(Boolean);
|
|
309
317
|
return undefined;
|
|
310
318
|
}
|
|
311
319
|
function bodyAsInstructions(text, path) {
|
|
@@ -348,7 +356,7 @@ async function readOptional(read, path) {
|
|
|
348
356
|
const NAME_RE = /^[A-Za-z0-9 _-]+$/;
|
|
349
357
|
function parentDirName(path) {
|
|
350
358
|
const parts = path.split(/[/\\]+/).filter(Boolean);
|
|
351
|
-
return parts.length >= 2 ? parts[parts.length - 2] : parts[parts.length - 1] ?? "";
|
|
359
|
+
return parts.length >= 2 ? parts[parts.length - 2] : (parts[parts.length - 1] ?? "");
|
|
352
360
|
}
|
|
353
361
|
/** Parse a `CONTEXT.md` into a static {@link ContextProvider}. The markdown
|
|
354
362
|
* body becomes the context block content; frontmatter `name` overrides the
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ContributionFileKind, DiscoveredContribution } from "../contracts.js";
|
|
2
|
-
import type { PermissionPolicy } from "../security.js";
|
|
3
|
-
import type { TrustPolicy } from "../security.js";
|
|
2
|
+
import type { PermissionPolicy, TrustPolicy } from "../security.js";
|
|
4
3
|
export interface DiscoveryOptions {
|
|
5
4
|
readonly kinds: readonly ContributionFileKind[];
|
|
6
5
|
/** Workspace root. Scans `<root>/.agents/<kind>s/<name>/`. Gated by `trust`. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { isJsonObject } from "../config.js";
|
|
4
|
-
import { assertPermission } from "../security.js";
|
|
5
|
-
import { isPathInsideReal } from "./trust.js";
|
|
6
4
|
import { parseSkillFile } from "../contribution-parsing.js";
|
|
5
|
+
import { assertPermission } from "../security.js";
|
|
7
6
|
import { isNodeErrorCode } from "./config.js";
|
|
7
|
+
import { isPathInsideReal } from "./trust.js";
|
|
8
8
|
/**
|
|
9
9
|
* Discover contributions on disk. Scans the workspace `.agents/` tree only;
|
|
10
10
|
* no global root. One `readdir` per kind-root; no `import()`. Inert output —
|
|
@@ -72,7 +72,7 @@ async function readEntry(dir, fallbackName, kind, origin) {
|
|
|
72
72
|
return readManifestEntry(dir, fallbackName, kind, origin);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
async function readSkillEntry(dir,
|
|
75
|
+
async function readSkillEntry(dir, _fallbackName, origin) {
|
|
76
76
|
const path = join(dir, "SKILL.md");
|
|
77
77
|
if (!(await isPathInsideReal(dir, path)))
|
|
78
78
|
return undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mkdir, readFile
|
|
1
|
+
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { isSessionEntryKind, SESSION_APPEND_CONFLICT_CODE, SESSION_ENTRY_SCHEMA_VERSION, SessionAppendConflictError, SessionSearchUnsupportedError, } from "../contracts.js";
|
|
4
4
|
import { isNodeErrorCode } from "./config.js";
|
|
5
5
|
export function createJsonlSessionStore(pathOrOptions) {
|
|
6
6
|
const options = typeof pathOrOptions === "string" ? { path: pathOrOptions, createDirectory: true } : pathOrOptions;
|
|
@@ -93,7 +93,10 @@ function parseEntry(line, lineNumber) {
|
|
|
93
93
|
}
|
|
94
94
|
function validateSessionEntry(value, lineNumber, raw) {
|
|
95
95
|
if (!isBasicSessionEntry(value)) {
|
|
96
|
-
return {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
error: { line: lineNumber, message: "Invalid session entry: expected object with id, sessionId, timestamp, and kind", raw },
|
|
99
|
+
};
|
|
97
100
|
}
|
|
98
101
|
const entry = value;
|
|
99
102
|
if (entry.parentId !== undefined && typeof entry.parentId !== "string") {
|
|
@@ -149,10 +152,10 @@ function isBasicSessionEntry(value) {
|
|
|
149
152
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
150
153
|
return false;
|
|
151
154
|
const entry = value;
|
|
152
|
-
return typeof entry.id === "string"
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
return (typeof entry.id === "string" &&
|
|
156
|
+
typeof entry.sessionId === "string" &&
|
|
157
|
+
typeof entry.timestamp === "string" &&
|
|
158
|
+
typeof entry.kind === "string");
|
|
156
159
|
}
|
|
157
160
|
function isMessage(value) {
|
|
158
161
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
package/dist/node/settings.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ConfigLayer } from "../config.js";
|
|
2
|
-
import { type NodeConfigFile } from "./config.js";
|
|
3
2
|
import type { SettingsProvider } from "../contracts.js";
|
|
3
|
+
import { type NodeConfigFile } from "./config.js";
|
|
4
4
|
export type NodeSettingsFile = NodeConfigFile;
|
|
5
5
|
export declare function defaultUserSettingsPath(appName?: string): string;
|
|
6
6
|
export declare function readSettingsFile(path: string): Promise<ConfigLayer["config"]>;
|
package/dist/node/settings.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { mergeConfigLayers } from "../config.js";
|
|
4
|
-
import { readConfigFile, isNodeErrorCode } from "./config.js";
|
|
5
4
|
import { createStaticSettingsProvider } from "../settings.js";
|
|
5
|
+
import { isNodeErrorCode, readConfigFile } from "./config.js";
|
|
6
6
|
export function defaultUserSettingsPath(appName = "prism") {
|
|
7
7
|
return join(homedir(), ".config", appName, "settings.json");
|
|
8
8
|
}
|
|
@@ -14,10 +14,8 @@ import { readOptionalFile } from "./contribution-discovery.js";
|
|
|
14
14
|
* singletons don't fit `discoverContributions`' named-subdir scanner, so this is a
|
|
15
15
|
* sibling loader mirroring `src/node/instruction-injectors.ts`'s per-concern adapter shape. */
|
|
16
16
|
export async function loadSystemPromptFiles(options) {
|
|
17
|
-
const agentsPath = options.agentsMdPath
|
|
18
|
-
|
|
19
|
-
const systemPath = options.systemMdPath
|
|
20
|
-
?? (options.globalRoot !== undefined ? join(options.globalRoot, ".prism", "agent", "SYSTEM.md") : undefined);
|
|
17
|
+
const agentsPath = options.agentsMdPath ?? (options.workspaceRoot !== undefined ? join(options.workspaceRoot, "AGENTS.md") : undefined);
|
|
18
|
+
const systemPath = options.systemMdPath ?? (options.globalRoot !== undefined ? join(options.globalRoot, ".prism", "agent", "SYSTEM.md") : undefined);
|
|
21
19
|
const out = [];
|
|
22
20
|
if (systemPath !== undefined) {
|
|
23
21
|
const layer = await readSystemFile(systemPath, options.permission);
|
package/dist/node/trust.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isAbsolute, relative, resolve, dirname, basename, join } from "node:path";
|
|
2
1
|
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { isNodeErrorCode } from "./config.js";
|
|
4
4
|
export function isPathInside(root, target) {
|
|
5
5
|
const from = resolve(root);
|
|
@@ -64,9 +64,7 @@ export function createMemoryPersistenceLifecycle() {
|
|
|
64
64
|
async applyRetention(input) {
|
|
65
65
|
throwIfAborted(input.signal);
|
|
66
66
|
assertOwnership(input);
|
|
67
|
-
const held = new Set([...holds.values()]
|
|
68
|
-
.filter((hold) => sameOwnership(input, hold) && hold.resourceKind === "session")
|
|
69
|
-
.map((hold) => hold.resourceId));
|
|
67
|
+
const held = new Set([...holds.values()].filter((hold) => sameOwnership(input, hold) && hold.resourceKind === "session").map((hold) => hold.resourceId));
|
|
70
68
|
const candidates = input.candidates ?? [];
|
|
71
69
|
const deletedIds = [];
|
|
72
70
|
const skippedHeld = [];
|
|
@@ -10,6 +10,7 @@ export declare function providerToolCallDelta(delta: {
|
|
|
10
10
|
readonly argumentsText?: string;
|
|
11
11
|
}): ProviderEvent;
|
|
12
12
|
export declare function providerToolCallDeltaContent(delta: Omit<ToolCallDeltaContent, "type">): ToolCallDeltaContent;
|
|
13
|
+
export declare function providerContinuationRequired(cursor: string, reason?: string): ProviderEvent;
|
|
13
14
|
export declare function reconstructToolCallDeltas(events: readonly ProviderEvent[]): readonly ToolCallContent[];
|
|
14
15
|
export declare function providerUsage(usage: Usage): ProviderEvent;
|
|
15
16
|
export declare function providerDone(usage?: Usage): ProviderEvent;
|
package/dist/provider-events.js
CHANGED
|
@@ -18,6 +18,9 @@ export function providerToolCallDelta(delta) {
|
|
|
18
18
|
export function providerToolCallDeltaContent(delta) {
|
|
19
19
|
return { type: "tool_call_delta", ...delta };
|
|
20
20
|
}
|
|
21
|
+
export function providerContinuationRequired(cursor, reason) {
|
|
22
|
+
return { type: "continuation_required", cursor, reason };
|
|
23
|
+
}
|
|
21
24
|
export function reconstructToolCallDeltas(events) {
|
|
22
25
|
const partials = new Map();
|
|
23
26
|
for (const event of events) {
|
|
@@ -32,7 +35,9 @@ export function reconstructToolCallDeltas(events) {
|
|
|
32
35
|
partial.argumentsText += event.argumentsText;
|
|
33
36
|
partials.set(event.index, partial);
|
|
34
37
|
}
|
|
35
|
-
return [...partials.entries()]
|
|
38
|
+
return [...partials.entries()]
|
|
39
|
+
.sort(([a], [b]) => a - b)
|
|
40
|
+
.map(([index, partial]) => {
|
|
36
41
|
if (!partial.id || !partial.name) {
|
|
37
42
|
throw new ProviderTransportError("incomplete_delta", `Incomplete tool call delta at index ${index}`);
|
|
38
43
|
}
|
|
@@ -50,10 +50,9 @@ export function mergeProviderRequestOptions(base, patch) {
|
|
|
50
50
|
cache: {
|
|
51
51
|
...base.cache,
|
|
52
52
|
...patch.cache,
|
|
53
|
-
breakpoints: base.cache?.breakpoints || patch.cache?.breakpoints
|
|
54
|
-
...(base.cache?.breakpoints ?? []),
|
|
55
|
-
|
|
56
|
-
] : undefined,
|
|
53
|
+
breakpoints: base.cache?.breakpoints || patch.cache?.breakpoints
|
|
54
|
+
? [...(base.cache?.breakpoints ?? []), ...(patch.cache?.breakpoints ?? [])]
|
|
55
|
+
: undefined,
|
|
57
56
|
},
|
|
58
57
|
};
|
|
59
58
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type MediaContentBlock, type ModelInputCapability, type ResolvedMediaContent, type ResolveMediaContentOptions } from "../content.js";
|
|
1
2
|
import type { AudioContent, ContentBlock, DocumentContent, FileContent, JsonObject, Message, ModelCapabilities, ModelConfig } from "../contracts.js";
|
|
2
|
-
import { type MediaContentBlock, type ModelInputCapability, type ResolveMediaContentOptions, type ResolvedMediaContent } from "../content.js";
|
|
3
3
|
/** Default upload-cache entry cap per provider media session. */
|
|
4
4
|
export declare const DEFAULT_PROVIDER_UPLOAD_CACHE_ENTRIES = 32;
|
|
5
5
|
/** Inline OpenAI file_data ceiling before preferring Files API upload. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolveCredentialValue } from "../credentials.js";
|
|
2
2
|
import { providerDone, providerError, providerTextDelta, providerThinkingDelta, providerToolCall, providerToolCallDelta, providerUsage, toolCallFromArgumentsText, } from "../provider-events.js";
|
|
3
|
-
import { assertOpenAIChatMessage, applyOpenAIChatStructuredOutput, mapOpenAIChatUsage, serializeOpenAIChatMessage, serializeOpenAITool, } from "./openai-primitives.js";
|
|
4
|
-
import { ProviderTransportError, readBoundedResponseText, readSseData, } from "./transport.js";
|
|
5
3
|
import { assertStructuredOutputRequestSupported } from "../structured-output.js";
|
|
4
|
+
import { applyOpenAIChatStructuredOutput, assertOpenAIChatMessage, mapOpenAIChatUsage, serializeOpenAIChatMessage, serializeOpenAITool, } from "./openai-primitives.js";
|
|
5
|
+
import { ProviderTransportError, readBoundedResponseText, readSseData } from "./transport.js";
|
|
6
6
|
export function createOpenAICompatibleProvider(options) {
|
|
7
7
|
const providerId = options.id ?? "openai-compatible";
|
|
8
8
|
return {
|
|
@@ -18,8 +18,7 @@ export function createOpenAICompatibleProvider(options) {
|
|
|
18
18
|
try {
|
|
19
19
|
const url = typeof options.chatCompletionsUrl === "function"
|
|
20
20
|
? options.chatCompletionsUrl(request)
|
|
21
|
-
: options.chatCompletionsUrl
|
|
22
|
-
?? `${options.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
21
|
+
: (options.chatCompletionsUrl ?? `${options.baseUrl.replace(/\/$/, "")}/chat/completions`);
|
|
23
22
|
const authStyle = options.authStyle ?? "bearer";
|
|
24
23
|
const headers = {
|
|
25
24
|
...Object.fromEntries(Object.entries(request.options?.headers ?? {}).filter((entry) => typeof entry[1] === "string")),
|
|
@@ -110,13 +110,13 @@ export function mapOpenAIChatUsage(usage) {
|
|
|
110
110
|
if (!usage || typeof usage !== "object" || Array.isArray(usage))
|
|
111
111
|
return undefined;
|
|
112
112
|
const wire = usage;
|
|
113
|
-
if (wire.prompt_tokens === undefined
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
if (wire.prompt_tokens === undefined &&
|
|
114
|
+
wire.completion_tokens === undefined &&
|
|
115
|
+
wire.total_tokens === undefined &&
|
|
116
|
+
wire.prompt_cache_hit_tokens === undefined &&
|
|
117
|
+
wire.prompt_tokens_details?.cached_tokens === undefined &&
|
|
118
|
+
wire.prompt_tokens_details?.cache_write_tokens === undefined &&
|
|
119
|
+
wire.prompt_tokens_details?.cache_creation_input_tokens === undefined) {
|
|
120
120
|
return undefined;
|
|
121
121
|
}
|
|
122
122
|
return {
|
package/dist/redaction.d.ts
CHANGED
|
@@ -8,5 +8,6 @@ export declare function redactAgentEvent(event: AgentEvent, redactor?: SecretRed
|
|
|
8
8
|
export declare function redactSessionEntry(entry: SessionEntry, redactor?: SecretRedactor): SessionEntry;
|
|
9
9
|
export declare function redactProviderRequest(request: ProviderRequest, redactor?: SecretRedactor): ProviderRequest;
|
|
10
10
|
export declare function redactRunLedgerRecord<T extends RunLedgerRecord>(record: T, redactor?: SecretRedactor): T;
|
|
11
|
+
export declare function resolveRedactor(redactor?: SecretRedactor, secrets?: readonly (string | undefined)[]): SecretRedactor | undefined;
|
|
11
12
|
export declare function redactSecrets<T>(value: T, secrets: readonly (string | undefined)[]): T;
|
|
12
13
|
export declare function errorToErrorInfo(error: unknown, secrets?: readonly (string | undefined)[]): ErrorInfo;
|
package/dist/redaction.js
CHANGED
|
@@ -17,6 +17,9 @@ export function redactProviderRequest(request, redactor) {
|
|
|
17
17
|
export function redactRunLedgerRecord(record, redactor) {
|
|
18
18
|
return redactor?.redact(record) ?? record;
|
|
19
19
|
}
|
|
20
|
+
export function resolveRedactor(redactor, secrets) {
|
|
21
|
+
return redactor ?? (secrets?.some((secret) => Boolean(secret)) ? createSecretRedactor(secrets) : undefined);
|
|
22
|
+
}
|
|
20
23
|
export function redactSecrets(value, secrets) {
|
|
21
24
|
const needles = secrets.filter((secret) => Boolean(secret));
|
|
22
25
|
if (needles.length === 0)
|
|
@@ -28,12 +31,12 @@ export function redactSecrets(value, secrets) {
|
|
|
28
31
|
return String(key);
|
|
29
32
|
};
|
|
30
33
|
const assignKey = (target, key, value) => {
|
|
31
|
-
if (!Object.
|
|
34
|
+
if (!Object.hasOwn(target, key)) {
|
|
32
35
|
target[key] = value;
|
|
33
36
|
return;
|
|
34
37
|
}
|
|
35
38
|
let suffix = 2;
|
|
36
|
-
while (Object.
|
|
39
|
+
while (Object.hasOwn(target, `${key}__${suffix}`))
|
|
37
40
|
suffix += 1;
|
|
38
41
|
target[`${key}__${suffix}`] = value;
|
|
39
42
|
};
|
package/dist/resources.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type PrismManifest } from "./manifests.js";
|
|
2
|
-
import type { JsonObject, ResourceLoader, ResourceLoadContext } from "./contracts.js";
|
|
3
1
|
import { type MediaContentBounds } from "./content.js";
|
|
2
|
+
import type { JsonObject, ResourceLoadContext, ResourceLoader } from "./contracts.js";
|
|
3
|
+
import { type PrismManifest } from "./manifests.js";
|
|
4
4
|
export declare function loadTextResource(loader: ResourceLoader, uri: string, context?: ResourceLoadContext): Promise<string>;
|
|
5
5
|
export declare function loadJsonResource(loader: ResourceLoader, uri: string, context?: ResourceLoadContext): Promise<JsonObject>;
|
|
6
6
|
export declare function loadManifestResource(loader: ResourceLoader, uri: string, context?: ResourceLoadContext): Promise<PrismManifest>;
|
package/dist/resources.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { parsePrismManifest } from "./manifests.js";
|
|
2
1
|
import { assertJsonObject } from "./config.js";
|
|
3
|
-
import { DEFAULT_MAX_MEDIA_ITEM_BYTES, loadBoundedBinaryResource
|
|
2
|
+
import { DEFAULT_MAX_MEDIA_ITEM_BYTES, loadBoundedBinaryResource } from "./content.js";
|
|
3
|
+
import { parsePrismManifest } from "./manifests.js";
|
|
4
4
|
import { assertPermission, assertTrusted } from "./security.js";
|
|
5
5
|
export async function loadTextResource(loader, uri, context) {
|
|
6
6
|
await assertTrusted(context?.trust, { kind: "resource", target: uri, capability: "load", metadata: context?.metadata });
|
package/dist/rpc.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
|
-
import { errorToErrorInfo } from "./redaction.js";
|
|
3
2
|
import { resolveInstructionInjectors } from "./instruction-injection.js";
|
|
3
|
+
import { errorToErrorInfo } from "./redaction.js";
|
|
4
4
|
export async function runRpcServer(options) {
|
|
5
5
|
const first = options.createSession();
|
|
6
6
|
const state = {
|
|
@@ -58,7 +58,11 @@ async function handleRequest(request, state, stdout, activeRuns) {
|
|
|
58
58
|
const session = state.current;
|
|
59
59
|
const existing = activeRuns.get(session.id);
|
|
60
60
|
if (existing) {
|
|
61
|
-
write(stdout, {
|
|
61
|
+
write(stdout, {
|
|
62
|
+
id: request.id,
|
|
63
|
+
ok: false,
|
|
64
|
+
error: errorToErrorInfo(new Error(`Session ${session.id} already has an active run for request ${existing.requestId}`)),
|
|
65
|
+
});
|
|
62
66
|
return;
|
|
63
67
|
}
|
|
64
68
|
// ponytail: Phase 30 — resolve run options (incl. instructionInjectors names) BEFORE opening the
|
|
@@ -74,9 +78,11 @@ async function handleRequest(request, state, stdout, activeRuns) {
|
|
|
74
78
|
await events;
|
|
75
79
|
}
|
|
76
80
|
write(stdout, { id: request.id, ok: true, result: { sessionId: session.id } });
|
|
77
|
-
})()
|
|
81
|
+
})()
|
|
82
|
+
.catch((error) => {
|
|
78
83
|
write(stdout, { id: request.id, ok: false, error: errorToErrorInfo(error) });
|
|
79
|
-
})
|
|
84
|
+
})
|
|
85
|
+
.finally(() => {
|
|
80
86
|
activeRuns.delete(session.id);
|
|
81
87
|
});
|
|
82
88
|
activeRuns.set(session.id, { requestId: request.id, promise });
|
|
@@ -100,16 +106,24 @@ async function handleRequest(request, state, stdout, activeRuns) {
|
|
|
100
106
|
write(stdout, { id: request.id, ok: true, result: { sessionId: state.current.id } });
|
|
101
107
|
break;
|
|
102
108
|
case "state":
|
|
103
|
-
write(stdout, {
|
|
109
|
+
write(stdout, {
|
|
110
|
+
id: request.id,
|
|
111
|
+
ok: true,
|
|
112
|
+
result: {
|
|
104
113
|
sessionId: state.current.id,
|
|
105
114
|
leafId: state.current.leafId,
|
|
106
115
|
handleId: state.currentHandleId,
|
|
107
116
|
// ponytail: `sessions` kept as a backward-compatible handle-id string list (== sessionId
|
|
108
117
|
// for the initial session and clones); `handles` is the branch-handle detail view.
|
|
109
118
|
sessions: [...state.sessions.keys()],
|
|
110
|
-
handles: [...state.sessions.entries()].map(([handleId, session]) => ({
|
|
119
|
+
handles: [...state.sessions.entries()].map(([handleId, session]) => ({
|
|
120
|
+
handleId,
|
|
121
|
+
sessionId: session.id,
|
|
122
|
+
leafId: session.leafId,
|
|
123
|
+
})),
|
|
111
124
|
model: state.model,
|
|
112
|
-
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
113
127
|
break;
|
|
114
128
|
case "messages":
|
|
115
129
|
write(stdout, { id: request.id, ok: true, result: { sessionId: state.current.id, entries: await state.current.entries() } });
|
|
@@ -152,7 +166,11 @@ async function handleRequest(request, state, stdout, activeRuns) {
|
|
|
152
166
|
if (!leafId)
|
|
153
167
|
throw new Error("checkout requires params.leafId");
|
|
154
168
|
await state.current.checkout(leafId);
|
|
155
|
-
write(stdout, {
|
|
169
|
+
write(stdout, {
|
|
170
|
+
id: request.id,
|
|
171
|
+
ok: true,
|
|
172
|
+
result: { sessionId: state.current.id, leafId: state.current.leafId, handleId: state.currentHandleId },
|
|
173
|
+
});
|
|
156
174
|
break;
|
|
157
175
|
}
|
|
158
176
|
case "command": {
|
|
@@ -245,7 +263,22 @@ function isObject(value) {
|
|
|
245
263
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
246
264
|
}
|
|
247
265
|
function isCommand(value) {
|
|
248
|
-
return typeof value === "string" &&
|
|
266
|
+
return (typeof value === "string" &&
|
|
267
|
+
[
|
|
268
|
+
"prompt",
|
|
269
|
+
"steer",
|
|
270
|
+
"followUp",
|
|
271
|
+
"abort",
|
|
272
|
+
"state",
|
|
273
|
+
"messages",
|
|
274
|
+
"setModel",
|
|
275
|
+
"compact",
|
|
276
|
+
"switchSession",
|
|
277
|
+
"forkSession",
|
|
278
|
+
"cloneSession",
|
|
279
|
+
"checkout",
|
|
280
|
+
"command",
|
|
281
|
+
].includes(value));
|
|
249
282
|
}
|
|
250
283
|
function readRequestId(value) {
|
|
251
284
|
return isObject(value) && (typeof value.id === "string" || typeof value.id === "number") ? value.id : null;
|
package/dist/run-ledger.js
CHANGED
|
@@ -32,12 +32,18 @@ export function createBatchedRunLedger(target, options = {}) {
|
|
|
32
32
|
let flushChain = Promise.resolve();
|
|
33
33
|
let disposed = false;
|
|
34
34
|
const status = () => ({ accepted, flushed, buffered: queue.length });
|
|
35
|
-
const cancelTimer = () => {
|
|
36
|
-
|
|
35
|
+
const cancelTimer = () => {
|
|
36
|
+
if (timer)
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
timer = undefined;
|
|
39
|
+
};
|
|
37
40
|
const schedule = () => {
|
|
38
41
|
if (timer || disposed || queue.length === 0)
|
|
39
42
|
return;
|
|
40
|
-
timer = setTimeout(() => {
|
|
43
|
+
timer = setTimeout(() => {
|
|
44
|
+
timer = undefined;
|
|
45
|
+
void flush().catch(() => undefined);
|
|
46
|
+
}, maxDelayMs);
|
|
41
47
|
timer.unref?.();
|
|
42
48
|
};
|
|
43
49
|
const write = (item) => {
|
|
@@ -83,7 +89,10 @@ export function createBatchedRunLedger(target, options = {}) {
|
|
|
83
89
|
queue.push({ ...item, bytes });
|
|
84
90
|
bufferedBytes += bytes;
|
|
85
91
|
accepted += 1;
|
|
86
|
-
if (durability === "write_through" ||
|
|
92
|
+
if (durability === "write_through" ||
|
|
93
|
+
queue.length >= maxBatchEntries ||
|
|
94
|
+
bufferedBytes >= maxBatchBytes ||
|
|
95
|
+
(item.kind === "run" && terminal(item.record) && durability === "flush_on_terminal"))
|
|
87
96
|
await flush();
|
|
88
97
|
else
|
|
89
98
|
schedule();
|
package/dist/run-limits.js
CHANGED
|
@@ -58,9 +58,23 @@ export function resolveRunLimits(agent, run) {
|
|
|
58
58
|
resolved[name] = base ? Math.min(resolved[name], override[name]) : override[name];
|
|
59
59
|
}
|
|
60
60
|
const maxCost = override?.maxCost ?? base?.maxCost;
|
|
61
|
-
return Object.freeze({
|
|
61
|
+
return Object.freeze({
|
|
62
|
+
...resolved,
|
|
63
|
+
...(maxCost
|
|
64
|
+
? {
|
|
65
|
+
maxCost: base?.maxCost && override?.maxCost
|
|
66
|
+
? {
|
|
67
|
+
amount: Math.min(base.maxCost.amount, override.maxCost.amount),
|
|
68
|
+
currency: base.maxCost.currency === override.maxCost.currency ? base.maxCost.currency : failCurrency(),
|
|
69
|
+
}
|
|
70
|
+
: maxCost,
|
|
71
|
+
}
|
|
72
|
+
: {}),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function failCurrency() {
|
|
76
|
+
throw new TypeError("Run limit currencies must match when narrowed");
|
|
62
77
|
}
|
|
63
|
-
function failCurrency() { throw new TypeError("Run limit currencies must match when narrowed"); }
|
|
64
78
|
function validateLimits(input) {
|
|
65
79
|
for (const name of LIMIT_NAMES) {
|
|
66
80
|
const value = input[name];
|
|
@@ -88,7 +102,20 @@ export class RunLimitTracker {
|
|
|
88
102
|
constructor(limits, options = {}) {
|
|
89
103
|
this.options = options;
|
|
90
104
|
this.limits = limits;
|
|
91
|
-
this.counters = {
|
|
105
|
+
this.counters = {
|
|
106
|
+
turns: 0,
|
|
107
|
+
providerAttempts: 0,
|
|
108
|
+
toolRounds: 0,
|
|
109
|
+
toolCalls: 0,
|
|
110
|
+
wallTimeMs: 0,
|
|
111
|
+
requestBytes: 0,
|
|
112
|
+
responseBytes: 0,
|
|
113
|
+
inputTokens: 0,
|
|
114
|
+
outputTokens: 0,
|
|
115
|
+
totalTokens: 0,
|
|
116
|
+
cost: 0,
|
|
117
|
+
...options.snapshot,
|
|
118
|
+
};
|
|
92
119
|
for (const [key, value] of Object.entries(this.counters)) {
|
|
93
120
|
if (!Number.isFinite(value) || value < 0 || (key !== "cost" && !Number.isSafeInteger(value))) {
|
|
94
121
|
throw new TypeError("Run limit snapshot must contain finite non-negative counters");
|
|
@@ -104,10 +131,17 @@ export class RunLimitTracker {
|
|
|
104
131
|
if (remaining === 0)
|
|
105
132
|
this.exceed("maxWallTimeMs", limits.maxWallTimeMs);
|
|
106
133
|
}
|
|
107
|
-
get breach() {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
get breach() {
|
|
135
|
+
return this.exceeded;
|
|
136
|
+
}
|
|
137
|
+
snapshot() {
|
|
138
|
+
return { ...this.counters, wallTimeMs: Math.min(this.limits.maxWallTimeMs, Math.ceil(performance.now() - this.startedAt)) };
|
|
139
|
+
}
|
|
140
|
+
dispose() {
|
|
141
|
+
if (this.timer)
|
|
142
|
+
clearTimeout(this.timer);
|
|
143
|
+
this.timer = undefined;
|
|
144
|
+
}
|
|
111
145
|
charge(limit, delta = 1) {
|
|
112
146
|
if (!Number.isSafeInteger(delta) || delta < 0)
|
|
113
147
|
throw new TypeError("Run limit delta must be a non-negative safe integer");
|
|
@@ -130,7 +164,7 @@ export class RunLimitTracker {
|
|
|
130
164
|
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0))
|
|
131
165
|
throw new TypeError(`Provider usage ${key} must be a non-negative safe integer`);
|
|
132
166
|
}
|
|
133
|
-
const total = usage.totalTokens ?? (
|
|
167
|
+
const total = usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
|
|
134
168
|
if (!Number.isSafeInteger(total))
|
|
135
169
|
throw new TypeError("Provider usage totalTokens is invalid");
|
|
136
170
|
this.charge("maxInputTokens", usage.inputTokens ?? 0);
|
|
@@ -149,8 +183,13 @@ export class RunLimitTracker {
|
|
|
149
183
|
}
|
|
150
184
|
exceed(limit, observed) {
|
|
151
185
|
if (!this.exceeded) {
|
|
152
|
-
const maximum = limit === "maxCost" ? this.limits.maxCost?.amount ?? 0 : this.limits[limit];
|
|
153
|
-
this.exceeded = {
|
|
186
|
+
const maximum = limit === "maxCost" ? (this.limits.maxCost?.amount ?? 0) : this.limits[limit];
|
|
187
|
+
this.exceeded = {
|
|
188
|
+
limit,
|
|
189
|
+
maximum,
|
|
190
|
+
observed,
|
|
191
|
+
...(limit === "maxCost" && this.limits.maxCost ? { currency: this.limits.maxCost.currency } : {}),
|
|
192
|
+
};
|
|
154
193
|
this.options.onExceeded?.(this.exceeded);
|
|
155
194
|
}
|
|
156
195
|
if (limit !== "maxWallTimeMs")
|
package/dist/secure-agent.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createAgent } from "./agents.js";
|
|
2
1
|
import { validateRunStateOptions } from "./agent-run-state.js";
|
|
2
|
+
import { createAgent } from "./agents.js";
|
|
3
3
|
import { assertIdentityActive, assertIdentityMatchesOwnership } from "./identity.js";
|
|
4
4
|
import { resolveRunLimits } from "./run-limits.js";
|
|
5
5
|
import { createToolParameterValidator, createToolRegistry } from "./tools.js";
|
package/dist/security.js
CHANGED
|
@@ -50,11 +50,16 @@ export function createStaticPermissionPolicy(options) {
|
|
|
50
50
|
const key = `${request.kind}:${request.target}:${request.action}`;
|
|
51
51
|
if (deny.has(key))
|
|
52
52
|
return { allowed: false, reason: `Permission denied: ${key}` };
|
|
53
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
allowed: allow.size === 0 || allow.has(key),
|
|
55
|
+
reason: allow.size && !allow.has(key) ? `Permission not allowed: ${key}` : undefined,
|
|
56
|
+
};
|
|
54
57
|
},
|
|
55
58
|
};
|
|
56
59
|
}
|
|
57
60
|
export function denialToErrorInfo(error) {
|
|
58
|
-
return error instanceof Error
|
|
61
|
+
return error instanceof Error
|
|
62
|
+
? { name: error.name, message: error.message, code: error.code }
|
|
63
|
+
: { message: String(error) };
|
|
59
64
|
}
|
|
60
65
|
//# sourceMappingURL=security.js.map
|
package/dist/session-stores.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type BranchReader, type Message, type SessionBranchRead, type SessionEntry, type SessionStore } from "./contracts.js";
|
|
2
2
|
export interface CreateSessionEntryOptions extends Omit<SessionEntry, "id" | "timestamp"> {
|
|
3
3
|
readonly id?: string;
|
|
4
4
|
readonly timestamp?: string;
|