@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +22 -2
  2. package/README.md +5 -4
  3. package/dist/agent-definitions.js +2 -3
  4. package/dist/agent-loops.d.ts +4 -0
  5. package/dist/agent-loops.js +28 -10
  6. package/dist/agent-run-lifecycle.d.ts +1 -2
  7. package/dist/agent-run-lifecycle.js +1 -1
  8. package/dist/agent-run-state.js +16 -3
  9. package/dist/agents.d.ts +1 -1
  10. package/dist/agents.js +121 -51
  11. package/dist/cache-helpers.js +18 -9
  12. package/dist/checkpoints.js +5 -9
  13. package/dist/cli-init.js +3 -7
  14. package/dist/cli-runner.d.ts +1 -1
  15. package/dist/cli-runner.js +74 -13
  16. package/dist/compaction.js +5 -4
  17. package/dist/config.js +7 -4
  18. package/dist/content.js +26 -24
  19. package/dist/context-budget.js +12 -8
  20. package/dist/contracts.d.ts +79 -3
  21. package/dist/contracts.js +4 -1
  22. package/dist/contribution-parsing.js +6 -2
  23. package/dist/conversations.js +2 -1
  24. package/dist/credentials.d.ts +1 -1
  25. package/dist/credentials.js +3 -1
  26. package/dist/event-multiplexer.js +1 -3
  27. package/dist/feedback.js +11 -9
  28. package/dist/guardrails.d.ts +1 -1
  29. package/dist/guardrails.js +17 -14
  30. package/dist/identity.js +10 -2
  31. package/dist/index.d.ts +83 -84
  32. package/dist/index.js +42 -42
  33. package/dist/input.d.ts +2 -2
  34. package/dist/input.js +40 -24
  35. package/dist/instruction-injection.d.ts +1 -1
  36. package/dist/node/agent-definitions.js +16 -8
  37. package/dist/node/contribution-discovery.d.ts +1 -2
  38. package/dist/node/contribution-discovery.js +3 -3
  39. package/dist/node/session-store-jsonl.js +10 -7
  40. package/dist/node/settings.d.ts +1 -1
  41. package/dist/node/settings.js +1 -1
  42. package/dist/node/system-project-prompts.js +2 -4
  43. package/dist/node/trust.js +1 -1
  44. package/dist/persistence-lifecycle.js +1 -3
  45. package/dist/provider-events.d.ts +1 -0
  46. package/dist/provider-events.js +6 -1
  47. package/dist/provider-request-policy.js +3 -4
  48. package/dist/providers/media.d.ts +1 -1
  49. package/dist/providers/openai-compatible.js +3 -4
  50. package/dist/providers/openai-primitives.js +7 -7
  51. package/dist/redaction.d.ts +1 -0
  52. package/dist/redaction.js +5 -2
  53. package/dist/resources.d.ts +2 -2
  54. package/dist/resources.js +2 -2
  55. package/dist/rpc.js +42 -9
  56. package/dist/run-ledger.js +13 -4
  57. package/dist/run-limits.js +49 -10
  58. package/dist/secure-agent.js +1 -1
  59. package/dist/security.js +7 -2
  60. package/dist/session-stores.d.ts +1 -1
  61. package/dist/session-stores.js +13 -13
  62. package/dist/structured-output.js +2 -2
  63. package/dist/system-prompts.js +7 -2
  64. package/dist/testing/compaction-conformance.js +5 -1
  65. package/dist/testing/extension-conformance.js +15 -3
  66. package/dist/testing/feedback.d.ts +1 -3
  67. package/dist/testing/feedback.js +1 -1
  68. package/dist/testing/persistence-schema.js +206 -37
  69. package/dist/testing/provider-conformance.js +3 -3
  70. package/dist/testing/run-ledger-conformance.js +1 -1
  71. package/dist/testing/session-store-conformance.js +1 -1
  72. package/dist/testing/tool-conformance.js +30 -5
  73. package/dist/thinking.js +4 -1
  74. package/dist/tools.d.ts +2 -2
  75. package/dist/tools.js +24 -5
  76. package/docs/0.1.0-readiness.md +139 -0
  77. package/docs/host-security.md +4 -1
  78. package/docs/index.md +14 -11
  79. package/docs/migration.md +58 -1
  80. package/docs/multimodal-content.md +8 -5
  81. package/docs/performance.md +67 -0
  82. package/docs/provider-caching.md +8 -0
  83. package/docs/provider-conformance.md +29 -5
  84. package/docs/provider-packages.md +22 -1
  85. package/docs/providers/ai-sdk.md +23 -7
  86. package/docs/providers/openai.md +22 -3
  87. package/docs/public-contracts.md +1 -1
  88. package/docs/rag.md +41 -12
  89. package/docs/release-and-install.md +153 -16
  90. package/docs/resource-loading.md +3 -0
  91. package/docs/working-and-semantic-memory.md +22 -4
  92. package/package.json +14 -6
  93. package/docs/review-coverage-2026-07-14.md +0 -260
  94. package/docs/review-coverage-2026-07-15.md +0 -193
  95. package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
  96. package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
  97. package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
  98. package/docs/review-coverage-2026-07-21-phase-5.md +0 -172
  99. package/docs/review-coverage-2026-07-22-phase-6.md +0 -209
  100. package/docs/review-coverage-2026-07-22-phase-7.md +0 -173
  101. package/docs/review-coverage-2026-07-23-phase-8.md +0 -245
  102. 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" && key !== "description" && key !== "model" && key !== "tools" && key !== "skills" && key !== "context" && key !== "instructions") {
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.split(",").map((s) => s.trim()).filter(Boolean);
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, fallbackName, origin) {
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, appendFile } from "node:fs/promises";
1
+ import { appendFile, mkdir, readFile } from "node:fs/promises";
2
2
  import { dirname } from "node:path";
3
- import { SESSION_APPEND_CONFLICT_CODE, SessionAppendConflictError, SessionSearchUnsupportedError, isSessionEntryKind, SESSION_ENTRY_SCHEMA_VERSION, } from "../contracts.js";
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 { ok: false, error: { line: lineNumber, message: "Invalid session entry: expected object with id, sessionId, timestamp, and kind", raw } };
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
- && typeof entry.sessionId === "string"
154
- && typeof entry.timestamp === "string"
155
- && typeof entry.kind === "string";
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))
@@ -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"]>;
@@ -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
- ?? (options.workspaceRoot !== undefined ? join(options.workspaceRoot, "AGENTS.md") : undefined);
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);
@@ -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;
@@ -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()].sort(([a], [b]) => a - b).map(([index, partial]) => {
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
- ...(patch.cache?.breakpoints ?? []),
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
- && 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) {
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 {
@@ -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.prototype.hasOwnProperty.call(target, key)) {
34
+ if (!Object.hasOwn(target, key)) {
32
35
  target[key] = value;
33
36
  return;
34
37
  }
35
38
  let suffix = 2;
36
- while (Object.prototype.hasOwnProperty.call(target, `${key}__${suffix}`))
39
+ while (Object.hasOwn(target, `${key}__${suffix}`))
37
40
  suffix += 1;
38
41
  target[`${key}__${suffix}`] = value;
39
42
  };
@@ -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, } from "./content.js";
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, { id: request.id, ok: false, error: errorToErrorInfo(new Error(`Session ${session.id} already has an active run for request ${existing.requestId}`)) });
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
- })().catch((error) => {
81
+ })()
82
+ .catch((error) => {
78
83
  write(stdout, { id: request.id, ok: false, error: errorToErrorInfo(error) });
79
- }).finally(() => {
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, { id: request.id, ok: true, result: {
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]) => ({ handleId, sessionId: session.id, leafId: session.leafId })),
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, { id: request.id, ok: true, result: { sessionId: state.current.id, leafId: state.current.leafId, handleId: state.currentHandleId } });
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" && ["prompt", "steer", "followUp", "abort", "state", "messages", "setModel", "compact", "switchSession", "forkSession", "cloneSession", "checkout", "command"].includes(value);
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;
@@ -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 = () => { if (timer)
36
- clearTimeout(timer); timer = undefined; };
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(() => { timer = undefined; void flush().catch(() => undefined); }, maxDelayMs);
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" || queue.length >= maxBatchEntries || bufferedBytes >= maxBatchBytes || (item.kind === "run" && terminal(item.record) && durability === "flush_on_terminal"))
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();
@@ -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({ ...resolved, ...(maxCost ? { maxCost: base?.maxCost && override?.maxCost ? { amount: Math.min(base.maxCost.amount, override.maxCost.amount), currency: base.maxCost.currency === override.maxCost.currency ? base.maxCost.currency : failCurrency() } : maxCost } : {}) });
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 = { turns: 0, providerAttempts: 0, toolRounds: 0, toolCalls: 0, wallTimeMs: 0, requestBytes: 0, responseBytes: 0, inputTokens: 0, outputTokens: 0, totalTokens: 0, cost: 0, ...options.snapshot };
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() { return this.exceeded; }
108
- snapshot() { return { ...this.counters, wallTimeMs: Math.min(this.limits.maxWallTimeMs, Math.ceil(performance.now() - this.startedAt)) }; }
109
- dispose() { if (this.timer)
110
- clearTimeout(this.timer); this.timer = undefined; }
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 ?? ((usage.inputTokens ?? 0) + (usage.outputTokens ?? 0));
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 = { limit, maximum, observed, ...(limit === "maxCost" && this.limits.maxCost ? { currency: this.limits.maxCost.currency } : {}) };
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")
@@ -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 { allowed: allow.size === 0 || allow.has(key), reason: allow.size && !allow.has(key) ? `Permission not allowed: ${key}` : undefined };
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 ? { name: error.name, message: error.message, code: error.code } : { message: String(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
@@ -1,4 +1,4 @@
1
- import { type Message, type SessionBranchRead, type BranchReader, type SessionEntry, type SessionStore } from "./contracts.js";
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;