@arnilo/prism 0.0.96 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +290 -2
- package/README.md +17 -3
- package/dist/agent-definitions.js +2 -3
- package/dist/agent-event-source.d.ts +11 -0
- package/dist/agent-event-source.js +512 -0
- package/dist/agent-loops.d.ts +5 -0
- package/dist/agent-loops.js +99 -14
- package/dist/agent-run-lifecycle.d.ts +5 -2
- package/dist/agent-run-lifecycle.js +18 -2
- package/dist/agent-run-state.d.ts +27 -1
- package/dist/agent-run-state.js +113 -7
- package/dist/agents.d.ts +3 -1
- package/dist/agents.js +1255 -129
- package/dist/artifacts.d.ts +132 -0
- package/dist/artifacts.js +44 -0
- package/dist/cache-helpers.js +18 -9
- package/dist/checkpoints.d.ts +4 -0
- package/dist/checkpoints.js +17 -9
- package/dist/cli-init.js +3 -7
- package/dist/cli-runner.d.ts +2 -6
- package/dist/cli-runner.js +71 -33
- package/dist/compaction.js +5 -4
- package/dist/config.js +7 -4
- package/dist/content.js +26 -24
- package/dist/context-budget.d.ts +67 -0
- package/dist/context-budget.js +288 -0
- package/dist/contracts.d.ts +590 -8
- package/dist/contracts.js +142 -1
- package/dist/contribution-parsing.js +6 -2
- package/dist/contributions.d.ts +2 -0
- package/dist/contributions.js +3 -0
- package/dist/conversations.d.ts +50 -0
- package/dist/conversations.js +98 -0
- package/dist/credentials.d.ts +22 -2
- package/dist/credentials.js +18 -3
- package/dist/devices.d.ts +94 -0
- package/dist/devices.js +138 -0
- package/dist/event-multiplexer.js +18 -4
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +79 -6
- package/dist/feedback.js +12 -10
- package/dist/guardrails.d.ts +1 -1
- package/dist/guardrails.js +26 -17
- package/dist/identity.d.ts +92 -0
- package/dist/identity.js +265 -0
- package/dist/index.d.ts +94 -72
- package/dist/index.js +48 -36
- package/dist/input.d.ts +10 -1
- package/dist/input.js +152 -52
- package/dist/instruction-injection.d.ts +1 -1
- package/dist/middleware.js +9 -1
- package/dist/models.d.ts +2 -0
- package/dist/models.js +3 -0
- package/dist/node/agent-definitions.js +16 -8
- package/dist/node/contribution-discovery.d.ts +1 -2
- package/dist/node/contribution-discovery.js +3 -3
- package/dist/node/session-store-jsonl.js +13 -7
- package/dist/node/settings.d.ts +1 -1
- package/dist/node/settings.js +1 -1
- package/dist/node/system-project-prompts.js +2 -4
- package/dist/node/trust.js +1 -1
- package/dist/persistence-lifecycle.d.ts +103 -0
- package/dist/persistence-lifecycle.js +202 -0
- package/dist/provider-events.d.ts +1 -0
- package/dist/provider-events.js +6 -1
- package/dist/provider-request-policy.js +3 -4
- package/dist/providers/media.d.ts +1 -1
- package/dist/providers/openai-compatible.d.ts +46 -1
- package/dist/providers/openai-compatible.js +123 -53
- package/dist/providers/openai-primitives.js +10 -7
- package/dist/providers/transport.d.ts +6 -0
- package/dist/providers/transport.js +21 -0
- package/dist/providers.d.ts +2 -0
- package/dist/providers.js +3 -0
- package/dist/redaction.d.ts +1 -0
- package/dist/redaction.js +26 -9
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +2 -2
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +8 -1
- package/dist/rpc.js +55 -11
- package/dist/run-ledger.d.ts +6 -0
- package/dist/run-ledger.js +16 -13
- package/dist/run-limits.js +49 -10
- package/dist/secure-agent.js +8 -2
- package/dist/security.js +7 -2
- package/dist/session-stores.d.ts +7 -2
- package/dist/session-stores.js +195 -21
- package/dist/skill-disclosure.d.ts +35 -0
- package/dist/skill-disclosure.js +101 -0
- package/dist/skill-load.d.ts +25 -0
- package/dist/skill-load.js +112 -0
- package/dist/structured-output.d.ts +5 -1
- package/dist/structured-output.js +20 -2
- package/dist/system-prompts.js +7 -2
- package/dist/testing/agent-event-source-conformance.d.ts +4 -0
- package/dist/testing/agent-event-source-conformance.js +54 -0
- package/dist/testing/compaction-conformance.js +5 -1
- package/dist/testing/extension-conformance.js +15 -3
- package/dist/testing/feedback.d.ts +1 -3
- package/dist/testing/feedback.js +1 -1
- package/dist/testing/persistence-schema.d.ts +2 -2
- package/dist/testing/persistence-schema.js +280 -35
- package/dist/testing/provider-conformance.js +3 -3
- package/dist/testing/run-ledger-conformance.js +1 -1
- package/dist/testing/session-store-conformance.d.ts +6 -0
- package/dist/testing/session-store-conformance.js +37 -2
- package/dist/testing/tool-conformance.js +30 -5
- package/dist/testing/tool-effect-store-conformance.d.ts +9 -0
- package/dist/testing/tool-effect-store-conformance.js +85 -0
- package/dist/thinking.js +4 -1
- package/dist/tool-effects.d.ts +15 -0
- package/dist/tool-effects.js +352 -0
- package/dist/tool-result-fold.d.ts +40 -0
- package/dist/tool-result-fold.js +176 -0
- package/dist/tools.d.ts +8 -3
- package/dist/tools.js +248 -13
- package/docs/0.1.0-readiness.md +215 -0
- package/docs/a2a.md +33 -2
- package/docs/acp.md +152 -0
- package/docs/ag-ui-adoption.md +77 -0
- package/docs/ag-ui.md +225 -0
- package/docs/agent-events.md +34 -3
- package/docs/agent-identity.md +144 -0
- package/docs/agent-loops.md +17 -2
- package/docs/agent-session-runtime.md +21 -4
- package/docs/browser-automation.md +5 -0
- package/docs/caveman.md +129 -0
- package/docs/cli-rpc.md +3 -6
- package/docs/coding-agent-tools.md +229 -25
- package/docs/coding-security.md +77 -11
- package/docs/compaction-and-retry.md +5 -2
- package/docs/compaction-llm.md +20 -1
- package/docs/compaction-observational-memory.md +52 -8
- package/docs/context-and-skills.md +94 -7
- package/docs/contribution-registries.md +1 -0
- package/docs/conversations.md +135 -0
- package/docs/credential-storage.md +34 -1
- package/docs/credentials-and-redaction.md +11 -1
- package/docs/database-persistence.md +27 -7
- package/docs/device-adapters.md +97 -0
- package/docs/enterprise-postgres-state.md +178 -0
- package/docs/evaluations.md +14 -1
- package/docs/extensions.md +4 -1
- package/docs/forge-integration.md +113 -0
- package/docs/guardrails.md +16 -2
- package/docs/host-security.md +35 -4
- package/docs/index.md +69 -37
- package/docs/input-and-prompt-assembly.md +8 -7
- package/docs/language-intelligence.md +162 -0
- package/docs/mcp-tools.md +62 -5
- package/docs/middleware-hooks.md +2 -2
- package/docs/migration.md +427 -2
- package/docs/model-routing.md +111 -0
- package/docs/multimodal-content.md +8 -5
- package/docs/node-jsonl-session-store.md +1 -1
- package/docs/observability.md +2 -0
- package/docs/openapi-tools.md +56 -0
- package/docs/performance.md +282 -0
- package/docs/policy-and-audit.md +171 -0
- package/docs/ponytail.md +127 -0
- package/docs/postgres-persistence.md +8 -4
- package/docs/process-sessions.md +147 -0
- package/docs/provider-caching.md +13 -1
- package/docs/provider-conformance.md +29 -5
- package/docs/provider-packages.md +43 -2
- package/docs/provider-request-policies.md +2 -0
- package/docs/providers/ai-sdk.md +24 -7
- package/docs/providers/alibaba.md +179 -0
- package/docs/providers/anthropic.md +93 -0
- package/docs/providers/azure.md +74 -0
- package/docs/providers/bedrock.md +72 -0
- package/docs/providers/google.md +89 -0
- package/docs/providers/ollama.md +166 -0
- package/docs/providers/openai-compatible.md +31 -2
- package/docs/providers/openai.md +24 -5
- package/docs/providers/openrouter.md +2 -0
- package/docs/providers/vertex.md +71 -0
- package/docs/public-contracts.md +68 -4
- package/docs/rag.md +41 -12
- package/docs/release-and-install.md +362 -208
- package/docs/resource-loading.md +3 -0
- package/docs/runs-and-usage.md +3 -0
- package/docs/server.md +44 -6
- package/docs/session-store-conformance.md +2 -0
- package/docs/session-stores.md +41 -2
- package/docs/sqlite-persistence.md +11 -3
- package/docs/structured-output.md +7 -1
- package/docs/supervisors.md +8 -0
- package/docs/tool-effects.md +95 -0
- package/docs/tools.md +5 -0
- package/docs/work-artifacts-and-review.md +102 -0
- package/docs/work-connectors.md +32 -0
- package/docs/work-tools.md +137 -0
- package/docs/workflows.md +6 -0
- package/docs/working-and-semantic-memory.md +40 -7
- package/package.json +30 -7
- package/templates/init/providers.json +22 -0
- package/docs/review-coverage-2026-07-14.md +0 -260
- package/docs/review-coverage-2026-07-15.md +0 -193
- package/docs/review-coverage-2026-07-17-provider-validation.md +0 -192
- package/docs/review-coverage-2026-07-19-phase-3.md +0 -174
- package/docs/review-coverage-2026-07-20-phase-4.md +0 -175
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
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
|
|
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, and metadata. It never applies `input_assembly` middleware itself: `assembleProviderInput()` owns that hook and runs it exactly once after whichever `InputBuilder` is installed returns, so a custom builder cannot bypass it.
|
|
6
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.
|
|
7
|
+
`createDefaultPromptBuilder()` composes messages, context blocks, selected skills, and host-supplied active tools into provider-ready messages. The `Available tools:` text listing is emitted only for models without declared tool support (`model.capabilities.tools !== true` — unknown capability keeps it, fail-safe for text-only providers); tool-capable models receive schemas via `request.tools` and skip the duplicated text. `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
8
|
|
|
9
9
|
## When to use it
|
|
10
10
|
|
|
@@ -18,7 +18,6 @@ Do not use it for tool execution, provider calls, file discovery, credential loo
|
|
|
18
18
|
import { createDefaultInputBuilder } from "@arnilo/prism";
|
|
19
19
|
|
|
20
20
|
const messages = await createDefaultInputBuilder().build("Summarize", {
|
|
21
|
-
inputLayout: "legacy", // default; "cache_aware" passes the cache-aware layout preference
|
|
22
21
|
systemInstructions: "Be accurate.",
|
|
23
22
|
developerInstructions: "Cite supplied context only.",
|
|
24
23
|
history,
|
|
@@ -58,12 +57,13 @@ Useful exported types:
|
|
|
58
57
|
|
|
59
58
|
- `AgentInput`: `string | Message | readonly Message[]`.
|
|
60
59
|
- `DefaultInputBuilder`: the default `InputBuilder` with typed default context.
|
|
61
|
-
- `InputAssemblyLayout`: `"legacy" | "cache_aware"`;
|
|
60
|
+
- `InputAssemblyLayout`: `"legacy" | "cache_aware"`; `cache_aware` is default.
|
|
62
61
|
- `DefaultInputBuildContext`: optional input layout, instructions, history, summaries, attachments, resource loader/URIs, tool results, middleware, ids, metadata, and abort signal.
|
|
63
62
|
- `InputAttachment`: already-loaded text/content blocks (including `audio`, `file`, and `document`) or an explicit URI loaded through a caller-provided `ResourceLoader`.
|
|
64
63
|
- `PromptInstruction`: labeled system instruction text.
|
|
65
64
|
- `DefaultPromptBuilder`: the default `PromptBuilder`.
|
|
66
|
-
- `AssembleProviderInputOptions`: model, input, optional builders, context providers, selected skills, active tools, generic provider options, metadata, and
|
|
65
|
+
- `AssembleProviderInputOptions`: model, input, optional builders, context providers, selected skills, active tools, generic provider options, metadata, signal, and optional `contextBudget` (`maxInputTokens` / `maxInputBytes` / `reportOmissions`).
|
|
66
|
+
- `applyContextBudget` / `getContextBudgetReport` / `resolveContextBudget`: deterministic eviction + omission report helpers (estimate = UTF-16 code units ÷ 4).
|
|
67
67
|
- `PromptTemplateOptions`: missing-variable behavior for `renderPromptTemplate()`.
|
|
68
68
|
|
|
69
69
|
## Outputs / response / events
|
|
@@ -72,7 +72,7 @@ The builder returns `readonly Message[]`.
|
|
|
72
72
|
|
|
73
73
|
- String input becomes one user text message.
|
|
74
74
|
- `Message` and `Message[]` input are preserved.
|
|
75
|
-
-
|
|
75
|
+
- `cache_aware` layout is the default. Set `inputLayout: "legacy"` on the default builder, `assembleProviderInput()`, `AgentConfig`, or `RunOptions` to restore the prior order.
|
|
76
76
|
|
|
77
77
|
| Layout | Input message order |
|
|
78
78
|
| --- | --- |
|
|
@@ -86,6 +86,7 @@ The default prompt builder still prepends context, selected skills, and tool dec
|
|
|
86
86
|
- 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. Cache-aware layout keeps tool results before the current user suffix so it does not split tool transcripts.
|
|
87
87
|
- Middleware runs only when `middleware` is supplied in the context.
|
|
88
88
|
- `assembleProviderInput()` returns a `ProviderRequest` with the caller's model/tools/provider options/metadata/signal and composed messages/context. It also calls `assertMessagesSupportModelCapabilities()` so unsupported `audio`/`file`/`document`/`image` blocks fail with `UnsupportedModalityError` when the model declares `capabilities.input`.
|
|
89
|
+
- Optional `contextBudget` (at least one of `maxInputTokens` / `maxInputBytes`) runs after default message groups are built and before final flatten. Eviction drops droppable sections first (toolResults → history → summaries → context → skills → attachments; layout-aware). Within `history`, oldest messages drop first. Protected instructions + current user `input` (+ tools catalog) fail closed with `ContextBudgetError` if they alone exceed the budget. When `reportOmissions: true`, attach `ProviderRequest.metadata[CONTEXT_BUDGET_REPORT_METADATA_KEY]` and read via `getContextBudgetReport(request)` (kinds/ids/sizes only — no secrets). Raw session store entries are never deleted.
|
|
89
90
|
- `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" }`.
|
|
90
91
|
|
|
91
92
|
## Request/response example
|
|
@@ -158,7 +159,7 @@ const request = await assembleProviderInput({
|
|
|
158
159
|
});
|
|
159
160
|
```
|
|
160
161
|
|
|
161
|
-
`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.
|
|
162
|
+
`input_assembly`, `context`, and `prompt_build` middleware are not global. They run only for helper calls that receive a `MiddlewareRegistry`, in that assembly order. Inside `assembleProviderInput()`, `input_assembly` always runs — for both the default and any custom `InputBuilder`, and on both the plain and context-budget paths. `assembleProviderInput()` keeps provider `tools` equal to the host-supplied active tool list after prompt middleware.
|
|
162
163
|
|
|
163
164
|
## Security and performance notes
|
|
164
165
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Language intelligence
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`createLanguageIntelligence` is an optional host-activated contract in `@arnilo/prism-coding-agent` that talks to **host-selected** language servers over one bounded in-package JSON-RPC client (LSP 3.17 Content-Length framing). It exposes workspace symbols, definitions, references, diagnostics, hover, and rename/workspace edits. No `vscode-languageserver-protocol` dependency. Nothing spawns on import or construction — servers start lazily on first use and stop on `dispose()`.
|
|
6
|
+
|
|
7
|
+
| Export | Purpose |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `createLanguageIntelligence(options)` | Build a `LanguageIntelligence` instance for one workspace root. |
|
|
10
|
+
| `LanguageIntelligence` | Contract: `workspaceSymbols`, `definitions`, `references`, `diagnostics`, `hover`, `rename`, `dispose`. |
|
|
11
|
+
| `LanguageServerSpec` | Host allow-listed `{ command, args?, languages, env? }`. Never model-supplied. |
|
|
12
|
+
| `LanguageLocation` / `LanguageSymbol` / `LanguageDiagnostic` / `LanguageWorkspaceEdit` | Normalized result shapes (paths workspace-relative; positions LSP 0-based). |
|
|
13
|
+
| `LanguageIntelligenceError` | Typed fail-closed errors (`ERR_PRISM_LSP_*`). |
|
|
14
|
+
| `resolveLanguageIntelligenceLimits` / `DEFAULT_MAX_LSP_*` / `HARD_MAX_LSP_*` | Finite caps for message bytes, diagnostics/file, pending requests, results/query, timeout, servers. |
|
|
15
|
+
| `encodeLspFrame` / `LspFrameReader` | Framing helpers (tests/hosts). |
|
|
16
|
+
|
|
17
|
+
## When to use it
|
|
18
|
+
|
|
19
|
+
Use when a host wants IDE-like language intelligence without embedding a parser framework or trusting model-chosen server commands. Wire host-pinned server binaries (for example `typescript-language-server --stdio`) and gate renames with the same `ExecutionPolicy` used for write/edit tools.
|
|
20
|
+
|
|
21
|
+
Do not use this as a sandbox, tool registry, or process session manager. Optional process-session registration of LSP children can use `createProcessSessions` ([Process sessions](process-sessions.md)).
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createLanguageIntelligence } from "@arnilo/prism-coding-agent";
|
|
25
|
+
|
|
26
|
+
const lang = createLanguageIntelligence({
|
|
27
|
+
workspaceRoot,
|
|
28
|
+
servers: {
|
|
29
|
+
typescript: {
|
|
30
|
+
command: "/usr/bin/typescript-language-server",
|
|
31
|
+
args: ["--stdio"],
|
|
32
|
+
languages: ["typescript", "typescriptreact"],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
policy: hostExecutionPolicy, // rename gated like edit
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const defs = await lang.definitions({ file: "src/a.ts", line: 10, character: 4 });
|
|
39
|
+
await lang.rename({ file: "src/a.ts", line: 10, character: 4, newName: "renamed" });
|
|
40
|
+
await lang.dispose();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Inputs / request
|
|
44
|
+
|
|
45
|
+
`createLanguageIntelligence` options:
|
|
46
|
+
|
|
47
|
+
| Field | Type | Purpose |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `workspaceRoot` | `string` | Absolute or relative workspace root; all file URIs must stay inside. |
|
|
50
|
+
| `servers` | `Record<string, LanguageServerSpec>` | Host map keyed by server name; size capped (`maxServers`). |
|
|
51
|
+
| `limits?` | `LanguageIntelligenceLimits` | Optional overrides; invalid values fail instead of clamping. |
|
|
52
|
+
| `policy?` | `ExecutionPolicy` | Applied before rename writes (`kind: "edit"`, `operation: "rename"`). |
|
|
53
|
+
|
|
54
|
+
`LanguageServerSpec`:
|
|
55
|
+
|
|
56
|
+
| Field | Purpose |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `command` | Host allow-listed executable path. |
|
|
59
|
+
| `args?` | Fixed argv (never from the model). |
|
|
60
|
+
| `languages` | Language ids this server handles (matched from file extension). |
|
|
61
|
+
| `env?` | Extra env merged onto `process.env` for the child. |
|
|
62
|
+
|
|
63
|
+
Operation inputs use workspace-relative `file` plus LSP **0-based** `line` / `character`. `rename` also requires `newName`.
|
|
64
|
+
|
|
65
|
+
## Outputs / response / events
|
|
66
|
+
|
|
67
|
+
| Method | Result |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `workspaceSymbols(query)` | `LanguageSymbol[]` (capped). |
|
|
70
|
+
| `definitions` / `references` | `LanguageLocation[]` (capped). |
|
|
71
|
+
| `diagnostics(file?)` | Normalized `LanguageDiagnostic[]` (per-file and aggregate caps). |
|
|
72
|
+
| `hover` | `{ text }` or `undefined`. |
|
|
73
|
+
| `rename` | `LanguageWorkspaceEdit` after policy-checked atomic writes. |
|
|
74
|
+
| `dispose` | Stops all spawned servers (bounded). |
|
|
75
|
+
|
|
76
|
+
Errors are `LanguageIntelligenceError` with codes: `ERR_PRISM_LSP_FRAMING`, `ERR_PRISM_LSP_SERVER`, `ERR_PRISM_LSP_TIMEOUT`, `ERR_PRISM_LSP_LIMIT`, `ERR_PRISM_LSP_UNSUPPORTED`, `ERR_PRISM_LSP_WORKSPACE`.
|
|
77
|
+
|
|
78
|
+
No package-owned events; hosts observe via their own run/tool wiring.
|
|
79
|
+
|
|
80
|
+
## Request/response example
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
// definitions request (host API, not JSON-RPC wire)
|
|
84
|
+
{ "file": "src/a.ts", "line": 10, "character": 4 }
|
|
85
|
+
|
|
86
|
+
// normalized definition
|
|
87
|
+
{ "file": "src/a.ts", "line": 2, "character": 0 }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
// rename workspace edit (after apply)
|
|
92
|
+
{
|
|
93
|
+
"edits": [
|
|
94
|
+
{
|
|
95
|
+
"file": "src/a.ts",
|
|
96
|
+
"newText": "renamed",
|
|
97
|
+
"range": {
|
|
98
|
+
"start": { "line": 10, "character": 4 },
|
|
99
|
+
"end": { "line": 10, "character": 7 }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Implementation example
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import {
|
|
110
|
+
createLanguageIntelligence,
|
|
111
|
+
DEFAULT_MAX_LSP_TIMEOUT_MS,
|
|
112
|
+
} from "@arnilo/prism-coding-agent";
|
|
113
|
+
import { createCodingApprovalPolicy } from "@arnilo/prism-coding-security";
|
|
114
|
+
|
|
115
|
+
const policy = createCodingApprovalPolicy({
|
|
116
|
+
roots: [workspaceRoot],
|
|
117
|
+
approve: async ({ action }) => host.confirm(action),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const lang = createLanguageIntelligence({
|
|
121
|
+
workspaceRoot,
|
|
122
|
+
servers: {
|
|
123
|
+
ts: {
|
|
124
|
+
command: process.execPath, // example only — pin a real language server in production
|
|
125
|
+
args: ["/path/to/typescript-language-server", "--stdio"],
|
|
126
|
+
languages: ["typescript", "typescriptreact"],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
limits: { requestTimeoutMs: DEFAULT_MAX_LSP_TIMEOUT_MS },
|
|
130
|
+
policy,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const diags = await lang.diagnostics("src/app.ts");
|
|
135
|
+
const hover = await lang.hover({ file: "src/app.ts", line: 0, character: 0 });
|
|
136
|
+
console.log(diags.length, hover?.text);
|
|
137
|
+
} finally {
|
|
138
|
+
await lang.dispose();
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Extension and configuration notes
|
|
143
|
+
|
|
144
|
+
- **Server map is the only language binding.** Extension ids map from common file extensions (`.ts` → `typescript`, `.py` → `python`, …); unknown extensions use `plaintext`. Hosts register servers for the language ids they need.
|
|
145
|
+
- **Lazy start.** First request for a language starts that server (`initialize` / `initialized`); `workspaceSymbols` / aggregate `diagnostics` start all configured servers.
|
|
146
|
+
- **Pluggable policy only.** Renames reuse `assertExecutionAllowed` + `withFileMutationQueue` + `atomicWriteUtf8File`. No second write path.
|
|
147
|
+
- **Framing helpers** (`encodeLspFrame`, `LspFrameReader`) are exported for tests and custom transports; production hosts normally use only `createLanguageIntelligence`.
|
|
148
|
+
- **Not in default tool aggregators.** Hosts call the contract directly or wrap it in their own `ToolDefinition`s.
|
|
149
|
+
|
|
150
|
+
## Security and performance notes
|
|
151
|
+
|
|
152
|
+
- Server `command`/`args` are host-config only — never taken from model tool arguments.
|
|
153
|
+
- File URIs must be `file:` and resolve inside `workspaceRoot`; escapes fail with `ERR_PRISM_LSP_WORKSPACE`.
|
|
154
|
+
- LSP payloads are untrusted: Content-Length framing is bounded; oversized/malformed frames fail closed; result lists and diagnostics are capped.
|
|
155
|
+
- Crash loop: unexpected exit increments a per-server restart counter; after the freeze budget (`LSP_RESTARTS_PER_SERVER` = 3) further starts fail with `ERR_PRISM_LSP_SERVER`.
|
|
156
|
+
- Defaults / hard caps (Phase 9 freeze): message 4 MiB / 32 MiB; diagnostics/file 200 / 1000; pending requests 32 / 128; results/query 500 / 5000; timeout 30 s / 120 s; servers/workspace 4 / 8.
|
|
157
|
+
|
|
158
|
+
## Related APIs
|
|
159
|
+
|
|
160
|
+
- [Coding agent tools](coding-agent-tools.md): shell/read/write/edit/list/search/glob and shared limits/policy seams this contract reuses for rename.
|
|
161
|
+
- [Coding execution approval and sandboxing](coding-security.md): `ExecutionPolicy` / approval composition for gating rename.
|
|
162
|
+
- [Tools](tools.md): host-owned `ToolDefinition` registration if you wrap language intelligence as tools.
|
package/docs/mcp-tools.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-mcp` has two explicit directions. Its client bridge connects hosts to remote [Model Context Protocol](https://modelcontextprotocol.io) servers and maps discovered tools to ordinary `ToolDefinition`s. Its server API registers selected Prism `ToolDefinition` and `CommandDefinition` values on the official SDK `McpServer`, with required authorization and a bounded optional Web-standard Streamable HTTP handler. The package pins `@modelcontextprotocol/sdk` **1.
|
|
5
|
+
`@arnilo/prism-mcp` has two explicit directions. Its client bridge connects hosts to remote [Model Context Protocol](https://modelcontextprotocol.io) servers and maps discovered tools to ordinary `ToolDefinition`s. Its server API registers selected Prism `ToolDefinition` and `CommandDefinition` values on the official SDK `McpServer`, with required authorization and a bounded optional Web-standard Streamable HTTP handler. The package pins `@modelcontextprotocol/sdk` **1.30.0** (MCP protocol negotiation remains SDK-owned) and adds no MCP branch to core Prism.
|
|
6
6
|
|
|
7
7
|
Primary API:
|
|
8
8
|
|
|
@@ -21,6 +21,17 @@ await bridge.close(); // close client + transport
|
|
|
21
21
|
|
|
22
22
|
Advanced hosts that manage their own `Client` + `Transport` can call `attachMcpToolBridge()` or `attachMcpCapabilities()` after connect. `connectMcpCapabilities()` keeps resources/prompts as host-facing facades rather than converting them into model tools, and declares roots/sampling/elicitation only when callbacks are supplied.
|
|
23
23
|
|
|
24
|
+
MCP Apps is an explicit opt-in on the normal bridge:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const bridge = await connectMcpTools({ serverId: "weather", transport, mcpApps: true });
|
|
28
|
+
// Fails unless the server acknowledges io.modelcontextprotocol/ui.
|
|
29
|
+
const app = await bridge.apps!.readResource("ui://weather/card");
|
|
30
|
+
// bridge.tools excludes _meta.ui.visibility: ["app"] tools.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`bridge.apps` exposes reviewed UI metadata, linked bounded `ui://` HTML, and same-server app tools for a host renderer/proxy; it never creates an iframe or executes HTML.
|
|
34
|
+
|
|
24
35
|
```ts
|
|
25
36
|
const bridge = await connectMcpCapabilities({
|
|
26
37
|
serverId: "research",
|
|
@@ -33,7 +44,7 @@ await bridge.listResources();
|
|
|
33
44
|
await bridge.getPrompt("review", { topic: "security" });
|
|
34
45
|
```
|
|
35
46
|
|
|
36
|
-
Server capability matrix for SDK 1.
|
|
47
|
+
Server capability matrix for SDK 1.30.0: tools/resources/prompts and their list-change notifications are supported through official registrations; roots/sampling/form+URL elicitation are supported as explicit client callbacks. Missing server resources/prompts throw `McpUnsupportedCapabilityError` with `ERR_PRISM_MCP_UNSUPPORTED_CAPABILITY`. Resource/prompt results and sampling/elicitation inputs/results are bounded JSON. Accepted form/URL elicitation requires host-only `humanInteraction: true`; bridge strips marker before protocol output and fails closed when absent. Automatic root discovery/consent, model selection, credential resolution, URL navigation, generic command proxying, and custom JSON-RPC are unsupported.
|
|
37
48
|
|
|
38
49
|
Server direction:
|
|
39
50
|
|
|
@@ -61,7 +72,7 @@ const handleMcp = await createPrismMcpWebHandler(server, {
|
|
|
61
72
|
});
|
|
62
73
|
```
|
|
63
74
|
|
|
64
|
-
`McpServer.connect(transport)` remains available for SDK stdio or in-memory transports. The helper uses SDK `WebStandardStreamableHTTPServerTransport`; it does not start a listener. Default remains bounded stateless JSON-response mode. Supplying `sessionIdGenerator` enables SDK `MCP-Session-Id` POST/GET/DELETE/SSE lifecycle and requires exact `allowedOrigins` plus host `resolveIdentity`. Every request re-authenticates, and a different principal receives non-disclosing 404. SDK owns protocol-version/session headers and SSE semantics. SDK 1.
|
|
75
|
+
`McpServer.connect(transport)` remains available for SDK stdio or in-memory transports. The helper uses SDK `WebStandardStreamableHTTPServerTransport`; it does not start a listener. Default remains bounded stateless JSON-response mode. Supplying `sessionIdGenerator` enables SDK `MCP-Session-Id` POST/GET/DELETE/SSE lifecycle and requires exact `allowedOrigins` plus host `resolveIdentity`. Every request re-authenticates, and a different principal receives non-disclosing 404. SDK owns protocol-version/session headers and SSE semantics. SDK 1.30.0's in-memory event store is not enabled, so `Last-Event-ID` replay is explicitly unsupported; reconnect starts only through SDK-supported active session GET.
|
|
65
76
|
|
|
66
77
|
## When to use it
|
|
67
78
|
|
|
@@ -78,7 +89,7 @@ Do **not** use this package as a sandbox, permission engine, or auto-discovery l
|
|
|
78
89
|
|
|
79
90
|
## Outputs / response / events
|
|
80
91
|
|
|
81
|
-
|
|
92
|
+
`McpToolBridge` exposes `tools`, optional `apps`, `refresh()`, and `close()`. Normal tools are Prism `ToolDefinition`s. Apps requires server acknowledgement; nested resource metadata wins over flat/deprecated and app-only tools stay outside `tools`. Resource reads require linked bounded `ui://` HTML5 with exact MIME; content metadata wins over list defaults.
|
|
82
93
|
|
|
83
94
|
`createPrismMcpServer()` returns the SDK `McpServer`. It lists only passed tools/commands and explicitly selected `agentRuns` lifecycle tools; JSON Schema parameters are converted through installed Zod v4 for SDK validation, then Prism tool calls still pass through `dispatchToolCall` permission/validator/redactor gates. Command definitions support explicitly selected direct/background/replay workflow operations and optional ownership-scoped schedule operations from `createWorkflowCommands()`; none are registered unless the host passes those command definitions. Calls return bounded MCP text content and `isError` on denial/failure. `createPrismMcpWebHandler()` returns `(Request) => Promise<Response>`.
|
|
84
95
|
|
|
@@ -126,6 +137,7 @@ Duplicate prefixed names throw `McpToolNameCollisionError` at refresh time.
|
|
|
126
137
|
| `serverId` | required | Stable identifier used in default name prefix |
|
|
127
138
|
| `transport` | required | `stdio` or `streamable-http` config |
|
|
128
139
|
| `namePrefix` | `mcp:<serverId>:` | Registry namespace for remote tools |
|
|
140
|
+
| `mcpApps` | `false` | Explicitly negotiate `io.modelcontextprotocol/ui`; exposes `bridge.apps` only after server acknowledgement |
|
|
129
141
|
| `listCacheTtlMs` | 30 s (24 h hard) | Skip re-listing until TTL expires (invalidated on list-changed) |
|
|
130
142
|
| `callTimeoutMs` | 60 s (30 min hard) | Connect, list-page, and tool-call SDK request timeout/abort |
|
|
131
143
|
| `maxListPages` / `maxTools` | 20 / 500 (hard 100 / 5,000) | Stop pagination before another request/append |
|
|
@@ -138,6 +150,8 @@ Duplicate prefixed names throw `McpToolNameCollisionError` at refresh time.
|
|
|
138
150
|
| `maxJsonDepth` / `maxJsonProperties` | 64 / 10,000 (hard 128 / 100,000) | Bound schema and result JSON walks |
|
|
139
151
|
| `signal` | none | Abort connect/list and trigger close on connect abort |
|
|
140
152
|
|
|
153
|
+
MCP elicitation maps onto the shared decision model: `mcpElicitationDecision(approvalId, params)` converts an untrusted `ElicitRequest` (message ≤ 2 KiB, schema ≤ 16 KiB) into a kind-`elicitation` pending decision, and `mcpElicitationResultFromDecision(decision, { humanInteraction })` maps a decision back to a protocol result — `reject_*` declines, `allow_*` accepts with the payload and fails closed unless the host proved explicit human interaction. Wire behavior is unchanged; the marker never reaches protocol output.
|
|
154
|
+
|
|
141
155
|
### Stdio transport
|
|
142
156
|
|
|
143
157
|
```ts
|
|
@@ -187,6 +201,8 @@ Plaintext is accepted only when `allowLoopbackHttp: true`, the URL hostname is l
|
|
|
187
201
|
|
|
188
202
|
Web handler defaults: 1 MiB request (8 MiB hard), 2 MiB response (16 MiB hard), 32 concurrent requests (512 hard), 60 s timeout (30 min hard), and 32 sessions (512 hard). Stateful mode is intentionally one official SDK transport/session lineage per handler; use one handler/server instance per independently hosted endpoint when multi-tenant transport isolation is required. It parses bounded JSON before passing `parsedBody` to the SDK transport. `allowedHosts`/`allowedOrigins` activate SDK DNS-rebinding checks only when explicitly configured. Authentication data comes only from host `resolveAuthInfo()`.
|
|
189
203
|
|
|
204
|
+
Remote MCP tools default to `external_mutation`/`unsupported` unless the host `effect` policy classifies them. MCP Apps (`io.modelcontextprotocol/ui`) stay behind host CSP/origin/visibility gates. See [tool effects](tool-effects.md) and [AG-UI adoption](ag-ui-adoption.md).
|
|
205
|
+
|
|
190
206
|
## Security and performance notes
|
|
191
207
|
|
|
192
208
|
| Risk | Mitigation |
|
|
@@ -195,9 +211,11 @@ Web handler defaults: 1 MiB request (8 MiB hard), 2 MiB response (16 MiB hard),
|
|
|
195
211
|
| SSRF / DNS rebinding / redirects (HTTP) | Exact HTTPS origins; credentials/fragments/redirects denied; every DNS answer public; one address pinned per request; explicit loopback-only HTTP escape hatch |
|
|
196
212
|
| Hostile discovery / schema compilation | Raw SDK `tools/list` requests avoid SDK Ajv output-schema compilation; finite pages/tools/cursors/metadata/schema totals; failed refresh leaves previous tools unchanged |
|
|
197
213
|
| Tool-name shadowing | Prefixed names + `createToolRegistry({ duplicate: "error" })` |
|
|
214
|
+
| MCP Apps metadata/HTML | Explicit extension acknowledgement; bounded nested metadata; app-only tools absent from model list; only linked `ui://` HTML/MIME resource body reaches the host renderer |
|
|
198
215
|
| Oversized/deep/wide server output | One aggregate byte/depth/property walk covers content, structured content, compatibility `toolResult`, and bounded remote errors before `ToolResult` |
|
|
199
216
|
| Unvalidated arguments | Register tools with `createJsonSchemaToolArgumentValidator()` at dispatch |
|
|
200
217
|
| Missing permission gate | Client direction: `PermissionPolicy` on `tool:mcp:<serverId>:<name>:execute`; server direction: required MCP `authorize` plus optional core `PermissionPolicy` |
|
|
218
|
+
| Unverified / widened identity | Optional `PrismMcpAuthorization.identity` must be host-verified and match ownership; invalid identity is forbidden before tool dispatch |
|
|
201
219
|
| Accidental server exposure | Empty default arrays/maps, duplicate-name rejection, explicit tools/commands/lifecycle only |
|
|
202
220
|
| Agent lifecycle data leak or cross-tenant resume | `agentRuns` requires exact tenant plus account/user ownership; core lifecycle returns public redacted state only and CAS-resumes with current agent/revision |
|
|
203
221
|
| Unbounded MCP HTTP | Bounded pre-parsed JSON, response bytes, concurrent requests, call timeout, SDK web-standard transport |
|
|
@@ -206,22 +224,61 @@ Web handler defaults: 1 MiB request (8 MiB hard), 2 MiB response (16 MiB hard),
|
|
|
206
224
|
|
|
207
225
|
For durable lifecycle exposure, construct `createAgentRunLifecycle({ checkpoints, resolveAgent })` in core, then pass selected entries as `agentRuns: { support: { lifecycle } }`. MCP registers two tools: `agent.support.status` accepts `{ runId, sessionId? }`; `agent.support.resume` accepts `{ runId, sessionId?, decision, expectedVersion }`. Do not expose an agent without durable checkpoints and a restart-safe `SessionStore`; no lifecycle tool appears by default.
|
|
208
226
|
|
|
209
|
-
MCP output is untrusted. Register bridge tools through core dispatch with a `SecretRedactor`
|
|
227
|
+
MCP output is untrusted. Register bridge tools through core dispatch with a `SecretRedactor`. Apps renderer needs separate origin, `allow-scripts allow-same-origin`, no-wider CSP, and authenticated proxy approval; no app mutation retry before Task 4 recovery. `CreatePrismMcpServerOptions.guardrails` applies shared tool-input/output stages to registered Prism tools; commands remain host callbacks. See [Guardrails](guardrails.md). Prism does not infer unknown secrets. MCP server authorization does not replace tool `PermissionPolicy`, argument validation, coding `ExecutionPolicy`, workflow ownership checks, TLS, rate limiting, or sandboxing. A timed-out tool must cooperate with `AbortSignal` to stop side effects; protocol retention and HTTP responses remain bounded when remote work ignores abort.
|
|
210
228
|
|
|
211
229
|
Discovery validation is atomic: cursor/page/tool/name/description/schema failures reject `refresh()` and preserve the previous immutable tool-array reference. The bridge intentionally uses raw SDK `request()` for `tools/list` and `tools/call`; this avoids eager Ajv compilation/validation of untrusted remote output schemas. Host `ToolValidator` remains the argument-validation owner.
|
|
212
230
|
|
|
231
|
+
## MCP OAuth (0.0.28)
|
|
232
|
+
|
|
233
|
+
Optional RFC 9728/8414 OAuth client and server wiring for Streamable HTTP transports.
|
|
234
|
+
|
|
235
|
+
**Client** — pass `auth` to the transport/bridge options:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
import { createMcpClientAuth } from "@arnilo/prism-mcp";
|
|
239
|
+
|
|
240
|
+
const auth = createMcpClientAuth(
|
|
241
|
+
{
|
|
242
|
+
state, // required persistence seam: load/save tokens, discovery, client info, code verifier
|
|
243
|
+
strategy: { kind: "static", clientId: "prism", clientSecret: "..." }, // or { kind: "dcr", clientMetadata }
|
|
244
|
+
redirectUri: "http://localhost:33418/callback",
|
|
245
|
+
onRedirectRequired: (url) => openBrowser(url), // interactive flows
|
|
246
|
+
},
|
|
247
|
+
{ serverUrl: "https://mcp.example.com/api", fetch },
|
|
248
|
+
);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
The flow reuses the MCP SDK's `auth()` helper (401 → protected-resource metadata → RFC 8414 discovery → PKCE S256 → token exchange/refresh) wrapped in Prism policy: discovery URLs are SSRF-checked, https-only (loopback http opt-in), DNS-pinned, zero-redirect, and byte-bounded; RFC 8707 resource binding is enforced on every token request (`ERR_PRISM_MCP_OAUTH_AUDIENCE` on origin drift); issuer origin must match the discovered authorization server (`ERR_PRISM_MCP_OAUTH_ORIGIN`); bearer tokens are only ever attached to the allow-listed server origin. `McpClientAuthState` has no default implementation — production hosts back it with an encrypted/keychain store (refresh tokens must not live in plaintext persistence).
|
|
252
|
+
|
|
253
|
+
**Server** — advertise protected-resource metadata and challenge unauthenticated requests:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const handler = await createPrismMcpWebHandler(factory, {
|
|
257
|
+
protectedResource: {
|
|
258
|
+
authorizationServers: ["https://as.example.com/"],
|
|
259
|
+
resource: "https://mcp.example.com/mcp", // required (RFC 9728)
|
|
260
|
+
scopesSupported: ["mcp"],
|
|
261
|
+
},
|
|
262
|
+
resolveIdentity, // host-owned token verification stays here
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The handler serves `GET /.well-known/oauth-protected-resource` and returns `401 WWW-Authenticate: Bearer resource_metadata="<origin>/.well-known/oauth-protected-resource"` on rejected requests. Token verification remains entirely host-owned via `resolveIdentity`; Prism only advertises and challenges.
|
|
267
|
+
|
|
213
268
|
## Vendor web MCP prototype boundary
|
|
214
269
|
|
|
215
270
|
Official Exa/Firecrawl MCP servers may be tested only as explicit hardened prototypes: pin endpoint/origin/auth, inspect declared capabilities, allow-list individual tools/resources, retain all MCP bounds, and never expose generic remote passthrough. Production web research uses direct host-selected `@arnilo/prism-web-tools` adapters so provider choice, credentials, schema, and costs remain outside model control.
|
|
216
271
|
|
|
217
272
|
## Related APIs
|
|
218
273
|
|
|
274
|
+
- [Agent identity](agent-identity.md): optional verified identity on MCP authorize results
|
|
219
275
|
- [Tools](tools.md): registry, dispatch, validation
|
|
220
276
|
- [Web search, fetch, and extraction](web-tools.md): preferred direct bounded Brave/Exa/Firecrawl production path
|
|
221
277
|
- [Tool execution primitives](tool-execution-primitives.md): Plan 055 design and conformance matrix
|
|
222
278
|
- [Host security guide](host-security.md): permission, trust, validation checklist
|
|
223
279
|
- [Web-standard server handler](server.md): agent/workflow HTTP routes and shared remote-boundary rules
|
|
224
280
|
- Package README: [`@arnilo/prism-mcp`](../packages/mcp/README.md)
|
|
281
|
+
- [ACP coding-host interop](acp.md): ACP clients may attach MCP servers to sessions — bounded configs (8/32 servers, 16 KiB/256 KiB config, 4 KiB/64 KiB header values), http/sse only when advertised, stdio accepted behind the gate, UNSTABLE `acp` always rejected, and every server approved by host `mcp.select` before the bridge connects.
|
|
225
282
|
|
|
226
283
|
## Testing
|
|
227
284
|
|
package/docs/middleware-hooks.md
CHANGED
|
@@ -43,11 +43,11 @@ Built-in hook names:
|
|
|
43
43
|
| `run(hook, value)` | hook name and payload | Runs registered middleware and returns the final payload. |
|
|
44
44
|
| `list(hook)` | hook name | Returns registered middleware for inspection. |
|
|
45
45
|
|
|
46
|
-
`Middleware<T>` receives `(value, next)` and returns a value or promise. Calling `next(updatedValue)` passes an updated value to later middleware.
|
|
46
|
+
`Middleware<T>` receives `(value, next)` and returns a value or promise. Calling `next(updatedValue)` passes an updated value to later middleware. Two rules are enforced: call `next()` **at most once** — a second call throws (routed through the registry `errorPolicy`) naming hook and index; and **either** `return next(v)` **or** return a new value, never both — when `next(v)` was already called, a conflicting return is discarded and diagnosed via `onError` (the `next()` value wins).
|
|
47
47
|
|
|
48
48
|
## Outputs / response / events
|
|
49
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.
|
|
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 `input_assembly` call is unconditional — it runs after whatever `InputBuilder` produced the messages, so host middleware at that hook cannot be skipped by a custom builder. 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
51
|
|
|
52
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
53
|
|