@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.
Files changed (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +10 -10
  3. package/dist/cli-runner.d.ts +8 -1
  4. package/dist/cli-runner.js +97 -7
  5. package/dist/extensions.d.ts +18 -1
  6. package/dist/extensions.js +10 -0
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.js +2 -2
  9. package/dist/run-limits.js +7 -2
  10. package/docs/cli-rpc.md +15 -1
  11. package/docs/credential-storage.md +1 -1
  12. package/docs/extension-authoring.md +8 -9
  13. package/docs/extensions.md +13 -1
  14. package/docs/graft.md +27 -5
  15. package/docs/{0.1.0-readiness.md → history/0.1.0-readiness.md} +6 -6
  16. package/docs/history/README.md +11 -0
  17. package/docs/{migrate-to-0.4.md → history/migrate-to-0.4.md} +4 -4
  18. package/docs/history/migration-0.0.md +738 -0
  19. package/docs/history/migration-0.1.md +151 -0
  20. package/docs/history/migration-0.2.md +173 -0
  21. package/docs/history/migration-0.3.md +39 -0
  22. package/docs/history/migration-0.4.md +6 -0
  23. package/docs/{persistence-credentials-multimodality-primitives.md → history/persistence-credentials-multimodality-primitives.md} +13 -13
  24. package/docs/history/release-handoffs.md +848 -0
  25. package/docs/{workflow-orchestration-primitives.md → history/workflow-orchestration-primitives.md} +11 -11
  26. package/docs/host-security.md +1 -1
  27. package/docs/index.md +160 -143
  28. package/docs/migrate-to-0.5.md +1 -1
  29. package/docs/migration.md +15 -1042
  30. package/docs/multimodal-content.md +1 -1
  31. package/docs/performance.md +1 -1
  32. package/docs/postgres-persistence.md +1 -1
  33. package/docs/provider-packages.md +20 -20
  34. package/docs/release-and-install.md +57 -842
  35. package/docs/runs-and-usage.md +2 -2
  36. package/docs/session-stores-and-branching.md +1 -122
  37. package/docs/sqlite-persistence.md +1 -1
  38. package/docs/wiki.md +46 -2
  39. package/docs/workflows.md +1 -1
  40. package/package.json +1 -1
  41. /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.4 | core — runtime, CLI/RPC, templates, docs |
166
- | `@arnilo/prism-coding-tools` | 0.5.4 | family — /agent, /security, /document-reader, /openapi, /computer-use-linux, /dev, /caveman, /ponytail, /impeccable subpaths |
167
- | `@arnilo/prism-core` | 0.5.4 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /work, /validation subpaths |
168
- | `@arnilo/prism-providers` | 0.5.4 | family — all provider adapters as `/<adapter>` subpaths |
169
- | `@arnilo/prism-acp-agent` | 0.5.4 | capability — ACP adapter |
170
- | `@arnilo/prism-ag-ui` | 0.5.4 | capability — AG-UI/A2A/A2UI adapter |
171
- | `@arnilo/prism-mcp` | 0.5.4 | capability — MCP client/server/OAuth interop |
172
- | `@arnilo/prism-memory` | 0.5.4 | capability — memory plus /rag, /compaction/*, /graft, /wiki subpaths |
173
- | `@arnilo/prism-office` | 0.5.4 | capability — /documents, /sheets, /diagrams subpaths |
174
- | `@arnilo/prism-web-tools` | 0.5.4 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
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
@@ -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>;
@@ -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", "--extension", "--tool"]);
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
- ...(options.discoveredSkills.length > 0 ? { skills: createSkillRegistry(options.discoveredSkills) } : {}),
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
- ...(options.resolvedInstructionInjectors.length > 0 ? { instructionInjectors: options.resolvedInstructionInjectors } : {}),
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
  }
@@ -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;
@@ -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.4";
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.4";
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
@@ -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 !== null && observed > cap)
213
- this.exceed(limit, observed);
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, extension discovery, resource discovery, or credential storage. `init` uses Node standard-library filesystem APIs and checked-in templates only — no interactive prompts or template-engine dependency.
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: select contributions explicitly.
143
- const tool = kernel.registries.tools.resolve("acme.echo");
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: createToolRegistry([tool]),
151
- skills: createSkillRegistry([skill]),
152
- context: kernel.registries.contextProviders.list(),
148
+ tools: activated.tools,
149
+ skills: activated.skills,
150
+ context: activated.context,
153
151
  promptBuilder: kernel.registries.promptBuilders.resolve("acme.prompt"),
154
- middleware: kernel.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)
@@ -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` | Shared budgeted JSON runner for hosts building custom surfaces. |
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
- - Not included in `@arnilo/prism-coding-tools`, `@arnilo/prism-core`, or the `prism-all` umbrella (deliberate opt-out, like Caveman/Ponytail) — opt-in install only.
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`](./_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/)
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`,