@arnilo/prism 0.3.0 → 0.3.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 +27 -0
- package/README.md +3 -1
- package/dist/agent-loops.js +45 -8
- package/dist/agent-session/helpers.js +2 -2
- package/dist/cache-helpers.d.ts +11 -0
- package/dist/cache-helpers.js +29 -5
- package/dist/cli-provider-add.js +2 -1
- package/dist/context-budget.js +9 -6
- package/dist/contracts-core/agent.d.ts +2 -0
- package/dist/contracts-core/provider.d.ts +2 -0
- package/dist/event-multiplexer.js +0 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +5 -3
- package/dist/input.js +19 -11
- package/dist/node/session-store-jsonl.js +7 -3
- package/dist/providers/openai-compatible.js +2 -1
- package/dist/providers/openai-primitives.js +2 -1
- package/dist/providers/schema.d.ts +7 -0
- package/dist/providers/schema.js +25 -0
- package/dist/testing/provider-conformance.d.ts +10 -0
- package/dist/testing/provider-conformance.js +37 -0
- package/dist/trim-trailing-slashes.d.ts +8 -0
- package/dist/trim-trailing-slashes.js +14 -0
- package/docs/0.1.0-readiness.md +1 -1
- package/docs/acp.md +1 -0
- package/docs/ag-ui.md +1 -0
- package/docs/agent-loops.md +3 -0
- package/docs/agent-session-runtime.md +1 -0
- package/docs/browser-automation.md +1 -0
- package/docs/database-persistence.md +1 -1
- package/docs/graft.md +125 -0
- package/docs/host-security.md +3 -1
- package/docs/index.md +11 -9
- package/docs/input-and-prompt-assembly.md +11 -6
- package/docs/instruction-injection.md +1 -1
- package/docs/mcp-tools.md +1 -0
- package/docs/migration.md +10 -0
- package/docs/node-jsonl-session-store.md +1 -1
- package/docs/obscura.md +175 -0
- package/docs/observability.md +21 -1
- package/docs/performance.md +58 -4
- package/docs/ponytail.md +1 -1
- package/docs/provider-caching.md +13 -11
- package/docs/provider-conformance.md +6 -0
- package/docs/provider-packages.md +1 -1
- package/docs/provider-primitives.md +15 -2
- package/docs/providers/ai-sdk.md +1 -1
- package/docs/providers/anthropic.md +1 -1
- package/docs/providers/azure.md +1 -0
- package/docs/providers/bedrock.md +1 -0
- package/docs/providers/kimi.md +2 -1
- package/docs/providers/openai.md +19 -7
- package/docs/providers/opencode-go.md +3 -1
- package/docs/providers/openrouter.md +4 -3
- package/docs/providers/vertex.md +1 -0
- package/docs/public-contracts.md +2 -1
- package/docs/rag.md +55 -8
- package/docs/release-and-install.md +45 -7
- package/docs/server.md +1 -0
- package/docs/supervisors.md +3 -2
- package/docs/system-prompts.md +1 -1
- package/docs/tools.md +1 -1
- package/docs/web-tools.md +2 -0
- package/docs/wiki.md +140 -0
- package/docs/workflows.md +4 -3
- package/docs/working-and-semantic-memory.md +20 -0
- package/package.json +12 -5
- package/docs/api-page-template.md +0 -32
- package/docs/release-0.2.7-evidence.md +0 -514
package/docs/providers/openai.md
CHANGED
|
@@ -159,14 +159,26 @@ Official: [Prompt caching](https://developers.openai.com/api/docs/guides/prompt-
|
|
|
159
159
|
model declares `ModelConfig.cache.longRetention === true`; models without that
|
|
160
160
|
metadata omit the field. Featured `gpt-5.1` declares
|
|
161
161
|
`cache: { kind: "openai_key", longRetention: true, maxKeyLength: 64 }`.
|
|
162
|
-
- GPT-5.6+
|
|
163
|
-
`listOpenAIModels`
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`
|
|
162
|
+
- GPT-5.6+ models use current `prompt_cache_options` instead of retention:
|
|
163
|
+
`listOpenAIModels` / `mapOpenAIModel` set `cache.explicitBreakpoints: true` and
|
|
164
|
+
`longRetention: false` for those ids, so Prism never emits
|
|
165
|
+
`prompt_cache_retention` for them. When the host supplies
|
|
166
|
+
`cache.breakpoints` (or forces `cache.mode: "on"`), Prism emits
|
|
167
|
+
`prompt_cache_options: { mode: "explicit" }` and stamps
|
|
168
|
+
`prompt_cache_breakpoint: { mode: "explicit" }` on the last text block of each
|
|
169
|
+
selected message anchor (shared breakpoint selection with
|
|
170
|
+
`applyCacheControl`, capped at the official 4 cache writes per request;
|
|
171
|
+
`tools` breakpoints are skipped — tool definitions are not markable blocks).
|
|
172
|
+
The only supported TTL is `"30m"` (also the default), so no ttl field is ever
|
|
173
|
+
emitted. `cache.mode: "off"` suppresses explicit options and markers.
|
|
174
|
+
- Resolved cache fields win over caller `extra`: `prompt_cache_key`,
|
|
175
|
+
`prompt_cache_retention`, and `prompt_cache_options` are re-applied after the
|
|
176
|
+
`extra` spread, so invalid caller values cannot replace the resolved policy.
|
|
167
177
|
- Cache accounting is preserved in normalized `Usage`: OpenAI
|
|
168
|
-
`input_tokens_details.cached_tokens` maps to `Usage.cacheReadTokens
|
|
169
|
-
|
|
178
|
+
`input_tokens_details.cached_tokens` maps to `Usage.cacheReadTokens` and
|
|
179
|
+
`input_tokens_details.cache_write_tokens` maps to `Usage.cacheWriteTokens`
|
|
180
|
+
(GPT-5.6+ report cache writes; older models omit the field, leaving
|
|
181
|
+
`Usage.cacheWriteTokens` undefined).
|
|
170
182
|
- Provider-owned headers (`content-type`, `authorization`, `x-client-request-id`)
|
|
171
183
|
are applied after caller `ProviderRequestOptions.headers` so caller config
|
|
172
184
|
cannot replace credentials, content type, or the session request id; non-owned
|
|
@@ -219,7 +219,9 @@ Owned compat keys (`route`, `thinking`, `reasoning`, `reasoning_effort`,
|
|
|
219
219
|
shared `applyCacheControl()` helper) on the last content block of each selected
|
|
220
220
|
message — not to every block. Caching is enabled unless disabled
|
|
221
221
|
(`cacheRetention: "none"` / `cache.mode: "off"`) and the model opts in via
|
|
222
|
-
`ModelConfig.cache.kind: "cache_control"` (or `cache.mode: "on"`).
|
|
222
|
+
`ModelConfig.cache.kind: "cache_control"` (or `cache.mode: "on"`). A
|
|
223
|
+
`system_prompt` breakpoint serializes `system` as native text blocks with the
|
|
224
|
+
marker (plain string otherwise).
|
|
223
225
|
- `cacheRetention: "long"` emits `cache_control: { type: "ephemeral", ttl: "1h" }`
|
|
224
226
|
markers when the model allows long retention
|
|
225
227
|
(`ModelConfig.cache.longRetention !== false`); otherwise the default ephemeral
|
|
@@ -136,9 +136,10 @@ await kernel.load([
|
|
|
136
136
|
### Cache and session behavior
|
|
137
137
|
|
|
138
138
|
- `session_id` (request body) and the `X-Session-Id` header are derived from
|
|
139
|
-
`ProviderRequestOptions.
|
|
140
|
-
+ clamped to 256 characters via the shared
|
|
141
|
-
OpenRouter uses this for provider sticky routing
|
|
139
|
+
`ProviderRequestOptions.cache.key` (falling back to legacy `cacheKey`, then
|
|
140
|
+
`sessionId`) and sanitized + clamped to 256 characters via the shared
|
|
141
|
+
`sanitizeCacheKey()` helper. OpenRouter uses this for provider sticky routing
|
|
142
|
+
to maximize cache hits.
|
|
142
143
|
- **Automatic caching** (no breakpoints): when caching is enabled for an
|
|
143
144
|
explicit `cache_control` model (or `compat.openRouterCache` /
|
|
144
145
|
`cache.mode: "on"`), Prism emits a top-level
|
package/docs/providers/vertex.md
CHANGED
|
@@ -60,6 +60,7 @@ const provider = createVertexProvider({
|
|
|
60
60
|
- No Google Cloud SDK dependency in the package.
|
|
61
61
|
- Custom/private endpoint hosts are preserved.
|
|
62
62
|
- Tokens redacted from errors; no import-time credential prefetch — the credential is resolved exactly once per request (a rotating `CredentialValueSource` is never consumed twice; the same resolved token drives the wrapper check and the inner auth header).
|
|
63
|
+
- Conformance-proven (Task 6): package `setup()` performs zero fetch and zero credential resolution; an already-aborted signal fails fast; a truncated SSE stream (no `data: [DONE]`) ends in an `error` event; native Vertex cached-content lifecycle is intentionally unsupported on the OpenAI-compatible route — no cache wire fields are emitted even when the request carries Prism cache hints (use `@arnilo/prism-provider-google`'s `extra.cachedContent` on that package, or manage cache resources host-side).
|
|
63
64
|
- Pair with model-router residency allow-lists on `location`.
|
|
64
65
|
|
|
65
66
|
## Related APIs
|
package/docs/public-contracts.md
CHANGED
|
@@ -124,6 +124,7 @@ Important request shapes:
|
|
|
124
124
|
| `ContextResolutionContext` | Context provider input: messages plus optional session/run ids, metadata, and signal. |
|
|
125
125
|
| `InputAssemblyLayout` | Default input layout selector: `"cache_aware"` (default) or opt-in `"legacy"`. |
|
|
126
126
|
| `DefaultInputBuildContext` | Optional default input assembly context: input layout, instructions, history, summaries, attachments, explicit resources, tool results, middleware, ids, metadata, and signal. |
|
|
127
|
+
| `PromptBuildRequest` | Prompt-builder input: messages, context, selected skills, active tools, model, metadata, signal, and optional `inputLayout`; the default builder uses cache-aware ordering unless `legacy` is explicit. |
|
|
127
128
|
| `ResolveContextOptions` | Ordered context resolution input: selected providers, messages, ids, metadata, signal, and optional middleware. |
|
|
128
129
|
| `AssembleProviderInputOptions` | Provider input assembly input: model, input, optional builders, selected context providers/skills, active tools, metadata, and signal. |
|
|
129
130
|
| `PromptTemplateOptions` | Missing-variable behavior for tiny `renderPromptTemplate()` substitutions. |
|
|
@@ -148,7 +149,7 @@ Important request shapes:
|
|
|
148
149
|
| `CheckpointStore` | Generic versioned checkpoint capability: save/load/bounded-list/delete by namespace and key, with ownership, exact-version CAS, and lease fencing. `createMemoryCheckpointStore()` is the reference implementation; it is bounded — `maxRecords` (default 10,000, evicts least-recently-saved) and `maxValueBytes` (default 1 MiB per JSON value). |
|
|
149
150
|
| `LeaseStore` | Atomic acquire/renew/release/get by namespace and key, with opaque claim tokens, expiry, ownership scope, and monotonically increasing takeover fences. `createMemoryLeaseStore()` is the reference implementation. |
|
|
150
151
|
| `RunFeedbackStore` | Immutable append, bounded owned query, and owned deletion for ratings/comments/tags linked to existing run/trace/evaluation IDs. `createMemoryRunFeedbackStore()` is the reference implementation. |
|
|
151
|
-
| `EventMultiplexer<T>` | Generic bounded fan-in from async sources. `createEventMultiplexer()` owns queue limits, overflow policy, abort, source teardown, and close behavior. Single-consumer contract: a second concurrent `subscribe()` throws `EventMultiplexerError` (`ERR_PRISM_EVENT_MULTIPLEXER_SINGLE_CONSUMER`); the slot frees when the active consumer completes
|
|
152
|
+
| `EventMultiplexer<T>` | Generic bounded fan-in from async sources. `createEventMultiplexer()` owns queue limits, overflow policy, abort, source teardown, and close behavior. Graceful `close()` stops publishes/sources and drains already-queued events before the subscriber completes; overflow `close` still emits one notice and terminates. Single-consumer contract: a second concurrent `subscribe()` throws `EventMultiplexerError` (`ERR_PRISM_EVENT_MULTIPLEXER_SINGLE_CONSUMER`); the slot frees when the active consumer completes or is `return()`ed at a yield. `observe` fan-in is unchanged (broadcast happens at the source). |
|
|
152
153
|
| `PersistencePage<T>` | Cursor-paginated result page: `items`, optional `nextCursor`, optional `total`. |
|
|
153
154
|
| `PersistenceQuery` | Common pagination controls: `cursor?`, `limit?`, `order?: "asc" \| "desc"`. |
|
|
154
155
|
| `OwnershipScope` | Multi-tenant scope: `tenantId?`, `accountId?`, `userId?`. Included in records and queries. |
|
package/docs/rag.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-rag` is an optional package for deterministic text/Markdown chunking, bounded embedding/vector indexing, atomic scoped source replacement/deletion, focused text/Markdown/HTML/PDF parsing, bounded reranking, ingestion status, attributable citations, content-trust metadata, and explicit `ContextProvider` injection. It reuses `Embedder` and `VectorStore` from `@arnilo/prism-memory`; Prism core input assembly is unchanged.
|
|
5
|
+
`@arnilo/prism-rag` is an optional package for deterministic text/Markdown chunking (with ATX heading-stack metadata), bounded embedding/vector indexing with embedder-identity drift guards, atomic scoped source replacement/deletion with content-hash skip and generation visibility, hybrid vector+lexical retrieval with reciprocal-rank fusion (one embed / one RRF / one rerank across one or many exact scopes), focused text/Markdown/HTML/PDF parsing, bounded reranking (host seam plus a TEI REST adapter), ingestion status, attributable citations, content-trust metadata, and explicit `ContextProvider` injection. It reuses `Embedder` and `VectorStore` from `@arnilo/prism-memory`; Prism core input assembly is unchanged.
|
|
6
6
|
|
|
7
7
|
## When to use it
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ Chunking:
|
|
|
18
18
|
| `chunkMarkdown(markdown, options)` | Same engine, preferring heading/paragraph boundaries |
|
|
19
19
|
| `sourceId` | Required stable, non-secret source identifier |
|
|
20
20
|
| `size` / `overlap` | Character ceiling and repeated context |
|
|
21
|
-
| `metadata` | JSON metadata copied to every chunk
|
|
21
|
+
| `metadata` | JSON metadata copied to every chunk; Markdown chunking additionally stamps `heading` (ordered parent-first heading stack, e.g. `["Policy", "3.2 Leave"]`) unless the caller supplies one.
|
|
22
22
|
|
|
23
23
|
Document lifecycle:
|
|
24
24
|
|
|
@@ -35,13 +35,18 @@ Index/retrieve:
|
|
|
35
35
|
| Field | Required | Meaning |
|
|
36
36
|
| --- | --- | --- |
|
|
37
37
|
| `embedder` / `store` | yes | Phase 7 `Embedder` and `VectorStore` |
|
|
38
|
-
| `scope` |
|
|
38
|
+
| `scope` / `scopes` | one or the other | Exact `{ tenantId, resourceId, corpusId }` (corpus → vector thread). `scope` is the single-corpus path; `scopes` is 0..`HARD_RETRIEVE_SCOPE_CAP` (8) exact scopes. Empty `scopes` returns no hits and does not embed/search/rerank. Passing both or neither throws. |
|
|
39
39
|
| `chunks` | indexing | `RagChunk[]` from package chunkers or compatible host parser |
|
|
40
|
-
| `topK` / `queryCandidates` | retrieval | Returned result count and bounded pre-filter candidates |
|
|
40
|
+
| `topK` / `queryCandidates` | retrieval | Returned result count and bounded pre-filter candidates (`queryCandidates` is **per scope**) |
|
|
41
|
+
| `lexical` | no | `"fts"` \| `"bm25"` \| `"off"` (default `"off"`); enables the lexical retrieval leg when the store advertises it |
|
|
42
|
+
| `fusion` / `rrfK` | no | `"rrf"` fusion of vector+lexical legs (default `"rrf"` when `lexical` is on; `rrfK` default 60, hard cap 1,000) |
|
|
41
43
|
| `filter` | no | Shallow JSON metadata equality filter |
|
|
42
44
|
| `reranker` | no | Host-owned `Reranker` receives redacted bounded `RagHit[]` and must return the same IDs once each, in preferred order. |
|
|
43
45
|
| `maxRerankBytes` / `maxRerankMs` / `rerankConcurrency` | no | Reranker caps; defaults/hard limits are 64/256 KiB, 2/10 s, and 2/8 active calls per reranker object. |
|
|
44
46
|
| `statusStore` | no | `IngestionStatusStore` records per-source pending/indexed/failed/partial byte/chunk progress; use `listIngestionStatus()` for capped exact-scope pages. |
|
|
47
|
+
| `contentHash` | no | Host-computed document digest; stamped on records and enables unchanged-source skip in `replaceSource` (`skipIfUnchanged`, default true when present) |
|
|
48
|
+
| `reuseEmbeddings` | no | `ReadonlyMap<string, ReusableEmbedding>` — chunk id → `{ text, embedding }`; embeddings reused (no embed call) when texts match |
|
|
49
|
+
| `telemetry` / `telemetryParent` | no | `RagTelemetry` seam (e.g. `createRagTelemetry()` from `@arnilo/prism-observability-opentelemetry`); spans nest under `telemetryParent` |
|
|
45
50
|
| `redactor` / `secrets` | no | Redact before embedding, persistence, reranking, and injection |
|
|
46
51
|
| `signal` | no | Abort embedding, vector operations, reranking, and batch progression |
|
|
47
52
|
|
|
@@ -51,7 +56,8 @@ Index/retrieve:
|
|
|
51
56
|
- `indexChunks()` returns `{ indexed, sourceIds }` after bounded batch upserts.
|
|
52
57
|
- `replaceSource()` / `deleteSource()` return `{ sourceId, deleted, indexed }`.
|
|
53
58
|
- `replaceDocument()` carries loader parser metadata into chunk metadata; the web loader preserves web-tools citation ID and `untrusted: true`.
|
|
54
|
-
- `retrieveContext()` returns `{ query, trust, text, hits, citations, truncated }`. Every hit/citation carries `{ provenance: { sourceId, chunkId, citationId, provider, retrieval: "vector", retrievedAt }, trust: { untrusted: true, inert: true, injectionCapable: true } }`; `retrievalRank` preserves pre-rerank order. Rendered text uses `[citation-id] text` blocks.
|
|
59
|
+
- `retrieveContext()` returns `{ query, trust, text, hits, citations, truncated }`. Every hit/citation carries `{ provenance: { sourceId, chunkId, citationId, provider, tenantId, resourceId, corpusId, retrieval: "vector" | "lexical" | "hybrid", retrievedAt }, trust: { untrusted: true, inert: true, injectionCapable: true } }`; `retrieval` labels the leg(s) that surfaced the hit after RRF fusion, and `retrievalRank` preserves pre-rerank order. Rendered text uses `[citation-id] text` blocks.
|
|
60
|
+
- `replaceSource()` returns `{ sourceId, deleted, indexed, skipped? }` (skipped when the stored `contentHash` matched and no writes occurred). Records carry `embedderId` (from `Embedder.id`, the Task 2 identity contract) and `generation` (scope-level monotonically bumped index per replacement; `_rag` metadata carries `contentHash` when supplied). `store.getCurrentGeneration(scope)` / `store.setCurrentGeneration(scope, n)` let hosts read and roll back the visible generation; retrieval filters to the current generation while legacy generation-less rows stay visible.
|
|
55
61
|
- `createMemoryIngestionStatusStore()` is a bounded in-memory reference adapter. `listIngestionStatus({ store, scope, limit, cursor })` returns capped status pages; hosts supply durable stores when status must survive process restart.
|
|
56
62
|
- `createRagContextProvider()` returns one ordinary context provider. Empty queries/results contribute no block.
|
|
57
63
|
- No events, tools, permissions, provider calls, loaders, or network requests are added.
|
|
@@ -94,7 +100,7 @@ await indexChunks({ chunks, embedder, store, scope, statusStore });
|
|
|
94
100
|
const found = await retrieveContext("approval policy", {
|
|
95
101
|
embedder,
|
|
96
102
|
store,
|
|
97
|
-
scope,
|
|
103
|
+
scopes: [scope], // or `scope` for one corpus
|
|
98
104
|
topK: 4,
|
|
99
105
|
filter: { category: "security" },
|
|
100
106
|
reranker: { rerank: async ({ hits }) => [...hits].sort((a, b) => b.score - a.score) },
|
|
@@ -109,11 +115,47 @@ const agent = createAgent({
|
|
|
109
115
|
console.log(found.text, await agent.createSession().run("How do approvals work?"));
|
|
110
116
|
```
|
|
111
117
|
|
|
118
|
+
Content-hash skip and hash validation:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { isValidContentHash } from "@arnilo/prism-rag";
|
|
122
|
+
|
|
123
|
+
const digest = "ab12..."; // host-computed SHA-256 hex of the document
|
|
124
|
+
if (!isValidContentHash(digest)) throw new Error("invalid digest");
|
|
125
|
+
await replaceSource({ sourceId: "doc", chunks, embedder, store, scope, contentHash: digest }); // unchanged → skipped, zero embeds
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Hybrid retrieval, TEI reranking, and telemetry:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { createRagTelemetry } from "@arnilo/prism-observability-opentelemetry";
|
|
132
|
+
import { createTeiReranker } from "@arnilo/prism-rag";
|
|
133
|
+
|
|
134
|
+
const telemetry = createRagTelemetry({ tracer, meter }); // @opentelemetry/api instruments
|
|
135
|
+
const org = { tenantId: "t1", resourceId: "docs", corpusId: "org" };
|
|
136
|
+
const user = { tenantId: "t1", resourceId: "docs", corpusId: "user" };
|
|
137
|
+
const session = { tenantId: "t1", resourceId: "docs", corpusId: "session" };
|
|
138
|
+
const found = await retrieveContext("leave balance", {
|
|
139
|
+
embedder,
|
|
140
|
+
store, // a store that advertises lexicalModes: ["fts"]
|
|
141
|
+
scopes: [org, user, session], // one embed, per-scope legs, one RRF, one rerank
|
|
142
|
+
lexical: "fts",
|
|
143
|
+
topK: 8,
|
|
144
|
+
reranker: createTeiReranker({ baseUrl: "https://tei.svc:8080" }),
|
|
145
|
+
telemetry, // roots a rag_request span tree; attachSession/handleAgentEvent NOT required
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
112
149
|
## Extension and configuration notes
|
|
113
150
|
|
|
114
151
|
- Supply any Phase 7-conforming embedder/vector store, including the in-memory reference or PostgreSQL/pgvector adapter.
|
|
115
152
|
- Metadata filtering is package-local after a bounded candidate query so existing vector contracts/adapters remain unchanged. Increase `queryCandidates` only when selective filters measurably need it.
|
|
116
153
|
- `Reranker` is a host seam, not a provider integration. Return each redacted candidate ID exactly once; Prism retains canonical hit/provenance/trust fields and exposes `retrievalRank` for diagnostics. Add a hosted reranker only when a host owns its credentials, quota, and retry policy.
|
|
154
|
+
- `createTeiReranker({ baseUrl, model?, timeoutMs?, maxResponseBytes?, ssrf?, allowLoopback?, fetch? })` (`CreateTeiRerankerOptions`) adapts a Hugging Face TEI `POST <baseUrl>/rerank` endpoint (`{query, texts, raw_scores:false}` → `{results:[{index,score}]}`) into the `Reranker` seam. It returns a permutation-only reorder of the same hit objects, so provenance/trust move untouched. Response parsing is strict — short/duplicate/out-of-range indices, non-finite scores, HTTP errors, timeouts, and oversized bodies all fail closed; the `rerankHits` caps (`maxRerankBytes`, `maxRerankMs`, `rerankConcurrency`) still apply around it. The default transport is the core DNS-pinned `pinnedFetch` (redirect-free, byte-bounded to 65,536 by default); HTTPS is required unless `allowLoopback: true` (loopback dev/test) or the host supplies `ssrf`/`fetch` for cluster networking. The adapter validates URL shape only — SSRF policy enforcement stays host-side. No credentials are ever sent; there is no SaaS default URL.
|
|
155
|
+
- Hybrid retrieval: pass `lexical: "fts"` (or `"bm25"` when the store supports it) to `retrieveContext()`; the two legs are fused with reciprocal-rank fusion (`fusion: "rrf"`, `rrfK` 60 default; the pure helper `fuseReciprocalRank()` returns `FusedCandidate[]` for custom orchestration). Stores advertise support via `lexicalModes?: readonly LexicalMode[]` and `tokenizeLexical()` is the shared tokenizer. Each hit's provenance `retrieval` field reports `vector`/`lexical`/`hybrid`; fusion internals expose `RetrievalLeg`.
|
|
156
|
+
- Multi-scope retrieve: `scopes: RagScope[]` searches each exact scope against that scope's current generation, then runs **one** RRF over the union and **one** rerank. The query is embedded once. `queryCandidates` is per scope. Duplicate scopes are dropped. `HARD_RETRIEVE_SCOPE_CAP` is 8.
|
|
157
|
+
- Embedder identity/drift guard: `Embedder.id` (memory contract) is stamped onto every vector record as `embedderId`. `retrieveContext()` fails closed with `ERR_PRISM_RAG_EMBEDDER_MISMATCH` when a stored record's `embedderId` or dimensions differ from the active embedder (for example after a model change) — re-index the source before retrieving. Legacy records without an `embedderId` also fail closed, naming the re-index path.
|
|
158
|
+
- Generations: `replaceSource()` stamps a scope-level generation (auto-incremented per replacement) on staged records and the vector store filters retrieval to the current generation. `setCurrentGeneration()` supports rollback; stores without generation tracking keep legacy behavior (everything visible).
|
|
117
159
|
- `IngestionStatusStore` is optional observability storage. It is keyed by exact scope and source ID; use `listIngestionStatus()` rather than an unbounded corpus scan. The reference memory store is process-local; implement the same capped scope behavior for durable status.
|
|
118
160
|
- `createRagContextProvider()` derives its query from latest user text by default; pass a fixed string or callback for host-controlled query generation.
|
|
119
161
|
- `createResourceDocumentLoader({ loader })` calls one host-owned `ResourceLoader`; it scans nothing and performs no filesystem or network I/O itself. Pass the host's permission/trust context to that loader.
|
|
@@ -123,14 +165,19 @@ console.log(found.text, await agent.createSession().run("How do approvals work?"
|
|
|
123
165
|
|
|
124
166
|
## Security and performance notes
|
|
125
167
|
|
|
126
|
-
- Every index/query includes exact tenant/resource/corpus scope; returned records are rechecked and malformed/foreign records fail closed.
|
|
168
|
+
- Every index/query includes exact tenant/resource/corpus scope; returned records are rechecked and malformed/foreign records fail closed. `retrieveContext` accepts `scope` or `scopes` (never both, never neither). Empty `scopes` is the host “no allowed corpora” path — no embed, no search, no rerank. A hit whose stored scope is not in the requested list fails closed. Generation filters stay per scope.
|
|
169
|
+
- Embedding identity is a privacy/consistency boundary: records from a different embedder (or dimension) never silently mingle with new ones — retrieval fails closed and names the re-index path. Generation pointers are scope-scoped: a pointer row belongs to exactly one scope, and visibility is computed inside the store (SQL), never by post-filtering in JS.
|
|
127
170
|
- Source IDs become citation/storage IDs and must be stable non-secret identifiers. Text and user metadata can be redacted before external embedding and persistence.
|
|
171
|
+
- Heading metadata is document text only — it passes through the existing `maxMetadataBytes` cap as chunk metadata; no new content path is introduced.
|
|
172
|
+
- `contentHash` skip and `reuseEmbeddings` never leak embeddings: reused embeddings are keyed by chunk id within one replacement and only accepted when the stored text matches exactly.
|
|
128
173
|
- Retrieved documents are untrusted inert context. Prompt-injection text cannot activate tools, skills, credentials, permissions, or extensions.
|
|
129
174
|
- Remote sources must pass existing resource/media trust, SSRF, MIME, and byte policies before their decoded text reaches this package.
|
|
130
175
|
- `replaceSource()` stages every bounded embedding before opening the store transaction. It requires a source-aware transactional store and fails closed rather than pretending generic upserts are atomic. `createMemoryVectorStore()` supplies the reference `getBySource()` / transaction capability; durable stores must implement equivalent exact-scope behavior.
|
|
131
176
|
- `deleteSource()` rechecks every returned record's tenant/resource/corpus and source metadata before delete. Same source IDs in another corpus remain untouched.
|
|
132
177
|
- Parsers enforce byte/page/time caps, abort before and after parsing, decode UTF-8 strictly, and strip HTML script/style content. Parsed and retrieved text remains untrusted inert context; it never gains tool authority.
|
|
133
|
-
- Rerankers receive redacted input under byte/time/concurrency caps. Timeout, abort, unknown/duplicate/missing IDs, oversized input, and reranker failures fail closed; returned objects cannot overwrite Prism provenance/trust fields.
|
|
178
|
+
- Rerankers receive redacted input under byte/time/concurrency caps. Timeout, abort, unknown/duplicate/missing IDs, oversized input, and reranker failures fail closed; returned objects cannot overwrite Prism provenance/trust fields. The TEI adapter adds fail-closed response parsing (permutation completeness, finite scores) and honors the 65,536-byte response ceiling; SSRF/URL policy is host-side (see Extension notes).
|
|
179
|
+
- Telemetry is a host-owned seam: `RagTelemetry` adapter (`createRagTelemetry()`) drops anything outside a fixed span-name set and `rag.*`-shaped attribute keys, so raw chunk text never reaches the tracer unless the host's own `attributeFilter` opts it in; when the seam is absent, instrumentation costs nothing.
|
|
180
|
+
- Durable vector stores (PostgreSQL/pgvector path via `@arnilo/prism-memory`) run their DDL against the host's knowledge database — tables are created in a schema/table the host names (default `prism_memory.semantic_memory`), and hosts must own backup/retention of that database. See [Working and semantic memory](working-and-semantic-memory.md).
|
|
134
181
|
- Ingestion failure errors are redacted before status storage. Status reads reject foreign scope entries and page-limit violations; status itself creates no permission or tool authority.
|
|
135
182
|
- Filtering scans at most `queryCandidates` hits; rendering stops at top-K, UTF-8 result bytes, or estimated context-token ceiling.
|
|
136
183
|
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
Prism's current **0.3.
|
|
5
|
+
Prism's current **0.3.1** line has **60 publishable manifests**: the root `@arnilo/prism` core package plus **59 workspace packages** — 17 provider adapters, 10 `prism-*` family/profile packages, and 32 capability packages. (Generated by `node scripts/package-truth.mjs` → `scripts/package-truth.json` — the manifest-derived single source for counts, provider membership, umbrella closures, and profile closures.) The last lockstep cut was 0.3.0; Decision B now publishes changed packages independently inside `^0.3.0` — the plan 039 changed-package cut moved root `@arnilo/prism` and every plan-035+ changed package to **0.3.1** (obscura joined at its reviewed initial 0.3.0), and independent publication continues inside `^0.3.0` ranges (which satisfy 0.3.1). This page describes how they are packed, what each tarball contains, how to install them, the required `@arnilo/prism` peer range, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](./0.1.0-readiness.md).
|
|
6
6
|
|
|
7
|
-
Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package has a required `@arnilo/prism
|
|
7
|
+
Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package has a required `@arnilo/prism` peer inside the Decision B window — packages republishing in the plan 039 cut carry `^0.3.1`; unchanged packages keep their `^0.3.0` peer (both satisfy `@arnilo/prism@0.3.1`); profiles are pure manifests. The plan 039 republished set declares the required `@arnilo/prism@^0.3.1` peer; unchanged packages keep `^0.3.0`. Installation activates no provider, listener, database, browser, credential, or tool capability.
|
|
8
8
|
|
|
9
|
-
Current **
|
|
9
|
+
Current **58** publishable manifests (root + 57 workspace packages):
|
|
10
10
|
|
|
11
11
|
`@arnilo/prism`, `@arnilo/prism-ag-ui`, `@arnilo/prism-browser`, `@arnilo/prism-coding-agent`, `@arnilo/prism-coding-security`, `@arnilo/prism-compaction-llm`
|
|
12
12
|
`@arnilo/prism-compaction-observational-memory`, `@arnilo/prism-credentials-node`, `@arnilo/prism-enterprise-postgres`, `@arnilo/prism-evals`, `@arnilo/prism-mcp`, `@arnilo/prism-memory`
|
|
@@ -15,7 +15,7 @@ Current **57** publishable manifests (root + 56 workspace packages):
|
|
|
15
15
|
`@arnilo/prism-provider-alibaba`, `@arnilo/prism-provider-anthropic`, `@arnilo/prism-provider-azure`, `@arnilo/prism-provider-bedrock`, `@arnilo/prism-provider-google`, `@arnilo/prism-provider-kimi`
|
|
16
16
|
`@arnilo/prism-provider-neuralwatt`, `@arnilo/prism-provider-ollama`, `@arnilo/prism-provider-openai`, `@arnilo/prism-provider-opencode-go`, `@arnilo/prism-provider-openrouter`, `@arnilo/prism-provider-vertex`
|
|
17
17
|
`@arnilo/prism-provider-clinepass`, `@arnilo/prism-provider-deepseek`, `@arnilo/prism-provider-xai`, `@arnilo/prism-provider-zai`, `@arnilo/prism-rag`, `@arnilo/prism-server`, `@arnilo/prism-session-store-codecs`, `@arnilo/prism-session-store-nats`, `@arnilo/prism-session-store-postgres`, `@arnilo/prism-session-store-sqlite`
|
|
18
|
-
`@arnilo/prism-openapi-tools`, `@arnilo/prism-supervisor`, `@arnilo/prism-tool-validator-json-schema`, `@arnilo/prism-web-tools`, `@arnilo/prism-work-tools`, `@arnilo/prism-workflows`, `@arnilo/prism-document-reader`, `@arnilo/prism-computer-use-linux`, `@arnilo/prism-antigravity-agent`
|
|
18
|
+
`@arnilo/prism-openapi-tools`, `@arnilo/prism-supervisor`, `@arnilo/prism-tool-validator-json-schema`, `@arnilo/prism-web-tools`, `@arnilo/prism-wiki`, `@arnilo/prism-work-tools`, `@arnilo/prism-workflows`, `@arnilo/prism-document-reader`, `@arnilo/prism-computer-use-linux`, `@arnilo/prism-antigravity-agent`, `@arnilo/prism-graft`, `@arnilo/prism-obscura`
|
|
19
19
|
|
|
20
20
|
Core ships `dist`, docs, templates, and `CHANGELOG.md`; code packages ship compiled output, README, license, and changelog. Family/profile packages ship manifest, README, and changelog. `@arnilo/prism-providers` includes all fourteen `@arnilo/prism-provider-*` packages in its family (Azure/Bedrock/Vertex stay on `@arnilo/prism-all`).
|
|
21
21
|
|
|
@@ -41,6 +41,7 @@ Consumers install the core package for the runtime and add first-party packages
|
|
|
41
41
|
| 0.0.12 AG-UI (after release) | `npm install @arnilo/prism@0.0.12 @arnilo/prism-ag-ui@0.0.12` |
|
|
42
42
|
| Install bounded web research tools | `npm install @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-tool-validator-json-schema` |
|
|
43
43
|
| Install browser automation tools | `npm install @arnilo/prism @arnilo/prism-browser playwright-core@1.61.0` |
|
|
44
|
+
| Install Obscura browser-engine tools (host supplies the binary) | `npm install @arnilo/prism @arnilo/prism-obscura` |
|
|
44
45
|
| Build everything (core + workspaces) | `npm run build` |
|
|
45
46
|
| Delete all build output (explicit one-shot, see build notes) | `npm run clean` |
|
|
46
47
|
| Run the default (network-free) test suite | `npm test` |
|
|
@@ -63,6 +64,7 @@ Run `npm run clean` explicitly after deleting source files or switching branches
|
|
|
63
64
|
| `@arnilo/prism/providers/openai-compatible` | `dist/providers/openai-compatible.{js,d.ts}` |
|
|
64
65
|
| `@arnilo/prism/providers/transport` | `dist/providers/transport.{js,d.ts}` |
|
|
65
66
|
| `@arnilo/prism/providers/openai` | `dist/providers/openai-primitives.{js,d.ts}` |
|
|
67
|
+
| `@arnilo/prism/providers/schema` | `dist/providers/schema.{js,d.ts}` |
|
|
66
68
|
| `@arnilo/prism/providers/media` | `dist/providers/media.{js,d.ts}` |
|
|
67
69
|
| `@arnilo/prism/testing/provider-conformance` | `dist/testing/provider-conformance.{js,d.ts}` |
|
|
68
70
|
| `@arnilo/prism/testing/agent-event-source-conformance` | `dist/testing/agent-event-source-conformance.{js,d.ts}` |
|
|
@@ -92,7 +94,7 @@ A packed tarball contains only public compiled output and release files:
|
|
|
92
94
|
- Code packages ship `README.md`, `LICENSE`, and `CHANGELOG.md`; family/profile packages ship `README.md` and `CHANGELOG.md`.
|
|
93
95
|
- The core tarball additionally ships the full `docs/` directory (the docs hub) and `templates/init/` used by `prism init`.
|
|
94
96
|
- `dist/cli.js` and the `bin` link in core.
|
|
95
|
-
- **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.3.
|
|
97
|
+
- **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.3.1.tgz`; first-party packages produce `arnilo-prism-provider-<name>-0.3.1.tgz` / `arnilo-prism-compaction-<name>-0.3.1.tgz` / `arnilo-prism-coding-agent-0.3.1.tgz`; family/profile packages produce `arnilo-prism-{providers,compaction,base,code,sdk,all}-0.3.1.tgz`; independent Decision B tags (e.g. `@arnilo/prism-obscura@0.3.0`, the 0.3.1 RAG engine patch) carry their own package version. Later independent package tags carry their own package version. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
|
|
96
98
|
|
|
97
99
|
Excluded from every tarball by `files` negation:
|
|
98
100
|
|
|
@@ -365,6 +367,41 @@ For a later coding-agent-only patch, bump its manifest with `bump --package @arn
|
|
|
365
367
|
|
|
366
368
|
**Rollback notes.** Before publication, restore the 0.2.9 manifests/tag. After publication, roll forward with an additive 0.3.x package patch; npm unpublish is not a rollback strategy.
|
|
367
369
|
|
|
370
|
+
### 0.3.1 independent RAG engine patch (plan 034 Task 12)
|
|
371
|
+
|
|
372
|
+
**Decision: GO when the operator prerequisites below are recorded.** Release **0.3.1** is the first Decision B independent patch: only `@arnilo/prism-memory`, `@arnilo/prism-rag`, and `@arnilo/prism-observability-opentelemetry` move `0.3.0 → 0.3.1`. Internal `^0.3.0` ranges stay. Root current line remains **0.3.0**.
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
node scripts/release.mjs bump --package @arnilo/prism-memory --type patch
|
|
376
|
+
node scripts/release.mjs bump --package @arnilo/prism-rag --type patch
|
|
377
|
+
node scripts/release.mjs bump --package @arnilo/prism-observability-opentelemetry --type patch
|
|
378
|
+
node scripts/release.mjs gate --update-baseline --skip-tarball # review Embedder.id; scanner is additive-only
|
|
379
|
+
node scripts/release.mjs check --allow-dirty --allow-untagged
|
|
380
|
+
# publish tags (operator handoff; not this task):
|
|
381
|
+
# git tag @arnilo/prism-memory@0.3.1 && git tag @arnilo/prism-rag@0.3.1
|
|
382
|
+
# git tag @arnilo/prism-observability-opentelemetry@0.3.1 && git push --tags
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Compat.** Baselines regenerated with `--update-baseline`. Expected deltas are additive exports (`createPostgresVectorStore`, `createTeiReranker`, `createRagTelemetry`, multi-scope `scopes`, `HARD_RETRIEVE_SCOPE_CAP`, fusion/hash/generation helpers). `Embedder.id` is a TypeScript implementer break documented in `docs/migration.md` `0.3.0 → 0.3.1`; the name-level scanner does not see interface members, so `--allow-break` is not required. `RagProvenance` gained `tenantId`/`resourceId`/`corpusId` (additive interface members, invisible to scanner). **Store:** additive Postgres DDL; 0.3.0 rows remain readable. **Rollback:** restore the 0.3.0 package versions. Publication remains the operator handoff — this task does not publish.
|
|
386
|
+
|
|
387
|
+
### 0.3.1 changed-package cut (plan 039 Task 8)
|
|
388
|
+
|
|
389
|
+
**Decision: GO when the operator prerequisites below are recorded.** The plan 039 cut is the first Decision B **root** patch: the baseline is the plan 035 completion parent `c600eaa`; 30 packages publish in dependency order — root `@arnilo/prism` plus 27 changed workspace packages move to **0.3.1** (`@arnilo/prism-rag` moves 0.3.1 → 0.3.2), and `@arnilo/prism-obscura` publishes new at its reviewed initial **0.3.0**. Every unchanged package stays byte-identical (peers keep the `^0.3.0` window; republished packages carry `^0.3.1` root peers — both satisfy the root). Additive-only compat (version literal + plan 036/037 additive exports + the obscura CDP/browser surface; baselines regenerated with `--update-baseline`, no `--allow-break`, no migration).
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
node scripts/release.mjs changed --baseline c600eaa18f65b56764ec2fb408ec813536eff6f7 # 30 packages
|
|
393
|
+
# per-package: node scripts/release.mjs bump --package <name> --type patch (applied by plan 039 task 8)
|
|
394
|
+
node scripts/release.mjs gate --update-baseline --skip-tarball
|
|
395
|
+
node scripts/release.mjs check --independent --baseline c600eaa18f65b56764ec2fb408ec813536eff6f7
|
|
396
|
+
node scripts/release.mjs publish --independent --baseline c600eaa18f65b56764ec2fb408ec813536eff6f7 --dry-run
|
|
397
|
+
# publish tags (operator handoff; not this task): push the 30 annotated
|
|
398
|
+
# `<name>@<version>` package tags (e.g. @arnilo/prism@0.3.1,
|
|
399
|
+
# @arnilo/prism-obscura@0.3.0, @arnilo/prism-rag@0.3.2) — release.yml's publish
|
|
400
|
+
# job runs deterministic release:publish in dependency order with OIDC provenance.
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Compat.** Baselines regenerated (`--update-baseline`): version literal, obscura `connectObscuraCdp`/`createObscuraWebTools` types, plan 036/037 additive exports. **Rollback:** restore the pre-cut manifests/tags. Publication remains the operator handoff — this task does not publish.
|
|
404
|
+
|
|
368
405
|
### 0.2.9 publish handoff (plan 029 Task 10)
|
|
369
406
|
|
|
370
407
|
**Decision: GO when the operator prerequisites below are recorded.** Release **0.2.9** (plan 029) is the provider-adoption and behavior-packages cut on the 0.2.x review-remediation line. API surface **additive-only** (plain reviewed compat gate at 0.2.9: expected deltas are the version literal plus the new provider/OAuth/impeccable exports and the form-urlencoded `pollDeviceCodeToken` options; zero removals; baselines regenerated with `--update-baseline`, no `--allow-break`). Ships `@arnilo/prism-provider-deepseek`, `@arnilo/prism-provider-xai` (API key + SuperGrok RFC 8628), `@arnilo/prism-provider-clinepass`, and `@arnilo/prism-impeccable`. Ponytail peer `^4.9.0` (bare `/ponytail` reports status). Caveman registers extra `SKILL.md`. SuperGrok is host-invoked; Cline WorkOS, DeepSeek `/anthropic`, grok-cli file scan, harness/Cordis/Muse, Caveman 2 engine, and Impeccable live detector stay out. Release graph is **55** publishable manifests at exact **0.2.9** (root + 54 workspace). Store compatibility with 0.2.8: **compatible, no migration**.
|
|
@@ -876,8 +913,9 @@ Audit fixes, dependency updates, and security patches land only for the supporte
|
|
|
876
913
|
|
|
877
914
|
## Extension and configuration notes
|
|
878
915
|
|
|
879
|
-
- **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **caret** `@arnilo/prism@^0.3.0` peer (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 030, Decision B — independent packages):** internal ranges stay inside the 0.x `^0.3.0` window, so a package may patch independently while consumers remain on a compatible 0.3.x line. A package outside that window (for example `0.4.0`) is refused by the release gate until the next coordinated peer bump. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
|
|
916
|
+
- **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **caret** `@arnilo/prism@^0.3.1` peer (plan 039 republished set; unchanged packages keep the prior `^0.3.0` window peer — both satisfy the root) (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 030, Decision B — independent packages):** internal ranges stay inside the 0.x `^0.3.0` window, so a package may patch independently while consumers remain on a compatible 0.3.x line. A package outside that window (for example `0.4.0`) is refused by the release gate until the next coordinated peer bump. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
|
|
880
917
|
- **Public access.** All 56 manifests (root + 55 workspace packages: 49 code packages + 6 pure-manifest family/profile packages — the 10 `prism-*` family/profile set is the 6 pure-manifest profiles plus the 4 code packages `prism-caveman`, `prism-impeccable`, `prism-openapi-tools`, `prism-ponytail`) declare `"publishConfig": { "access": "public" }`; the publisher also passes `--access public` explicitly because scoped packages otherwise default to restricted on first publish.
|
|
918
|
+
- **Shipped vs repository docs.** The npm tarball ships `docs/` pages linked from `docs/index.md` (public API, security, migration, providers, install). It excludes `docs/_evidence/` (per-phase evidence freezes, including `release-0.2.7-evidence.md`), `docs/release-*-evidence.md`, and `docs/api-page-template.md`. Those files remain in git for audit. `dist/__tests__` and `*.map` stay excluded.
|
|
881
919
|
- **Map retention knob.** Source maps are emitted locally but stripped from tarballs by `!dist/**/*.map`. Removing that `files` negation ships maps in releases (larger tarballs, better consumer stack traces).
|
|
882
920
|
- **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Node 24; `node20-compat` builds/imports every public root `exports` default target on Node 20 for declared `engines.node >=20` (docs examples need Node >=22.6 native TypeScript stripping); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. `publish` runs on `v0.3.0` for the one lockstep cut and on `@arnilo/*@*` package tags afterward; it needs all five gates, preserves clean tagged/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `npm run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
|
|
883
921
|
- **Adding a package.** New workspace packages are picked up automatically by `npm run build --workspaces`, `npm test --workspaces`, `npm run pack:dry-run`, the packaging guard (`src/__tests__/packaging.test.ts`), and the install-smoke test (`src/__tests__/install-smoke.test.ts`) via the workspace glob; add the package to both tests' config arrays for explicit per-package assertions.
|
|
@@ -1051,7 +1089,7 @@ Every release gate maps to an exact enforcement test or command, so the checklis
|
|
|
1051
1089
|
| Root SDK export surface freeze | `public-export-contract.test.ts` `root export surface is frozen` snapshots every value and type export of `src/index.ts` (107 value + 69 type) so any add/remove is a deliberate test update; `every frozen value export resolves at runtime` rebuilds `dist/index.js` and asserts each value export is present (catches build drift), and `every frozen type export appears in the built type declarations` asserts each type export is in `dist/index.d.ts`. |
|
|
1052
1090
|
| Examples compile and are listed; runnable demos execute | `npm run typecheck` runs `tsc -p examples --noEmit`; `docs.test.ts` checks every `examples/*.ts` file is listed in `examples/README.md`, then runs demos offline and scans output for secrets. |
|
|
1053
1091
|
| Examples run to completion with no secret leakage | `docs.test.ts` `examples_demos_run_to_completion_and_emit_no_secret` runs each demo (Node strips TypeScript types natively) with exit-0 and real-secret scans; `external_app_example_*` pins the DB-backed adapter reference exercising the `RunLedger`, branch-handle checkout, fork, and prior-run resume. |
|
|
1054
|
-
| Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. `prism-all` reaches 47 of the
|
|
1092
|
+
| Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. `prism-all` reaches 47 of the 59 workspace packages (21 direct + 26 transitive); the deliberate Caveman/Ponytail/Impeccable/computer-use-linux/antigravity-agent/Graft/Obscura opt-outs and the other non-closure packages (document-reader, OpenAPI tools, NATS, wiki) are not in its install set. |
|
|
1055
1093
|
| NeuralWatt package/docs/examples release gate | `packaging.test.ts` pins `@arnilo/prism-provider-neuralwatt` package exports/type declarations and `@arnilo/prism-providers`/`@arnilo/prism-all` membership; `docs.test.ts` asserts `docs/index.md` links `providers/neuralwatt.md` and `provider-caching.md`, and that `examples/cache-aware-prompt-assembly.ts` plus `examples/neuralwatt-agent-run.ts` exist and are listed. |
|
|
1056
1094
|
| Enterprise PostgreSQL package/docs/example gate | Packaging/install/public-contract tests include `@arnilo/prism-enterprise-postgres`; `docs.test.ts` pins its API page, four-store migration/ownership/unknown-outcome/async-router guidance, and `examples/enterprise-postgres-state.ts`; `npm run test:postgres` exercises migration, restart, contention, and cleanup with an explicit database URL. |
|
|
1057
1095
|
| Version graph and resumable publication | `release.test.ts` covers exact package/lock/range validation, topological order, registry collisions, dry-run, interrupted reports/resume, clean tagged git state, provenance/public/tag arguments, and token-safe errors. `release:check` and `release:publish` derive the workspace graph without a manual package list. |
|
package/docs/server.md
CHANGED
|
@@ -173,6 +173,7 @@ A2A routes are not added to `createPrismHandler()`. Install `@arnilo/prism-super
|
|
|
173
173
|
- [MCP client and server exposure](mcp-tools.md): selected MCP capabilities and web-standard MCP transport.
|
|
174
174
|
- [Host security guide](host-security.md): remote-boundary checklist.
|
|
175
175
|
- [A2A interoperability](a2a.md): separately mounted A2A 1.0 handler/client.
|
|
176
|
+
- [Obscura browser engine](obscura.md): optional binary-backed generic tools for hosted agents.
|
|
176
177
|
- [Conversations](conversations.md): durable user-scoped conversation service, replay, branches, export, deletion.
|
|
177
178
|
- [Work artifacts and review](work-artifacts-and-review.md): durable artifact review service, revisions, approvals, authorized expiring delivery links.
|
|
178
179
|
- [Frontend interoperability (AG-UI and ACP)](ag-ui.md): separately installed authorized AG-UI Web handler.
|
package/docs/supervisors.md
CHANGED
|
@@ -17,11 +17,11 @@ Use a supervisor when a host or agent must choose a child dynamically. Use `@arn
|
|
|
17
17
|
| `delegate({ childId, input, threadId?, limits?, signal? })` | Invokes one allow-listed child. Input is text and byte-bounded. |
|
|
18
18
|
| `hooks.before` | May reject, modify redacted input, or narrow limits/policy. |
|
|
19
19
|
| `hooks.after` | Observes redacted terminal summary; failures cannot alter settled result. |
|
|
20
|
-
| `limits` | Depth 4/16, active children 4/32, input 64 KiB/1 MiB, steps 8/64, tools 32/256, tokens 20k/1m, timeout 60s/30m, event queue 128/4096 default/hard. |
|
|
20
|
+
| `limits` | Depth 4/16, active children 4/32, input 64 KiB/1 MiB, steps 8/64, tools 32/256, tokens 20k/1m, timeout 60s/30m, event queue 128/4096 default/hard. Over-cap `delegate()` throws `SupervisorLimitError` before incrementing `activeChildren`. Hook rejection and timeout decrement the count exactly once (no leaked timers). |
|
|
21
21
|
|
|
22
22
|
## Outputs / response / events
|
|
23
23
|
|
|
24
|
-
`delegate()` returns the child's `AgentRunResult` or throws its `AgentRunError`/a supervisor denial or limit error. `subscribe()` emits bounded `delegation_started`, `delegation_finished`, `delegation_rejected`, and `delegation_error` metadata events. Hosts may project those events through observability `handleDelegation()` using the parent Prism run ID; no OpenTelemetry dependency enters this package.
|
|
24
|
+
`delegate()` returns the child's `AgentRunResult` or throws its `AgentRunError`/a supervisor denial or limit error. `subscribe()` emits bounded `delegation_started`, `delegation_finished`, `delegation_rejected`, and `delegation_error` metadata events. Graceful close drains already-queued terminal events before the iterator completes (same core multiplexer contract). Hosts may project those events through observability `handleDelegation()` using the parent Prism run ID; no OpenTelemetry dependency enters this package.
|
|
25
25
|
|
|
26
26
|
## Request/response example
|
|
27
27
|
|
|
@@ -77,3 +77,4 @@ Supervisors propagate parent `identity` and `effectStore` to every child agent/r
|
|
|
77
77
|
- [Workflows](workflows.md): preferred deterministic orchestration.
|
|
78
78
|
- [Working and semantic memory](working-and-semantic-memory.md): child scope construction.
|
|
79
79
|
- [Host security](host-security.md): permission and credential boundaries.
|
|
80
|
+
- [Obscura browser engine](obscura.md): optional binary-backed generic tools for child agents.
|
package/docs/system-prompts.md
CHANGED
|
@@ -45,7 +45,7 @@ Known `source` order is deterministic: `user`, `package`, `app`, then `run`. Unk
|
|
|
45
45
|
|
|
46
46
|
## Outputs / response / events
|
|
47
47
|
|
|
48
|
-
`composeSystemPrompt()` returns the composed prompt string or `undefined` when no prompt text remains. The agent/session runtime passes that string to `assembleProviderInput()` as `systemInstructions`; it does not emit a separate event or store prompt layers.
|
|
48
|
+
`composeSystemPrompt()` returns the composed prompt string or `undefined` when no prompt text remains. The agent/session runtime passes that string to `assembleProviderInput()` as `systemInstructions`; it does not emit a separate event or store prompt layers. With the default `cache_aware` layout, this composed system message is emitted before dynamic context, skills, history, tool results, and current input; set `inputLayout: "legacy"` to retain the prior whole-prompt order.
|
|
49
49
|
|
|
50
50
|
## Request/response example
|
|
51
51
|
|
package/docs/tools.md
CHANGED
|
@@ -216,7 +216,7 @@ By default tools without `parameters` skip schema validation (`missingSchema: "a
|
|
|
216
216
|
|
|
217
217
|
### Parallel tool execution (single-shot loop)
|
|
218
218
|
|
|
219
|
-
Opt in through `loop.toolConcurrency` on `AgentConfig` / `RunOptions` (single-shot strategy only). Default is `1` (sequential). Independent calls from one provider turn run concurrently up to the limit; transcript rows and `appendMessage` stay in original call order. Each call still uses `dispatchToolCall` (permission, validation, abort signal). See [Agent loops](agent-loops.md).
|
|
219
|
+
Opt in through `loop.toolConcurrency` on `AgentConfig` / `RunOptions` (single-shot strategy only). Default is `1` (sequential). Independent calls from one provider turn run concurrently up to the limit; transcript rows and `appendMessage` stay in original call order. Each call still uses `dispatchToolCall` (permission, validation, abort signal). If a worker throws or the run aborts, workers stop claiming new calls, already-claimed calls settle, buffered tool-result rows are not appended, and the first failure is rethrown. Already-claimed side effects are not rolled back; the shared abort signal is still passed to each dispatch. The round-level `chargeToolRound` approval gate runs before any worker starts. See [Agent loops](agent-loops.md).
|
|
220
220
|
|
|
221
221
|
```ts
|
|
222
222
|
await session.run(input, {
|
package/docs/web-tools.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
Use when agent needs explicit public-web discovery or host-approved document retrieval/extraction. Keep search separate from fetch/extract so model cannot select provider, credential, API origin, extraction schema, or cost path.
|
|
10
10
|
|
|
11
|
+
**Obscura-backed alternative**: the optional [`@arnilo/prism-obscura`](obscura.md) package provides `web_search`/`web_fetch` behavior backed by a host-installed Obscura headless browser through its CLI (one replaceable HTML search profile instead of an API key), plus explicit native `obscura_fetch`/`obscura_scrape` batch tools. It reuses this package's normalized citation/untrusted shapes (`provider: "obscura"`) but does not require credentials; the API-backed Brave/Exa/Firecrawl adapters here remain the preferred path when an API key is available.
|
|
12
|
+
|
|
11
13
|
## Inputs / request
|
|
12
14
|
|
|
13
15
|
| Tool | Model-visible input | Host-only construction input |
|
package/docs/wiki.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# LLM Wiki (@arnilo/prism-wiki)
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`@arnilo/prism-wiki` implements Andrej Karpathy's **LLM Wiki Pattern** for the Prism agent ecosystem. It acts as a knowledge compiler that transforms raw, immutable sources (source code, AST symbols, notes, markdown clips, transcripts, journal entries) into a persistent, compounding, cross-linked Markdown knowledge base (`.wiki/`).
|
|
6
|
+
|
|
7
|
+
It integrates Tobias Lütke's [`qmd`](https://github.com/tobi/qmd) on-device hybrid search engine (BM25, vector search, and LLM reranking) and hydrates search results with Context7-inspired hierarchical breadcrumbs (`# Category > ## Topic`) and live clickable source line anchors (`file:///path/to/file#Lxx-Lyy` format), enabling agents and humans to navigate code and notes directly without blind regex loops (`grep`/`rg`).
|
|
8
|
+
|
|
9
|
+
## When to use it
|
|
10
|
+
|
|
11
|
+
- **Codebase Knowledge Compilation**: Ingesting modules, architecture patterns, and decision records (ADRs) with exact AST and line anchors that track code drift.
|
|
12
|
+
- **Personal Knowledge Management (PKM)**: Ingesting research papers, meeting notes, book summaries, and journal entries into an interlinked knowledge graph.
|
|
13
|
+
- **Context7-Style Navigation**: Allowing agents to query concepts and immediately jump to exact file and line locations without broad repository scans.
|
|
14
|
+
- **Compounding Q&A**: Persisting valuable answers, analyses, and architectural comparisons back into the wiki for future sessions.
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
The Karpathy LLM Wiki pattern is structured into 3 distinct tiers:
|
|
19
|
+
|
|
20
|
+
1. **Raw Sources (Immutable)**: Source code files, design docs, transcripts, journals, and Markdown notes. Raw sources are strictly read-only and never mutated.
|
|
21
|
+
2. **Compiled Wiki (`.wiki/`)**: Persistent, cross-linked Markdown documents containing synthesized architecture models, entity descriptions, decision records, and line-anchored claims.
|
|
22
|
+
3. **Schema & Protocols (`SCHEMA.md`)**: Operational guidelines governing entity categorization, link formatting (`[[wikilink]]`), citation rules (`file:///path#Lxx-Lyy`), catalog indexing (`index.md`), and chronological change logging (`log.md`).
|
|
23
|
+
|
|
24
|
+
## Inputs / request
|
|
25
|
+
|
|
26
|
+
### `createWikiExtension(options)`
|
|
27
|
+
|
|
28
|
+
| Field | Type | Required | Default | Description |
|
|
29
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
30
|
+
| `wikiRoot` | `string` | No | `".wiki"` | Path to the compiled wiki directory. |
|
|
31
|
+
| `rawRoots` | `readonly string[]` | No | `["."]` | Directories containing raw source files (code, notes, docs). |
|
|
32
|
+
| `profile` | `"codebase" \| "pkm" \| "hybrid" \| "auto"` | No | `"auto"` | Operating strategy for parsing and symbol indexing. |
|
|
33
|
+
| `qmdPath` | `string` | No | `"qmd"` | Path or executable name for the `qmd` CLI binary. |
|
|
34
|
+
| `workspaceRoot` | `string` | No | `process.cwd()` | Workspace root for resolving relative paths and `.agents/skills/`. |
|
|
35
|
+
| `autoDeploySkills` | `boolean` | No | `true` | Auto-deploys `wiki-maintainer` and `wiki-searcher` skills to `.agents/skills/` on init. |
|
|
36
|
+
|
|
37
|
+
### Tools
|
|
38
|
+
|
|
39
|
+
- `wiki_search`: `{ query: string, mode?: "search" | "vsearch" | "query", maxResults?: number }`
|
|
40
|
+
- `wiki_read_page`: `{ pagePath: string }` — `pagePath` must resolve inside the wiki root (lexical + `fs.realpath` containment). Traversal (sibling-prefix, `..`, absolute paths) and symlinks pointing outside the wiki throw an access-denied error; a missing contained page returns `found: false`.
|
|
41
|
+
- `wiki_record_insight`: `{ title: string, content: string, category?: "decision" | "concept" | "entity" }` — title and content must be non-empty; titles are capped at 200 characters, content at 65,536 bytes, and control characters/newlines in titles are collapsed to spaces so titles cannot inject Markdown headings, index entries, or log entries.
|
|
42
|
+
|
|
43
|
+
### Slash Commands
|
|
44
|
+
|
|
45
|
+
- `/wiki-init`: Scaffolds `.wiki/`, instantiates `SCHEMA.md`, `index.md`, and `log.md`, deploys skills, and adds the `qmd` collection.
|
|
46
|
+
- `/wiki-refresh`: Detects modified source files via SHA-256 Merkle diffing, compiles updates to affected entity pages, reconciles contradictions in `log.md`, and runs `qmd update`.
|
|
47
|
+
- `/wiki-lint`: Checks for broken `[[wikilinks]]`, dead line anchors, orphan pages, and unindexed symbols.
|
|
48
|
+
|
|
49
|
+
### Standalone CLI Commands
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Initialize wiki in project
|
|
53
|
+
npx prism-wiki init --profile codebase
|
|
54
|
+
|
|
55
|
+
# Refresh wiki after code edits
|
|
56
|
+
npx prism-wiki refresh
|
|
57
|
+
|
|
58
|
+
# Check wiki health and dead anchors
|
|
59
|
+
npx prism-wiki lint
|
|
60
|
+
|
|
61
|
+
# Search wiki from terminal
|
|
62
|
+
npx prism-wiki search "How does authentication work?" --mode query
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Outputs / response / events
|
|
66
|
+
|
|
67
|
+
- `wiki_search` returns a structured markdown payload containing section breadcrumbs, conceptual summaries, and clickable source line links (`file:///path#Lxx-Lyy`).
|
|
68
|
+
- Lifecycle commands return status objects (`{ status: "initialized" | "refreshed" | "clean", ok: boolean }`).
|
|
69
|
+
|
|
70
|
+
## Request/response example
|
|
71
|
+
|
|
72
|
+
### `wiki_search` Query:
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"query": "How is authentication handled?",
|
|
76
|
+
"mode": "query",
|
|
77
|
+
"maxResults": 2
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Response Content:
|
|
82
|
+
```markdown
|
|
83
|
+
### Match 1: Authentication Architecture > Token Verification
|
|
84
|
+
- **Wiki Page:** [[entities/authentication.md]]
|
|
85
|
+
- **Category:** Core Module
|
|
86
|
+
- **Freshness:** Current (Source hash matches manifest)
|
|
87
|
+
|
|
88
|
+
**Synthesized Summary:**
|
|
89
|
+
The authentication layer uses asymmetric Ed25519 JWT verification in middleware, backed by a persistent token-revocation denylist stored in PostgreSQL.
|
|
90
|
+
|
|
91
|
+
**Code & Source Anchors (Clickable):**
|
|
92
|
+
- Token verification: `verifyToken()` (`file:///src/auth/jwt.ts#L45-L89`)
|
|
93
|
+
- Revocation check: `assertNotRevoked()` (`file:///src/auth/session-store.ts#L112-L138`)
|
|
94
|
+
- Architecture Decision: [[decisions/ADR-004-ed25519-migration.md]]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Implementation example
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { createExtensionKernel } from "@arnilo/prism";
|
|
101
|
+
import { createWikiExtension, initWiki, refreshWiki, lintWiki } from "@arnilo/prism-wiki";
|
|
102
|
+
|
|
103
|
+
const kernel = createExtensionKernel();
|
|
104
|
+
|
|
105
|
+
const wiki = createWikiExtension({
|
|
106
|
+
wikiRoot: ".wiki",
|
|
107
|
+
profile: "codebase",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await kernel.load([wiki]);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Skills and Auto-Deployment
|
|
114
|
+
|
|
115
|
+
`@arnilo/prism-wiki` includes two specialized skills formatted according to `.agents/skills/skill-creator`:
|
|
116
|
+
|
|
117
|
+
1. **`wiki-maintainer`**: Ingestion, compilation, line-anchor validation, and contradiction reconciliation rules.
|
|
118
|
+
2. **`wiki-searcher`**: Context7 hierarchical breadcrumb query resolution, zero-grep instructions, and compounding insight recording.
|
|
119
|
+
|
|
120
|
+
When initialized (`wiki-init` or `createWikiExtension`), these skills are automatically deployed to the host workspace's `.agents/skills/` folder so any compatible agent can leverage them immediately.
|
|
121
|
+
|
|
122
|
+
## Extension and configuration notes
|
|
123
|
+
|
|
124
|
+
- `@arnilo/prism-wiki` registers tools (`wiki_search`, `wiki_read_page`, `wiki_record_insight`), commands (`wiki-init`, `wiki-refresh`, `wiki-lint`), skills (`wiki-maintainer`, `wiki-searcher`), and instruction injectors (`wiki-guidance`) into Prism registries.
|
|
125
|
+
- It operates with zero core modifications and can be used with any `@arnilo/prism` agent.
|
|
126
|
+
- `qmd` is optional but recommended. When `@tobilu/qmd` is not installed, the search engine falls back to catalog matching against `index.md`.
|
|
127
|
+
|
|
128
|
+
## Security and performance notes
|
|
129
|
+
|
|
130
|
+
- **Source Immutability**: Raw source files are read-only and never modified by wiki operations.
|
|
131
|
+
- **Subprocess Safety**: All `qmd` subprocess calls use argument arrays (`execFile`) to prevent shell injection.
|
|
132
|
+
- **Path Containment**: Wiki and raw source paths are confined to the workspace root; directory traversal (`../`) is rejected.
|
|
133
|
+
- **Bounded Token Consumption**: Incremental Merkle hashing ensures only modified files and 1-hop dependent wiki pages are processed during refresh passes.
|
|
134
|
+
|
|
135
|
+
## Related APIs
|
|
136
|
+
|
|
137
|
+
- [`@arnilo/prism-rag`](rag.md): Bounded document chunking and vector context injection.
|
|
138
|
+
- [`@arnilo/prism-memory`](working-and-semantic-memory.md): Embedder and VectorStore primitives.
|
|
139
|
+
- [`@arnilo/prism-coding-agent`](coding-agent-tools.md): Code manipulation and reading tools.
|
|
140
|
+
- [`Contribution registries`](contribution-registries.md): Extension contribution model.
|