@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.
- package/CHANGELOG.md +19 -2
- package/README.md +17 -7
- package/dist/agent-definitions.d.ts +12 -0
- package/dist/agent-definitions.js +131 -0
- package/dist/agent-loops.d.ts +14 -0
- package/dist/agent-loops.js +161 -0
- package/dist/agents.js +263 -76
- package/dist/cache-helpers.d.ts +28 -0
- package/dist/cache-helpers.js +73 -0
- package/dist/cli-runner.d.ts +38 -2
- package/dist/cli-runner.js +167 -5
- package/dist/compaction.js +2 -0
- package/dist/config.js +47 -12
- package/dist/contracts.d.ts +581 -6
- package/dist/contracts.js +41 -1
- package/dist/contribution-parsing.d.ts +19 -0
- package/dist/contribution-parsing.js +124 -0
- package/dist/contributions.d.ts +13 -3
- package/dist/contributions.js +96 -20
- package/dist/extensions.js +3 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.js +10 -4
- package/dist/input.d.ts +7 -1
- package/dist/input.js +52 -11
- package/dist/instruction-injection.d.ts +28 -0
- package/dist/instruction-injection.js +55 -0
- package/dist/manifests.d.ts +1 -1
- package/dist/manifests.js +3 -3
- package/dist/models.d.ts +4 -1
- package/dist/models.js +5 -2
- package/dist/node/agent-definitions.d.ts +98 -0
- package/dist/node/agent-definitions.js +389 -0
- package/dist/node/contribution-discovery.d.ts +17 -0
- package/dist/node/contribution-discovery.js +163 -0
- package/dist/node/instruction-injectors.d.ts +32 -0
- package/dist/node/instruction-injectors.js +72 -0
- package/dist/node/session-store-jsonl.d.ts +1 -1
- package/dist/node/session-store-jsonl.js +42 -4
- package/dist/node/system-project-prompts.d.ts +30 -0
- package/dist/node/system-project-prompts.js +53 -0
- package/dist/provider-events.d.ts +3 -1
- package/dist/provider-events.js +34 -0
- package/dist/provider-request-policy.js +15 -1
- package/dist/providers/openai-compatible.js +1 -1
- package/dist/providers.d.ts +6 -2
- package/dist/providers.js +15 -1
- package/dist/redaction.d.ts +2 -1
- package/dist/redaction.js +3 -0
- package/dist/registry-options.d.ts +5 -0
- package/dist/registry-options.js +5 -0
- package/dist/rpc.d.ts +6 -2
- package/dist/rpc.js +71 -13
- package/dist/session-stores.d.ts +3 -1
- package/dist/session-stores.js +67 -6
- package/dist/skills.d.ts +4 -1
- package/dist/skills.js +3 -1
- package/dist/system-prompts.js +6 -2
- package/dist/testing/compaction-conformance.d.ts +17 -0
- package/dist/testing/compaction-conformance.js +61 -0
- package/dist/testing/extension-conformance.d.ts +26 -0
- package/dist/testing/extension-conformance.js +55 -0
- package/dist/testing/provider-conformance.d.ts +7 -0
- package/dist/testing/provider-conformance.js +18 -31
- package/dist/testing/session-store-conformance.d.ts +20 -0
- package/dist/testing/session-store-conformance.js +92 -0
- package/dist/testing/tool-conformance.d.ts +39 -0
- package/dist/testing/tool-conformance.js +79 -0
- package/dist/tools.d.ts +7 -2
- package/dist/tools.js +50 -13
- package/docs/agent-definitions.md +251 -0
- package/docs/agent-events.md +199 -0
- package/docs/agent-loops.md +217 -0
- package/docs/agent-session-runtime.md +20 -8
- package/docs/cli-rpc.md +39 -4
- package/docs/coding-agent-tools.md +208 -0
- package/docs/compaction-and-retry.md +2 -2
- package/docs/compaction-conformance.md +76 -0
- package/docs/compaction-llm.md +6 -3
- package/docs/compaction-observational-memory.md +4 -4
- package/docs/configuration-and-manifests.md +6 -1
- package/docs/context-and-skills.md +79 -6
- package/docs/contribution-discovery.md +149 -0
- package/docs/contribution-registries.md +9 -6
- package/docs/credentials-and-redaction.md +2 -0
- package/docs/customization.md +191 -0
- package/docs/database-persistence.md +407 -0
- package/docs/extension-authoring.md +193 -0
- package/docs/extension-conformance.md +80 -0
- package/docs/extensions.md +6 -0
- package/docs/host-security.md +141 -0
- package/docs/index.md +41 -19
- package/docs/input-and-prompt-assembly.md +19 -3
- package/docs/instruction-injection.md +183 -0
- package/docs/migration.md +201 -0
- package/docs/model-registry.md +122 -0
- package/docs/node-jsonl-session-store.md +5 -4
- package/docs/performance.md +127 -0
- package/docs/provider-caching.md +206 -0
- package/docs/provider-conformance.md +32 -5
- package/docs/provider-layer.md +51 -11
- package/docs/provider-packages.md +65 -5
- package/docs/provider-request-policies.md +113 -0
- package/docs/providers/kimi.md +22 -0
- package/docs/providers/neuralwatt.md +388 -0
- package/docs/providers/openai-compatible.md +1 -0
- package/docs/providers/openai.md +21 -0
- package/docs/providers/opencode-go.md +31 -3
- package/docs/providers/openrouter.md +29 -0
- package/docs/providers/zai.md +17 -0
- package/docs/public-contracts.md +87 -12
- package/docs/release-and-install.md +79 -27
- package/docs/runs-and-usage.md +236 -0
- package/docs/session-store-conformance.md +78 -0
- package/docs/session-stores-and-branching.md +10 -6
- package/docs/session-stores.md +126 -0
- package/docs/settings-auth-trust-security.md +18 -4
- package/docs/structured-output.md +247 -0
- package/docs/system-prompts.md +104 -2
- package/docs/tool-conformance.md +87 -0
- package/docs/tools.md +65 -8
- package/package.json +36 -2
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# Database persistence
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
The production persistence contracts describe database-neutral types for durable, multi-tenant storage of Prism sessions, branch handles, session entries, runs, agent-event ledger rows, tool-call rows, usage rows, agent-definition versions, retention policies, and migration records. They also define cursor-paginated query shapes so hosts can implement SQL, NoSQL, or object-store adapters without changing Prism runtime internals.
|
|
6
|
+
|
|
7
|
+
Prism itself does not ship a production database adapter. The built-in `SessionStore` contract (`append` / `list` / optional `get`) remains the runtime seam; `ProductionPersistenceStore` is the optional adapter-facing contract for hosts that need paginated reads, tenant isolation, audit tables, and retention.
|
|
8
|
+
|
|
9
|
+
## When to use it
|
|
10
|
+
|
|
11
|
+
Use these contracts when you write a database-backed `SessionStore` or a separate persistence adapter that needs:
|
|
12
|
+
|
|
13
|
+
- paginated branch/session/event reads via cursor, limit, and order
|
|
14
|
+
- filters by `sessionId`, `runId`, `parentId`, branch `leafId`, timestamps, tenant/account/user, event type, and entry kind
|
|
15
|
+
- durable tables for runs, events, tool calls, usage, and agent-definition versions
|
|
16
|
+
- retention policies and migration records
|
|
17
|
+
|
|
18
|
+
Do not use these contracts as a required runtime dependency. The agent/session runtime only requires `SessionStore`. `ProductionPersistenceStore` is an extension point for hosts that want richer querying. A network-free, runnable reference adapter that implements `SessionStore` + `RunLedger` + `ProductionPersistenceStore` reads against in-memory tables lives at [`examples/external-app-db-backed.ts`](../examples/external-app-db-backed.ts) — lift its contract shapes into your own SQL/NoSQL adapter. The example also calls `assertSessionStoreConforms(..., { exerciseReadBranchPath: true })`, so adapter authors have an executable baseline before adding database-specific tests.
|
|
19
|
+
|
|
20
|
+
## Inputs / request
|
|
21
|
+
|
|
22
|
+
Import the contracts from the root package:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import type {
|
|
26
|
+
ProductionPersistenceStore,
|
|
27
|
+
PersistencePage,
|
|
28
|
+
PersistenceQuery,
|
|
29
|
+
SessionRecord,
|
|
30
|
+
SessionQuery,
|
|
31
|
+
BranchRecord,
|
|
32
|
+
BranchQuery,
|
|
33
|
+
SessionEntryQuery,
|
|
34
|
+
SessionBranchRead,
|
|
35
|
+
RunRecord,
|
|
36
|
+
RunQuery,
|
|
37
|
+
AgentEventRecord,
|
|
38
|
+
AgentEventQuery,
|
|
39
|
+
ToolCallRecord,
|
|
40
|
+
ToolCallQuery,
|
|
41
|
+
UsageRecord,
|
|
42
|
+
UsageQuery,
|
|
43
|
+
AgentDefinitionRecord,
|
|
44
|
+
AgentDefinitionQuery,
|
|
45
|
+
RetentionPolicy,
|
|
46
|
+
RetentionPolicyQuery,
|
|
47
|
+
MigrationRecord,
|
|
48
|
+
MigrationQuery,
|
|
49
|
+
OwnershipScope,
|
|
50
|
+
} from "@arnilo/prism";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Important shapes:
|
|
54
|
+
|
|
55
|
+
| Contract | Purpose |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `ProductionPersistenceStore` | Adapter-facing interface with `query*` methods plus optional `readBranchPath(query)`, all returning `PersistencePage<T>`. No SQL/ORM/host file storage/network dependency is required. |
|
|
58
|
+
| `PersistencePage<T>` | `{ items; nextCursor?; total? }` cursor page. |
|
|
59
|
+
| `PersistenceQuery` | `{ cursor?; limit?; order?: "asc" \| "desc" }`. |
|
|
60
|
+
| `OwnershipScope` | `{ tenantId?; accountId?; userId? }` included in records and queries for multi-tenant isolation. |
|
|
61
|
+
| `SessionRecord` | Stored session with ids, timestamps, optional parent session, agent-definition reference, retention policy, and ownership scope. |
|
|
62
|
+
| `BranchRecord` | Branch handle / leaf pointer with `sessionId`, optional `name`, `rootEntryId`, `parentBranchId`, and `leafEntryId`. |
|
|
63
|
+
| `SessionEntryQuery` | Cursor query for entry ranges by session/run/parent/leaf/kind/time. |
|
|
64
|
+
| `SessionBranchRead` | `{ sessionId, leafId?, cursor?, limit? }` request for one branch's ancestor chain. Used by `readBranchPath` so runtime branch reads avoid `list(sessionId)`. |
|
|
65
|
+
| `RunRecord` | Stored run with `sessionId`, `branchId`, status (`queued` \| `running` \| `succeeded` \| `failed` \| `aborted`), `model`, `provider`, `idempotencyKey`, `abortReason`, and `error`. |
|
|
66
|
+
| `AgentEventRecord` | Event ledger row with `event: AgentEvent` and a `redacted` flag. Hosts redact before storage. |
|
|
67
|
+
| `ToolCallRecord` | Tool-call row with `arguments`, optional `result: ToolResult`, `reason`, `progress` snapshots, status, and a `redacted` flag. |
|
|
68
|
+
| `UsageRecord` | Usage row wrapping `Usage` with session/run/entry linkage. |
|
|
69
|
+
| `AgentDefinitionRecord` | Versioned agent definition snapshot. Only stores `AgentDefinition` data; never provider credentials/resolvers/instances. |
|
|
70
|
+
| `RetentionPolicy` | Policy with `maxAgeDays`, `maxEntriesPerSession`, `maxTotalBytes`, `archiveStore`, and `appliedKinds`. |
|
|
71
|
+
| `MigrationRecord` | Applied migration with name, version, timestamp, checksum, and applied-by. |
|
|
72
|
+
|
|
73
|
+
## Outputs / response / events
|
|
74
|
+
|
|
75
|
+
Each `query*` method returns a `PersistencePage<T>`:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
{
|
|
79
|
+
items: readonly T[];
|
|
80
|
+
nextCursor?: string;
|
|
81
|
+
total?: number;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`nextCursor` is opaque to Prism; hosts encode whatever cursor they need. Absent `nextCursor` means the end of the result set. `total` is optional because exact counts can be expensive on some stores.
|
|
86
|
+
|
|
87
|
+
## Reference relational schema
|
|
88
|
+
|
|
89
|
+
This schema is a reference, not generated DDL. Hosts map these tables/columns to their chosen database. Names snake_case here map to the camelCase TypeScript contracts in `src/contracts.ts`.
|
|
90
|
+
|
|
91
|
+
### Multi-tenant ownership (host-managed)
|
|
92
|
+
|
|
93
|
+
Hosts that need tenant/account/user isolation can use these optional tables. The persistence contracts only require `tenantId`/`accountId`/`userId` strings on records.
|
|
94
|
+
|
|
95
|
+
| Table | Key columns |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `prism_tenants` | `id`, `name`, `created_at`, `metadata` |
|
|
98
|
+
| `prism_accounts` | `id`, `tenant_id`, `name`, `created_at`, `metadata` |
|
|
99
|
+
| `prism_users` | `id`, `tenant_id`, `account_id`, `name`, `created_at`, `metadata` |
|
|
100
|
+
|
|
101
|
+
### Agent definitions
|
|
102
|
+
|
|
103
|
+
| Table | Key columns |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `prism_agent_definitions` | `id` PK, `name`, `version`, `source`, `agent_definition` JSONB, `tenant_id`, `account_id`, `user_id`, `created_at`, `created_by`, `metadata` JSONB |
|
|
106
|
+
|
|
107
|
+
The `agent_definition` column stores the `AgentDefinition` shape: name, description, model, tools, skills, context, system prompt, instructions, loop, and metadata. It never stores provider credentials, resolvers, or provider instances.
|
|
108
|
+
|
|
109
|
+
### Sessions
|
|
110
|
+
|
|
111
|
+
| Table | Key columns |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `prism_sessions` | `id` PK, `tenant_id`, `account_id`, `user_id`, `parent_session_id`, `agent_definition_id`, `agent_definition_version`, `created_at`, `updated_at`, `expires_at`, `retention_policy_id`, `metadata` JSONB |
|
|
114
|
+
|
|
115
|
+
### Branches
|
|
116
|
+
|
|
117
|
+
| Table | Key columns |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `prism_branches` | `id` PK, `session_id` FK, `name`, `root_entry_id`, `parent_branch_id`, `leaf_entry_id`, `created_at`, `metadata` JSONB |
|
|
120
|
+
|
|
121
|
+
A branch leaf (`leaf_entry_id`) is the current entry id for that branch. Rebuild logic walks `parent_id` from the leaf back to the root.
|
|
122
|
+
|
|
123
|
+
### Session entries
|
|
124
|
+
|
|
125
|
+
| Table | Key columns |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `prism_session_entries` | `id` PK, `session_id` FK, `parent_id`, `run_id`, `timestamp`, `kind`, `schema_version`, `message` JSONB, `event` JSONB, `model` JSONB, `previous_model` JSONB, `label`, `summary`, `data` JSONB, `metadata` JSONB |
|
|
128
|
+
|
|
129
|
+
Maps directly to `SessionEntry`. `kind` is one of the `SessionEntryKind` values. `schema_version` defaults to `1`. `parent_id` may be null for the root entry of a session.
|
|
130
|
+
|
|
131
|
+
`SessionAppendOptions.idempotencyKey` is not part of `SessionEntry`; store it in an adapter-owned side table when you need durable retry detection:
|
|
132
|
+
|
|
133
|
+
| Table | Key columns |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `prism_session_append_idempotency` | `session_id`, `expected_parent_id`, `idempotency_key`, `entry_id`, `created_at`, `tenant_id`, `account_id`, `user_id` |
|
|
136
|
+
|
|
137
|
+
Use a unique key on `(session_id, expected_parent_id, idempotency_key)` (plus tenant/account columns when scoped). That matches the runtime retry shape: the same run may append several entries with one run-level key, but each append has a different `expectedParentId` as the leaf advances.
|
|
138
|
+
|
|
139
|
+
### Runs
|
|
140
|
+
|
|
141
|
+
| Table | Key columns |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `prism_runs` | `id` PK, `session_id` FK, `branch_id`, `agent_definition_id`, `agent_definition_version`, `status`, `started_at`, `finished_at`, `model` JSONB, `provider`, `idempotency_key`, `abort_reason`, `error` JSONB, `tenant_id`, `account_id`, `user_id`, `metadata` JSONB |
|
|
144
|
+
|
|
145
|
+
`status` values: `queued`, `running`, `succeeded`, `failed`, `aborted`. Hosts that do not queue runs will only see `running`, `succeeded`, `failed`, and `aborted` from the runtime.
|
|
146
|
+
|
|
147
|
+
### Agent event ledger
|
|
148
|
+
|
|
149
|
+
| Table | Key columns |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| `prism_agent_events` | `id` PK, `session_id` FK, `run_id`, `entry_id`, `sequence`, `type`, `timestamp`, `event` JSONB, `redacted` boolean, `tenant_id`, `account_id`, `user_id`, `metadata` JSONB |
|
|
152
|
+
|
|
153
|
+
The `event` JSONB stores a redacted `AgentEvent`. The `sequence` column is an implementation aid for stable ordering when timestamps collide. Set `redacted = true` after applying a `SecretRedactor`.
|
|
154
|
+
|
|
155
|
+
### Tool calls
|
|
156
|
+
|
|
157
|
+
| Table | Key columns |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `prism_tool_calls` | `id` PK, `session_id` FK, `run_id`, `entry_id`, `tool_call_id`, `name`, `arguments` JSONB, `result` JSONB, `status`, `reason`, `progress` JSONB, `progress_metadata` JSONB, `progress_at`, `started_at`, `finished_at`, `redacted` boolean, `tenant_id`, `account_id`, `user_id`, `metadata` JSONB |
|
|
160
|
+
|
|
161
|
+
`status` values: `started`, `finished`, `error`, `blocked`. Progress snapshots are stored with status `started` and the `progress`/`progress_metadata`/`progress_at` columns populated. `arguments` and `result` must be redacted before storage when they contain secrets.
|
|
162
|
+
|
|
163
|
+
### Usage
|
|
164
|
+
|
|
165
|
+
| Table | Key columns |
|
|
166
|
+
| --- | --- |
|
|
167
|
+
| `prism_usage` | `id` PK, `session_id` FK, `run_id`, `entry_id`, `usage` JSONB, `recorded_at`, `tenant_id`, `account_id`, `user_id`, `metadata` JSONB |
|
|
168
|
+
|
|
169
|
+
The `usage` JSONB stores the `Usage` shape: input/output/total/cache tokens, cost, and currency.
|
|
170
|
+
|
|
171
|
+
### Retention policies
|
|
172
|
+
|
|
173
|
+
| Table | Key columns |
|
|
174
|
+
| --- | --- |
|
|
175
|
+
| `prism_retention_policies` | `id` PK, `tenant_id`, `account_id`, `user_id`, `name`, `max_age_days`, `max_entries_per_session`, `max_total_bytes`, `archive_store`, `applied_kinds` JSONB, `created_at`, `metadata` JSONB |
|
|
176
|
+
|
|
177
|
+
`applied_kinds` is a JSON array of `SessionEntryKind` values; null means all kinds.
|
|
178
|
+
|
|
179
|
+
### Migrations
|
|
180
|
+
|
|
181
|
+
| Table | Key columns |
|
|
182
|
+
| --- | --- |
|
|
183
|
+
| `prism_migrations` | `id` PK, `name`, `version`, `applied_at`, `applied_by`, `checksum`, `metadata` JSONB |
|
|
184
|
+
|
|
185
|
+
Prism does not run migrations; hosts own migration tooling and use this table to record applied changes.
|
|
186
|
+
|
|
187
|
+
## Adapter readiness checklist
|
|
188
|
+
|
|
189
|
+
Before using a host database adapter in production:
|
|
190
|
+
|
|
191
|
+
- Implement `SessionStore.append()` transactionally with duplicate-id rejection, `expectedParentId` existence validation, and `(session_id, expected_parent_id, idempotency_key)` retry deduplication.
|
|
192
|
+
- Implement `readBranchPath(query)` for large sessions and run `assertSessionStoreConforms(adapter, { exerciseReadBranchPath: true })` from `@arnilo/prism/testing/session-store-conformance` in adapter tests.
|
|
193
|
+
- Implement `RunLedger` writes for runs, events, tool calls, and usage if the host needs audit replay, billing, or observability; query those rows through `ProductionPersistenceStore` or host-specific read APIs.
|
|
194
|
+
- Prove secrets stay out of durable rows: no provider credentials, provider instances, credential resolvers, API keys, raw provider clients, or unredacted payloads in session, branch, ledger, usage, idempotency, migration, or definition records.
|
|
195
|
+
- Keep Prism core dependency-free: no ORM, migrations, connection pool, or database driver belongs in `@arnilo/prism`.
|
|
196
|
+
|
|
197
|
+
## Adapter performance guidance
|
|
198
|
+
|
|
199
|
+
Database adapters should keep Prism reads and writes cursor-shaped and indexed. Do not add an ORM or adapter dependency to Prism core; implement this in host code.
|
|
200
|
+
|
|
201
|
+
Minimum production guidance:
|
|
202
|
+
|
|
203
|
+
- **Branch context:** implement `SessionStore.readBranchPath(query)` with an ancestor query / recursive CTE. Treat `SessionStore.list(sessionId)` as an O(n) development fallback only.
|
|
204
|
+
- **Cursor pagination:** every `query*` method should honor `cursor`, `limit`, and `order`. Encode cursors from indexed columns such as `(timestamp, id)`, `(started_at, id)`, `(recorded_at, id)`, or `(run_id, sequence)`; never use offset pagination for long sessions.
|
|
205
|
+
- **Batch appends:** `SessionStore.append()` is single-entry because the runtime advances one branch leaf at a time. Hosts may batch inside their DB/ledger adapters for `RunLedger` rows, but the adapter must preserve per-run event order and must not acknowledge writes before durable enqueue/commit.
|
|
206
|
+
- **Event sequence allocation:** allocate a monotonic `sequence` per `run_id` when inserting `prism_agent_events`. Use it with `run_id` for stable event timeline pagination when timestamps collide.
|
|
207
|
+
- **Run/event/usage query shapes:** runs page by `(session_id, started_at, id)` or `(branch_id, started_at, id)`; events page by `(run_id, sequence)` or `(session_id, timestamp, id)`; usage pages by `(run_id, recorded_at, id)` or `(session_id, recorded_at, id)`.
|
|
208
|
+
- **Host-owned sizing:** hosts own connection pools, transaction timeouts, page-size caps, queue/batch size, retention jobs, partitioning, and tenant/account/user isolation. Prism does not guess production limits.
|
|
209
|
+
- **Security:** persist redacted `SessionEntry`, `AgentEventRecord`, `ToolCallRecord`, and `UsageRecord` data only. Never store provider objects, credential resolvers, API keys, or raw provider clients.
|
|
210
|
+
|
|
211
|
+
## Indexes
|
|
212
|
+
|
|
213
|
+
Recommended indexes for the reference schema. Hosts should add DB-specific partial or expression indexes as needed.
|
|
214
|
+
|
|
215
|
+
| Table | Index | Supports |
|
|
216
|
+
| --- | --- | --- |
|
|
217
|
+
| `prism_sessions` | `(tenant_id, account_id, user_id, created_at)` | tenant-scoped session listing |
|
|
218
|
+
| `prism_sessions` | `(expires_at)` | retention expiry scans |
|
|
219
|
+
| `prism_sessions` | `(agent_definition_id, agent_definition_version)` | definition-version usage |
|
|
220
|
+
| `prism_branches` | `(session_id, name)` | named branch lookup |
|
|
221
|
+
| `prism_branches` | `(leaf_entry_id)` | leaf-to-branch resolution |
|
|
222
|
+
| `prism_session_entries` | `(session_id, parent_id)` | parent existence checks and child lookups |
|
|
223
|
+
| `prism_session_entries` | `(session_id, kind, timestamp)` | kind-filtered entry listing |
|
|
224
|
+
| `prism_session_entries` | `(session_id, run_id, timestamp)` | run-scoped entry listing |
|
|
225
|
+
| `prism_session_entries` | `(session_id, timestamp, id)` | cursor pagination |
|
|
226
|
+
| `prism_session_entries` | `(session_id, id)` | append parent validation and recursive branch reads |
|
|
227
|
+
| `prism_session_append_idempotency` | unique `(session_id, expected_parent_id, idempotency_key)` | append retry deduplication |
|
|
228
|
+
| `prism_runs` | `(session_id, started_at)` | run history |
|
|
229
|
+
| `prism_runs` | `(branch_id, started_at)` | branch-scoped runs |
|
|
230
|
+
| `prism_runs` | `(status, finished_at)` | retention/completion scans |
|
|
231
|
+
| `prism_agent_events` | `(session_id, timestamp, id)` | event stream pagination |
|
|
232
|
+
| `prism_agent_events` | `(run_id, timestamp, id)` | run event stream |
|
|
233
|
+
| `prism_agent_events` | `(run_id, sequence)` | stable per-run event timeline pagination |
|
|
234
|
+
| `prism_agent_events` | `(session_id, type, timestamp)` | event-type filtering |
|
|
235
|
+
| `prism_agent_events` | `(entry_id)` | entry-to-event lookup |
|
|
236
|
+
| `prism_tool_calls` | `(session_id, name, started_at)` | tool usage by name |
|
|
237
|
+
| `prism_tool_calls` | `(run_id, started_at)` | run tool-call listing |
|
|
238
|
+
| `prism_tool_calls` | `(tool_call_id)` | deduplication / replay |
|
|
239
|
+
| `prism_usage` | `(session_id, recorded_at)` | usage aggregation |
|
|
240
|
+
| `prism_usage` | `(run_id, recorded_at)` | run usage |
|
|
241
|
+
| `prism_agent_definitions` | `(name, version)` | definition lookup |
|
|
242
|
+
| `prism_retention_policies` | `(tenant_id, account_id, user_id)` | policy listing |
|
|
243
|
+
| `prism_migrations` | `(name, version)` | applied-migration uniqueness |
|
|
244
|
+
|
|
245
|
+
Run idempotency keys are written by the runtime into `RunRecord.idempotencyKey` and the `prism_runs.idempotency_key` column. Hosts should add a unique index on `(tenant_id, idempotency_key)` or `(account_id, idempotency_key)` in `prism_runs` for run-level deduplication. Append idempotency uses the separate `prism_session_append_idempotency` unique key above because `SessionEntry` itself does not carry `idempotencyKey`.
|
|
246
|
+
|
|
247
|
+
## Conditional append transaction pattern
|
|
248
|
+
|
|
249
|
+
Implement `SessionStore.append(entry, options)` in one DB transaction:
|
|
250
|
+
|
|
251
|
+
1. If `options.idempotencyKey` exists, insert `(session_id, expected_parent_id, idempotency_key, entry_id)` into `prism_session_append_idempotency`. A unique-key hit means an exact retry; raise/return a `SessionAppendConflictError` with `idempotencyDuplicate: true` (or no-op if your adapter deliberately chooses idempotent success).
|
|
252
|
+
2. If `options.expectedParentId` exists, verify that `(session_id, id)` exists in `prism_session_entries`. If missing, rollback and raise `SessionAppendConflictError` with `expectedParentId`.
|
|
253
|
+
3. Insert the `prism_session_entries` row. A duplicate entry id should fail the transaction.
|
|
254
|
+
4. Optionally update a `prism_branches.leaf_entry_id` row with a compare-and-swap if the host wants one-writer linear branches. Prism's built-in stores use existence-validation so checkout/fork can intentionally create two children of the same existing parent.
|
|
255
|
+
|
|
256
|
+
This keeps append guards O(1) with indexes, prevents dangling parent links, and deduplicates exact retries without forcing every branch to be linear.
|
|
257
|
+
|
|
258
|
+
## Run, event, and usage query shapes
|
|
259
|
+
|
|
260
|
+
Use cursor columns that match query filters:
|
|
261
|
+
|
|
262
|
+
```sql
|
|
263
|
+
-- Run history for one session.
|
|
264
|
+
CREATE INDEX prism_runs_session_started_idx ON prism_runs (session_id, started_at, id);
|
|
265
|
+
|
|
266
|
+
-- Stable event pagination within one run.
|
|
267
|
+
CREATE INDEX prism_agent_events_run_sequence_idx ON prism_agent_events (run_id, sequence);
|
|
268
|
+
|
|
269
|
+
-- Usage totals / billing reads by run.
|
|
270
|
+
CREATE INDEX prism_usage_run_recorded_idx ON prism_usage (run_id, recorded_at, id);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
For NoSQL stores, use equivalent partition/sort keys: partition by `session_id` or `run_id`; sort by `started_at`, `recorded_at`, or event `sequence`. Keep page sizes capped by host policy. `total` is optional because counting large partitions can be expensive.
|
|
274
|
+
|
|
275
|
+
## Branch reads: no full-session scan
|
|
276
|
+
|
|
277
|
+
Implement `readBranchPath(query: SessionBranchRead): Promise<PersistencePage<SessionEntry>>` on database-backed stores. It should return the selected leaf's ancestor chain (any order is allowed; Prism's helper re-walks and orders it). Use one recursive CTE / ancestor query, for example:
|
|
278
|
+
|
|
279
|
+
```sql
|
|
280
|
+
WITH RECURSIVE branch AS (
|
|
281
|
+
SELECT * FROM prism_session_entries
|
|
282
|
+
WHERE session_id = $1
|
|
283
|
+
AND id = COALESCE($2, (SELECT leaf_entry_id FROM prism_branches WHERE session_id = $1 LIMIT 1))
|
|
284
|
+
UNION ALL
|
|
285
|
+
SELECT parent.*
|
|
286
|
+
FROM prism_session_entries parent
|
|
287
|
+
JOIN branch child ON child.parent_id = parent.id
|
|
288
|
+
WHERE parent.session_id = $1
|
|
289
|
+
)
|
|
290
|
+
SELECT * FROM branch;
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Use `cursor`/`limit` when an adapter pages very long branches. Do not implement common runtime reads by `list(sessionId)` followed by an in-memory parent walk for large production sessions; that is the development fallback only.
|
|
294
|
+
|
|
295
|
+
## Retention policies
|
|
296
|
+
|
|
297
|
+
A retention policy is a host-managed rule attached to sessions via `retention_policy_id`. Enforcement is host-owned and typically runs as a background job:
|
|
298
|
+
|
|
299
|
+
1. Select policies whose `max_age_days`, `max_entries_per_session`, or `max_total_bytes` thresholds are exceeded.
|
|
300
|
+
2. For each affected session, delete or archive entries older than the policy age, beyond the entry count, or over the byte budget.
|
|
301
|
+
3. Respect `applied_kinds` — only delete kinds listed in the policy (null means all kinds).
|
|
302
|
+
4. Compact or soft-delete sessions whose `expires_at` has passed.
|
|
303
|
+
5. Write audit metadata to the migration or host audit log; do not delete the policy row unless explicitly requested.
|
|
304
|
+
|
|
305
|
+
Retention jobs should not run inside the agent/session runtime. They are a host concern.
|
|
306
|
+
|
|
307
|
+
## Migrations
|
|
308
|
+
|
|
309
|
+
Hosts own schema migrations. Prism publishes only the TypeScript contracts; no DDL is generated or executed by the core library. Recommended migration practices:
|
|
310
|
+
|
|
311
|
+
- Use a sequential or timestamped migration naming convention.
|
|
312
|
+
- Store applied migrations in `prism_migrations` with `name`, `version`, `applied_at`, `applied_by`, and `checksum`.
|
|
313
|
+
- Make entry-kind and schema-version changes additive when possible; new kinds and versions fail closed in the JSONL parser, so DB schemas should accept the same additive expansion.
|
|
314
|
+
- Index new query columns before deploying code that uses them.
|
|
315
|
+
- Back-fill redacted flags and ownership columns before enforcing tenant isolation.
|
|
316
|
+
|
|
317
|
+
## NoSQL mapping notes
|
|
318
|
+
|
|
319
|
+
For document or wide-column stores, map the relational tables above to the store's native partitioning model:
|
|
320
|
+
|
|
321
|
+
- **Partition key:** `session_id` is usually the best partition key. For multi-tenant workloads, use a composite partition key (`tenant_id`, `session_id`) or a synthetic `tenant_session_id`.
|
|
322
|
+
- **Sort/range key:** Use `timestamp` + `id` for entries and events; use `started_at` + `id` for runs and tool calls. This supports cursor pagination and branch rebuild.
|
|
323
|
+
- **Global secondary indexes / collections:** Duplicate run, kind, type, and name dimensions into GSIs or secondary collections so queries by `run_id`, `kind`, `type`, or `tool_call_id` remain efficient without full scans.
|
|
324
|
+
- **JSON payloads:** Store `AgentEvent`, `ToolResult`, `Message`, `Usage`, `AgentDefinition`, and `data`/`metadata` values as nested documents or serialized JSONB. Redact sensitive fields before writing.
|
|
325
|
+
- **Branches:** In document stores, a branch can be a lightweight document keyed by `leaf_entry_id` that points to the session and root. Rebuild still walks `parent_id` links in entries.
|
|
326
|
+
- **Retention:** Use TTL columns or scheduled map-reduce/streaming jobs. TTL on `expires_at` or entry timestamps is the simplest NoSQL implementation.
|
|
327
|
+
|
|
328
|
+
The Node JSONL session store is a single-process development adapter. It has no cross-process locking, no migrations, no retention enforcement, and no tenant isolation. Do not use it as a production multi-writer store.
|
|
329
|
+
|
|
330
|
+
## Request/response example
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"sessionId": "session-1",
|
|
335
|
+
"runId": "run-7",
|
|
336
|
+
"kind": ["message", "event"],
|
|
337
|
+
"fromTimestamp": "2024-01-01T00:00:00Z",
|
|
338
|
+
"toTimestamp": "2024-12-31T23:59:59Z",
|
|
339
|
+
"tenantId": "tenant-a",
|
|
340
|
+
"limit": 50,
|
|
341
|
+
"order": "desc"
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Example page:
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"items": [
|
|
350
|
+
{ "id": "e3", "sessionId": "session-1", "kind": "message", "timestamp": "2024-06-15T10:00:00Z" }
|
|
351
|
+
],
|
|
352
|
+
"nextCursor": "eyJpZCI6ImUzIn0=",
|
|
353
|
+
"total": 128
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Implementation example
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import type {
|
|
361
|
+
ProductionPersistenceStore,
|
|
362
|
+
SessionEntryQuery,
|
|
363
|
+
SessionBranchRead,
|
|
364
|
+
PersistencePage,
|
|
365
|
+
SessionEntry,
|
|
366
|
+
} from "@arnilo/prism";
|
|
367
|
+
|
|
368
|
+
const dbStore: ProductionPersistenceStore = {
|
|
369
|
+
name: "host-postgres-store",
|
|
370
|
+
async queryEntries(query: SessionEntryQuery): Promise<PersistencePage<SessionEntry>> {
|
|
371
|
+
// Host owns SQL/NoSQL implementation.
|
|
372
|
+
return { items: [], nextCursor: undefined };
|
|
373
|
+
},
|
|
374
|
+
async readBranchPath(query: SessionBranchRead): Promise<PersistencePage<SessionEntry>> {
|
|
375
|
+
// Use one recursive/ancestor query, not list(sessionId) + in-memory scan.
|
|
376
|
+
return { items: [], nextCursor: undefined };
|
|
377
|
+
},
|
|
378
|
+
// ...other query methods
|
|
379
|
+
};
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Extension and configuration notes
|
|
383
|
+
|
|
384
|
+
- `ProductionPersistenceStore` is an optional extension point. The runtime does not require it.
|
|
385
|
+
- Hosts choose the database, schema, transaction, and indexing strategy. The contract only specifies query shapes.
|
|
386
|
+
- `SessionStore` (`append`/`list`/`get`/optional `readBranchPath`) can be implemented on top of `ProductionPersistenceStore` or kept separate.
|
|
387
|
+
- Cursor values and idempotency keys are host-defined and opaque to Prism.
|
|
388
|
+
|
|
389
|
+
## Security and performance notes
|
|
390
|
+
|
|
391
|
+
- **No credentials in storage.** The contracts never include `CredentialResolver`, `AIProvider`, `ProviderResolver`, provider API keys, or credential values.
|
|
392
|
+
- **Redact before storage.** Runtime session entries are redacted before `SessionStore.append`; `AgentEventRecord.event` and `ToolCallRecord.result` may contain secrets, so hosts must redact them (for example with `redactAgentEvent()` and a `SecretRedactor`) before writing to durable storage and set `redacted: true`.
|
|
393
|
+
- **Tenant isolation.** `OwnershipScope` fields are available on records and queries, but enforcement is the host's responsibility.
|
|
394
|
+
- **Pagination and branch reads.** Every query supports `cursor`/`limit`/`order` so hosts can avoid full-table or full-session scans. Loading an entire large session into memory to serve a provider context is an anti-pattern; implement `readBranchPath` and use branch-relevant filters / recursive ancestor queries.
|
|
395
|
+
- **Indexes.** Production schemas should index `sessionId`, `runId`, `parentId`, `leafId`, timestamps, tenant/account/user, event type, and entry kind. See the reference indexes above.
|
|
396
|
+
|
|
397
|
+
## Related APIs
|
|
398
|
+
|
|
399
|
+
- [Session store conformance](session-store-conformance.md): executable adapter baseline for append/idempotency/conflict/branch invariants.
|
|
400
|
+
- [Migration guide](migration.md): before/after shapes for moving from in-memory/JSONL to this contract.
|
|
401
|
+
- [Performance limits](performance.md): production sizing, subscriber queues, branch-read limits, and database adapter guidance.
|
|
402
|
+
- [Session stores and branching](session-stores-and-branching.md): `SessionStore`, `SessionEntry`, branch helpers, and runtime branch semantics.
|
|
403
|
+
- [Node JSONL session store](node-jsonl-session-store.md): development-only file adapter; not for production multi-writer storage.
|
|
404
|
+
- [Agent/session runtime](agent-session-runtime.md): sessions, runs, and event emission.
|
|
405
|
+
- [Agent events](agent-events.md): `AgentEvent` variants and redaction.
|
|
406
|
+
- [Tools](tools.md): `ToolResult`, `ToolCallContent`, and tool execution events.
|
|
407
|
+
- [Public contracts](public-contracts.md): full public contract inventory.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Extension authoring guide
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
This guide shows third-party package authors how to publish a Prism extension package without taking over a host app. An extension exports an `Extension` object with a `setup(api)` function. During explicit host loading, `setup()` registers inert contributions into host-owned registries: providers, models, auth descriptors, tools, context providers, skills, commands, input/prompt builders, compaction strategies, retry policies, store/resource/settings/credential hooks, provider request policies, system prompt contributions, and instruction injectors.
|
|
6
|
+
|
|
7
|
+
Extensions do not start agents, execute tools, read credentials, scan files, or call providers by themselves. The host app loads the extension, inspects/filters contributions, then chooses which entries become active runtime config.
|
|
8
|
+
|
|
9
|
+
## When to use it
|
|
10
|
+
|
|
11
|
+
Use an extension package when you want reusable Prism capabilities that many host apps can opt into:
|
|
12
|
+
|
|
13
|
+
- provider/model metadata and provider-package registration
|
|
14
|
+
- reusable tools, context providers, skills, commands, input builders, and prompt builders
|
|
15
|
+
- compaction/retry strategies and middleware hooks
|
|
16
|
+
- data-only manifests/resources that hosts can inspect before importing code
|
|
17
|
+
|
|
18
|
+
Do not use an extension to hide host policy. The host still owns trust, permissions, credentials, provider selection, active tool registries, skill activation, storage, UI, and sandboxing. Prism does not auto-discover or sandbox extension packages.
|
|
19
|
+
|
|
20
|
+
## Inputs / request
|
|
21
|
+
|
|
22
|
+
Package authors export a public `Extension` value:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import type { Extension } from "@arnilo/prism";
|
|
26
|
+
|
|
27
|
+
export const extension: Extension = {
|
|
28
|
+
name: "acme-prism-extension",
|
|
29
|
+
setup(api) {
|
|
30
|
+
api.registerSkill({ name: "acme.brief", instructions: "Answer briefly." });
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Host apps load it explicitly:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { createExtensionKernel } from "@arnilo/prism";
|
|
39
|
+
import { extension } from "acme-prism-extension";
|
|
40
|
+
|
|
41
|
+
import { createContributionRegistries } from "@arnilo/prism";
|
|
42
|
+
|
|
43
|
+
const registries = createContributionRegistries({ duplicate: "error" });
|
|
44
|
+
const kernel = createExtensionKernel({ registries, secrets: [apiKey] });
|
|
45
|
+
await kernel.load([extension]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Common `ExtensionAPI` registration calls:
|
|
49
|
+
|
|
50
|
+
| Call | Registers | Activation is host-owned |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `registerProviderPackage()` | `ProviderPackage` setup metadata | host calls package setup / selects provider |
|
|
53
|
+
| `registerProvider()` / `registerModel()` | provider/model records | host resolves provider/model for an agent/run |
|
|
54
|
+
| `registerAuthMethod()` | credential descriptor | host resolves actual credentials |
|
|
55
|
+
| `registerTool()` | `ToolDefinition` | host copies selected tools into an active `ToolRegistry` |
|
|
56
|
+
| `registerContextProvider()` | `ContextProvider` | host passes selected providers to agent/input assembly |
|
|
57
|
+
| `registerSkill()` | `Skill` | host selects skills via config or `RunOptions.activeSkills` |
|
|
58
|
+
| `registerInputBuilder()` / `registerPromptBuilder()` | replaceable builders | host passes selected builders to `createAgent()` / assembly |
|
|
59
|
+
| `registerCompactionStrategy()` / `registerRetryPolicy()` | strategies | host selects them in agent/run config |
|
|
60
|
+
| `registerCommand()` / `registerAgent()` | command/agent definitions | host exposes/runs selected entries |
|
|
61
|
+
| `registerProviderRequestPolicy()` / `registerSystemPromptContribution()` | provider/prompt policies | host includes selected policy/layer in runtime config |
|
|
62
|
+
| `registerInstructionInjector()` | inert instruction injector | host passes selected injectors to agent/run config |
|
|
63
|
+
| `use(hook, middleware)` | middleware hook | host passes the kernel middleware registry to runtime config |
|
|
64
|
+
|
|
65
|
+
## Outputs / response / events
|
|
66
|
+
|
|
67
|
+
`kernel.load([extension])` returns after `setup(api)` completes. The host can then inspect `kernel.registries.*.list()` or resolve named entries. Contributions stay inert until the host wires them into runtime config.
|
|
68
|
+
|
|
69
|
+
Extension errors follow the kernel policy:
|
|
70
|
+
|
|
71
|
+
- default `errorPolicy: "event"` emits an `extension_error` event with known secrets redacted
|
|
72
|
+
- `errorPolicy: "throw"` rejects/throws so the host can fail fast
|
|
73
|
+
|
|
74
|
+
The event bus and middleware registry are ordered and explicit. No hidden global extension kernel is created.
|
|
75
|
+
|
|
76
|
+
## Request/response example
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"loaded": ["acme-prism-extension"],
|
|
81
|
+
"contributed": {
|
|
82
|
+
"tools": ["acme.echo"],
|
|
83
|
+
"skills": ["acme.brief"],
|
|
84
|
+
"contextProviders": ["acme.project"]
|
|
85
|
+
},
|
|
86
|
+
"active": {
|
|
87
|
+
"tools": ["acme.echo"],
|
|
88
|
+
"skills": ["acme.brief"]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The `contributed` set is what the extension registered. The `active` set is what the host chose to pass into the runtime.
|
|
94
|
+
|
|
95
|
+
## Implementation example
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import {
|
|
99
|
+
createAgent,
|
|
100
|
+
createExtensionKernel,
|
|
101
|
+
createMockProvider,
|
|
102
|
+
createContributionRegistries,
|
|
103
|
+
createSkillRegistry,
|
|
104
|
+
createToolRegistry,
|
|
105
|
+
providerDone,
|
|
106
|
+
type Extension,
|
|
107
|
+
} from "@arnilo/prism";
|
|
108
|
+
|
|
109
|
+
export const extension: Extension = {
|
|
110
|
+
name: "acme-prism-extension",
|
|
111
|
+
setup(api) {
|
|
112
|
+
api.registerModel({ provider: "mock", model: "demo" });
|
|
113
|
+
api.registerAuthMethod({ provider: "mock", kind: "api_key", credentialName: "ACME_API_KEY" });
|
|
114
|
+
api.registerTool({
|
|
115
|
+
name: "acme.echo",
|
|
116
|
+
description: "Echo a JSON object.",
|
|
117
|
+
execute(args, ctx) {
|
|
118
|
+
return { toolCallId: ctx.toolCallId, name: "acme.echo", value: args };
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
api.registerContextProvider({
|
|
122
|
+
name: "acme.project",
|
|
123
|
+
resolve: () => [{ title: "Project", content: "Use Acme conventions." }],
|
|
124
|
+
});
|
|
125
|
+
api.registerSkill({
|
|
126
|
+
name: "acme.brief",
|
|
127
|
+
instructions: "Answer in one short paragraph.",
|
|
128
|
+
toolNames: ["acme.echo"],
|
|
129
|
+
});
|
|
130
|
+
api.registerPromptBuilder({ name: "acme.prompt", build: async (request) => request.messages });
|
|
131
|
+
api.registerCompactionStrategy({ name: "acme.compact", compact: async () => ({ summary: "summary" }) });
|
|
132
|
+
api.registerRetryPolicy({ name: "acme.retry", decide: () => ({ retry: false }) });
|
|
133
|
+
api.registerCommand({ name: "acme.status", execute: () => ({ ok: true }) });
|
|
134
|
+
api.use("provider_request", (request) => request);
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const registries = createContributionRegistries({ duplicate: "error" });
|
|
139
|
+
const kernel = createExtensionKernel({ registries, errorPolicy: "throw" });
|
|
140
|
+
await kernel.load([extension]);
|
|
141
|
+
|
|
142
|
+
// Host activation: select contributions explicitly.
|
|
143
|
+
const tool = kernel.registries.tools.resolve("acme.echo");
|
|
144
|
+
const skill = kernel.registries.skills.resolve("acme.brief");
|
|
145
|
+
const provider = createMockProvider([providerDone()]);
|
|
146
|
+
|
|
147
|
+
const agent = createAgent({
|
|
148
|
+
model: { provider: "mock", model: "demo" },
|
|
149
|
+
provider,
|
|
150
|
+
tools: createToolRegistry([tool]),
|
|
151
|
+
skills: createSkillRegistry([skill]),
|
|
152
|
+
context: kernel.registries.contextProviders.list(),
|
|
153
|
+
promptBuilder: kernel.registries.promptBuilders.resolve("acme.prompt"),
|
|
154
|
+
middleware: kernel.middleware,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await agent.createSession().run("Use the Acme extension.", { activeSkills: ["acme.brief"] });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Extension and configuration notes
|
|
161
|
+
|
|
162
|
+
- Export a stable named `Extension` value. Avoid side effects at module top level; keep registration inside `setup(api)`.
|
|
163
|
+
- Prefix contribution names (`acme.echo`, `acme.brief`) to avoid collisions. Hosts loading third-party packages should use `duplicate: "error"`.
|
|
164
|
+
- A data-only `prism` manifest can describe contributions/resources before the host imports executable package code. Manifest parsing never executes modules.
|
|
165
|
+
- `registerTool()` contributes a definition only. It does not grant permission, add allow-list entries, or execute the tool.
|
|
166
|
+
- `registerSkill()` contributes instructions only. Referenced `toolNames` are checked against host-active tools when the skill is activated.
|
|
167
|
+
- `registerAuthMethod()` and `registerCredentialResolver()` must not contain resolved credential values. Use descriptors/resolvers; the host resolves secrets at the provider/request edge.
|
|
168
|
+
- Middleware from `api.use()` runs only when the host passes `kernel.middleware` into runtime configuration.
|
|
169
|
+
- Provider packages, provider request policies, system prompt contributions, instruction injectors, builders, strategies, commands, store factories, resource loaders, settings providers, and credential resolvers are all inert until host code selects or invokes them.
|
|
170
|
+
|
|
171
|
+
## Security and performance notes
|
|
172
|
+
|
|
173
|
+
- Prism does not sandbox extension code. Hosts should load only trusted packages or run untrusted packages in their own sandbox/process before calling Prism APIs.
|
|
174
|
+
- Prism does not auto-discover extensions. Filesystem discovery is a separate opt-in scanner that reads `SKILL.md`/`manifest.json` text and still does not activate contributions.
|
|
175
|
+
- Use host trust and permission policies to deny extension setup (`extension:<name>:setup`), resource loads, and tool execution before side effects.
|
|
176
|
+
- Pass known secret values to `createExtensionKernel({ secrets })` so setup/listener errors are redacted. Redaction is exact known-secret replacement, not general secret detection.
|
|
177
|
+
- Never put API keys, OAuth tokens, provider clients, credential resolver outputs, headers, or raw secrets in manifests, registry metadata, extension events, prompts, sessions, ledgers, or idempotency keys.
|
|
178
|
+
- Extension loading performs only the code in `setup(api)` and registry/middleware/event operations. Prism adds no background workers, watchers, network calls, provider calls, filesystem scans, or tool execution.
|
|
179
|
+
- Keep `setup(api)` bounded and deterministic. Long-running initialization, remote auth flows, migrations, and approval UI belong in the host app.
|
|
180
|
+
|
|
181
|
+
## Related APIs
|
|
182
|
+
|
|
183
|
+
- [Extension kernel and event bus](extensions.md): low-level `ExtensionAPI`, registries, events, middleware, and error policy.
|
|
184
|
+
- [Contribution registries](contribution-registries.md): inert registry bundle populated by extensions.
|
|
185
|
+
- [Configuration and manifests](configuration-and-manifests.md): data-only package manifests and contribution declarations.
|
|
186
|
+
- [Contribution discovery (workspace)](contribution-discovery.md): opt-in filesystem scanner; no import or activation.
|
|
187
|
+
- [Provider packages](provider-packages.md): package-level provider/model/auth/request-policy contributions.
|
|
188
|
+
- [Tools](tools.md): host-owned active tool registry, filtering, dispatch, and permission checks.
|
|
189
|
+
- [Context and skills](context-and-skills.md): host selection and `toolNames` fail-closed skill activation.
|
|
190
|
+
- [Input and prompt assembly](input-and-prompt-assembly.md): selecting contributed builders/context/providers.
|
|
191
|
+
- [Instruction injection](instruction-injection.md): inert injectors that grant no capabilities.
|
|
192
|
+
- [Settings/auth/trust](settings-auth-trust-security.md): trust, permission, credentials, no sandbox, and redaction boundaries.
|
|
193
|
+
- [Extension conformance](extension-conformance.md): test extension setup, inertness, and error redaction/rethrow behavior.
|