pi-better-harness 0.35.0 → 0.35.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-subagents",
3
- "version": "0.23.0",
3
+ "version": "0.23.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",
@@ -7,6 +7,8 @@ import { SANDBOX_POLICY_CHANNEL, SANDBOX_POLICY_REQUEST_CHANNEL } from "./permis
7
7
  import { failureDispositionTool, intentBashDefinition } from "./child-incidents.ts";
8
8
  import { DISPOSITION_TOOL } from "./incident-model.ts";
9
9
  import { SandboxDiagnostics, diagnosticPackageVersion } from "./shared-sandbox-diagnostics.ts";
10
+ import { fileURLToPath } from "node:url";
11
+ import { claimTaskToolOwner } from "./task-tool-owner.ts";
10
12
 
11
13
  const TRUSTED_INLINE_SOURCE = "<inline:task-sandbox>";
12
14
 
@@ -31,7 +33,7 @@ export function trustedToolRefusal(policy: TaskPolicy, name: string, sourcePath:
31
33
  return undefined;
32
34
  }
33
35
 
34
- export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (error: unknown) => never): void {
36
+ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (error: unknown) => never, ownedTools = false): void {
35
37
  const policy = parseTaskPolicy(input);
36
38
  const support = describeSandboxSupport();
37
39
  if (!support.supported) throw new Error(`Task sandbox unavailable: ${support.reason}`);
@@ -54,9 +56,10 @@ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (erro
54
56
  diagnosticsWarned = true;
55
57
  },
56
58
  });
57
- const boundary = installTaskTools(pi, { controller, cwd: process.cwd(), shellPath: () => shellPath,
59
+ const toolApi = ownedTools ? { ...pi, registerTool: claimTaskToolOwner(pi) } : pi;
60
+ const installBoundary = () => installTaskTools(toolApi, { controller, cwd: process.cwd(), shellPath: () => shellPath,
58
61
  diagnostics,
59
- trustedSources: [TRUSTED_INLINE_SOURCE],
62
+ trustedSources: [ownedTools ? fileURLToPath(new URL("./task-tool-owner.ts", import.meta.url)) : TRUSTED_INLINE_SOURCE],
60
63
  // Structured command intent (#315): validated before the confined command runs.
61
64
  bashDefinition: (cwd, operations) => intentBashDefinition(cwd, operations) as any,
62
65
  applyPatch: policy.applyPatch,
@@ -66,6 +69,9 @@ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (erro
66
69
  admitExtensionTool: (name, _input, sourcePath) => (name === DISPOSITION_TOOL && sourcePath === TRUSTED_INLINE_SOURCE) ||
67
70
  trustedToolRefusal(policy, name, sourcePath) === undefined,
68
71
  });
72
+ // Defer owned registrations until after SDK duplicate-name diagnostics.
73
+ // The final session_start handler still installs and verifies them before activation.
74
+ let boundary = ownedTools ? undefined : installBoundary();
69
75
  pi.registerTool(failureDispositionTool(() => currentCwd) as any);
70
76
  const profile = Object.freeze({ enabled: true, ...policy.permissions });
71
77
  const status = Object.freeze({
@@ -80,6 +86,7 @@ export default function taskGuard(pi: ExtensionAPI, input: unknown, fatal: (erro
80
86
  try {
81
87
  shellPath = SettingsManager.create(ctx.cwd, policy.agentDir, { projectTrusted: false }).getShellPath();
82
88
  currentCwd = ctx.cwd;
89
+ boundary ??= installBoundary();
83
90
  boundary.register(ctx.cwd);
84
91
  const builtins: readonly string[] = policy.applyPatch ? [...TASK_BUILTINS, ...GUARDED_TASK_TOOLS] : TASK_BUILTINS;
85
92
  const known = new Set<string>(builtins);
@@ -29,9 +29,12 @@ try {
29
29
  const guard = await load.import(fileURLToPath(new URL("./task-guard.ts", import.meta.url)), { default: true });
30
30
  if (typeof guard !== "function") throw new Error("Task guard has no extension factory.");
31
31
  const { main } = await import(pathToFileURL(join(dirname(sdkEntry), "main.js")).href);
32
- await main(args, {
32
+ // SDK tool names belong to the first extension registering them. Reserve a
33
+ // source before provider/trusted packages, but keep activation in the final guard.
34
+ const ownerPath = fileURLToPath(new URL("./task-tool-owner.ts", import.meta.url));
35
+ await main(["--extension", ownerPath, ...args], {
33
36
  extensionFactories: [{ name: "task-sandbox", factory: async (pi) => {
34
- try { await guard(pi, policy, fail); } catch (error) { fail(error); }
37
+ try { await guard(pi, policy, fail, true); } catch (error) { fail(error); }
35
38
  } }],
36
39
  });
37
40
  } catch (error) {
@@ -0,0 +1,21 @@
1
+ /** Loaded before third-party extensions so guarded tools win SDK name resolution. */
2
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+
4
+ const CLAIM_CHANNEL = "pi-better-subagents:claim-task-tool-owner";
5
+ type RegisterTool = ExtensionAPI["registerTool"];
6
+
7
+ export default function taskToolOwner(pi: ExtensionAPI): void {
8
+ const unsubscribe = pi.events.on(CLAIM_CHANNEL, (claim) => {
9
+ if (typeof claim !== "function") return;
10
+ unsubscribe();
11
+ claim(pi.registerTool.bind(pi));
12
+ });
13
+ }
14
+
15
+ /** Hand registration to the final guard without changing the SDK's source attribution. */
16
+ export function claimTaskToolOwner(pi: ExtensionAPI): RegisterTool {
17
+ let register: RegisterTool | undefined;
18
+ pi.events.emit(CLAIM_CHANNEL, (owner: RegisterTool) => { register = owner; });
19
+ if (!register) throw new Error("Task tool owner was not installed before extension loading.");
20
+ return register;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-harness",
3
- "version": "0.35.0",
3
+ "version": "0.35.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.10.3",
59
59
  "pi-better-sandbox": "0.14.0",
60
60
  "pi-better-ssh": "0.1.3",
61
- "pi-better-subagents": "0.23.0",
61
+ "pi-better-subagents": "0.23.1",
62
62
  "proper-lockfile": "^4.1.2",
63
63
  "shell-quote": "1.12.0",
64
64
  "smol-toml": "1.9.0",