pi-better-harness 0.37.0 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ /** Native Pi codemode, explicitly loaded because children disable extension discovery. */
2
+ import * as SDK from "@earendil-works/pi-coding-agent";
3
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
4
+
5
+ /** Older supported SDKs have no native codemode; keep their existing tool set. */
6
+ export function installChildCodemode(pi: ExtensionAPI, confined = false): boolean {
7
+ const factory = (SDK as any).createCodemodeExtension;
8
+ if (typeof factory !== "function") return false;
9
+ // Model globals make direct provider calls outside nested tool hooks.
10
+ // Confined children get only tool dispatch, which passes through the guard.
11
+ factory(confined ? { models: false } : {})(pi);
12
+ return true;
13
+ }
14
+
15
+ export default function childCodemode(pi: ExtensionAPI): void {
16
+ installChildCodemode(pi);
17
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "defaultModel": null,
3
- "defaultTools": "read, bash, edit, write, web_search, web_fetch",
3
+ "defaultTools": "read, bash, edit, write, web_search, web_fetch, codemode",
4
4
  "maxConcurrent": 6,
5
5
  "delegationMode": "adaptive",
6
6
 
@@ -85,7 +85,7 @@ export function normalizeConcurrencyCap(value: unknown): number {
85
85
  }
86
86
 
87
87
  /** Built-in default tool set when config.json sets nothing. */
88
- export const SAFE_DEFAULT_TOOLS = "read, bash, edit, write, web_search, web_fetch";
88
+ export const SAFE_DEFAULT_TOOLS = "read, bash, edit, write, web_search, web_fetch, codemode";
89
89
  /** Safe default for a hermetic (clean) child where extension tools don't exist. */
90
90
  export const SAFE_CLEAN_TOOLS = "read, bash";
91
91
 
@@ -162,8 +162,8 @@ Continuation rules:
162
162
  On Pi 0.99.1 or newer, enable native codemode alongside the existing direct
163
163
  tools with `defaultTools: ["+codemode"]` and `codemode.mode: "on"`. SDK sessions
164
164
  must also load Pi's `createCodemodeExtension()`. This support is for the
165
- foreground coordinator; it does not enable codemode in confined children or
166
- change sandbox admission.
165
+ foreground coordinator. Child codemode is enabled separately by the default
166
+ tool set and preserves sandbox tool admission.
167
167
 
168
168
  `subagent_spawn_batch` returns the same readable text to direct callers and a
169
169
  structured launch receipt to codemode scripts. No text parsing is needed:
@@ -514,10 +514,17 @@ the task boundary.
514
514
  ## Tool scoping (allowlist)
515
515
 
516
516
  Precedence, highest first: the per-call `tools` param → `config.json`
517
- `defaultTools` → a built-in default (`read, bash, edit, write, web_search,
517
+ `defaultTools` → a built-in default (`read, bash, edit, write, codemode, web_search,
518
518
  web_fetch`; just `read, bash` in a `clean` child). `exclude_tools` subtracts on
519
519
  top.
520
520
 
521
+ On Pi 0.99.1 or newer, default children explicitly load native `codemode` even
522
+ with extension discovery disabled. Custom tool lists must include `codemode`;
523
+ `exclude_tools: "codemode"` removes it. Older SDKs keep their existing tools.
524
+ In confined children, nested tool calls pass through the task guard. Direct
525
+ classifier and image-model globals are disabled there because they bypass tool
526
+ hooks. Unconfined children retain native model globals.
527
+
521
528
  User configuration lives in
522
529
  `~/.pi/agent/settings.json` under `piBetterHarness.subagents` (under
523
530
  `PI_CODING_AGENT_DIR` when set). The old user config migrates on first use.
@@ -33,7 +33,8 @@
33
33
  * pi -p --no-extensions "list your tools" -> read, bash, edit, write
34
34
  * A builtin needs no extension, so it never counts as unmapped.
35
35
  */
36
- export const BUILTIN_TOOLS = ["read", "bash", "edit", "write"];
36
+ // codemode is supplied by the harness child adapter, not a discovered package.
37
+ export const BUILTIN_TOOLS = ["read", "bash", "edit", "write", "codemode"];
37
38
 
38
39
  /** Marker spec meaning "this extension's own package" (for allow_nested). */
39
40
  export const SELF_SPEC = "self";
@@ -1868,7 +1868,7 @@ export default function (pi: ExtensionAPI) {
1868
1868
  requested: allow.split(",").filter((name) => name && !requestedExcludes.has(name)),
1869
1869
  settings: permissionPlan.tools, network: permissionPlan.permissions?.network ?? true,
1870
1870
  processAccess: permissionPlan.permissions?.processAccess ?? "off",
1871
- builtins: TASK_BUILTINS, registered: pi.getAllTools?.() ?? [],
1871
+ builtins: [...TASK_BUILTINS, "codemode"], registered: pi.getAllTools?.() ?? [],
1872
1872
  toolExtensions: cfg.toolExtensions, resolvePath: resolveExtensionPath,
1873
1873
  }) : undefined;
1874
1874
  const resolution = resolveExtensions({
@@ -1882,10 +1882,12 @@ export default function (pi: ExtensionAPI) {
1882
1882
  .filter((path) => !resolvedExtArgs.includes(path)).flatMap((path) => ["--extension", path]);
1883
1883
  // Harness child controls ride along in every mode, including clean and unsandboxed runs.
1884
1884
  // They deliver deadline steers and keep external monitoring with the coordinator.
1885
- const extArgs = [...resolvedExtArgs, ...trustedExtArgs,
1885
+ const codemodeArgs = !sandboxEnabled && allow.split(",").includes("codemode") && !requestedExcludes.has("codemode")
1886
+ ? ["--extension", join(selfDir(), "child-codemode.ts")] : [];
1887
+ const extArgs = [...resolvedExtArgs, ...trustedExtArgs, ...codemodeArgs,
1886
1888
  "--extension", childSteerExtensionPath(), "--extension", join(selfDir(), "child-ownership.ts")]
1887
1889
  .map((value, index, all) => sandboxEnabled && all[index - 1] === "--extension" ? canonicalizePath(value) : value);
1888
- const baseExtensionArgs = [...resolvedExtArgs,
1890
+ const baseExtensionArgs = [...resolvedExtArgs, ...codemodeArgs,
1889
1891
  "--extension", childSteerExtensionPath(), "--extension", join(selfDir(), "child-ownership.ts")]
1890
1892
  .map((value, index, all) => sandboxEnabled && all[index - 1] === "--extension" ? canonicalizePath(value) : value);
1891
1893
  if (resume?.recipe.extensionArgs) {
@@ -1895,7 +1897,7 @@ export default function (pi: ExtensionAPI) {
1895
1897
  tools: sandboxEnabled ? allow.split(",").filter((name) => (SDK_BUILTIN_TOOLS as readonly string[]).includes(name)).join(",") : allow,
1896
1898
  model: resume.recipe.model, clean, allowNested: sandboxEnabled ? false : allowNested, config: cfg,
1897
1899
  });
1898
- const referenceArgs = [...extensionArgs(reference, resolveExtensionPath).args,
1900
+ const referenceArgs = [...extensionArgs(reference, resolveExtensionPath).args, ...codemodeArgs,
1899
1901
  ...(!resume.recipe.baseExtensionArgs ? trustedExtArgs : []),
1900
1902
  "--extension", childSteerExtensionPath(), "--extension", join(selfDir(), "child-ownership.ts")]
1901
1903
  .map((value, index, all) => sandboxEnabled && all[index - 1] === "--extension" ? canonicalizePath(value) : value);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-subagents",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
4
4
  "description": "Pi extension for detached, sandboxed subagent runs that keep the foreground session free.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,7 @@ import { DISPOSITION_TOOL } from "./incident-model.ts";
9
9
  import { SandboxDiagnostics, diagnosticPackageVersion } from "./shared-sandbox-diagnostics.ts";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { claimTaskToolOwner } from "./task-tool-owner.ts";
12
+ import { installChildCodemode } from "./child-codemode.ts";
12
13
 
13
14
  const TRUSTED_INLINE_SOURCE = "<inline:task-sandbox>";
14
15
 
@@ -56,6 +57,7 @@ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (erro
56
57
  diagnosticsWarned = true;
57
58
  },
58
59
  });
60
+ const codemode = policy.tools.includes("codemode") && installChildCodemode(pi, true);
59
61
  const toolApi = ownedTools ? { ...pi, registerTool: claimTaskToolOwner(pi) } : pi;
60
62
  const installBoundary = () => installTaskTools(toolApi, { controller, cwd: process.cwd(), shellPath: () => shellPath,
61
63
  diagnostics,
@@ -66,7 +68,7 @@ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (erro
66
68
  processAccess: () => policy.permissions.processAccess ?? "off",
67
69
  // The disposition tool performs no file or command I/O; only this inline registration is admitted.
68
70
  // Trusted tools are admitted by name and package (ADR 0009).
69
- admitExtensionTool: (name, _input, sourcePath) => (name === DISPOSITION_TOOL && sourcePath === TRUSTED_INLINE_SOURCE) ||
71
+ admitExtensionTool: (name, _input, sourcePath) => ((name === DISPOSITION_TOOL || (name === "codemode" && codemode)) && sourcePath === TRUSTED_INLINE_SOURCE) ||
70
72
  trustedToolRefusal(policy, name, sourcePath) === undefined,
71
73
  });
72
74
  // Defer owned registrations until after SDK duplicate-name diagnostics.
@@ -107,7 +109,8 @@ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (erro
107
109
  const reason = registered ? trustedToolRefusal(policy, name, registered.sourceInfo?.path) : `${name} was not registered by its package`;
108
110
  if (reason) refused.push({ name, reason }); else trusted.push(name);
109
111
  }
110
- const activated = selected.length || trusted.length ? [...selected, ...trusted, DISPOSITION_TOOL] : selected;
112
+ const activated = selected.length || trusted.length || codemode
113
+ ? [...selected, ...trusted, ...(codemode ? ["codemode"] : []), DISPOSITION_TOOL] : selected;
111
114
  pi.setActiveTools(activated);
112
115
  const active = new Set(pi.getActiveTools());
113
116
  if (active.size !== activated.length || activated.some((name) => !active.has(name))) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-harness",
3
- "version": "0.37.0",
3
+ "version": "0.37.1",
4
4
  "description": "Pi extension bundle for a write sandbox, subagents, background tasks, SSH, goals, and Outcome & Todo.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -58,7 +58,7 @@
58
58
  "pi-better-goal": "0.11.0",
59
59
  "pi-better-sandbox": "0.14.0",
60
60
  "pi-better-ssh": "0.1.4",
61
- "pi-better-subagents": "0.25.0",
61
+ "pi-better-subagents": "0.25.1",
62
62
  "proper-lockfile": "^4.1.2",
63
63
  "shell-quote": "1.12.0",
64
64
  "smol-toml": "1.9.0",