@arnilo/prism 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +139 -0
  4. package/dist/agents.d.ts +5 -0
  5. package/dist/agents.js +439 -0
  6. package/dist/cli-runner.d.ts +33 -0
  7. package/dist/cli-runner.js +167 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +10 -0
  10. package/dist/compaction.d.ts +9 -0
  11. package/dist/compaction.js +67 -0
  12. package/dist/config.d.ts +17 -0
  13. package/dist/config.js +69 -0
  14. package/dist/contracts.d.ts +670 -0
  15. package/dist/contracts.js +2 -0
  16. package/dist/contributions.d.ts +35 -0
  17. package/dist/contributions.js +47 -0
  18. package/dist/credentials.d.ts +22 -0
  19. package/dist/credentials.js +63 -0
  20. package/dist/extensions.d.ts +25 -0
  21. package/dist/extensions.js +131 -0
  22. package/dist/index.d.ts +47 -0
  23. package/dist/index.js +28 -0
  24. package/dist/input.d.ts +57 -0
  25. package/dist/input.js +225 -0
  26. package/dist/manifests.d.ts +28 -0
  27. package/dist/manifests.js +108 -0
  28. package/dist/middleware.d.ts +15 -0
  29. package/dist/middleware.js +49 -0
  30. package/dist/mock-provider.d.ts +6 -0
  31. package/dist/mock-provider.js +14 -0
  32. package/dist/models.d.ts +8 -0
  33. package/dist/models.js +25 -0
  34. package/dist/node/config.d.ts +9 -0
  35. package/dist/node/config.js +51 -0
  36. package/dist/node/session-store-jsonl.d.ts +17 -0
  37. package/dist/node/session-store-jsonl.js +134 -0
  38. package/dist/node/settings.d.ts +7 -0
  39. package/dist/node/settings.js +26 -0
  40. package/dist/node/trust.d.ts +13 -0
  41. package/dist/node/trust.js +56 -0
  42. package/dist/provider-events.d.ts +15 -0
  43. package/dist/provider-events.js +30 -0
  44. package/dist/provider-packages.d.ts +4 -0
  45. package/dist/provider-packages.js +12 -0
  46. package/dist/provider-request-policy.d.ts +9 -0
  47. package/dist/provider-request-policy.js +49 -0
  48. package/dist/providers/openai-compatible.d.ts +9 -0
  49. package/dist/providers/openai-compatible.js +197 -0
  50. package/dist/providers.d.ts +8 -0
  51. package/dist/providers.js +25 -0
  52. package/dist/redaction.d.ts +11 -0
  53. package/dist/redaction.js +68 -0
  54. package/dist/resources.d.ts +5 -0
  55. package/dist/resources.js +30 -0
  56. package/dist/retry.d.ts +11 -0
  57. package/dist/retry.js +48 -0
  58. package/dist/rpc.d.ts +18 -0
  59. package/dist/rpc.js +187 -0
  60. package/dist/security.d.ts +51 -0
  61. package/dist/security.js +60 -0
  62. package/dist/session-stores.d.ts +25 -0
  63. package/dist/session-stores.js +116 -0
  64. package/dist/settings.d.ts +3 -0
  65. package/dist/settings.js +26 -0
  66. package/dist/skills.d.ts +8 -0
  67. package/dist/skills.js +34 -0
  68. package/dist/system-prompts.d.ts +6 -0
  69. package/dist/system-prompts.js +47 -0
  70. package/dist/testing/provider-conformance.d.ts +36 -0
  71. package/dist/testing/provider-conformance.js +164 -0
  72. package/dist/tools.d.ts +25 -0
  73. package/dist/tools.js +109 -0
  74. package/docs/agent-session-runtime.md +167 -0
  75. package/docs/api-page-template.md +32 -0
  76. package/docs/cli-rpc.md +140 -0
  77. package/docs/compaction-and-retry.md +177 -0
  78. package/docs/compaction-llm.md +108 -0
  79. package/docs/compaction-observational-memory.md +123 -0
  80. package/docs/configuration-and-manifests.md +142 -0
  81. package/docs/context-and-skills.md +113 -0
  82. package/docs/contribution-registries.md +118 -0
  83. package/docs/credentials-and-redaction.md +122 -0
  84. package/docs/extensions.md +139 -0
  85. package/docs/index.md +56 -0
  86. package/docs/input-and-prompt-assembly.md +168 -0
  87. package/docs/middleware-hooks.md +123 -0
  88. package/docs/node-filesystem-config.md +85 -0
  89. package/docs/node-jsonl-session-store.md +81 -0
  90. package/docs/provider-conformance.md +109 -0
  91. package/docs/provider-layer.md +172 -0
  92. package/docs/provider-packages.md +171 -0
  93. package/docs/providers/kimi.md +110 -0
  94. package/docs/providers/openai-compatible.md +125 -0
  95. package/docs/providers/openai.md +131 -0
  96. package/docs/providers/opencode-go.md +103 -0
  97. package/docs/providers/openrouter.md +105 -0
  98. package/docs/providers/zai.md +108 -0
  99. package/docs/public-contracts.md +375 -0
  100. package/docs/release-and-install.md +141 -0
  101. package/docs/resource-loading.md +97 -0
  102. package/docs/session-stores-and-branching.md +119 -0
  103. package/docs/settings-auth-trust-security.md +73 -0
  104. package/docs/system-prompts.md +116 -0
  105. package/docs/tools.md +151 -0
  106. package/package.json +93 -0
@@ -0,0 +1,119 @@
1
+ # Session stores and branching
2
+
3
+ ## What it does
4
+
5
+ Session store helpers define branch-aware session entries and pure utilities for creating entries, listing branch leaves, reading a leaf path, and rebuilding provider context from a selected leaf.
6
+
7
+ Public helpers:
8
+
9
+ - `createSessionEntry(options)`
10
+ - `createMemorySessionStore(initialEntries?)`
11
+ - `getSessionBranchEntries(entries, options)`
12
+ - `listSessionBranches(entries)`
13
+ - `rebuildSessionContext(entries, options)`
14
+
15
+ ## When to use it
16
+
17
+ Use these helpers when a host, runtime session, or store adapter needs durable, branch-aware session data without coupling stores to providers, tools, credentials, or files.
18
+
19
+ Do not use them as a database layer, migration system, lock service, compaction strategy, retry policy, CLI/RPC protocol, or hidden global store registry.
20
+
21
+ ## Inputs / request
22
+
23
+ `SessionEntry` has stable branch fields plus typed payloads:
24
+
25
+ | Field | Purpose |
26
+ | --- | --- |
27
+ | `id` | Unique entry id. |
28
+ | `parentId` | Previous entry on the branch, if any. |
29
+ | `sessionId` | Session that owns the entry. |
30
+ | `timestamp` | ISO timestamp chosen by the caller/helper. |
31
+ | `kind` | `message`, `event`, `summary`, `metadata`, `model_change`, `label`, `custom`, or `compaction`. |
32
+ | `runId` | Optional run id. |
33
+ | `message`, `event`, `model`, `previousModel`, `label`, `summary`, `data`, `metadata` | Optional payload fields for the entry kind. |
34
+
35
+ `rebuildSessionContext()` and `getSessionBranchEntries()` accept an optional `leafId`. If omitted, the last entry is used as the leaf. `createMemorySessionStore()` accepts optional initial entries.
36
+
37
+ ## Outputs / response / events
38
+
39
+ | Helper | Output |
40
+ | --- | --- |
41
+ | `createSessionEntry()` | A `SessionEntry` with generated `id` and `timestamp` when omitted. |
42
+ | `getSessionBranchEntries()` | Ordered entries from root to selected leaf (deep copies). |
43
+ | `listSessionBranches()` | Leaf ids and their root-to-leaf entry paths (deep copies). |
44
+ | `rebuildSessionContext()` | `{ leafId, entries, messages, summaries }` for provider input rebuild; with a compaction entry, raw `entries` stay intact while `messages` becomes recent context and `summaries` includes the compaction summary. All arrays and objects are deep copies. |
45
+ | `createMemorySessionStore()` | Async `SessionStore` with `append()`, `list(sessionId)`, and `get(id)`. `list()` and `get()` return deep copies. |
46
+
47
+ Helpers throw on duplicate entry ids, unknown leaves, or missing parents. They do not mutate input arrays.
48
+
49
+ For `kind: "compaction"`, `data` may contain `throughEntryId`, `keepEntryIds`, `strategy`, and `trigger`. The latest valid compaction entry on a branch is used as the provider-context boundary; raw history remains in `entries`.
50
+
51
+ ## Request/response example
52
+
53
+ ```json
54
+ {
55
+ "leafId": "entry_2",
56
+ "messages": [{ "role": "user", "content": [{ "type": "text", "text": "Hi" }] }]
57
+ }
58
+ ```
59
+
60
+ ## Implementation example
61
+
62
+ ```ts
63
+ import { createMemorySessionStore, createSessionEntry, rebuildSessionContext } from "@arnilo/prism";
64
+
65
+ const first = createSessionEntry({
66
+ id: "entry_1",
67
+ sessionId: "s1",
68
+ kind: "message",
69
+ message: { role: "user", content: [{ type: "text", text: "Hi" }] },
70
+ });
71
+ const label = createSessionEntry({
72
+ id: "entry_2",
73
+ parentId: first.id,
74
+ sessionId: "s1",
75
+ kind: "label",
76
+ label: "investigation",
77
+ });
78
+
79
+ const store = createMemorySessionStore([first]);
80
+ await store.append(label);
81
+
82
+ const context = rebuildSessionContext(await store.list("s1"), { leafId: label.id });
83
+ ```
84
+
85
+ ## Extension and configuration notes
86
+
87
+ Stores and extensions can use these data helpers directly. Store adapters only need append/list/get behavior; branch queries are derived in memory from listed entries.
88
+
89
+ `createMemorySessionStore()` is the built-in in-memory implementation. It preserves append order per session, isolates session ids, returns entries by id in O(1), rejects duplicate entry ids, and returns deep copies from `list()` and `get()`. It is process memory only; hosts that need durability should pass another `SessionStore`.
90
+
91
+ `getSessionBranchEntries()` and `rebuildSessionContext()` also return deep copies of entries and messages, so callers cannot mutate the input arrays or the memory store by editing returned objects.
92
+
93
+ `AgentSession` uses `AgentSessionConfig.store` before `AgentConfig.store`, otherwise a private memory store. It appends user, assistant, tool-result, and model-change entries, resumes from `leafId`, rebuilds provider history from the selected branch, checks out old leaves, forks by selecting a leaf in the same session, and clones the selected branch to a new session id.
94
+
95
+ Node hosts that need simple file durability can import `createJsonlSessionStore()` from the explicit `@arnilo/prism/node/session-store-jsonl` subpath.
96
+
97
+ Use `createDefaultCompactionStrategy()` to create compaction entries that `rebuildSessionContext()` understands. Compaction adds summaries; it does not delete or rewrite raw store entries.
98
+
99
+ `createSessionEntry()` accepts injectable `createId` and `now` functions for deterministic tests or host id policy. Prism does not create a global store or id service.
100
+
101
+ ## Security and performance notes
102
+
103
+ - Helpers are pure data functions: no provider calls, tool calls, settings reads, credential resolution, filesystem access, network access, timers, or dependencies.
104
+ - Store only host-approved session entries. Do not put provider credentials, credential resolvers, provider objects, full provider requests, or secrets in entries.
105
+ - Branch rebuild is linear over listed entries and uses `Map`, `Set`, and arrays only.
106
+ - Compaction-aware rebuild keeps raw branch entries in `entries`; only provider-context `messages`/`summaries` are reduced.
107
+ - Memory store lookup by id is O(1); list is O(n) for that session.
108
+ - Duplicate ids and missing parents fail clearly instead of guessing a branch.
109
+
110
+ ## Related APIs
111
+
112
+ - [Public contracts](public-contracts.md): `SessionEntry`, `SessionStore`, `StoreFactory`, and session contracts.
113
+ - [Agent/session runtime](agent-session-runtime.md): runtime sessions use these branch helpers for store-backed history, checkout, fork, and clone.
114
+ - [Node JSONL session store](node-jsonl-session-store.md): optional Node filesystem store for caller-named JSONL files.
115
+ - [Compaction and retry policies](compaction-and-retry.md): default strategy for creating compaction entries.
116
+ - [Input and prompt assembly](input-and-prompt-assembly.md): provider input assembly consumes rebuilt `messages` and `summaries`.
117
+ - [Credentials and redaction](credentials-and-redaction.md): security boundary for secrets that must not enter session entries.
118
+
119
+ Session stores persist the entries they receive. Configure `AgentConfig.redactor` or `RunOptions.redactor` before a run when known secrets must be removed before entries reach durable stores.
@@ -0,0 +1,73 @@
1
+ # Settings, auth, trust, and security controls
2
+
3
+ ## What it does
4
+ Prism exposes small host-owned helpers for settings, in-memory credentials, trust checks, permission checks, and exact known-secret redaction. It adds no hidden settings discovery, no hidden credential loading, no persistent secret store, does not auto-load project-local code, and no sandbox.
5
+
6
+ ## When to use it
7
+ Use these APIs when a host wants one explicit place to compose settings, resolve caller-supplied credentials, deny untrusted resources/extensions/tools, or redact known secret strings before runtime serialization.
8
+
9
+ ## Inputs / request
10
+ - `createStaticSettingsProvider(settings)` reads dotted keys from an in-memory JSON object.
11
+ - `createChainedSettingsProvider(providers)` returns the first defined setting.
12
+ - `createMemoryCredentialStore(initial?)` stores explicit credentials in memory only.
13
+ - `createChainedCredentialResolver(resolvers)` returns the first credential found.
14
+ - `createExplicitCredentialResolver(sources)` documents and applies named source order such as runtime override → stored → host env object → fallback.
15
+ - `createEnvCredentialResolver(env, map)` reads only the caller-supplied object; it does not read `process.env`.
16
+ - `assertTrusted(policy, request)` and `assertPermission(policy, request)` fail closed on denial.
17
+ - `createSecretRedactor(secrets)` redacts exact known strings.
18
+ - Node-only subpaths: `@arnilo/prism/node/settings` for caller-named JSON settings files and `@arnilo/prism/node/trust` for explicit trusted path roots with symlink-aware realpath checks.
19
+
20
+ ## Outputs / response / events
21
+ Settings and credential helpers return existing `SettingsProvider` and `CredentialResolver` contracts. Permission denial blocks tool execution, extension setup, and resource loader calls before side effects. A configured `AgentConfig.redactor` or `RunOptions.redactor` redacts provider requests, emitted `AgentEvent` payloads, and stored `SessionEntry` values.
22
+
23
+ ## Request/response example
24
+ ```ts
25
+ import { createSecretRedactor, createStaticPermissionPolicy, createStaticSettingsProvider } from "@arnilo/prism";
26
+
27
+ const settings = createStaticSettingsProvider({ demo: { enabled: true } });
28
+ const permission = createStaticPermissionPolicy({ allow: ["tool:echo:execute"] });
29
+ const redactor = createSecretRedactor(["token-value"]);
30
+
31
+ console.log(await settings.get("demo.enabled"));
32
+ ```
33
+
34
+ ## Implementation example
35
+ ```ts
36
+ import { createAgent, createMemoryCredentialStore, createSecretRedactor, resolveCredentialValue } from "@arnilo/prism";
37
+ import { loadSettingsFiles, defaultUserSettingsPath } from "@arnilo/prism/node/settings";
38
+ import { createPathTrustPolicy } from "@arnilo/prism/node/trust";
39
+
40
+ const settings = await loadSettingsFiles([
41
+ { name: "user", path: defaultUserSettingsPath(), optional: true },
42
+ ]);
43
+ const credentials = createMemoryCredentialStore();
44
+ credentials.set({ name: "api", provider: "demo", credential: { type: "api_key", value: "token-value" } });
45
+ const trust = createPathTrustPolicy({ trustedRoots: [process.cwd()] });
46
+ const apiKey = await resolveCredentialValue(credentials, { name: "api", provider: "demo" });
47
+
48
+ const agent = createAgent({
49
+ model: { provider: "demo", model: "model" },
50
+ settings,
51
+ credentials,
52
+ redactor: apiKey ? createSecretRedactor([apiKey]) : undefined,
53
+ });
54
+ void trust;
55
+ void agent;
56
+ ```
57
+
58
+ ## Extension and configuration notes
59
+ Root imports stay filesystem-free. Node settings files are caller-named and read once; optional missing files are skipped. Trust storage, prompts, approval UI, OAuth token storage, environment-variable selection, and persistent credentials belong in the host or an extension package.
60
+
61
+ ## Security and performance notes
62
+ Prism does not sandbox host tools or extensions. Prism does not read environment variables, keychains, user config files, package manifests, resources, or project-local extensions unless the host explicitly wires those operations. Redaction is exact known-secret replacement only; it is not secret detection. Permission and trust checks are one operation per guarded call and add no workers, watchers, retries, network, or filesystem scans.
63
+
64
+ `@arnilo/prism/node/trust` resolves symlinks on both the trusted root and the target path. A path that is lexically inside a trusted root but escapes it through a symlink is rejected, and realpath failures (missing root, permission error) fail closed.
65
+
66
+ ## Related APIs
67
+ - `createStaticSettingsProvider`, `createChainedSettingsProvider`
68
+ - `createMemoryCredentialStore`, `createChainedCredentialResolver`, `createExplicitCredentialResolver`, `createEnvCredentialResolver`, `refreshOAuthCredential`, `resolveCredentialValue`
69
+ - `createStaticTrustPolicy`, `assertTrusted`, `isTrusted`, `TrustDeniedError`
70
+ - `createStaticPermissionPolicy`, `assertPermission`, `checkPermission`, `PermissionDeniedError`
71
+ - `createSecretRedactor`, `redactMessage`, `redactAgentEvent`, `redactSessionEntry`, `redactProviderRequest`
72
+ - `@arnilo/prism/node/settings`: `defaultUserSettingsPath`, `readSettingsFile`, `loadSettingsFiles`
73
+ - `@arnilo/prism/node/trust`: `createPathTrustPolicy`, `isPathInside`, `isPathInsideReal`
@@ -0,0 +1,116 @@
1
+ # System prompts
2
+
3
+ ## What it does
4
+
5
+ Layered system prompts let hosts compose caller-supplied prompt contributions before the default input builder turns them into system messages.
6
+
7
+ Public helpers:
8
+
9
+ - `composeSystemPrompt(contributions, { base })`
10
+ - `mergeSystemPromptConfig(config, override)`
11
+ - `SystemPromptContribution`, `SystemPromptMode`, `SystemPromptSource`, `SystemPromptConfig`
12
+
13
+ `AgentConfig.instructions` stays the simple base prompt path. `AgentConfig.systemPrompt` and `RunOptions.systemPrompt` add explicit layers on top.
14
+
15
+ ## When to use it
16
+
17
+ Use this API when an app wants deterministic package/app/user/run prompt layers without filesystem discovery or package loading.
18
+
19
+ Do not use it for prompt template expansion, resource loading, settings discovery, credential lookup, provider calls, or hidden global prompts.
20
+
21
+ ## Inputs / request
22
+
23
+ ```ts
24
+ composeSystemPrompt([
25
+ { id: "pkg", source: "package", mode: "append", text: "Package rule." },
26
+ { id: "app", source: "app", mode: "replace", text: "App rule." },
27
+ { id: "run", source: "run", mode: "append", text: "Run rule." },
28
+ ], { base: "Base instruction." });
29
+ ```
30
+
31
+ `source` order is deterministic for known sources: `package`, `app`, `user`, then `run`. Unknown sources keep input order after known sources.
32
+
33
+ `mode` behavior:
34
+
35
+ | Mode | Behavior |
36
+ | --- | --- |
37
+ | `append` or omitted | Add text after earlier prompt text. |
38
+ | `prepend` | Add text before current prompt text. |
39
+ | `replace` | Clear earlier prompt text and use this text. |
40
+ | `disable` | Clear earlier prompt text and add no text. Later layers may still add text. |
41
+
42
+ `RunOptions.systemPrompt: false` disables configured prompt layers for that run while keeping `AgentConfig.instructions` as the base prompt.
43
+
44
+ ## Outputs / response / events
45
+
46
+ `composeSystemPrompt()` returns the composed prompt string or `undefined` when no prompt text remains. The agent/session runtime passes that string to `assembleProviderInput()` as `systemInstructions`; it does not emit a separate event or store prompt layers.
47
+
48
+ ## Request/response example
49
+
50
+ ```json
51
+ {
52
+ "base": "Base",
53
+ "layers": [
54
+ { "id": "app", "source": "app", "mode": "replace", "text": "App" },
55
+ { "id": "run", "source": "run", "mode": "append", "text": "Run" }
56
+ ],
57
+ "composed": "App\n\nRun"
58
+ }
59
+ ```
60
+
61
+ ## Implementation example
62
+
63
+ ```ts
64
+ import { composeSystemPrompt, createAgent } from "@arnilo/prism";
65
+
66
+ const prompt = composeSystemPrompt([
67
+ { id: "app", source: "app", mode: "replace", text: "You are concise." },
68
+ { id: "user", source: "user", mode: "append", text: "Prefer bullet points." },
69
+ ], { base: "Base instruction." });
70
+
71
+ const agent = createAgent({
72
+ model,
73
+ provider,
74
+ instructions: "Base instruction.",
75
+ systemPrompt: { id: "app", source: "app", mode: "append", text: "Use safe JSON." },
76
+ });
77
+
78
+ await agent.createSession().run("Hi", {
79
+ systemPrompt: { id: "run", source: "run", mode: "append", text: "Answer briefly." },
80
+ });
81
+ ```
82
+
83
+ ## Extension and configuration notes
84
+
85
+ Extensions and provider packages can register `SystemPromptContribution` values, but registration is inert. Hosts must select contributions and pass them to `AgentConfig.systemPrompt` or `RunOptions.systemPrompt`.
86
+
87
+ Manifests can declare `systemPromptContribution` kinds as data-only references:
88
+
89
+ ```ts
90
+ import { definePrismManifest } from "@arnilo/prism";
91
+
92
+ export default definePrismManifest({
93
+ name: "demo-prompts",
94
+ contributions: [
95
+ { kind: "systemPromptContribution", name: "demo.prompt", metadata: { id: "demo-prompt", source: "package", mode: "append" } },
96
+ ],
97
+ });
98
+ ```
99
+
100
+ The manifest entry does not load or apply the prompt. The host must still select it and pass the `SystemPromptContribution` to the runtime.
101
+
102
+ No `SYSTEM.md`, `APPEND_SYSTEM.md`, prompt template, settings, or manifest discovery happens in core.
103
+
104
+ ## Security and performance notes
105
+
106
+ - Composition is a single in-memory pass plus deterministic ordering; no dependency, watcher, filesystem read, provider call, or tokenizer is added.
107
+ - Prompt text is caller-supplied content. Do not put secrets in prompts, settings, manifests, session entries, package metadata, or docs examples.
108
+ - `replace`/`disable` make prompt policy explicit; they are not permission or sandbox controls.
109
+
110
+ ## Related APIs
111
+
112
+ - [Input and prompt assembly](input-and-prompt-assembly.md): default input builder receives the composed system instruction string.
113
+ - [Agent/session runtime](agent-session-runtime.md): runtime fields `AgentConfig.systemPrompt` and `RunOptions.systemPrompt`.
114
+ - [Contribution registries](contribution-registries.md): inert system prompt contribution registry.
115
+ - [Extensions](extensions.md): `registerSystemPromptContribution()`.
116
+ - [Public contracts](public-contracts.md): exported prompt contribution contracts.
package/docs/tools.md ADDED
@@ -0,0 +1,151 @@
1
+ # Tools
2
+
3
+ ## What it does
4
+
5
+ The tool harness gives hosts a small active registry, exact allow/deny filtering, and explicit dispatch for host-owned tools. Prism stores tool definitions and JSON Schema-compatible `parameters` metadata, but does not ship app tools, sandbox host code, or interpret schemas.
6
+
7
+ APIs:
8
+
9
+ - `createToolRegistry()` / `ToolRegistry`
10
+ - `filterTools()`
11
+ - `dispatchToolCall()`
12
+ - `ToolFilter`, `ToolFilterInput`, `ToolValidator`, `DispatchToolCallOptions`
13
+
14
+ ## When to use it
15
+
16
+ Use `createToolRegistry()` when a host has selected the tools that may be active for an agent, session, or run. Use `dispatchToolCall()` when provider output or host code requests one of those tools and the host wants Prism to enforce lookup, filtering, object arguments, validation, middleware order, and lifecycle events. `session.run()` uses these same primitives for its bounded tool loop.
17
+
18
+ Do not use the harness as a sandbox, package loader, app-tool pack, permission policy engine, provider loop, or schema validator.
19
+
20
+ ## Inputs / request
21
+
22
+ ```ts
23
+ createToolRegistry(tools?: readonly ToolDefinition[]): ToolRegistry
24
+ filterTools(tools: readonly ToolDefinition[], filter?: ToolFilter | readonly ToolFilter[]): readonly ToolDefinition[]
25
+ dispatchToolCall(options: DispatchToolCallOptions): Promise<ToolResult>
26
+ ```
27
+
28
+ `ToolRegistry` methods:
29
+
30
+ | Method | Input | Result |
31
+ | --- | --- | --- |
32
+ | `register(tool)` | `ToolDefinition` | Stores or replaces by `tool.name`. |
33
+ | `get(name)` | tool name | Returns the tool or `undefined`. |
34
+ | `resolve(name)` | tool name | Returns the tool or throws `Unknown tool: <name>`. |
35
+ | `list()` | none | Returns tools in insertion order. |
36
+
37
+ `ToolFilter` fields:
38
+
39
+ | Field | Purpose |
40
+ | --- | --- |
41
+ | `allow` | Exact tool names allowed by this scope. Empty or missing means no allow restriction. |
42
+ | `deny` | Exact tool names denied by this scope. Deny wins over allow. |
43
+
44
+ When multiple filters are provided, each non-empty allow list must include the tool, and any deny list excludes it.
45
+
46
+ `DispatchToolCallOptions` fields:
47
+
48
+ | Field | Purpose |
49
+ | --- | --- |
50
+ | `call` | `ToolCallContent` to dispatch. Runtime checks still reject non-object arguments. |
51
+ | `registry` | Active host `ToolRegistry`. |
52
+ | `context` | `ToolExecutionContext` with session/run/tool call ids, signal, metadata, and optional progress callback. |
53
+ | `filter` | Optional exact allow/deny filter or ordered filters. |
54
+ | `middleware` | Optional `MiddlewareRegistry`; `tool_call` runs before validation/execution and `tool_result` runs after execution. |
55
+ | `validate` | Optional host validator returning `void`, a message string, or `ErrorInfo`. |
56
+ | `emit` | Optional `AgentEvent` callback for lifecycle events. |
57
+ | `secrets` | Known secret values to redact from thrown tool errors. |
58
+
59
+ ## Outputs / response / events
60
+
61
+ Registry calls return plain `ToolDefinition` objects. `resolve()` fails closed for unknown names before any tool can execute. Filtering returns only tools already present in the input list; it never creates or enables new tools.
62
+
63
+ `dispatchToolCall()` returns a `ToolResult`. Unknown tools, denied tools, invalid arguments, validator failures, and thrown tool errors return a result with `error` and do not throw by default.
64
+
65
+ Dispatch can emit these `AgentEvent` types:
66
+
67
+ | Event | When |
68
+ | --- | --- |
69
+ | `tool_execution_blocked` | Unknown, denied, invalid-argument, or validator-blocked call. |
70
+ | `tool_execution_started` | Immediately before `tool.execute()`. |
71
+ | `tool_execution_progress` | When the tool calls `context.progress()`. |
72
+ | `tool_execution_finished` | After successful execution and `tool_result` middleware. |
73
+ | `tool_execution_error` | When `tool.execute()` throws. |
74
+
75
+ ## Request/response example
76
+
77
+ ```json
78
+ {
79
+ "call": { "type": "tool_call", "id": "call_1", "name": "echo", "arguments": { "text": "hi" } },
80
+ "filter": { "allow": ["echo"] },
81
+ "result": { "toolCallId": "call_1", "name": "echo", "value": { "text": "hi" } }
82
+ }
83
+ ```
84
+
85
+ ## Implementation example
86
+
87
+ ```ts
88
+ import { createToolRegistry, dispatchToolCall, filterTools, type ToolDefinition } from "@arnilo/prism";
89
+
90
+ const echo: ToolDefinition = {
91
+ name: "echo",
92
+ parameters: { type: "object", properties: { text: { type: "string" } } },
93
+ execute(args, context) {
94
+ return { toolCallId: context.toolCallId, name: "echo", value: args };
95
+ },
96
+ };
97
+
98
+ const registry = createToolRegistry([echo]);
99
+ const active = filterTools(registry.list(), { allow: ["echo"] });
100
+
101
+ const result = await dispatchToolCall({
102
+ call: { type: "tool_call", id: "call_1", name: "echo", arguments: { text: "hi" } },
103
+ registry,
104
+ context: { sessionId: "s1", runId: "r1", toolCallId: "call_1" },
105
+ filter: { allow: active.map((tool) => tool.name) },
106
+ validate: (_tool, args) => typeof args.text === "string" ? undefined : "text is required",
107
+ });
108
+
109
+ console.log(result.value);
110
+ ```
111
+
112
+ ## Extension and configuration notes
113
+
114
+ Extensions can contribute tool definitions through `ExtensionAPI.registerTool()`, which stores them in `ContributionRegistries.tools`. Contributions are inert until the host explicitly selects/registers them in a tool registry and calls dispatch.
115
+
116
+ ```ts
117
+ import { createContributionRegistries, createToolRegistry } from "@arnilo/prism";
118
+
119
+ const contributions = createContributionRegistries();
120
+ contributions.tools.register("echo", echo);
121
+
122
+ const activeTools = createToolRegistry([contributions.tools.resolve("echo")]);
123
+ ```
124
+
125
+ Middleware can transform `tool_call` and `tool_result` payloads, but dispatch re-checks active registry lookup, filters, and object arguments after `tool_call` middleware. Middleware cannot grant permission by changing a tool name.
126
+
127
+ Configuration can carry allow/deny names, but Prism does not define a policy class or hidden global active tool set. Skills may reference `toolNames`, but `resolveActiveSkills()` only checks those names against the host-active tool list; it does not register, allow, or execute tools.
128
+
129
+ ## Security and performance notes
130
+
131
+ - Tool lookup uses a `Map` for O(1) name lookup.
132
+ - Filtering is exact-name matching over the provided tools and rules.
133
+ - Unknown, denied, malformed, and validator-blocked calls fail closed.
134
+ - Tool arguments must be JSON object-shaped before validation or execution.
135
+ - `parameters` is pass-through metadata; hosts own schema interpretation and validation.
136
+ - Prism does not sandbox host tools and does not include built-in app tools.
137
+ - Contribution registration and registry/filter calls do not perform provider calls, credential resolution, resource loading, network, filesystem discovery, or tool execution.
138
+ - Dispatch performs explicit in-memory checks and executes only the selected host-active tool; it adds no retries, queues, timers, or new dependencies.
139
+
140
+ ## Related APIs
141
+
142
+ - [Agent/session runtime](agent-session-runtime.md): dispatches complete provider tool calls through the host-active tool harness and returns tool results on the next provider turn.
143
+ - [Public contracts](public-contracts.md): `ToolDefinition`, `ToolRegistry`, `ToolExecutionContext`, `ToolResult`, and tool `AgentEvent` contracts.
144
+ - [Contribution registries](contribution-registries.md): inert extension/package tool contribution storage.
145
+ - [Extension kernel and event bus](extensions.md): `ExtensionAPI.registerTool()` contribution registration.
146
+ - [Context and skills](context-and-skills.md): skill `toolNames` validation against host-active tools.
147
+ - [Middleware hooks](middleware-hooks.md): `tool_call` and `tool_result` middleware used during dispatch.
148
+ - [Credentials and redaction](credentials-and-redaction.md): redaction helpers used for tool execution errors.
149
+ - [Observational memory compaction package](compaction-observational-memory.md): optional exact-id recall tool factory.
150
+
151
+ `DispatchToolCallOptions.permission` can provide a `PermissionPolicy`; denial emits `tool_execution_blocked` before validation or `execute()`. Middleware cannot bypass this guard. Prism does not sandbox tools. See [Security/auth/trust](settings-auth-trust-security.md).
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@arnilo/prism",
3
+ "version": "0.0.1",
4
+ "description": "Agent harness for AI providers, agents, sessions, and tools.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./providers/openai-compatible": {
14
+ "types": "./dist/providers/openai-compatible.d.ts",
15
+ "default": "./dist/providers/openai-compatible.js"
16
+ },
17
+ "./testing/provider-conformance": {
18
+ "types": "./dist/testing/provider-conformance.d.ts",
19
+ "default": "./dist/testing/provider-conformance.js"
20
+ },
21
+ "./node/config": {
22
+ "types": "./dist/node/config.d.ts",
23
+ "default": "./dist/node/config.js"
24
+ },
25
+ "./node/settings": {
26
+ "types": "./dist/node/settings.d.ts",
27
+ "default": "./dist/node/settings.js"
28
+ },
29
+ "./node/trust": {
30
+ "types": "./dist/node/trust.d.ts",
31
+ "default": "./dist/node/trust.js"
32
+ },
33
+ "./node/session-store-jsonl": {
34
+ "types": "./dist/node/session-store-jsonl.d.ts",
35
+ "default": "./dist/node/session-store-jsonl.js"
36
+ }
37
+ },
38
+ "bin": {
39
+ "prism": "./dist/cli.js"
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "!dist/__tests__",
44
+ "!dist/**/*.map",
45
+ "docs",
46
+ "CHANGELOG.md"
47
+ ],
48
+ "workspaces": [
49
+ "packages/provider-*",
50
+ "packages/compaction-*",
51
+ "packages/prism-providers",
52
+ "packages/prism-compaction",
53
+ "packages/prism-all"
54
+ ],
55
+ "scripts": {
56
+ "build:core": "tsc",
57
+ "build": "npm run build:core && npm run build --workspaces --if-present",
58
+ "typecheck": "npm run build:core && npm run typecheck --workspaces --if-present && tsc -p examples --noEmit",
59
+ "test": "npm run build && node --test dist/__tests__/*.test.js && npm run test --workspaces --if-present",
60
+ "pack:dry-run": "npm pack --dry-run && npm run pack:dry-run --workspaces --if-present",
61
+ "release:dry-run": "npm test && npm run pack:dry-run"
62
+ },
63
+ "devDependencies": {
64
+ "typescript": "^5.7.0",
65
+ "@types/node": "^22.0.0"
66
+ },
67
+ "engines": {
68
+ "node": ">=20"
69
+ },
70
+ "license": "MIT",
71
+ "repository": {
72
+ "type": "git",
73
+ "url": "git+https://github.com/ashiqrniloy/prism.git"
74
+ },
75
+ "bugs": {
76
+ "url": "https://github.com/ashiqrniloy/prism/issues"
77
+ },
78
+ "homepage": "https://github.com/ashiqrniloy/prism#readme",
79
+ "keywords": [
80
+ "prism",
81
+ "agent",
82
+ "llm",
83
+ "ai",
84
+ "framework",
85
+ "chatbot"
86
+ ],
87
+ "sideEffects": [
88
+ "dist/cli.js"
89
+ ],
90
+ "publishConfig": {
91
+ "access": "public"
92
+ }
93
+ }