pi-background-tasks 2.4.2 → 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 +6 -6
  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 +35 -13
  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 +1505 -237
  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,215 @@
1
+ export type LazyModuleState = 'unloaded' | 'loading' | 'loaded' | 'failed' | 'closed';
2
+
3
+ /** Activation-local synchronous close callbacks installed by the entrypoint. */
4
+ export class SynchronousActivationCloseFence {
5
+ private closed = false;
6
+ private readonly callbacks = new Set<() => void>();
7
+
8
+ add(callback: () => void): void {
9
+ this.callbacks.add(callback);
10
+ if (this.closed) callback();
11
+ }
12
+
13
+ close(): void {
14
+ this.closed = true;
15
+ // Re-run idempotent teardown callbacks on repeated lifecycle dispatch. The
16
+ // core callback deliberately clears handles assigned by racing continuations.
17
+ const failures: unknown[] = [];
18
+ for (const callback of this.callbacks) {
19
+ try {
20
+ callback();
21
+ } catch (error) {
22
+ failures.push(error);
23
+ }
24
+ }
25
+ if (failures.length > 0) {
26
+ throw new AggregateError(
27
+ failures,
28
+ `synchronous activation close barrier failed in ${String(failures.length)} callback(s)`,
29
+ );
30
+ }
31
+ }
32
+ }
33
+
34
+ const MODULE_ID_MAX_CHARS = 120;
35
+ const CAUSE_MAX_CHARS = 480;
36
+ const CLOSE_REASON_MAX_CHARS = 160;
37
+
38
+ function boundedSingleLine(value: string, maximum: number): string {
39
+ const singleLine = value.replace(/\s+/gu, ' ').trim();
40
+ if (singleLine.length <= maximum) return singleLine;
41
+ return `${singleLine.slice(0, maximum)}…`;
42
+ }
43
+
44
+ function boundedModuleId(moduleId: string): string {
45
+ const bounded = boundedSingleLine(moduleId, MODULE_ID_MAX_CHARS);
46
+ return bounded.length > 0 ? bounded : 'unnamed-module';
47
+ }
48
+
49
+ function causeText(error: unknown): string {
50
+ if (error instanceof Error) {
51
+ const message = error.message.trim();
52
+ return boundedSingleLine(
53
+ message.length > 0 ? `${error.name}: ${message}` : error.name,
54
+ CAUSE_MAX_CHARS,
55
+ );
56
+ }
57
+ return boundedSingleLine(String(error), CAUSE_MAX_CHARS);
58
+ }
59
+
60
+ class LazyModuleLoadError extends Error {
61
+ readonly code = 'lazy_module_load_failed';
62
+ readonly moduleId: string;
63
+
64
+ constructor(moduleId: string, error: unknown) {
65
+ const cause = causeText(error);
66
+ super(`lazy_module_load_failed: deferred module ${moduleId} could not load: ${cause}`);
67
+ this.name = 'LazyModuleLoadError';
68
+ this.moduleId = moduleId;
69
+ }
70
+ }
71
+
72
+ class LazyModuleClosedError extends Error {
73
+ readonly code = 'lazy_module_closed';
74
+ readonly moduleId: string;
75
+
76
+ constructor(moduleId: string, reason: string) {
77
+ super(
78
+ `lazy_module_closed: deferred module ${moduleId} belongs to a closed activation (${boundedSingleLine(reason, CLOSE_REASON_MAX_CHARS) || 'closed'})`,
79
+ );
80
+ this.name = 'LazyModuleClosedError';
81
+ this.moduleId = moduleId;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Activation-local, single-flight deferred module loader.
87
+ *
88
+ * The loader is deliberately terminal after `close()`: a Pi reload creates a
89
+ * fresh facade and therefore a fresh loader rather than reviving stale state.
90
+ */
91
+ export class LazyModule<T> {
92
+ readonly moduleId: string;
93
+ private readonly importer: () => Promise<T>;
94
+ private currentState: LazyModuleState = 'unloaded';
95
+ private activationGeneration = 0;
96
+ private loadingPromise: Promise<T> | undefined;
97
+ private loadedValue: { value: T } | undefined;
98
+ private loadFailure: Error | undefined;
99
+ private closedFailure: Error | undefined;
100
+
101
+ constructor(moduleId: string, importer: () => Promise<T>) {
102
+ this.moduleId = boundedModuleId(moduleId);
103
+ this.importer = importer;
104
+ }
105
+
106
+ get state(): LazyModuleState {
107
+ return this.currentState;
108
+ }
109
+
110
+ /** Synchronously and permanently close this activation. */
111
+ close(reason = 'activation closed'): void {
112
+ if (this.currentState === 'closed') return;
113
+ this.activationGeneration += 1;
114
+ this.currentState = 'closed';
115
+ this.loadedValue = undefined;
116
+ this.loadingPromise = undefined;
117
+ this.loadFailure = undefined;
118
+ this.closedFailure = new LazyModuleClosedError(this.moduleId, reason);
119
+ }
120
+
121
+ /** Assert immediately before an activation-owned side effect. */
122
+ assertOpen(expectedGeneration = this.activationGeneration): void {
123
+ if (
124
+ this.currentState === 'closed' ||
125
+ expectedGeneration !== this.activationGeneration
126
+ ) {
127
+ throw this.closedError();
128
+ }
129
+ }
130
+
131
+ load(): Promise<T> {
132
+ const generation = this.activationGeneration;
133
+ try {
134
+ this.assertOpen(generation);
135
+ } catch (error) {
136
+ return Promise.reject(error);
137
+ }
138
+
139
+ if (this.currentState === 'loaded') {
140
+ const loaded = this.loadedValue;
141
+ if (loaded === undefined) {
142
+ return Promise.reject(
143
+ new Error(`lazy_module_invariant: ${this.moduleId} is loaded without a value`),
144
+ );
145
+ }
146
+ return Promise.resolve(loaded.value);
147
+ }
148
+ if (this.currentState === 'failed') {
149
+ const failure = this.loadFailure;
150
+ if (failure === undefined) {
151
+ return Promise.reject(
152
+ new Error(`lazy_module_invariant: ${this.moduleId} failed without an error`),
153
+ );
154
+ }
155
+ return Promise.reject(failure);
156
+ }
157
+ if (this.currentState === 'loading') {
158
+ const pending = this.loadingPromise;
159
+ if (pending === undefined) {
160
+ return Promise.reject(
161
+ new Error(`lazy_module_invariant: ${this.moduleId} is loading without a promise`),
162
+ );
163
+ }
164
+ return pending;
165
+ }
166
+
167
+ this.currentState = 'loading';
168
+ // The importer itself runs in a later microtask. `loadingPromise` is stored
169
+ // before that callback can run, including under re-entrant test importers.
170
+ const pending = Promise.resolve()
171
+ .then(() => {
172
+ this.assertOpen(generation);
173
+ return this.importer();
174
+ })
175
+ .then(
176
+ (value) => {
177
+ this.assertOpen(generation);
178
+ this.loadedValue = { value };
179
+ this.loadingPromise = undefined;
180
+ this.currentState = 'loaded';
181
+ return value;
182
+ },
183
+ (error: unknown) => {
184
+ this.assertOpen(generation);
185
+ const failure = new LazyModuleLoadError(this.moduleId, error);
186
+ this.loadFailure = failure;
187
+ this.loadingPromise = undefined;
188
+ this.currentState = 'failed';
189
+ throw failure;
190
+ },
191
+ );
192
+ this.loadingPromise = pending;
193
+ return pending;
194
+ }
195
+
196
+ /**
197
+ * Load once, then invoke this caller's operation independently after the
198
+ * final generation check. Only module loading is shared between callers.
199
+ */
200
+ async run<R>(operation: (module: T) => R | Promise<R>): Promise<R> {
201
+ const generation = this.activationGeneration;
202
+ this.assertOpen(generation);
203
+ const module = await this.load();
204
+ this.assertOpen(generation);
205
+ return operation(module);
206
+ }
207
+
208
+ private closedError(): Error {
209
+ const failure = this.closedFailure;
210
+ if (failure !== undefined) return failure;
211
+ const created = new LazyModuleClosedError(this.moduleId, 'activation closed');
212
+ this.closedFailure = created;
213
+ return created;
214
+ }
215
+ }