@arnilo/prism 0.0.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 +31 -0
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/agents.d.ts +5 -0
- package/dist/agents.js +439 -0
- package/dist/cli-runner.d.ts +33 -0
- package/dist/cli-runner.js +167 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +10 -0
- package/dist/compaction.d.ts +9 -0
- package/dist/compaction.js +67 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +69 -0
- package/dist/contracts.d.ts +670 -0
- package/dist/contracts.js +2 -0
- package/dist/contributions.d.ts +35 -0
- package/dist/contributions.js +47 -0
- package/dist/credentials.d.ts +22 -0
- package/dist/credentials.js +63 -0
- package/dist/extensions.d.ts +25 -0
- package/dist/extensions.js +131 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +28 -0
- package/dist/input.d.ts +57 -0
- package/dist/input.js +225 -0
- package/dist/manifests.d.ts +28 -0
- package/dist/manifests.js +108 -0
- package/dist/middleware.d.ts +15 -0
- package/dist/middleware.js +49 -0
- package/dist/mock-provider.d.ts +6 -0
- package/dist/mock-provider.js +14 -0
- package/dist/models.d.ts +8 -0
- package/dist/models.js +25 -0
- package/dist/node/config.d.ts +9 -0
- package/dist/node/config.js +51 -0
- package/dist/node/session-store-jsonl.d.ts +17 -0
- package/dist/node/session-store-jsonl.js +134 -0
- package/dist/node/settings.d.ts +7 -0
- package/dist/node/settings.js +26 -0
- package/dist/node/trust.d.ts +13 -0
- package/dist/node/trust.js +56 -0
- package/dist/provider-events.d.ts +15 -0
- package/dist/provider-events.js +30 -0
- package/dist/provider-packages.d.ts +4 -0
- package/dist/provider-packages.js +12 -0
- package/dist/provider-request-policy.d.ts +9 -0
- package/dist/provider-request-policy.js +49 -0
- package/dist/providers/openai-compatible.d.ts +9 -0
- package/dist/providers/openai-compatible.js +197 -0
- package/dist/providers.d.ts +8 -0
- package/dist/providers.js +25 -0
- package/dist/redaction.d.ts +11 -0
- package/dist/redaction.js +68 -0
- package/dist/resources.d.ts +5 -0
- package/dist/resources.js +30 -0
- package/dist/retry.d.ts +11 -0
- package/dist/retry.js +48 -0
- package/dist/rpc.d.ts +18 -0
- package/dist/rpc.js +187 -0
- package/dist/security.d.ts +51 -0
- package/dist/security.js +60 -0
- package/dist/session-stores.d.ts +25 -0
- package/dist/session-stores.js +116 -0
- package/dist/settings.d.ts +3 -0
- package/dist/settings.js +26 -0
- package/dist/skills.d.ts +8 -0
- package/dist/skills.js +34 -0
- package/dist/system-prompts.d.ts +6 -0
- package/dist/system-prompts.js +47 -0
- package/dist/testing/provider-conformance.d.ts +36 -0
- package/dist/testing/provider-conformance.js +164 -0
- package/dist/tools.d.ts +25 -0
- package/dist/tools.js +109 -0
- package/docs/agent-session-runtime.md +167 -0
- package/docs/api-page-template.md +32 -0
- package/docs/cli-rpc.md +140 -0
- package/docs/compaction-and-retry.md +177 -0
- package/docs/compaction-llm.md +108 -0
- package/docs/compaction-observational-memory.md +123 -0
- package/docs/configuration-and-manifests.md +142 -0
- package/docs/context-and-skills.md +113 -0
- package/docs/contribution-registries.md +118 -0
- package/docs/credentials-and-redaction.md +122 -0
- package/docs/extensions.md +139 -0
- package/docs/index.md +56 -0
- package/docs/input-and-prompt-assembly.md +168 -0
- package/docs/middleware-hooks.md +123 -0
- package/docs/node-filesystem-config.md +85 -0
- package/docs/node-jsonl-session-store.md +81 -0
- package/docs/provider-conformance.md +109 -0
- package/docs/provider-layer.md +172 -0
- package/docs/provider-packages.md +171 -0
- package/docs/providers/kimi.md +110 -0
- package/docs/providers/openai-compatible.md +125 -0
- package/docs/providers/openai.md +131 -0
- package/docs/providers/opencode-go.md +103 -0
- package/docs/providers/openrouter.md +105 -0
- package/docs/providers/zai.md +108 -0
- package/docs/public-contracts.md +375 -0
- package/docs/release-and-install.md +141 -0
- package/docs/resource-loading.md +97 -0
- package/docs/session-stores-and-branching.md +119 -0
- package/docs/settings-auth-trust-security.md +73 -0
- package/docs/system-prompts.md +116 -0
- package/docs/tools.md +151 -0
- package/package.json +93 -0
package/docs/index.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Prism Docs
|
|
2
|
+
|
|
3
|
+
Prism is a TypeScript/Node.js agent harness. Host apps and extension packages own providers, tools, resources, credentials, storage, UI, and business behavior. Prism supplies contracts, registries, streaming events, and replaceable runtime primitives.
|
|
4
|
+
|
|
5
|
+
## Public contracts
|
|
6
|
+
- [Public contracts](public-contracts.md): type shapes for messages, content, agents, sessions, providers, tools, context, skills, extensions, stores, resources, settings, credentials, and events.
|
|
7
|
+
|
|
8
|
+
## Agent/session runtime
|
|
9
|
+
- [Agent/session runtime](agent-session-runtime.md): create agents and sessions, run prompts, and subscribe to normalized session events.
|
|
10
|
+
|
|
11
|
+
## Compaction/session memory
|
|
12
|
+
- [Compaction and retry policies](compaction-and-retry.md): summarize branch history and retry transient provider failures with host-replaceable policies.
|
|
13
|
+
- [LLM compaction package](compaction-llm.md): optional provider-backed compaction strategy package.
|
|
14
|
+
- [Observational memory compaction package](compaction-observational-memory.md): optional source-backed memory, fast compaction, recall tool, and status/view command package.
|
|
15
|
+
- [Session stores and branching](session-stores-and-branching.md): store session entries, rebuild branch context, and navigate branch leaves.
|
|
16
|
+
- [Node JSONL session store](node-jsonl-session-store.md): persist session entries to caller-named JSONL files in Node hosts.
|
|
17
|
+
|
|
18
|
+
## Provider and model connection
|
|
19
|
+
- [Provider layer](provider-layer.md): register and resolve host-owned providers/models, create provider events, use generic provider request options, and test with the mock provider.
|
|
20
|
+
- [Provider packages](provider-packages.md): define explicit provider packages, model metadata, auth descriptors, and request/cache policies without package discovery or provider-specific core behavior.
|
|
21
|
+
- Phase 12 package workspaces: [`@arnilo/prism-provider-openai`](providers/openai.md), [`@arnilo/prism-provider-opencode-go`](providers/opencode-go.md), [`@arnilo/prism-provider-openrouter`](providers/openrouter.md), [`@arnilo/prism-provider-zai`](providers/zai.md), and [`@arnilo/prism-provider-kimi`](providers/kimi.md).
|
|
22
|
+
- [OpenAI-compatible provider](providers/openai-compatible.md): optional provider subpath using native or injected `fetch` for Chat Completions streaming.
|
|
23
|
+
|
|
24
|
+
## Input, prompt, and context assembly
|
|
25
|
+
- [Input and prompt assembly](input-and-prompt-assembly.md): render tiny prompt templates and turn common host input, history, attachments, explicit resources, summaries, and tool results into messages with replaceable builders and provider-input assembly.
|
|
26
|
+
- [System prompts](system-prompts.md): compose explicit package/app/user/run system prompt layers without filesystem discovery or hidden globals.
|
|
27
|
+
- [Context and skills](context-and-skills.md): resolve ordered context providers and keep context/skill selection host-owned.
|
|
28
|
+
|
|
29
|
+
## Tools
|
|
30
|
+
- [Tools](tools.md): register host-owned active tools, apply exact allow/deny filtering, and dispatch tool calls.
|
|
31
|
+
|
|
32
|
+
## Extensions/plugins
|
|
33
|
+
- [Contribution registries](contribution-registries.md): explicit host-owned registries for extension/package contributions without hidden globals.
|
|
34
|
+
- [Extension kernel and event bus](extensions.md): load host-provided extensions in order, register contributions, emit lifecycle events, and isolate extension errors.
|
|
35
|
+
- [Middleware hooks](middleware-hooks.md): ordered hook registry for provider, input, context, tool, retry, compaction, and session lifecycle boundaries.
|
|
36
|
+
|
|
37
|
+
## Configuration/manifests
|
|
38
|
+
- [Configuration and manifests](configuration-and-manifests.md): merge in-memory JSON config layers and validate data-only package manifests.
|
|
39
|
+
- [Node filesystem config loader](node-filesystem-config.md): explicitly read caller-named JSON config files in Node hosts.
|
|
40
|
+
- [Resource loading](resource-loading.md): decode text, JSON, and manifest resources through caller-provided loaders.
|
|
41
|
+
|
|
42
|
+
## CLI/RPC
|
|
43
|
+
- [CLI/RPC](cli-rpc.md): Run print/json modes and LF-delimited RPC over the public AgentSession runtime.
|
|
44
|
+
|
|
45
|
+
## Security and credentials
|
|
46
|
+
- [Security/auth/trust](settings-auth-trust-security.md): settings providers, credential helpers, trust/permission policies, and redaction controls.
|
|
47
|
+
- [Credentials and redaction](credentials-and-redaction.md): compose explicit credential resolver order, use caller-supplied env objects/OAuth refresh helpers, and redact known secret values.
|
|
48
|
+
|
|
49
|
+
## Testing and examples
|
|
50
|
+
- [Provider layer](provider-layer.md): use `createMockProvider()` and provider event helpers for deterministic tests without timers, credentials, or network.
|
|
51
|
+
- [Provider conformance](provider-conformance.md): run network-free provider adapter assertions from `@arnilo/prism/testing/provider-conformance`.
|
|
52
|
+
- `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, stores/branching, compaction, observational-memory recall, CLI, RPC).
|
|
53
|
+
|
|
54
|
+
## Release and install
|
|
55
|
+
- [Release and install](release-and-install.md): package layout, install specifiers, required `@arnilo/prism` peer, tarball contents and exclusions, the map-retention knob, the release workflow, and the offline test budget.
|
|
56
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Input and prompt assembly
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`createDefaultInputBuilder()` turns common host input into Prism `Message[]` without starting an agent loop or calling a provider. It accepts strings, `Message`, or `Message[]`, and can add host-supplied instructions, history, summaries, attachments, explicit text resources, tool results, metadata, and optional `input_assembly` middleware.
|
|
6
|
+
|
|
7
|
+
`createDefaultPromptBuilder()` composes messages, context blocks, selected skills, and host-supplied active tools into provider-ready messages. `assembleProviderInput()` wires input assembly, ordered context resolution, prompt middleware, and prompt composition into a `ProviderRequest` without calling a provider. Layered system prompts are composed before this helper and passed as `systemInstructions`. `renderPromptTemplate()` expands tiny `{{name}}` variables for CLI/RPC prompt strings before input assembly.
|
|
8
|
+
|
|
9
|
+
## When to use it
|
|
10
|
+
|
|
11
|
+
Use it when a host wants the boring default shape before a later prompt builder or provider request step. Use `renderPromptTemplate()` when CLI/RPC callers need simple variable replacement before sending a string to the input builder. Use a custom `InputBuilder` or `PromptBuilder` when an app has its own message or prompt policy.
|
|
12
|
+
|
|
13
|
+
Do not use it for tool execution, provider calls, file discovery, credential lookup, package activation, template logic, or an agent/session runtime.
|
|
14
|
+
|
|
15
|
+
## Inputs / request
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createDefaultInputBuilder } from "@arnilo/prism";
|
|
19
|
+
|
|
20
|
+
const messages = await createDefaultInputBuilder().build("Summarize", {
|
|
21
|
+
systemInstructions: "Be accurate.",
|
|
22
|
+
developerInstructions: "Cite supplied context only.",
|
|
23
|
+
history,
|
|
24
|
+
attachments: [{ name: "notes.md", text: "# Notes" }],
|
|
25
|
+
toolResults: [{ toolCallId: "call_1", name: "lookup", value: { ok: true } }],
|
|
26
|
+
metadata: { requestId: "r1" },
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Prompt templates:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { renderPromptTemplate } from "@arnilo/prism";
|
|
34
|
+
|
|
35
|
+
const prompt = renderPromptTemplate("Review {{file}} for {{focus}}", {
|
|
36
|
+
file: "src/index.ts",
|
|
37
|
+
focus: "public exports",
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Prompt/provider assembly:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { assembleProviderInput, createDefaultPromptBuilder } from "@arnilo/prism";
|
|
45
|
+
|
|
46
|
+
const request = await assembleProviderInput({
|
|
47
|
+
model: { provider: "mock", model: "demo" },
|
|
48
|
+
input: "Explain this file",
|
|
49
|
+
contextProviders: [projectContext],
|
|
50
|
+
promptBuilder: createDefaultPromptBuilder(),
|
|
51
|
+
tools: activeTools,
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Useful exported types:
|
|
56
|
+
|
|
57
|
+
- `AgentInput`: `string | Message | readonly Message[]`.
|
|
58
|
+
- `DefaultInputBuilder`: the default `InputBuilder` with typed default context.
|
|
59
|
+
- `DefaultInputBuildContext`: optional instructions, history, summaries, attachments, resource loader/URIs, tool results, middleware, ids, metadata, and abort signal.
|
|
60
|
+
- `InputAttachment`: already-loaded text/content or an explicit URI loaded through a caller-provided `ResourceLoader`.
|
|
61
|
+
- `PromptInstruction`: labeled system instruction text.
|
|
62
|
+
- `DefaultPromptBuilder`: the default `PromptBuilder`.
|
|
63
|
+
- `AssembleProviderInputOptions`: model, input, optional builders, context providers, selected skills, active tools, generic provider options, metadata, and signal.
|
|
64
|
+
- `PromptTemplateOptions`: missing-variable behavior for `renderPromptTemplate()`.
|
|
65
|
+
|
|
66
|
+
## Outputs / response / events
|
|
67
|
+
|
|
68
|
+
The builder returns `readonly Message[]`.
|
|
69
|
+
|
|
70
|
+
- String input becomes one user text message.
|
|
71
|
+
- `Message` and `Message[]` input are preserved.
|
|
72
|
+
- History is prepended before current input.
|
|
73
|
+
- Instructions and summaries are system messages; compacted branch summaries from `rebuildSessionContext()` use the same path.
|
|
74
|
+
- Text attachments and explicit text resources are user messages.
|
|
75
|
+
- Tool results are tool messages containing `tool_result` content; the agent/session runtime uses this to feed dispatched tool results into the next provider turn, placing the assistant `tool_call` and the matching role `tool` `tool_result` before any final assistant content.
|
|
76
|
+
- Middleware runs only when `middleware` is supplied in the context.
|
|
77
|
+
- `assembleProviderInput()` returns a `ProviderRequest` with the caller's model/tools/provider options/metadata/signal and composed messages/context.
|
|
78
|
+
- `renderPromptTemplate()` replaces top-level `{{name}}` variables with caller-supplied JSON-compatible values. Strings are inserted directly; numbers, booleans, `null`, arrays, and objects are stringified deterministically with sorted object keys. Missing variables throw by default or stay unchanged with `{ missing: "preserve" }`.
|
|
79
|
+
|
|
80
|
+
## Request/response example
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"template": "Review {{file}} for {{focus}}",
|
|
85
|
+
"variables": { "file": "src/index.ts", "focus": "public exports" },
|
|
86
|
+
"rendered": "Review src/index.ts for public exports"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"input": "Hello",
|
|
93
|
+
"context": {
|
|
94
|
+
"systemInstructions": "Answer briefly.",
|
|
95
|
+
"attachments": [{ "name": "notes.md", "text": "Remember the release date." }]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
[
|
|
102
|
+
{ "role": "system", "content": [{ "type": "text", "text": "System instruction:\nAnswer briefly." }] },
|
|
103
|
+
{ "role": "user", "content": [{ "type": "text", "text": "Hello" }] },
|
|
104
|
+
{ "role": "user", "content": [{ "type": "text", "text": "Attachment notes.md:\nRemember the release date." }] }
|
|
105
|
+
]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Implementation example
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { createDefaultInputBuilder, createMiddlewareRegistry, renderPromptTemplate } from "@arnilo/prism";
|
|
112
|
+
|
|
113
|
+
const middleware = createMiddlewareRegistry();
|
|
114
|
+
middleware.use("input_assembly", (messages) => messages);
|
|
115
|
+
|
|
116
|
+
const prompt = renderPromptTemplate("Review {{resource}}", { resource: "package://demo/prompt.md" });
|
|
117
|
+
const messages = await createDefaultInputBuilder().build(prompt, {
|
|
118
|
+
resourceUris: ["package://demo/prompt.md"],
|
|
119
|
+
resourceLoader: {
|
|
120
|
+
async load(uri) {
|
|
121
|
+
return { uri, text: "Host-loaded resource text." };
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
middleware,
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Extension and configuration notes
|
|
129
|
+
|
|
130
|
+
Extensions can contribute `InputBuilder`, `PromptBuilder`, and `ContextProvider` objects through the extension API, but contributions stay inert until the host resolves and calls or passes them. The agent/session runtime uses configured builders/providers only when the host puts them on `AgentConfig`; it does not load extensions or registries itself. Defaults are built-ins; hosts can replace them with compatible builders. Prompt templates are caller-side string expansion only; they do not load resources or contributions.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const kernel = createExtensionKernel();
|
|
134
|
+
await kernel.load([extension]);
|
|
135
|
+
|
|
136
|
+
const request = await assembleProviderInput({
|
|
137
|
+
model: { provider: "mock", model: "demo" },
|
|
138
|
+
input: "Hello",
|
|
139
|
+
inputBuilder: kernel.registries.inputBuilders.resolve("custom-input"),
|
|
140
|
+
promptBuilder: kernel.registries.promptBuilders.resolve("custom-prompt"),
|
|
141
|
+
contextProviders: [kernel.registries.contextProviders.resolve("project")],
|
|
142
|
+
middleware: kernel.middleware,
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`input_assembly`, `context`, and `prompt_build` middleware are not global. They run only for helper calls that receive a `MiddlewareRegistry`, in that assembly order. `assembleProviderInput()` keeps provider `tools` equal to the host-supplied active tool list after prompt middleware.
|
|
147
|
+
|
|
148
|
+
## Security and performance notes
|
|
149
|
+
|
|
150
|
+
- The builder is linear in supplied messages, attachments, resources, and tool results.
|
|
151
|
+
- Template expansion is dependency-free string replacement over `{{name}}` variables. It does not evaluate expressions, filters, loops, partials, JavaScript, globals, or prototype properties.
|
|
152
|
+
- It performs no provider calls, tool execution, credential resolution, package discovery, filesystem scan, network access, timers, or watchers.
|
|
153
|
+
- URI attachments/resources load only through the caller-provided `ResourceLoader`.
|
|
154
|
+
- Do not place secrets in templates, variables, instructions, messages, attachments, tool results, metadata, middleware payloads, or docs examples.
|
|
155
|
+
- Active tools are passed through from the host; prompt middleware cannot grant additional provider tools.
|
|
156
|
+
- Skill selection is handled by the host/skill registry path; this builder only includes selected skills passed by the caller.
|
|
157
|
+
|
|
158
|
+
## Related APIs
|
|
159
|
+
|
|
160
|
+
- [Public contracts](public-contracts.md): `Message`, `ContentBlock`, `InputBuilder`, `InputBuildContext`, `ToolResult`, and `ResourceLoader` shapes.
|
|
161
|
+
- [Context and skills](context-and-skills.md): ordered context resolution feeding prompt composition.
|
|
162
|
+
- [Resource loading](resource-loading.md): `loadTextResource()` behavior used for explicit URI resources.
|
|
163
|
+
- [Middleware hooks](middleware-hooks.md): ordered middleware registry and `input_assembly`, `context`, and `prompt_build` hooks.
|
|
164
|
+
- [System prompts](system-prompts.md): compose layered package/app/user/run prompts before input assembly.
|
|
165
|
+
- [Contribution registries](contribution-registries.md): inert input, prompt, context, and skill contributions.
|
|
166
|
+
- [Agent/session runtime](agent-session-runtime.md): calls assembly each turn and supplies runtime tool results to the next provider request.
|
|
167
|
+
- [Tools](tools.md): host-owned tool registry and tool result boundary.
|
|
168
|
+
- [Compaction and retry policies](compaction-and-retry.md): default compaction strategy that feeds summaries into input assembly.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Middleware hooks
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
Middleware hooks are ordered, host-owned functions that transform a payload only when a host/runtime explicitly calls `run()`. They are a primitive for provider, input, tool, compaction, retry, and session runtime phases.
|
|
6
|
+
|
|
7
|
+
APIs:
|
|
8
|
+
|
|
9
|
+
- `createMiddlewareRegistry()` / `MiddlewareRegistry`
|
|
10
|
+
- `MiddlewareHookName`, `Middleware<T>`, and `MiddlewareNext<T>`
|
|
11
|
+
- `ExtensionAPI.use()` for extension registration
|
|
12
|
+
|
|
13
|
+
## When to use it
|
|
14
|
+
|
|
15
|
+
Use middleware hooks when a host wants extension/package code to observe or transform a value at a named runtime boundary.
|
|
16
|
+
|
|
17
|
+
Do not use middleware hooks as a provider adapter, prompt builder, retry policy, compaction strategy, tool dispatcher, permission system, or agent/session runtime.
|
|
18
|
+
|
|
19
|
+
## Inputs / request
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
createMiddlewareRegistry(options?: MiddlewareRegistryOptions): MiddlewareRegistry
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Built-in hook names:
|
|
26
|
+
|
|
27
|
+
- `provider_request`
|
|
28
|
+
- `input_assembly`
|
|
29
|
+
- `prompt_build`
|
|
30
|
+
- `context`
|
|
31
|
+
- `tool_call`
|
|
32
|
+
- `tool_result`
|
|
33
|
+
- `retry`
|
|
34
|
+
- `compaction`
|
|
35
|
+
- `session_start`
|
|
36
|
+
- `session_shutdown`
|
|
37
|
+
|
|
38
|
+
`MiddlewareRegistry` methods:
|
|
39
|
+
|
|
40
|
+
| Method | Input | Result |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `use(hook, middleware)` | hook name and middleware | Registers middleware in order and returns an unsubscribe function. |
|
|
43
|
+
| `run(hook, value)` | hook name and payload | Runs registered middleware and returns the final payload. |
|
|
44
|
+
| `list(hook)` | hook name | Returns registered middleware for inspection. |
|
|
45
|
+
|
|
46
|
+
`Middleware<T>` receives `(value, next)` and returns a value or promise. Calling `next(updatedValue)` passes an updated value to later middleware.
|
|
47
|
+
|
|
48
|
+
## Outputs / response / events
|
|
49
|
+
|
|
50
|
+
`run()` returns the transformed value. If no middleware is registered for a hook, `run()` returns the original value. `assembleProviderInput()` calls Phase 5 hooks in this order when middleware is supplied: `input_assembly`, then `context`, then `prompt_build`. The agent/session runtime applies configured provider request policies, then invokes `provider_request` once with the `ProviderRequest` before `AIProvider.generate()`, invokes `tool_call` and `tool_result` through `dispatchToolCall()` for complete provider tool calls, invokes `compaction` with `{ context, result }` after a compaction strategy returns and before the runtime appends its standard compaction entry, and invokes `retry` with `{ context, decision }` before scheduling a provider-turn retry. There is no `provider_response` hook; observing provider output belongs to the provider adapter or subscriber events.
|
|
51
|
+
|
|
52
|
+
With default `errorPolicy: "event"`, middleware errors become `extension_error` events when `onError` is provided, and later middleware still runs with the current value. With `errorPolicy: "throw"`, `run()` rejects on the first middleware error.
|
|
53
|
+
|
|
54
|
+
## Request/response example
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"hook": "provider_request",
|
|
59
|
+
"before": { "metadata": {} },
|
|
60
|
+
"after": { "metadata": { "source": "demo" } }
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Implementation example
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { createMiddlewareRegistry } from "@arnilo/prism";
|
|
68
|
+
|
|
69
|
+
const middleware = createMiddlewareRegistry();
|
|
70
|
+
|
|
71
|
+
middleware.use("provider_request", async (request, next) => {
|
|
72
|
+
return next({
|
|
73
|
+
...request,
|
|
74
|
+
metadata: { ...request.metadata, source: "demo" },
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const request = await middleware.run("provider_request", { metadata: {} });
|
|
79
|
+
console.log(request.metadata.source);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Extensions can register middleware through the runtime API:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import type { Extension } from "@arnilo/prism";
|
|
86
|
+
|
|
87
|
+
export const extension: Extension = {
|
|
88
|
+
name: "demo-middleware",
|
|
89
|
+
setup(api) {
|
|
90
|
+
api.use("session_start", (event) => event);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Extension and configuration notes
|
|
96
|
+
|
|
97
|
+
- Middleware registration is explicit through `createMiddlewareRegistry()` or `ExtensionAPI.use()`.
|
|
98
|
+
- `provider_request` middleware sees generic `ProviderRequest.options` after request policies have run; do not add secrets unless a redactor/policy secret list covers that boundary.
|
|
99
|
+
- Middleware runs only when the host/runtime calls `run()` or passes the registry to a helper that documents a call site.
|
|
100
|
+
- `compaction` middleware may adjust the compaction result summary/data, but runtime still owns session store append ordering and branch parent ids.
|
|
101
|
+
- `retry` middleware may stop retrying or adjust delay, but runtime still owns retry event emission, abort-aware waiting, and provider-turn boundaries.
|
|
102
|
+
- The registry does not discover packages, read manifests, load config, call providers, execute tools, read resources, or start sessions.
|
|
103
|
+
- Hosts may pass a middleware registry into `createExtensionKernel({ middleware })` to share it with direct host code.
|
|
104
|
+
|
|
105
|
+
## Security and performance notes
|
|
106
|
+
|
|
107
|
+
- Middleware is in-memory, ordered, dependency-free, and synchronous-or-async.
|
|
108
|
+
- Default error handling can emit redacted `extension_error` events through the extension kernel.
|
|
109
|
+
- Do not put resolved credential values, tokens, headers, secret settings, or permission grants into middleware payloads unless the host boundary explicitly allows it.
|
|
110
|
+
- Tool dispatch re-checks registry lookup, active allow/deny filters, and object arguments after `tool_call` middleware, so middleware cannot bypass host tool permissions by changing a tool name. `assembleProviderInput()` also keeps provider `tools` equal to the host-supplied active tool list after `prompt_build` middleware.
|
|
111
|
+
|
|
112
|
+
## Related APIs
|
|
113
|
+
|
|
114
|
+
- [Extension kernel and event bus](extensions.md): `ExtensionAPI.use()` and shared error policy.
|
|
115
|
+
- [Contribution registries](contribution-registries.md): direct contribution registration separate from middleware.
|
|
116
|
+
- [Agent/session runtime](agent-session-runtime.md): provider request policy/middleware timing, bounded tool loop call site for `tool_call`/`tool_result` hooks, and runtime call sites for `compaction` and `retry`.
|
|
117
|
+
- [Tools](tools.md): tool dispatch behavior that runs `tool_call` and `tool_result` hooks.
|
|
118
|
+
- [Input and prompt assembly](input-and-prompt-assembly.md): `input_assembly` and `prompt_build` helper call sites.
|
|
119
|
+
- [Compaction and retry policies](compaction-and-retry.md): compaction/retry middleware payloads and runtime timing.
|
|
120
|
+
- [Context and skills](context-and-skills.md): `context` helper call site.
|
|
121
|
+
- [Public contracts](public-contracts.md): provider, tool, context, session, and extension contracts that runtimes can pass through hooks.
|
|
122
|
+
|
|
123
|
+
Permission checks for tools, extensions, and resources are hard guards; middleware can transform payloads but cannot bypass a denied `PermissionPolicy`.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Node filesystem config loader
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
The optional `@arnilo/prism/node/config` subpath reads JSON config files that a Node host explicitly names. It also computes the conventional user config path, such as `~/.config/prism/config.json`.
|
|
6
|
+
|
|
7
|
+
APIs:
|
|
8
|
+
|
|
9
|
+
- `defaultUserConfigPath()`
|
|
10
|
+
- `readConfigFile()`
|
|
11
|
+
- `loadConfigFiles()`
|
|
12
|
+
- `NodeConfigFile`
|
|
13
|
+
|
|
14
|
+
## When to use it
|
|
15
|
+
|
|
16
|
+
Use this subpath in Node CLI/host code that wants filesystem config layers for `mergeConfigLayers()`.
|
|
17
|
+
|
|
18
|
+
Do not use it from core root imports, browsers, package manifests, agent/session runtime startup, extension setup by default, or any path where filesystem access must stay unavailable.
|
|
19
|
+
|
|
20
|
+
## Inputs / request
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { defaultUserConfigPath, loadConfigFiles, readConfigFile } from "@arnilo/prism/node/config";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`NodeConfigFile`:
|
|
27
|
+
|
|
28
|
+
| Field | Type | Purpose |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `name` | `string` | Config layer name. |
|
|
31
|
+
| `path` | `string` | Explicit JSON file path to read. |
|
|
32
|
+
| `optional` | `boolean` | Skip missing files when true. |
|
|
33
|
+
|
|
34
|
+
## Outputs / response / events
|
|
35
|
+
|
|
36
|
+
- `defaultUserConfigPath(appName = "prism")` returns a path ending in `.config/<appName>/config.json` under the current user's home directory.
|
|
37
|
+
- `readConfigFile(path)` returns a JSON object or rejects for read errors, invalid JSON, or non-object JSON.
|
|
38
|
+
- `loadConfigFiles(files)` returns `ConfigLayer[]` in caller-provided order.
|
|
39
|
+
- No events are emitted and no config is merged automatically.
|
|
40
|
+
|
|
41
|
+
## Request/response example
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"files": [
|
|
46
|
+
{ "name": "user", "path": "/home/demo/.config/prism/config.json", "optional": true }
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Implementation example
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { mergeConfigLayers } from "@arnilo/prism";
|
|
55
|
+
import { defaultUserConfigPath, loadConfigFiles } from "@arnilo/prism/node/config";
|
|
56
|
+
|
|
57
|
+
const layers = await loadConfigFiles([
|
|
58
|
+
{ name: "user", path: defaultUserConfigPath(), optional: true },
|
|
59
|
+
{ name: "runtime", path: "./prism.config.json" },
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const config = mergeConfigLayers(layers);
|
|
63
|
+
console.log(config);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Extension and configuration notes
|
|
67
|
+
|
|
68
|
+
- This loader is an explicit Node subpath. Importing `@arnilo/prism` does not read files or compute config layers.
|
|
69
|
+
- Hosts choose which paths to read and which missing files are optional.
|
|
70
|
+
- The loader returns `ConfigLayer[]`; use `mergeConfigLayers()` from the root package to combine layers.
|
|
71
|
+
- It does not discover packages, scan directories, watch files, import extension modules, load manifests, or start agent/session runtime behavior.
|
|
72
|
+
|
|
73
|
+
## Security and performance notes
|
|
74
|
+
|
|
75
|
+
- Only caller-provided files are read.
|
|
76
|
+
- Invalid JSON and non-object JSON fail closed.
|
|
77
|
+
- Errors include the path and reason, not file contents.
|
|
78
|
+
- Config files must not contain resolved credential values, tokens, headers, or executable code.
|
|
79
|
+
- The loader uses Node built-ins and has no polling, watchers, network calls, or dependencies.
|
|
80
|
+
|
|
81
|
+
## Related APIs
|
|
82
|
+
|
|
83
|
+
- [Configuration and manifests](configuration-and-manifests.md): in-memory config layers and merge behavior.
|
|
84
|
+
- [Credentials and redaction](credentials-and-redaction.md): keep credentials out of config files.
|
|
85
|
+
- [Extension kernel and event bus](extensions.md): extension loading remains separate from filesystem config loading.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Node JSONL session store
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
The optional `@arnilo/prism/node/session-store-jsonl` subpath stores `SessionEntry` records in a caller-named JSONL file: one JSON object per line.
|
|
6
|
+
|
|
7
|
+
APIs:
|
|
8
|
+
|
|
9
|
+
- `createJsonlSessionStore(pathOrOptions)`
|
|
10
|
+
- `JsonlSessionStoreOptions`
|
|
11
|
+
|
|
12
|
+
## When to use it
|
|
13
|
+
|
|
14
|
+
Use it in Node hosts that want a small durable `SessionStore` without adding a database.
|
|
15
|
+
|
|
16
|
+
Do not use it for browser code, automatic discovery, shared multi-process locking, migrations, compaction, credentials, or app-specific tools.
|
|
17
|
+
|
|
18
|
+
## Inputs / request
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createJsonlSessionStore } from "@arnilo/prism/node/session-store-jsonl";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`pathOrOptions` can be a string path or:
|
|
25
|
+
|
|
26
|
+
| Field | Type | Purpose |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `path` | `string` | Explicit JSONL file path to read/write. |
|
|
29
|
+
| `createDirectory` | `boolean` | Create parent directories before append. Defaults to `true`. |
|
|
30
|
+
|
|
31
|
+
## Outputs / response / events
|
|
32
|
+
|
|
33
|
+
`createJsonlSessionStore()` returns a `SessionStore`:
|
|
34
|
+
|
|
35
|
+
- `append(entry)` appends one JSON line and rejects duplicate entry ids.
|
|
36
|
+
- `list(sessionId)` reads the file and returns valid entries for that session id. Corrupt or shape-invalid lines are skipped; they do not poison the whole file.
|
|
37
|
+
- `get(id)` reads the file and returns the matching valid entry, if any.
|
|
38
|
+
- `readJsonlSessionEntries(path)` returns `{ entries: SessionEntry[]; errors: SessionEntryParseError[] }` so hosts/tests can inspect per-line parse errors.
|
|
39
|
+
|
|
40
|
+
Missing files read as empty stores. Invalid JSON, missing required fields, or wrong per-kind shapes (`message`, `summary`, `model_change`, `custom`, `compaction`, `label`, or non-string `parentId`) are quarantined per line with line number and reason; the raw line is included in `SessionEntryParseError.raw`.
|
|
41
|
+
|
|
42
|
+
## Request/response example
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"path": "./sessions.jsonl",
|
|
47
|
+
"createDirectory": true
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Implementation example
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { createJsonlSessionStore, readJsonlSessionEntries } from "@arnilo/prism/node/session-store-jsonl";
|
|
55
|
+
|
|
56
|
+
const store = createJsonlSessionStore("./sessions.jsonl");
|
|
57
|
+
const { entries, errors } = await readJsonlSessionEntries("./sessions.jsonl");
|
|
58
|
+
if (errors.length) console.warn("quarantined lines", errors);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Use `createMemorySessionStore()` for tests or throwaway sessions; use the JSONL store when entries should survive a process restart.
|
|
62
|
+
|
|
63
|
+
## Extension and configuration notes
|
|
64
|
+
|
|
65
|
+
- This adapter is an explicit Node subpath. Importing `@arnilo/prism` does not touch the filesystem.
|
|
66
|
+
- Hosts choose the file path. Prism does not discover, watch, rotate, compact, or migrate files.
|
|
67
|
+
- The adapter stores only `SessionEntry` data passed to `append()`.
|
|
68
|
+
|
|
69
|
+
## Security and performance notes
|
|
70
|
+
|
|
71
|
+
- Reads and writes use only the caller-provided path.
|
|
72
|
+
- Errors include path/reason or line number, not file contents.
|
|
73
|
+
- Do not put secrets in messages, metadata, summaries, labels, or custom entries.
|
|
74
|
+
- Reads are linear in file size. Appends are serialized per store instance.
|
|
75
|
+
- There is no cross-process lock; add a database or external lock if multiple processes write the same file.
|
|
76
|
+
|
|
77
|
+
## Related APIs
|
|
78
|
+
|
|
79
|
+
- [Session stores and branching](session-stores-and-branching.md): `SessionStore`, entries, branch helpers, and runtime branch semantics.
|
|
80
|
+
- [Agent/session runtime](agent-session-runtime.md): sessions that append user, assistant, tool-result, and model-change entries.
|
|
81
|
+
- [Node filesystem config loader](node-filesystem-config.md): similar explicit Node subpath pattern.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Provider conformance
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
Provider conformance helpers are dependency-free assertions for provider package tests. They exercise normalized Prism `AIProvider` streams without live network or credentials.
|
|
6
|
+
|
|
7
|
+
Exported from `@arnilo/prism/testing/provider-conformance`:
|
|
8
|
+
|
|
9
|
+
- `collectProviderEvents(provider, request)`
|
|
10
|
+
- `assertProviderStreamConforms(options)`
|
|
11
|
+
- `assertAbortIsObserved(options)`
|
|
12
|
+
- `assertToolCallDeltasReconstruct(events, expected)`
|
|
13
|
+
- `assertUsageAccounting(events, expected)`
|
|
14
|
+
- `assertSerializedRequestCoversContent(request, body, options?)`
|
|
15
|
+
- `assertNoSecretLeak(events, secrets)`
|
|
16
|
+
|
|
17
|
+
## When to use it
|
|
18
|
+
|
|
19
|
+
Use these helpers in provider package tests to check event order, terminal events, abort propagation, streamed tool-call deltas, usage/cache accounting, request body content preservation, and secret redaction.
|
|
20
|
+
|
|
21
|
+
Do not use them as a live integration runner, provider simulator, retry framework, credential loader, or test framework replacement.
|
|
22
|
+
|
|
23
|
+
## Inputs / request
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { assertProviderStreamConforms } from "@arnilo/prism/testing/provider-conformance";
|
|
27
|
+
|
|
28
|
+
await assertProviderStreamConforms({
|
|
29
|
+
provider,
|
|
30
|
+
request: {
|
|
31
|
+
model: { provider: "demo", model: "demo-model" },
|
|
32
|
+
messages: [{ role: "user", content: [{ type: "text", text: "Hi" }] }],
|
|
33
|
+
},
|
|
34
|
+
expect: { text: "Hello", usage: { cacheReadTokens: 10 } },
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Helpers accept normal `AIProvider`, `ProviderRequest`, `ProviderEvent`, `Usage`, and request body objects. They throw `Error` on failed assertions so any runner can use them.
|
|
39
|
+
|
|
40
|
+
## Outputs / response / events
|
|
41
|
+
|
|
42
|
+
- `collectProviderEvents()` returns provider events in stream order.
|
|
43
|
+
- `assertProviderStreamConforms()` returns collected events after verifying the stream ends with `done` or `error`, terminal events are last, and optional text/usage expectations match.
|
|
44
|
+
- `assertAbortIsObserved()` passes an already-aborted signal and expects provider generation to reject.
|
|
45
|
+
- `assertToolCallDeltasReconstruct()` rebuilds streamed `tool_call_delta` fragments into tool calls and validates expected id/name/arguments.
|
|
46
|
+
- `assertUsageAccounting()` finds `usage` or `done.usage` and checks selected token fields including `cacheReadTokens` and `cacheWriteTokens`.
|
|
47
|
+
- `assertSerializedRequestCoversContent()` scans a serialized provider request body for primitive canaries from each Prism content block and fails if any supported block type is silently dropped.
|
|
48
|
+
- `assertNoSecretLeak()` stringifies all collected events and fails if any known secret string is present.
|
|
49
|
+
|
|
50
|
+
## Request/response example
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"events": ["content_delta", "usage", "done"],
|
|
55
|
+
"usage": { "inputTokens": 10, "cacheReadTokens": 4, "cacheWriteTokens": 2 }
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Content-preservation example:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { assertSerializedRequestCoversContent } from "@arnilo/prism/testing/provider-conformance";
|
|
63
|
+
|
|
64
|
+
const request = {
|
|
65
|
+
model: { provider: "demo", model: "demo-model" },
|
|
66
|
+
messages: [{
|
|
67
|
+
role: "user",
|
|
68
|
+
content: [
|
|
69
|
+
{ type: "text", text: "Hello" },
|
|
70
|
+
{ type: "image", url: "https://example.invalid/img.png" },
|
|
71
|
+
{ type: "tool_result", toolCallId: "call_1", name: "lookup", result: { id: "42" } },
|
|
72
|
+
],
|
|
73
|
+
}],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const body = JSON.parse(String(fetchInit.body));
|
|
77
|
+
assertSerializedRequestCoversContent(request, body, { unsupported: ["image"] });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Implementation example
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { createMockProvider, providerDone, providerTextDelta } from "@arnilo/prism";
|
|
84
|
+
import { assertProviderStreamConforms } from "@arnilo/prism/testing/provider-conformance";
|
|
85
|
+
|
|
86
|
+
await assertProviderStreamConforms({
|
|
87
|
+
provider: createMockProvider([providerTextDelta("Hello"), providerDone()]),
|
|
88
|
+
request: { model: { provider: "mock", model: "demo" }, messages: [] },
|
|
89
|
+
expect: { text: "Hello" },
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Extension and configuration notes
|
|
94
|
+
|
|
95
|
+
The helpers are a testing subpath only. Provider packages can use them with their own mocked fetch/transport or `createMockProvider()`. Live provider tests should stay opt-in and env-gated outside Prism's default test suite.
|
|
96
|
+
|
|
97
|
+
## Security and performance notes
|
|
98
|
+
|
|
99
|
+
- No credentials, env vars, OAuth tokens, filesystem discovery, provider SDKs, or network calls are required.
|
|
100
|
+
- Use fake credentials only in fixtures.
|
|
101
|
+
- The helpers collect one stream into memory; keep conformance fixtures small.
|
|
102
|
+
- Redaction remains the provider/runtime boundary's job. Use `assertNoSecretLeak()` with known fake secrets to catch regressions, not as a general secret scanner.
|
|
103
|
+
|
|
104
|
+
## Related APIs
|
|
105
|
+
|
|
106
|
+
- [Provider layer](provider-layer.md): `AIProvider`, provider events, and mock provider.
|
|
107
|
+
- [Provider packages](provider-packages.md): package authors can use conformance helpers for adapters.
|
|
108
|
+
- [OpenAI-compatible provider](providers/openai-compatible.md): optional provider adapter tested with mocked streams.
|
|
109
|
+
- [Public contracts](public-contracts.md): provider request/event/usage contracts.
|