@arnilo/prism 0.0.20 → 0.0.23
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 +46 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/0.1.0-readiness.md +10 -9
- package/docs/caveman.md +129 -0
- package/docs/coding-agent-tools.md +123 -21
- package/docs/coding-security.md +3 -3
- package/docs/context-and-skills.md +11 -0
- package/docs/database-persistence.md +3 -0
- package/docs/enterprise-postgres-state.md +173 -0
- package/docs/evaluations.md +13 -0
- package/docs/extensions.md +2 -0
- package/docs/host-security.md +3 -0
- package/docs/index.md +15 -10
- package/docs/migration.md +68 -0
- package/docs/model-routing.md +17 -8
- package/docs/performance.md +14 -0
- package/docs/policy-and-audit.md +12 -0
- package/docs/ponytail.md +127 -0
- package/docs/postgres-persistence.md +2 -1
- package/docs/release-and-install.md +89 -19
- package/docs/work-tools.md +17 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.23] - 2026-08-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `@arnilo/prism-enterprise-postgres`: optional PostgreSQL composition for policy decisions, evaluation records, work-mutation idempotency, and model-router state.
|
|
7
|
+
- Checked enterprise PostgreSQL conformance/restart/contention, cleanup/index/storage performance evidence, and protected `PRISM_TEST_POSTGRES_URL` gate.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- `@arnilo/prism-work-tools` idempotency uses claim/CAS lifecycle states; ambiguous connector outcomes are `unknown` and require reconciliation.
|
|
11
|
+
- `@arnilo/prism-model-router` accepts durable async state; `recordUsage`/`recordOutcome` are awaited and `providerSource` cannot bypass a supplied state store.
|
|
12
|
+
- Publishable graph: **47** manifests (was 46); `@arnilo/prism-all` includes enterprise PostgreSQL state.
|
|
13
|
+
|
|
14
|
+
### Breaking (minor, pre-1.0)
|
|
15
|
+
- Hosts implementing `IdempotencyStore` must migrate from `get`/`put` to `begin`/transition methods.
|
|
16
|
+
- Hosts using durable router state must await router methods with verified identity; synchronous `providerSource` is memory-state only.
|
|
17
|
+
|
|
18
|
+
See [docs/migration.md](docs/migration.md) for the 0.0.22 → 0.0.23 guide.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## [0.0.22] - 2026-07-31
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- `@arnilo/prism-caveman` and `@arnilo/prism-ponytail`: optional third-party behavior integrations (Phase 5).
|
|
25
|
+
- Example `examples/caveman-ponytail.ts`.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Publishable manifest count: **46** (was 44).
|
|
29
|
+
|
|
30
|
+
See [docs/migration.md](docs/migration.md) for the full 0.0.21 → 0.0.22 notes.
|
|
31
|
+
|
|
32
|
+
## [0.0.21] - 2026-07-31
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- `@arnilo/prism-coding-agent`: `repo_search` `outputMode`, bounded `glob`, optional `requireReadBeforeWrite`/`ReadPathSet`, bounded `delete`/`move`.
|
|
36
|
+
- Example `examples/coding-tools-capability-gaps.ts`.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- Default coding aggregator: 9 tools (`createCodingTools`); read-only aggregator: 4 (includes `glob`).
|
|
40
|
+
- `@arnilo/prism-coding-security`: approval + sandbox wiring for `delete`/`move`.
|
|
41
|
+
|
|
42
|
+
### Breaking (minor, pre-1.0)
|
|
43
|
+
- Hosts asserting exact `createCodingTools().length === 6` or readonly length `3` must update (now 9 / 4).
|
|
44
|
+
- Custom sandbox `RepositoryOperations` must implement `glob`; full sandbox custom ops must supply `delete`/`move`.
|
|
45
|
+
|
|
46
|
+
See [docs/migration.md](docs/migration.md) for the full 0.0.20 → 0.0.21 notes.
|
|
47
|
+
|
|
48
|
+
|
|
3
49
|
## [0.0.20] - 2026-07-31
|
|
4
50
|
|
|
5
51
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -100,5 +100,5 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
|
|
|
100
100
|
export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
|
|
101
101
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
102
102
|
export declare const name = "prism";
|
|
103
|
-
export declare const version = "0.0.
|
|
103
|
+
export declare const version = "0.0.23";
|
|
104
104
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -54,6 +54,6 @@ export { applyThinkingLevel, isThinkingLevel, normalizeThinkingLevel, THINKING_L
|
|
|
54
54
|
export { createToolParameterValidator, createToolRegistry, dispatchToolCall, filterTools } from "./tools.js";
|
|
55
55
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
56
56
|
export const name = "prism";
|
|
57
|
-
export const version = "0.0.
|
|
57
|
+
export const version = "0.0.23";
|
|
58
58
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
59
59
|
//# sourceMappingURL=index.js.map
|
package/docs/0.1.0-readiness.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# 0.1.0 / 1.0 Readiness Gates
|
|
2
2
|
|
|
3
|
-
Status: **0.0.
|
|
3
|
+
Status: **0.0.23** is the current release line (Phase 6 production enterprise PostgreSQL state adapters); **1.0** readiness remains operator-gated, not automatic.
|
|
4
4
|
|
|
5
5
|
This page distills runnable readiness gates into one command-per-gate table. The
|
|
6
6
|
**Last evidence** column records the 2026-07-26 **0.0.16** baseline snapshot
|
|
7
7
|
(Phase 11, Node v24.18.0, Linux x86_64). Treat it as historical floor evidence,
|
|
8
8
|
not the current release tag. Re-run each gate on the target release tree before
|
|
9
|
-
cutting 0.0.
|
|
9
|
+
cutting 0.0.23 / 1.0. The decision to cut 1.0 stays with the operator after
|
|
10
10
|
operator-gated legs run in a protected environment and Phase 12 demand evidence
|
|
11
11
|
exists.
|
|
12
12
|
|
|
@@ -14,14 +14,15 @@ Evidence trail: [`docs/review-coverage-2026-07-26-phase-11.md`](./review-coverag
|
|
|
14
14
|
(addenda 0–9), [`docs/release-and-install.md`](./release-and-install.md),
|
|
15
15
|
[`docs/migration.md`](./migration.md), [`docs/performance.md`](./performance.md).
|
|
16
16
|
|
|
17
|
-
## Current line (0.0.
|
|
17
|
+
## Current line (0.0.23)
|
|
18
18
|
|
|
19
19
|
| Item | Status |
|
|
20
20
|
|---|---|
|
|
21
|
-
| Published graph | **
|
|
22
|
-
| Phase
|
|
23
|
-
| Docs tripwires | `node --test dist/__tests__/docs.test.js` — migration section `0.0.
|
|
24
|
-
|
|
|
21
|
+
| Published graph | **47** publishable manifests at **0.0.23** (`docs/release-and-install.md`) |
|
|
22
|
+
| Phase 6 enterprise state | `@arnilo/prism-enterprise-postgres`: policy/evaluation/work-idempotency/router state, checksummed migration, explicit cleanup |
|
|
23
|
+
| Docs tripwires | `node --test dist/__tests__/docs.test.js` — migration section `0.0.22 → 0.0.23 production enterprise state adapters` documents idempotency and async-router changes |
|
|
24
|
+
| Protected database evidence | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres`; benchmark evidence fixes 10 scenarios, 14 index plans, and 50/100 ms p95 ceilings |
|
|
25
|
+
| Readiness table below | **0.0.16 measured values** remain historical network-free baseline; 0.0.23 database evidence is recorded separately |
|
|
25
26
|
|
|
26
27
|
## Gate table
|
|
27
28
|
|
|
@@ -39,7 +40,7 @@ Evidence trail: [`docs/review-coverage-2026-07-26-phase-11.md`](./review-coverag
|
|
|
39
40
|
| Whitespace hygiene | `git diff --check` | clean | CI |
|
|
40
41
|
| Publish order + tarball validation | `node scripts/release.mjs publish --version <v> --dry-run --allow-dirty --allow-untagged` | 44/44 packages `dry-run`, deterministic dependency order, no failures | Operator (dry-run), CI |
|
|
41
42
|
| Node 20 compatibility | CI `node20-compat` (build + public-import smoke) | all 21 root exports import cleanly on Node 20.20.2 | CI |
|
|
42
|
-
| PostgreSQL suite | `npm run test:postgres` |
|
|
43
|
+
| PostgreSQL suite | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres` | 57 checks including enterprise migration/restart/contention/cleanup; operator-gated | Operator |
|
|
43
44
|
| Keychain / live-provider suites | `npm run test:live` (protected) | **operator-gated** (requires credentials) | Operator |
|
|
44
45
|
| SAST | GitHub CodeQL | **operator-gated** (runs in CI workflow) | CI |
|
|
45
46
|
| Signed, provenance publication | `npm run release:publish` (clean tagged tree, OIDC) | **operator-gated** (see "Remaining for 1.0") | Operator |
|
|
@@ -79,7 +80,7 @@ Deterministic budgets (CI gate, `scripts/budget-gate.test.mjs`):
|
|
|
79
80
|
| Root unpacked bytes | 2,043,402 | +5% | 2.1 MB (within) |
|
|
80
81
|
| Root file count | 270 | +5% | 270 |
|
|
81
82
|
| Cold-startup import | 38 ms | ceiling 250 ms | ~38 ms |
|
|
82
|
-
| Aggregate packed (
|
|
83
|
+
| Aggregate packed (47 manifests, reference only) | 1,217,694 | +10% | remeasure for the 0.0.23 graph before release |
|
|
83
84
|
|
|
84
85
|
Benchmark medians (on-demand evidence, `scripts/benchmark-0.0.16.mjs`, ±25%):
|
|
85
86
|
|
package/docs/caveman.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Caveman behavior integration
|
|
2
|
+
|
|
3
|
+
## What it does
|
|
4
|
+
|
|
5
|
+
`@arnilo/prism-caveman` is an optional package that wires [juliusbrussee/caveman](https://github.com/juliusbrussee/caveman) into Prism contribution contracts.
|
|
6
|
+
|
|
7
|
+
It registers upstream skills and commands, injects active level prompt slices via `InstructionInjector`, and persists level as session custom `caveman-level` entries. Import and extension `setup` without a resolvable upstream path fail closed with a bounded redacted error and register zero contributions.
|
|
8
|
+
|
|
9
|
+
Upstream prompt fragments, skill bodies, and rules load from the host-supplied upstream checkout — Prism does not reimplement or vendor Caveman content.
|
|
10
|
+
|
|
11
|
+
## When to use it
|
|
12
|
+
|
|
13
|
+
Use it when a host wants terse token-efficient communication modes (`lite`, `full`, `ultra`, wenyan variants, `micro`) with upstream Caveman skills (`caveman-commit`, `caveman-review`, `caveman-stats`, `caveman-compress`, `caveman-help`, `cavecrew`) in a Prism extension kernel.
|
|
14
|
+
|
|
15
|
+
Skip it when you do not have a local Caveman checkout (Caveman is not published on npm) or when you only need progressive skill catalog without mode injection.
|
|
16
|
+
|
|
17
|
+
Pair with Phase 3 progressive disclosure: register `createLoadSkillTool` and keep `skillsDisclosure: "progressive"` so full `SKILL.md` bodies stay catalog-only; mode slices come from the `caveman-mode` injector, not eager skill bodies.
|
|
18
|
+
|
|
19
|
+
## Inputs / request
|
|
20
|
+
|
|
21
|
+
`createCavemanExtension(options)`:
|
|
22
|
+
|
|
23
|
+
| Field | Type | Required | Purpose |
|
|
24
|
+
| --- | --- | --- | --- |
|
|
25
|
+
| `upstreamPath` | `string` | yes | Absolute path to a Caveman checkout containing `skills/`. |
|
|
26
|
+
| `defaultLevel` | `CavemanLevel` | no | Initial level when no session entry exists (default upstream: `full`). |
|
|
27
|
+
| `showStatus` | `boolean` | no | Emit `caveman:status` extension events on level changes. |
|
|
28
|
+
| `appendEntry` | `(entry, opts?) => Promise<void>` | yes | Host session append (OM `attach` pattern). |
|
|
29
|
+
| `getEntries` | `() => readonly SessionEntry[] \| Promise<...>` | yes | Current branch entries for level restore. |
|
|
30
|
+
| `configPath` | `string` | no | Bounded local config file for `defaultLevel` / `showStatus`. |
|
|
31
|
+
|
|
32
|
+
`CavemanLevel`: `off` \| `lite` \| `full` \| `ultra` \| `wenyan-lite` \| `wenyan` \| `wenyan-ultra` \| `micro`.
|
|
33
|
+
|
|
34
|
+
Session custom entry shape:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{ "kind": "custom", "data": { "type": "caveman-level", "level": "full" } }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Registered skills: `caveman`, `caveman-commit`, `caveman-review`, `caveman-stats`, `caveman-compress`, `caveman-help`, `cavecrew`.
|
|
41
|
+
|
|
42
|
+
Registered commands: `caveman`, `caveman-init`, `caveman-commit`, `caveman-review`, `caveman-stats`, `caveman-compress`.
|
|
43
|
+
|
|
44
|
+
## Outputs / response / events
|
|
45
|
+
|
|
46
|
+
| Export | Purpose |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `createCavemanExtension(options)` | Returns an inert `Extension` until `kernel.load([...])`. |
|
|
49
|
+
| `caveman-mode` injector | `InstructionInjector` — upstream filtered `skills/caveman/SKILL.md` slice when level ≠ `off`. |
|
|
50
|
+
| `caveman` command | Set level (`/caveman lite\|full\|ultra\|wenyan\|micro\|off`) or toggle `off`↔`full`. |
|
|
51
|
+
| Alias commands | Dispatch `{ skill, dispatch: "load_skill" }` metadata for companion skills. |
|
|
52
|
+
| `caveman:status` event | Optional metadata when `showStatus: true`. |
|
|
53
|
+
|
|
54
|
+
Deactivation phrases `stop caveman` and `normal mode` clear active injection without erasing session history.
|
|
55
|
+
|
|
56
|
+
## Request/response example
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{ "command": "caveman", "args": { "level": "ultra" }, "sessionId": "s1" }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{ "kind": "custom", "data": { "type": "caveman-level", "level": "ultra" } }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Implementation example
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createCavemanExtension } from "@arnilo/prism-caveman";
|
|
70
|
+
import {
|
|
71
|
+
createExtensionKernel,
|
|
72
|
+
createLoadSkillTool,
|
|
73
|
+
createLoadedSkillSet,
|
|
74
|
+
createMemorySessionStore,
|
|
75
|
+
createSkillRegistry,
|
|
76
|
+
createSessionEntry,
|
|
77
|
+
} from "@arnilo/prism";
|
|
78
|
+
|
|
79
|
+
const store = createMemorySessionStore();
|
|
80
|
+
const callbacks = {
|
|
81
|
+
appendEntry: async (entry, options) => store.append(entry, options),
|
|
82
|
+
getEntries: async () => store.list("s1"),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const kernel = createExtensionKernel({ errorPolicy: "throw" });
|
|
86
|
+
await kernel.load([
|
|
87
|
+
createCavemanExtension({
|
|
88
|
+
upstreamPath: "/path/to/juliusbrussee-caveman",
|
|
89
|
+
defaultLevel: "full",
|
|
90
|
+
...callbacks,
|
|
91
|
+
}),
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
const registry = createSkillRegistry(kernel.registries.skills.list());
|
|
95
|
+
const loaded = createLoadedSkillSet();
|
|
96
|
+
const loadSkill = createLoadSkillTool({ registry, loaded });
|
|
97
|
+
|
|
98
|
+
await kernel.registries.commands.get("caveman")!.execute({ level: "lite" }, { sessionId: "s1" });
|
|
99
|
+
// Select instructionInjectors: ["caveman-mode"] on runs that should receive level slices.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See `examples/caveman-ponytail.ts` for progressive catalog + `load_skill` wiring with fixture upstream trees (network-free).
|
|
103
|
+
|
|
104
|
+
## Extension and configuration notes
|
|
105
|
+
|
|
106
|
+
- Import alone registers nothing and starts no timers, watchers, or network I/O (`sideEffects: false`).
|
|
107
|
+
- `kernel.load` calls `setup`, which resolves upstream first; failure throws before any `register*`.
|
|
108
|
+
- Level restore scans `getEntries()` for the latest `data.type === "caveman-level"` — same OM attach pattern; core does not auto-emit `session_start`.
|
|
109
|
+
- Host must register `createLoadSkillTool` and pass `skillsDisclosure: "progressive"` for catalog-only skill bodies.
|
|
110
|
+
- `caveman-stats` dispatches skill metadata only; full stats need host session-log integration.
|
|
111
|
+
- `caveman-init` returns upstream guidance text; it does not write files in the host repo.
|
|
112
|
+
- No TUI status bar; optional `caveman:status` events for host UI.
|
|
113
|
+
|
|
114
|
+
## Security and performance notes
|
|
115
|
+
|
|
116
|
+
- Upstream `SKILL.md` and injected text are untrusted host-supplied content; reads are size-bounded (`MAX_SKILL_FILE_BYTES` 256 KiB, `MAX_INJECTED_INSTRUCTION_BYTES` 32 KiB).
|
|
117
|
+
- Config read/write is bounded (`MAX_CONFIG_FILE_BYTES` 16 KiB) at host-owned `configPath` only.
|
|
118
|
+
- Errors redact home directories and absolute paths.
|
|
119
|
+
- Setup is O(skills) directory scan; mode read/write is O(1) per change; injection is O(1) upstream lookup per turn.
|
|
120
|
+
- Session custom entries respect host session ownership and redaction policies.
|
|
121
|
+
|
|
122
|
+
## Related APIs
|
|
123
|
+
|
|
124
|
+
- [Ponytail behavior integration](ponytail.md): complementary lazy-minimalism mode package.
|
|
125
|
+
- [Extension kernel and event bus](extensions.md): `kernel.load` and contribution registration.
|
|
126
|
+
- [Context and skills](context-and-skills.md): progressive disclosure + `createLoadSkillTool`.
|
|
127
|
+
- [Instruction injection](instruction-injection.md): `caveman-mode` injector selection.
|
|
128
|
+
- [Observational memory compaction package](compaction-observational-memory.md): `appendEntry` / `getEntries` attach precedent.
|
|
129
|
+
- [Migration guide](migration.md): `0.0.21 → 0.0.22` install and opt-in notes.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What it does
|
|
4
4
|
|
|
5
|
-
`@arnilo/prism-coding-agent` is an optional first-party package that provides host shell/filesystem/repository tools as Prism `ToolDefinition` objects. It ships
|
|
5
|
+
`@arnilo/prism-coding-agent` is an optional first-party package that provides host shell/filesystem/repository tools as Prism `ToolDefinition` objects. It ships nine default coding tools — `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, `move` — plus opt-in structured Git/check set (`createGitTools`), opt-in `createAskUserDecisionTool({ ask })`, and bounded coding-plan/checkpoint helpers. The tools are **inert** until a host imports them and registers them into a `ToolRegistry`. Hosts may register any subset, omit aggregators entirely, or mix first-party tools with host-owned `ToolDefinition`s. Behavior for shell/read/write/edit is a behavioral port of the pi coding agent's tools, adapted to Prism's `ToolDefinition` / `ToolResult` contracts (no `@earendil-works/*` or `typebox` dependencies; only `diff` plus the Node standard library). List/search/glob/Git are native Prism tools with no picomatch/ripgrep/Git-library dependency (hand-rolled `*`/`?`/`**` glob matcher).
|
|
6
6
|
|
|
7
7
|
| Export | Purpose |
|
|
8
8
|
| --- | --- |
|
|
@@ -11,14 +11,18 @@
|
|
|
11
11
|
| `createWriteTool(cwd, options?)` | `write` tool: create or overwrite a file, creating parent directories. |
|
|
12
12
|
| `createEditTool(cwd, options?)` | `edit` tool: precise exact-then-fuzzy text replacement in an existing file. |
|
|
13
13
|
| `createRepoListTool(cwd, options?)` | `repo_list` tool: bounded deterministic repository listing. |
|
|
14
|
-
| `createRepoSearchTool(cwd, options?)` | `repo_search` tool: bounded literal text search. |
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
14
|
+
| `createRepoSearchTool(cwd, options?)` | `repo_search` tool: bounded literal text search (`outputMode`: content / files_with_matches / count). |
|
|
15
|
+
| `createGlobTool(cwd, options?)` | `glob` tool: bounded filename-pattern match (`*` / `?` / `**`; no brace expansion). |
|
|
16
|
+
| `createDeleteTool(cwd, options?)` | `delete` tool: high-risk delete of a file or empty directory (no recursive delete, no trash). |
|
|
17
|
+
| `createMoveTool(cwd, options?)` | `move` tool: high-risk rename/move within the workspace (`overwrite` default false). |
|
|
18
|
+
| `createReadPathSet()` | Session-scoped path set for optional `requireReadBeforeWrite` soft guard. |
|
|
19
|
+
| `createCodingTools(cwd, options?)` | Default nine tools (`shell`, `read`, `write`, `edit`, `repo_list`, `repo_search`, `glob`, `delete`, `move`). |
|
|
20
|
+
| `createReadOnlyTools(cwd, options?)` | Read-only subset: `read`, `repo_list`, `repo_search`, `glob`. |
|
|
17
21
|
| `createAllTools(cwd, options?)` | Identical to `createCodingTools` (Git tools remain opt-in via `createGitTools`). |
|
|
18
22
|
| `createGitTools(cwd, options?)` | Opt-in Git tools (`git_status`/`git_diff`/`git_branch`/`git_worktree`/`git_apply`/`git_commit`/`git_pr_handoff`) plus optional `coding_check`. |
|
|
19
23
|
| `createCodingCheckTool(cwd, options)` | Named host-declared checks; model selects only a name. |
|
|
20
24
|
| `createAskUserDecisionTool(options)` | Opt-in user decision tool (`ask_user_decision`); host supplies `ask` callback. Not in default aggregators. |
|
|
21
|
-
| `createLocalRepositoryOperations(limits?)` | Default streaming Node filesystem backend for list/search. |
|
|
25
|
+
| `createLocalRepositoryOperations(limits?)` | Default streaming Node filesystem backend for list/search/glob. |
|
|
22
26
|
| `createGitOperations(options)` | Typed Git operations backend (argument arrays, safe config, finite output). |
|
|
23
27
|
| `buildCodingCheckpointMetadata` / `validateCodingCheckpointMetadata` / `assertCodingResumeAllowed` | Bounded durable coding-task metadata for workflow `state.coding` (no second runtime). |
|
|
24
28
|
| `writeCodingPlanFile` / `readCodingPlanFile` / `createCodingPlanMarkdown` / `parseCodingPlanTodos` | Workspace plan/todo Markdown helpers with finite byte/todo caps and hash verification. |
|
|
@@ -66,13 +70,39 @@ const tools = createCodingTools(workspaceRoot, {
|
|
|
66
70
|
| `read` | `read` |
|
|
67
71
|
| `write` | `write` |
|
|
68
72
|
| `edit` | `edit` |
|
|
69
|
-
| `repo_list` / `repo_search` | _(native; no pi equivalent)_ |
|
|
73
|
+
| `repo_list` / `repo_search` / `glob` | _(native; no pi equivalent)_ |
|
|
74
|
+
| `delete` / `move` | _(native; no pi equivalent)_ |
|
|
75
|
+
|
|
76
|
+
### Tool selection guide
|
|
77
|
+
|
|
78
|
+
| Need | Prefer | Avoid |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| Enumerate directories | `repo_list` | `shell` `find`/`ls` |
|
|
81
|
+
| Match filename patterns | `glob` | `shell` `find` |
|
|
82
|
+
| Find text in files | `repo_search` | `shell` `grep`/`rg` |
|
|
83
|
+
| Read one file (paged) | `read` | `shell` `cat` |
|
|
84
|
+
| Create / full overwrite | `write` | — |
|
|
85
|
+
| Targeted replace | `edit` | full `write` rewrite when a small edit works |
|
|
86
|
+
| Remove file / empty dir | `delete` | `shell` `rm` |
|
|
87
|
+
| Rename / relocate | `move` | `shell` `mv` |
|
|
88
|
+
| Arbitrary process | `shell` | dedicated tools above |
|
|
89
|
+
|
|
90
|
+
### Phase 4 non-goals (0.0.21)
|
|
91
|
+
|
|
92
|
+
These are **out of scope** for this package release (see roadmap Phase 9 / later):
|
|
93
|
+
|
|
94
|
+
- **No PDF / document reader** — text and supported images only via `read`.
|
|
95
|
+
- **No trash / recycle daemon** — `delete` / `move` are permanent; host undo is not automatic.
|
|
96
|
+
- **No PTY / interactive process control** — `shell` is one-shot exec with bounded capture.
|
|
97
|
+
- **No LSP / language-server tools** — use host-owned tools if needed.
|
|
98
|
+
- **No recursive directory delete** — `delete` refuses non-empty directories.
|
|
99
|
+
- **No brace-expansion globs** — `glob` supports only `*`, `?`, and `**`.
|
|
70
100
|
|
|
71
101
|
## Inputs / request
|
|
72
102
|
|
|
73
103
|
### `shell`
|
|
74
104
|
|
|
75
|
-
Run a shell command and return combined stdout+stderr.
|
|
105
|
+
Run a shell command and return combined stdout+stderr. Prefer dedicated coding tools (table above) when they fit.
|
|
76
106
|
|
|
77
107
|
**Inputs:**
|
|
78
108
|
|
|
@@ -141,7 +171,7 @@ const read = createReadTool(cwd, {
|
|
|
141
171
|
|
|
142
172
|
### `write`
|
|
143
173
|
|
|
144
|
-
Create or overwrite a file, creating parent directories as needed.
|
|
174
|
+
Create or **overwrite** a file (full replace), creating parent directories as needed. Prefer `edit` for targeted changes.
|
|
145
175
|
|
|
146
176
|
**Inputs:**
|
|
147
177
|
|
|
@@ -149,6 +179,7 @@ Create or overwrite a file, creating parent directories as needed.
|
|
|
149
179
|
| --- | --- | --- |
|
|
150
180
|
| `path` | `string` | Path to the file to write (relative or absolute). Required. |
|
|
151
181
|
| `content` | `string` | Content to write (empty string creates an empty file). Required. |
|
|
182
|
+
| `force` | `boolean` | Bypass optional read-before-write guard when the host enabled `requireReadBeforeWrite`. |
|
|
152
183
|
|
|
153
184
|
**Outputs:** a `TextContent` confirmation naming the **absolute path** with UTF-8 byte and line counts (e.g. `Successfully wrote 42 bytes (3 lines) to /abs/path.txt`). `maxInputBytes` defaults to 8 MiB (64 MiB hard cap); oversized UTF-8 input fails before policy evaluation, directory creation, or write. Write failures and abort are error results. Empty `content` is valid.
|
|
154
185
|
|
|
@@ -156,6 +187,19 @@ Default local `writeFile` uses same-directory temp + `rename` so a crash mid-wri
|
|
|
156
187
|
|
|
157
188
|
`write` result `metadata`: `{ bytes, lines, path }` (absolute path). Concurrent writes to the same path serialize through `withFileMutationQueue`; writes to different paths run in parallel.
|
|
158
189
|
|
|
190
|
+
### Optional read-before-write guard
|
|
191
|
+
|
|
192
|
+
Hosts may opt in to a session-scoped soft guard: share one `createReadPathSet()` across `read` / `write` / `edit` and set `requireReadBeforeWrite: true` on write/edit options. Successful `read` marks the path; unread existing-file writes/edits fail with a clear error unless `force: true`. Default is **off** (no behavior change for hosts that ignore it).
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { createReadPathSet, createReadTool, createWriteTool, createEditTool } from "@arnilo/prism-coding-agent";
|
|
196
|
+
|
|
197
|
+
const readPaths = createReadPathSet();
|
|
198
|
+
const read = createReadTool(cwd, { readPathSet: readPaths });
|
|
199
|
+
const write = createWriteTool(cwd, { requireReadBeforeWrite: true, readPathSet: readPaths });
|
|
200
|
+
const edit = createEditTool(cwd, { requireReadBeforeWrite: true, readPathSet: readPaths });
|
|
201
|
+
```
|
|
202
|
+
|
|
159
203
|
### `edit`
|
|
160
204
|
|
|
161
205
|
Precise text replacement in an existing file via exact-then-fuzzy matching.
|
|
@@ -166,8 +210,13 @@ Precise text replacement in an existing file via exact-then-fuzzy matching.
|
|
|
166
210
|
| --- | --- | --- |
|
|
167
211
|
| `path` | `string` | Path to the file to edit. Required. |
|
|
168
212
|
| `edits` | `Array<{ oldText: string, newText: string }>` | Targeted replacements, each matched against the **original** file (not incrementally). No overlapping/nested edits. Required, non-empty. |
|
|
213
|
+
| `force` | `boolean` | Bypass optional read-before-write guard when enabled. |
|
|
214
|
+
|
|
215
|
+
Each `edits[].oldText` must match a unique, non-overlapping region of the original file. Matching is exact first, then fuzzy (unicode normalization / whitespace collapse).
|
|
169
216
|
|
|
170
|
-
|
|
217
|
+
**Fuzzy silent-success tradeoff (loud):** when exact match fails, fuzzy may still apply a replacement **without warning the model**. That can edit the wrong region if `oldText` is slightly off (extra/missing whitespace, unicode lookalikes). Prefer exact `oldText` copied from a fresh `read`. Duplicate / non-unique matches already **fail closed** and leave the file unchanged — ambiguity is not silently resolved by picking the first hit.
|
|
218
|
+
|
|
219
|
+
A BOM is stripped before matching and re-prepended on write; original line endings are restored. Defaults reject targets over 8 MiB, aggregate old/new UTF-8 input over 2 MiB, or more than 100 edits (hard caps: 64 MiB, 16 MiB, and 1,000). Stat and bounded read checks run before matching or mutation. Default local `writeFile` uses same-directory temp + `rename` (crash-safe replace).
|
|
171
220
|
|
|
172
221
|
**Outputs:** a `TextContent` confirmation (`Successfully replaced N block(s) in {path}.`) plus `metadata`. Any failure — missing/unreadable file, no match, duplicate (non-unique) match, overlap, empty `oldText`, no-op edit, or abort — is an error result, and the file is left **unchanged** (the match runs before the write).
|
|
173
222
|
|
|
@@ -175,7 +224,7 @@ Each `edits[].oldText` must match a unique, non-overlapping region of the origin
|
|
|
175
224
|
|
|
176
225
|
### `repo_list`
|
|
177
226
|
|
|
178
|
-
List repository entries with deterministic relative paths. Uses Node `opendir`/`lstat` only — no glob dependency. Does not follow symlinks; rejects path escapes outside the workspace root. Hidden names and excluded basenames (default `.git`, `node_modules`, `dist`) are skipped unless `includeHidden` is set / host `exclude` is overridden.
|
|
227
|
+
List repository entries with deterministic relative paths. Uses Node `opendir`/`lstat` only — no glob dependency. Prefer `glob` when you already know a filename pattern. Prefer `repo_search` to find text inside files. Does not follow symlinks; rejects path escapes outside the workspace root. Hidden names and excluded basenames (default `.git`, `node_modules`, `dist`) are skipped unless `includeHidden` is set / host `exclude` is overridden.
|
|
179
228
|
|
|
180
229
|
**Inputs:**
|
|
181
230
|
|
|
@@ -202,10 +251,59 @@ Search text files under the workspace using literal substring match. Binary file
|
|
|
202
251
|
| `mode` | `"literal"` | Literal only (default). `regex` removed in 0.0.18. |
|
|
203
252
|
| `caseSensitive` | `boolean` | Default false. |
|
|
204
253
|
| `includeHidden` | `boolean` | Default false. |
|
|
205
|
-
| `context` | `number` | Context lines before/after each match (default 5, hard 20). |
|
|
254
|
+
| `context` | `number` | Context lines before/after each match (default 5, hard 20). Ignored for non-content `outputMode`. |
|
|
206
255
|
| `maxMatches` | `number` | Match cap (default 1,000, hard 10,000). |
|
|
256
|
+
| `outputMode` | `"content"` \| `"files_with_matches"` \| `"count"` | Result shape (default `content`). |
|
|
257
|
+
|
|
258
|
+
**Outputs:**
|
|
259
|
+
- `content` (default): ripgrep-like lines `path:line:column:text` with optional `path-` / `path+` context.
|
|
260
|
+
- `files_with_matches`: unique matching paths only.
|
|
261
|
+
- `count`: totals (`N matches in M files`) without line bodies.
|
|
262
|
+
|
|
263
|
+
Metadata includes `matches`, `truncated`, scan/skip counts; non-content modes also expose `fileCount`.
|
|
264
|
+
|
|
265
|
+
### `glob`
|
|
266
|
+
|
|
267
|
+
Find workspace files by filename pattern without shell `find`. Hand-rolled matcher: `*` (one path segment), `?` (one char), `**` (directories). Brace expansion (`{a,b}`) is **rejected**. Patterns match workspace-relative full paths (e.g. `src/util/a.ts`). Returns **files only** (directories traversed but not listed). Same exclude/hidden/depth/page/time caps as `repo_list`.
|
|
207
268
|
|
|
208
|
-
**
|
|
269
|
+
**Inputs:**
|
|
270
|
+
|
|
271
|
+
| Field | Type | Purpose |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| `pattern` | `string` | Glob pattern (required). |
|
|
274
|
+
| `path` | `string` | Workspace-relative start directory (default root). |
|
|
275
|
+
| `includeHidden` | `boolean` | Default false. |
|
|
276
|
+
| `maxDepth` | `number` | Depth cap (default 32, hard 128). |
|
|
277
|
+
| `maxResults` | `number` | Page size (default 1,000, hard 10,000). |
|
|
278
|
+
| `offset` | `number` | Matches to skip (default 0). |
|
|
279
|
+
|
|
280
|
+
**Outputs:** one relative path per line plus metadata (`truncated`, `truncatedBy`, `nextOffset`, scan counts). Continue with `offset=nextOffset` when truncated.
|
|
281
|
+
|
|
282
|
+
### `delete`
|
|
283
|
+
|
|
284
|
+
High-risk: permanently delete a **single file or empty directory**. Non-empty directories fail closed (no recursive delete). Symlinks are unlinked as links (targets not followed for containment). **No trash daemon** — host undo is not automatic; gate with approval policy.
|
|
285
|
+
|
|
286
|
+
**Inputs:**
|
|
287
|
+
|
|
288
|
+
| Field | Type | Purpose |
|
|
289
|
+
| --- | --- | --- |
|
|
290
|
+
| `path` | `string` | File or empty directory to delete. Required. |
|
|
291
|
+
|
|
292
|
+
**Outputs:** confirmation with absolute path, or error (missing, non-empty dir, escape, abort).
|
|
293
|
+
|
|
294
|
+
### `move`
|
|
295
|
+
|
|
296
|
+
High-risk: rename or move a file within the workspace. Dual-path mutation queue (lexicographic lock order). `overwrite` defaults **false**; when true, replaces an existing destination **file** only. Does not create parent directories. **No trash** — host undo is not automatic.
|
|
297
|
+
|
|
298
|
+
**Inputs:**
|
|
299
|
+
|
|
300
|
+
| Field | Type | Purpose |
|
|
301
|
+
| --- | --- | --- |
|
|
302
|
+
| `from` | `string` | Source path. Required. |
|
|
303
|
+
| `to` | `string` | Destination path. Required. |
|
|
304
|
+
| `overwrite` | `boolean` | Replace existing destination file (default false). |
|
|
305
|
+
|
|
306
|
+
**Outputs:** confirmation with absolute from/to, or error (missing source, dest exists without overwrite, escape, abort).
|
|
209
307
|
|
|
210
308
|
### Structured Git tools (`createGitTools`)
|
|
211
309
|
|
|
@@ -351,10 +449,10 @@ Minimal drop-in for any Prism app:
|
|
|
351
449
|
import { createToolRegistry } from "@arnilo/prism";
|
|
352
450
|
import { createCodingTools, createReadOnlyTools } from "@arnilo/prism-coding-agent";
|
|
353
451
|
|
|
354
|
-
// Full coding set (shell + read + write + edit + repo_list + repo_search
|
|
452
|
+
// Full coding set (shell + read + write + edit + repo_list + repo_search + glob + delete + move):
|
|
355
453
|
const tools = createToolRegistry(createCodingTools(process.cwd()));
|
|
356
454
|
|
|
357
|
-
// Or a read-only set for inspection-only agents (read + repo_list + repo_search):
|
|
455
|
+
// Or a read-only set for inspection-only agents (read + repo_list + repo_search + glob):
|
|
358
456
|
const ro = createToolRegistry(createReadOnlyTools(process.cwd()));
|
|
359
457
|
```
|
|
360
458
|
|
|
@@ -381,23 +479,27 @@ const remoteWrite = createWriteTool("/repo", {
|
|
|
381
479
|
});
|
|
382
480
|
```
|
|
383
481
|
|
|
482
|
+
Packed capability demo: `examples/coding-tools-capability-gaps.ts` (search modes, glob, read-before-write, delete/move).
|
|
483
|
+
|
|
384
484
|
## Extension and configuration notes
|
|
385
485
|
|
|
386
486
|
- **Long coding sessions.** Use `createCodingCompactionStrategy()` from optional `@arnilo/prism-compaction-llm` when history needs a bounded coding handoff. It is selected explicitly through normal `session.compact()` / agent compaction configuration, preserves raw session entries, and prioritizes file paths, patch intent, checks, plan/todo state, blockers, and verification steps. It does not read files, retain full diffs, or create a second coding runtime.
|
|
387
|
-
- **Pluggable operation backends.** Every tool accepts an `operations` seam. Custom `ReadOperations` must implement bounded `readText` plus `statFile`; custom `EditOperations` must implement `statFile`; read/write methods receive caps/signals. `BashOperations` must stream through `onData` and honor `signal`/`timeout`. Custom `RepositoryOperations` must honor depth/entry/file/match/scan/time caps and abort. A hostile custom backend can still violate its host-owned contract, so isolate it separately.
|
|
388
|
-
- **Per-tool options.** `ShellToolOptions` adds `timeout` and `maxTotalOutputBytes`; `ReadToolOptions` adds `maxScanBytes`; `WriteToolOptions`
|
|
389
|
-
- **Aggregator options.** `ToolsOptions` (`{ executionPolicy?, shell?, read?, write?, edit?, list?, search?, repository? }`) threads each sub-object to the matching tool. `createCodingTools()`, `createAllTools()`, and `createReadOnlyTools()` apply the shared policy unless that tool has an explicit per-tool override.
|
|
390
|
-
- **Sandbox composition.** Prefer `@arnilo/prism-coding-security` `createSandboxCodingComposition(cwd, { workspaceMode, sandbox, ... })` (or tools-only wrappers). `workspaceMode` is required: `"sandbox"` keeps shell/read/write/edit/list/search on one disposable tree; `"host"` runs against host cwd and never claims containment. Mixed sandbox-shell + host-FS wiring throws unless `allowMixedWorkspaceWiring: true`. Same-tree Git: `createGitTools(composition.workspaceRoot, { execFile: sandbox.execFile, commitIdentity })`.
|
|
487
|
+
- **Pluggable operation backends.** Every tool accepts an `operations` seam. Custom `ReadOperations` must implement bounded `readText` plus `statFile`; custom `EditOperations` must implement `statFile`; read/write methods receive caps/signals. `BashOperations` must stream through `onData` and honor `signal`/`timeout`. Custom `RepositoryOperations` must honor depth/entry/file/match/scan/time caps and abort (including `glob`). Custom `DeleteOperations` / `MoveOperations` must honor containment and abort. A hostile custom backend can still violate its host-owned contract, so isolate it separately.
|
|
488
|
+
- **Per-tool options.** `ShellToolOptions` adds `timeout` and `maxTotalOutputBytes`; `ReadToolOptions` adds `maxScanBytes` and optional `readPathSet`; `WriteToolOptions` / `EditToolOptions` add input caps plus optional `requireReadBeforeWrite` / `readPathSet` / `force`; list/search/glob accept `repository` limits and shared aggregator `ToolsOptions.repository`.
|
|
489
|
+
- **Aggregator options.** `ToolsOptions` (`{ executionPolicy?, shell?, read?, write?, edit?, delete?, move?, list?, search?, glob?, repository? }`) threads each sub-object to the matching tool. `createCodingTools()`, `createAllTools()`, and `createReadOnlyTools()` apply the shared policy unless that tool has an explicit per-tool override. Full membership is nine tools; read-only is `read` + `repo_list` + `repo_search` + `glob`.
|
|
490
|
+
- **Sandbox composition.** Prefer `@arnilo/prism-coding-security` `createSandboxCodingComposition(cwd, { workspaceMode, sandbox, ... })` (or tools-only wrappers). `workspaceMode` is required: `"sandbox"` keeps shell/read/write/edit/list/search/glob/delete/move on one disposable tree; `"host"` runs against host cwd and never claims containment. Mixed sandbox-shell + host-FS wiring throws unless `allowMixedWorkspaceWiring: true`. Same-tree Git: `createGitTools(composition.workspaceRoot, { execFile: sandbox.execFile, commitIdentity })`.
|
|
391
491
|
- **`ToolsOptions`** and the per-tool option types are exported from the package barrel for host configuration.
|
|
392
492
|
- No auto-discovery or manifest registration: import and register explicitly. This package registers no extensions and owns no globals (the mutation queue is a process-wide per-path map — see `ponytail:` note in the source).
|
|
393
493
|
|
|
394
494
|
## Security and performance notes
|
|
395
495
|
|
|
396
|
-
- **Host shell/filesystem access.** These tools run real commands and read/write/list/search real files. They provide **no sandbox**. Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. Shared `executionPolicy` applies to both full and read-only aggregators before filesystem/process side effects. See [Host security guide](host-security.md) and [Security/auth/trust](settings-auth-trust-security.md).
|
|
496
|
+
- **Host shell/filesystem access.** These tools run real commands and read/write/list/search/glob/delete/move real files. They provide **no sandbox**. Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. Shared `executionPolicy` applies to both full and read-only aggregators before filesystem/process side effects. See [Host security guide](host-security.md) and [Security/auth/trust](settings-auth-trust-security.md).
|
|
497
|
+
- **High-risk mutations.** `delete` and `move` are permanent (no trash). Prefer host confirmation via `ExecutionPolicy` before allowing them. Do not instruct models to bypass policy/sandbox.
|
|
397
498
|
- **Non-zero exit is not an error.** A failing command is a normal `shell` result (exit code in metadata); only timeout/abort/spawn failures are error results. Do not assume `error == undefined` means the command succeeded.
|
|
398
|
-
- **Bounded I/O.** `read` streams one page and bounds scan bytes; image/edit reads use stat plus a shared cap-enforcing reader; write/edit inputs are measured before mutation. `repo_list`/`repo_search` stream walks and charge depth/entry/file/match/scan/time before retention. Structured Git tools use argument arrays with finite output/path/ref/message/patch caps, disable hooks/credential prompts/external diff by default, and never push or open PRs. `shell` retains only a rolling display tail and synchronously spills accepted raw chunks so stream backpressure cannot grow heap; wall time and total raw output remain finite.
|
|
399
|
-
- **Per-path serialization.** Concurrent mutations to the same file serialize; concurrent mutations to different files do not block each other. The queue is a process-wide map — across sessions in one process, same-path writes still serialize (upgrade path: scope per registry if throughput matters).
|
|
499
|
+
- **Bounded I/O.** `read` streams one page and bounds scan bytes; image/edit reads use stat plus a shared cap-enforcing reader; write/edit inputs are measured before mutation. `repo_list`/`repo_search`/`glob` stream walks and charge depth/entry/file/match/scan/time before retention. Structured Git tools use argument arrays with finite output/path/ref/message/patch caps, disable hooks/credential prompts/external diff by default, and never push or open PRs. `shell` retains only a rolling display tail and synchronously spills accepted raw chunks so stream backpressure cannot grow heap; wall time and total raw output remain finite.
|
|
500
|
+
- **Per-path serialization.** Concurrent mutations to the same file serialize; concurrent mutations to different files do not block each other. `move` locks both paths in lexicographic order. The queue is a process-wide map — across sessions in one process, same-path writes still serialize (upgrade path: scope per registry if throughput matters).
|
|
400
501
|
- **Bounded image reads.** `read` rejects images over `maxImageBytes` (default 10 MB) by `stat` before read when possible; MIME is detected from magic bytes only. Optional `transformImage` is host-owned — the base package has no image-processing dependency.
|
|
502
|
+
- **Fuzzy edit risk.** Silent fuzzy success can mis-apply edits; duplicate matches fail closed. See the `edit` section above.
|
|
401
503
|
|
|
402
504
|
### Resource-limit defaults and hard caps
|
|
403
505
|
|
package/docs/coding-security.md
CHANGED
|
@@ -26,14 +26,14 @@ import type { ExecutionAction, ExecutionPolicy, ExecutionDecision } from "@arnil
|
|
|
26
26
|
|
|
27
27
|
Use this package when coding tools need path scoping, human approval, command rules, or a pluggable sandbox backend. Wire the returned policy through `createCodingTools(cwd, { executionPolicy })` or per-tool `executionPolicy` options.
|
|
28
28
|
|
|
29
|
-
Use `createDockerSandbox()` when the host wants a production-reference containment boundary. Prism does **not** claim OS-level isolation unless the host constructs this adapter (or supplies an equivalent custom `DisposableSandbox`). Default policy denies shell/write/edit without an `approve` callback and rejects paths outside configured roots. Coding shell definitions are marked `exclusive: true`, matching the approval policy's shell decision, so a single-shot turn containing shell work runs sequentially even when `toolConcurrency > 1`. Non-shell turns retain configured parallelism.
|
|
29
|
+
Use `createDockerSandbox()` when the host wants a production-reference containment boundary. Prism does **not** claim OS-level isolation unless the host constructs this adapter (or supplies an equivalent custom `DisposableSandbox`). Default policy denies shell/write/edit/delete/move without an `approve` callback and rejects paths outside configured roots. Coding shell definitions are marked `exclusive: true`, matching the approval policy's shell decision, so a single-shot turn containing shell work runs sequentially even when `toolConcurrency > 1`. Non-shell turns retain configured parallelism.
|
|
30
30
|
|
|
31
31
|
## Inputs / request
|
|
32
32
|
|
|
33
33
|
| Option | Default | Purpose |
|
|
34
34
|
| --- | --- | --- |
|
|
35
35
|
| `roots` | required | Realpath-contained filesystem roots. |
|
|
36
|
-
| `readOnly` | `false` | Deny shell/write/edit
|
|
36
|
+
| `readOnly` | `false` | Deny non-`read` actions (including shell/write/edit/delete/move). |
|
|
37
37
|
| `commandRules` | `[]` | Ordered allow/deny/approval command classification. |
|
|
38
38
|
| `approve` | none | Host callback for actions not statically allowed; omission fails closed. |
|
|
39
39
|
| `approvalCacheScope` | `"none"` | Optional `run` or `session` decision cache scope. |
|
|
@@ -110,7 +110,7 @@ const sandbox = await createDockerSandbox({
|
|
|
110
110
|
limits: { cpus: 2, memoryBytes: 2 * 1024 ** 3, maxPids: 256, workspaceBytes: 1024 ** 3 },
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
// Sandbox mode: shell/read/write/edit/list/search share one disposable tree.
|
|
113
|
+
// Sandbox mode: shell/read/write/edit/list/search/glob/delete/move share one disposable tree.
|
|
114
114
|
const { tools, composition } = createSandboxCodingComposition("/srv/jobs/task-1/source", {
|
|
115
115
|
workspaceMode: "sandbox",
|
|
116
116
|
sandbox,
|
|
@@ -196,6 +196,17 @@ await agent.createSession().run("…", { activeSkills: ["ponytail"] });
|
|
|
196
196
|
// Turn 1: catalog only. After load_skill({ name: "ponytail" }), later turns include instructions.
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
+
### Third-party behavior packages (Caveman, Ponytail)
|
|
200
|
+
|
|
201
|
+
`@arnilo/prism-caveman` and `@arnilo/prism-ponytail` register upstream skills into the extension kernel skill registry. Hosts should:
|
|
202
|
+
|
|
203
|
+
1. `kernel.load([createCavemanExtension(...), createPonytailExtension(...)])` with session `appendEntry` / `getEntries` callbacks.
|
|
204
|
+
2. Build `createSkillRegistry(kernel.registries.skills.list())` and pass `activeSkills` / `resolveActiveSkills` names.
|
|
205
|
+
3. Keep `skillsDisclosure: "progressive"` and register `createLoadSkillTool` — full `SKILL.md` bodies stay catalog-only until `load_skill`.
|
|
206
|
+
4. Select `instructionInjectors: ["caveman-mode", "ponytail-mode"]` (or subset) for mode/level slices **without** forcing `skillsDisclosure: "eager"`.
|
|
207
|
+
|
|
208
|
+
Mode slices and skill bodies are independent: the injector can add `PONYTAIL MODE ACTIVE` while `ponytail-audit` remains catalog-only until loaded. See [Caveman](caveman.md), [Ponytail](ponytail.md), and `examples/caveman-ponytail.ts`.
|
|
209
|
+
|
|
199
210
|
Pure validation without the tool: `resolveSkillLoad({ registry, name, tools, loaded, activeSkillNames })`.
|
|
200
211
|
|
|
201
212
|
### Context budget priority and skill demotion
|
|
@@ -8,6 +8,8 @@ Prism itself does not ship a production database adapter. The built-in `SessionS
|
|
|
8
8
|
|
|
9
9
|
Plan 056 Task 1 adds dialect-neutral shared primitives under `@arnilo/prism/testing/persistence-schema`, `@arnilo/prism/testing/session-store-conformance`, and `@arnilo/prism/testing/run-ledger-conformance`. Task 2 ships `@arnilo/prism-session-store-sqlite` (see [SQLite persistence](sqlite-persistence.md)); Task 3 ships `@arnilo/prism-session-store-postgres` (see [PostgreSQL persistence](postgres-persistence.md)). Both implement dialect-local SQL against the shared model; Prism core still ships no ORM, driver, or migration runner.
|
|
10
10
|
|
|
11
|
+
Release 0.0.23 additionally ships [`@arnilo/prism-enterprise-postgres`](enterprise-postgres-state.md), a separate PostgreSQL composition for policy decisions, evaluations, work-mutation claims, and model-router state. It is not a `ProductionPersistenceStore` replacement and does not store sessions/runs. Its fixed `prism_enterprise_migrations` history is independent of `prism_migrations`; hosts may use both compositions against the same validated schema.
|
|
12
|
+
|
|
11
13
|
## When to use it
|
|
12
14
|
|
|
13
15
|
Use these contracts when you write a database-backed `SessionStore` or a separate persistence adapter that needs:
|
|
@@ -461,6 +463,7 @@ const dbStore: ProductionPersistenceStore = {
|
|
|
461
463
|
## Related APIs
|
|
462
464
|
|
|
463
465
|
- [Session store conformance](session-store-conformance.md): executable adapter baseline for append/idempotency/conflict/branch invariants.
|
|
466
|
+
- [Enterprise PostgreSQL state](enterprise-postgres-state.md): durable governance and connector/router state outside the session/run contract.
|
|
464
467
|
- [Migration guide](migration.md): before/after shapes for moving from in-memory/JSONL to this contract.
|
|
465
468
|
- [Performance limits](performance.md): production sizing, subscriber queues, branch-read limits, and database adapter guidance.
|
|
466
469
|
- [Session stores and branching](session-stores-and-branching.md): `SessionStore`, `SessionEntry`, branch helpers, and runtime branch semantics.
|