@arnilo/prism 0.0.96 → 0.1.1
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 +290 -2
- package/README.md +17 -3
- package/dist/agent-definitions.js +2 -3
- package/dist/agent-event-source.d.ts +11 -0
- package/dist/agent-event-source.js +512 -0
- package/dist/agent-loops.d.ts +5 -0
- package/dist/agent-loops.js +99 -14
- package/dist/agent-run-lifecycle.d.ts +5 -2
- package/dist/agent-run-lifecycle.js +18 -2
- package/dist/agent-run-state.d.ts +27 -1
- package/dist/agent-run-state.js +113 -7
- package/dist/agents.d.ts +3 -1
- package/dist/agents.js +1255 -129
- package/dist/artifacts.d.ts +132 -0
- package/dist/artifacts.js +44 -0
- package/dist/cache-helpers.js +18 -9
- package/dist/checkpoints.d.ts +4 -0
- package/dist/checkpoints.js +17 -9
- package/dist/cli-init.js +3 -7
- package/dist/cli-runner.d.ts +2 -6
- package/dist/cli-runner.js +71 -33
- package/dist/compaction.js +5 -4
- package/dist/config.js +7 -4
- package/dist/content.js +26 -24
- package/dist/context-budget.d.ts +67 -0
- package/dist/context-budget.js +288 -0
- package/dist/contracts.d.ts +590 -8
- package/dist/contracts.js +142 -1
- package/dist/contribution-parsing.js +6 -2
- package/dist/contributions.d.ts +2 -0
- package/dist/contributions.js +3 -0
- package/dist/conversations.d.ts +50 -0
- package/dist/conversations.js +98 -0
- package/dist/credentials.d.ts +22 -2
- package/dist/credentials.js +18 -3
- package/dist/devices.d.ts +94 -0
- package/dist/devices.js +138 -0
- package/dist/event-multiplexer.js +18 -4
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +79 -6
- package/dist/feedback.js +12 -10
- package/dist/guardrails.d.ts +1 -1
- package/dist/guardrails.js +26 -17
- package/dist/identity.d.ts +92 -0
- package/dist/identity.js +265 -0
- package/dist/index.d.ts +94 -72
- package/dist/index.js +48 -36
- package/dist/input.d.ts +10 -1
- package/dist/input.js +152 -52
- package/dist/instruction-injection.d.ts +1 -1
- package/dist/middleware.js +9 -1
- package/dist/models.d.ts +2 -0
- package/dist/models.js +3 -0
- 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 +13 -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.d.ts +103 -0
- package/dist/persistence-lifecycle.js +202 -0
- 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.d.ts +46 -1
- package/dist/providers/openai-compatible.js +123 -53
- package/dist/providers/openai-primitives.js +10 -7
- package/dist/providers/transport.d.ts +6 -0
- package/dist/providers/transport.js +21 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +3 -0
- package/dist/redaction.d.ts +1 -0
- package/dist/redaction.js +26 -9
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +2 -2
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +8 -1
- package/dist/rpc.js +55 -11
- package/dist/run-ledger.d.ts +6 -0
- package/dist/run-ledger.js +16 -13
- package/dist/run-limits.js +49 -10
- package/dist/secure-agent.js +8 -2
- package/dist/security.js +7 -2
- package/dist/session-stores.d.ts +7 -2
- package/dist/session-stores.js +195 -21
- package/dist/skill-disclosure.d.ts +35 -0
- package/dist/skill-disclosure.js +101 -0
- package/dist/skill-load.d.ts +25 -0
- package/dist/skill-load.js +112 -0
- package/dist/structured-output.d.ts +5 -1
- package/dist/structured-output.js +20 -2
- package/dist/system-prompts.js +7 -2
- package/dist/testing/agent-event-source-conformance.d.ts +4 -0
- package/dist/testing/agent-event-source-conformance.js +54 -0
- 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.d.ts +2 -2
- package/dist/testing/persistence-schema.js +280 -35
- package/dist/testing/provider-conformance.js +3 -3
- package/dist/testing/run-ledger-conformance.js +1 -1
- package/dist/testing/session-store-conformance.d.ts +6 -0
- package/dist/testing/session-store-conformance.js +37 -2
- package/dist/testing/tool-conformance.js +30 -5
- package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
- package/dist/testing/tool-effect-store-conformance.js +85 -0
- package/dist/thinking.js +4 -1
- package/dist/tool-effects.d.ts +15 -0
- package/dist/tool-effects.js +352 -0
- package/dist/tool-result-fold.d.ts +40 -0
- package/dist/tool-result-fold.js +176 -0
- package/dist/tools.d.ts +8 -3
- package/dist/tools.js +248 -13
- package/docs/0.1.0-readiness.md +215 -0
- package/docs/a2a.md +33 -2
- package/docs/acp.md +152 -0
- package/docs/ag-ui-adoption.md +77 -0
- package/docs/ag-ui.md +225 -0
- package/docs/agent-events.md +34 -3
- package/docs/agent-identity.md +144 -0
- package/docs/agent-loops.md +17 -2
- package/docs/agent-session-runtime.md +21 -4
- package/docs/browser-automation.md +5 -0
- package/docs/caveman.md +129 -0
- package/docs/cli-rpc.md +3 -6
- package/docs/coding-agent-tools.md +229 -25
- package/docs/coding-security.md +77 -11
- package/docs/compaction-and-retry.md +5 -2
- package/docs/compaction-llm.md +20 -1
- package/docs/compaction-observational-memory.md +52 -8
- package/docs/context-and-skills.md +94 -7
- package/docs/contribution-registries.md +1 -0
- package/docs/conversations.md +135 -0
- package/docs/credential-storage.md +34 -1
- package/docs/credentials-and-redaction.md +11 -1
- package/docs/database-persistence.md +27 -7
- package/docs/device-adapters.md +97 -0
- package/docs/enterprise-postgres-state.md +178 -0
- package/docs/evaluations.md +14 -1
- package/docs/extensions.md +4 -1
- package/docs/forge-integration.md +113 -0
- package/docs/guardrails.md +16 -2
- package/docs/host-security.md +35 -4
- package/docs/index.md +69 -37
- package/docs/input-and-prompt-assembly.md +8 -7
- package/docs/language-intelligence.md +162 -0
- package/docs/mcp-tools.md +62 -5
- package/docs/middleware-hooks.md +2 -2
- package/docs/migration.md +427 -2
- package/docs/model-routing.md +111 -0
- package/docs/multimodal-content.md +8 -5
- package/docs/node-jsonl-session-store.md +1 -1
- package/docs/observability.md +2 -0
- package/docs/openapi-tools.md +56 -0
- package/docs/performance.md +282 -0
- package/docs/policy-and-audit.md +171 -0
- package/docs/ponytail.md +127 -0
- package/docs/postgres-persistence.md +8 -4
- package/docs/process-sessions.md +147 -0
- package/docs/provider-caching.md +13 -1
- package/docs/provider-conformance.md +29 -5
- package/docs/provider-packages.md +43 -2
- package/docs/provider-request-policies.md +2 -0
- package/docs/providers/ai-sdk.md +24 -7
- package/docs/providers/alibaba.md +179 -0
- package/docs/providers/anthropic.md +93 -0
- package/docs/providers/azure.md +74 -0
- package/docs/providers/bedrock.md +72 -0
- package/docs/providers/google.md +89 -0
- package/docs/providers/ollama.md +166 -0
- package/docs/providers/openai-compatible.md +31 -2
- package/docs/providers/openai.md +24 -5
- package/docs/providers/openrouter.md +2 -0
- package/docs/providers/vertex.md +71 -0
- package/docs/public-contracts.md +68 -4
- package/docs/rag.md +41 -12
- package/docs/release-and-install.md +362 -208
- package/docs/resource-loading.md +3 -0
- package/docs/runs-and-usage.md +3 -0
- package/docs/server.md +44 -6
- package/docs/session-store-conformance.md +2 -0
- package/docs/session-stores.md +41 -2
- package/docs/sqlite-persistence.md +11 -3
- package/docs/structured-output.md +7 -1
- package/docs/supervisors.md +8 -0
- package/docs/tool-effects.md +95 -0
- package/docs/tools.md +5 -0
- package/docs/work-artifacts-and-review.md +102 -0
- package/docs/work-connectors.md +32 -0
- package/docs/work-tools.md +137 -0
- package/docs/workflows.md +6 -0
- package/docs/working-and-semantic-memory.md +40 -7
- package/package.json +30 -7
- package/templates/init/providers.json +22 -0
- 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
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# 0.1.0 / 1.0 Readiness Gates
|
|
2
|
+
|
|
3
|
+
Status: **0.1.1** is the current release line (plan 013 hardening patch on the frozen 0.1.x line; plan 012 release-candidate hardening); **1.0** readiness remains operator-gated, not automatic.
|
|
4
|
+
|
|
5
|
+
This page distills runnable readiness gates into one command-per-gate table.
|
|
6
|
+
The **Last evidence** column records the 0.1.0-tree snapshot (plan 012 Tasks
|
|
7
|
+
0–7, Node v24.18.0, Linux x86_64) with the 2026-07-26 **0.0.16** values kept
|
|
8
|
+
as the historical floor where the baseline predates it. Re-run each gate on
|
|
9
|
+
the release tree before cutting 1.0. The decision to cut 1.0 stays with the
|
|
10
|
+
operator after operator-gated legs run in a protected environment and Phase
|
|
11
|
+
12 demand evidence exists.
|
|
12
|
+
|
|
13
|
+
Evidence trail: [`docs/review-coverage-2026-07-26-phase-11.md`](./review-coverage-2026-07-26-phase-11.md)
|
|
14
|
+
(addenda 0–9), [`docs/release-and-install.md`](./release-and-install.md),
|
|
15
|
+
[`docs/migration.md`](./migration.md), [`docs/performance.md`](./performance.md),
|
|
16
|
+
[`docs/public-contracts.md`](./public-contracts.md) (frozen 0.1.x contract).
|
|
17
|
+
Historical release lines (0.0.16 floor → 0.0.27 Phase 10 ACP interop → 0.1.0)
|
|
18
|
+
keep their per-phase evidence in the pages above; this page records the 0.1.1
|
|
19
|
+
snapshot (plan 013) with the 0.1.0 table below as the previous line.
|
|
20
|
+
|
|
21
|
+
## Current line (0.1.1)
|
|
22
|
+
|
|
23
|
+
| Item | Status |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Published graph | **49** publishable manifests at exact **0.1.1** (root + 48 workspace packages; `docs/release-and-install.md`) |
|
|
26
|
+
| Plan 013 hardening patch | Five scoped fixes (plan 013 freeze `scripts/phase13-freeze-manifest.json`): build single-flight (`npm run clean` standalone), deterministic MCP SSE relay test (`relayStatelessBody` internal export, `sse-relay.test.ts`), combined coverage summary (`scripts/coverage-summary.mjs`, core gate the only hard threshold), canonical manifest-count narrative (49 = root + 48: 14 provider + 9 prism-* + 25 capability), ACP modes/config ownership-scoped persistence guidance (agent never persists them; host stores key by `sessions.ownership`, cross-tenant restore rejects `ERR_PRISM_ACP_INPUT`) |
|
|
27
|
+
| Upgrade path | `docs/migration.md` `0.1.0 → 0.1.1` (additive, no migration); store-compatible both directions |
|
|
28
|
+
| Compat promise | Additive-only vs the frozen 0.1.x contract; `scripts/compat-baseline` regenerated at 0.1.1 (single version-literal delta + one additive internal relay export), zero breaking deltas |
|
|
29
|
+
| Security policy | `npm audit --audit-level=moderate` 0 at 0.1.1; threat-suites leg unchanged (plan 012) |
|
|
30
|
+
| Docs freeze | tripwires green including the manifest-count tripwire and the plan 013 Task 6 handoff/migration tripwire |
|
|
31
|
+
| Previous line | the **0.1.0** table below keeps the plan 012 snapshot; the **0.0.16** values remain the historical network-free floor |
|
|
32
|
+
|
|
33
|
+
## Current line (0.1.0)
|
|
34
|
+
|
|
35
|
+
| Item | Status |
|
|
36
|
+
|---|---|
|
|
37
|
+
| Published graph | **49** publishable manifests at exact **0.1.0** (root + 48 workspace packages; `docs/release-and-install.md`) |
|
|
38
|
+
| Phase 12 RC hardening | Freeze manifest (`scripts/phase12-freeze-manifest.json`): no new packages/exports/migrations/dependencies, additive-only compat promise vs `scripts/compat-baseline`; compatibility/support matrix machine-checked (Node 20+24 measured, PostgreSQL 16, linux-x64, protocol SDK pins) |
|
|
39
|
+
| Upgrade path | `docs/migration.md` `0.0.28 → 0.1.0` (no migration) + full `0.0.17 → 0.1.0` upgrade matrix (compatible / tested migration / tested refusal per release line) |
|
|
40
|
+
| Packed-install e2e journeys | enterprise + coding journeys install the exact packed 0.1.0 manifest graph into fresh consumers (`scripts/e2e-*-journey.test.mjs`, in `npm test`) |
|
|
41
|
+
| Protected restart-recovery evidence | `npm run test:postgres` includes `scripts/phase12-restart-recovery.test.mjs` (multi-replica kill/resume, unknown-outcome window, DB restart during streaming, reconnect/contention p95 vs frozen ceilings) |
|
|
42
|
+
| Capacity envelopes | `scripts/benchmark-0.1.0.json` — 24 network-free + 16 protected p95 rows under the frozen 0.1.0 contract, re-gated on every `npm test` |
|
|
43
|
+
| Security policy | `npm audit --audit-level=moderate` enforced in workflows; named threat-suites leg (`npm run security:threat-suites`); supply-chain negative fixtures in `scripts/release-gate.test.mjs` |
|
|
44
|
+
| Docs freeze | every public page, package README, and changelog consistent with 0.1.0 behavior; tripwires green (121/121) |
|
|
45
|
+
| Readiness table below | **0.0.16 measured values** remain the historical network-free floor; 0.1.0 evidence is recorded per row |
|
|
46
|
+
|
|
47
|
+
## Gate table
|
|
48
|
+
|
|
49
|
+
| Gate | Command | Last evidence (0.1.0 tree; 0.0.16 floor where noted) | Owner |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| Full quality gate | `npm run sdk:ready` | RC=0 at 0.1.0: typecheck (+examples), lint 0, format clean, full test, coverage, pack, release:gate (clean-checkout run is part of the operator release checklist). `npm run test:coverage` also prints the combined coverage summary (core + 41 workspace suites, additive reporting; core gate lines≥60 / functions≥70 / branches≥75 is the only hard threshold — plan 013 Task 3) | CI |
|
|
52
|
+
| Exact version graph | `node scripts/release.mjs check --version 0.1.0` | pass at 0.1.0: exact versions/ranges/lockfile/access + registry-collision check, **49** manifests | CI + operator |
|
|
53
|
+
| Frozen public API surface + compat gate | `node scripts/release.mjs gate` | 0 breaks / 0 errors vs checked-in baselines (`scripts/compat-baseline/`); additive-only delta (empty at the 0.0.28 → 0.1.0 bump) | CI |
|
|
54
|
+
| Migration coverage + docs tripwires | `node --test dist/__tests__/docs.test.js` | 121/121 at 0.1.0; `docs/migration.md` sections tripwired per release line | Maintainer |
|
|
55
|
+
| Deterministic artifact budget | `node --test dist/__tests__/budget-gate.test.mjs` | root 713.5 kB packed / 2.1 MB unpacked / 295 files within +5% of the regenerated 0.1.0 baseline; startup < 250 ms | CI (in `npm test`) |
|
|
56
|
+
| **0.1.0 capacity envelope (frozen performance contract)** | `node --test scripts/benchmark-0.1.0.test.mjs` | **`scripts/benchmark-0.1.0.json`** re-gated on every `npm test`: 24 network-free p95 rows ≤ frozen ceilings, 16 protected PostgreSQL rows ≤ budgets.json ceilings (50/100 ms), startup 41.7 ms < 250 ms, root pack within ±5%; regenerate with `node scripts/benchmark-0.1.0.mjs --out scripts/benchmark-0.1.0.json` (+ `PRISM_TEST_POSTGRES_URL` for protected legs) | CI (in `npm test`) |
|
|
57
|
+
| Performance benchmark medians | `node scripts/benchmark-0.1.0.mjs --out scripts/benchmark-0.1.0.json` (+ `PRISM_TEST_POSTGRES_URL` for the 16 protected rows) | envelope re-recorded at 0.1.0 (see capacity-envelope row above); historical phase medians remain in `docs/performance.md` | On-demand release evidence |
|
|
58
|
+
| Secret scan | `node scripts/scan-secrets.mjs` | 0.1.0 tree: 0 findings (3,095-file 0.0.16 floor) | CI |
|
|
59
|
+
| License / SBOM | `node scripts/verify-sbom.mjs` | 0.1.0 tree: 317 locked packages, allow-listed licenses (227/12 at 0.0.16 floor) | CI |
|
|
60
|
+
| Dependency audit | `npm audit --audit-level=moderate` | rc=0 (0 moderate+, 2 moderate at 0.0.16 baseline); 0 vulns at every severity for the 0.1.0 tree | CI |
|
|
61
|
+
| Whitespace hygiene | `git diff --check` | clean | CI |
|
|
62
|
+
| Publish order + tarball validation | `node scripts/release.mjs publish --version 0.1.0 --dry-run --allow-dirty --allow-untagged` | 49/49 packages `dry-run` twice with byte-identical reports, deterministic dependency order, no failures (Task 7) | Operator (dry-run), CI |
|
|
63
|
+
| Node 20 compatibility | CI `node20-compat` (build + public-import smoke) | all 21 root exports import cleanly on Node 20.20.2 | CI |
|
|
64
|
+
| PostgreSQL suite | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres` | 0.1.0: Phase 7 conformance + Phase 12 restart-recovery + 74 workspace checks green against PostgreSQL 16 (Task 4 recording, re-run green at 0.1.0 on 2026-08-09); operator-gated | Operator |
|
|
65
|
+
| Keychain suite | `PRISM_TEST_KEYCHAIN=1 npm test --workspace @arnilo/prism-credentials-node` (protected) | 28/28 green incl. native keychain round-trip against the OS secret-service backend (gnome-keyring, 2026-08-09) | Operator host |
|
|
66
|
+
| Live-provider suites | `npm run test:live` (protected) | **operator-gated** (requires credentials; `live-canaries.yml` blocked gate, `canary-report.json` retained) | Operator |
|
|
67
|
+
| SAST | GitHub CodeQL | **operator-gated** (runs in CI workflow) | CI |
|
|
68
|
+
| Signed, provenance publication | `npm run release:publish` (clean tagged tree, OIDC) | **operator-gated** (see "Remaining for 1.0") | Operator |
|
|
69
|
+
|
|
70
|
+
## Frozen public API surface
|
|
71
|
+
|
|
72
|
+
The compat gate diffs every package's generated `.d.ts` export surface against
|
|
73
|
+
checked-in baselines in `scripts/compat-baseline/` (one file per package,
|
|
74
|
+
regenerated at 0.1.0). It fails on any **removed** export or changed
|
|
75
|
+
declaration; additive exports are allowed. `scripts/release-gates.mjs` also
|
|
76
|
+
enforces a tarball deny list (no reviews/plans/maps/tests/binaries/credential
|
|
77
|
+
material in published artifacts) and exact version-range drift. A genuine break
|
|
78
|
+
requires `--allow-break` **and** a `docs/migration.md` entry mentioning the
|
|
79
|
+
version. The frozen 0.1.x contract (declaration/exports, events, protocol
|
|
80
|
+
payloads, migration checksums, patch-release compatibility promise) is
|
|
81
|
+
published in [docs/public-contracts.md](public-contracts.md).
|
|
82
|
+
|
|
83
|
+
**Baseline maintenance:** `scripts/compat-baseline/` must stay committed.
|
|
84
|
+
Regenerate only after review with `node scripts/release.mjs gate --update-baseline`,
|
|
85
|
+
having first confirmed zero removed exports (the gate's order-sensitive
|
|
86
|
+
signatures can drift on a TypeScript bump without any real API change).
|
|
87
|
+
|
|
88
|
+
## Migration coverage
|
|
89
|
+
|
|
90
|
+
`docs/migration.md` carries release migration sections tripwired by
|
|
91
|
+
`docs.test.ts` (headings and key phrases). A missing or gutted section fails
|
|
92
|
+
the suite. **0.0.19** adds observational-memory lifecycle and nested settings in
|
|
93
|
+
`@arnilo/prism-compaction-observational-memory` — see `0.0.18 → 0.0.19 observational memory lifecycle`.
|
|
94
|
+
**0.0.18** adds intentional pre-1.0 breaks (`repo_search` literal-only,
|
|
95
|
+
`cache_aware` default layout, oldest-first history eviction, atomic write/edit
|
|
96
|
+
durability, MCP SDK bump) — see `0.0.17 → 0.0.18 restore integrity`.
|
|
97
|
+
|
|
98
|
+
## Budget table
|
|
99
|
+
|
|
100
|
+
Deterministic budgets (CI gate, `scripts/budget-gate.test.mjs`):
|
|
101
|
+
|
|
102
|
+
| Metric | Baseline | Tolerance | 0.1.0 measured |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| Root packed bytes | 713,454 (regenerated 0.1.0, dev-001) | +5% | 713.5 kB (within) |
|
|
105
|
+
| Root unpacked bytes | 2,388,118 | +5% | within |
|
|
106
|
+
| Root file count | 293 (regenerated 0.1.0, dev-001) | +5% | 295 (within) |
|
|
107
|
+
| Cold-startup import | 41.7 ms | ceiling 250 ms | within |
|
|
108
|
+
|
|
109
|
+
Baselines are the 0.1.0 snapshot (`scripts/budgets.json`, amended once by
|
|
110
|
+
freeze deviation dev-001 for the Task 1–5 evidence scripts); raise them only
|
|
111
|
+
after a deliberate reviewed performance change.
|
|
112
|
+
|
|
113
|
+
## Live-suite matrix (operator-gated)
|
|
114
|
+
|
|
115
|
+
| Suite | Command | Environment |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| PostgreSQL persistence | `npm run test:postgres` | live PostgreSQL |
|
|
118
|
+
| Keychain credentials | protected live suite | OS keychain |
|
|
119
|
+
| Provider live-canary (OpenAI/Anthropic/Google/Kimi/Ollama/…) | protected live-canary matrix | vendor credentials |
|
|
120
|
+
| RAG / memory / workflows live journeys | protected live-canary matrix | vendor + DB credentials |
|
|
121
|
+
| Enterprise adapters live canary (Phase 11: real OIDC IdP/JWKS, real OPA endpoint, real MCP OAuth authorization server, S3-compatible object store) | protected live-canary matrix | IdP / policy / object-store credentials |
|
|
122
|
+
|
|
123
|
+
These do not run on a contributor machine; their evidence is recorded in the
|
|
124
|
+
protected environment, never faked. Phase 11 live-endpoint evidence is a
|
|
125
|
+
**blocked release gate, not a passing skip**: `npm test` proves the seams
|
|
126
|
+
against network-free fake servers (`scripts/phase11-conformance.test.mjs`),
|
|
127
|
+
and the live matrix above stays blocked until the protected environment
|
|
128
|
+
records it.
|
|
129
|
+
|
|
130
|
+
## Packed-install e2e journeys (plan 012 Task 3)
|
|
131
|
+
|
|
132
|
+
| Journey | Command | Environment |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| Enterprise: OIDC identity → OPA policy decision → agent run with durable events → batched approval → OpenAPI side effect with idempotency → artifact upload + signed delivery | `npm test` (scripts/e2e-enterprise-journey.test.mjs) | network-free (memory stores, loopback fake servers); PostgreSQL leg when `PRISM_TEST_POSTGRES_URL` is set |
|
|
135
|
+
| Coding: ACP editor session (init capability negotiation, session new + load/resume) → bounded coding tools (git-aware list/search, glob, read-before-write write, delete, move) → sandboxed process session → forge handoff | `npm test` (scripts/e2e-coding-journey.test.mjs) | network-free (fake GitHub + git runner) |
|
|
136
|
+
|
|
137
|
+
Both install the exact packed manifest graph into a fresh consumer (`npm pack`
|
|
138
|
+
tarballs, never workspace paths) and run the journey against only public
|
|
139
|
+
exports. Each fixture ends with a success marker (`ENTERPRISE JOURNEY OK` /
|
|
140
|
+
`CODING JOURNEY OK`) that the test asserts. Runtime is bounded by
|
|
141
|
+
`e2eJourneyFixtureMsCeiling` in `scripts/phase12-freeze-manifest.json` (120 s
|
|
142
|
+
per fixture).
|
|
143
|
+
|
|
144
|
+
## Protected restart-recovery evidence (plan 012 Task 4)
|
|
145
|
+
|
|
146
|
+
| Leg | Command | Environment |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| Multi-replica kill/resume: replica A runs a durable agent (PostgreSQL checkpoints + durable events), suspends on a batched tool approval, and is SIGKILLed; replica B resumes with no event gap/duplicate, exactly-once durable tool effects, partial-approval re-suspend with CAS, and ownership rechecks | `npm run test:postgres` (scripts/phase12-restart-recovery.test.mjs) | live PostgreSQL 16 |
|
|
149
|
+
| Tool-effect unknown-outcome window: a dispatched-and-expired claim replays as `ERR_PRISM_TOOL_EFFECT_UNKNOWN` (reconciliation demanded), never a silent double-apply | `npm run test:postgres` (same suite) | live PostgreSQL 16 |
|
|
150
|
+
| Database restart during streaming: terminated LISTEN backend recovers by polling (missed event delivered, no gap) | `npm run test:postgres` (Phase 7 + Phase 12 legs) | live PostgreSQL 16 |
|
|
151
|
+
| Reconnect p95 + append contention p95 recorded against frozen ceilings (`reconnectP95Ms`, `pointOpP95Ms` in `scripts/phase12-freeze-manifest.json`) | `npm run test:postgres`; evidence in `scripts/phase12-restart-recovery.json` | live PostgreSQL 16 |
|
|
152
|
+
|
|
153
|
+
Missing `PRISM_TEST_POSTGRES_URL` is a **named, visible blocked gate**: `npm run
|
|
154
|
+
test:postgres` fails at `scripts/require-postgres-url.mjs`, and running the
|
|
155
|
+
suite directly records a `BLOCKED GATE` failure instead of a passing skip.
|
|
156
|
+
Durable ACP session registries and sandbox process sessions stay host-owned
|
|
157
|
+
by design (Prism provides the durable agent-run lifecycle underneath); the
|
|
158
|
+
coding journey and Phase 9/10 conformance suites cover their in-process
|
|
159
|
+
semantics, and the legs above prove the durable restart classes.
|
|
160
|
+
|
|
161
|
+
## Security matrix
|
|
162
|
+
|
|
163
|
+
| Control | Command / source | 0.0.16 baseline |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| Secret scan | `node scripts/scan-secrets.mjs` | 3095 files / 0 findings |
|
|
166
|
+
| License / SBOM | `node scripts/verify-sbom.mjs` | 227 packages / 12 licenses, allow-listed |
|
|
167
|
+
| Dependency audit | `npm audit --audit-level=moderate` | 0 high (2 moderate) at 0.0.16; **0 vulnerabilities at every severity for the 0.1.0 tree** (317 locked deps; MCP SDK 1.30.0 fix baseline) |
|
|
168
|
+
| SAST | GitHub CodeQL workflow | CI-gated |
|
|
169
|
+
| Sandbox / protocol / tenant threat suites | `npm test` (coding-security, MCP, policy, guardrail suites) | green at 0.0.16 |
|
|
170
|
+
| **0.1.0 threat-suites leg** | `npm run security:threat-suites` (Phase 8–11 conformance) + `npm run test:postgres` (Phase 7 tenant leg, blocked gate without URL) | 28/28 network-free at 0.1.0 recording |
|
|
171
|
+
| **Supply-chain negative fixtures** | `scripts/release-gate.test.mjs` | tampered tarball content, unexpected file types/credential material, and suppressed-provenance-in-CI all detected |
|
|
172
|
+
| Signed deterministic publication | `release.mjs publish` on clean tagged tree | operator-gated |
|
|
173
|
+
|
|
174
|
+
## Remaining for 1.0 (operator / protected environment)
|
|
175
|
+
|
|
176
|
+
Exact prerequisites that must be satisfied before cutting 1.0 (the 0.1.0
|
|
177
|
+
publication itself is the same list minus the 1.0-specific items):
|
|
178
|
+
|
|
179
|
+
1. **Signed tag + commits:** create and sign `v1.0.0` on a clean tree;
|
|
180
|
+
`release.mjs publish` refuses real publication with `--allow-dirty`
|
|
181
|
+
or `--allow-untagged` (verified: the 0.1.0 dry-run proceeds only with
|
|
182
|
+
both flags, the real run always refuses).
|
|
183
|
+
2. **npm authentication + OIDC provenance/attestation:** publish with
|
|
184
|
+
`--provenance` and `--access public` from the protected registry identity
|
|
185
|
+
(the 0.1.0 publication is the first exercise of this operator step;
|
|
186
|
+
`publishArgs` derives `--provenance` from `GITHUB_ACTIONS` and the
|
|
187
|
+
release workflow holds `id-token: write` + `attestations: write`).
|
|
188
|
+
3. **Protected live-canary matrix green:** provider/RAG/memory/workflows live
|
|
189
|
+
journeys pass with real credentials (`live-canaries.yml` — blocked gate,
|
|
190
|
+
never a silent skip).
|
|
191
|
+
4. **PostgreSQL + keychain protected suites green.**
|
|
192
|
+
5. **CodeQL SAST green** on the release commit.
|
|
193
|
+
6. **`scripts/compat-baseline/` committed** (frozen 0.1.x baselines are
|
|
194
|
+
already checked in).
|
|
195
|
+
7. **Phase 12 demand evidence** (below) recorded for any capability that 1.0
|
|
196
|
+
is expected to anchor.
|
|
197
|
+
8. **0.1.0 lifecycle gates green** on the release candidate (docs suite,
|
|
198
|
+
audit at moderate, coding-tool durability, layout/eviction defaults) —
|
|
199
|
+
recorded in this page at 0.1.0.
|
|
200
|
+
|
|
201
|
+
## Phase 12 demand-evidence entry criteria
|
|
202
|
+
|
|
203
|
+
Phase 12 (demand-gated 0.1.x) promotes no capability on comparison-table parity
|
|
204
|
+
alone. Each candidate must present, before it becomes a numbered plan:
|
|
205
|
+
|
|
206
|
+
- **Named user** (a concrete person/team who will use it),
|
|
207
|
+
- **Concrete integration** (the real system it connects to),
|
|
208
|
+
- **Operational owner** (who runs and pages for it),
|
|
209
|
+
- **Measurable acceptance criteria** (scale/cost/latency/storage budgets that
|
|
210
|
+
do not expand default core/install/runtime cost),
|
|
211
|
+
- then the pipeline: demand evidence → primitive review → threat model →
|
|
212
|
+
optional package/service → conformance → release gate.
|
|
213
|
+
|
|
214
|
+
The readiness gates above are the stable API/compat/budget/security floor that
|
|
215
|
+
Phase 12 capabilities must consume and must not regress.
|
package/docs/a2a.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-supervisor` implements bounded A2A 1.0 over the JSON-RPC/HTTPS binding. Supported operations: `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask`, push-notification-config create/get/list/delete, and `GetExtendedAgentCard`. Agent Cards retain explicit ES256 verification. gRPC, HTTP+JSON, discovery registries, automatic JWK/OAuth fetching, and an internal task worker/store are absent.
|
|
5
|
+
`@arnilo/prism-supervisor` implements bounded A2A 1.0 over the JSON-RPC/HTTPS binding. Supported operations: `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask`, push-notification-config create/get/list/delete, and `GetExtendedAgentCard`. `client.streamMessage()` additionally exposes verified rich task/message events for frontend adapters while legacy `stream()` remains text-compatible. Agent Cards retain explicit ES256 verification. gRPC, HTTP+JSON, discovery registries, automatic JWK/OAuth fetching, and an internal task worker/store are absent.
|
|
6
6
|
|
|
7
7
|
## When to use it
|
|
8
8
|
|
|
@@ -39,6 +39,30 @@ const handler = createA2AHandler({
|
|
|
39
39
|
|
|
40
40
|
Parts, messages, artifacts, histories, metadata, and aggregate responses are untrusted. Rich content remains in A2A task/message/artifact contracts for host mapping; it is never promoted to system instructions or automatically loaded as a Prism resource.
|
|
41
41
|
|
|
42
|
+
## AG-UI server-side exposure (Task 13, 0.0.26)
|
|
43
|
+
|
|
44
|
+
`createAgUiA2AServer()` in `@arnilo/prism-ag-ui` fronts one host-selected **local AG-UI agent** as an A2A 1.0 server, the reverse direction of `createAgUiA2AAdapter()`: remote A2A clients start and stream local runs through the same AG-UI input allow-list and event mapper as the AG-UI SSE path (same projection, redaction, and byte caps). It reuses this package's `createA2AHandler` transport/lifecycle; it creates no second runtime, task store, or worker. Requires the optional `@arnilo/prism-supervisor` peer (imported lazily; plain `@arnilo/prism-ag-ui` imports keep working without it).
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { createAgentEventSourceAgUiReplay, createAgUiA2AServer } from "@arnilo/prism-ag-ui";
|
|
48
|
+
|
|
49
|
+
const server = await createAgUiA2AServer({
|
|
50
|
+
card: agentCard, // A2A agent card (streaming: true)
|
|
51
|
+
authorize: (input) => authorizeA2A(input), // A2A auth → { ownership } (also the AG-UI authorization)
|
|
52
|
+
sessionFactory: ({ threadId, authorization, signal, input }) =>
|
|
53
|
+
createAgUiSession(authorization, input), // same shape as createAgUiHandler
|
|
54
|
+
input: { project: projectAgUiInput }, // AG-UI full-input allow-list
|
|
55
|
+
projection, redactor, a2ui, limits, // AG-UI mapper options
|
|
56
|
+
durable: { // optional: GetTask/SubscribeToTask after a run finishes
|
|
57
|
+
source: persistence.events, // durable AgentEventSource
|
|
58
|
+
resolveTask: async ({ id, authorization }) => ({ task, run }), // host-owned task→run correlation
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
// host mounts: new Request(url, init) → server(request)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Semantics: `SendMessage` runs the local agent to completion and returns a terminal task with collected text artifacts; `SendStreamingMessage` (client `returnImmediately: true`) streams text/activity/state as bounded A2A artifact updates, then a terminal task. `agent_suspended` closes the stream with `TASK_STATE_INPUT_REQUIRED`; continuation stays host-owned (AG-UI resume). `GetTask`/`ListTasks`/`CancelTask` cover a bounded in-memory registry of tasks started on this instance; with `durable`, `SubscribeToTask`/`GetTask` also resolve host-correlated runs and replay the durable source with cursor event ids (at-least-once; clients dedupe by `eventId`). Text parts become the AG-UI user message; raw/data/url parts stay disabled unless `parts` selects them, and then arrive only in `forwardedProps.a2a` for `input.project`. Task ids default to `task-<uuid>`; hosts may own them via `selectTaskId`. `tasks` may be supplied to replace the built-in lifecycle entirely. A2A remains separately mounted — no route is added to `createPrismHandler()`.
|
|
65
|
+
|
|
42
66
|
## Implementation example
|
|
43
67
|
|
|
44
68
|
```ts
|
|
@@ -59,12 +83,15 @@ Streams use ordered SSE frames with `id:` and JSON-RPC `result` containing one `
|
|
|
59
83
|
Client APIs:
|
|
60
84
|
|
|
61
85
|
- `send()` / `stream()` preserve text-to-`AgentRunResult` compatibility.
|
|
86
|
+
- `streamMessage(message)` exposes bounded verified `A2AStreamEvent` task/message records without discarding artifact/data parts.
|
|
62
87
|
- `sendMessage()` returns rich/durable `A2ATask`.
|
|
63
88
|
- `getTask()`, `listTasks()`, `cancelTask()`, `subscribeToTask()` operate on durable tasks.
|
|
64
89
|
- `createPushConfig()`, `getPushConfig()`, `listPushConfigs()`, `deletePushConfig()` expose declared push config operations.
|
|
65
90
|
|
|
66
91
|
Every protocol request sends/negotiates `A2A-Version: 1.0`. Client endpoint/card URLs require exact allow-listed HTTPS and `redirect: "error"`. Cards are parsed then optionally verified against host-pinned keys; no key URL is fetched.
|
|
67
92
|
|
|
93
|
+
`createA2AAgentEventSource({ source, resolveTask, map })` supplies only the durable `subscribe` seam for a host-owned `A2ATaskLifecycle`. It resolves task→exact Prism run under authorization, consumes `AgentEventSource.subscribe()`, and uses each opaque source cursor as stable A2A `eventId`. With no cursor, the first mapped update must be a full Task, matching A2A streaming rules. It creates no task store or worker. Standard `SubscribeToTask` has no `afterEventId`; Prism retains that bounded field as an explicitly documented reconnect extension.
|
|
94
|
+
|
|
68
95
|
## Request/response example
|
|
69
96
|
|
|
70
97
|
```json
|
|
@@ -73,13 +100,14 @@ Every protocol request sends/negotiates `A2A-Version: 1.0`. Client endpoint/card
|
|
|
73
100
|
|
|
74
101
|
## Extension and configuration notes
|
|
75
102
|
|
|
76
|
-
Handler requires `card.capabilities.pushNotifications` to exactly match supplied `push`; mismatch fails construction, preserving signed-card integrity and preventing false capability claims. Streaming remains available for direct text invocation. Push adapter owns exact-owner persistence, signing/auth credentials, and network transport. Host explicitly calls `deliverA2APushEvent()` from its durable update path; helper bounds event, timeout (10s default/60s hard), attempts (1 default/3 hard), and passes stable event ID as idempotency key to host `A2APushDelivery`. It starts no hidden sender and performs no network itself. Config handling validates IDs/count/bytes and requires same explicit URL policy used for URL parts. Returned push configs omit token and authentication credentials.
|
|
103
|
+
Handler requires `card.capabilities.pushNotifications` to exactly match supplied `push`; mismatch fails construction, preserving signed-card integrity and preventing false capability claims. `createAgUiA2AAdapter({ client, select, correlate, projectPart })` in `@arnilo/prism-ag-ui` fronts one host-selected verified client: host selects new/follow task mode, persists exact run/thread/task correlation before output, and may project non-text/tool/A2UI parts. It never discovers agents, opens a local session, or replaces this direct A2A API. Streaming remains available for direct text invocation. Push adapter owns exact-owner persistence, signing/auth credentials, and network transport. Host explicitly calls `deliverA2APushEvent()` from its durable update path; helper bounds event, timeout (10s default/60s hard), attempts (1 default/3 hard), and passes stable event ID as idempotency key to host `A2APushDelivery`. It starts no hidden sender and performs no network itself. Config handling validates IDs/count/bytes and requires same explicit URL policy used for URL parts. Returned push configs omit token and authentication credentials.
|
|
77
104
|
|
|
78
105
|
Defaults/hard caps include: request 64 KiB/1 MiB; response 1/8 MiB; event 64 KiB/1 MiB; stream 10/64 MiB and 10k/100k events; replay 1k/10k events; concurrency 16/256; timeout 120s/30m; IDs 256/4096 B; parts 32/256; part/raw 1/8 MiB; data 256 KiB/4 MiB; artifacts 32/256; history/page 100/1000; cursor 4/16 KiB; push configs 10/100. Hosts may narrow limits.
|
|
79
106
|
|
|
80
107
|
## Security and performance notes
|
|
81
108
|
|
|
82
109
|
- Authorize every operation; lifecycle/push adapters enforce exact owner again at durable storage boundary. Missing and foreign tasks/configs share `-32001`.
|
|
110
|
+
- Optional `A2AAuthorization.identity` is host-verified; the handler asserts activity/ownership match and forwards identity into `session.run`. Cross-tenant or widened scopes fail closed.
|
|
83
111
|
- URL policy must reject private, loopback, link-local, rebound, redirected, or otherwise disallowed destinations. Package never fetches file URLs. Host push delivery must repeat equivalent checks for every attempt/redirect and process event IDs idempotently.
|
|
84
112
|
- Push token/auth credentials are accepted only into host adapter input and removed from protocol reads/responses. Keep them out of task parts, events, telemetry, ledgers, and errors.
|
|
85
113
|
- Known-secret redaction applies before handler JSON/SSE output. Client redacts mapped text/errors. Raw/data/url content remains explicitly untrusted.
|
|
@@ -88,7 +116,10 @@ Defaults/hard caps include: request 64 KiB/1 MiB; response 1/8 MiB; event 64 KiB
|
|
|
88
116
|
|
|
89
117
|
## Related APIs
|
|
90
118
|
|
|
119
|
+
- [Agent identity](agent-identity.md)
|
|
91
120
|
- [Supervisor delegation](supervisors.md)
|
|
92
121
|
- [Agent/session runtime](agent-session-runtime.md)
|
|
93
122
|
- [Workflows](workflows.md)
|
|
94
123
|
- [Host security](host-security.md)
|
|
124
|
+
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): browser/editor protocol adapters over a Prism session; not an A2A card, task lifecycle, or remote-agent transport.
|
|
125
|
+
- [AG-UI adoption evaluation](ag-ui-adoption.md): official AG-UI A2A fronting assessment and shipped explicit adapter.
|
package/docs/acp.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Agent Client Protocol (ACP) coding-host interop
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`@arnilo/prism-ag-ui/acp` (stable ACP **v1**, `@agentclientprotocol/sdk@1.3.0` root exports only) exposes two adapters:
|
|
6
|
+
|
|
7
|
+
- `createPrismAcpAgent(options)` — serves ACP as an **agent**: an editor/AI client connects through the SDK transport and drives host-owned Prism sessions with `session/new`, `session/load`, `session/resume`, `session/prompt`, `session/set_mode`, `session/set_config_option`, `session/list`, `session/delete`, `session/close`, and `session/cancel`. The agent is a thin protocol adapter: every capability, decision, and byte cap is wired from host seams, and there is **no second policy engine** on the agent side.
|
|
8
|
+
- `createAcpEventMapper(options)` — maps a Prism `AgentEvent` stream (or `CoWorkEvent`) to ACP `SessionUpdate`s for hosts that stream through their own transport.
|
|
9
|
+
|
|
10
|
+
The adapter builds on the Phase 8/9 shared machinery: redacted event projection (`AgUiProjection`), the durable pending-decision batch model (`allow_once` / `allow_for_run` / `reject_once` / `reject_for_run`), `AgentRunLifecycle` resume, `CodingLifecycleEvent` emission, and the AG-UI/ACP package caps. It never ships experimental ACP v2 or UNSTABLE fields (`providers`, `nes`, `positionEncoding`, `sessionCapabilities.fork`, `mcpCapabilities.acp/auth`, `elicitation` is consumed client-side only and never advertised).
|
|
11
|
+
|
|
12
|
+
## When to use it
|
|
13
|
+
|
|
14
|
+
Use `createPrismAcpAgent` when an editor/AI client already speaks ACP and you want it to reach host-owned Prism runs: text streaming, safe tool status, usage, four-outcome approvals, session persistence, modes, config options, and — when the client advertises them — editor-buffer filesystem (`fs/read_text_file`, `fs/write_text_file`) and terminal (`terminal/create` … `terminal/kill`) client methods, plus prompt media (`image`, `audio`, `embeddedContext`) and `elicitation` decisions.
|
|
15
|
+
|
|
16
|
+
Do **not** use it when the host needs a browser/TUI Web endpoint (use [AG-UI](ag-ui.md)), remote agent-to-agent tasks ([A2A](a2a.md)), or a full editor integration — ACP is a protocol adapter, not an editor, a TUI, or a credential provider.
|
|
17
|
+
|
|
18
|
+
## Inputs / request
|
|
19
|
+
|
|
20
|
+
`createPrismAcpAgent(options: CreatePrismAcpAgentOptions)` — every field is host-supplied:
|
|
21
|
+
|
|
22
|
+
| Option | Shape | Effect |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `authorize` | `(input) => AcpAuthorization \| Promise` | **Required.** Ownership/identity gate for every inbound call, scoped by `sessionId`; unknown sessions fail. |
|
|
25
|
+
| `sessionFactory` | `(input) => AcpSessionBinding \| Promise` | **Required.** Builds the Prism `AgentSession` for `session/new`. Input carries `authorization`, `cwd`, `additionalDirectories`, `mcpServers` (policy-checked), `signal`, optional pre-generated `sessionId`, and `coding` (built client fs/terminal adapters when the client advertised them). |
|
|
26
|
+
| `lifecycle` | `AgentRunLifecycle` | **Required.** `status`/`resume`/`resumeStream` for durable `session/load` and `session/resume`. |
|
|
27
|
+
| `sessions?` | `AcpSessionStoreSeams` | `load` (advertises `sessionCapabilities.loadSession`), `list` (list), `delete` (delete), `resume` (resume), `additionalDirectories` (policy narrowing of `additionalDirectories`). `close` is always advertised. |
|
|
28
|
+
| `mcp?` | `AcpMcpSeams` | `transports: ("http" \| "sse")[]` and `select({ servers, signal })` — **required** for any client-supplied MCP server; select must approve before the bridge connects. Advertises `mcpCapabilities.http`/`sse` per transport. |
|
|
29
|
+
| `modes?` | `{ modes: AcpSessionMode[], defaultModeId? }` | `AcpSessionMode { id, name, description?, apply? }`; `apply({ sessionId?, fromModeId?, modeId, signal })` is the host hook run on switch. Advertised in `SessionModeState` on new/load/resume; enables `session/set_mode`. |
|
|
30
|
+
| `configOptions?` | `{ options: AcpConfigOption[], onChange? }` | `boolean`/`select` options with `defaultValue`; enables `session/set_config_option` (requires the client to advertise `session.configOptions.boolean`). |
|
|
31
|
+
| `capabilities?` | `AcpCapabilitiesOptions` | `prompt.media`/`prompt.embedded` policy seams, re-checked **live at prompt time**; presence advertises `promptCapabilities.image`/`audio`/`embeddedContext`. |
|
|
32
|
+
| `coding?` | `AcpCodingSeams` | `filesystem(client, sessionId)` / `processes(client, sessionId)` factories building `AcpClientFilesystem` / `AcpClientTerminals` over client methods; `lifecycle?: CodingLifecycleEmitter` subscribes `CodingLifecycleEvent`s into ACP updates. |
|
|
33
|
+
| `name?` | `string` | `agentInfo.name` (default `"Prism"`). |
|
|
34
|
+
|
|
35
|
+
Client capabilities are read at `initialize` and gate **client-method use**, not advertisement: `fs.readTextFile`/`writeTextFile` gate the fs seam, `terminal` the processes seam, `session.configOptions.boolean` the config path, `elicitation` the elicitation route.
|
|
36
|
+
|
|
37
|
+
## Outputs / response / events
|
|
38
|
+
|
|
39
|
+
`initialize` returns `{ protocolVersion, agentCapabilities, agentInfo }`; `agentInfo.version` comes from the package manifest. Advertised capability = a host seam is wired (pure function, no hand-maintained matrix): `loadSession`/`sessionCapabilities.*` iff the matching `sessions` seam exists (`close` always), `promptCapabilities.*` iff the matching `capabilities.prompt` seam exists, `mcpCapabilities.*` iff `mcp.select` + the transport are wired. Unadvertised agent methods fail naturally with JSON-RPC `-32601`; unadvertised client methods are never called.
|
|
40
|
+
|
|
41
|
+
In-stream `SessionUpdate`s:
|
|
42
|
+
|
|
43
|
+
| Prism event | ACP update |
|
|
44
|
+
|---|---|
|
|
45
|
+
| Assistant text | `agent_message_chunk` |
|
|
46
|
+
| Tool lifecycle | `tool_call` / `tool_call_update` (title/status/content) |
|
|
47
|
+
| Tool result, projected | `tool_call_update` with `locations` (≤ `acpLocationsPerUpdate`) and/or a `diff` block (≤ `acpDiffBytes`) — only from `AgUiProjection.toolLocations`/`toolDiff` allow-lists, at `finish()` |
|
|
48
|
+
| Provider usage / errors | `usage_update`, `error` |
|
|
49
|
+
| Durable suspension | `session/request_permission` with the four outcomes `allow_once` / `allow_for_run` / `reject_once` / `reject_for_run`; cancel, unknown options, and request failure deny. Sticky decisions expire at run end. |
|
|
50
|
+
| Elicitation suspension (all-elicitation batch + client advertised `elicitation`) | `elicitation/create` (form mode, bounded schema, redacted reason); `accept` → `allow_once` with the typed payload as `RunDecision.elicitation`, `decline`/`cancel` → `reject_once`. Otherwise falls back to the shared four-option permission path. |
|
|
51
|
+
| `file_changed` lifecycle | `tool_call_update` with `locations: [{ path }]` (needs a `toolCallId`; diff only from `fileDiff` allow-list, capped + redacted) |
|
|
52
|
+
| `worktree_changed` / process events | Projection-gated `agent_message_chunk` (deny-by-default: no `lifecycle` projection hook = no update) |
|
|
53
|
+
| `permission_denied` lifecycle | `tool_call_update` status `failed` (never raw args; synthesized id `prism:denied:<approvalId>` when no `toolCallId`) |
|
|
54
|
+
| `configuration_changed` lifecycle | `config_option_update` with the full current set, per streaming session |
|
|
55
|
+
| Session mode/config switch | `current_mode_update` / `config_option_update` |
|
|
56
|
+
|
|
57
|
+
Frozen caps (default / hard, from the Phase 10 freeze manifest): sessions 32/128, additional directories 8/32 (path 4 KiB/16 KiB), MCP servers 8/32 (config 16 KiB/256 KiB, header values 4 KiB/64 KiB), modes 16/64, config options 16/64, list page 20/100, diff bytes 64 KiB/1 MiB, locations per update 32/128, prompt media parts 16/64 and media bytes 64 KiB/1 MiB (shared AG-UI caps), terminal output chunks 51200 B/1 MiB (Phase 9 `process.outputChunkBytes`), stream events/bytes per AG-UI budgets. `session/load`/`session/resume` of a still-registered session rejects with `ERR_PRISM_ACP_INPUT` ("ACP session already exists"); model reconnect as resume of a pre-seeded stored session.
|
|
58
|
+
|
|
59
|
+
Errors surface as `AcpError` with codes `ERR_PRISM_ACP_INPUT` (malformed), `ERR_PRISM_ACP_LIMIT` (caps), `ERR_PRISM_ACP_POLICY` (host denied), `ERR_PRISM_ACP_CAPABILITY` (not advertised), `ERR_PRISM_ACP_MCP` (MCP bridging). Over the wire they become JSON-RPC `-32603` with the message in `data.details` (SDK behavior).
|
|
60
|
+
|
|
61
|
+
## Request/response example
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
// initialize → agentCapabilities (all seams wired)
|
|
65
|
+
{
|
|
66
|
+
"protocolVersion": 1,
|
|
67
|
+
"agentCapabilities": {
|
|
68
|
+
"loadSession": {},
|
|
69
|
+
"sessionCapabilities": { "list": {}, "delete": {}, "additionalDirectories": {}, "resume": {}, "close": {} },
|
|
70
|
+
"promptCapabilities": { "image": true, "audio": true, "embeddedContext": true },
|
|
71
|
+
"mcpCapabilities": { "http": true, "sse": true }
|
|
72
|
+
},
|
|
73
|
+
"agentInfo": { "name": "Prism", "version": "0.0.27" }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// session/new response (modes + configOptions wired)
|
|
77
|
+
{ "sessionId": "acp-9f1c…", "modes": { "currentModeId": "edit", "availableModes": [{ "id": "edit", "name": "Edit" }] },
|
|
78
|
+
"configOptions": [{ "type": "boolean", "id": "verbose", "name": "Verbose", "defaultValue": false, "currentValue": false }] }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Implementation example
|
|
82
|
+
|
|
83
|
+
See [`examples/acp-coding-host.ts`](../examples/acp-coding-host.ts) (runs in the demo gate). The host owns all state and policy:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { createPrismAcpAgent } from "@arnilo/prism-ag-ui/acp";
|
|
87
|
+
|
|
88
|
+
const agent = createPrismAcpAgent({
|
|
89
|
+
authorize: ({ sessionId }) => (sessionId ? hostSessions.has(sessionId) : true)
|
|
90
|
+
? { ownership: { userId: "host-user" } }
|
|
91
|
+
: false,
|
|
92
|
+
sessionFactory: (input) => ({ session: hostSessionFor(input) }), // Prism AgentSession
|
|
93
|
+
lifecycle: { status, resume, resumeStream }, // durable
|
|
94
|
+
sessions: { load, list, delete, resume, additionalDirectories }, // capability seams
|
|
95
|
+
mcp: { transports: ["http", "sse"], select: ({ servers }) => approve(servers) },
|
|
96
|
+
modes: { modes: [{ id: "edit", name: "Edit" }, { id: "review", name: "Review", apply: narrow }], defaultModeId: "edit" },
|
|
97
|
+
configOptions: { options: [{ type: "boolean", id: "verbose", name: "Verbose", defaultValue: false }] },
|
|
98
|
+
capabilities: { prompt: { media: async () => true, embedded: async () => false } },
|
|
99
|
+
coding: { lifecycle, filesystem: clientFsAdapter, processes: clientTerminalAdapter },
|
|
100
|
+
});
|
|
101
|
+
// serve over the host's ACP transport: agent.connect(stream)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Extension and configuration notes
|
|
105
|
+
|
|
106
|
+
- **Seam = capability.** Wiring `sessions.load` advertises `loadSession`; removing it withdraws the method. There is no separate capability flag to keep in sync — the freeze manifest's advertise-when matrix is enforced by construction and asserted by `scripts/phase10-conformance.test.mjs`.
|
|
107
|
+
- **Client fs/terminal are adapters, not a second implementation.** `AcpClientFilesystem` / `AcpClientTerminals` wrap the client's `fs/*` and `terminal/*` methods behind the Phase 9 `ProcessSession`-flavored interfaces; the agent pre-generates the session id so terminal requests can carry it. Host repo operations remain default when the client fs is absent.
|
|
108
|
+
- **Modes and config options are a pure host overlay.** The agent stores only a thin per-session registry; `apply`/`onChange` hooks narrow the host's own behavior. Mode switches can narrow or host-authorized widen — never a parallel policy evaluator, never a client-enabled tool.
|
|
109
|
+
- **Lifecycle wiring.** Pass your `createCodingLifecycleEmitter()` as `coding.lifecycle`; `file_changed` etc. then flow to streaming sessions. `configuration_changed` broadcasts `config_option_update` (agent-message fallback if the SDK rejects the kind).
|
|
110
|
+
- **Stream budgets.** Every lifecycle update counts against the same per-run stream event/byte budget as prompt updates; overflowing closes the update, never the run.
|
|
111
|
+
|
|
112
|
+
### Persistence and ownership
|
|
113
|
+
|
|
114
|
+
- **The agent never persists `modeId`/`configValues`.** Defaults are recomputed per session from the `modes`/`configOptions` seams — a fresh `session/new`, `load`, or `resume` always starts from `defaultModeId` / option `defaultValue`, and the agent's per-session registry is in-memory only. Persisting mode/config across sessions is a **host** decision, and host-side persistence MUST be ownership-scoped.
|
|
115
|
+
- **Host persistence MUST key by `sessions.ownership`.** `authorize` binds transport identity to ownership; a host store that persists `modeId`/`configValues` must refuse any restore whose stored ownership differs from the current session's ownership — a `sessionId` alone is never a sufficient key (session ids may collide across tenants). A cross-tenant restore rejects with `ERR_PRISM_ACP_INPUT` and never returns the other tenant's mode/config.
|
|
116
|
+
- **Ownership-scoped restore (host-owned store).** The store is keyed by `sessionId` and records the owning `userId`; restore refuses on mismatch (this exact pattern is asserted in `packages/ag-ui/src/__tests__/acp-modes-config.test.ts`):
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
// Host-owned store; the agent is never asked to persist anything.
|
|
120
|
+
class HostModeConfigStore {
|
|
121
|
+
private readonly entries = new Map<string, { userId: string; modeId?: string; configValues: Record<string, boolean | string> }>();
|
|
122
|
+
save(userId: string, sessionId: string, state: { modeId?: string; configValues: Record<string, boolean | string> }): void {
|
|
123
|
+
this.entries.set(sessionId, { userId, ...state });
|
|
124
|
+
}
|
|
125
|
+
restore(userId: string, sessionId: string): { modeId?: string; configValues: Record<string, boolean | string> } | undefined {
|
|
126
|
+
const entry = this.entries.get(sessionId);
|
|
127
|
+
if (entry && entry.userId !== userId) {
|
|
128
|
+
throw new AcpError("ERR_PRISM_ACP_INPUT", `mode/config load rejected: ownership mismatch for session '${sessionId}'`);
|
|
129
|
+
}
|
|
130
|
+
return entry; // absent or cross-tenant -> nothing restored, fail closed
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Because the agent recomputes defaults on every `load`/`resume`, a host that restores state re-applies it after load through the same gated seams (`session/set_mode`, `session/set_config_option` — both run the `apply`/`onChange` hooks) and must refuse cross-tenant loads at the `authorize` seam first (falsy `authorize` = `Unauthorized ACP session`, before any mode/config state is reachable).
|
|
136
|
+
- **Agent-owned persistence is 0.2.0.** A durable, ownership-scoped ACP session store (agent-side persistence of mode/config and session state) is roadmap 0.2.0 Module E, demand-gated; on the 0.1.x line the agent stays a thin per-session registry. See the [Host security guide](host-security.md) fail-closed checklist for the ACP boundary rows.
|
|
137
|
+
|
|
138
|
+
## Security and performance notes
|
|
139
|
+
|
|
140
|
+
- **Untrusted client input.** Client-supplied paths, `additionalDirectories`, MCP server configs, terminal env/args, and media are validated at the boundary: count/byte caps, ownership-scoped sessions, path policy via the `sessions.additionalDirectories` seam, MCP servers only through host `select` (never auto-connected), UNSTABLE `acp` transport always rejected.
|
|
141
|
+
- **Deny-closed by default.** Unknown mode ids, unadvertised methods, unprojected lifecycle events, oversize diffs/locations/media, thrown projection hooks, and failed elicitation all fail closed. Raw tool arguments/results are never sent unless a projection allow-list says otherwise.
|
|
142
|
+
- **No secrets.** Updates carry no raw file bodies, terminal output is capped by the Phase 9 chunk budget, and the shared redactor is applied before anything leaves the host. `permission_denied` never includes raw args.
|
|
143
|
+
- **Performance.** The adapter is O(1) per update with no unbounded buffering; p95 targets (fs round trip 250 ms, mode switch 250 ms, terminal chunk ack 1000 ms, prompt first update 2000 ms, prompt end 30 s) are recorded by `scripts/benchmark-0.0.27.mjs` and gated in `scripts/budgets.json` `phase10`.
|
|
144
|
+
|
|
145
|
+
## Related APIs
|
|
146
|
+
|
|
147
|
+
- [AG-UI](ag-ui.md): sibling frontend protocol; shared projection/redaction/caps and the same pending-decision model. This page is the full ACP reference.
|
|
148
|
+
- [Coding agent tools](coding-agent-tools.md): the `CodingLifecycleEvent` source mapped here; `@arnilo/prism-coding-agent` `process.outputChunkBytes` caps terminal chunks.
|
|
149
|
+
- [Agent events](agent-events.md): the durable `AgentEventSource`/replay story behind `session/load` and `session/resume`.
|
|
150
|
+
- [Host security guide](host-security.md): fail-closed checklist rows for ACP boundaries (authorize, ownership, redaction, untrusted MCP).
|
|
151
|
+
- [Migration guide](migration.md): 0.0.26 → 0.0.27 advertise/surface changes for hosts that parsed the old `initialize`.
|
|
152
|
+
- [AG-UI adoption evaluation](ag-ui-adoption.md): the underlying input/event/capability matrix.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# AG-UI adoption evaluation
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
This page records Prism's compatibility review against official AG-UI `@ag-ui/core` **0.0.57** and the official repository at commit [`a40b5c0`](https://github.com/ag-ui-protocol/ag-ui/commit/a40b5c0824564eb2f9ab9edf2be43f355f42a3b8). It separates shipped transport/replay support from remaining work needed to claim full AG-UI support, including AG-UI fronting MCP and A2A agents.
|
|
6
|
+
|
|
7
|
+
Official material reviewed:
|
|
8
|
+
|
|
9
|
+
- [Events](https://docs.ag-ui.com/concepts/events), [messages](https://docs.ag-ui.com/concepts/messages), [tools](https://docs.ag-ui.com/concepts/tools), [state](https://docs.ag-ui.com/concepts/state), [reasoning](https://docs.ag-ui.com/concepts/reasoning), [interrupts](https://docs.ag-ui.com/concepts/interrupts), [capabilities](https://docs.ag-ui.com/concepts/capabilities), [serialization](https://docs.ag-ui.com/concepts/serialization), [server quickstart](https://docs.ag-ui.com/quickstart/server), and [protocol architecture](https://docs.ag-ui.com/concepts/architecture).
|
|
10
|
+
- Official [MCP/A2A/AG-UI relationship](https://docs.ag-ui.com/agentic-protocols), [integrations](https://docs.ag-ui.com/integrations), [`@ag-ui/mcp-middleware`](https://github.com/ag-ui-protocol/ag-ui/tree/main/middlewares/mcp-middleware), [`@ag-ui/mcp-apps-middleware`](https://github.com/ag-ui-protocol/ag-ui/tree/main/middlewares/mcp-apps-middleware), [`@ag-ui/a2ui-middleware`](https://github.com/ag-ui-protocol/ag-ui/tree/main/middlewares/a2ui-middleware) (Prism ships an in-package opt-in painter with frozen caps; no runtime dependency), [`@ag-ui/a2a`](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/a2a/typescript), and [`@ag-ui/a2a-middleware`](https://github.com/ag-ui-protocol/ag-ui/tree/main/middlewares/a2a-middleware).
|
|
11
|
+
- A2A [current specification](https://a2a-protocol.org/latest/specification/) and [streaming rules](https://a2a-protocol.org/latest/topics/streaming-and-async/).
|
|
12
|
+
- MCP Apps [SEP-1865](https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp) and the [`io.modelcontextprotocol/ui` draft specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx).
|
|
13
|
+
|
|
14
|
+
## When to use it
|
|
15
|
+
|
|
16
|
+
Use this matrix when selecting Prism for an AG-UI client or planning protocol work. Tasks 3A and 3B complete full AG-UI 0.0.57 request/event compatibility plus explicit hardened MCP, MCP Apps, and remote A2A fronting. These are opt-in adapters over existing Prism clients, not an alternate runtime or discovery path.
|
|
17
|
+
|
|
18
|
+
## Inputs / request
|
|
19
|
+
|
|
20
|
+
Official `RunAgentInput` fields—lineage, all message roles/history, state, tools, context, props, media, and resume—are schema/bound checked then have no authority until `input.project` returns host-selected messages. Client tools remain client handoffs; state/props/media never grant identity, ownership, or server tools. Resume is exact `${runId}:${version}` CAS; edited arguments deny.
|
|
21
|
+
|
|
22
|
+
## Outputs / response / events
|
|
23
|
+
|
|
24
|
+
Prism emits current standard lifecycle, step, text, tool, state, messages, activity, reasoning, raw, and custom families when its mapper or an explicit projector can prove them. Deprecated `THINKING_*` and convenience chunk output are intentionally absent. SSE is baseline; capabilities truthfully narrow to configured replay/projectors/lifecycle, and source cursors remain bounded `prismCursor` metadata for reconnect.
|
|
25
|
+
|
|
26
|
+
## Request/response example
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"type": "TEXT_MESSAGE_CONTENT",
|
|
31
|
+
"messageId": "message-1",
|
|
32
|
+
"delta": "hello",
|
|
33
|
+
"prismEventId": "event-42",
|
|
34
|
+
"prismCursor": "opaque-owner-run-bound-cursor"
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Implementation example
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createAgentEventSourceAgUiReplay, createAgUiHandler } from "@arnilo/prism-ag-ui";
|
|
42
|
+
|
|
43
|
+
const replay = createAgentEventSourceAgUiReplay(persistence.events, {
|
|
44
|
+
resolveRun: hostResolveProtocolRun,
|
|
45
|
+
ownership: (authorization) => authorization.ownership,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const handle = createAgUiHandler({ authorize, sessionFactory, lifecycle, resolveRun, replay });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Extension and configuration notes
|
|
52
|
+
|
|
53
|
+
### A2A adoption
|
|
54
|
+
|
|
55
|
+
A2A stays separately mounted. `createA2AAgentEventSource()` maps durable runs to task events; `afterEventId` remains Prism-only.
|
|
56
|
+
|
|
57
|
+
`createAgUiA2AAdapter({ client, select, correlate, projectPart })` fronts one verified host-selected client: task text/status becomes AG-UI text/activity, correlation persists before output, and non-text/tool/A2UI needs a schema-validated projector. It uses streaming when declared; fallback accepts only a terminal task. Client origin/card/auth/bounds/abort checks remain active.
|
|
58
|
+
|
|
59
|
+
### MCP adoption through AG-UI
|
|
60
|
+
|
|
61
|
+
Prism adapts its hardened MCP bridge; no official middleware or second runtime. `connectMcpTools({ mcpApps: true })` requires `io.modelcontextprotocol/ui` acknowledgement, retains nested UI metadata over deprecated flat metadata, hides app-only tools, and bounds linked `ui://` HTML through `bridge.apps`. `createAgUiMcpAdapter()` selects model-visible tools for normal core dispatch; `createAgUiMcpAppHandler()` reauthorizes one-bridge initialize/ping/logging/tool/resource calls with approval and visibility; sandbox helper returns fixed iframe/CSP constraints. No generic proxy, cross-server call, raw HTML rendering, or automatic mutation retry.
|
|
62
|
+
|
|
63
|
+
## Security and performance notes
|
|
64
|
+
|
|
65
|
+
- Every replay/reconnect reauthorizes, then source access uses exact host ownership and host-resolved internal run IDs. Cursor content never selects ownership.
|
|
66
|
+
- Durable streams are at-least-once. Clients deduplicate `prismEventId`; source cursors resume strictly after a durable record.
|
|
67
|
+
- Client tools, state, context, forwarded properties, media URLs/data, remote A2A parts, MCP metadata, HTML, iframe messages, and reasoning blobs are untrusted. Projectors must use existing Prism media URL/SSRF/MIME policy before any resolution.
|
|
68
|
+
- `input.project` and all output projectors are allow-lists; all generic JSON has byte/depth/property/array caps and prototype-pollution keys fail before host callbacks. Tool handoffs are client-only and cannot widen identity, ownership, permissions, or active server tools.
|
|
69
|
+
- MCP Apps requires sandbox-origin separation, restrictive CSP, declared-domain ceilings, audited JSON-RPC, app/tool visibility checks, and user approval for UI-initiated mutations. The shipped proxy does not retry those mutations; Task 4 adds generic durable effect recovery.
|
|
70
|
+
|
|
71
|
+
## Related APIs
|
|
72
|
+
|
|
73
|
+
- [Frontend interoperability](ag-ui.md): shipped Prism AG-UI/ACP API.
|
|
74
|
+
- [Agent events](agent-events.md): source events and durable delivery.
|
|
75
|
+
- [Web-standard server](server.md): SSE `Last-Event-ID` reconnect route.
|
|
76
|
+
- [A2A interoperability](a2a.md): separately mounted A2A lifecycle and durable source adapter.
|
|
77
|
+
- [MCP bridge/server](mcp-tools.md): hardened MCP transport and capability boundary reused by future AG-UI MCP support.
|