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.
- package/node_modules/pi-better-subagents/child-codemode.ts +17 -0
- package/node_modules/pi-better-subagents/config.json +1 -1
- package/node_modules/pi-better-subagents/config.ts +1 -1
- package/node_modules/pi-better-subagents/docs/usage.md +10 -3
- package/node_modules/pi-better-subagents/extensions.mjs +2 -1
- package/node_modules/pi-better-subagents/index.ts +6 -4
- package/node_modules/pi-better-subagents/package.json +1 -1
- package/node_modules/pi-better-subagents/task-guard.ts +5 -2
- package/package.json +2 -2
|
@@ -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
|
+
}
|
|
@@ -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
|
|
166
|
-
|
|
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
|
-
|
|
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
|
|
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);
|
|
@@ -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
|
|
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.
|
|
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.
|
|
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",
|