@arnilo/prism 0.5.4 → 0.5.6
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 +14 -0
- package/README.md +10 -10
- package/dist/cli-runner.d.ts +8 -1
- package/dist/cli-runner.js +97 -7
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +10 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/run-limits.js +7 -2
- package/docs/cli-rpc.md +15 -1
- package/docs/credential-storage.md +1 -1
- package/docs/extension-authoring.md +8 -9
- package/docs/extensions.md +13 -1
- package/docs/graft.md +27 -5
- package/docs/{0.1.0-readiness.md → history/0.1.0-readiness.md} +6 -6
- package/docs/history/README.md +11 -0
- package/docs/{migrate-to-0.4.md → history/migrate-to-0.4.md} +4 -4
- package/docs/history/migration-0.0.md +738 -0
- package/docs/history/migration-0.1.md +151 -0
- package/docs/history/migration-0.2.md +173 -0
- package/docs/history/migration-0.3.md +39 -0
- package/docs/history/migration-0.4.md +6 -0
- package/docs/{persistence-credentials-multimodality-primitives.md → history/persistence-credentials-multimodality-primitives.md} +13 -13
- package/docs/history/release-handoffs.md +848 -0
- package/docs/{workflow-orchestration-primitives.md → history/workflow-orchestration-primitives.md} +11 -11
- package/docs/host-security.md +1 -1
- package/docs/index.md +160 -143
- package/docs/migrate-to-0.5.md +1 -1
- package/docs/migration.md +15 -1042
- package/docs/multimodal-content.md +1 -1
- package/docs/performance.md +1 -1
- package/docs/postgres-persistence.md +1 -1
- package/docs/provider-packages.md +20 -20
- package/docs/release-and-install.md +57 -842
- package/docs/runs-and-usage.md +2 -2
- package/docs/session-stores-and-branching.md +1 -122
- package/docs/sqlite-persistence.md +1 -1
- package/docs/wiki.md +46 -2
- package/docs/workflows.md +1 -1
- package/package.json +1 -1
- /package/docs/{workflow-tui-primitives.md → history/workflow-tui-primitives.md} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [0.5.6] - 2026-09-09 (plan 069)
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- **Trusted extension activation**: `activateKernel(kernel)` / `ActivatedKernelConfig` turn a loaded extension kernel into ready-to-spread `AgentConfig` contributions (tools, skills, context, middleware, commands, instruction injectors). No auto-picked single-slot builders/providers.
|
|
5
|
+
- **CLI `--extension`**: repeatable flag loads trusted extension packages — cwd-relative paths (realpath-contained) or `PRISM_EXTENSION_ALLOWLIST` specifiers — and merges their contributions into the run. Modules must export `createExtension()`, a default function, or a default `{name, setup}` object; `--config`/`--resource`/`--tool` stay rejected.
|
|
6
|
+
- **Wiki ingest**: `ingestWikiSource`, `/wiki-ingest` command, `wiki_ingest` tool, and `prism-wiki ingest` stage one external source (text, file, image, PDF; URL via a host `fetchUrl` hook with `assertSsrfAllowedUrl` first) into `raw/ingest/<utc>-<slug>/` as an immutable `source.*` + UTF-8 `extract.md`, then return a Karpathy/OKF filing brief (`metadata.trust: "untrusted_external"`). Compressed PDF/DOCX ride an optional host `extractDocument` hook. Caps: 32 MiB input / 2 MiB extract.
|
|
7
|
+
- **Ingest filing protocol**: `wiki-maintainer` skill + scaffolded `SCHEMA.md` gained the ten-step ingest procedure (catalog-first, integrate-don't-duplicate, OKF v0.2 frontmatter with `sources[].id` footnotes, index/log sync, raw layer read-only, one source per ingest).
|
|
8
|
+
- **Graft graph commands**: `/graft-init` (non-interactive `graft init --no-global`, host `initAgents`/`initYes`), `/graft-build-deep` (`build --deep` with host-configured `deepModel` — provider/model/base-url on argv, `GRAFT_API_KEY` env-only, never on argv, fails closed unconfigured) alongside the keyless `/graft-build`. New `runGraftExit` exit-code runner (build/init are plain text, not JSON) with separate `buildBudgetMs` (120s) / `deepBuildBudgetMs` (600s) / `buildMaxResultBytes` (2 MiB) budgets.
|
|
9
|
+
|
|
10
|
+
## [0.5.5] - 2026-09-08
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Byte limits are per-frame, not cumulative.** `maxRequestBytes`/`maxResponseBytes` now compare each individual provider frame (request payload, streamed event) against the cap instead of a run-lifetime byte sum, so long autonomous coding runs (many megabyte-scale frames, each far under 64 MiB) no longer trip `Run limit exceeded: maxRequestBytes` after ~40 turns. HARD stays per-frame process safety — one giant frame still fails, 40 legal frames never do. Cumulative byte counters remain in snapshots/telemetry; token/turn/wall/cost axes stay cumulative.
|
|
14
|
+
|
|
1
15
|
## [0.5.4] - 2026-09-08 (plan 067)
|
|
2
16
|
|
|
3
17
|
### Changed
|
package/README.md
CHANGED
|
@@ -162,16 +162,16 @@ printf '{"id":"1","command":"prompt","params":{"input":"Hi"}}\n' \
|
|
|
162
162
|
|
|
163
163
|
| package | version | notes |
|
|
164
164
|
| --- | --- | --- |
|
|
165
|
-
| `@arnilo/prism` | 0.5.
|
|
166
|
-
| `@arnilo/prism-coding-tools` | 0.5.
|
|
167
|
-
| `@arnilo/prism-core` | 0.5.
|
|
168
|
-
| `@arnilo/prism-providers` | 0.5.
|
|
169
|
-
| `@arnilo/prism-acp-agent` | 0.5.
|
|
170
|
-
| `@arnilo/prism-ag-ui` | 0.5.
|
|
171
|
-
| `@arnilo/prism-mcp` | 0.5.
|
|
172
|
-
| `@arnilo/prism-memory` | 0.5.
|
|
173
|
-
| `@arnilo/prism-office` | 0.5.
|
|
174
|
-
| `@arnilo/prism-web-tools` | 0.5.
|
|
165
|
+
| `@arnilo/prism` | 0.5.6 | core — runtime, CLI/RPC, templates, docs |
|
|
166
|
+
| `@arnilo/prism-coding-tools` | 0.5.6 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
|
|
167
|
+
| `@arnilo/prism-core` | 0.5.6 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
|
|
168
|
+
| `@arnilo/prism-providers` | 0.5.6 | family — all provider adapters as `/<adapter>` subpaths |
|
|
169
|
+
| `@arnilo/prism-acp-agent` | 0.5.6 | capability — ACP adapter |
|
|
170
|
+
| `@arnilo/prism-ag-ui` | 0.5.6 | capability — AG-UI/A2A/A2UI adapter |
|
|
171
|
+
| `@arnilo/prism-mcp` | 0.5.6 | capability — MCP client/server/OAuth interop |
|
|
172
|
+
| `@arnilo/prism-memory` | 0.5.6 | capability — memory plus /rag, /compaction/*, /graft, /wiki subpaths |
|
|
173
|
+
| `@arnilo/prism-office` | 0.5.6 | capability — /documents, /sheets, /diagrams subpaths |
|
|
174
|
+
| `@arnilo/prism-web-tools` | 0.5.6 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
|
|
175
175
|
<!-- generated:package-truth:inventory end -->
|
|
176
176
|
|
|
177
177
|
## Scripts
|
package/dist/cli-runner.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Readable, Writable } from "node:stream";
|
|
2
2
|
import type { AgentSession, ContributionFileKind, InstructionInjector, Skill, SystemPromptContribution } from "./contracts.js";
|
|
3
|
+
import { type ActivatedKernelConfig } from "./index.js";
|
|
3
4
|
import type { AgentBundle } from "./node/agent-definitions.js";
|
|
4
5
|
import { type RpcSessionFactory } from "./rpc.js";
|
|
5
6
|
export type CliMode = "print" | "json" | "rpc";
|
|
@@ -35,6 +36,12 @@ export interface CliOptions {
|
|
|
35
36
|
readonly injectorFiles: readonly string[];
|
|
36
37
|
/** Runtime-populated: `--instruction`/`--injector-file` resolved to live injectors. */
|
|
37
38
|
readonly resolvedInstructionInjectors: readonly InstructionInjector[];
|
|
39
|
+
/** Parsed flag: `--extension <specifier>` (repeatable). Loaded via {@link loadCliExtensions}
|
|
40
|
+
* into {@link CliOptions.activatedExtensions} before session creation. */
|
|
41
|
+
readonly extensions: readonly string[];
|
|
42
|
+
/** Runtime-populated (not a parsed flag): activated contributions from `--extension` modules
|
|
43
|
+
* (`kernel.load()` → `activateKernel()`). Merged into `createAgent()` by `agentSession`. */
|
|
44
|
+
readonly activatedExtensions?: ActivatedKernelConfig;
|
|
38
45
|
/** Parsed flag: `--no-agents-md` / `--no-system-md` skip the corresponding auto-load. */
|
|
39
46
|
readonly noAgentsMd: boolean;
|
|
40
47
|
readonly noSystemMd: boolean;
|
|
@@ -71,7 +78,7 @@ export interface CliRuntime {
|
|
|
71
78
|
/** Test injection for `prism dev`: overrides `@arnilo/prism-dev` resolution. */
|
|
72
79
|
readonly loadDevCli?: () => Promise<unknown>;
|
|
73
80
|
}
|
|
74
|
-
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n prism init <dir> [--template <name>] [--list-templates] [--provider <name>] [--with-workflows] [--with-evals] [--force]\n prism providers add <name> [--base-url <url>] [--env-key <name>] [--model <id>] [--force]\n prism dev [--port <n>] [--host <addr>] (loopback inspector; delegates into @arnilo/prism-dev)\n\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Provider id from the init provider catalog ('mock' is built in;\n real providers need their @arnilo/prism-providers package +\n credential env var)\n --model <name> Explicit model name\n --session <id> Session id\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n --discover Enable workspace contribution discovery (opt-in)\n --discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)\n --no-discovery Disable discovery even if --discover is set\n --agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)\n --no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md\n --no-system-md Skip auto-loading the global SYSTEM.md layer\n --agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)\n --system-md-file <path> Read SYSTEM.md from <path> instead (source: user)\n -h, --help Show this help\n";
|
|
81
|
+
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n prism init <dir> [--template <name>] [--list-templates] [--provider <name>] [--with-workflows] [--with-evals] [--force]\n prism providers add <name> [--base-url <url>] [--env-key <name>] [--model <id>] [--force]\n prism dev [--port <n>] [--host <addr>] (loopback inspector; delegates into @arnilo/prism-dev)\n\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Provider id from the init provider catalog ('mock' is built in;\n real providers need their @arnilo/prism-providers package +\n credential env var)\n --model <name> Explicit model name\n --session <id> Session id\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n --discover Enable workspace contribution discovery (opt-in)\n --discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)\n --no-discovery Disable discovery even if --discover is set\n --extension <specifier> Load a trusted extension module (repeatable). Relative paths load\n from the working directory; package names and absolute paths must\n be listed in PRISM_EXTENSION_ALLOWLIST (comma-separated).\n --agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)\n --no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md\n --no-system-md Skip auto-loading the global SYSTEM.md layer\n --agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)\n --system-md-file <path> Read SYSTEM.md from <path> instead (source: user)\n -h, --help Show this help\n";
|
|
75
82
|
export declare function parseCliArgs(argv: readonly string[]): CliOptions;
|
|
76
83
|
export declare function runCli(argv: readonly string[], runtime: CliRuntime): Promise<number>;
|
|
77
84
|
export declare function runPromptMode(session: AgentSession, options: CliOptions, stdout: Writable, mode: "print" | "json"): Promise<void>;
|
package/dist/cli-runner.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { basename, dirname } from "node:path";
|
|
1
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, isAbsolute, resolve, sep } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
4
5
|
import { runPrismDevSubcommand } from "./cli-dev.js";
|
|
5
6
|
import { initUsage, loadProvidersCatalog, runInitCommand } from "./cli-init.js";
|
|
6
7
|
import { providerAddUsage, runProviderAddCommand } from "./cli-provider-add.js";
|
|
7
8
|
import { createContributionRegistries, registerDiscoveredContributions } from "./contributions.js";
|
|
8
|
-
import { createAgent, createContributionRegistry, createMockProvider, providerDone, providerTextDelta, resolveInstructionInjectors, } from "./index.js";
|
|
9
|
+
import { activateKernel, createAgent, createContributionRegistry, createExtensionKernel, createMockProvider, providerDone, providerTextDelta, resolveInstructionInjectors, } from "./index.js";
|
|
9
10
|
import { discoverAgentBundles } from "./node/agent-definitions.js";
|
|
10
11
|
import { discoverContributions } from "./node/contribution-discovery.js";
|
|
11
12
|
import { registerDiscoveredInstructionInjectors } from "./node/instruction-injectors.js";
|
|
@@ -33,6 +34,9 @@ Options:
|
|
|
33
34
|
--discover Enable workspace contribution discovery (opt-in)
|
|
34
35
|
--discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)
|
|
35
36
|
--no-discovery Disable discovery even if --discover is set
|
|
37
|
+
--extension <specifier> Load a trusted extension module (repeatable). Relative paths load
|
|
38
|
+
from the working directory; package names and absolute paths must
|
|
39
|
+
be listed in PRISM_EXTENSION_ALLOWLIST (comma-separated).
|
|
36
40
|
--agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)
|
|
37
41
|
--no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md
|
|
38
42
|
--no-system-md Skip auto-loading the global SYSTEM.md layer
|
|
@@ -56,11 +60,12 @@ const valueFlags = new Set([
|
|
|
56
60
|
"--agents-md-file",
|
|
57
61
|
"--system-md-file",
|
|
58
62
|
"--agents-config",
|
|
63
|
+
"--extension",
|
|
59
64
|
]);
|
|
60
65
|
const boolFlags = new Set(["--discover", "--no-discovery", "--no-agents-md", "--no-system-md"]);
|
|
61
66
|
// Known-but-inert flags: parsed by earlier builds, never wired to any behavior.
|
|
62
67
|
// Rejected loudly (rather than silently ignored) until a CLI-harness plan wires them.
|
|
63
|
-
const unsupportedFlags = new Set(["--config", "--resource", "--
|
|
68
|
+
const unsupportedFlags = new Set(["--config", "--resource", "--tool"]);
|
|
64
69
|
const ALL_KINDS = ["skill", "tool", "context", "instructions"];
|
|
65
70
|
export function parseCliArgs(argv) {
|
|
66
71
|
let mode = "print";
|
|
@@ -83,6 +88,7 @@ export function parseCliArgs(argv) {
|
|
|
83
88
|
const context = [];
|
|
84
89
|
const instructions = [];
|
|
85
90
|
const injectorFiles = [];
|
|
91
|
+
const extensions = [];
|
|
86
92
|
for (let i = 0; i < argv.length; i += 1) {
|
|
87
93
|
const flag = argv[i];
|
|
88
94
|
if (flag === "-h" || flag === "--help") {
|
|
@@ -163,6 +169,9 @@ export function parseCliArgs(argv) {
|
|
|
163
169
|
case "--agents-config":
|
|
164
170
|
agentsConfig = value;
|
|
165
171
|
break;
|
|
172
|
+
case "--extension":
|
|
173
|
+
extensions.push(value);
|
|
174
|
+
break;
|
|
166
175
|
}
|
|
167
176
|
}
|
|
168
177
|
return {
|
|
@@ -180,6 +189,7 @@ export function parseCliArgs(argv) {
|
|
|
180
189
|
discoverKinds,
|
|
181
190
|
noDiscovery,
|
|
182
191
|
agentsConfig,
|
|
192
|
+
extensions,
|
|
183
193
|
discoveredSkills: [],
|
|
184
194
|
discoveredInjectors: [],
|
|
185
195
|
discoveredAgents: [],
|
|
@@ -317,6 +327,13 @@ export async function runCli(argv, runtime) {
|
|
|
317
327
|
});
|
|
318
328
|
options = { ...options, systemPromptLayers: layers };
|
|
319
329
|
}
|
|
330
|
+
// ponytail: 069 — --extension imports trusted modules only (cwd-contained relative paths,
|
|
331
|
+
// or allow-listed package/absolute specifiers), then activates their array contributions
|
|
332
|
+
// into the CLI agent. Throw policy: a broken extension is a usage error, not a silent skip.
|
|
333
|
+
if (options.extensions.length > 0) {
|
|
334
|
+
const activatedExtensions = await loadCliExtensions(options.extensions);
|
|
335
|
+
options = { ...options, activatedExtensions };
|
|
336
|
+
}
|
|
320
337
|
const session = await (runtime.createSession ?? defaultCreateSession)(options);
|
|
321
338
|
await runPromptMode(session, options, runtime.stdout, mode);
|
|
322
339
|
return 0;
|
|
@@ -386,6 +403,8 @@ function mockSession(options) {
|
|
|
386
403
|
return agentSession({ ...options, providerInstance: createMockProvider([providerTextDelta("Hello"), providerDone()]), modelConfig });
|
|
387
404
|
}
|
|
388
405
|
function agentSession(options) {
|
|
406
|
+
const activated = options.activatedExtensions;
|
|
407
|
+
const skills = activated ? [...options.discoveredSkills, ...activated.skills] : options.discoveredSkills;
|
|
389
408
|
return createAgent({
|
|
390
409
|
model: options.modelConfig,
|
|
391
410
|
provider: options.providerInstance,
|
|
@@ -393,11 +412,13 @@ function agentSession(options) {
|
|
|
393
412
|
// ponytail: Phase 31 — file layers compose with `instructions` (base) via the existing
|
|
394
413
|
// composeSystemPrompt pipeline; rank order (user<package<app<run) is enforced inside.
|
|
395
414
|
...(options.systemPromptLayers.length > 0 ? { systemPrompt: options.systemPromptLayers } : {}),
|
|
396
|
-
// ponytail: discovered skills become selectable via RunOptions.activeSkills (set by runOptions below).
|
|
397
|
-
...(
|
|
415
|
+
// ponytail: discovered/extension skills become selectable via RunOptions.activeSkills (set by runOptions below).
|
|
416
|
+
...(skills.length > 0 ? { skills: createSkillRegistry(skills) } : {}),
|
|
417
|
+
...(activated ? { tools: activated.tools, context: activated.context, middleware: activated.middleware } : {}),
|
|
398
418
|
}).createSession({ id: options.session });
|
|
399
419
|
}
|
|
400
420
|
function runOptions(options) {
|
|
421
|
+
const injectors = [...(options.activatedExtensions?.instructionInjectors ?? []), ...options.resolvedInstructionInjectors];
|
|
401
422
|
return {
|
|
402
423
|
...(options.maxToolRounds !== undefined ? { limits: { maxToolRounds: options.maxToolRounds } } : {}),
|
|
403
424
|
compaction: options.compact ? { thresholdEntries: options.compact } : undefined,
|
|
@@ -405,7 +426,7 @@ function runOptions(options) {
|
|
|
405
426
|
...(options.discover && !options.noDiscovery && options.discoveredSkills.length > 0
|
|
406
427
|
? { activeSkills: options.discoveredSkills.map((s) => s.name) }
|
|
407
428
|
: {}),
|
|
408
|
-
...(
|
|
429
|
+
...(injectors.length > 0 ? { instructionInjectors: injectors } : {}),
|
|
409
430
|
};
|
|
410
431
|
}
|
|
411
432
|
// ponytail: resolve --instruction names against discovered injectors (fail-closed) and load
|
|
@@ -432,6 +453,75 @@ function positiveInt(value, flag) {
|
|
|
432
453
|
throw new CliUsageError(`Invalid value for ${flag}: ${value}`);
|
|
433
454
|
return parsed;
|
|
434
455
|
}
|
|
456
|
+
/** Load `--extension` specifiers into one kernel and activate it. Trust model:
|
|
457
|
+
* the loaded code is trusted host code (same level as the provider factory
|
|
458
|
+
* import in `defaultCreateSession`) — the gates decide WHICH code may load,
|
|
459
|
+
* not sandbox what loaded code does.
|
|
460
|
+
* - Relative `./`/`../` paths: no allow-list needed, but must `realpath`
|
|
461
|
+
* contain inside the working directory (symlinks cannot escape).
|
|
462
|
+
* - Bare package names and absolute paths: exact match in
|
|
463
|
+
* `PRISM_EXTENSION_ALLOWLIST` (comma-separated), evaluated before `import()`.
|
|
464
|
+
* Accepted module shapes: `createExtension()` export, a default function, or
|
|
465
|
+
* a default `{ name, setup }` Extension object. */
|
|
466
|
+
async function loadCliExtensions(specifiers) {
|
|
467
|
+
const allowList = (process.env.PRISM_EXTENSION_ALLOWLIST ?? "")
|
|
468
|
+
.split(",")
|
|
469
|
+
.map((entry) => entry.trim())
|
|
470
|
+
.filter(Boolean);
|
|
471
|
+
const kernel = createExtensionKernel({ errorPolicy: "throw" });
|
|
472
|
+
const extensions = [];
|
|
473
|
+
for (const specifier of specifiers) {
|
|
474
|
+
extensions.push(await importTrustedExtension(specifier, allowList));
|
|
475
|
+
}
|
|
476
|
+
await kernel.load(extensions);
|
|
477
|
+
return activateKernel(kernel);
|
|
478
|
+
}
|
|
479
|
+
async function importTrustedExtension(specifier, allowList) {
|
|
480
|
+
if (specifier.includes("\0"))
|
|
481
|
+
throw new CliUsageError(`Invalid value for --extension: ${specifier}`);
|
|
482
|
+
const relative = specifier.startsWith("./") || specifier.startsWith("../");
|
|
483
|
+
let moduleSpecifier;
|
|
484
|
+
if (relative) {
|
|
485
|
+
const real = await realpath(resolve(specifier)).catch(() => {
|
|
486
|
+
throw new CliUsageError(`--extension "${specifier}" does not exist`);
|
|
487
|
+
});
|
|
488
|
+
const realCwd = await realpath(process.cwd());
|
|
489
|
+
if (real !== realCwd && !real.startsWith(realCwd + sep)) {
|
|
490
|
+
throw new CliUsageError(`--extension "${specifier}" escapes the working directory`);
|
|
491
|
+
}
|
|
492
|
+
moduleSpecifier = pathToFileURL(real).href;
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
if (!allowList.includes(specifier)) {
|
|
496
|
+
throw new CliUsageError(`--extension "${specifier}" is not in PRISM_EXTENSION_ALLOWLIST (package names and absolute paths require it; relative extension paths need ./ or ../)`);
|
|
497
|
+
}
|
|
498
|
+
moduleSpecifier = isAbsolute(specifier) ? pathToFileURL(specifier).href : specifier;
|
|
499
|
+
}
|
|
500
|
+
let mod;
|
|
501
|
+
try {
|
|
502
|
+
mod = await import(moduleSpecifier);
|
|
503
|
+
}
|
|
504
|
+
catch (error) {
|
|
505
|
+
throw new CliUsageError(`--extension "${specifier}" failed to load: ${error instanceof Error ? error.message : String(error)}`);
|
|
506
|
+
}
|
|
507
|
+
return extensionFromModule(mod, specifier);
|
|
508
|
+
}
|
|
509
|
+
function extensionFromModule(mod, specifier) {
|
|
510
|
+
const record = mod;
|
|
511
|
+
if (typeof record.createExtension === "function")
|
|
512
|
+
return record.createExtension();
|
|
513
|
+
if (typeof record.default === "function")
|
|
514
|
+
return record.default();
|
|
515
|
+
if (isExtensionObject(record.default))
|
|
516
|
+
return record.default;
|
|
517
|
+
throw new CliUsageError(`--extension "${specifier}" must export createExtension(), a default function, or a default { name, setup } extension`);
|
|
518
|
+
}
|
|
519
|
+
function isExtensionObject(value) {
|
|
520
|
+
return (typeof value === "object" &&
|
|
521
|
+
value !== null &&
|
|
522
|
+
typeof value.name === "string" &&
|
|
523
|
+
typeof value.setup === "function");
|
|
524
|
+
}
|
|
435
525
|
function write(stream, text) {
|
|
436
526
|
stream.write(text);
|
|
437
527
|
}
|
package/dist/extensions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Extension, ExtensionEvent, ExtensionLifecycleEventName } from "./contracts.js";
|
|
1
|
+
import type { CommandDefinition, ContextProvider, Extension, ExtensionEvent, ExtensionLifecycleEventName, InstructionInjector, Skill, ToolDefinition } from "./contracts.js";
|
|
2
2
|
import { type ContributionRegistries } from "./contributions.js";
|
|
3
3
|
import { type MiddlewareRegistry } from "./middleware.js";
|
|
4
4
|
import { type PermissionPolicy } from "./security.js";
|
|
@@ -40,3 +40,20 @@ export interface ExtensionKernel {
|
|
|
40
40
|
}
|
|
41
41
|
export declare function createExtensionEventBus(options?: Pick<ExtensionKernelOptions, "errorPolicy" | "secrets">): ExtensionEventBus;
|
|
42
42
|
export declare function createExtensionKernel(options?: ExtensionKernelOptions): ExtensionKernel;
|
|
43
|
+
/** Host-owned activation: copy contributed entries into the `createAgent()`
|
|
44
|
+
* fields that accept plain arrays. Contributions stay inert until the host
|
|
45
|
+
* passes the returned fields into runtime config. Array slots only —
|
|
46
|
+
* single-slot builders (`inputBuilder`/`promptBuilder`), `compaction`,
|
|
47
|
+
* `retry`, provider/model selection, and skill activation remain host-owned
|
|
48
|
+
* decisions; `commands` are for host RPC surfaces, not an `AgentConfig` field. */
|
|
49
|
+
export interface ActivatedKernelConfig {
|
|
50
|
+
readonly tools: readonly ToolDefinition[];
|
|
51
|
+
readonly skills: readonly Skill[];
|
|
52
|
+
readonly instructionInjectors: readonly InstructionInjector[];
|
|
53
|
+
readonly context: readonly ContextProvider[];
|
|
54
|
+
/** For host command surfaces (CLI/RPC/UI); not part of `AgentConfig`. */
|
|
55
|
+
readonly commands: readonly CommandDefinition[];
|
|
56
|
+
/** The kernel middleware registry itself; runs only when passed to runtime config. */
|
|
57
|
+
readonly middleware: MiddlewareRegistry;
|
|
58
|
+
}
|
|
59
|
+
export declare function activateKernel(kernel: ExtensionKernel): ActivatedKernelConfig;
|
package/dist/extensions.js
CHANGED
|
@@ -190,6 +190,16 @@ export function createExtensionKernel(options = {}) {
|
|
|
190
190
|
},
|
|
191
191
|
};
|
|
192
192
|
}
|
|
193
|
+
export function activateKernel(kernel) {
|
|
194
|
+
return {
|
|
195
|
+
tools: kernel.registries.tools.list(),
|
|
196
|
+
skills: kernel.registries.skills.list(),
|
|
197
|
+
instructionInjectors: kernel.registries.instructionInjectors.list(),
|
|
198
|
+
context: kernel.registries.contextProviders.list(),
|
|
199
|
+
commands: kernel.registries.commands.list(),
|
|
200
|
+
middleware: kernel.middleware,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
193
203
|
async function assertExtensionLoadPolicy(policy, extension) {
|
|
194
204
|
if (!policy)
|
|
195
205
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -43,8 +43,8 @@ export type { EventMultiplexer, EventMultiplexerOptions, EventOverflowInfo, Even
|
|
|
43
43
|
export { createEventMultiplexer, EVENT_MULTIPLEXER_SINGLE_CONSUMER_CODE, EventMultiplexerError } from "./event-multiplexer.js";
|
|
44
44
|
export type { ExecutionAction, ExecutionDecision, ExecutionPolicy, ExecutionRisk } from "./execution-policy.js";
|
|
45
45
|
export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
|
|
46
|
-
export type { ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions, ExtensionLoadPolicy, LoadedExtension, } from "./extensions.js";
|
|
47
|
-
export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
46
|
+
export type { ActivatedKernelConfig, ExtensionErrorPolicy, ExtensionEventBus, ExtensionEventHandler, ExtensionKernel, ExtensionKernelOptions, ExtensionLoadPolicy, LoadedExtension, } from "./extensions.js";
|
|
47
|
+
export { activateKernel, createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
48
48
|
export type { MemoryRunFeedbackStoreOptions, PrepareRunFeedbackOptions, RunFeedbackLimits, RunFeedbackRun, RunFeedbackRunResolver, } from "./feedback.js";
|
|
49
49
|
export { createMemoryRunFeedbackStore, prepareRunFeedback, RunFeedbackError, requireRunFeedbackOwnership, runFeedbackPageLimit, } from "./feedback.js";
|
|
50
50
|
export type { ApplyFieldPolicyOptions, AuditFieldRedaction, AuditFieldRedactorLike, AuditFieldRedactorOptions, FieldPolicy, FieldPolicyAction, FieldPolicyDecision, FieldPolicyInput, ProtectedFieldPolicyOptions, } from "./field-policy.js";
|
|
@@ -119,5 +119,5 @@ export { trimTrailingSlashes } from "./trim-trailing-slashes.js";
|
|
|
119
119
|
export type { ResolvedUseCaseModel, ResolveUseCaseModelInput, UseCaseModelBinding, } from "./use-case-model.js";
|
|
120
120
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
121
121
|
export declare const name = "prism";
|
|
122
|
-
export declare const version = "0.5.
|
|
122
|
+
export declare const version = "0.5.6";
|
|
123
123
|
export declare const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ export { createDelegatedAgentStep, DelegatedAgentStepError, MAX_DELEGATED_AGENT_
|
|
|
22
22
|
export { acceptDeviceChunk, assertDeviceAdmit, DEFAULT_DEVICE_MAX_CHUNK_BYTES, DEFAULT_DEVICE_MAX_CONCURRENT_SESSIONS, DevicePolicyError, HARD_DEVICE_MAX_CHUNK_BYTES, HARD_DEVICE_MAX_CONCURRENT_SESSIONS, redactDeviceTelemetry, resolveDevicePolicy, runDevicePolicyConformance, } from "./devices.js";
|
|
23
23
|
export { createEventMultiplexer, EVENT_MULTIPLEXER_SINGLE_CONSUMER_CODE, EventMultiplexerError } from "./event-multiplexer.js";
|
|
24
24
|
export { applyExecutionDecision, assertExecutionAllowed, checkExecution, ExecutionDeniedError } from "./execution-policy.js";
|
|
25
|
-
export { createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
25
|
+
export { activateKernel, createExtensionEventBus, createExtensionKernel } from "./extensions.js";
|
|
26
26
|
export { createMemoryRunFeedbackStore, prepareRunFeedback, RunFeedbackError, requireRunFeedbackOwnership, runFeedbackPageLimit, } from "./feedback.js";
|
|
27
27
|
export { ALLOW_FIELD_POLICY, applyFieldPolicy, createAuditFieldRedactor, createProtectedFieldPolicy, FIELD_POLICY_LIMITS, FieldPolicyError, } from "./field-policy.js";
|
|
28
28
|
export { assertGuardrailsAllowed, GuardrailError, MAX_GUARDRAIL_CONCURRENCY, runGuardrails } from "./guardrails.js";
|
|
@@ -66,6 +66,6 @@ export { createToolParameterValidator, createToolRegistry, dispatchToolCall, fil
|
|
|
66
66
|
export { trimTrailingSlashes } from "./trim-trailing-slashes.js";
|
|
67
67
|
export { resolveUseCaseModel, resolveUseCaseModelBinding, useCaseCredentialProviderId, } from "./use-case-model.js";
|
|
68
68
|
export const name = "prism";
|
|
69
|
-
export const version = "0.5.
|
|
69
|
+
export const version = "0.5.6";
|
|
70
70
|
export const description = "Agent harness for AI providers, agents, sessions, and tools.";
|
|
71
71
|
//# sourceMappingURL=index.js.map
|
package/dist/run-limits.js
CHANGED
|
@@ -209,8 +209,13 @@ export class RunLimitTracker {
|
|
|
209
209
|
this.exceed(limit, Number.MAX_SAFE_INTEGER + 1);
|
|
210
210
|
this.counters[counter] = observed;
|
|
211
211
|
const cap = this.limits[limit];
|
|
212
|
-
if (cap
|
|
213
|
-
|
|
212
|
+
if (cap === null)
|
|
213
|
+
return;
|
|
214
|
+
// Byte caps are per-frame (HARD exists so one giant provider frame cannot OOM the host),
|
|
215
|
+
// not run-lifetime sums; every other axis stays cumulative.
|
|
216
|
+
const against = limit === "maxRequestBytes" || limit === "maxResponseBytes" ? delta : observed;
|
|
217
|
+
if (against > cap)
|
|
218
|
+
this.exceed(limit, against);
|
|
214
219
|
}
|
|
215
220
|
recordUsage(usage) {
|
|
216
221
|
if (!usage) {
|
package/docs/cli-rpc.md
CHANGED
|
@@ -10,7 +10,7 @@ The `prism` bin is a thin adapter over `AgentSession` plus a tiny project scaffo
|
|
|
10
10
|
- `prism init <dir>`: create a minimal TypeScript project with one selected provider, `.env.example`, and one offline mock test.
|
|
11
11
|
- `prism dev`: boot the loopback dev inspector over the scaffolded agent (delegates into `@arnilo/prism-coding-tools/dev` when resolvable; plan 040 Task 4).
|
|
12
12
|
|
|
13
|
-
It does not add a TUI, app tools, provider globals,
|
|
13
|
+
It does not add a TUI, app tools, provider globals, resource discovery, or credential storage, and it never auto-discovers extension packages — `--extension` loads only explicitly named modules (relative paths inside the working directory, or allow-listed package/absolute specifiers). `init` uses Node standard-library filesystem APIs and checked-in templates only — no interactive prompts or template-engine dependency.
|
|
14
14
|
|
|
15
15
|
## Live CLI journey (plans/064 Task 5)
|
|
16
16
|
|
|
@@ -115,6 +115,7 @@ Manifests of the negotiation are simple: the agent is loaded from the scaffold c
|
|
|
115
115
|
| `--discover` | Opt-in workspace contribution discovery (`SKILL.md`/`manifest.json`). Never auto-activates or imports. |
|
|
116
116
|
| `--discover-kinds <csv>` | Kinds to scan; defaults to `skill`. Accepts `skill,tool,context,instructions`. |
|
|
117
117
|
| `--no-discovery` | Hard-disable discovery even if `--discover` is set. |
|
|
118
|
+
| `--extension <specifier>` | Load a trusted extension module (repeatable). Relative `./`/`../` paths must realpath-contain inside the working directory; package names and absolute paths must exactly match an entry in `PRISM_EXTENSION_ALLOWLIST` (comma-separated), checked before `import()`. See the extension loading section below. |
|
|
118
119
|
| `--agents-config <path>` | App config root holding `agents/<name>/AGENT.md` bundles (opt-in). Envelopes only; the host resolves them via `resolveAgentBundle`. The CLI never defaults to the user's home directory. |
|
|
119
120
|
| `--instruction <name>` | Select a registered/discovered instruction injector (repeatable). `--instruction false` disables injectors for the run. Names resolve fail-closed. |
|
|
120
121
|
| `--injector-file <path>` | Load a markdown file as a static `every_turn` injector (repeatable). |
|
|
@@ -124,6 +125,19 @@ Manifests of the negotiation are simple: the agent is loaded from the scaffold c
|
|
|
124
125
|
| `--system-md-file <path>` | Read SYSTEM.md from `<path>` instead — user-owned, `source: "user"` (Phase 31). |
|
|
125
126
|
| `--help` | Print usage. |
|
|
126
127
|
|
|
128
|
+
### Extension loading (`--extension`)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# cwd-relative trusted module (no allow-list needed)
|
|
132
|
+
prism --provider mock --extension ./my-ext.js -p "hello"
|
|
133
|
+
|
|
134
|
+
# package name or absolute path requires an exact allow-list entry
|
|
135
|
+
PRISM_EXTENSION_ALLOWLIST=@acme/prism-foo \
|
|
136
|
+
prism --provider mock --extension @acme/prism-foo -p "hello"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Each module must export `createExtension()`, a default function, or a default `{ name, setup }` extension. Loaded modules are trusted host code (same trust level as the provider factory import) — the gates decide which code may load, not what loaded code may do: relative paths fail closed on realpath escape (symlinks cannot leave the working directory), package/absolute specifiers fail closed without an allow-list entry, and a broken or wrong-shaped module is a usage error, not a silent skip. Registered contributions activate through `activateKernel` into the run's agent (tools, skills, injectors, context, middleware); extension skills follow the normal fail-closed skill activation (`RunOptions.activeSkills`). There is no npm marketplace, no `plugin.json`, and no MCP auto-start.
|
|
140
|
+
|
|
127
141
|
RPC request envelope:
|
|
128
142
|
|
|
129
143
|
```ts
|
|
@@ -269,5 +269,5 @@ MCP credentials remain host inputs: resolve them before constructing client `req
|
|
|
269
269
|
- [Azure OpenAI / Foundry](providers/azure.md) / [Amazon Bedrock](providers/bedrock.md) / [Google Vertex AI](providers/vertex.md): enterprise workload-identity credential callbacks
|
|
270
270
|
- [Web search, fetch, and extraction](web-tools.md): late-bound Brave/Exa/Firecrawl credentials
|
|
271
271
|
- [Security/auth/trust](settings-auth-trust-security.md): host-owned settings/credentials boundaries
|
|
272
|
-
- [Persistence, credentials, and multimodality primitives](persistence-credentials-multimodality-primitives.md): Plan 056 threat model and conformance matrix rows 7–10
|
|
272
|
+
- [Persistence, credentials, and multimodality primitives](history/persistence-credentials-multimodality-primitives.md): Plan 056 threat model and conformance matrix rows 7–10
|
|
273
273
|
- `@arnilo/prism`: `CredentialResolver`, `OAuthCredentialStore`, `createMemoryCredentialStore()`
|
|
@@ -96,12 +96,11 @@ The `contributed` set is what the extension registered. The `active` set is what
|
|
|
96
96
|
|
|
97
97
|
```ts
|
|
98
98
|
import {
|
|
99
|
+
activateKernel,
|
|
99
100
|
createAgent,
|
|
100
101
|
createExtensionKernel,
|
|
101
102
|
createMockProvider,
|
|
102
103
|
createContributionRegistries,
|
|
103
|
-
createSkillRegistry,
|
|
104
|
-
createToolRegistry,
|
|
105
104
|
providerDone,
|
|
106
105
|
type Extension,
|
|
107
106
|
} from "@arnilo/prism";
|
|
@@ -139,19 +138,18 @@ const registries = createContributionRegistries({ duplicate: "error" });
|
|
|
139
138
|
const kernel = createExtensionKernel({ registries, errorPolicy: "throw" });
|
|
140
139
|
await kernel.load([extension]);
|
|
141
140
|
|
|
142
|
-
// Host activation:
|
|
143
|
-
const
|
|
144
|
-
const skill = kernel.registries.skills.resolve("acme.brief");
|
|
141
|
+
// Host activation: copy the array slots; filter/narrow before createAgent() as needed.
|
|
142
|
+
const activated = activateKernel(kernel);
|
|
145
143
|
const provider = createMockProvider([providerDone()]);
|
|
146
144
|
|
|
147
145
|
const agent = createAgent({
|
|
148
146
|
model: { provider: "mock", model: "demo" },
|
|
149
147
|
provider,
|
|
150
|
-
tools:
|
|
151
|
-
skills:
|
|
152
|
-
context:
|
|
148
|
+
tools: activated.tools,
|
|
149
|
+
skills: activated.skills,
|
|
150
|
+
context: activated.context,
|
|
153
151
|
promptBuilder: kernel.registries.promptBuilders.resolve("acme.prompt"),
|
|
154
|
-
middleware:
|
|
152
|
+
middleware: activated.middleware,
|
|
155
153
|
});
|
|
156
154
|
|
|
157
155
|
await agent.createSession().run("Use the Acme extension.", { activeSkills: ["acme.brief"] });
|
|
@@ -166,6 +164,7 @@ await agent.createSession().run("Use the Acme extension.", { activeSkills: ["acm
|
|
|
166
164
|
- `registerSkill()` contributes instructions only. Referenced `toolNames` are checked against host-active tools when the skill is activated.
|
|
167
165
|
- `registerAuthMethod()` and `registerCredentialResolver()` must not contain resolved credential values. Use descriptors/resolvers; the host resolves secrets at the provider/request edge.
|
|
168
166
|
- Middleware from `api.use()` runs only when the host passes `kernel.middleware` into runtime configuration.
|
|
167
|
+
- `activateKernel(kernel)` copies the array slots (`tools`, `skills`, `instructionInjectors`, `context`, `commands`) plus `kernel.middleware` in one call; filter the returned arrays before `createAgent()` when the host wants narrower selection.
|
|
169
168
|
- 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
169
|
|
|
171
170
|
### Host driver hooks (opt-in)
|
package/docs/extensions.md
CHANGED
|
@@ -42,6 +42,7 @@ createExtensionEventBus(options?: { errorPolicy?: "event" | "throw"; secrets?: r
|
|
|
42
42
|
- `kernel.events.on(type, handler)` registers ordered event handlers and returns an unsubscribe function.
|
|
43
43
|
- `kernel.events.emit(event)` calls matching handlers in registration order.
|
|
44
44
|
- `kernel.middleware.run(hook, value)` runs matching middleware in registration order.
|
|
45
|
+
- `activateKernel(kernel)` copies the `createAgent()` array slots into one config: `{ tools, skills, instructionInjectors, context, commands, middleware }`. Contributions stay inert until the host passes them into runtime config; single-slot builders, `compaction`/`retry`, provider/model selection, and skill activation remain host-owned decisions.
|
|
45
46
|
- With default `errorPolicy: "event"`, setup/listener/middleware errors become `extension_error` events with redacted `ErrorInfo`.
|
|
46
47
|
- With `errorPolicy: "throw"`, setup/listener/middleware errors reject/throw.
|
|
47
48
|
|
|
@@ -57,7 +58,7 @@ createExtensionEventBus(options?: { errorPolicy?: "event" | "throw"; secrets?: r
|
|
|
57
58
|
## Implementation example
|
|
58
59
|
|
|
59
60
|
```ts
|
|
60
|
-
import { createAgent, createExtensionKernel, type Extension } from "@arnilo/prism";
|
|
61
|
+
import { activateKernel, createAgent, createExtensionKernel, type Extension } from "@arnilo/prism";
|
|
61
62
|
|
|
62
63
|
const extension: Extension = {
|
|
63
64
|
name: "demo-extension",
|
|
@@ -96,6 +97,17 @@ console.log(kernel.registries.skills.resolve("brief").name); // contributed only
|
|
|
96
97
|
console.log(kernel.registries.agents.resolve("demo").name); // contributed only; host must create/select before runtime use
|
|
97
98
|
console.log(kernel.registries.systemPromptContributions.resolve("demo-prompt").text); // contributed only; host must select before prompt use
|
|
98
99
|
await kernel.middleware.run("provider_request", { metadata: {} });
|
|
100
|
+
|
|
101
|
+
// Host activation: copy the array slots into createAgent() fields.
|
|
102
|
+
const activated = activateKernel(kernel);
|
|
103
|
+
const agent = createAgent({
|
|
104
|
+
model: { provider: "mock", model: "demo" },
|
|
105
|
+
tools: activated.tools,
|
|
106
|
+
skills: activated.skills,
|
|
107
|
+
instructionInjectors: activated.instructionInjectors,
|
|
108
|
+
context: activated.context,
|
|
109
|
+
middleware: activated.middleware,
|
|
110
|
+
});
|
|
99
111
|
```
|
|
100
112
|
|
|
101
113
|
## Extension and configuration notes
|
package/docs/graft.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`@arnilo/prism-memory/graft` is an optional subpath that wires [nanonets/graft](https://github.com/nanonets/graft) — a repository context-graph CLI (`graft/` directory, INDEX.md orientation, symbol-level wiring graph) — into Prism contribution contracts.
|
|
6
6
|
|
|
7
|
-
It registers six pull tools backed by the graft CLI (`--json`, argv-safe), a push-mode retrieval-pack context provider plus first-turn orientation injector carried on the `graft` skill, commands (`graft`, `graft-build`, `graft-check`, `graft-viz`), and an edit-watch middleware that computes blast radius after mutating tool calls. Import is inert; a missing graft CLI fails closed at `setup` with a bounded redacted error.
|
|
7
|
+
It registers six pull tools backed by the graft CLI (`--json`, argv-safe), a push-mode retrieval-pack context provider plus first-turn orientation injector carried on the `graft` skill, commands (`graft`, `graft-build`, `graft-build-deep`, `graft-check`, `graft-viz`, `graft-init`), and an edit-watch middleware that computes blast radius after mutating tool calls. Import is inert; a missing graft CLI fails closed at `setup` with a bounded redacted error.
|
|
8
8
|
|
|
9
9
|
## When to use it
|
|
10
10
|
|
|
@@ -32,6 +32,9 @@ Zero-code alternative (L0): hosts can skip this package entirely and let agents
|
|
|
32
32
|
| `maxPromptChars` | `number` | no | Prompts longer than this never become ask argv (default 4096). |
|
|
33
33
|
| `allowUpstreamTelemetry` | `boolean` | no | Default false → children run with `DO_NOT_TRACK=1`. |
|
|
34
34
|
| `providerEnv` | `Record<string, string>` | no | Explicit graft provider settings (`GRAFT_API_KEY`, …). Never inherited from host env; only `GRAFT_*` keys reach the child. |
|
|
35
|
+
| `deepModel` | `{ provider: "openai" \| "anthropic" \| "litellm" \| "orcarouter", model: string, apiKey: string, baseUrl?: string }` | no | Model for `graft build --deep` (Graft's own LLM client — **Prism's `Provider` is not Graft's LLM**; they have different protocols). Merged over `providerEnv` as `GRAFT_PROVIDER`/`GRAFT_MODEL`/`GRAFT_API_KEY`/`GRAFT_BASE_URL`; wins on conflict. |
|
|
36
|
+
| `initAgents` / `initYes` / `initWireMcp` | `readonly string[]` / `boolean` / `boolean` | no | `graft init` configuration: agent ids for `--agents`, `--yes`, and whether to wire graft MCP servers (default off — Prism provides its own graft surfaces). `graft-init` refuses to spawn without `initAgents` or `initYes` (the child has no TTY). |
|
|
37
|
+
| `buildBudgetMs` / `deepBuildBudgetMs` / `buildMaxResultBytes` | `number` | no | Budgets for graph builds: structural `build`/`init` default 120000 ms, `--deep` default 600000 ms (the LLM pass over the graph), stdout cap 2 MiB. Ask/grep stay on `retrievalBudgetMs`. |
|
|
35
38
|
| `editToolNames` | `readonly string[]` | no | Tools triggering blast-radius lookup. Default `write`, `edit`, `move`. |
|
|
36
39
|
| `quietStartup`, `hideStatus` | `boolean` | no | Suppress startup status events / status reporting. |
|
|
37
40
|
| `appendEntry` | `(entry, opts?) => Promise<void>` | yes | Host session append (OM attach pattern). |
|
|
@@ -41,7 +44,20 @@ Pull tools (mode includes `pull`): `graft_ask`, `graft_grep`, `graft_callers`, `
|
|
|
41
44
|
|
|
42
45
|
Push surfaces (mode includes `push`): skill `graft` carrying context provider `graft-context` (per-turn pointers-only pack, gated: ≥12-char prompt, dedup by seen node ids, 32 KiB block ceiling) and instruction injector `graft-orient` (`first_turn`, byte-capped INDEX.md cut + staleness banner).
|
|
43
46
|
|
|
44
|
-
Registered commands: `graft` (`status` \| `build` \| `check` \| `viz` dispatch), plus `graft-build`, `graft-check`, `graft-viz` aliases.
|
|
47
|
+
Registered commands: `graft` (`status` \| `build` [deep:true] \| `check` \| `viz` \| `init` dispatch), plus `graft-build`, `graft-build-deep`, `graft-check`, `graft-viz`, `graft-init` aliases.
|
|
48
|
+
|
|
49
|
+
### Graph builds and init
|
|
50
|
+
|
|
51
|
+
- `/graft-build` — structural rebuild via `graft build` (tree-sitter pass, no API key, plain-text progress — no `JSON.parse` on this surface).
|
|
52
|
+
- `/graft-build-deep` — `graft build --deep --provider <> --model <> [--base-url <>]` using the host's `deepModel`. Without a configured model it errors before spawning. `GRAFT_API_KEY` rides in the child env, never on argv.
|
|
53
|
+
- `/graft-init` — `graft init --no-global` (never writes user-level state), default `--no-mcp --no-hooks --no-statusline` (opt in via `initWireMcp`), plus `--agents <id>` per `initAgents` and `--yes` when `initYes`. Requires one of the two; non-interactive by design.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# structural, no key
|
|
57
|
+
/graft-build
|
|
58
|
+
# deep — host-configured model
|
|
59
|
+
e.g. /graft-build-deep
|
|
60
|
+
```
|
|
45
61
|
|
|
46
62
|
## Outputs / response / events
|
|
47
63
|
|
|
@@ -49,7 +65,8 @@ Registered commands: `graft` (`status` \| `build` \| `check` \| `viz` dispatch),
|
|
|
49
65
|
| --- | --- |
|
|
50
66
|
| `createGraftExtension(options)` | Returns an inert `Extension` until `kernel.load([...])`; emits `graft:loaded` on setup. |
|
|
51
67
|
| `resolveGraftCli(options)` | Fail-closed CLI resolution (`explicit` → command+argv, `peer-bin` → node + manifest bin). |
|
|
52
|
-
| `runGraftJson(cli, argv, options)` / `childEnv(options)` / `childTimeoutMs` / `DEFAULT_MAX_RESULT_BYTES` |
|
|
68
|
+
| `runGraftJson(cli, argv, options)` / `runGraftExit(cli, argv, options)` / `childEnv(options)` / `childTimeoutMs` / `DEFAULT_MAX_RESULT_BYTES` | Budgeted runners for hosts building custom surfaces — JSON surfaces (`check`/`ask`) vs exit-code surfaces (`build`/`init`). |
|
|
69
|
+
| `deepProviderEnv(deepModel, providerEnv)` | `deepModel` merged over `providerEnv`, filtered to `GRAFT_*`. |
|
|
53
70
|
| `readBoundedFile` / `redactPaths` / `GraftResolveError` | Bounded-read and redaction helpers. |
|
|
54
71
|
|
|
55
72
|
Events: `graft:status` (check/build outcomes), `graft:dirty` (post-edit, repo-relative path + optional `staleCountEstimate`), `graft:loaded` (mode + cliKind metadata).
|
|
@@ -92,12 +109,15 @@ await kernel.load([
|
|
|
92
109
|
packageRoot: "./vendor/graft-checkout",
|
|
93
110
|
mode: "both",
|
|
94
111
|
quietStartup: true,
|
|
112
|
+
deepModel: { provider: "anthropic", model: "claude-sonnet-4-5", apiKey: process.env.ANTHROPIC_API_KEY! },
|
|
113
|
+
initAgents: ["codex"],
|
|
95
114
|
appendEntry: async (entry, options) => store.append(entry, options),
|
|
96
115
|
getEntries: async () => store.list("s1"),
|
|
97
116
|
}),
|
|
98
117
|
]);
|
|
99
118
|
// Pull: dispatch graft_ask/… tools. Push: runs assemble the skill-carried
|
|
100
119
|
// provider + graft-orient injector. Edits: middleware emits graft:dirty.
|
|
120
|
+
// /graft-build-deep runs graft's own LLM pass; /graft-init wires codex, --no-global.
|
|
101
121
|
```
|
|
102
122
|
|
|
103
123
|
## Extension and configuration notes
|
|
@@ -105,12 +125,14 @@ await kernel.load([
|
|
|
105
125
|
- Import alone registers nothing (`sideEffects: false`); no timers, watchers, or network. The only child processes are budgeted graft CLI calls.
|
|
106
126
|
- Retrieval happens in-process via Prism primitives (context provider, injector, tool_result middleware) — no external hook shims.
|
|
107
127
|
- Ask result shape is parsed tolerantly (`nodes|results|matches|hits`) because graft is pre-1.0; formatters emit pointers (`title` + `file:line` + `[[wikilink]]`), never source bodies.
|
|
108
|
-
-
|
|
128
|
+
- Deliberate opt-out from family/umbrella packaging (like Caveman/Ponytail) — opt-in install only.
|
|
109
129
|
- Multi-repo layouts work as upstream graft defines them (workspaces, submodules with `--follow-submodules`, sibling repos); point `projectDir` at the graft root that owns the target repo.
|
|
110
130
|
|
|
111
131
|
## Security and performance notes
|
|
112
132
|
|
|
113
|
-
- Telemetry default-off: children always get `DO_NOT_TRACK=1` unless `allowUpstreamTelemetry` is true; child env is fixed-base — host env vars are never inherited, and only explicit `GRAFT_*` keys from `providerEnv` pass through. Route secrets like `GRAFT_API_KEY` through the host's credential resolution when populating `providerEnv`.
|
|
133
|
+
- Telemetry default-off: children always get `DO_NOT_TRACK=1` unless `allowUpstreamTelemetry` is true; child env is fixed-base — host env vars are never inherited, and only explicit `GRAFT_*` keys from `providerEnv`/`deepModel` pass through. Route secrets like `GRAFT_API_KEY` through the host's credential resolution when populating `deepModel`/`providerEnv`. `deepModel`'s API key reaches the child via env only — never on argv (no `--api-key` flag exists in the surface), so it cannot leak through `ps` or logs.
|
|
134
|
+
- Build/init commands are budgeted separately from retrieval (`buildBudgetMs`, `deepBuildBudgetMs`, `buildMaxResultBytes`); deep builds fail closed without a configured model instead of spawning unconfigured.
|
|
135
|
+
- `graft-init` always passes `--no-global` — it never writes user-level agent state; MCP/hook/statusline wiring stays off unless the host opts in via `initWireMcp`.
|
|
114
136
|
- Upstream output is untrusted: stdout capped (`maxResultBytes`), prompts capped (`maxPromptChars`), injected packs bounded (32 KiB), orientation cut byte-capped (8 KiB); error paths are logged redacted (absolute paths/home dirs).
|
|
115
137
|
- Every CLI call is wall-clock-budgeted (`retrievalBudgetMs`, minus fixed overhead for the timeout math) and every failure degrades silently: pull tools return structured errors, the push pack contributes nothing, edit-watch passes the tool result through untouched.
|
|
116
138
|
- No background workers; state persists through two CAS appends per turn at most (freshness patch, seen-set/saved-tokens update).
|
|
@@ -10,11 +10,11 @@ the release tree before cutting 1.0. The decision to cut 1.0 stays with the
|
|
|
10
10
|
operator after operator-gated legs run in a protected environment and Phase
|
|
11
11
|
12 demand evidence exists.
|
|
12
12
|
|
|
13
|
-
Evidence trail: [`docs/_evidence/review-coverage-2026-07-26-phase-11.md`](
|
|
14
|
-
(addenda 0–9), [`docs/release-and-install.md`](
|
|
15
|
-
[`docs/migration.md`](
|
|
16
|
-
[`docs/public-contracts.md`](
|
|
17
|
-
The per-phase review-coverage evidence archive lives in [`docs/_evidence/`](
|
|
13
|
+
Evidence trail: [`docs/_evidence/review-coverage-2026-07-26-phase-11.md`](.././_evidence/review-coverage-2026-07-26-phase-11.md)
|
|
14
|
+
(addenda 0–9), [`docs/release-and-install.md`](.././release-and-install.md),
|
|
15
|
+
[`docs/migration.md`](.././migration.md), [`docs/performance.md`](.././performance.md),
|
|
16
|
+
[`docs/public-contracts.md`](.././public-contracts.md) (frozen 0.1.x contract).
|
|
17
|
+
The per-phase review-coverage evidence archive lives in [`docs/_evidence/`](.././_evidence/)
|
|
18
18
|
(plans 067–079, releases 0.0.4–0.0.16; tarball-excluded, kept in-repo for audit).
|
|
19
19
|
Historical release lines (0.0.16 floor → 0.0.27 Phase 10 ACP interop → 0.1.0)
|
|
20
20
|
keep their per-phase evidence in the pages above; this page records the 0.2.6
|
|
@@ -82,7 +82,7 @@ material in published artifacts) and exact version-range drift. A genuine break
|
|
|
82
82
|
requires `--allow-break` **and** a `docs/migration.md` entry mentioning the
|
|
83
83
|
version. The frozen 0.1.x contract (declaration/exports, events, protocol
|
|
84
84
|
payloads, migration checksums, patch-release compatibility promise) is
|
|
85
|
-
published in [docs/public-contracts.md](public-contracts.md).
|
|
85
|
+
published in [docs/public-contracts.md](../public-contracts.md).
|
|
86
86
|
|
|
87
87
|
**Baseline maintenance:** `scripts/compat-baseline/` must stay committed.
|
|
88
88
|
Regenerate only after review with `node scripts/release.mjs gate --update-baseline`,
|