@marcoscale98/pi-extensible-workflows 5.14.1-fork.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.
Files changed (204) hide show
  1. package/CHANGELOG.md +536 -0
  2. package/README.md +64 -0
  3. package/dist/src/agent-execution.d.ts +338 -0
  4. package/dist/src/agent-execution.js +1633 -0
  5. package/dist/src/background-widget.d.ts +74 -0
  6. package/dist/src/background-widget.js +1340 -0
  7. package/dist/src/budget.d.ts +38 -0
  8. package/dist/src/budget.js +188 -0
  9. package/dist/src/changelog.d.ts +6 -0
  10. package/dist/src/changelog.js +112 -0
  11. package/dist/src/decoders.d.ts +96 -0
  12. package/dist/src/decoders.js +629 -0
  13. package/dist/src/execution.d.ts +19 -0
  14. package/dist/src/execution.js +749 -0
  15. package/dist/src/herdr.d.ts +53 -0
  16. package/dist/src/herdr.js +270 -0
  17. package/dist/src/host-delivery.d.ts +95 -0
  18. package/dist/src/host-delivery.js +376 -0
  19. package/dist/src/host-navigator.d.ts +83 -0
  20. package/dist/src/host-navigator.js +1087 -0
  21. package/dist/src/host-phases.d.ts +89 -0
  22. package/dist/src/host-phases.js +320 -0
  23. package/dist/src/host-recovery.d.ts +141 -0
  24. package/dist/src/host-recovery.js +356 -0
  25. package/dist/src/host-runtime.d.ts +78 -0
  26. package/dist/src/host-runtime.js +378 -0
  27. package/dist/src/host-view.d.ts +116 -0
  28. package/dist/src/host-view.js +868 -0
  29. package/dist/src/host.d.ts +40 -0
  30. package/dist/src/host.js +1641 -0
  31. package/dist/src/index.d.ts +29 -0
  32. package/dist/src/index.js +13725 -0
  33. package/dist/src/index.js.map +6 -0
  34. package/dist/src/io.d.ts +14 -0
  35. package/dist/src/io.js +82 -0
  36. package/dist/src/package-identity.d.ts +4 -0
  37. package/dist/src/package-identity.js +6 -0
  38. package/dist/src/paths.d.ts +14 -0
  39. package/dist/src/paths.js +51 -0
  40. package/dist/src/persistence.d.ts +5 -0
  41. package/dist/src/persistence.js +5 -0
  42. package/dist/src/pi-runtime-adapter.d.ts +27 -0
  43. package/dist/src/pi-runtime-adapter.js +243 -0
  44. package/dist/src/pi-runtime-runner.d.ts +42 -0
  45. package/dist/src/pi-runtime-runner.js +505 -0
  46. package/dist/src/registry.d.ts +48 -0
  47. package/dist/src/registry.js +379 -0
  48. package/dist/src/retention.d.ts +17 -0
  49. package/dist/src/retention.js +195 -0
  50. package/dist/src/runtime/agent-runner.d.ts +143 -0
  51. package/dist/src/runtime/agent-runner.js +12 -0
  52. package/dist/src/runtime/index.d.ts +2 -0
  53. package/dist/src/runtime/index.js +2 -0
  54. package/dist/src/runtime/namespace.d.ts +14 -0
  55. package/dist/src/runtime/namespace.js +1 -0
  56. package/dist/src/runtime/workflow-result.d.ts +2 -0
  57. package/dist/src/runtime/workflow-result.js +6 -0
  58. package/dist/src/session-handoff.d.ts +2 -0
  59. package/dist/src/session-handoff.js +82 -0
  60. package/dist/src/session-lease.d.ts +12 -0
  61. package/dist/src/session-lease.js +180 -0
  62. package/dist/src/store.d.ts +95 -0
  63. package/dist/src/store.js +951 -0
  64. package/dist/src/subagent-manager-handle.d.ts +4 -0
  65. package/dist/src/subagent-manager-handle.js +6 -0
  66. package/dist/src/tool-timing.d.ts +12 -0
  67. package/dist/src/tool-timing.js +33 -0
  68. package/dist/src/trajectory-contracts.d.ts +8 -0
  69. package/dist/src/trajectory-contracts.js +9 -0
  70. package/dist/src/trajectory-host-handle.d.ts +12 -0
  71. package/dist/src/trajectory-host-handle.js +6 -0
  72. package/dist/src/trajectory.d.ts +128 -0
  73. package/dist/src/trajectory.js +594 -0
  74. package/dist/src/types.d.ts +960 -0
  75. package/dist/src/types.js +60 -0
  76. package/dist/src/utils.d.ts +57 -0
  77. package/dist/src/utils.js +225 -0
  78. package/dist/src/validation.d.ts +29 -0
  79. package/dist/src/validation.js +1100 -0
  80. package/dist/src/workflow-artifacts.d.ts +14 -0
  81. package/dist/src/workflow-artifacts.js +40 -0
  82. package/dist/starter/index.d.ts +1 -0
  83. package/dist/starter/index.js +103 -0
  84. package/dist/starter/index.js.map +6 -0
  85. package/dist/starter/review-loop.d.ts +72 -0
  86. package/dist/starter/review-loop.js +43 -0
  87. package/dist/starter/roles/developer.md +6 -0
  88. package/dist/starter/roles/oracle.md +18 -0
  89. package/dist/starter/roles/researcher.md +20 -0
  90. package/dist/starter/roles/reviewer.md +19 -0
  91. package/dist/starter/roles/scout.md +18 -0
  92. package/dist/subagents/index.d.ts +8 -0
  93. package/dist/subagents/index.js +8021 -0
  94. package/dist/subagents/index.js.map +6 -0
  95. package/dist/subagents/src/contracts.d.ts +173 -0
  96. package/dist/subagents/src/contracts.js +68 -0
  97. package/dist/subagents/src/decode.d.ts +15 -0
  98. package/dist/subagents/src/decode.js +182 -0
  99. package/dist/subagents/src/index.d.ts +12 -0
  100. package/dist/subagents/src/index.js +180 -0
  101. package/dist/subagents/src/manager.d.ts +3 -0
  102. package/dist/subagents/src/manager.js +1636 -0
  103. package/dist/subagents/src/navigator.d.ts +5 -0
  104. package/dist/subagents/src/navigator.js +627 -0
  105. package/dist/subagents/src/view.d.ts +70 -0
  106. package/dist/subagents/src/view.js +387 -0
  107. package/dist/subagents/src/worktree.d.ts +34 -0
  108. package/dist/subagents/src/worktree.js +68 -0
  109. package/dist/trajectory/assets/favicon.png +0 -0
  110. package/dist/trajectory/assets/index.html +772 -0
  111. package/dist/trajectory/assets/marked.min.js +6 -0
  112. package/dist/trajectory/assets/morphdom.min.js +1 -0
  113. package/dist/trajectory/index.d.ts +5 -0
  114. package/dist/trajectory/index.js +3459 -0
  115. package/dist/trajectory/index.js.map +6 -0
  116. package/dist/trajectory/src/assets/favicon.png +0 -0
  117. package/dist/trajectory/src/assets/index.html +772 -0
  118. package/dist/trajectory/src/assets/marked.min.js +6 -0
  119. package/dist/trajectory/src/assets/morphdom.min.js +1 -0
  120. package/dist/trajectory/src/export.d.ts +25 -0
  121. package/dist/trajectory/src/export.js +80 -0
  122. package/dist/trajectory/src/index.d.ts +26 -0
  123. package/dist/trajectory/src/index.js +732 -0
  124. package/dist/trajectory/src/server.d.ts +7 -0
  125. package/dist/trajectory/src/server.js +2534 -0
  126. package/dist/trajectory/src/server.js.map +6 -0
  127. package/examples/advanced-patterns/auto-model-router.mjs +78 -0
  128. package/examples/workflow-extension-template/README.md +37 -0
  129. package/examples/workflow-extension-template/extension.test.mjs +59 -0
  130. package/examples/workflow-extension-template/index.js +51 -0
  131. package/examples/workflow-extension-template/roles/reviewer.md +4 -0
  132. package/package.json +105 -0
  133. package/skills/pi-extensible-workflows/SKILL.md +160 -0
  134. package/src/agent-execution.ts +1533 -0
  135. package/src/background-widget.ts +1496 -0
  136. package/src/budget.ts +109 -0
  137. package/src/changelog.ts +114 -0
  138. package/src/decoders.ts +567 -0
  139. package/src/execution.ts +652 -0
  140. package/src/herdr.ts +240 -0
  141. package/src/host-delivery.ts +346 -0
  142. package/src/host-navigator.ts +871 -0
  143. package/src/host-phases.ts +264 -0
  144. package/src/host-recovery.ts +333 -0
  145. package/src/host-runtime.ts +373 -0
  146. package/src/host-view.ts +828 -0
  147. package/src/host.ts +1502 -0
  148. package/src/index.ts +29 -0
  149. package/src/io.ts +73 -0
  150. package/src/package-identity.ts +7 -0
  151. package/src/paths.ts +49 -0
  152. package/src/persistence.ts +11 -0
  153. package/src/pi-runtime-adapter.ts +247 -0
  154. package/src/pi-runtime-runner.ts +436 -0
  155. package/src/registry.ts +321 -0
  156. package/src/retention.ts +178 -0
  157. package/src/runtime/agent-runner.ts +158 -0
  158. package/src/runtime/index.ts +2 -0
  159. package/src/runtime/namespace.ts +16 -0
  160. package/src/runtime/workflow-result.ts +8 -0
  161. package/src/session-handoff.ts +78 -0
  162. package/src/session-lease.ts +125 -0
  163. package/src/store.ts +832 -0
  164. package/src/subagent-manager-handle.ts +8 -0
  165. package/src/tool-timing.ts +47 -0
  166. package/src/trajectory-contracts.ts +13 -0
  167. package/src/trajectory-host-handle.ts +16 -0
  168. package/src/trajectory.ts +543 -0
  169. package/src/types.ts +374 -0
  170. package/src/utils.ts +192 -0
  171. package/src/validation.ts +910 -0
  172. package/src/workflow-artifacts.ts +35 -0
  173. package/starter/index.ts +35 -0
  174. package/starter/prompts/council.md +16 -0
  175. package/starter/prompts/deep-research.md +16 -0
  176. package/starter/prompts/oracle.md +14 -0
  177. package/starter/prompts/parallel-review.md +22 -0
  178. package/starter/prompts/parallel-scout.md +16 -0
  179. package/starter/prompts/review-loop.md +14 -0
  180. package/starter/prompts/review.md +14 -0
  181. package/starter/prompts/scout.md +14 -0
  182. package/starter/review-loop.ts +73 -0
  183. package/starter/roles/developer.md +6 -0
  184. package/starter/roles/oracle.md +18 -0
  185. package/starter/roles/researcher.md +20 -0
  186. package/starter/roles/reviewer.md +19 -0
  187. package/starter/roles/scout.md +18 -0
  188. package/subagents/README.md +145 -0
  189. package/subagents/index.ts +8 -0
  190. package/subagents/src/contracts.ts +176 -0
  191. package/subagents/src/decode.ts +158 -0
  192. package/subagents/src/index.ts +194 -0
  193. package/subagents/src/manager.ts +1560 -0
  194. package/subagents/src/navigator.ts +488 -0
  195. package/subagents/src/view.ts +376 -0
  196. package/subagents/src/worktree.ts +99 -0
  197. package/trajectory/index.ts +5 -0
  198. package/trajectory/src/assets/favicon.png +0 -0
  199. package/trajectory/src/assets/index.html +772 -0
  200. package/trajectory/src/assets/marked.min.js +6 -0
  201. package/trajectory/src/assets/morphdom.min.js +1 -0
  202. package/trajectory/src/export.ts +85 -0
  203. package/trajectory/src/index.ts +604 -0
  204. package/trajectory/src/server.ts +447 -0
@@ -0,0 +1,38 @@
1
+ import { type BudgetDimension, type BudgetEvent, type WorkflowBudget, type WorkflowBudgetPatch, type WorkflowBudgetUsage, type AgentAccounting, type RunState } from "./types.js";
2
+ export declare function validateBudget(value: unknown): WorkflowBudget | undefined;
3
+ export declare function validateBudgetPatch(value: unknown): WorkflowBudgetPatch;
4
+ export declare function budgetUsage(value?: Partial<WorkflowBudgetUsage>): WorkflowBudgetUsage;
5
+ export declare class WorkflowBudgetRuntime {
6
+ #private;
7
+ readonly budget: WorkflowBudget | undefined;
8
+ readonly version: number;
9
+ constructor(budget: WorkflowBudget | undefined, version?: number, usage?: Partial<WorkflowBudgetUsage>, events?: readonly BudgetEvent[], options?: {
10
+ now?: () => number;
11
+ onChange?: () => void;
12
+ active?: boolean;
13
+ });
14
+ get usage(): WorkflowBudgetUsage;
15
+ get events(): readonly BudgetEvent[];
16
+ get hardExhausted(): boolean;
17
+ checkAgentLaunch(): void;
18
+ beforeAttempt(): void;
19
+ beforeTurn(): void;
20
+ afterTurn(accounting: AgentAccounting, final: boolean): void;
21
+ instruction(agentId?: string): string | undefined;
22
+ forAgent(agentId: string): {
23
+ beforeAttempt: () => void;
24
+ beforeTurn: () => void;
25
+ afterTurn: (accounting: AgentAccounting, final: boolean) => void;
26
+ instruction: () => string | undefined;
27
+ };
28
+ transition(state: RunState): void;
29
+ recordEvent(event: BudgetEvent): void;
30
+ snapshot(): {
31
+ usage: WorkflowBudgetUsage;
32
+ budgetEvents: readonly BudgetEvent[];
33
+ };
34
+ }
35
+ export declare function mergeBudget(budget: WorkflowBudget | undefined, patch: WorkflowBudgetPatch): WorkflowBudget | undefined;
36
+ export declare function budgetRelaxed(previous: WorkflowBudget | undefined, next: WorkflowBudget | undefined): boolean;
37
+ export declare function exhaustedBudgetDimensions(budget: WorkflowBudget | undefined, usage: WorkflowBudgetUsage): BudgetDimension[];
38
+ export declare function resumeBudgetAllowed(budget: WorkflowBudget | undefined, usage: WorkflowBudgetUsage): boolean;
@@ -0,0 +1,188 @@
1
+ import { BUDGET_DIMENSIONS, WorkflowError } from "./types.js";
2
+ import { fail, object } from "./utils.js";
3
+ const BUDGET_LIMITS = ["soft", "hard"];
4
+ function isBudgetDimension(value) { return BUDGET_DIMENSIONS.some((dimension) => dimension === value); }
5
+ function isBudgetLimit(value) { return BUDGET_LIMITS.some((limit) => limit === value); }
6
+ function dimensionEntries(value) {
7
+ const entries = [];
8
+ for (const [dimension, raw] of Object.entries(value)) {
9
+ if (!isBudgetDimension(dimension))
10
+ fail("INVALID_METADATA", `Unknown budget dimension: ${dimension}`);
11
+ entries.push([dimension, raw]);
12
+ }
13
+ return entries;
14
+ }
15
+ function limitEntries(value, message, ownOnly = false) {
16
+ if (Object.keys(value).some((key) => !isBudgetLimit(key)))
17
+ fail("INVALID_METADATA", message);
18
+ const entries = [];
19
+ for (const key of BUDGET_LIMITS) {
20
+ if (!ownOnly || Object.prototype.hasOwnProperty.call(value, key))
21
+ entries.push([key, value[key]]);
22
+ }
23
+ return entries;
24
+ }
25
+ function budgetEntries(value) {
26
+ const entries = [];
27
+ for (const [dimension, limits] of Object.entries(value)) {
28
+ if (!isBudgetDimension(dimension))
29
+ continue;
30
+ entries.push([dimension, limits]);
31
+ }
32
+ return entries;
33
+ }
34
+ function nonNegativeInteger(value) { return typeof value === "number" && Number.isInteger(value) && value >= 0; }
35
+ function nonNegativeFinite(value) { return typeof value === "number" && Number.isFinite(value) && value >= 0; }
36
+ function validatedLimit(value, dimension, key) {
37
+ const kind = dimension === "costUsd" ? "finite number" : "integer";
38
+ if (dimension === "costUsd") {
39
+ if (!nonNegativeFinite(value))
40
+ fail("INVALID_METADATA", `${dimension}.${key} must be a non-negative ${kind}`);
41
+ return value;
42
+ }
43
+ if (!nonNegativeInteger(value))
44
+ fail("INVALID_METADATA", `${dimension}.${key} must be a non-negative ${kind}`);
45
+ return value;
46
+ }
47
+ export function validateBudget(value) {
48
+ if (value === undefined)
49
+ return undefined;
50
+ if (!object(value))
51
+ fail("INVALID_METADATA", "budget must be an object");
52
+ const result = {};
53
+ for (const [dimension, raw] of dimensionEntries(value)) {
54
+ if (!object(raw))
55
+ fail("INVALID_METADATA", `${dimension} budget must be an object`);
56
+ const limits = {};
57
+ for (const [key, rawLimit] of limitEntries(raw, `${dimension} budget has an unknown limit`)) {
58
+ if (rawLimit !== undefined)
59
+ limits[key] = validatedLimit(rawLimit, dimension, key);
60
+ }
61
+ if (limits.soft !== undefined && limits.hard !== undefined && limits.soft >= limits.hard)
62
+ fail("INVALID_METADATA", `${dimension}.soft must be less than hard`);
63
+ if (Object.keys(limits).length)
64
+ result[dimension] = limits;
65
+ }
66
+ return Object.freeze(result);
67
+ }
68
+ export function validateBudgetPatch(value) {
69
+ if (!object(value))
70
+ fail("INVALID_METADATA", "budget patch must be an object");
71
+ const result = {};
72
+ for (const [dimension, raw] of dimensionEntries(value)) {
73
+ if (raw === null) {
74
+ result[dimension] = null;
75
+ continue;
76
+ }
77
+ if (!object(raw))
78
+ fail("INVALID_METADATA", `${dimension} budget patch must contain only soft and hard`);
79
+ const limits = {};
80
+ for (const [key, rawLimit] of limitEntries(raw, `${dimension} budget patch must contain only soft and hard`, true)) {
81
+ if (rawLimit === null)
82
+ limits[key] = null;
83
+ else if (rawLimit !== undefined)
84
+ limits[key] = validatedLimit(rawLimit, dimension, key);
85
+ }
86
+ if (limits.soft !== null && limits.hard !== null && limits.soft !== undefined && limits.hard !== undefined && limits.soft >= limits.hard)
87
+ fail("INVALID_METADATA", `${dimension}.soft must be less than hard`);
88
+ result[dimension] = limits;
89
+ }
90
+ return result;
91
+ }
92
+ export function budgetUsage(value) { return { tokens: value?.tokens ?? 0, costUsd: value?.costUsd ?? 0, durationMs: value?.durationMs ?? 0, agentLaunches: value?.agentLaunches ?? 0 }; }
93
+ export class WorkflowBudgetRuntime {
94
+ budget;
95
+ version;
96
+ #now;
97
+ #onChange;
98
+ #injected = new Set();
99
+ #seen = new Set();
100
+ #active;
101
+ #activeSince;
102
+ #usage;
103
+ #events;
104
+ #turnAccounting;
105
+ constructor(budget, version = 1, usage, events = [], options = {}) {
106
+ this.budget = budget;
107
+ this.version = version;
108
+ this.#now = options.now ?? (() => Date.now());
109
+ this.#onChange = options.onChange;
110
+ this.#active = options.active ?? true;
111
+ this.#activeSince = this.#active ? this.#now() : undefined;
112
+ this.#usage = budgetUsage(usage);
113
+ this.#events = [...events];
114
+ for (const event of events)
115
+ if (event.budgetVersion === version)
116
+ this.#seen.add(event.type);
117
+ }
118
+ get usage() { this.#syncDuration(); return { ...this.#usage }; }
119
+ get events() { return this.#events; }
120
+ get hardExhausted() { return this.#events.some((event) => event.type === "hard_exhausted" && event.budgetVersion === this.version); }
121
+ checkAgentLaunch() { this.#checkHard(["agentLaunches"]); }
122
+ beforeAttempt() { this.#checkHard(["agentLaunches"]); this.#usage.agentLaunches += 1; this.#evaluate(); }
123
+ beforeTurn() { this.#syncDuration(); this.#evaluate(); this.#checkHard(["tokens", "costUsd", "durationMs"]); }
124
+ afterTurn(accounting, final) { this.#syncDuration(); this.#applyTurn(accounting, final, this.#turnAccounting); this.#turnAccounting = { input: accounting.input, output: accounting.output, cost: accounting.cost }; }
125
+ #applyTurn(accounting, final, previous = { input: 0, output: 0, cost: 0 }) { this.#usage.tokens += Math.max(0, accounting.input - previous.input) + Math.max(0, accounting.output - previous.output); this.#usage.costUsd += Math.max(0, accounting.cost - previous.cost); this.#evaluate(); if (!final)
126
+ this.#checkHard(["tokens", "costUsd", "durationMs"]); }
127
+ instruction(agentId = "agent") { if (!this.#hasSoftCrossed() || this.#injected.has(agentId))
128
+ return undefined; this.#injected.add(agentId); return `The workflow budget soft limit has been reached. Finish the requested output now, preserving any required output schema. Current usage: ${JSON.stringify(this.usage)}. Do not start additional model work unless it is required to produce the final requested result.`; }
129
+ forAgent(agentId) { let attempt = 0; let previous; return { beforeAttempt: () => { attempt += 1; previous = undefined; this.beforeAttempt(); }, beforeTurn: () => { this.beforeTurn(); }, afterTurn: (accounting, final) => { this.#applyTurn(accounting, final, previous); previous = { input: accounting.input, output: accounting.output, cost: accounting.cost }; }, instruction: () => this.instruction(`${agentId}:${String(attempt + 1)}`) }; }
130
+ transition(state) { const active = state === "running"; if (active === this.#active)
131
+ return; if (active) {
132
+ this.#active = true;
133
+ this.#activeSince = this.#now();
134
+ }
135
+ else {
136
+ this.#syncDuration();
137
+ this.#evaluate();
138
+ this.#active = false;
139
+ this.#activeSince = undefined;
140
+ } this.#onChange?.(); }
141
+ #syncDuration() { if (this.#active && this.#activeSince !== undefined) {
142
+ const now = this.#now();
143
+ this.#usage.durationMs += Math.max(0, now - this.#activeSince);
144
+ this.#activeSince = now;
145
+ } }
146
+ #hasSoftCrossed() { return !!this.budget && budgetEntries(this.budget).some(([dimension, limits]) => limits.soft !== undefined && this.#usage[dimension] >= limits.soft); }
147
+ #checkHard(dimensions) { const exhausted = dimensions.filter((dimension) => { const hard = this.budget?.[dimension]?.hard; return hard !== undefined && this.#usage[dimension] >= hard; }); if (!exhausted.length)
148
+ return; this.#record("hard_exhausted", exhausted); const detail = exhausted.map((dimension) => `${dimension} usage=${String(this.#usage[dimension])} hard=${String(this.budget?.[dimension]?.hard)}`).join(", "); throw new WorkflowError("BUDGET_EXHAUSTED", `Budget version ${String(this.version)} exhausted: ${detail}`); }
149
+ #evaluate() { const budget = this.budget; if (!budget)
150
+ return; const entries = budgetEntries(budget); const soft = entries.filter(([dimension, limits]) => limits.soft !== undefined && this.#usage[dimension] >= limits.soft).map(([dimension]) => dimension); if (soft.length)
151
+ this.#record("soft_crossed", soft); const overrun = entries.filter(([dimension, limits]) => limits.hard !== undefined && this.#usage[dimension] > limits.hard).map(([dimension]) => dimension); if (overrun.length)
152
+ this.#record("hard_overrun", overrun); }
153
+ #record(type, dimensions) { if (this.#seen.has(type))
154
+ return; this.#seen.add(type); this.#events.push({ type, budgetVersion: this.version, dimensions: [...dimensions], usage: this.usage, limits: structuredClone(this.budget ?? {}), at: this.#now() }); this.#onChange?.(); }
155
+ recordEvent(event) { this.#events.push(structuredClone(event)); }
156
+ snapshot() { return { usage: this.usage, budgetEvents: [...this.#events] }; }
157
+ }
158
+ export function mergeBudget(budget, patch) { const merged = structuredClone(budget ?? {}); for (const dimension of BUDGET_DIMENSIONS)
159
+ if (Object.prototype.hasOwnProperty.call(patch, dimension)) {
160
+ const value = patch[dimension];
161
+ if (value === null) {
162
+ Reflect.deleteProperty(merged, dimension);
163
+ continue;
164
+ }
165
+ const next = { ...(merged[dimension] ?? {}) };
166
+ for (const key of BUDGET_LIMITS)
167
+ if (value && Object.prototype.hasOwnProperty.call(value, key)) {
168
+ const limit = value[key];
169
+ if (limit === null)
170
+ Reflect.deleteProperty(next, key);
171
+ else if (limit !== undefined)
172
+ next[key] = limit;
173
+ }
174
+ if (Object.keys(next).length)
175
+ merged[dimension] = next;
176
+ else
177
+ Reflect.deleteProperty(merged, dimension);
178
+ } return validateBudget(merged); }
179
+ export function budgetRelaxed(previous, next) { for (const dimension of BUDGET_DIMENSIONS) {
180
+ const oldLimit = previous?.[dimension];
181
+ const newLimit = next?.[dimension];
182
+ for (const key of BUDGET_LIMITS)
183
+ if ((oldLimit?.[key] !== undefined && newLimit?.[key] === undefined) || (oldLimit?.[key] !== undefined && newLimit?.[key] !== undefined && newLimit[key] > oldLimit[key]))
184
+ return true;
185
+ } return false; }
186
+ export function exhaustedBudgetDimensions(budget, usage) { if (!budget)
187
+ return []; return budgetEntries(budget).filter(([dimension, limits]) => limits.hard !== undefined && usage[dimension] >= limits.hard).map(([dimension]) => dimension); }
188
+ export function resumeBudgetAllowed(budget, usage) { return exhaustedBudgetDimensions(budget, usage).length === 0; }
@@ -0,0 +1,6 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ type ChangelogContext = Pick<ExtensionContext, "hasUI" | "mode"> & {
3
+ ui: Pick<ExtensionContext["ui"], "notify">;
4
+ };
5
+ export declare function showChangelogNotice(context: ChangelogContext, agentDir: string, packageDirectory?: string): Promise<void>;
6
+ export {};
@@ -0,0 +1,112 @@
1
+ import { mkdir, readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { atomicJson, json } from "./io.js";
5
+ import { object } from "./utils.js";
6
+ import { isCorePackageName } from "./package-identity.js";
7
+ const STATE_DIRECTORY = "pi-extensible-workflows";
8
+ const STATE_FILE = "changelog-state.json";
9
+ const MAX_NOTICE_CHARACTERS = 6_000;
10
+ function packageMetadata(value, directory) {
11
+ if (!object(value) || !isCorePackageName(value.name) || typeof value.version !== "string" || !value.version.trim())
12
+ return undefined;
13
+ return { directory, version: value.version.trim() };
14
+ }
15
+ async function packageMetadataAt(directory) {
16
+ try {
17
+ const value = JSON.parse(await readFile(join(directory, "package.json"), "utf8"));
18
+ return packageMetadata(value, directory);
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ }
24
+ async function installedPackageMetadata() {
25
+ const moduleDirectory = dirname(fileURLToPath(import.meta.url));
26
+ for (const directory of [join(moduleDirectory, ".."), join(moduleDirectory, "../..")]) {
27
+ const metadata = await packageMetadataAt(directory);
28
+ if (metadata)
29
+ return metadata;
30
+ }
31
+ return undefined;
32
+ }
33
+ function parseChangelog(markdown) {
34
+ const entries = [];
35
+ let current;
36
+ for (const line of markdown.split(/\r?\n/)) {
37
+ const heading = /^##\s+(?:\[([^\]\r\n]+)\]|([^\s\r\n]+))(?:\s+-.*)?\s*$/.exec(line);
38
+ if (heading) {
39
+ const version = heading[1]?.trim() ?? heading[2]?.trim();
40
+ current = undefined;
41
+ if (version && version.toLowerCase() !== "unreleased") {
42
+ current = { version, body: "" };
43
+ entries.push(current);
44
+ }
45
+ continue;
46
+ }
47
+ if (current)
48
+ current.body = current.body ? `${current.body}\n${line}` : line;
49
+ }
50
+ return entries;
51
+ }
52
+ async function readChangelog(directory) {
53
+ try {
54
+ return parseChangelog(await readFile(join(directory, "CHANGELOG.md"), "utf8"));
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ }
60
+ function releaseEntries(entries, currentVersion, previousVersion) {
61
+ const currentIndex = entries.findIndex((entry) => entry.version === currentVersion);
62
+ const current = entries[currentIndex];
63
+ if (current === undefined || current.body.trim() === "")
64
+ return [];
65
+ if (previousVersion === currentVersion)
66
+ return [];
67
+ const previousIndex = previousVersion === undefined ? -1 : entries.findIndex((entry) => entry.version === previousVersion);
68
+ const selected = previousIndex > currentIndex ? entries.slice(currentIndex, previousIndex) : [current];
69
+ return selected.filter((entry) => entry.body.trim() !== "");
70
+ }
71
+ function noticeText(version, entries) {
72
+ const notes = entries.map((entry) => `## ${entry.version}\n${entry.body.trim()}`).join("\n\n");
73
+ const bounded = notes.length > MAX_NOTICE_CHARACTERS ? `${notes.slice(0, MAX_NOTICE_CHARACTERS).trimEnd()}\n...` : notes;
74
+ return `pi-extensible-workflows updated to ${version}\n\n${bounded}`;
75
+ }
76
+ async function lastNotifiedVersion(path) {
77
+ try {
78
+ const value = await json(path);
79
+ return object(value) && typeof value.lastNotifiedVersion === "string" && value.lastNotifiedVersion.trim() ? value.lastNotifiedVersion : undefined;
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ }
85
+ export async function showChangelogNotice(context, agentDir, packageDirectory) {
86
+ if (!context.hasUI || (context.mode !== "tui" && context.mode !== "rpc"))
87
+ return;
88
+ const { ui } = context;
89
+ try {
90
+ const metadata = packageDirectory === undefined ? await installedPackageMetadata() : await packageMetadataAt(packageDirectory);
91
+ if (!metadata)
92
+ return;
93
+ const statePath = join(agentDir, STATE_DIRECTORY, STATE_FILE);
94
+ const previousVersion = await lastNotifiedVersion(statePath);
95
+ if (previousVersion === metadata.version)
96
+ return;
97
+ const entries = releaseEntries(await readChangelog(metadata.directory), metadata.version, previousVersion);
98
+ if (!entries.length)
99
+ return;
100
+ ui.notify(noticeText(metadata.version, entries), "info");
101
+ try {
102
+ await mkdir(dirname(statePath), { recursive: true, mode: 0o700 });
103
+ await atomicJson(statePath, { lastNotifiedVersion: metadata.version });
104
+ }
105
+ catch {
106
+ // The notice is advisory; a failed state write may repeat it on the next start.
107
+ }
108
+ }
109
+ catch {
110
+ // Changelog data is optional and must never prevent Pi startup.
111
+ }
112
+ }
@@ -0,0 +1,96 @@
1
+ import { type AgentDefinition, type BudgetApprovalRequest, type JsonValue, type LaunchSnapshot, type RunRecord, type WorkflowBudgetUsage, type WorkflowExtensionSettings } from "./types.js";
2
+ import type { OwnershipRecord } from "./agent-execution.js";
3
+ export interface EffectiveSystemPrompt {
4
+ sessionId: string;
5
+ attempt: number;
6
+ turn: number;
7
+ sha256: string;
8
+ prompt: string;
9
+ }
10
+ export type PersistedRun = RunRecord;
11
+ export interface RunSummaryAgent {
12
+ id: string;
13
+ name: string;
14
+ label?: string;
15
+ state: string;
16
+ role?: string;
17
+ attempts: number;
18
+ }
19
+ export interface RunSummaryArtifacts {
20
+ runDirectory: string;
21
+ statePath: string;
22
+ journalPath: string;
23
+ snapshotPath: string;
24
+ workflowPath: string;
25
+ resultPath: string;
26
+ summaryPath: string;
27
+ }
28
+ export interface RunSummary {
29
+ schemaVersion: 1;
30
+ runId: string;
31
+ sessionId: string;
32
+ workflowName: string;
33
+ state: RunRecord["state"];
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ terminalAt?: string;
37
+ usage: WorkflowBudgetUsage;
38
+ agents: readonly RunSummaryAgent[];
39
+ error?: RunRecord["error"];
40
+ failedAt?: string;
41
+ replayablePaths: readonly string[];
42
+ incompletePaths: readonly string[];
43
+ artifacts: RunSummaryArtifacts;
44
+ }
45
+ export interface CompletedOperation {
46
+ path: string;
47
+ value: JsonValue;
48
+ }
49
+ export interface AwaitingCheckpoint {
50
+ path: string;
51
+ name: string;
52
+ prompt: string;
53
+ context: JsonValue;
54
+ }
55
+ export type PendingWorkflowDecision = BudgetApprovalRequest;
56
+ export type PersistedOwnershipNode = OwnershipRecord;
57
+ export type Journal = {
58
+ completed: Record<string, CompletedOperation>;
59
+ awaiting?: Record<string, AwaitingCheckpoint>;
60
+ decisions?: Record<string, PendingWorkflowDecision>;
61
+ };
62
+ export declare function decodeAgentDefinition(value: unknown): AgentDefinition | undefined;
63
+ export declare function decodeWorkflowExtensions(value: unknown): WorkflowExtensionSettings | undefined;
64
+ export declare function decodePersistedRun(value: unknown, allowLegacyAgentSessions?: boolean): PersistedRun | undefined;
65
+ export declare function isPersistedRun(value: unknown): value is PersistedRun;
66
+ export declare function decodeLaunchSnapshot(value: unknown): LaunchSnapshot | undefined;
67
+ export declare function decodeSessionOwner(value: unknown): SessionOwner | undefined;
68
+ export declare function decodeOwnershipRecords(value: unknown): OwnershipRecord[] | undefined;
69
+ export declare function decodeWorktreeReferences(value: unknown): WorktreeReference[] | undefined;
70
+ export declare function decodeBorrowedWorktreeBindings(value: unknown): BorrowedWorktreeBinding[] | undefined;
71
+ export declare function decodeJournal(value: unknown): Journal | undefined;
72
+ export declare function decodeBooleanCheckpointResult(value: unknown): boolean | undefined;
73
+ export declare function decodeSummaryProjection(value: unknown): Partial<RunSummary> | undefined;
74
+ export declare function decodeSystemPromptArtifact(value: unknown): {
75
+ version: number;
76
+ format?: unknown;
77
+ storage?: unknown;
78
+ entries?: EffectiveSystemPrompt[];
79
+ } | undefined;
80
+ export interface WorktreeReference {
81
+ owner: string;
82
+ path: string;
83
+ branch: string;
84
+ cwd: string;
85
+ base: string;
86
+ }
87
+ export interface BorrowedWorktreeBinding {
88
+ name: string;
89
+ sourceRunId: string;
90
+ owner: string;
91
+ }
92
+ export type SessionOwner = {
93
+ pid: number;
94
+ token: string;
95
+ startedAt: number;
96
+ };