@arnilo/prism 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/README.md +23 -20
  3. package/dist/agent-run-state.d.ts +1 -2
  4. package/dist/agent-run-state.js +0 -3
  5. package/dist/agent-session/session/assemble.d.ts +6 -0
  6. package/dist/agent-session/session/assemble.js +391 -0
  7. package/dist/agent-session/session/persist.d.ts +28 -0
  8. package/dist/agent-session/session/persist.js +166 -0
  9. package/dist/agent-session/session/provider-round.d.ts +6 -0
  10. package/dist/agent-session/session/provider-round.js +231 -0
  11. package/dist/agent-session/session/tool-round.d.ts +31 -0
  12. package/dist/agent-session/session/tool-round.js +473 -0
  13. package/dist/agent-session/session/types.d.ts +115 -0
  14. package/dist/agent-session/session/types.js +5 -0
  15. package/dist/agent-session/session.d.ts +49 -43
  16. package/dist/agent-session/session.js +11 -1177
  17. package/dist/capture.d.ts +63 -0
  18. package/dist/capture.js +67 -0
  19. package/dist/cli-init.d.ts +18 -2
  20. package/dist/cli-init.js +2 -7
  21. package/dist/cli-runner.d.ts +2 -2
  22. package/dist/cli-runner.js +45 -9
  23. package/dist/content.d.ts +3 -3
  24. package/dist/content.js +3 -1
  25. package/dist/contracts-core/agent.d.ts +2 -0
  26. package/dist/contracts-core/batch.d.ts +97 -0
  27. package/dist/contracts-core/batch.js +65 -0
  28. package/dist/contracts-core/content.d.ts +72 -1
  29. package/dist/contracts-core/embeddings.d.ts +30 -0
  30. package/dist/contracts-core/embeddings.js +17 -0
  31. package/dist/contracts-core/images.d.ts +60 -0
  32. package/dist/contracts-core/images.js +17 -0
  33. package/dist/contracts-core/moderation.d.ts +46 -0
  34. package/dist/contracts-core/moderation.js +34 -0
  35. package/dist/contracts-core/speech.d.ts +39 -0
  36. package/dist/contracts-core/speech.js +17 -0
  37. package/dist/contracts-core/transcription.d.ts +48 -0
  38. package/dist/contracts-core/transcription.js +17 -0
  39. package/dist/contracts-core/video.d.ts +61 -0
  40. package/dist/contracts-core/video.js +17 -0
  41. package/dist/contracts-core.d.ts +7 -0
  42. package/dist/contracts-core.js +7 -0
  43. package/dist/index.d.ts +5 -3
  44. package/dist/index.js +4 -3
  45. package/dist/node/agent-definitions.d.ts +1 -8
  46. package/dist/node/agent-definitions.js +0 -34
  47. package/dist/node/settings.d.ts +0 -1
  48. package/dist/node/settings.js +0 -5
  49. package/dist/pinned-fetch.js +29 -3
  50. package/dist/provider-events.js +3 -4
  51. package/dist/providers/media.d.ts +1 -2
  52. package/dist/providers/media.js +1 -4
  53. package/dist/rpc.d.ts +1 -1
  54. package/dist/rpc.js +4 -4
  55. package/dist/testing/provider-conformance.d.ts +114 -5
  56. package/dist/testing/provider-conformance.js +342 -0
  57. package/dist/testing/tool-effect-store-conformance.d.ts +0 -1
  58. package/dist/testing/tool-effect-store-conformance.js +0 -3
  59. package/dist/thinking.d.ts +48 -9
  60. package/dist/thinking.js +134 -8
  61. package/docs/0.1.0-readiness.md +3 -3
  62. package/docs/a2a.md +2 -2
  63. package/docs/acp.md +3 -3
  64. package/docs/ag-ui-adoption.md +1 -1
  65. package/docs/ag-ui.md +1 -2
  66. package/docs/agent-definitions.md +1 -1
  67. package/docs/agent-events.md +5 -5
  68. package/docs/agent-identity.md +13 -2
  69. package/docs/audit-export.md +3 -3
  70. package/docs/batch-jobs.md +120 -0
  71. package/docs/cli-rpc.md +20 -9
  72. package/docs/coding-agent-tools.md +19 -19
  73. package/docs/coding-review-and-diagnostics.md +2 -2
  74. package/docs/coding-security.md +4 -4
  75. package/docs/coding-workspaces.md +2 -2
  76. package/docs/computer-use-linux.md +13 -2
  77. package/docs/context-and-skills.md +1 -1
  78. package/docs/conversations.md +4 -4
  79. package/docs/credential-storage.md +11 -7
  80. package/docs/credentials-and-redaction.md +1 -1
  81. package/docs/data-classification.md +1 -1
  82. package/docs/database-persistence.md +4 -4
  83. package/docs/dev-inspector.md +6 -6
  84. package/docs/device-adapters.md +2 -2
  85. package/docs/diagrams.md +1 -1
  86. package/docs/document-reader.md +6 -6
  87. package/docs/documents.md +5 -4
  88. package/docs/embeddings.md +112 -0
  89. package/docs/enterprise-postgres-state.md +7 -7
  90. package/docs/evaluations.md +8 -8
  91. package/docs/extensions.md +3 -3
  92. package/docs/forge-integration.md +3 -3
  93. package/docs/graft.md +2 -2
  94. package/docs/guardrails.md +1 -1
  95. package/docs/host-security.md +15 -15
  96. package/docs/image-generation.md +129 -0
  97. package/docs/impeccable.md +5 -3
  98. package/docs/index.md +60 -33
  99. package/docs/indexed-code-search.md +2 -2
  100. package/docs/language-intelligence.md +4 -4
  101. package/docs/live-testing.md +126 -0
  102. package/docs/mcp-tools.md +43 -12
  103. package/docs/middleware-hooks.md +1 -1
  104. package/docs/migrate-to-0.4.md +3 -3
  105. package/docs/migrate-to-0.5.md +122 -0
  106. package/docs/migration.md +29 -1
  107. package/docs/model-registry.md +38 -0
  108. package/docs/model-routing.md +5 -5
  109. package/docs/moderation.md +117 -0
  110. package/docs/multi-agent-patterns.md +4 -4
  111. package/docs/multimodal-content.md +26 -2
  112. package/docs/obscura.md +2 -2
  113. package/docs/observability.md +32 -7
  114. package/docs/openapi-tools.md +13 -3
  115. package/docs/operations.md +11 -0
  116. package/docs/performance.md +7 -7
  117. package/docs/persistence-credentials-multimodality-primitives.md +6 -6
  118. package/docs/policy-and-audit.md +17 -7
  119. package/docs/ponytail.md +1 -1
  120. package/docs/postgres-persistence.md +5 -5
  121. package/docs/process-sessions.md +2 -2
  122. package/docs/prompt-registry.md +7 -7
  123. package/docs/provider-caching.md +4 -0
  124. package/docs/provider-conformance.md +23 -1
  125. package/docs/provider-packages.md +39 -3
  126. package/docs/provider-primitives.md +1 -1
  127. package/docs/provider-request-policies.md +1 -1
  128. package/docs/providers/ai-sdk.md +15 -3
  129. package/docs/providers/alibaba.md +5 -1
  130. package/docs/providers/anthropic.md +4 -0
  131. package/docs/providers/azure.md +17 -1
  132. package/docs/providers/bedrock.md +15 -0
  133. package/docs/providers/clinepass.md +4 -0
  134. package/docs/providers/commandcode.md +253 -0
  135. package/docs/providers/deepseek.md +4 -0
  136. package/docs/providers/google.md +4 -0
  137. package/docs/providers/hyper.md +284 -0
  138. package/docs/providers/kimi.md +4 -0
  139. package/docs/providers/neuralwatt.md +4 -0
  140. package/docs/providers/ollama.md +15 -0
  141. package/docs/providers/openai-compatible.md +4 -0
  142. package/docs/providers/openai.md +4 -0
  143. package/docs/providers/opencode-go.md +4 -0
  144. package/docs/providers/openrouter.md +5 -1
  145. package/docs/providers/vertex.md +16 -0
  146. package/docs/providers/xai.md +4 -0
  147. package/docs/providers/zai.md +4 -0
  148. package/docs/rag.md +26 -4
  149. package/docs/release-and-install.md +103 -46
  150. package/docs/resource-loading.md +1 -1
  151. package/docs/runs-and-usage.md +14 -2
  152. package/docs/server.md +5 -5
  153. package/docs/settings-auth-trust-security.md +7 -5
  154. package/docs/sheets.md +2 -2
  155. package/docs/speech.md +126 -0
  156. package/docs/sqlite-persistence.md +4 -4
  157. package/docs/supervisors.md +3 -3
  158. package/docs/thinking-and-reasoning.md +93 -60
  159. package/docs/tool-conformance.md +1 -1
  160. package/docs/tool-execution-primitives.md +8 -8
  161. package/docs/tools.md +4 -4
  162. package/docs/web-tools.md +1 -1
  163. package/docs/wiki.md +1 -1
  164. package/docs/work-artifacts-and-review.md +17 -6
  165. package/docs/work-connectors.md +4 -4
  166. package/docs/work-tools.md +5 -5
  167. package/docs/workflow-orchestration-primitives.md +11 -11
  168. package/docs/workflows.md +5 -5
  169. package/package.json +11 -8
  170. package/templates/init/providers.json +24 -8
  171. package/docs/antigravity-agent.md +0 -207
package/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ ## [0.5.0] - 2026-09-06 (plans 055-065)
2
+
3
+ ### Changed
4
+ - **Thinking/reasoning effort is now model-aware, declared, and snapped across all first-party providers (plan 065):** core gains `applyThinkingLevelForModel(base, level, model)` (family resolution + snap + merge in one call), `parseThinkingLevel` (known portable level → canonical; other non-empty string → opaque passthrough; empty/non-string → `undefined`, fail closed), `isSupportedThinkingLevel`, `thinkingLevelsForModel`, and `snapThinkingLevel` (nearest declared by ladder distance, ties break up, below-minimum snaps up; provider-documented tables remain wire authority). `ModelCapabilities` gains `thinkingLevels?: readonly string[]`; family inference is stamp-first via the new `compat.thinkingFamily` stamp set by every first-party catalog. Two new core compat families: `google` (`{ thinkingLevel }` → `generationConfig.thinkingConfig`) and `output_config_effort` (`{ output_config: { effort } }` → Anthropic Messages / DeepSeek anthropic-format). Per-provider: OpenAI catalogs declare per-family effort tables (gpt-5.1 `none/low/medium/high` default `none`; gpt-5.2 `none`–`xhigh` default `medium`) with Responses-side clamping; Google stamps per-model thinkingLevel sets (3.x) and budget ranges (2.5, `none` → range minimum); Anthropic emits generation-aware thinking (`adaptive` on 4.6+, legacy 4.5 budget default 10000) with per-model effort declarations; Azure/Vertex/Bedrock forward sanitized thinking compat through `buildBodyExtra` (previously silently dropped); Kimi K3, Z.AI GLM-5.2/5.3, and DeepSeek declare documented snap tables; xAI now sends `reasoning_effort` (grok-4.6 `low`–`xhigh`, grok-4.5 `low/medium/high`, grok-4.3 `none`–`high`) instead of dropping it; OpenRouter derives declared levels from the models API (`reasoning.supported_efforts`); Hyper snaps instead of dropping out-of-set efforts and derives levels from the live `effort_levels` list; ClinePass declares levels by inverting its slot maps; the shared Anthropic-Messages route gained `thinking`/`effort` hook slots (hyper/commandcode bind resolvers; opencode-go deleted its private serializer copy for the shared route); commandcode/opencode-go carry provenance-commented level tables; Alibaba/Qwen maps the `thinking_type` family onto `enable_thinking` with thinking-only models never disabled; Ollama/NeuralWatt declare + snap `reasoning_effort`. `@arnilo/prism-memory` workers (LLM compaction, observational memory) now use the single model-aware adapter. Conformance: a cross-catalog walk (`thinking-conformance.test.ts`) machine-checks all 133 reasoning-capable catalog models (declarations + legal wire fields + no verbatim leakage), generated evidence matrix `docs/_evidence/thinking-coverage-2026-09-05.md`, and env-gated live probes pin the riskiest wire changes (Anthropic `output_config.effort` + legacy-field outcome, Google `thinkingLevel`, xAI/Kimi/Z.AI `reasoning_effort`, GLM-5.3 `thinking.type: disabled` rejection gate, Azure `reasoning_effort`). Breaking wire moves + new exports in `docs/migrate-to-0.5.md`; contract rewrite in `docs/thinking-and-reasoning.md`; per-provider pages document declared levels, wire fields, and snapping.
5
+
6
+
7
+ ### Changed
8
+ - **CLI real-provider support (plan 064 Task 5):** `prism --provider <id>` now runs real providers, not just the built-in mock — provider ids resolve through the init provider catalog (`templates/init/providers.json`, single source shared with `prism init` scaffolding via new `factoryModule`/`factoryExport` fields): the CLI dynamically imports the provider factory from the installed `@arnilo/prism-providers/*` package and reads the catalog's credential env var. `--mode print|json|rpc` all honor it; RPC's session factory became async-capable (`AgentSession | Promise<AgentSession>`) to support the dynamic import. Without `--provider` the CLI still fails with a usage error (unchanged contract). Covered by the new packed-install live journey `scripts/e2e-cli-live.test.mjs` (scaffold + `providers add` + print/json/rpc over the real wire + full-transcript secret scan; matrix entry `cli/journey`).
9
+ - **`@arnilo/prism-mcp` completes the MCP 2026-07-28 adoption (plan 063):** modular TypeScript SDK v2 (`@modelcontextprotocol/client` + `@modelcontextprotocol/server` 2.0.0 exact pins) replaces the monolithic `@modelcontextprotocol/sdk` 1.30.0; modern client negotiation (`legacy`/`auto`/`{pin}` with one bounded discovery probe), negotiated-era exposure, SDK-managed routing headers with `Mcp-Param-*` mirroring (SEP-2243), SDK subscriptions + explicit-cursor lists + `callTool({toolDefinition})`, SEP-2549 list-cache hints (private per principal by default), elicitation via SDK MRTR auto-fulfilment with `maxMrtrRounds` (roots/sampling callbacks `@deprecated` per SEP-2577), dual-era HTTP/stdio serving through `createMcpHandler`/`serveStdio` with host/origin gates in front, and 2026-07-28-conformant OAuth (issuer-keyed storage, CIMD preferred, RFC 9207 `iss` validation, explicit insufficient-scope policy). Tasks (`io.modelcontextprotocol/tasks`) intentionally not advertised; draft-era task vocabulary fails closed. Conformance: official suite (latest published spec version 2025-11-25 — no 2026-07-28 scenarios upstream yet) 20/20 expressible scenarios green against the dual-era serving fixture with the remaining 14 scenarios recorded as documented Prism boundaries in `scripts/mcp-conformance-2026-baseline.yaml`; two real defects found and fixed by the matrix (no-argument prompts rejected `undefined` args; tool inputSchema lost `$schema`/`$defs`/`$ref`/`additionalProperties` through the zod round-trip). Full migration table + legacy-session timeline in `docs/migration.md`; canonical API in `docs/mcp-tools.md`.
10
+
11
+
12
+ ### Changed
13
+ - **pdf-parse 1.1 → 2.4 migration (plan 062, parser CVE class):** `@arnilo/prism-coding-tools` document-reader moves `pdf-parse` `^1.1.1` → `^2.4.5` (optional peer) and the two optional peers (`pdf-parse`, `mammoth`) become explicit devDependencies so the fixture suites actually run in this repo (previously they self-skipped via `PEERS_OK`). v2 replaced the v1 `pdf(buffer) → {numpages, text}` function with a `PDFParse` class: adapter now constructs `new PDFParse({ data, isEvalSupported: false })`, calls `getText({ pageJoiner: "" })`, and `destroy()`s in `finally`; page count reads `result.total` instead of `numpages`. `isEvalSupported: false` hard-disables pdf.js `eval` of PDF functions — the reader contract forbids embedded script execution (v1 did not disable it). Engine constraints for hosts: v2 parses in a worker thread (the `data` TypedArray is transferred, so the caller must not reuse the buffer — the adapter passes a fresh view per extract), requires Node ≥ 20.16 (repo already `engines.node >=20`), and ships CJS + ESM builds with a browser bundle; bundlers must resolve the `import`/`require` conditions of the new exports map. Golden deltas: extracted text for the committed fixtures is byte-identical to v1 modulo trailing `\n` (page-boundary markers suppressed via `pageJoiner: ""`); the 1000-page budget fixture parses in ~223ms (ceiling 2000ms, v1 baseline ~162ms — no regression). New fuzz smoke: a `%PDF-`-magic 256KiB garbage buffer rejects within the budget ceiling instead of hanging. Full coding-tools suite 646 pass / 0 fail; workspace typecheck green; root suite green.
14
+ - **better-sqlite3 12 → 13 migration (plan 062):** `@arnilo/prism-core` moves `better-sqlite3` `^12.11.1` → `^13.0.3` (optional peer) with `@types/better-sqlite3` `^7.6.13` → `^9.6.0`. Upstream 13 is an N-API rewrite (`node-addon-api`): no removed or changed APIs — only additions (`db.explain()`, `statement.toString()`) and a `SqliteError` cross-realm fix. Packaging change for hosts: 13 removes the deprecated `prebuild-install` and bundles prebuilt N-API binaries for supported platforms directly in the package (one binary across Node versions — no per-Node rebuild); unsupported platform/arch compiles from source during install as before (`npm rebuild better-sqlite3` after a toolchain change). No SQL construction or prepared-statement layout changed in the adapter (statements remain hoisted `db.prepare` consts); all sqlite session/enterprise/prompt suites green (34 pass / 1 env-gated skip), packed-tarball fresh-install native-load + round-trip smoke green, benchmark gate medians green.
15
+ - **Keyring 2 migration (plan 062, credential-storage boundary):** `@arnilo/prism-core` moves `@napi-rs/keyring` `^1.3.0` → `^2.0.0`. Upstream 2.0 changes error reporting, not success paths: reads (`getPassword`/`getSecret`) and deletes (`deleteCredential`) now reject on locked/inaccessible stores instead of silently resolving `undefined`/`false`; a missing credential still resolves `undefined`, and `false` from a delete now guarantees absence. The keychain adapter (`packages/prism-core/src/credentials/node/keychain-store.ts`) already routes every native call through `runKeychainOperation` → typed-error mapping (`CredentialStoreLockedError`/`CredentialStoreUnavailableError`/`CredentialStoreTimeoutError`), so host-visible behavior is unchanged in shape and strictly safer in semantics: a locked keychain now surfaces as a typed locked-store error instead of masquerading as an empty vault (which under 1.x could lead a `set()` to overwrite the real vault). Async signatures (`signal` parameters, binary `getSecret`, `AsyncEntry`) unchanged. Evidence: full credential-node suite 75/75 green; live Secret Service round-trip (`PRISM_TEST_KEYCHAIN=1`, credential + OAuth set/get/delete) green on Linux keyring 2.0.0; macOS/Windows legs are not exercisable in CI (all legs ubuntu) — the round-trip test ships gated per platform via `PRISM_TEST_KEYCHAIN=1`.
16
+
17
+ ### Removed
18
+ - **Dead-export removal wave (plan 058 task 3, breaking):** all 27 unused public exports classified `deprecate`/`remove` in `docs/_evidence/dead-export-verification-2026-09-03.md` are deleted in one coordinated 0.5.0 cut (deprecated at 0.5.0-precursor in the same cycle). Per package — `@arnilo/prism`: `statusFromState`, `isInitProvider`, `isInitTemplate`, `parseContextFile`, `parseToolFile`, `defaultUserSettingsPath`, `resolveProviderMediaBlock`, `ProviderSecretLeakConformanceOptions`, `runToolEffectStoreConformance`; `@arnilo/prism-core`: `OidcIdentityVerifierResult`, `assertDiffLines`, `ResolvedPromptLimits`, `encodeMetadata`, `parseListOffsetCursor`, `nodeKindOf`; `@arnilo/prism-memory`: `DEFAULT_MAX_PROMPT_CHARS`, `GRAFT_RESOLVE_ERROR_CODE`, `LinterOptions`, `ResolvedGraftExtension`, `WikiCategory`; `@arnilo/prism-coding-tools`: `codingSha256Hex`, `DEFAULT_MAX_REVIEW_DELTA_ENTRIES`, `HARD_MAX_REVIEW_DELTA_ENTRIES`, `indexErrorCode`, `PONYTAIL_PEER_RANGE`, `CavemanSkillName`; `@arnilo/prism-providers`: `withOpenRouterCacheMarker`. All had zero in-repo references (test-only false positives were reclassified keep by the verification sweep; security guards are keep-classified and untouched). Compat baselines regenerated at the cut; intentional breaks recorded in `docs/migration.md`; removal table + replacements in `docs/migrate-to-0.5.md`. `scripts/unused-report.json` deadExports → 0 (the scan now counts test/example/script/template usage and suppresses keep-classified zero-ref exports). All manifests bump 0.4.x → 0.5.0 lockstep; internal ranges `^0.4.0` → `^0.5.0`. Security keeper surface (ownership/checkpoint guards, `secureCompare`, `zeroBuffer`, sandbox path-escape guard, RAG scope guard, MCP content-bounds guard, secret-leak conformance assert) unchanged.
19
+ - Removed `@arnilo/prism-antigravity-agent` workspace, package surface, active documentation, release wiring, and host-conformance branch. Delegated CLI execution belongs to hosts and composes through generic process, tool, MCP, supervisor, or A2A contracts.
20
+
21
+ ### Changed
22
+ - Updated `@nanonets/graft` peer support to `^0.16.0`, AG-UI packages to `0.0.59`, `@ai-sdk/provider` to `4.0.10`, Office Open packages to `0.13.1`, Biome to `2.5.11`, and `fast-xml-parser` to `5.11.1`.
23
+ - Refreshed lockfile transitive dependencies; `npm audit` reports zero vulnerabilities.
24
+
25
+ ### Deprecated
26
+ - **Type-level deprecation wave (plan 058, task 2):** 27 unused public exports now carry `@deprecated` JSDoc naming "no replacement" and the 0.5.0 removal plan; runtime behavior is unchanged (no warnings). Per package — `@arnilo/prism`: `statusFromState`, `isInitProvider`, `isInitTemplate`, `parseContextFile`, `parseToolFile`, `defaultUserSettingsPath`, `resolveProviderMediaBlock`, `ProviderSecretLeakConformanceOptions`, `runToolEffectStoreConformance`; `@arnilo/prism-core`: `OidcIdentityVerifierResult`, `assertDiffLines`, `encodeMetadata`, `parseListOffsetCursor`, `nodeKindOf`, `ResolvedPromptLimits` (marked for removal); `@arnilo/prism-memory`: `DEFAULT_MAX_PROMPT_CHARS`, `GRAFT_RESOLVE_ERROR_CODE`, `LinterOptions`, `ResolvedGraftExtension` (marked for removal), `WikiCategory` (marked for removal); `@arnilo/prism-coding-tools`: `codingSha256Hex`, `DEFAULT_MAX_REVIEW_DELTA_ENTRIES`, `HARD_MAX_REVIEW_DELTA_ENTRIES`, `indexErrorCode`, `PONYTAIL_PEER_RANGE`, `CavemanSkillName` (marked for removal); `@arnilo/prism-providers`: `withOpenRouterCacheMarker`. All are removed in the 0.5.0 cut above (plan 058 task 3, `docs/migrate-to-0.5.md`).
27
+
28
+ ## [0.4.1] - 2026-09-02 (plan 055)
29
+
30
+ ### Changed
31
+ - **Two new first-class provider adapters (plan 055):** `@arnilo/prism-providers` (0.4.0 → 0.4.1, Decision B changed-package cut) gains `./hyper` (Charm Hyper — dual-route `/v1/chat/completions` + `/v1/messages` adapter with cache_control on Anthropic-shaped models and Hypercredit cost telemetry) and `./commandcode` (Command Code Provider API — dual-route `/provider/v1/chat/completions` + `/provider/v1/messages` adapter with cache_control on Claude tiers and optional ZDR). Both keep zero network at setup, caller-gated model discovery, provider-owned header enforcement, secret redaction, and offline conformance suites; live probes are operator-gated behind `PRISM_LIVE_PROVIDER_TESTS=1` + provider key. Public symbols and trust boundaries unchanged except the two new subpaths; peer `@arnilo/prism@^0.4.0` unchanged; no root package change (docs only).
32
+
1
33
  ## [0.4.0] - 2026-09-01 (plan 054)
2
34
 
3
35
  ### Changed
@@ -62,7 +94,7 @@
62
94
 
63
95
  ### Added
64
96
  - `@arnilo/prism-obscura` (new workspace package, 0.3.0): optional Obscura headless-browser engine over a **host-installed** binary — fail-closed `spawnObscuraProcess` lifecycle (absolute shell-free command, Docker argv, bounded readiness, group close), `createObscuraMcpTools` bridging the complete advertised MCP surface (reads effect-free; mutations and unknown future tools exclusive/serialized; `obscura_` prefix; loopback-default HTTP), `connectObscuraCdp` managed/external CDP + Playwright `connectOverCDP` composition, and `createObscuraWebTools` (standard `web_search`/`web_fetch` through one replaceable HTML search profile plus native `obscura_fetch`/`obscura_scrape`; public-HTTP(S)-only URL validation, byte/count/timeout caps, `allowEval`-gated custom expressions, untrusted-content labeling). Deliberately omitted from `prism-all`/`prism-base`/`prism-sdk` umbrella profiles — installation does not supply the binary/image; consumers install it explicitly.
65
- - Cross-host conformance (`scripts/obscura-host-conformance.test.mjs`, wired into `npm test`): the same Obscura `ToolDefinition[]` executes through core agent sessions, the Prism MCP server, the server handler, AG-UI, ACP, workflow nodes, supervisor children, and Antigravity delegated exposure — no host branch; host authorization/selection deny before execution; abort kills owned children.
97
+ - Cross-host conformance (`scripts/obscura-host-conformance.test.mjs`, wired into `npm test`): the same Obscura `ToolDefinition[]` executes through core agent sessions, the Prism MCP server, the server handler, AG-UI, ACP, workflow nodes and supervisor children — no host branch; host authorization/selection deny before execution; abort kills owned children.
66
98
  - New required peers: `@arnilo/prism-obscura` peers `@arnilo/prism` and `@arnilo/prism-web-tools` (reused citation/normalized shapes; `WebProvider` widened with `"obscura"`); `@arnilo/prism-obscura` also peers `@arnilo/prism-mcp`, `@arnilo/prism-browser`, and optional `playwright-core@1.61.0`.
67
99
 
68
100
  ## [0.3.1] - 2026-08-26
package/README.md CHANGED
@@ -4,9 +4,8 @@
4
4
  bring their tools, providers, credentials, storage, and UI; Prism supplies the
5
5
  common contracts, registries, agent/session runtime, replaceable input/prompt
6
6
  and compaction strategies, CLI/RPC adapters, and first-party provider/compaction
7
- packages. The current 0.3.3 line contains 11 publishable manifests, including
8
- optional Linux desktop control, a versioned prompt registry, and independent
9
- package versioning after the final lockstep cut. Prism defines contracts, not apps.
7
+ packages. The current 0.4 line publishes the generated package inventory (see
8
+ [Packages](#packages)) with explicit family subpaths and independent package versioning. Prism defines contracts, not apps.
10
9
 
11
10
  ## Current scope
12
11
 
@@ -20,7 +19,7 @@ package versioning after the final lockstep cut. Prism defines contracts, not ap
20
19
  OpenAI/OpenRouter use best-effort explicit cache hints, NeuralWatt uses
21
20
  best-effort implicit prefix caching, and other providers have route/model-specific
22
21
  or no cache-control support; see [docs/provider-caching.md](docs/provider-caching.md).
23
- - **First-party packages**: seventeen provider adapter subpaths, two compaction strategies,
22
+ - **First-party packages**: nineteen provider adapter subpaths, two compaction strategies,
24
23
  coding tools/security, JSON Schema validation, MCP, workflows, OpenTelemetry,
25
24
  encrypted credentials, SQLite/PostgreSQL persistence, Linux desktop control,
26
25
  and manifest-only install profiles.
@@ -55,13 +54,13 @@ a non-optional peer. Install atomic packages directly or choose a manifest-only
55
54
  family/profile; profiles install packages but expose no alias exports and activate nothing:
56
55
 
57
56
  ```bash
58
- npm install @arnilo/prism @arnilo/prism-providers # core + all 17 provider adapters
57
+ npm install @arnilo/prism @arnilo/prism-providers # core + all provider adapters
59
58
  npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-memory # replaces prism-base
60
59
  npm install @arnilo/prism @arnilo/prism-coding-tools @arnilo/prism-mcp @arnilo/prism-providers # replaces prism-code
61
60
  npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-mcp @arnilo/prism-providers # replaces prism-sdk
62
61
  npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-providers # pick families explicitly (no umbrella)
63
- npm install @arnilo/prism-server @arnilo/prism-workflows # optional Web API boundary
64
- npm install @arnilo/prism-supervisor # optional local delegation + A2A 1.0
62
+ npm install @arnilo/prism-core/runtime/server @arnilo/prism-core/runtime/workflows # optional Web API boundary
63
+ npm install @arnilo/prism-core/runtime/supervisor # optional local delegation + A2A 1.0
65
64
  npm install @arnilo/prism-web-tools # unified web tools family (root search + /browser + /obscura subpaths)
66
65
  ```
67
66
 
@@ -158,19 +157,22 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
158
157
 
159
158
  ## Packages
160
159
 
161
- | package | purpose |
162
- |---------|---------|
163
- | `@arnilo/prism` | core contracts, runtime, registries, CLI/RPC |
164
- | `@arnilo/prism-core` | runtime/sessions/governance/credentials/enterprise/work/validation family |
165
- | `@arnilo/prism-providers` | all 17 first-party adapters as `/<adapter>` subpaths (openai, anthropic, google, azure, bedrock, vertex, deepseek, xai, zai, alibaba, kimi, clinepass, neuralwatt, ollama, opencode-go, openrouter, ai-sdk) |
166
- | `@arnilo/prism-coding-tools` | `/agent`, `/security`, `/document-reader`, `/openapi`, `/computer-use-linux`, `/dev`, `/caveman`, `/ponytail`, `/impeccable` |
167
- | `@arnilo/prism-web-tools` | Brave/Exa/Firecrawl plus peer-gated `/browser` and `/obscura` |
168
- | `@arnilo/prism-memory` | memory plus `/rag`, `/compaction/{llm,observational-memory}`, `/graft`, `/wiki` |
169
- | `@arnilo/prism-mcp` | MCP client/server/OAuth interop |
170
- | `@arnilo/prism-acp-agent` | ACP adapter |
171
- | `@arnilo/prism-ag-ui` | AG-UI/A2A/A2UI adapter |
172
- | `@arnilo/prism-antigravity-agent` | Antigravity CLI adapter |
173
- | `@arnilo/prism-office` | `/documents`, `/sheets`, `/diagrams` |
160
+ <!-- generated:package-truth:inventory begin -->
161
+ **10 publishable manifests** — root `@arnilo/prism` plus 9 workspace packages (3 `prism-*` family packages, 6 capability packages). Generated by `node scripts/package-truth.mjs --emit-docs` — do not hand-edit.
162
+
163
+ | package | version | notes |
164
+ | --- | --- | --- |
165
+ | `@arnilo/prism` | 0.5.0 | core — runtime, CLI/RPC, templates, docs |
166
+ | `@arnilo/prism-coding-tools` | 0.5.0 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
167
+ | `@arnilo/prism-core` | 0.5.0 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
168
+ | `@arnilo/prism-providers` | 0.5.0 | family — all provider adapters as `/<adapter>` subpaths |
169
+ | `@arnilo/prism-acp-agent` | 0.5.0 | capability — ACP adapter |
170
+ | `@arnilo/prism-ag-ui` | 0.5.0 | capability — AG-UI/A2A/A2UI adapter |
171
+ | `@arnilo/prism-mcp` | 0.5.0 | capability — MCP client/server/OAuth interop |
172
+ | `@arnilo/prism-memory` | 0.5.0 | capability — memory plus /rag, /compaction/*, /graft, /wiki subpaths |
173
+ | `@arnilo/prism-office` | 0.5.0 | capability — /documents, /sheets, /diagrams subpaths |
174
+ | `@arnilo/prism-web-tools` | 0.5.0 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
175
+ <!-- generated:package-truth:inventory end -->
174
176
 
175
177
  ## Scripts
176
178
 
@@ -179,6 +181,7 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
179
181
  | `npm run build` | Compile TypeScript to `dist/` (core + workspaces) |
180
182
  | `npm run typecheck` | Type-check without emitting |
181
183
  | `npm test` | Build + run network-free tests |
184
+ | `npm run test:live` | Run live suites whose credentials are present (skip the rest) |
182
185
  | `prism --help` | CLI help |
183
186
 
184
187
  ## Non-goals (v1)
@@ -1,4 +1,4 @@
1
- import type { Agent, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, AgentRunStatusResult, CheckpointRecord, CheckpointStore, JsonValue, Message, ModelConfig, NestedRunRef, OwnershipScope, RunDecision, RunLimitCounters, StickyDecision, ToolCallContent } from "./contracts.js";
1
+ import type { Agent, AgentRunInterruption, AgentRunRef, AgentRunState, AgentRunStateOptions, CheckpointRecord, CheckpointStore, JsonValue, Message, ModelConfig, NestedRunRef, OwnershipScope, RunDecision, RunLimitCounters, StickyDecision, ToolCallContent } from "./contracts.js";
2
2
  import type { SecretRedactor } from "./redaction.js";
3
3
  import { type LoadedSkillBodiesEntry } from "./skill-load.js";
4
4
  export declare const AGENT_RUN_STATE_NAMESPACE = "prism.agent-run";
@@ -75,7 +75,6 @@ export declare function saveAgentRunState(input: {
75
75
  readonly record: CheckpointRecord;
76
76
  readonly state: StoredAgentRunState;
77
77
  }>;
78
- export declare function statusFromState(state: StoredAgentRunState, version: number): AgentRunStatusResult;
79
78
  export declare function publicState(state: StoredAgentRunState): AgentRunState;
80
79
  export declare function initialAgentRunState(input: {
81
80
  readonly agent: Agent;
@@ -127,9 +127,6 @@ export async function saveAgentRunState(input) {
127
127
  });
128
128
  return { record, state: { ...bounded, version: record.version } };
129
129
  }
130
- export function statusFromState(state, version) {
131
- return { state: publicState({ ...state, version }), version };
132
- }
133
130
  export function publicState(state) {
134
131
  const { input: _input, pending: _pending, pendingCalls: _pendingCalls, nestedRuns: _nestedRuns, interruptBeforeTool: _interruptBeforeTool, counters: _counters, deadlineAt: _deadlineAt, ...publicValue } = state;
135
132
  return publicValue;
@@ -0,0 +1,6 @@
1
+ /** Context-assembly + runInternal orchestrator (plan 059). */
2
+ import type { ActiveDurableRun } from "../../agent-approval.js";
3
+ import type { AgentRunResult, RunOptions } from "../../contracts.js";
4
+ import type { AgentInput } from "../../input.js";
5
+ import type { SessionHost } from "./types.js";
6
+ export declare function executeRun(session: SessionHost, input: AgentInput, options: RunOptions, runId: string, resumed?: ActiveDurableRun): Promise<AgentRunResult>;
@@ -0,0 +1,391 @@
1
+ /** Context-assembly + runInternal orchestrator (plan 059). */
2
+ import { AgentRunSuspended } from "../../agent-approval.js";
3
+ import { resolveLoop, resolveToolConcurrency } from "../../agent-loops.js";
4
+ import { validateRunStateOptions } from "../../agent-run-state.js";
5
+ import { activeTools } from "../../agent-tool-dispatch.js";
6
+ import { AgentLoopStateError, AgentRunError, AgentRunStateError } from "../../contracts.js";
7
+ import { assertGuardrailsAllowed, runGuardrails } from "../../guardrails.js";
8
+ import { identityTelemetryAttributes, ownershipFromIdentity, resolveRunIdentity } from "../../identity.js";
9
+ import { assembleProviderInput } from "../../input.js";
10
+ import { errorToErrorInfo, redactRunLedgerRecord } from "../../redaction.js";
11
+ import { RunLimitError, RunLimitTracker, resolveRunLimits } from "../../run-limits.js";
12
+ import { createSessionEntry } from "../../session-stores.js";
13
+ import { resolveSkillsDisclosure } from "../../skill-disclosure.js";
14
+ import { applyRestoredSkillBodies } from "../../skill-load.js";
15
+ import { assertStructuredOutputRequestSupported, resolveRunProviderOptions } from "../../structured-output.js";
16
+ import { composeSystemPrompt, mergeSystemPromptConfig } from "../../system-prompts.js";
17
+ import { resolveToolResultFold } from "../../tool-result-fold.js";
18
+ import { createSearchToolsTool, createToolSearchState, resolveToolsDisclosure } from "../../tool-search.js";
19
+ import { createToolRegistry } from "../../tools.js";
20
+ import { bridgeAbort, createUsageAccumulator, inputToMessages, isDurableLoop, isSteerSoftInterrupt, mergeGuardrails, throwIfAborted, } from "../helpers.js";
21
+ import { cleanupRun, persistDurable, persistSucceeded, suspendDurable } from "./persist.js";
22
+ import { generateWithRetry, recordProviderUsage } from "./provider-round.js";
23
+ import { bindChargeToolRound, bindDispatchToolCall, replayDurableNestedAndPending, runLoopUntilSettled, suspendGatedRound, } from "./tool-round.js";
24
+ const PROMPT_VERSION_MAX_NAME_BYTES = 256;
25
+ const PROMPT_VERSION_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
26
+ function assertPromptVersionRef(ref) {
27
+ if (ref === undefined)
28
+ return undefined;
29
+ if (typeof ref !== "object" || ref === null)
30
+ throw new TypeError("RunOptions.promptVersion must be a PromptVersionRef object");
31
+ const { name, version, hash } = ref;
32
+ if (typeof name !== "string" || name.length === 0 || Buffer.byteLength(name, "utf8") > PROMPT_VERSION_MAX_NAME_BYTES) {
33
+ throw new TypeError(`RunOptions.promptVersion.name must be 1-${PROMPT_VERSION_MAX_NAME_BYTES} UTF-8 bytes`);
34
+ }
35
+ if (!Number.isInteger(version) || version < 1 || version > 0x7fffffff) {
36
+ throw new TypeError("RunOptions.promptVersion.version must be an integer in [1, 2147483647]");
37
+ }
38
+ if (typeof hash !== "string" || !PROMPT_VERSION_HASH_PATTERN.test(hash)) {
39
+ throw new TypeError('RunOptions.promptVersion.hash must be "sha256:" plus 64 lowercase hex characters');
40
+ }
41
+ return ref;
42
+ }
43
+ async function assembleRoundContext(params) {
44
+ const { session, input, options, runId, resumed, controller, model, startedAt, promptVersion, metadata, limits, runUsage } = params;
45
+ session.resolveRunProvider(options);
46
+ throwIfAborted(controller.signal);
47
+ session.emit({ type: "agent_started", sessionId: session.id, runId });
48
+ if (resumed)
49
+ session.emit({ type: "agent_resumed", sessionId: session.id, runId, version: resumed.version });
50
+ const startRecord = {
51
+ id: runId,
52
+ sessionId: session.id,
53
+ branchId: session.currentLeafId,
54
+ model,
55
+ provider: model.provider,
56
+ idempotencyKey: session.activeIdempotencyKey,
57
+ status: "running",
58
+ startedAt,
59
+ ...(promptVersion ? { promptVersion } : {}),
60
+ ...session.activeOwnership,
61
+ };
62
+ await session.activeLedger?.appendRun(redactRunLedgerRecord(startRecord, session.activeRedactor));
63
+ await session.rebuildHistory();
64
+ const { registry: baseRegistry, tools: activeToolList } = activeTools(session.agent.config.tools);
65
+ const toolsDisclosure = resolveToolsDisclosure(options.toolsDisclosure, session.agent.config.toolsDisclosure);
66
+ const toolSearch = toolsDisclosure === "search" && activeToolList.length > 0
67
+ ? {
68
+ state: createToolSearchState({
69
+ tools: activeToolList,
70
+ activated: session.activatedTools,
71
+ search: session.agent.config.toolsSearch,
72
+ }),
73
+ }
74
+ : undefined;
75
+ const searchTool = toolSearch ? createSearchToolsTool(toolSearch.state) : undefined;
76
+ const registry = searchTool ? createToolRegistry([...activeToolList, searchTool]) : baseRegistry;
77
+ const tools = searchTool ? [...activeToolList, searchTool] : activeToolList;
78
+ const activeSkills = session.resolveRunSkills(options, tools);
79
+ session.activeRunSkills = activeSkills;
80
+ if (options.model && JSON.stringify(options.model) !== JSON.stringify(session.agent.config.model)) {
81
+ await session.appendEntry(createSessionEntry({
82
+ sessionId: session.id,
83
+ parentId: session.currentLeafId,
84
+ runId,
85
+ kind: "model_change",
86
+ previousModel: session.agent.config.model,
87
+ model: options.model,
88
+ }));
89
+ }
90
+ const inputMessages = inputToMessages(input).map((message) => session.redact(message));
91
+ const inputGuardrails = await runGuardrails({
92
+ stage: "input",
93
+ guardrails: session.activeGuardrails,
94
+ value: inputMessages,
95
+ context: { sessionId: session.id, runId, metadata, signal: controller.signal },
96
+ redactor: session.activeRedactor,
97
+ emit: (event) => session.emit(event),
98
+ });
99
+ const approvedByResume = resumed !== undefined && inputGuardrails.terminal?.action === "interrupt" && session.activeDurable !== undefined;
100
+ if (inputGuardrails.terminal?.action === "interrupt" && session.activeDurable && !approvedByResume) {
101
+ const interruption = { kind: "input_guardrail", reason: inputGuardrails.terminal.reason ?? "Input requires approval" };
102
+ throw new AgentRunSuspended(await suspendDurable(session, { runId, model, limits, interruption, messages: inputMessages }), interruption);
103
+ }
104
+ if (inputGuardrails.terminal && !approvedByResume)
105
+ assertGuardrailsAllowed(inputGuardrails);
106
+ for (const message of inputMessages)
107
+ await session.appendMessage(message, runId);
108
+ await session.autoCompact(runId, options, controller.signal, inputMessages);
109
+ const maxToolRounds = resolveRunLimits(session.agent.config.limits, options.limits).maxToolRounds;
110
+ const systemInstructions = composeSystemPrompt(mergeSystemPromptConfig(session.agent.config.systemPrompt, options.systemPrompt), {
111
+ base: session.agent.config.instructions,
112
+ });
113
+ const contextProviders = [...(session.agent.config.context ?? []), ...activeSkills.flatMap((skill) => skill.context ?? [])];
114
+ const providerOptions = resolveRunProviderOptions(options, session.agent.config);
115
+ assertStructuredOutputRequestSupported(options.model ?? session.agent.config.model, providerOptions);
116
+ const validate = options.validate ?? session.agent.config.validator;
117
+ const instructionInjectors = options.instructionInjectors ?? session.agent.config.instructionInjectors ?? [];
118
+ const inputLayout = options.inputLayout ?? session.agent.config.inputLayout;
119
+ const loop = resolveLoop(options, session.agent.config);
120
+ session.activeLoop = loop;
121
+ const toolConcurrency = resolveToolConcurrency(options, session.agent.config);
122
+ session.activeLoopTurn = 1;
123
+ const ctx = {
124
+ session,
125
+ input,
126
+ options,
127
+ runId,
128
+ resumed,
129
+ controller,
130
+ model,
131
+ metadata,
132
+ limits,
133
+ registry,
134
+ tools,
135
+ activeSkills,
136
+ inputMessages,
137
+ maxToolRounds,
138
+ systemInstructions,
139
+ contextProviders,
140
+ providerOptions,
141
+ validate,
142
+ instructionInjectors,
143
+ inputLayout,
144
+ loop,
145
+ toolConcurrency,
146
+ toolsDisclosure,
147
+ assembledTurn: false,
148
+ artifactFinished: false,
149
+ artifactFailedInfo: undefined,
150
+ runUsage,
151
+ loopCtx: undefined,
152
+ };
153
+ const loopCtx = {
154
+ sessionId: session.id,
155
+ runId,
156
+ metadata,
157
+ signal: controller.signal,
158
+ history: session.history,
159
+ input,
160
+ inputMessages,
161
+ maxToolRounds,
162
+ toolConcurrency,
163
+ restoredLoopState: resumed?.state?.loopState?.snapshot,
164
+ assemble: async (nextInput, toolResults, turn) => {
165
+ limits.charge("maxTurns");
166
+ const request = await assembleProviderInput({
167
+ model: options.model ?? session.agent.config.model,
168
+ input: nextInput,
169
+ history: session.history,
170
+ summaries: (await session.snapshot()).summaries,
171
+ toolResults: toolResults ?? [],
172
+ turn,
173
+ instructionInjectors,
174
+ inputLayout,
175
+ systemInstructions,
176
+ inputBuilder: session.agent.config.inputBuilder,
177
+ promptBuilder: session.agent.config.promptBuilder,
178
+ contextProviders,
179
+ skills: session.restoredSkillBodies.length ? applyRestoredSkillBodies(activeSkills, session.restoredSkillBodies) : activeSkills,
180
+ skillsDisclosure: resolveSkillsDisclosure(options.skillsDisclosure, session.agent.config.skillsDisclosure),
181
+ toolsDisclosure,
182
+ toolsSearch: session.agent.config.toolsSearch,
183
+ activatedTools: session.activatedTools,
184
+ toolResultFold: resolveToolResultFold(options.toolResultFold, session.agent.config.toolResultFold),
185
+ loadedSkills: session.loadedSkills,
186
+ tools,
187
+ resourceLoader: session.agent.config.resourceLoader,
188
+ permission: session.agent.config.permission,
189
+ trust: session.agent.config.trust,
190
+ providerOptions,
191
+ redactor: session.activeRedactor,
192
+ middleware: session.agent.config.middleware,
193
+ sessionId: session.id,
194
+ runId,
195
+ metadata,
196
+ signal: controller.signal,
197
+ });
198
+ ctx.assembledTurn = true;
199
+ return request;
200
+ },
201
+ chargeToolRound: bindChargeToolRound(ctx),
202
+ generate: async (request) => {
203
+ await suspendGatedRound(ctx);
204
+ if (!ctx.assembledTurn)
205
+ limits.charge("maxTurns");
206
+ ctx.assembledTurn = false;
207
+ const policyResult = await session.applyProviderRequestPolicies(request, runId, options, metadata, controller.signal);
208
+ const middlewareRequest = (await session.agent.config.middleware?.run("provider_request", policyResult.request)) ?? policyResult.request;
209
+ try {
210
+ return await generateWithRetry(session, session.redactProviderRequest(middlewareRequest), runId, options, controller.signal, policyResult.secrets, session.activeLoopTurn, (turnUsage, turn, attempt) => recordProviderUsage(ctx, turnUsage, turn, attempt));
211
+ }
212
+ catch (error) {
213
+ if (isSteerSoftInterrupt(error)) {
214
+ return { content: [], calls: [], started: false, usage: undefined };
215
+ }
216
+ throw error;
217
+ }
218
+ },
219
+ isToolCallExclusive: (call) => registry.get(call.name)?.exclusive === true,
220
+ dispatchToolCall: bindDispatchToolCall(ctx),
221
+ appendMessage: (message) => session.appendMessage(message, runId),
222
+ hasPendingSteers: () => session.pendingSteers.length > 0,
223
+ applyPendingSteers: () => session.applyPendingSteers(runId, metadata, controller.signal),
224
+ emit: (event) => {
225
+ if (event.type === "turn_started")
226
+ session.activeLoopTurn = event.turn;
227
+ if (event.type === "artifact_finished")
228
+ ctx.artifactFinished = true;
229
+ if (event.type === "artifact_failed") {
230
+ const first = event.result.errors?.[0];
231
+ const reason = event.result.metadata?.reason;
232
+ ctx.artifactFailedInfo = {
233
+ message: first?.message ?? "artifact failed",
234
+ code: typeof reason === "string" || typeof reason === "number" ? reason : "artifact_failed",
235
+ };
236
+ }
237
+ session.emit(event);
238
+ },
239
+ };
240
+ ctx.loopCtx = loopCtx;
241
+ return ctx;
242
+ }
243
+ export async function executeRun(session, input, options, runId, resumed) {
244
+ const legacyMaxToolRounds = options.maxToolRounds;
245
+ if (legacyMaxToolRounds !== undefined) {
246
+ throw new TypeError("RunOptions.maxToolRounds was removed in 0.1.5; use RunOptions.limits.maxToolRounds instead");
247
+ }
248
+ const promptVersion = assertPromptVersionRef(options.promptVersion);
249
+ if (session.agent.config.secure &&
250
+ (options.redactor !== undefined ||
251
+ options.ownership !== undefined ||
252
+ options.validate !== undefined ||
253
+ options.effectStore !== undefined ||
254
+ options.runState !== undefined)) {
255
+ throw new AgentRunStateError("Secure agent defaults cannot be replaced per run");
256
+ }
257
+ const requestedLimits = options.limits;
258
+ const resolvedLimits = resolveRunLimits(session.agent.config.limits, requestedLimits);
259
+ const durableOptions = options.runState ?? session.agent.config.runState;
260
+ if (session.agent.config.runState && options.runState && session.agent.config.runState !== options.runState) {
261
+ throw new AgentRunStateError("RunOptions cannot replace agent durable run-state configuration");
262
+ }
263
+ if (durableOptions) {
264
+ validateRunStateOptions(durableOptions);
265
+ if (options.model || options.guardrails || options.loop || options.effectStore)
266
+ throw new AgentRunStateError("Durable runs require model, guardrails, loop, and effect store on AgentConfig for fingerprinting");
267
+ const configuredLoop = session.agent.config.loop;
268
+ if (configuredLoop && !isDurableLoop(configuredLoop)) {
269
+ throw new AgentLoopStateError("ERR_PRISM_LOOP_NOT_DURABLE", "Custom AgentLoopStrategy on a durable run requires snapshot and restore hooks");
270
+ }
271
+ }
272
+ if (session.activeRun) {
273
+ const error = new Error("Agent session already has an active run");
274
+ session.emit({ type: "error", sessionId: session.id, runId, error: errorToErrorInfo(error) });
275
+ throw error;
276
+ }
277
+ const controller = new AbortController();
278
+ const cleanupSignal = bridgeAbort(options.signal, controller);
279
+ session.activeRun = controller;
280
+ session.activeRunId = runId;
281
+ session.pendingSteers = [];
282
+ session.pendingSteerBytes = 0;
283
+ session.pendingSoftInterrupt = false;
284
+ session.activeRedactor = options.redactor ?? session.agent.config.redactor;
285
+ session.activeLedger = options.runLedger ?? session.agent.config.runLedger;
286
+ session.activeEffectStore = options.effectStore ?? session.agent.config.effectStore;
287
+ session.activeOwnership = options.ownership ?? session.agent.config.ownership;
288
+ session.activeIdentity = resolveRunIdentity(options.identity, session.agent.config.identity, session.activeOwnership);
289
+ if (session.activeIdentity && !session.activeOwnership)
290
+ session.activeOwnership = ownershipFromIdentity(session.activeIdentity);
291
+ session.activeIdempotencyKey = options.idempotencyKey ?? session.agent.config.idempotencyKey;
292
+ session.activeGuardrails = mergeGuardrails(session.agent.config.guardrails, options.guardrails);
293
+ session.activeDurable = resumed ?? (durableOptions ? { options: durableOptions, version: 0 } : undefined);
294
+ session.activeGatedRound = undefined;
295
+ if (resumed)
296
+ session.invalidateSnapshot();
297
+ const model = options.model ?? session.agent.config.model;
298
+ const startedAt = new Date().toISOString();
299
+ let runError;
300
+ let runStatus = "succeeded";
301
+ const runUsage = createUsageAccumulator();
302
+ let usage;
303
+ const metadata = {
304
+ ...session.agent.config.metadata,
305
+ ...session.metadata,
306
+ ...options.metadata,
307
+ ...(session.activeIdentity ? identityTelemetryAttributes(session.activeIdentity) : {}),
308
+ };
309
+ session.activeMetadata = metadata;
310
+ session.activePromptVersion = promptVersion;
311
+ const limits = new RunLimitTracker(resolvedLimits, {
312
+ onExceeded: (breach) => {
313
+ session.emit({ type: "run_limit_exceeded", sessionId: session.id, runId, breach });
314
+ controller.abort(new RunLimitError(breach));
315
+ },
316
+ snapshot: resumed?.state?.counters,
317
+ deadlineAt: resumed?.state?.deadlineAt,
318
+ });
319
+ session.activeLimits = limits;
320
+ session.activeLimitOutputBuffer = [session.agent.config.limits, requestedLimits].some((value) => value?.maxOutputTokens !== undefined || value?.maxTotalTokens !== undefined || value?.maxCost !== undefined);
321
+ try {
322
+ const ctx = await assembleRoundContext({
323
+ session,
324
+ input,
325
+ options,
326
+ runId,
327
+ resumed,
328
+ controller,
329
+ model,
330
+ startedAt,
331
+ promptVersion,
332
+ metadata,
333
+ limits,
334
+ runUsage,
335
+ });
336
+ await replayDurableNestedAndPending(ctx);
337
+ const resumedLoopState = resumed?.state?.loopState;
338
+ if (resumedLoopState) {
339
+ if (ctx.loop.name !== resumedLoopState.name || (ctx.loop.revision ?? "1") !== resumedLoopState.revision) {
340
+ throw new AgentLoopStateError("ERR_PRISM_LOOP_REVISION", `Loop ${resumedLoopState.name} revision ${resumedLoopState.revision} does not match the resumed durable run`);
341
+ }
342
+ ctx.loop.restore?.(resumedLoopState.snapshot);
343
+ }
344
+ const loopUsage = await runLoopUntilSettled(ctx);
345
+ if (ctx.loop.name === "generate-validate-revise" && !ctx.artifactFinished) {
346
+ throw Object.assign(new Error(ctx.artifactFailedInfo?.message ?? "artifact loop ended without a validated artifact"), {
347
+ name: "ArtifactFailed",
348
+ code: ctx.artifactFailedInfo?.code ?? "artifact_failed",
349
+ });
350
+ }
351
+ usage = runUsage.value() ?? loopUsage;
352
+ return await persistSucceeded(ctx, loopUsage);
353
+ }
354
+ catch (error) {
355
+ if (error instanceof AgentRunSuspended) {
356
+ runStatus = "suspended";
357
+ const version = error.state.version;
358
+ session.emit({ type: "agent_suspended", sessionId: session.id, runId, interruption: error.interruption, version });
359
+ return session.buildRunResult({ runId, status: "suspended", runState: error.state, interruption: error.interruption });
360
+ }
361
+ runError = errorToErrorInfo(error);
362
+ session.emit({ type: "error", sessionId: session.id, runId, error: runError });
363
+ const breach = error instanceof RunLimitError ? error.breach : limits.breach;
364
+ runStatus = breach ? "failed" : controller.signal.aborted ? "aborted" : "failed";
365
+ const runState = session.activeDurable?.state
366
+ ? await persistDurable(session, {
367
+ ...session.activeDurable.state,
368
+ status: runStatus,
369
+ interruption: undefined,
370
+ loopState: undefined,
371
+ pendingCalls: undefined,
372
+ nestedRuns: undefined,
373
+ stickyDecisions: undefined,
374
+ })
375
+ : undefined;
376
+ const result = session.buildRunResult({
377
+ runId,
378
+ status: runStatus,
379
+ usage: runUsage.value() ?? usage,
380
+ limit: breach,
381
+ error: runError,
382
+ abortReason: !breach && controller.signal.aborted ? String(controller.signal.reason) : undefined,
383
+ runState,
384
+ });
385
+ throw new AgentRunError(result, { cause: error });
386
+ }
387
+ finally {
388
+ await cleanupRun({ session, controller, cleanupSignal, runId, model, startedAt, runStatus, runError });
389
+ }
390
+ }
391
+ //# sourceMappingURL=assemble.js.map
@@ -0,0 +1,28 @@
1
+ /** Finalize/persist phase of runInternal (plan 059). */
2
+ import type { PendingToolCall, StoredAgentRunState } from "../../agent-run-state.js";
3
+ import type { AgentRunResult, AgentRunState, ErrorInfo, Message, ModelConfig, NestedRunRef, Usage } from "../../contracts.js";
4
+ import type { RunLimitTracker } from "../../run-limits.js";
5
+ import type { RoundContext, SessionHost } from "./types.js";
6
+ export declare function persistDurable(session: SessionHost, state: StoredAgentRunState): Promise<AgentRunState>;
7
+ export declare function suspendDurable(session: SessionHost, input: {
8
+ readonly runId: string;
9
+ readonly model: ModelConfig;
10
+ readonly limits: RunLimitTracker;
11
+ readonly interruption: import("../../contracts.js").AgentRunInterruption;
12
+ readonly messages?: readonly Message[];
13
+ readonly pending?: StoredAgentRunState["pending"];
14
+ readonly pendingCalls?: readonly PendingToolCall[];
15
+ /** Full replacement when provided; otherwise the recorded nested runs are preserved. */
16
+ readonly nestedRuns?: readonly NestedRunRef[];
17
+ }): Promise<AgentRunState>;
18
+ export declare function persistSucceeded(ctx: RoundContext, loopUsage: Usage | undefined): Promise<AgentRunResult>;
19
+ export declare function cleanupRun(input: {
20
+ session: SessionHost;
21
+ controller: AbortController;
22
+ cleanupSignal: () => void;
23
+ runId: string;
24
+ model: ModelConfig;
25
+ startedAt: string;
26
+ runStatus: AgentRunResult["status"];
27
+ runError: ErrorInfo | undefined;
28
+ }): Promise<void>;