pi-background-tasks 2.5.0 → 2.6.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 (203) hide show
  1. package/BACKGROUND-TASKS-INSTRUCTIONS.md +1 -1
  2. package/README.md +70 -25
  3. package/TESTING.md +42 -2
  4. package/TEST_PLAN.md +7 -4
  5. package/dist/extensions/anthropic-attribution-child.js +4 -0
  6. package/dist/extensions/anthropic-attribution-child.js.map +1 -0
  7. package/dist/extensions/anthropic-attribution.js +107 -0
  8. package/dist/extensions/anthropic-attribution.js.map +1 -0
  9. package/dist/extensions/background-tasks.js +2 -0
  10. package/dist/extensions/background-tasks.js.map +1 -0
  11. package/dist/extensions/delegate-child.js +2 -0
  12. package/dist/extensions/delegate-child.js.map +1 -0
  13. package/dist/extensions/fusion-child.js +2 -0
  14. package/dist/extensions/fusion-child.js.map +1 -0
  15. package/dist/package.json +5 -0
  16. package/dist/src/core/anthropic-attribution-path.js +24 -0
  17. package/dist/src/core/anthropic-attribution-path.js.map +1 -0
  18. package/dist/src/core/anthropic-attribution.js +2247 -0
  19. package/dist/src/core/anthropic-attribution.js.map +1 -0
  20. package/dist/src/core/attested-pi-contract.js +5 -0
  21. package/dist/src/core/attested-pi-contract.js.map +1 -0
  22. package/dist/src/core/attested-pi-run.js +749 -0
  23. package/dist/src/core/attested-pi-run.js.map +1 -0
  24. package/dist/src/core/canonical-json.js +19 -0
  25. package/dist/src/core/canonical-json.js.map +1 -0
  26. package/dist/src/core/common.js +787 -0
  27. package/dist/src/core/common.js.map +1 -0
  28. package/dist/src/core/config.js +78 -0
  29. package/dist/src/core/config.js.map +1 -0
  30. package/dist/src/core/context/parent-snapshot.js +75 -0
  31. package/dist/src/core/context/parent-snapshot.js.map +1 -0
  32. package/dist/src/core/context/token-budget.js +631 -0
  33. package/dist/src/core/context/token-budget.js.map +1 -0
  34. package/dist/src/core/context/visible-conversation-v2.js +390 -0
  35. package/dist/src/core/context/visible-conversation-v2.js.map +1 -0
  36. package/dist/src/core/delegate/artifacts.js +357 -0
  37. package/dist/src/core/delegate/artifacts.js.map +1 -0
  38. package/dist/src/core/delegate/budget.js +266 -0
  39. package/dist/src/core/delegate/budget.js.map +1 -0
  40. package/dist/src/core/delegate/facade-contract.js +7 -0
  41. package/dist/src/core/delegate/facade-contract.js.map +1 -0
  42. package/dist/src/core/delegate/hook-contract-evidence.json +18 -0
  43. package/dist/src/core/delegate/hook-contract.js +124 -0
  44. package/dist/src/core/delegate/hook-contract.js.map +1 -0
  45. package/dist/src/core/delegate/launch.js +366 -0
  46. package/dist/src/core/delegate/launch.js.map +1 -0
  47. package/dist/src/core/delegate/result-package.js +293 -0
  48. package/dist/src/core/delegate/result-package.js.map +1 -0
  49. package/dist/src/core/delegate/runner.js +398 -0
  50. package/dist/src/core/delegate/runner.js.map +1 -0
  51. package/dist/src/core/delegate/seed.js +334 -0
  52. package/dist/src/core/delegate/seed.js.map +1 -0
  53. package/dist/src/core/delegate/types.js +103 -0
  54. package/dist/src/core/delegate/types.js.map +1 -0
  55. package/dist/src/core/durable-fs.js +372 -0
  56. package/dist/src/core/durable-fs.js.map +1 -0
  57. package/dist/src/core/extension-api.js +382 -0
  58. package/dist/src/core/extension-api.js.map +1 -0
  59. package/dist/src/core/fusion/artifacts.js +654 -0
  60. package/dist/src/core/fusion/artifacts.js.map +1 -0
  61. package/dist/src/core/fusion/budget.js +949 -0
  62. package/dist/src/core/fusion/budget.js.map +1 -0
  63. package/dist/src/core/fusion/child-protocol.js +176 -0
  64. package/dist/src/core/fusion/child-protocol.js.map +1 -0
  65. package/dist/src/core/fusion/claude-cache.js +156 -0
  66. package/dist/src/core/fusion/claude-cache.js.map +1 -0
  67. package/dist/src/core/fusion/clean-context.js +64 -0
  68. package/dist/src/core/fusion/clean-context.js.map +1 -0
  69. package/dist/src/core/fusion/config.js +349 -0
  70. package/dist/src/core/fusion/config.js.map +1 -0
  71. package/dist/src/core/fusion/context.js +184 -0
  72. package/dist/src/core/fusion/context.js.map +1 -0
  73. package/dist/src/core/fusion/evaluation.js +696 -0
  74. package/dist/src/core/fusion/evaluation.js.map +1 -0
  75. package/dist/src/core/fusion/facade-contract.js +3 -0
  76. package/dist/src/core/fusion/facade-contract.js.map +1 -0
  77. package/dist/src/core/fusion/orchestrator.js +867 -0
  78. package/dist/src/core/fusion/orchestrator.js.map +1 -0
  79. package/dist/src/core/fusion/output-contract.js +27 -0
  80. package/dist/src/core/fusion/output-contract.js.map +1 -0
  81. package/dist/src/core/fusion/pi-child.js +1741 -0
  82. package/dist/src/core/fusion/pi-child.js.map +1 -0
  83. package/dist/src/core/fusion/prompts.js +260 -0
  84. package/dist/src/core/fusion/prompts.js.map +1 -0
  85. package/dist/src/core/fusion/result-package.js +857 -0
  86. package/dist/src/core/fusion/result-package.js.map +1 -0
  87. package/dist/src/core/fusion/source-policy.js +259 -0
  88. package/dist/src/core/fusion/source-policy.js.map +1 -0
  89. package/dist/src/core/fusion/types.js +207 -0
  90. package/dist/src/core/fusion/types.js.map +1 -0
  91. package/dist/src/core/fusion/web-fetch.js +777 -0
  92. package/dist/src/core/fusion/web-fetch.js.map +1 -0
  93. package/dist/src/core/fusion/workflows.js +126 -0
  94. package/dist/src/core/fusion/workflows.js.map +1 -0
  95. package/dist/src/core/lazy-module.js +181 -0
  96. package/dist/src/core/lazy-module.js.map +1 -0
  97. package/dist/src/core/pi-launch.js +431 -0
  98. package/dist/src/core/pi-launch.js.map +1 -0
  99. package/dist/src/core/registry.js +3331 -0
  100. package/dist/src/core/registry.js.map +1 -0
  101. package/dist/src/core/reload-shell-owner.js +1396 -0
  102. package/dist/src/core/reload-shell-owner.js.map +1 -0
  103. package/dist/src/core/shell-policy.js +80 -0
  104. package/dist/src/core/shell-policy.js.map +1 -0
  105. package/dist/src/core/task-durable.js +51 -0
  106. package/dist/src/core/task-durable.js.map +1 -0
  107. package/dist/src/core/update-check.js +92 -0
  108. package/dist/src/core/update-check.js.map +1 -0
  109. package/dist/src/core/windows-taskkill.js +185 -0
  110. package/dist/src/core/windows-taskkill.js.map +1 -0
  111. package/dist/src/delegate-child-extension.js +776 -0
  112. package/dist/src/delegate-child-extension.js.map +1 -0
  113. package/dist/src/delegate-extension.js +746 -0
  114. package/dist/src/delegate-extension.js.map +1 -0
  115. package/dist/src/extension.js +953 -0
  116. package/dist/src/extension.js.map +1 -0
  117. package/dist/src/fusion-child-extension.js +760 -0
  118. package/dist/src/fusion-child-extension.js.map +1 -0
  119. package/dist/src/fusion-extension.js +1030 -0
  120. package/dist/src/fusion-extension.js.map +1 -0
  121. package/dist/src/ui/background-tasks-manager.js +689 -0
  122. package/dist/src/ui/background-tasks-manager.js.map +1 -0
  123. package/dist/src/ui/fusion-model-selector.js +277 -0
  124. package/dist/src/ui/fusion-model-selector.js.map +1 -0
  125. package/docs/INDEX.md +35 -33
  126. package/docs/api/eventbus-v1.md +21 -5
  127. package/docs/choose-a-workflow.md +2 -0
  128. package/docs/commands/bg-clear.md +3 -3
  129. package/docs/commands/bg-update.md +3 -3
  130. package/docs/commands/bg.md +18 -6
  131. package/docs/commands/claude-cache.md +4 -4
  132. package/docs/commands/fusion-models.md +49 -4
  133. package/docs/commands/fusion.md +3 -3
  134. package/docs/commands/jobs.md +3 -3
  135. package/docs/commands/kill.md +3 -3
  136. package/docs/commands/logs.md +3 -3
  137. package/docs/commands/task-manager.md +5 -5
  138. package/docs/concepts/completion-delivery.md +6 -2
  139. package/docs/getting-started.md +7 -1
  140. package/docs/manifest.json +228 -42
  141. package/docs/operations/configuration.md +58 -5
  142. package/docs/operations/releasing.md +4 -2
  143. package/docs/operations/testing.md +22 -0
  144. package/docs/operations/troubleshooting.md +6 -2
  145. package/docs/read-before-edit.md +44 -31
  146. package/docs/reference/runtime-contracts.md +87 -51
  147. package/docs/reference/shortcuts-and-dock.md +28 -14
  148. package/docs/subsystems/anthropic-attribution.md +23 -11
  149. package/docs/subsystems/attested-pi-runs.md +12 -7
  150. package/docs/subsystems/background-task-runtime.md +53 -11
  151. package/docs/subsystems/child-launch-durability-and-safety.md +28 -9
  152. package/docs/subsystems/delegation.md +16 -3
  153. package/docs/subsystems/docs-freshness-gate.md +14 -7
  154. package/docs/subsystems/fusion.md +36 -11
  155. package/docs/subsystems/host-ui-and-telemetry.md +22 -9
  156. package/docs/tools/bg_delegate.md +3 -1
  157. package/docs/tools/bg_kill.md +3 -1
  158. package/docs/tools/bg_logs.md +3 -1
  159. package/docs/tools/bg_result.md +5 -3
  160. package/docs/tools/bg_run.md +31 -5
  161. package/docs/tools/bg_run_pi_attested.md +3 -1
  162. package/docs/tools/bg_status.md +3 -1
  163. package/docs/tools/fusion_investigate.md +3 -1
  164. package/docs/tools/fusion_reason.md +3 -1
  165. package/docs/tools/fusion_research.md +3 -1
  166. package/docs/tools/fusion_validate.md +3 -1
  167. package/extensions/anthropic-attribution-child.ts +3 -0
  168. package/extensions/anthropic-attribution.ts +166 -1
  169. package/package.json +12 -10
  170. package/src/core/anthropic-attribution-path.ts +5 -4
  171. package/src/core/anthropic-attribution.ts +293 -113
  172. package/src/core/attested-pi-contract.ts +4 -0
  173. package/src/core/attested-pi-run.ts +468 -81
  174. package/src/core/canonical-json.ts +21 -0
  175. package/src/core/common.ts +450 -40
  176. package/src/core/config.ts +121 -0
  177. package/src/core/context/visible-conversation-v2.ts +3 -6
  178. package/src/core/delegate/artifacts.ts +13 -9
  179. package/src/core/delegate/budget.ts +13 -6
  180. package/src/core/delegate/facade-contract.ts +6 -0
  181. package/src/core/delegate/result-package.ts +32 -14
  182. package/src/core/delegate/runner.ts +129 -58
  183. package/src/core/delegate/seed.ts +5 -7
  184. package/src/core/durable-fs.ts +178 -30
  185. package/src/core/extension-api.ts +36 -8
  186. package/src/core/fusion/artifacts.ts +17 -16
  187. package/src/core/fusion/clean-context.ts +8 -5
  188. package/src/core/fusion/config.ts +2 -1
  189. package/src/core/fusion/context.ts +2 -6
  190. package/src/core/fusion/facade-contract.ts +2 -0
  191. package/src/core/fusion/orchestrator.ts +3 -3
  192. package/src/core/fusion/prompts.ts +1 -1
  193. package/src/core/fusion/result-package.ts +259 -72
  194. package/src/core/fusion/source-policy.ts +43 -17
  195. package/src/core/lazy-module.ts +215 -0
  196. package/src/core/pi-launch.ts +390 -65
  197. package/src/core/registry.ts +2010 -381
  198. package/src/core/reload-shell-owner.ts +1662 -0
  199. package/src/core/shell-policy.ts +134 -0
  200. package/src/core/task-durable.ts +67 -0
  201. package/src/delegate-extension.ts +404 -144
  202. package/src/extension.ts +387 -114
  203. package/src/fusion-extension.ts +202 -96
@@ -0,0 +1,134 @@
1
+ import type {
2
+ BeforeAgentStartEvent,
3
+ BeforeAgentStartEventResult,
4
+ ExtensionAPI,
5
+ } from '@earendil-works/pi-coding-agent';
6
+ import {
7
+ resolveShellPolicy,
8
+ type ResolvedShellPolicy,
9
+ type ShellPolicySnapshot,
10
+ } from './common.js';
11
+
12
+ export const SHELL_POLICY_SECTION = 'pi_background_shell_policy';
13
+ const SHELL_POLICY_OPEN = `<${SHELL_POLICY_SECTION}>`;
14
+ const SHELL_POLICY_CLOSE = `</${SHELL_POLICY_SECTION}>`;
15
+
16
+ interface MutableStructuredPromptOptions {
17
+ sections?: Record<string, string> | undefined;
18
+ forceSystemPrompt?: string | undefined;
19
+ }
20
+
21
+ export interface ShellPolicyInitializationOptions {
22
+ readonly platform?: NodeJS.Platform | undefined;
23
+ readonly env?: NodeJS.ProcessEnv | undefined;
24
+ readonly activationCwd?: string | undefined;
25
+ }
26
+
27
+ function displayedArgs(policy: ShellPolicySnapshot): string[] {
28
+ return [
29
+ ...policy.argvPrefix,
30
+ policy.dialect === 'cmd' ? '"<command>"' : '<command>',
31
+ ];
32
+ }
33
+
34
+ /** Stable, non-secret guidance generated from the same selection used for spawning. */
35
+ export function shellPolicyGuidance(policy: ShellPolicySnapshot): string {
36
+ const launch = JSON.stringify({
37
+ policy: policy.policy,
38
+ executable: policy.executable,
39
+ dialect: policy.dialect,
40
+ args: displayedArgs(policy),
41
+ });
42
+ const lines = [
43
+ `bg_run and /bg execute commands with the activation shell policy ${launch}.`,
44
+ 'The executable and arguments are passed directly to process spawn; the executable path is never interpolated into another shell command.',
45
+ ];
46
+
47
+ if (policy.dialect === 'user-non-posix') {
48
+ lines.push(
49
+ 'This inherited user shell is not classified as POSIX or Bash. Do not generate Bash/POSIX syntax or assume Bash startup files for bg_run or /bg.',
50
+ 'Bash remediation: set PI_BG_POSIX_SHELL=bash before starting or reloading Pi; optionally set PI_BG_POSIX_SHELL_PATH to an absolute executable Bash path.',
51
+ );
52
+ } else if (policy.dialect === 'bash') {
53
+ lines.push(
54
+ 'Generate Bash syntax for bg_run and /bg. Commands use Bash -c, never -lc, so login-shell startup files are not loaded implicitly.',
55
+ );
56
+ } else if (policy.dialect === 'posix') {
57
+ lines.push(
58
+ 'Generate portable POSIX shell syntax for bg_run and /bg; do not assume Bash-only syntax. Commands use -c and do not request login-shell startup.',
59
+ );
60
+ } else {
61
+ lines.push(
62
+ 'Generate Windows cmd.exe syntax for bg_run and /bg. The POSIX shell-selection variables do not change Windows execution.',
63
+ );
64
+ }
65
+
66
+ return lines.join('\n');
67
+ }
68
+
69
+ export function renderShellPolicyGuidanceBlock(policy: ShellPolicySnapshot): string {
70
+ return `${SHELL_POLICY_OPEN}\n${shellPolicyGuidance(policy)}\n${SHELL_POLICY_CLOSE}`;
71
+ }
72
+
73
+ /** Replace this package's section without disturbing guidance owned by another hook. */
74
+ export function upsertShellPolicyGuidance(
75
+ systemPrompt: string,
76
+ policy: ShellPolicySnapshot,
77
+ ): string {
78
+ const block = renderShellPolicyGuidanceBlock(policy);
79
+ const start = systemPrompt.indexOf(SHELL_POLICY_OPEN);
80
+ if (start >= 0) {
81
+ const close = systemPrompt.indexOf(SHELL_POLICY_CLOSE, start + SHELL_POLICY_OPEN.length);
82
+ if (close >= 0) {
83
+ return `${systemPrompt.slice(0, start)}${block}${systemPrompt.slice(close + SHELL_POLICY_CLOSE.length)}`;
84
+ }
85
+ }
86
+ return systemPrompt.length > 0 ? `${systemPrompt}\n\n${block}` : block;
87
+ }
88
+
89
+ /**
90
+ * Compose across Pi 0.84's chained string hook and Pi 0.86's structured prompt sections.
91
+ * A pre-existing forced prompt is updated as well because structured sections are opaque
92
+ * while `forceSystemPrompt` is active.
93
+ */
94
+ export function applyShellPolicyGuidance(
95
+ event: Pick<BeforeAgentStartEvent, 'systemPrompt' | 'systemPromptOptions'>,
96
+ policy: ShellPolicySnapshot,
97
+ ): BeforeAgentStartEventResult | undefined {
98
+ const options = event.systemPromptOptions as MutableStructuredPromptOptions;
99
+ if (
100
+ typeof options.sections === 'object' &&
101
+ options.sections !== null &&
102
+ !Array.isArray(options.sections)
103
+ ) {
104
+ options.sections[SHELL_POLICY_SECTION] = shellPolicyGuidance(policy);
105
+ if (typeof options.forceSystemPrompt === 'string') {
106
+ options.forceSystemPrompt = upsertShellPolicyGuidance(options.forceSystemPrompt, policy);
107
+ }
108
+ return undefined;
109
+ }
110
+ return { systemPrompt: upsertShellPolicyGuidance(event.systemPrompt, policy) };
111
+ }
112
+
113
+ export function createShellPolicyGuidanceHandler(policy: ResolvedShellPolicy) {
114
+ return (event: BeforeAgentStartEvent): BeforeAgentStartEventResult | undefined =>
115
+ applyShellPolicyGuidance(event, policy);
116
+ }
117
+
118
+ export function registerShellPolicyGuidance(
119
+ pi: Pick<ExtensionAPI, 'on'>,
120
+ policy: ResolvedShellPolicy,
121
+ ): void {
122
+ pi.on('before_agent_start', createShellPolicyGuidanceHandler(policy));
123
+ }
124
+
125
+ /** Resolve once at extension activation; the returned object is deeply frozen. */
126
+ export function initializeShellPolicy(
127
+ options: ShellPolicyInitializationOptions = {},
128
+ ): ResolvedShellPolicy {
129
+ return resolveShellPolicy(
130
+ options.platform ?? process.platform,
131
+ options.env ?? process.env,
132
+ options.activationCwd ?? process.cwd(),
133
+ );
134
+ }
@@ -0,0 +1,67 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { replaceFileDurable, writeFileDurable } from './durable-fs.js';
4
+
5
+ function signalError(signal: AbortSignal): Error {
6
+ const reason: unknown = signal.reason;
7
+ return reason instanceof Error
8
+ ? reason
9
+ : new Error(
10
+ `Attested Git preflight cancelled${reason === undefined ? '' : `: ${String(reason)}`}`,
11
+ );
12
+ }
13
+
14
+ function throwIfAborted(signal?: AbortSignal): void {
15
+ if (signal?.aborted === true) throw signalError(signal);
16
+ }
17
+
18
+ export async function writeFileFsynced(
19
+ path: string,
20
+ data: Buffer | string,
21
+ signal?: AbortSignal,
22
+ ): Promise<void> {
23
+ throwIfAborted(signal);
24
+ await mkdir(dirname(path), { recursive: true });
25
+ throwIfAborted(signal);
26
+ await writeFileDurable(path, data, signal === undefined ? {} : { signal });
27
+ }
28
+
29
+ export async function writeJsonAtomic(
30
+ path: string,
31
+ value: unknown,
32
+ signal?: AbortSignal,
33
+ ): Promise<void> {
34
+ throwIfAborted(signal);
35
+ await replaceFileDurable(
36
+ path,
37
+ `${JSON.stringify(value, null, 2)}\n`,
38
+ signal === undefined ? {} : { signal },
39
+ );
40
+ }
41
+
42
+ export async function closeAndFsyncOutputStream(
43
+ stream: NodeJS.WritableStream | undefined,
44
+ ): Promise<void> {
45
+ if (!stream) return;
46
+ await new Promise<void>((resolvePromise, reject) => {
47
+ let settled = false;
48
+ const finish = () => {
49
+ if (settled) return;
50
+ settled = true;
51
+ stream.off('error', fail);
52
+ stream.off('close', finish);
53
+ stream.off('finish', finish);
54
+ resolvePromise();
55
+ };
56
+ const fail = (error: Error) => {
57
+ if (settled) return;
58
+ settled = true;
59
+ stream.off('close', finish);
60
+ reject(error);
61
+ };
62
+ stream.once('close', finish);
63
+ stream.once('finish', finish);
64
+ stream.once('error', fail);
65
+ stream.end();
66
+ });
67
+ }