@pi-unipi/background-tasks 2.16.0 → 2.17.0

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 (46) hide show
  1. package/README.md +21 -27
  2. package/package.json +3 -4
  3. package/src/cards.ts +76 -0
  4. package/src/child-process.ts +1 -1
  5. package/src/config.ts +0 -42
  6. package/src/context-visible-conversation-v2.ts +1 -1
  7. package/src/delegate/artifacts.ts +1 -1
  8. package/src/delegate/launch.ts +17 -30
  9. package/src/delegate/result-package.ts +1 -1
  10. package/src/delegate/runner.ts +1 -20
  11. package/src/delegate/seed.ts +1 -1
  12. package/src/delegate-extension.ts +16 -168
  13. package/src/index.ts +53 -25
  14. package/src/json-utils.ts +56 -0
  15. package/src/package-assets.ts +51 -0
  16. package/src/registry.ts +8 -459
  17. package/src/task-manager.ts +13 -2
  18. package/src/tools.ts +4 -189
  19. package/src/types.ts +17 -70
  20. package/extensions/anthropic-attribution.ts +0 -1
  21. package/extensions/fusion-child.ts +0 -1
  22. package/src/anthropic-attribution-path.ts +0 -21
  23. package/src/anthropic-attribution.ts +0 -1983
  24. package/src/attested-pi-run.ts +0 -612
  25. package/src/fixtures/fusion-golden-bytes.json +0 -310
  26. package/src/fixtures/fusion-validate-golden-bytes.json +0 -282
  27. package/src/fusion/artifacts.ts +0 -967
  28. package/src/fusion/budget.ts +0 -1162
  29. package/src/fusion/child-protocol.ts +0 -305
  30. package/src/fusion/claude-cache.ts +0 -207
  31. package/src/fusion/clean-context.ts +0 -91
  32. package/src/fusion/config.ts +0 -449
  33. package/src/fusion/context.ts +0 -265
  34. package/src/fusion/evaluation.ts +0 -800
  35. package/src/fusion/orchestrator.ts +0 -1288
  36. package/src/fusion/output-contract.ts +0 -34
  37. package/src/fusion/pi-child.ts +0 -2373
  38. package/src/fusion/prompts.ts +0 -345
  39. package/src/fusion/result-package.ts +0 -959
  40. package/src/fusion/source-policy.ts +0 -257
  41. package/src/fusion/types.ts +0 -1139
  42. package/src/fusion/web-fetch.ts +0 -1060
  43. package/src/fusion/workflows.ts +0 -184
  44. package/src/fusion-child-extension.ts +0 -1052
  45. package/src/fusion-extension.ts +0 -1293
  46. package/src/ui/fusion-model-selector.ts +0 -322
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # @pi-unipi/background-tasks
2
2
 
3
- Background tasks for UniPi — a full adoption of
4
- [pi-background-tasks](https://github.com/ismailsaleekh/pi-background-tasks) into the
5
- unipi conventions: durable background shell jobs, read-only delegated agents,
6
- attested Pi runs, fixed-purpose multi-model Fusion workflows, and package-owned
7
- Anthropic subscription attribution.
3
+ Background tasks for UniPi — durable background shell jobs plus one read-only
4
+ delegated background agent, with a footer dock the user can manage while the
5
+ agent keeps working. Originally adopted from
6
+ [pi-background-tasks](https://github.com/ismailsaleekh/pi-background-tasks);
7
+ the Fusion council, attested Pi runs, and Anthropic attribution surfaces were
8
+ removed in 2.17.0 so the module does exactly one thing.
8
9
 
9
10
  ## Master toggle
10
11
 
@@ -19,37 +20,37 @@ One config key disables the entire module — no tools, no commands, no hooks, n
19
20
  ```
20
21
 
21
22
  Open `/unipi:bg-settings` for the interactive settings overlay (master toggle,
22
- defaults, output caps, delegate/fusion defaults).
23
+ defaults, output caps, delegate defaults).
23
24
 
24
25
  ## Surfaces
25
26
 
26
- ### Tools (reference names kept)
27
+ ### Tools
27
28
 
28
29
  | Tool | Purpose |
29
30
  | --- | --- |
30
31
  | `bg_run` | Start a named long-running shell command; terminal notification wakes a follow-up turn by default |
31
32
  | `bg_status` / `bg_logs` / `bg_kill` | Point-in-time inspection, bounded log reads, stop — never polling primitives |
32
33
  | `bg_delegate` + `bg_result` | One read-only child Pi agent seeded with a frozen projection of this conversation; hash-verified answer retrieval |
33
- | `bg_run_pi_attested` | Evidence-oriented direct Pi spawn with local hashes and route attestation |
34
- | `fusion_reason` | 3 candidates → blind evaluator → merger on the session projection |
35
- | `fusion_investigate` / `fusion_research` / `fusion_validate` | Fixed-purpose multi-model workflows on clean-task input (inspect tools / caller-URL research / advisory review) |
36
34
 
37
- ### Commands (our namespace)
35
+ ### Commands
38
36
 
39
- `/unipi:bg`, `/unipi:bg-clear`, `/unipi:bg-tasks`, `/unipi:bg-update`,
40
- `/unipi:tasks`, `/unipi:jobs`, `/unipi:kill`, `/unipi:logs`,
41
- `/unipi:bg-settings`, `/unipi:fusion`, `/unipi:fusion-models`,
42
- `/unipi:claude-cache`.
37
+ `/unipi:bg` (start a shell task), `/unipi:bg-tasks` (open the dock),
38
+ `/unipi:bg-settings`.
43
39
 
44
40
  Shortcuts: `Shift↓` opens the task manager dock; `Ctrl+Alt+C` clears finished notices.
45
41
 
42
+ ## What the user sees
43
+
44
+ - **Launch card** — `bg_run` results render as a tinted card (`● bg started <name> · wakes agent on completion`) so the start of a task is visible in the transcript.
45
+ - **Pending-wake line** — while the agent is idle but a task that will wake it is still running, a spinner line sits above the editor: `⠋ waiting on 1 bg task · <name> 12s — agent resumes automatically when done`. Without this the UI looks finished and users assume the turn is over.
46
+ - **Completion card** — the terminal notification renders as a tinted card (`✓ bg done <name> · exit 0 · 25s · agent woken`) with the last three output lines. Failed / stopped tasks use the error tint.
47
+ - **Dock** (`Shift↓`) — rows show `⏰ wakes agent` for tasks that will resume the agent, and the last output line for running shell tasks.
48
+
46
49
  ## Storage layout (ours — never `.pi/`)
47
50
 
48
51
  - Runtime artifacts (task output/metadata): `$TMPDIR/unipi-bg-tasks/<session>-<pid>-<nonce>/`
49
52
  - Durable delegate artifacts: `<workspace>/.unipi/delegate/<session>-<pid>/<task-id>/`
50
- - Durable fusion artifacts: `<workspace>/.unipi/fusion/<session>-<pid>/<run-id>/`
51
53
  - Config: `~/.unipi/config/background-tasks.json` + workspace override
52
- - Fusion model config: five-slot selector persisted through `/unipi:fusion-models`
53
54
 
54
55
  ## Environment
55
56
 
@@ -67,13 +68,6 @@ Shortcuts: `Shift↓` opens the task manager dock; `Ctrl+Alt+C` clears finished
67
68
 
68
69
  Treat `<background-task-notification>` as durable terminal truth — do not poll.
69
70
 
70
- ## Attribution
71
-
72
- `extensions/anthropic-attribution.ts` loads first (provider-gated): Claude Code
73
- subscription OAuth attribution, exact-match system-prompt sanitization, and
74
- cache-retention policy for Anthropic routes. Duplicate installed copies resolve
75
- ownership through an EventBus claim; later copies go inert.
76
-
77
71
  ## Shared registry (for sibling extensions)
78
72
 
79
73
  Other packages can read the live task registry synchronously — no events, no polling:
@@ -94,10 +88,10 @@ treat that as "no data", e.g. the footer's glance process line does.
94
88
 
95
89
  - Commands live in the `/unipi:*` namespace; env prefix is `UNIPI_BG_*`.
96
90
  - Runtime artifacts under the OS temp root (per-registry nonce) and durable
97
- delegate/fusion artifacts under workspace `.unipi/` — never `.pi/`.
91
+ delegate artifacts under workspace `.unipi/` — never `.pi/`.
98
92
  - The reference's `update-check` footer surface is dropped (unipi's updater
99
93
  module owns updates).
100
- - `agent_settled` / `before_provider_headers` hooks register defensively on our
101
- pi SDK with an `agent_end` settlement fallback.
94
+ - Removed: attested Pi runs, the five-slot Fusion council tools, and the
95
+ Anthropic attribution provider override.
102
96
 
103
97
  ISC-licensed reference: Copyright Ismail <ismailsalikhodjaev@gmail.com>.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pi-unipi/background-tasks",
3
- "version": "2.16.0",
4
- "description": "Background tasks for UniPi — durable shell jobs, delegated agents, attested Pi runs, and fixed-purpose Fusion workflows",
3
+ "version": "2.17.0",
4
+ "description": "Background tasks for UniPi — durable shell jobs and delegated background agents with a footer dock",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "types": "src/index.ts",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@earendil-works/pi-ai": "^0.84.0",
13
- "@pi-unipi/core": "2.16.0",
13
+ "@pi-unipi/core": "2.17.0",
14
14
  "turndown": "^7.2.4"
15
15
  },
16
16
  "devDependencies": {
@@ -31,7 +31,6 @@
31
31
  ],
32
32
  "pi": {
33
33
  "extensions": [
34
- "./extensions/anthropic-attribution.ts",
35
34
  "./src/index.ts"
36
35
  ],
37
36
  "skills": [],
package/src/cards.ts ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @pi-unipi/background-tasks — tinted chat cards
3
+ *
4
+ * Background-task launch/completion messages were plain single lines that got
5
+ * lost in the transcript. These helpers render them as padded, background-
6
+ * tinted boxes (same visual language as pi's tool cards) so a user scanning
7
+ * the chat can spot "a bg task started here" / "it finished here" at a glance.
8
+ */
9
+ import { Box, Text } from "@earendil-works/pi-tui";
10
+ import { formatDuration, taskDisplayName, type BgTaskSnapshot } from "./types.js";
11
+
12
+ type ThemeLike = {
13
+ fg: (color: string, text: string) => string;
14
+ bg: (color: string, text: string) => string;
15
+ bold: (text: string) => string;
16
+ };
17
+
18
+ type CardTone = "pending" | "success" | "error";
19
+
20
+ const BG_FOR_TONE: Record<CardTone, string> = {
21
+ pending: "toolPendingBg",
22
+ success: "toolSuccessBg",
23
+ error: "toolErrorBg",
24
+ };
25
+
26
+ function toneFor(status: BgTaskSnapshot["status"]): CardTone {
27
+ if (status === "running") return "pending";
28
+ if (status === "completed") return "success";
29
+ return "error";
30
+ }
31
+
32
+ function statusGlyph(theme: ThemeLike, status: BgTaskSnapshot["status"]): string {
33
+ if (status === "running") return theme.fg("accent", "●");
34
+ if (status === "completed") return theme.fg("success", "✓");
35
+ if (status === "killed") return theme.fg("warning", "■");
36
+ return theme.fg("error", "✗");
37
+ }
38
+
39
+ function card(theme: ThemeLike, tone: CardTone, lines: string[]): Box {
40
+ const box = new Box(1, 0, (text) => theme.bg(BG_FOR_TONE[tone], text));
41
+ box.addChild(new Text(lines.join("\n"), 0, 0));
42
+ return box;
43
+ }
44
+
45
+ /** `bg_run` result card: "● bg started <name> (id) · will wake agent" */
46
+ export function renderLaunchCard(theme: ThemeLike, task: BgTaskSnapshot): Box {
47
+ const wake = task.triggerOnCompletion
48
+ ? theme.fg("dim", " · wakes agent on completion")
49
+ : task.notifyOnCompletion
50
+ ? theme.fg("dim", " · notifies on completion")
51
+ : theme.fg("dim", " · silent");
52
+ const head = `${statusGlyph(theme, "running")} ${theme.fg("accent", theme.bold("bg started"))} ${theme.fg("accent", taskDisplayName(task))} ${theme.fg("dim", `(${task.id})`)}${wake}`;
53
+ const cmd = theme.fg("dim", `$ ${task.command.length > 120 ? `${task.command.slice(0, 120)}…` : task.command}`);
54
+ return card(theme, "pending", [head, cmd]);
55
+ }
56
+
57
+ /** Completion notification card: "✓ bg done <name> · exit 0 · 25s" + last output lines */
58
+ export function renderCompletionCard(theme: ThemeLike, task: BgTaskSnapshot | undefined): Box {
59
+ if (task === undefined) {
60
+ return card(theme, "success", [`${theme.fg("success", "✓")} ${theme.fg("accent", theme.bold("bg done"))}`]);
61
+ }
62
+ const tone = toneFor(task.status);
63
+ const label =
64
+ task.status === "completed" ? "bg done" : task.status === "killed" ? "bg stopped" : "bg failed";
65
+ const meta: string[] = [];
66
+ if (task.exitCode !== undefined && task.exitCode !== null) meta.push(`exit ${String(task.exitCode)}`);
67
+ if (task.endTime !== undefined) meta.push(formatDuration(task.endTime - task.startTime));
68
+ if (task.triggerOnCompletion) meta.push("agent woken");
69
+ const head = `${statusGlyph(theme, task.status)} ${theme.fg(tone === "error" ? "error" : "success", theme.bold(label))} ${theme.fg("accent", taskDisplayName(task))} ${theme.fg("dim", `(${task.id})`)}${meta.length > 0 ? theme.fg("dim", ` · ${meta.join(" · ")}`) : ""}`;
70
+ const lines = [head];
71
+ if (task.error) lines.push(theme.fg("error", task.error));
72
+ const tail = task.outputTail ?? [];
73
+ for (const line of tail) lines.push(theme.fg("toolOutput", ` ${line}`));
74
+ lines.push(theme.fg("dim", ` ${task.outputPath}`));
75
+ return card(theme, tone, lines);
76
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @pi-unipi/background-tasks — Shared child-process & context interfaces
3
3
  *
4
- * Consumed by types.ts, registry.ts, attested-pi-run.ts, and tests.
4
+ * Consumed by types.ts, registry.ts, and tests.
5
5
  */
6
6
 
7
7
  import type { SpawnOptions } from "node:child_process";
package/src/config.ts CHANGED
@@ -34,13 +34,6 @@ export interface BackgroundTasksConfig {
34
34
  maxToolCalls: number;
35
35
  timeoutSeconds: number;
36
36
  };
37
- /** Fusion defaults. */
38
- fusion: {
39
- /** Five-slot model selections ('' = $current). */
40
- candidates: [string, string, string];
41
- evaluator: string;
42
- merger: string;
43
- };
44
37
  }
45
38
 
46
39
  export const DEFAULT_CONFIG: BackgroundTasksConfig = {
@@ -57,11 +50,6 @@ export const DEFAULT_CONFIG: BackgroundTasksConfig = {
57
50
  maxToolCalls: 120,
58
51
  timeoutSeconds: 900,
59
52
  },
60
- fusion: {
61
- candidates: ["", "", ""],
62
- evaluator: "",
63
- merger: "",
64
- },
65
53
  };
66
54
 
67
55
  function getGlobalConfigPath(): string {
@@ -181,28 +169,6 @@ export function validateBackgroundTasksConfig(config: unknown): string[] {
181
169
  }
182
170
  }
183
171
  }
184
- if (c.fusion !== undefined) {
185
- if (typeof c.fusion !== "object" || c.fusion === null) {
186
- problems.push("fusion must be an object");
187
- } else {
188
- const f = c.fusion as Record<string, unknown>;
189
- for (const key of ["candidates", "evaluator", "merger"] as const) {
190
- const value = f[key];
191
- if (value === undefined) continue;
192
- if (key === "candidates") {
193
- if (
194
- !Array.isArray(value) ||
195
- value.length !== 3 ||
196
- value.some((v) => typeof v !== "string")
197
- ) {
198
- problems.push("fusion.candidates must be an array of exactly 3 strings");
199
- }
200
- } else if (typeof value !== "string") {
201
- problems.push(`fusion.${key} must be a string`);
202
- }
203
- }
204
- }
205
- }
206
172
  return problems;
207
173
  }
208
174
 
@@ -272,14 +238,6 @@ export function loadBackgroundTasksConfig(cwd: string): LoadedBackgroundTasksCon
272
238
  ? merged.delegate.autoDeliver
273
239
  : "when_small",
274
240
  },
275
- fusion: {
276
- candidates:
277
- Array.isArray(merged.fusion?.candidates) && merged.fusion.candidates.length === 3
278
- ? ([...merged.fusion.candidates] as [string, string, string])
279
- : DEFAULT_CONFIG.fusion.candidates,
280
- evaluator: typeof merged.fusion?.evaluator === "string" ? merged.fusion.evaluator : "",
281
- merger: typeof merged.fusion?.merger === "string" ? merged.fusion.merger : "",
282
- },
283
241
  };
284
242
  return { config, warnings };
285
243
  }
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import type { Message } from '@earendil-works/pi-ai';
3
- import { canonicalJson } from './attested-pi-run.js';
3
+ import { canonicalJson } from './json-utils.js';
4
4
 
5
5
  /**
6
6
  * Frozen `visible-conversation-ledger-v2` transform.
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { chmod, mkdir, open, readFile, rename, rm } from 'node:fs/promises';
4
4
  import { basename, isAbsolute, join, relative, sep } from 'node:path';
5
- import { canonicalJson } from '../attested-pi-run.js';
5
+ import { canonicalJson } from '../json-utils.js';
6
6
  import { sanitizePathSegment } from '../types.js';
7
7
  import { replaceFileDurable, writeFileDurable } from '../durable-fs.js';
8
8
  import {
@@ -1,5 +1,6 @@
1
1
  import { randomBytes } from 'node:crypto';
2
2
  import { existsSync } from 'node:fs';
3
+ import { packageAssetSearchHint, resolvePackageAsset } from '../package-assets.js';
3
4
  import { mkdir } from 'node:fs/promises';
4
5
  import { dirname, join, resolve } from 'node:path';
5
6
  import { fileURLToPath } from 'node:url';
@@ -184,15 +185,20 @@ export function resolveDelegateChildExtensionPath(
184
185
  moduleUrl = import.meta.url,
185
186
  pathExists: (path: string) => boolean = existsSync,
186
187
  ): string {
187
- const modulePath = fileURLToPath(moduleUrl);
188
- const extension = modulePath.endsWith('.ts') ? 'delegate-child.ts' : 'delegate-child.js';
189
- const candidate = resolve(dirname(modulePath), '../../extensions', extension);
190
- if (!pathExists(candidate)) {
191
- throw new DelegateError(`delegate child extension is missing: ${candidate}`, {
192
- code: 'delegate_isolation_unsupported',
193
- childCreated: false,
194
- remediation: ['Reinstall the package; the child guard extension is required to spawn.'],
195
- });
188
+ // Prefer the TypeScript guard (pi loads extensions through jiti); fall back
189
+ // to a compiled copy when one exists.
190
+ const candidate =
191
+ resolvePackageAsset('extensions/delegate-child.ts', { moduleUrl, pathExists }) ??
192
+ resolvePackageAsset('extensions/delegate-child.js', { moduleUrl, pathExists });
193
+ if (candidate === undefined) {
194
+ throw new DelegateError(
195
+ `delegate child extension is missing: ${packageAssetSearchHint('extensions/delegate-child.ts')}`,
196
+ {
197
+ code: 'delegate_isolation_unsupported',
198
+ childCreated: false,
199
+ remediation: ['Reinstall the package; the child guard extension is required to spawn.'],
200
+ },
201
+ );
196
202
  }
197
203
  return candidate;
198
204
  }
@@ -201,7 +207,7 @@ export function resolveDelegateChildExtensionPath(
201
207
  * Tools the inspect capability permits.
202
208
  *
203
209
  * v1 is read-only by construction: no shell, no network, no edit/write, no
204
- * recursive delegation, no Fusion. The list is passed to `--tools`, so it is
210
+ * recursive delegation. The list is passed to `--tools`, so it is
205
211
  * enforced by the child's tool registry rather than by prompt text.
206
212
  */
207
213
  export const DELEGATE_INSPECT_TOOLS: readonly string[] = [
@@ -223,12 +229,6 @@ export const DELEGATE_FORBIDDEN_TOOLS: readonly string[] = [
223
229
  'bg_kill',
224
230
  'bg_status',
225
231
  'bg_logs',
226
- 'bg_run_pi_attested',
227
- 'fusion_brainstorm',
228
- 'fusion_reason',
229
- 'fusion_investigate',
230
- 'fusion_research',
231
- 'fusion_validate',
232
232
  ];
233
233
 
234
234
  export function delegateToolsFor(capability: DelegateCapability): readonly string[] {
@@ -256,7 +256,6 @@ export interface DelegateChildArgvInput {
256
256
  childSessionId: string;
257
257
  childSessionDir: string;
258
258
  childExtensionPath: string;
259
- attributionExtensionPath?: string | undefined;
260
259
  systemPrompt: string;
261
260
  }
262
261
 
@@ -282,19 +281,7 @@ export function buildDelegateChildArgv(input: DelegateChildArgvInput): string[]
282
281
  );
283
282
  }
284
283
  }
285
- const extensionPaths =
286
- input.route.provider === 'anthropic'
287
- ? [
288
- input.attributionExtensionPath ??
289
- (() => {
290
- throw new DelegateError(
291
- 'Anthropic delegate launch requires the package attribution extension',
292
- { code: 'delegate_isolation_unsupported', childCreated: false },
293
- );
294
- })(),
295
- input.childExtensionPath,
296
- ]
297
- : [input.childExtensionPath];
284
+ const extensionPaths = [input.childExtensionPath];
298
285
 
299
286
  return [
300
287
  '--mode',
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { canonicalJson } from '../attested-pi-run.js';
2
+ import { canonicalJson } from '../json-utils.js';
3
3
  import {
4
4
  DELEGATE_RECEIPT_SCHEMA_VERSION,
5
5
  DELEGATE_RESULT_PACKAGE_SCHEMA_VERSION,
@@ -1,8 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
- import { canonicalJson } from '../attested-pi-run.js';
3
+ import { canonicalJson } from '../json-utils.js';
4
4
  import { replaceFileDurable } from '../durable-fs.js';
5
- import { resolveAnthropicAttributionExtensionPath } from '../anthropic-attribution-path.js';
6
5
  import { join } from 'node:path';
7
6
  import { DelegateArtifactStore, discardDelegateArtifactRoot } from './artifacts.js';
8
7
  import {
@@ -52,7 +51,6 @@ export interface PrepareDelegateLaunchInput extends DelegatePreflightInput {
52
51
  autoDeliver: DelegateAutoDeliverMode;
53
52
  extensionMode: DelegateExtensionMode;
54
53
  childExtensionPath?: string | undefined;
55
- attributionExtensionPath?: string | undefined;
56
54
  env?: NodeJS.ProcessEnv | undefined;
57
55
  now?: (() => Date) | undefined;
58
56
  }
@@ -72,22 +70,6 @@ export async function prepareDelegateLaunch(
72
70
  // its child guard must refuse rather than spawn an unguarded child.
73
71
  const childExtensionPath =
74
72
  input.childExtensionPath ?? resolveDelegateChildExtensionPath();
75
- let attributionExtensionPath: string | undefined;
76
- if (input.route.provider === 'anthropic') {
77
- try {
78
- attributionExtensionPath =
79
- input.attributionExtensionPath ?? resolveAnthropicAttributionExtensionPath();
80
- } catch (error) {
81
- throw new DelegateError(
82
- `Anthropic delegate attribution extension could not be resolved: ${error instanceof Error ? error.message : String(error)}`,
83
- {
84
- code: 'delegate_isolation_unsupported',
85
- childCreated: false,
86
- remediation: ['Reinstall the package; Anthropic delegates require attribution.'],
87
- },
88
- );
89
- }
90
- }
91
73
 
92
74
  const preflight = preflightDelegateLaunch(input);
93
75
 
@@ -132,7 +114,6 @@ export async function prepareDelegateLaunch(
132
114
  childSessionId: preflight.childSessionId,
133
115
  childSessionDir: childSessionDirAbs,
134
116
  childExtensionPath,
135
- attributionExtensionPath,
136
117
  systemPrompt: preflight.childSystemPrompt,
137
118
  });
138
119
  const env = delegateChildEnv(
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { canonicalJson } from '../attested-pi-run.js';
2
+ import { canonicalJson } from '../json-utils.js';
3
3
  import {
4
4
  UnsupportedConversationBlockError,
5
5
  projectVisibleConversationV2,