@arnilo/prism 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +19 -2
  2. package/README.md +17 -7
  3. package/dist/agent-definitions.d.ts +12 -0
  4. package/dist/agent-definitions.js +131 -0
  5. package/dist/agent-loops.d.ts +14 -0
  6. package/dist/agent-loops.js +161 -0
  7. package/dist/agents.js +263 -76
  8. package/dist/cache-helpers.d.ts +28 -0
  9. package/dist/cache-helpers.js +73 -0
  10. package/dist/cli-runner.d.ts +38 -2
  11. package/dist/cli-runner.js +167 -5
  12. package/dist/compaction.js +2 -0
  13. package/dist/config.js +47 -12
  14. package/dist/contracts.d.ts +581 -6
  15. package/dist/contracts.js +41 -1
  16. package/dist/contribution-parsing.d.ts +19 -0
  17. package/dist/contribution-parsing.js +124 -0
  18. package/dist/contributions.d.ts +13 -3
  19. package/dist/contributions.js +96 -20
  20. package/dist/extensions.js +3 -0
  21. package/dist/index.d.ts +19 -9
  22. package/dist/index.js +10 -4
  23. package/dist/input.d.ts +7 -1
  24. package/dist/input.js +52 -11
  25. package/dist/instruction-injection.d.ts +28 -0
  26. package/dist/instruction-injection.js +55 -0
  27. package/dist/manifests.d.ts +1 -1
  28. package/dist/manifests.js +3 -3
  29. package/dist/models.d.ts +4 -1
  30. package/dist/models.js +5 -2
  31. package/dist/node/agent-definitions.d.ts +98 -0
  32. package/dist/node/agent-definitions.js +389 -0
  33. package/dist/node/contribution-discovery.d.ts +17 -0
  34. package/dist/node/contribution-discovery.js +163 -0
  35. package/dist/node/instruction-injectors.d.ts +32 -0
  36. package/dist/node/instruction-injectors.js +72 -0
  37. package/dist/node/session-store-jsonl.d.ts +1 -1
  38. package/dist/node/session-store-jsonl.js +42 -4
  39. package/dist/node/system-project-prompts.d.ts +30 -0
  40. package/dist/node/system-project-prompts.js +53 -0
  41. package/dist/provider-events.d.ts +3 -1
  42. package/dist/provider-events.js +34 -0
  43. package/dist/provider-request-policy.js +15 -1
  44. package/dist/providers/openai-compatible.js +1 -1
  45. package/dist/providers.d.ts +6 -2
  46. package/dist/providers.js +15 -1
  47. package/dist/redaction.d.ts +2 -1
  48. package/dist/redaction.js +3 -0
  49. package/dist/registry-options.d.ts +5 -0
  50. package/dist/registry-options.js +5 -0
  51. package/dist/rpc.d.ts +6 -2
  52. package/dist/rpc.js +71 -13
  53. package/dist/session-stores.d.ts +3 -1
  54. package/dist/session-stores.js +67 -6
  55. package/dist/skills.d.ts +4 -1
  56. package/dist/skills.js +3 -1
  57. package/dist/system-prompts.js +6 -2
  58. package/dist/testing/compaction-conformance.d.ts +17 -0
  59. package/dist/testing/compaction-conformance.js +61 -0
  60. package/dist/testing/extension-conformance.d.ts +26 -0
  61. package/dist/testing/extension-conformance.js +55 -0
  62. package/dist/testing/provider-conformance.d.ts +7 -0
  63. package/dist/testing/provider-conformance.js +18 -31
  64. package/dist/testing/session-store-conformance.d.ts +20 -0
  65. package/dist/testing/session-store-conformance.js +92 -0
  66. package/dist/testing/tool-conformance.d.ts +39 -0
  67. package/dist/testing/tool-conformance.js +79 -0
  68. package/dist/tools.d.ts +7 -2
  69. package/dist/tools.js +50 -13
  70. package/docs/agent-definitions.md +251 -0
  71. package/docs/agent-events.md +199 -0
  72. package/docs/agent-loops.md +217 -0
  73. package/docs/agent-session-runtime.md +20 -8
  74. package/docs/cli-rpc.md +39 -4
  75. package/docs/coding-agent-tools.md +208 -0
  76. package/docs/compaction-and-retry.md +2 -2
  77. package/docs/compaction-conformance.md +76 -0
  78. package/docs/compaction-llm.md +6 -3
  79. package/docs/compaction-observational-memory.md +4 -4
  80. package/docs/configuration-and-manifests.md +6 -1
  81. package/docs/context-and-skills.md +79 -6
  82. package/docs/contribution-discovery.md +149 -0
  83. package/docs/contribution-registries.md +9 -6
  84. package/docs/credentials-and-redaction.md +2 -0
  85. package/docs/customization.md +191 -0
  86. package/docs/database-persistence.md +407 -0
  87. package/docs/extension-authoring.md +193 -0
  88. package/docs/extension-conformance.md +80 -0
  89. package/docs/extensions.md +6 -0
  90. package/docs/host-security.md +141 -0
  91. package/docs/index.md +41 -19
  92. package/docs/input-and-prompt-assembly.md +19 -3
  93. package/docs/instruction-injection.md +183 -0
  94. package/docs/migration.md +201 -0
  95. package/docs/model-registry.md +122 -0
  96. package/docs/node-jsonl-session-store.md +5 -4
  97. package/docs/performance.md +127 -0
  98. package/docs/provider-caching.md +206 -0
  99. package/docs/provider-conformance.md +32 -5
  100. package/docs/provider-layer.md +51 -11
  101. package/docs/provider-packages.md +65 -5
  102. package/docs/provider-request-policies.md +113 -0
  103. package/docs/providers/kimi.md +22 -0
  104. package/docs/providers/neuralwatt.md +388 -0
  105. package/docs/providers/openai-compatible.md +1 -0
  106. package/docs/providers/openai.md +21 -0
  107. package/docs/providers/opencode-go.md +31 -3
  108. package/docs/providers/openrouter.md +29 -0
  109. package/docs/providers/zai.md +17 -0
  110. package/docs/public-contracts.md +87 -12
  111. package/docs/release-and-install.md +79 -27
  112. package/docs/runs-and-usage.md +236 -0
  113. package/docs/session-store-conformance.md +78 -0
  114. package/docs/session-stores-and-branching.md +10 -6
  115. package/docs/session-stores.md +126 -0
  116. package/docs/settings-auth-trust-security.md +18 -4
  117. package/docs/structured-output.md +247 -0
  118. package/docs/system-prompts.md +104 -2
  119. package/docs/tool-conformance.md +87 -0
  120. package/docs/tools.md +65 -8
  121. package/package.json +36 -2
@@ -0,0 +1,80 @@
1
+ # Extension conformance
2
+
3
+ ## What it does
4
+
5
+ Extension conformance helpers are dependency-free assertions for `Extension` adapter tests. They exercise setup execution, inert contribution registration, and setup-error handling under the kernel's error policy, without network or credentials.
6
+
7
+ Exported from `@arnilo/prism/testing/extension-conformance`:
8
+
9
+ - `assertExtensionConforms(extension, options?)`
10
+ - `ExtensionConformanceOptions`
11
+
12
+ ## When to use it
13
+
14
+ Use this helper when authoring an `Extension` package. It asserts:
15
+
16
+ - `setup` runs on load
17
+ - registered contributions land in the inert contribution registries (no side effects until host code resolves and invokes them)
18
+ - under the default `errorPolicy: "event"`, a failing setup emits a redacted `extension_error` event (when `secrets` is supplied)
19
+ - under `expectThrow: true`, a failing setup rethrows to the caller
20
+
21
+ ## Inputs / request
22
+
23
+ ```ts
24
+ import { assertExtensionConforms } from "@arnilo/prism/testing/extension-conformance";
25
+ import type { Extension } from "@arnilo/prism";
26
+
27
+ const extension: Extension = {
28
+ name: "demo",
29
+ setup(api) { api.registerSkill({ name: "brief", instructions: "Be brief." }); },
30
+ };
31
+
32
+ const kernel = await assertExtensionConforms(extension, { secrets: ["token-123"] });
33
+ ```
34
+
35
+ `ExtensionConformanceOptions`:
36
+ - `secrets?: readonly string[]` — secrets that must be redacted in the `extension_error` event (default policy only)
37
+ - `expectThrow?: boolean` — assert a failing setup rethrows under `errorPolicy: "throw"`
38
+
39
+ ## Outputs / response / events
40
+
41
+ Returns `Promise<ExtensionKernel>` so the caller can inspect registered contributions; throws a plain `Error` on the first violation. No runner.
42
+
43
+ ## Request/response example
44
+
45
+ ```ts
46
+ import { assertExtensionConforms } from "@arnilo/prism/testing/extension-conformance";
47
+
48
+ const kernel = await assertExtensionConforms(myExtension, { secrets: ["api-key"] });
49
+ // throws if a failing setup's error event leaks one of the supplied secrets.
50
+ ```
51
+
52
+ ## Implementation example
53
+
54
+ ```ts
55
+ import { assertExtensionConforms } from "@arnilo/prism/testing/extension-conformance";
56
+
57
+ const kernel = await assertExtensionConforms({
58
+ name: "demo",
59
+ setup(api) { api.registerTool({ name: "ping", execute: () => ({ value: "pong" }) }); },
60
+ });
61
+ ```
62
+
63
+ ## Extension and configuration notes
64
+
65
+ - The helper builds a fresh `ExtensionKernel` for each call; it does not reuse host kernel state.
66
+ - Contributions are inert by construction — the kernel stores envelopes and never invokes provider/tool/skill capabilities until host code resolves and calls them.
67
+ - Under `expectThrow`, the helper asserts the kernel rethrows a failing setup rather than isolating it.
68
+
69
+ ## Security and performance notes
70
+
71
+ - No credentials, no network required; pass fake secret strings.
72
+ - Redaction is exact-match (mirroring `createSecretRedactor`); the helper does not detect arbitrary secret patterns.
73
+ - The kernel does not sandbox tool/extension execution — hosts retain responsibility for trust and permission gating (see [Settings, auth, trust, security](settings-auth-trust-security.md)).
74
+
75
+ ## Related APIs
76
+
77
+ - [Extensions](extensions.md)
78
+ - [Contribution registries](contribution-registries.md)
79
+ - [Settings, auth, trust, security](settings-auth-trust-security.md)
80
+ - [Provider conformance](provider-conformance.md)
@@ -101,11 +101,13 @@ await kernel.middleware.run("provider_request", { metadata: {} });
101
101
  ## Extension and configuration notes
102
102
 
103
103
  - Extension loading is explicit. Prism does not discover packages, read manifests, or load filesystem config in the kernel.
104
+ - `AgentConfig.extensions` is host-owned metadata for compatibility; `createAgent()` and `session.run()` do not load it or call `Extension.setup()`. Load extensions with `createExtensionKernel().load(...)`, then pass selected contributions (`tools`, `context`, `skills`, middleware, etc.) into `createAgent()`.
104
105
  - Setup order is the order provided by the host.
105
106
  - The kernel writes only to explicit registries returned by `createContributionRegistries()` or provided by the host.
106
107
  - `api.registerTool()` contributes an inert `ToolDefinition` to `registries.tools`; it does not add the tool to an active tool registry, allow list, or dispatch loop.
107
108
  - `api.registerInputBuilder()`, `api.registerPromptBuilder()`, and `api.registerContextProvider()` contribute inert builders/providers; they do not replace defaults or run until the host passes selected entries to Phase 5 helpers.
108
109
  - `api.registerSkill()` contributes an inert `Skill` to `registries.skills`; it does not disclose instructions, activate referenced tools, or grant permissions until the host selects it.
110
+ - `api.registerInstructionInjector()` (Phase 30) contributes an inert `InstructionInjector` to `registries.instructionInjectors`; it grants no tools, skills, or permissions and is only applied when the host selects it via `AgentConfig.instructionInjectors`/`RunOptions.instructionInjectors`. See [Instruction injection](instruction-injection.md).
109
111
  - `api.registerProviderPackage()`, `api.registerAuthMethod()`, `api.registerProviderRequestPolicy()`, and `api.registerSystemPromptContribution()` contribute inert provider-package data; they do not load packages, resolve credentials, mutate provider payloads, or change prompts until selected by a host/runtime helper that documents that behavior.
110
112
  - `api.registerAgent()` contributes an inert `AgentDefinition`; its `create()` can call `createAgent()`, but the runtime is not started until host code resolves the definition and creates/runs a session.
111
113
  - The kernel registers middleware only into the explicit registry returned by `createMiddlewareRegistry()` or provided by the host.
@@ -115,6 +117,7 @@ await kernel.middleware.run("provider_request", { metadata: {} });
115
117
  ## Security and performance notes
116
118
 
117
119
  - No hidden global extension kernel, provider registry, credential resolver, settings provider, store, or resource loader is created.
120
+ - `AgentConfig.extensions` does not auto-execute, so constructing or running an agent cannot unexpectedly run extension code.
118
121
  - Error events use `ErrorInfo` and redact only known secret values passed in `secrets`.
119
122
  - Do not put resolved credential values in extension events, registry metadata, docs, logs, prompts, or session stores.
120
123
  - Event and middleware dispatch are ordered and dependency-free. They use no timers, background workers, filesystem discovery, network calls, provider calls, or tool execution.
@@ -122,10 +125,13 @@ await kernel.middleware.run("provider_request", { metadata: {} });
122
125
 
123
126
  ## Related APIs
124
127
 
128
+ - [Extension authoring guide](extension-authoring.md): package-author checklist for inert contributions, host activation, trust, permissions, no sandbox, and redaction.
125
129
  - [Middleware hooks](middleware-hooks.md): ordered hook registry populated by `ExtensionAPI.use()`.
126
130
  - [Provider packages](provider-packages.md): provider package and model metadata registration through `ExtensionAPI`.
127
131
  - [Contribution registries](contribution-registries.md): registry bundle populated by `ExtensionAPI`.
132
+ - [Contribution discovery (workspace)](contribution-discovery.md): filesystem-driven complement to extension registration — opt-in scan without `import()` or activation.
128
133
  - [Tools](tools.md): host activation, filtering, and dispatch for contributed tool definitions.
134
+ - [Instruction injection](instruction-injection.md): package injectors that layer instructions and context blocks for `first_turn`/`every_turn`/`on_input` without granting tools.
129
135
  - [Input and prompt assembly](input-and-prompt-assembly.md): host selection for contributed input/prompt builders.
130
136
  - [System prompts](system-prompts.md): host selection for contributed system prompt layers.
131
137
  - [Context and skills](context-and-skills.md): host selection and tool checks for contributed context providers and skills.
@@ -0,0 +1,141 @@
1
+ # Host security guide
2
+
3
+ ## What it does
4
+
5
+ This guide is a fail-closed checklist for apps embedding Prism. It maps host security responsibilities to existing Prism APIs for credentials, settings, redaction, trust roots, permission policies, session and ledger persistence, extension loading, and tool validation.
6
+
7
+ Prism supplies seams and checks. The host owns policy decisions, secret sources, durable storage, approval UI, sandboxing, and which contributed capabilities become active.
8
+
9
+ ## When to use it
10
+
11
+ Use this guide before exposing an agent to users, third-party extensions, durable storage, or real provider credentials.
12
+
13
+ Do not use it as a replacement for product threat modeling, process/container sandboxing, secret management, database access control, or provider-side policy. Prism does not sandbox tools/extensions and does not detect arbitrary secrets.
14
+
15
+ ## Inputs / request
16
+
17
+ Start from explicit host inputs. Do not let runtime code discover security state implicitly.
18
+
19
+ | Security input | Host-owned source | Prism API / page |
20
+ | --- | --- | --- |
21
+ | Settings | app config object or caller-named files | `createStaticSettingsProvider`, `loadSettingsFiles()` |
22
+ | Credentials | runtime override, memory store, vault/env object | `createExplicitCredentialResolver`, `createEnvCredentialResolver`, `resolveCredentialValue()` |
23
+ | Redaction values | exact known credential strings | `createSecretRedactor`, `redactSecrets()` |
24
+ | Trust roots | app-selected directories/resources | `createPathTrustPolicy`, `assertTrusted()` |
25
+ | Permission decisions | allow/deny rules or approval UI result | `createStaticPermissionPolicy`, `assertPermission()` |
26
+ | Tool allow-list | active tools for this agent/session/run | `createToolRegistry`, `filterTools()`, `dispatchToolCall()` |
27
+ | Tool argument rules | host validator | `AgentConfig.validator`, `RunOptions.validate`, `ToolValidator` |
28
+ | Durable history | host database adapter | `SessionStore`, `assertSessionStoreConforms()` |
29
+ | Durable audit | host ledger adapter | `RunLedger`, `redactRunLedgerRecord()` |
30
+ | Extensions | explicit package imports only | `createExtensionKernel`, `ExtensionAPI` |
31
+
32
+ ## Outputs / response / events
33
+
34
+ Security controls fail closed before side effects when wired at the guarded edge:
35
+
36
+ - trust denial blocks resource/extension reads before load/use
37
+ - permission denial blocks extension setup, resource loading, and tool execution
38
+ - unknown or denied tools emit `tool_execution_blocked`
39
+ - validator failures emit `tool_execution_blocked` with `validation_failed`
40
+ - configured redactors scrub provider requests, agent events, session entries, ledger records, tool errors, extension errors, and injector context
41
+
42
+ These checks are explicit function calls during load, assembly, dispatch, append, or run handling. Prism adds no background watchers, filesystem scanners, network probes, credential polling, or automatic extension discovery.
43
+
44
+ ## Request/response example
45
+
46
+ ```json
47
+ {
48
+ "credentialSource": "caller-owned env object",
49
+ "trustedRoots": ["/workspace/app"],
50
+ "allowedActions": ["tool:notes/read:execute", "extension:acme:setup"],
51
+ "activeTools": ["notes/read"],
52
+ "toolValidation": "host ToolValidator",
53
+ "persistence": "redacted SessionStore + RunLedger"
54
+ }
55
+ ```
56
+
57
+ The JSON above is an app security plan, not a Prism config format. Hosts translate each field into the explicit APIs listed in this guide.
58
+
59
+ ## Implementation example
60
+
61
+ ```ts
62
+ import {
63
+ createEnvCredentialResolver,
64
+ createSecretRedactor,
65
+ createStaticPermissionPolicy,
66
+ createToolRegistry,
67
+ filterTools,
68
+ resolveCredentialValue,
69
+ type ToolDefinition,
70
+ type ToolValidator,
71
+ } from "@arnilo/prism";
72
+ import { createPathTrustPolicy } from "@arnilo/prism/node/trust";
73
+
74
+ const workspaceRoot = "/workspace/app";
75
+ const env = { DEMO_API_KEY: "fake-demo-key" }; // docs-only placeholder
76
+ const credentials = createEnvCredentialResolver(env, { demo: "DEMO_API_KEY" });
77
+ const apiKey = await resolveCredentialValue(credentials, { provider: "demo", name: "apiKey" });
78
+
79
+ const redactor = createSecretRedactor([apiKey]);
80
+ const permission = createStaticPermissionPolicy({
81
+ allow: ["tool:notes/read:execute", "extension:acme:setup"],
82
+ });
83
+ const trust = createPathTrustPolicy({ trustedRoots: [workspaceRoot] });
84
+
85
+ const readNotes: ToolDefinition = {
86
+ name: "notes/read",
87
+ parameters: { type: "object", properties: { id: { type: "string" } } },
88
+ execute(args, context) {
89
+ return { toolCallId: context.toolCallId, name: "notes/read", value: { id: args.id } };
90
+ },
91
+ };
92
+
93
+ const validate: ToolValidator = (_tool, args) =>
94
+ typeof args.id === "string" && args.id.length <= 100
95
+ ? undefined
96
+ : "id must be a short string";
97
+
98
+ const tools = createToolRegistry(filterTools([readNotes], { allow: ["notes/read"] }), { duplicate: "error" });
99
+
100
+ void { apiKey, redactor, permission, trust, tools, validate };
101
+ ```
102
+
103
+ Wire those values where they matter: provider adapters receive the resolved credential, agents/runs receive `redactor`, tool dispatch receives `permission` and `validate`, resource/extension loaders receive `trust` and `permission`, and durable adapters receive already-redacted entries/records.
104
+
105
+ ## Extension and configuration notes
106
+
107
+ - Keep security state explicit. `AgentConfig.settings` and `AgentConfig.credentials` are host-owned metadata; `createAgent()` and `session.run()` do not automatically call `settings.get()` or `credentials.resolve()`.
108
+ - Resolve credentials at the provider/request edge, as late as possible. Do not put resolved credentials in configs, manifests, registries, prompts, messages, events, session entries, run ledgers, idempotency keys, cache keys, or logs.
109
+ - Use `createExplicitCredentialResolver()` to document source order such as runtime override → stored credential → caller-supplied env object → fallback.
110
+ - Use `createEnvCredentialResolver()` only with an object the host passes in. Prism does not read `process.env` for credentials.
111
+ - Use `createPathTrustPolicy()` for workspace/resource roots and fail closed on symlink escapes.
112
+ - Use `createContributionRegistries({ duplicate: "error" })` and prefixed names for third-party packages to prevent silent shadowing.
113
+ - Extension contributions are inert until selected. Loading an extension package runs its `setup(api)` code, so hosts should load only trusted packages or isolate untrusted code outside Prism.
114
+ - Skills and instruction injectors grant no tools, permissions, validators, or resource access. Host-active tools and permission policies still decide execution.
115
+ - For production persistence, implement a database-backed `SessionStore`/`RunLedger`, run `assertSessionStoreConforms()` against the store, and follow the database schema guidance. Do not ship provider instances, credential resolvers, or secrets into durable rows.
116
+
117
+ ## Security and performance notes
118
+
119
+ - Fail closed: unknown providers, unknown tools, denied tools, invalid tool arguments, missing skill tool dependencies, trust failures, permission failures, append conflicts, and validator failures should stop the unsafe action.
120
+ - Prism does not sandbox host tools, extensions, provider adapters, credential resolvers, or custom middleware. Use OS/container/process isolation when code is untrusted.
121
+ - Redaction is exact known-secret replacement only. It is not arbitrary secret detection, entropy scanning, or DLP.
122
+ - Known secrets must be passed into redactors before data is emitted or persisted. Redact again in host adapters if they transform records after Prism redaction.
123
+ - Tool `parameters` metadata is not validation. Add a `ToolValidator` or validate inside the tool before side effects.
124
+ - Permission checks happen before tool validation and before `tool.execute()`. Middleware cannot grant permission by renaming a tool.
125
+ - Session stores and ledgers receive redacted values when a redactor is active, but durable storage remains host-owned. Enforce tenant/account/user ownership and retention in the database layer.
126
+ - Provider-owned auth/content/session/cache/security headers win over caller headers in adapters that merge headers.
127
+ - Security checks are bounded explicit calls on the active path. Prism adds no hidden global middleware, background workers, watchers, network calls, or filesystem scans.
128
+
129
+ ## Related APIs
130
+
131
+ - [Settings, auth, trust, and security controls](settings-auth-trust-security.md): low-level helpers and boundary hardening table.
132
+ - [Credentials and redaction](credentials-and-redaction.md): credential resolver order, caller-supplied env objects, OAuth refresh, exact redaction, and no persistent secret store.
133
+ - [Tools](tools.md): active tool registry, allow/deny filters, permission order, validator order, blocked events, and no sandbox.
134
+ - [Extension authoring guide](extension-authoring.md): inert contribution package boundary and extension loading security notes.
135
+ - [Extension kernel and event bus](extensions.md): `createExtensionKernel`, setup error redaction/rethrow, and permission-gated extension setup.
136
+ - [Contribution discovery](contribution-discovery.md): opt-in realpath-contained scanner that imports nothing and activates nothing.
137
+ - [Instruction injection](instruction-injection.md): redacted injector context and no capability grants.
138
+ - [Session stores](session-stores.md): durable session store contract and secret/persistence boundaries.
139
+ - [Runs and usage ledger](runs-and-usage.md): redacted run/event/tool/usage ledger records.
140
+ - [Database persistence](database-persistence.md): production schema, ownership, indexes, retention, and adapter readiness checklist.
141
+ - [Provider caching](provider-caching.md): cache keys and provider-owned header safety rules.
package/docs/index.md CHANGED
@@ -6,50 +6,72 @@ Prism is a TypeScript/Node.js agent harness. Host apps and extension packages ow
6
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
7
 
8
8
  ## Agent/session runtime
9
- - [Agent/session runtime](agent-session-runtime.md): create agents and sessions, run prompts, and subscribe to normalized session events.
9
+ - [Agent/session runtime](agent-session-runtime.md): create agents and sessions, run prompts, subscribe to normalized events, and see which `AgentConfig` fields are runtime-consumed vs host-owned metadata. Covers tool-call loop transcript shape and prior-reasoning preservation across turns.
10
+ - [Agent definitions](agent-definitions.md): resolve declarative `AgentDefinition` values via `resolveAgentDefinition`, and turn app-config `<configRoot>/agents/<name>/AGENT.md` bundles into runnable agents via `discoverAgentBundles` / `resolveAgentBundle` (explicit tool/skill activation by name, fail-closed omitted capabilities, migration-only `activateAllCapabilities`, strict duplicate scope checks, configurable prompt layers, no auto-discovery).
11
+ - [Agent loops](agent-loops.md): replaceable per-run control loops — `singleShotLoop` default and `generate-validate-revise` with host-supplied `validator`/`parser`/`repairer` callbacks.
12
+ - [Agent events](agent-events.md): the `AgentEvent` stream — agent/turn/message (including live `tool_call_delta` fragments), tool execution, queue/subscriber overflow, compaction/retry, artifact validation/refinement, and error variants, redacted via `redactAgentEvent`.
13
+ - [Runs and usage ledger](runs-and-usage.md): `RunLedger` adapter for durable run, event, tool-call, usage persistence, cache diagnostics, ownership/idempotency, and redaction guidance.
14
+ - [Performance limits](performance.md): bounded live subscriber queues, branch-read pagination expectations, JSONL/dev-store limits, and production sizing assumptions.
15
+ - [Structured output](structured-output.md): the `Artifact*` seam (parser/validator/repairer, host-defined `T`) — the only typed-output path from a loop, with a Synapta-style schema→`ArtifactValidation` mapping example and an end-to-end third-party integration walkthrough.
10
16
 
11
17
  ## Compaction/session memory
12
18
  - [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.
19
+ - [LLM compaction package](compaction-llm.md): optional provider-backed compaction strategy package with max-output budgets mapped through `model.parameters.maxTokens` to provider wire fields.
20
+ - [Observational memory compaction package](compaction-observational-memory.md): optional source-backed memory, owned runtime append callback, provider-valid worker transcripts, fast compaction, recall tool, and status/view command package.
21
+ - [Session stores](session-stores.md): `SessionStore` contract, `SessionAppendOptions`, `SessionAppendConflictError`, branch handles, `readBranchPath`, and dev-vs-production branch reads — start here for session persistence.
22
+ - [Session stores and branching](session-stores-and-branching.md): detailed branch semantics and helper reference (kept for compatibility; links back to the canonical atomic append / branch-handle sections).
23
+ - [Database persistence](database-persistence.md): production persistence contracts, conditional append transaction pattern, idempotency indexes, `readBranchPath`, reference relational schema, retention, migrations, and NoSQL mapping.
24
+ - [Migration guide](migration.md): the two cross-cutting app migrations in one place — in-memory/JSONL → database-backed `ProductionPersistenceStore` persistence (+ `RunLedger`) and permissive capability defaults → Phase 38 explicit `tools`/`skills` activation, with before/after shapes and links to the detailed pages.
25
+ - [Node JSONL session store](node-jsonl-session-store.md): development-only JSONL file adapter for single-process Node hosts; no cross-process safety.
17
26
 
18
27
  ## 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).
28
+ - [Provider layer](provider-layer.md): register and resolve host-owned providers/models, choose replace-or-error duplicate policy, create provider events, stream/reconstruct tool-call deltas, use generic provider request options, and test with the mock provider; deprecated provider-level timeout/retry hints point to runtime abort/retry.
29
+ - [Model registry](model-registry.md): register and resolve `ModelConfig` records with capabilities, limits, cost, cache support metadata, compat data, and duplicate policy.
30
+ - [Provider caching](provider-caching.md): use `PromptCacheHints`, `PromptCacheBreakpoint`, `ModelCacheCapabilities`, cache-aware stable-prefix guidance, and shared cache diagnostics helpers; includes a per-provider explicit/implicit cache matrix for OpenAI, OpenRouter, OpenCode Go, Z.AI, Kimi, and NeuralWatt; cache hints are best-effort and cache keys are never secrets.
31
+ - [Provider request policies](provider-request-policies.md): chain `ProviderRequestPolicy` hooks, use `createSessionCachePolicy`, and merge legacy/structured cache options safely.
32
+ - [Provider packages](provider-packages.md): define explicit provider packages, model metadata, auth descriptors, request/cache policies, and provider-owned header precedence without package discovery or provider-specific core behavior; includes a first-party cache behavior summary.
33
+ - 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), [`@arnilo/prism-provider-kimi`](providers/kimi.md), and [`@arnilo/prism-provider-neuralwatt`](providers/neuralwatt.md) with implicit vLLM prefix caching, reasoning controls (`reasoning_effort`/`thinking_token_budget`/`enable_thinking`/`preserve_thinking`/`clear_thinking`), reasoning preservation, OpenAI-style tool-call loop, quota, telemetry, and retry classification helpers.
22
34
  - [OpenAI-compatible provider](providers/openai-compatible.md): optional provider subpath using native or injected `fetch` for Chat Completions streaming.
23
35
 
24
36
  ## 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.
37
+ - [SDK customization guide](customization.md): map provider resolution, middleware, context, builders, injectors, loops, compaction, retry, stores, and skills to explicit host-wired APIs.
38
+ - [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, provider-input assembly, legacy default order, and opt-in cache-aware ordering.
39
+ - [System prompts](system-prompts.md): compose explicit user/package/app/run system prompt layers, auto-load the standard `AGENTS.md` (workspace) / `SYSTEM.md` prompt files via the Node `loadSystemPromptFiles` loader (trust-gated for `AGENTS.md`), and append `SYSTEM.md` → per-agent `AGENT.md` body → repo `AGENTS.md` layers from a discovered agent bundle via `resolveAgentBundle`.
40
+ - [Instruction injection](instruction-injection.md): register package injectors that layer redacted instructions/context blocks without granting tools, permissions, or resource escapes.
41
+ - [Context and skills](context-and-skills.md): resolve ordered context providers and keep context/skill selection host-owned; omitted declarative skills stay inactive by default, `toolNames` fail closed before provider turns, and strict skill registries prevent silent shadowing.
28
42
 
29
43
  ## Tools
30
- - [Tools](tools.md): register host-owned active tools, apply exact allow/deny filtering, and dispatch tool calls.
44
+ - [Tools](tools.md): register host-owned active tools with replace-or-error duplicate policy, apply exact allow/deny filtering, and dispatch tool calls.
45
+ - [Coding agent tools](coding-agent-tools.md): optional first-party package `@arnilo/prism-coding-agent` providing `shell`, `read`, `write`, and `edit` tools (ported from pi) as `ToolDefinition`s a host registers; pluggable operation backends, per-path mutation serialization, and read-only/coding aggregators. Host shell/filesystem access — gate with permission/trust policies.
31
46
 
32
47
  ## Extensions/plugins
33
- - [Contribution registries](contribution-registries.md): explicit host-owned registries for extension/package contributions without hidden globals.
48
+ - [Contribution discovery (workspace)](contribution-discovery.md): opt-in, realpath-contained directory scanner turning `SKILL.md`/`manifest.json` into inert `DiscoveredContribution` envelopes the host registers — no `import()`, no auto-activate, no provider scanning. (Per-agent `AGENT.md` bundles live under an app-controlled `configRoot`; see [Agent definitions](agent-definitions.md).)
49
+ - [Contribution registries](contribution-registries.md): explicit host-owned registries for extension/package contributions without hidden globals, with `duplicate: "error"` strict mode for provider/model/tool/skill shadowing prevention.
34
50
  - [Extension kernel and event bus](extensions.md): load host-provided extensions in order, register contributions, emit lifecycle events, and isolate extension errors.
51
+ - [Extension authoring guide](extension-authoring.md): publish third-party extension packages that register inert contributions and show host-owned activation, trust, permissions, redaction, and no-sandbox boundaries.
35
52
  - [Middleware hooks](middleware-hooks.md): ordered hook registry for provider, input, context, tool, retry, compaction, and session lifecycle boundaries.
36
53
 
37
54
  ## Configuration/manifests
38
- - [Configuration and manifests](configuration-and-manifests.md): merge in-memory JSON config layers and validate data-only package manifests.
55
+ - [Configuration and manifests](configuration-and-manifests.md): merge in-memory JSON config layers and validate data-only package manifests with prototype-pollution key rejection.
39
56
  - [Node filesystem config loader](node-filesystem-config.md): explicitly read caller-named JSON config files in Node hosts.
40
57
  - [Resource loading](resource-loading.md): decode text, JSON, and manifest resources through caller-provided loaders.
41
58
 
42
59
  ## CLI/RPC
43
- - [CLI/RPC](cli-rpc.md): Run print/json modes and LF-delimited RPC over the public AgentSession runtime.
60
+ - [CLI/RPC](cli-rpc.md): Run print/json modes and LF-delimited RPC over the public AgentSession runtime, including branch-handle results, fixed `forkSession`, and `checkout`.
44
61
 
45
62
  ## 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.
63
+ - [Host security guide](host-security.md): fail-closed checklist for credentials, settings, redaction, trust roots, permission policies, persistence, extension loading, and tool validation.
64
+ - [Security/auth/trust](settings-auth-trust-security.md): settings providers, credential helpers, trust/permission policies, redaction controls, host-owned `AgentConfig.settings`/`credentials`, and security-boundary hardening summary.
65
+ - [Credentials and redaction](credentials-and-redaction.md): compose explicit credential resolver order, use caller-supplied env objects/OAuth refresh helpers, avoid eager `AgentConfig.credentials` resolution, and redact known secret values.
48
66
 
49
67
  ## Testing and examples
50
68
  - [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).
69
+ - [Provider conformance](provider-conformance.md): run network-free provider adapter assertions (stream order, abort, tool-call reconstruction, cache usage, content coverage, protected header ownership, secret leak) from `@arnilo/prism/testing/provider-conformance`.
70
+ - [Session store conformance](session-store-conformance.md): assert any `SessionStore` adapter satisfies append/idempotency/conflict/branch invariants from `@arnilo/prism/testing/session-store-conformance`.
71
+ - [Compaction conformance](compaction-conformance.md): assert any `CompactionStrategy` returns a non-empty redacted summary and observes abort from `@arnilo/prism/testing/compaction-conformance`.
72
+ - [Tool conformance](tool-conformance.md): assert the tool-dispatch blocked-reason matrix (unknown/denied/invalid/permission/validator) and success path from `@arnilo/prism/testing/tool-conformance`.
73
+ - [Extension conformance](extension-conformance.md): assert an `Extension` setup runs, contributions stay inert, and setup errors are redacted or rethrown from `@arnilo/prism/testing/extension-conformance`.
74
+ - `examples/`: compile-checked typed examples and runnable mock demos (SDK basics, provider registration, auth, tools, cache-aware prompt assembly, NeuralWatt agent run, stores/branching, compaction, observational-memory recall, structured-output/artifact-loop, CLI, RPC).
53
75
 
54
76
  ## Release and install
55
77
  - [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.
@@ -18,6 +18,7 @@ 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
21
22
  systemInstructions: "Be accurate.",
22
23
  developerInstructions: "Cite supplied context only.",
23
24
  history,
@@ -46,6 +47,7 @@ import { assembleProviderInput, createDefaultPromptBuilder } from "@arnilo/prism
46
47
  const request = await assembleProviderInput({
47
48
  model: { provider: "mock", model: "demo" },
48
49
  input: "Explain this file",
50
+ inputLayout: "cache_aware",
49
51
  contextProviders: [projectContext],
50
52
  promptBuilder: createDefaultPromptBuilder(),
51
53
  tools: activeTools,
@@ -56,7 +58,8 @@ Useful exported types:
56
58
 
57
59
  - `AgentInput`: `string | Message | readonly Message[]`.
58
60
  - `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.
61
+ - `InputAssemblyLayout`: `"legacy" | "cache_aware"`; legacy is default.
62
+ - `DefaultInputBuildContext`: optional input layout, instructions, history, summaries, attachments, resource loader/URIs, tool results, middleware, ids, metadata, and abort signal.
60
63
  - `InputAttachment`: already-loaded text/content or an explicit URI loaded through a caller-provided `ResourceLoader`.
61
64
  - `PromptInstruction`: labeled system instruction text.
62
65
  - `DefaultPromptBuilder`: the default `PromptBuilder`.
@@ -69,10 +72,18 @@ The builder returns `readonly Message[]`.
69
72
 
70
73
  - String input becomes one user text message.
71
74
  - `Message` and `Message[]` input are preserved.
75
+ - Legacy layout is the default. Set `inputLayout: "cache_aware"` on the default builder, `assembleProviderInput()`, `AgentConfig`, or `RunOptions` to pass the cache-aware layout preference without replacing the builder.
76
+
77
+ | Layout | Input message order |
78
+ | --- | --- |
79
+ | `legacy` | instructions → summaries → history → current input → attachments/resources → tool results |
80
+ | `cache_aware` | instructions → attachments/resources → summaries → history → tool results → current input |
81
+
82
+ The default prompt builder still prepends context, selected skills, and tool declarations before those input messages. Cache-aware ordering gives cache-capable providers a stable prefix only while those stable inputs stay byte-stable; changing tools, context, resources, summaries, history, or attachments changes the prefix too.
72
83
  - History is prepended before current input.
73
84
  - Instructions and summaries are system messages; compacted branch summaries from `rebuildSessionContext()` use the same path.
74
85
  - 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.
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.
76
87
  - Middleware runs only when `middleware` is supplied in the context.
77
88
  - `assembleProviderInput()` returns a `ProviderRequest` with the caller's model/tools/provider options/metadata/signal and composed messages/context.
78
89
  - `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" }`.
@@ -123,8 +134,12 @@ const messages = await createDefaultInputBuilder().build(prompt, {
123
134
  },
124
135
  middleware,
125
136
  });
137
+
138
+ await session.run("Explain this", { inputLayout: "cache_aware" });
126
139
  ```
127
140
 
141
+ Cache-aware mode is opt-in; hosts that do nothing keep legacy order.
142
+
128
143
  ## Extension and configuration notes
129
144
 
130
145
  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.
@@ -147,7 +162,7 @@ const request = await assembleProviderInput({
147
162
 
148
163
  ## Security and performance notes
149
164
 
150
- - The builder is linear in supplied messages, attachments, resources, and tool results.
165
+ - The builder is linear in supplied messages, attachments, resources, and tool results. Layout selection is one flattening branch over already-built groups.
151
166
  - Template expansion is dependency-free string replacement over `{{name}}` variables. It does not evaluate expressions, filters, loops, partials, JavaScript, globals, or prototype properties.
152
167
  - It performs no provider calls, tool execution, credential resolution, package discovery, filesystem scan, network access, timers, or watchers.
153
168
  - URI attachments/resources load only through the caller-provided `ResourceLoader`.
@@ -157,6 +172,7 @@ const request = await assembleProviderInput({
157
172
 
158
173
  ## Related APIs
159
174
 
175
+ - [SDK customization guide](customization.md): high-level map of replaceable provider resolution, middleware, context, builder, injector, loop, compaction, retry, store, and skill seams.
160
176
  - [Public contracts](public-contracts.md): `Message`, `ContentBlock`, `InputBuilder`, `InputBuildContext`, `ToolResult`, and `ResourceLoader` shapes.
161
177
  - [Context and skills](context-and-skills.md): ordered context resolution feeding prompt composition.
162
178
  - [Resource loading](resource-loading.md): `loadTextResource()` behavior used for explicit URI resources.
@@ -0,0 +1,183 @@
1
+ # Instruction injection
2
+
3
+ ## What it does
4
+
5
+ Instruction injectors let a package modify how context is formulated and inject its own instructions to modify agent behavior — on the first turn, every turn, or in response to user input — without forking the input/prompt pipeline and without hidden globals. Each injector contributes only `instructions` (text) and `contextBlocks`; it cannot register tools, skills, or permissions, and cannot bypass the validator or the permission gate.
6
+
7
+ Injectors are the package-side complement to the host-owned `systemInstructions` base path: the host sets base instructions, then selected package injectors layer additional instructions and context blocks per turn.
8
+
9
+ ## When to use it
10
+
11
+ - A package wants to bias the model toward a response format (e.g. "answer in JSON") every turn.
12
+ - A package wants to inject project context (e.g. a repo summary) on the first turn only.
13
+ - A package wants to react to user input (via a `predicate`) without re-authoring the assembler.
14
+ - A package wants to ship a discoverable `.agents/instructions/<name>/` bundle that hosts opt into by name.
15
+
16
+ Injectors are **not** a way to grant tool access, change credentials, or mutate provider request options.
17
+
18
+ ## Inputs / request
19
+
20
+ Injectors implement `InstructionInjector`:
21
+
22
+ ```ts
23
+ type InstructionTiming = "first_turn" | "every_turn" | "on_input";
24
+
25
+ interface InstructionContext {
26
+ readonly sessionId: string;
27
+ readonly runId: string;
28
+ readonly turn: number; // 1-based; undefined is treated as turn 1
29
+ readonly input: readonly Message[]; // already redacted by the runtime
30
+ readonly history: readonly Message[]; // redacted messages from prior turns
31
+ readonly metadata: Readonly<Record<string, unknown>>;
32
+ readonly signal: AbortSignal;
33
+ }
34
+
35
+ interface InstructionContribution {
36
+ readonly instructions?: string;
37
+ readonly contextBlocks?: readonly ContextBlock[];
38
+ readonly when: InstructionTiming;
39
+ readonly predicate?: (ctx: InstructionContext) => boolean;
40
+ }
41
+
42
+ interface InstructionInjector {
43
+ readonly name: string;
44
+ readonly description?: string;
45
+ apply(ctx: InstructionContext): InstructionContribution;
46
+ }
47
+ ```
48
+
49
+ `InstructionContext` fields are already redacted by the runtime before `apply` runs: current input is run through the active `AgentConfig.redactor` / `RunOptions.redactor` during assembly, and history holds previously-redacted messages. Direct `assembleProviderInput()` callers that use injectors should pass the same `redactor` option to keep this boundary.
50
+
51
+ ### Lifecycle
52
+
53
+ | `when` | `predicate` | Applied when |
54
+ |---|---|---|
55
+ | `first_turn` | ignored | `ctx.turn === 1` |
56
+ | `every_turn` | ignored | every turn |
57
+ | `on_input` | absent | every turn (default) |
58
+ | `on_input` | present | turns where `predicate(ctx)` returns `true` |
59
+
60
+ Only `instructions` and `contextBlocks` are honored from a contribution; other fields grant nothing (see [Security and performance notes](#security-and-performance-notes)).
61
+
62
+ ## Outputs / response / events
63
+
64
+ Injectors do not emit events. Their output is folded into the assembled `ProviderRequest`:
65
+
66
+ - **Instructions** layer via `composeSystemPrompt(injectorContributions, { base: systemInstructions })` as `source: "package"`, `mode: "append"`. Host base instructions come first, then injector package instructions appended. This keeps a single prompt-composition code path (no parallel prompt code in the assembler).
67
+ - **Context blocks** merge via `resolveContextProviders`, appended after host+skill provider blocks, before the context middleware hook runs. `ponytail:` the assembler threads `injectedBlocks` into `resolveContextProviders` so the existing context middleware flow is untouched and the diff stays minimal.
68
+
69
+ `runInstructionInjectors(injectors, ctx)` runs each selected injector against a turn-local `InstructionContext`, returning `{ instructions: SystemPromptContribution[]; contextBlocks: ContextBlock[] }`. It aborts on `ctx.signal`.
70
+
71
+ ## Request/response example
72
+
73
+ ```ts
74
+ const jsonInjector: InstructionInjector = {
75
+ name: "json-always",
76
+ apply: () => ({ instructions: "Always answer in JSON.", when: "every_turn" }),
77
+ };
78
+
79
+ const projectContext: InstructionInjector = {
80
+ name: "project-context",
81
+ apply: () => ({
82
+ contextBlocks: [{ title: "Repo", content: "Prism monorepo — see docs/." }],
83
+ when: "first_turn",
84
+ }),
85
+ };
86
+
87
+ const onInputJson: InstructionInjector = {
88
+ name: "json-on-json-input",
89
+ apply: (ctx) => ({
90
+ instructions: "Reply with JSON because the user asked for JSON.",
91
+ when: "on_input",
92
+ predicate: (c) => c.input.some((m) => /json/i.test(JSON.stringify(m.content))),
93
+ }),
94
+ };
95
+ ```
96
+
97
+ ## Implementation example
98
+
99
+ ```ts
100
+ import { createAgent, createMockProvider, providerDone, createSecretRedactor } from "@arnilo/prism";
101
+
102
+ const jsonInjector = { name: "json-always", apply: () => ({ instructions: "Answer in JSON.", when: "every_turn" as const }) };
103
+
104
+ const session = createAgent({
105
+ model: { provider: "mock", model: "demo" },
106
+ provider: createMockProvider([providerDone()]),
107
+ instructions: "You are helpful.",
108
+ instructionInjectors: [jsonInjector],
109
+ }).createSession();
110
+
111
+ await session.run("List primes under 10.");
112
+ ```
113
+
114
+ ### Selection and override semantics
115
+
116
+ `AgentConfig.instructionInjectors` configures a base injector list; `RunOptions.instructionInjectors` overrides it (last wins), mirroring `activeSkills`. `resolveInstructionInjectors` resolves names against a registry fail-closed:
117
+
118
+ ```ts
119
+ import { resolveInstructionInjectors } from "@arnilo/prism";
120
+
121
+ const injectors = resolveInstructionInjectors({ registry, names: ["json-always", "project-context"] });
122
+ // Unknown name throws: Error: Unknown instruction injector: <name>
123
+ ```
124
+
125
+ ### Phase 29 discovery loading
126
+
127
+ A discovered `.agents/instructions/<name>/manifest.json` (see [Contribution discovery](contribution-discovery.md)) becomes a live injector via the host-owned Node adapter — core performs no `import()`:
128
+
129
+ ```ts
130
+ import { registerDiscoveredInstructionInjectors } from "@arnilo/prism/node/instruction-injectors";
131
+
132
+ // markdown-only (no `module` field) → static every_turn injector reading resource text;
133
+ // module-referenced → host-supplied moduleLoader (skipped when absent).
134
+ // resourceTrust is only needed for absolute/outside resource files.
135
+ await registerDiscoveredInstructionInjectors(registries, discovered, { moduleLoader, resourceTrust });
136
+ ```
137
+
138
+ The CLI wires this under `--discover` (see below). Hosts embedding the SDK keep the registry empty and supply injectors directly on `AgentConfig`/`RunOptions`.
139
+
140
+ ### CLI and RPC
141
+
142
+ CLI:
143
+
144
+ ```
145
+ # select a discovered injector by name (repeatable)
146
+ prism --discover --discover-kinds instructions --instruction json-always -p "Hi" --provider mock
147
+
148
+ # load a markdown file as a static every_turn injector (repeatable)
149
+ prism --injector-file ./rules/json.md -p "Hi" --provider mock
150
+
151
+ # disable all injectors for a run (including AgentConfig injectors)
152
+ prism --instruction false -p "Hi" --provider mock
153
+ ```
154
+
155
+ `--instruction false` disables; `--instruction <name>` fails closed (exit 1) on an unknown name. Names resolve against discovered injectors only when `--discover` ran; without discovery, `--instruction` requires a name present in the session's `instructionInjectors` (host-supplied).
156
+
157
+ RPC: `prompt`/`followUp` params accept an optional `instructionInjectors: readonly string[]` field. Names resolve against the `instructionInjectors` registry passed to `runRpcServer({ instructionInjectors })`; an unknown name fails closed with a correlated error response and no provider call.
158
+
159
+ ## Extension and configuration notes
160
+
161
+ - Register via `ExtensionAPI.registerInstructionInjector(injector)` (Phase 30). Each injector is stored by `injector.name` (last-write-wins).
162
+ - Select on `AgentConfig.instructionInjectors` or `RunOptions.instructionInjectors` (`RunOptions` wins). `RunOptions.instructionInjectors` is a list of `InstructionInjector` instances; hosts embed names by passing instances resolved through `resolveInstructionInjectors`.
163
+ - Manifest `kind: "instructionInjector"` (Phase 30) declares contributions data-only; discovery of `kind: "instructions"` is the filesystem vehicle (see [Configuration and manifests](configuration-and-manifests.md)).
164
+ - `turn` is plumbed through `LoopContext.assemble(nextInput, toolResults?, turn?)` (Phase 30) so injectors see the loop-local turn, not a stale value.
165
+
166
+ ## Security and performance notes
167
+
168
+ - **No privilege grant:** `InstructionContribution` exposes only `instructions`/`contextBlocks`/`when`/`predicate`. There is no `tools`, `skills`, `permissions`, or `execute` field; a malformed contribution smuggling those fields contributes only `instructions`. Registering an injector adds entries only to `instructionInjectors`; `tools`/`skills`/`contextProviders`/`systemPromptContributions` stay empty.
169
+ - **Cannot bypass validator or permissions:** injectors are layered into prompt assembly; tool dispatch still re-checks the active registry (`unknown_tool`), filters, arguments, the permission assertion, and `validate` (Phase 4/25/26).
170
+ - **Secrets never enter history/events:** secrets in injector-produced `instructions`/`contextBlocks` are redacted in the outgoing `ProviderRequest` (via `redactProviderRequest`) and in emitted events (via `redactAgentEvent`). Do not put secrets in injector text at authoring time; the redactor is a backstop, not an invitation.
171
+ - **Resource containment:** markdown-only discovered injectors resolve `resource` relative to their contribution directory and realpath-check it before read. Relative `..`, absolute paths, or symlinks that escape the contribution directory are rejected unless the host passes an explicit `resourceTrust` policy; `permission` is still checked before reading.
172
+ - No hidden globals: injectors are resolved explicitly per run; nothing is auto-activated or auto-imported by core.
173
+
174
+ ## Related APIs
175
+
176
+ - [Input and prompt assembly](input-and-prompt-assembly.md): default prompt builder and `assembleProviderInput`, where injector instructions/blocks are merged.
177
+ - [System prompts](system-prompts.md): `composeSystemPrompt` and the `package`/`app`/`user`/`run` layering injectors layer into.
178
+ - [Context and skills](context-and-skills.md): `resolveContextProviders` merge order and skill `context`.
179
+ - [Contribution registries](contribution-registries.md): `instructionInjectors` registry.
180
+ - [Contribution discovery](contribution-discovery.md): `.agents/instructions/<name>/` discovery and the host-owned `loadInstructionInjector` adapter.
181
+ - [Extensions](extensions.md): `registerInstructionInjector` in the contribution-kinds list.
182
+ - [CLI and RPC](cli-rpc.md): `--instruction`/`--injector-file` flags and the RPC `instructionInjectors` field.
183
+ - [Credentials and redaction](credentials-and-redaction.md): `createSecretRedactor`, `redactProviderRequest`, `redactAgentEvent`.