@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,70 @@
1
+ import type { AgentToolResult, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
2
+ import type { SubagentRunRequest, SubagentStatus } from "./contracts.js";
3
+ type SubagentRenderArgs = Partial<SubagentRunRequest> & {
4
+ id?: string;
5
+ };
6
+ type ProgressComponent = ReturnType<typeof subagentProgressBlock>;
7
+ export type SubagentRenderState = {
8
+ subagentSpinner?: ReturnType<typeof setInterval>;
9
+ subagentStatus?: SubagentStatus;
10
+ subagentProgressComponent?: ProgressComponent;
11
+ subagentProgressFrozenAt?: number;
12
+ };
13
+ export declare function formatSubagentPreview(args: Partial<SubagentRunRequest>): string;
14
+ export declare function renderSubagentCall(args: Partial<SubagentRunRequest>, context?: {
15
+ state?: SubagentRenderState;
16
+ isError?: boolean;
17
+ }): {
18
+ render(width: number): string[];
19
+ invalidate(): void;
20
+ };
21
+ export declare function subagentProgressBlock(status: SubagentStatus, args: SubagentRenderArgs, theme: Theme, freezeAt?: number): {
22
+ id: string;
23
+ update(next: SubagentStatus, nextTheme: Theme, nextFreezeAt?: number): void;
24
+ setExpanded(value: boolean): void;
25
+ render(width: number): string[];
26
+ invalidate(): void;
27
+ };
28
+ export declare function renderSubagentResult(result: AgentToolResult<unknown>, options: {
29
+ isPartial: boolean;
30
+ expanded: boolean;
31
+ }, theme: Theme, context: {
32
+ args: SubagentRenderArgs;
33
+ state: SubagentRenderState;
34
+ invalidate(): void;
35
+ isError?: boolean;
36
+ }): {
37
+ render(width: number): string[];
38
+ invalidate(): void;
39
+ };
40
+ export declare function createSubagentBackgroundWidget(): {
41
+ start(next: ExtensionContext): void;
42
+ update(status: Readonly<SubagentStatus>, request: Readonly<SubagentRunRequest>): void;
43
+ dispose(): void;
44
+ };
45
+ export declare function renderSubagentInspectCall(args: {
46
+ id?: string;
47
+ }, theme: Theme): {
48
+ render(width: number): string[];
49
+ invalidate(): void;
50
+ };
51
+ export declare function renderSubagentInspectResult(result: AgentToolResult<unknown>, options: {
52
+ expanded: boolean;
53
+ }, theme: Theme, args: {
54
+ id?: string;
55
+ }): {
56
+ render(width: number): string[];
57
+ invalidate(): void;
58
+ };
59
+ export declare function renderSubagentControlCall(name: string, args: {
60
+ id: string;
61
+ message?: string;
62
+ }, theme: Theme): {
63
+ render(width: number): string[];
64
+ invalidate(): void;
65
+ };
66
+ export declare function renderSubagentControlResult(result: AgentToolResult<unknown>, theme: Theme): {
67
+ render(width: number): string[];
68
+ invalidate(): void;
69
+ };
70
+ export {};
@@ -0,0 +1,387 @@
1
+ import { truncateToWidth } from "@earendil-works/pi-tui";
2
+ import { formatCost, formatStalledDuration, sanitizeDisplayText, WORKFLOW_AGENT_STALL_THRESHOLD_MS } from "../../src/index.js";
3
+ const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+ const TERMINAL_STATES = new Set(["completed", "failed", "stopped"]);
5
+ const MAX_INSPECT_VALUE_CHARS = 4000;
6
+ const MAX_INSPECT_TOOL_CALLS = 32;
7
+ const MAX_BACKGROUND_WIDGET_ROWS = 10;
8
+ function textBlock(text) {
9
+ return {
10
+ render(width) { return text.split("\n").map((line) => truncateToWidth(line, Math.max(1, width), "…")); },
11
+ invalidate() { },
12
+ };
13
+ }
14
+ function roleName(value) {
15
+ if (typeof value === "string")
16
+ return value.trim() || undefined;
17
+ if (typeof value !== "object" || value === null || Array.isArray(value))
18
+ return undefined;
19
+ const name = value.name;
20
+ return typeof name === "string" ? name.trim() || undefined : undefined;
21
+ }
22
+ function modeName(value) {
23
+ return value === "background" || value === "foreground" ? value : undefined;
24
+ }
25
+ function requestMetadata(args, metadataAvailable) {
26
+ const mode = modeName(args.mode) ?? (metadataAvailable ? "background" : undefined);
27
+ const role = metadataAvailable ? roleName(args.role) ?? "none" : undefined;
28
+ return [mode === undefined ? undefined : `mode=${mode}`, role === undefined ? undefined : `role=${role}`].filter((value) => value !== undefined).join(" ");
29
+ }
30
+ function label(args) {
31
+ if (typeof args.label === "string" && args.label.trim())
32
+ return args.label.trim();
33
+ const role = roleName(args.role);
34
+ if (role)
35
+ return role;
36
+ if (typeof args.id === "string" && args.id)
37
+ return args.id.slice(0, 8);
38
+ return "subagent";
39
+ }
40
+ export function formatSubagentPreview(args) {
41
+ return `subagent ${label(args)} ${requestMetadata(args, true)}`;
42
+ }
43
+ export function renderSubagentCall(args, context) {
44
+ const preview = formatSubagentPreview(args);
45
+ return {
46
+ render(width) {
47
+ if (context?.state?.subagentStatus && !context.isError)
48
+ return [];
49
+ return [truncateToWidth(preview, Math.max(1, width), "…")];
50
+ },
51
+ invalidate() { },
52
+ };
53
+ }
54
+ function statusValue(value) {
55
+ if (typeof value !== "object" || value === null || Array.isArray(value))
56
+ return undefined;
57
+ const record = value;
58
+ if (typeof record.id !== "string" || (record.state !== "running" && record.state !== "completed" && record.state !== "failed" && record.state !== "stopped"))
59
+ return undefined;
60
+ return value;
61
+ }
62
+ function runtime(startedAt, finishedAt, now) {
63
+ if (startedAt === undefined)
64
+ return "";
65
+ const seconds = Math.max(0, Math.floor(((finishedAt ?? now) - startedAt) / 1000));
66
+ if (seconds < 60)
67
+ return `${String(seconds)}s`;
68
+ const minutes = Math.floor(seconds / 60);
69
+ const remainingSeconds = seconds % 60;
70
+ if (minutes < 60)
71
+ return `${String(minutes)}m${remainingSeconds ? ` ${String(remainingSeconds)}s` : ""}`;
72
+ const hours = Math.floor(minutes / 60);
73
+ const remainingMinutes = minutes % 60;
74
+ return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
75
+ }
76
+ function stateGlyph(state, spinner) {
77
+ if (state === "running")
78
+ return spinner;
79
+ return state === "completed" ? "✓" : "✗";
80
+ }
81
+ function stateColor(state) {
82
+ if (state === "running")
83
+ return "accent";
84
+ return state === "completed" ? "success" : "error";
85
+ }
86
+ function activity(status) {
87
+ if (status.state !== "running")
88
+ return undefined;
89
+ const current = status.progress?.activity;
90
+ if (current?.kind === "reasoning" || current?.kind === "text") {
91
+ const name = current.kind === "reasoning" ? "reasoning" : "responding";
92
+ const text = sanitizeDisplayText(current.text);
93
+ return text && text !== name ? `${name} · ${text}` : name;
94
+ }
95
+ if (current?.kind === "tool")
96
+ return sanitizeDisplayText(current.text);
97
+ return sanitizeDisplayText([...(status.progress?.toolCalls ?? [])].reverse().find(({ state }) => state === "running")?.name ?? "");
98
+ }
99
+ function stalledDuration(status, now) {
100
+ const lastEventAt = status.progress?.lastEventAt;
101
+ if (status.state !== "running" || lastEventAt === undefined || !Number.isFinite(lastEventAt))
102
+ return undefined;
103
+ const duration = now - lastEventAt;
104
+ return duration >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? duration : undefined;
105
+ }
106
+ function accounting(status) {
107
+ const value = status.progress?.accounting;
108
+ if (!value)
109
+ return undefined;
110
+ const total = value.input + value.output + value.cacheRead + value.cacheWrite;
111
+ return `tokens=${String(total)} cost=${formatCost(value.cost) || "$0.00"}`;
112
+ }
113
+ function timestamp(value) {
114
+ if (value === undefined || !Number.isFinite(value) || value < 0)
115
+ return undefined;
116
+ const date = new Date(value);
117
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
118
+ }
119
+ function boundedValue(value) {
120
+ let text;
121
+ try {
122
+ const serialized = JSON.stringify(value, null, 2);
123
+ text = typeof serialized === "string" ? serialized : String(value);
124
+ }
125
+ catch {
126
+ text = String(value);
127
+ }
128
+ return text.length > MAX_INSPECT_VALUE_CHARS ? `${text.slice(0, MAX_INSPECT_VALUE_CHARS)}…` : text;
129
+ }
130
+ function formatSubagentProgress(status, args, theme, spinner, now, expanded) {
131
+ const color = stateColor(status.state);
132
+ const elapsed = runtime(status.startedAt, status.finishedAt, now);
133
+ const metadata = requestMetadata(args, args.id === undefined);
134
+ const lines = [
135
+ `${theme.fg(color, stateGlyph(status.state, spinner))} ${theme.bold(theme.fg("accent", `Subagent: ${label({ ...args, id: status.id })}`))} ${theme.fg(color, `[${status.state}]`)}${metadata ? ` ${theme.fg("dim", metadata)}` : ""}${elapsed ? ` runtime=${elapsed}` : ""}`,
136
+ ];
137
+ const current = activity(status);
138
+ const stalled = stalledDuration(status, now);
139
+ if (current)
140
+ lines.push(` ${theme.fg("accent", spinner)} ${theme.fg("dim", current)}${stalled === undefined ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
141
+ else if (stalled !== undefined)
142
+ lines.push(` ${theme.fg("warning", `stalled? ${formatStalledDuration(stalled)}`)}`);
143
+ if (status.error)
144
+ lines.push(` ${theme.fg("error", `${status.error.code}: ${status.error.message}`)}`);
145
+ if (expanded) {
146
+ lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
147
+ const model = status.progress?.state?.model;
148
+ if (model)
149
+ lines.push(` ${theme.fg("dim", `model=${model.provider}/${model.model}${model.thinking ? `:${model.thinking}` : ""}`)}`);
150
+ const usage = accounting(status);
151
+ if (usage)
152
+ lines.push(` ${theme.fg("dim", usage)}`);
153
+ if (status.worktree)
154
+ lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
155
+ }
156
+ return lines.join("\n");
157
+ }
158
+ function formatSubagentLaunch(status, args, theme, expanded) {
159
+ const metadata = requestMetadata(args, args.id === undefined);
160
+ const lines = [
161
+ `${theme.fg("success", "✓")} ${theme.bold(theme.fg("accent", `Subagent: ${label({ ...args, id: status.id })}`))} ${theme.fg("success", "[launched]")}${metadata ? ` ${theme.fg("dim", metadata)}` : ""}`,
162
+ ];
163
+ if (expanded)
164
+ lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
165
+ return lines.join("\n");
166
+ }
167
+ function formatSubagentInspection(status, args, details, theme, expanded) {
168
+ const lines = [formatSubagentProgress(status, { id: args.id ?? status.id }, theme, "◇", Date.now(), false)];
169
+ if (!expanded)
170
+ return lines[0] ?? "";
171
+ lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
172
+ const startedAt = timestamp(status.startedAt);
173
+ const finishedAt = timestamp(status.finishedAt);
174
+ const lastEventAt = timestamp(status.progress?.lastEventAt);
175
+ if (startedAt)
176
+ lines.push(` ${theme.fg("dim", `startedAt=${startedAt}`)}`);
177
+ if (finishedAt)
178
+ lines.push(` ${theme.fg("dim", `finishedAt=${finishedAt}`)}`);
179
+ if (lastEventAt)
180
+ lines.push(` ${theme.fg("dim", `lastEventAt=${lastEventAt}`)}`);
181
+ const model = status.progress?.state?.model;
182
+ if (model)
183
+ lines.push(` ${theme.fg("dim", `model=${model.provider}/${model.model}${model.thinking ? `:${model.thinking}` : ""}`)}`);
184
+ const currentActivity = status.progress?.activity;
185
+ if (currentActivity)
186
+ lines.push(` ${theme.fg("dim", `activity=${currentActivity.kind}${currentActivity.text ? ` ${sanitizeDisplayText(currentActivity.text)}` : ""}`)}`);
187
+ const accounting = status.progress?.accounting;
188
+ if (accounting)
189
+ lines.push(` ${theme.fg("dim", `accounting=input=${String(accounting.input)} output=${String(accounting.output)} cacheRead=${String(accounting.cacheRead)} cacheWrite=${String(accounting.cacheWrite)} cost=${formatCost(accounting.cost) || "$0.00"}`)}`);
190
+ if (status.worktree)
191
+ lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
192
+ const toolCalls = status.progress?.toolCalls;
193
+ if (toolCalls?.length) {
194
+ lines.push(` ${theme.fg("dim", `toolCalls=${String(toolCalls.length)}`)}`);
195
+ for (const call of toolCalls.slice(-MAX_INSPECT_TOOL_CALLS))
196
+ lines.push(` ${theme.fg("dim", `${call.name} [${call.state}]`)}`);
197
+ }
198
+ const record = typeof details === "object" && details !== null && !Array.isArray(details) ? details : undefined;
199
+ if (record && Object.prototype.hasOwnProperty.call(record, "value")) {
200
+ lines.push(` ${theme.fg("dim", "value:")}`);
201
+ for (const line of boundedValue(record.value).split("\n"))
202
+ lines.push(` ${line}`);
203
+ }
204
+ return lines.join("\n");
205
+ }
206
+ export function subagentProgressBlock(status, args, theme, freezeAt) {
207
+ let current = status;
208
+ let currentTheme = theme;
209
+ let expanded = false;
210
+ let frozenAt = freezeAt ?? Date.now();
211
+ return {
212
+ id: status.id,
213
+ update(next, nextTheme, nextFreezeAt) {
214
+ current = next;
215
+ currentTheme = nextTheme;
216
+ if (nextFreezeAt !== undefined)
217
+ frozenAt = nextFreezeAt;
218
+ },
219
+ setExpanded(value) { expanded = value; },
220
+ render(width) {
221
+ const now = TERMINAL_STATES.has(current.state) ? frozenAt : Date.now();
222
+ if (!TERMINAL_STATES.has(current.state))
223
+ frozenAt = now;
224
+ const frame = SPINNER[Math.floor(now / 80) % SPINNER.length] ?? "◇";
225
+ return formatSubagentProgress(current, args, currentTheme, frame, now, expanded).split("\n").map((line) => truncateToWidth(line, Math.max(1, width), "…"));
226
+ },
227
+ invalidate() { },
228
+ };
229
+ }
230
+ function resultText(result) {
231
+ return result.content.filter(({ type }) => type === "text").map((content) => content.type === "text" ? content.text : "").join("\n");
232
+ }
233
+ export function renderSubagentResult(result, options, theme, context) {
234
+ const incoming = statusValue(result.details);
235
+ const state = context.state;
236
+ if (incoming)
237
+ state.subagentStatus = state.subagentStatus?.id === incoming.id ? { ...state.subagentStatus, ...incoming } : incoming;
238
+ const status = state.subagentStatus;
239
+ if (status?.state === "running" && options.isPartial && !state.subagentSpinner) {
240
+ state.subagentSpinner = setInterval(() => { context.invalidate(); }, 80);
241
+ state.subagentSpinner.unref();
242
+ }
243
+ else if ((!options.isPartial || status?.state !== "running") && state.subagentSpinner) {
244
+ clearInterval(state.subagentSpinner);
245
+ delete state.subagentSpinner;
246
+ }
247
+ if (!status || context.isError)
248
+ return textBlock(resultText(result));
249
+ if (!options.isPartial && status.state === "running")
250
+ return textBlock(formatSubagentLaunch(status, context.args, theme, options.expanded));
251
+ if (TERMINAL_STATES.has(status.state))
252
+ state.subagentProgressFrozenAt ??= Date.now();
253
+ else
254
+ delete state.subagentProgressFrozenAt;
255
+ let component = state.subagentProgressComponent;
256
+ if (!component || component.id !== status.id) {
257
+ component = subagentProgressBlock(status, context.args, theme, state.subagentProgressFrozenAt);
258
+ state.subagentProgressComponent = component;
259
+ }
260
+ else {
261
+ component.update(status, theme, state.subagentProgressFrozenAt);
262
+ }
263
+ component.setExpanded(options.expanded);
264
+ return component;
265
+ }
266
+ export function createSubagentBackgroundWidget() {
267
+ const key = "piewf-subagents-background";
268
+ const runs = new Map();
269
+ let context;
270
+ let requestRender;
271
+ let timer;
272
+ let showing = false;
273
+ const hide = () => {
274
+ if (timer)
275
+ clearInterval(timer);
276
+ timer = undefined;
277
+ requestRender = undefined;
278
+ if (showing) {
279
+ try {
280
+ context?.ui.setWidget(key, undefined);
281
+ }
282
+ catch { /* The session UI may already be closing. */ }
283
+ }
284
+ showing = false;
285
+ };
286
+ const paint = () => {
287
+ if (context?.mode !== "tui" || runs.size === 0) {
288
+ hide();
289
+ return;
290
+ }
291
+ if (showing) {
292
+ requestRender?.();
293
+ return;
294
+ }
295
+ try {
296
+ context.ui.setWidget(key, (tui, theme) => {
297
+ requestRender = () => { tui.requestRender(); };
298
+ return {
299
+ render(width) {
300
+ const now = Date.now();
301
+ const frame = SPINNER[Math.floor(now / 80) % SPINNER.length] ?? "◇";
302
+ const title = theme.bold(theme.fg("accent", `Subagents (${String(runs.size)} running)`));
303
+ const blocks = [...runs.values()].map(({ status, request }) => formatSubagentProgress(status, request, theme, frame, now, false).split("\n"));
304
+ const allRunsFit = blocks.reduce((rows, block) => rows + block.length, 1) <= MAX_BACKGROUND_WIDGET_ROWS;
305
+ const runRowBudget = allRunsFit ? MAX_BACKGROUND_WIDGET_ROWS - 1 : MAX_BACKGROUND_WIDGET_ROWS - 2;
306
+ const lines = [title];
307
+ let runRows = 0;
308
+ let displayedRuns = 0;
309
+ for (const block of blocks) {
310
+ if (runRows + block.length > runRowBudget)
311
+ break;
312
+ lines.push(...block);
313
+ runRows += block.length;
314
+ displayedRuns += 1;
315
+ }
316
+ const hiddenRuns = blocks.length - displayedRuns;
317
+ if (hiddenRuns > 0)
318
+ lines.push(`… ${String(hiddenRuns)} more`);
319
+ return lines.map((line) => truncateToWidth(line, Math.max(1, width), "…"));
320
+ },
321
+ invalidate() { },
322
+ };
323
+ }, { placement: "belowEditor" });
324
+ showing = true;
325
+ timer = setInterval(() => { requestRender?.(); }, 80);
326
+ timer.unref();
327
+ }
328
+ catch {
329
+ hide();
330
+ }
331
+ };
332
+ return {
333
+ start(next) {
334
+ hide();
335
+ runs.clear();
336
+ context = next.mode === "tui" ? next : undefined;
337
+ },
338
+ update(status, request) {
339
+ if (request.mode !== "background" || context === undefined)
340
+ return;
341
+ if (TERMINAL_STATES.has(status.state))
342
+ runs.delete(status.id);
343
+ else
344
+ runs.set(status.id, { status, request });
345
+ paint();
346
+ },
347
+ dispose() {
348
+ hide();
349
+ runs.clear();
350
+ context = undefined;
351
+ },
352
+ };
353
+ }
354
+ export function renderSubagentInspectCall(args, theme) {
355
+ const title = theme.fg("toolTitle", theme.bold("subagents_inspect"));
356
+ return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", "all")}`);
357
+ }
358
+ export function renderSubagentInspectResult(result, options, theme, args) {
359
+ const status = statusValue(result.details);
360
+ if (status)
361
+ return textBlock(formatSubagentInspection(status, args, result.details, theme, options.expanded));
362
+ if (Array.isArray(result.details)) {
363
+ const statuses = result.details.map(statusValue).filter((value) => value !== undefined);
364
+ const lines = [theme.bold(theme.fg("accent", `Subagents (${String(statuses.length)})`)), ...statuses.map((entry) => {
365
+ const color = stateColor(entry.state);
366
+ return ` ${theme.fg(color, stateGlyph(entry.state, "◇"))} ${entry.id.slice(0, 8)} ${theme.fg(color, `[${entry.state}]`)}`;
367
+ })];
368
+ return textBlock(lines.join("\n"));
369
+ }
370
+ return textBlock(resultText(result));
371
+ }
372
+ export function renderSubagentControlCall(name, args, theme) {
373
+ const title = theme.fg("toolTitle", theme.bold(name));
374
+ const message = args.message ? ` ${theme.fg("dim", args.message)}` : "";
375
+ return textBlock(`${title} ${theme.fg("accent", args.id)}${message}`);
376
+ }
377
+ export function renderSubagentControlResult(result, theme) {
378
+ const status = statusValue(result.details);
379
+ if (status) {
380
+ const color = stateColor(status.state);
381
+ return textBlock(`${theme.fg(color, stateGlyph(status.state, "◇"))} ${status.id.slice(0, 8)} ${theme.fg(color, `[${status.state}]`)}`);
382
+ }
383
+ const record = typeof result.details === "object" && result.details !== null && !Array.isArray(result.details) ? result.details : undefined;
384
+ if (record && typeof record.id === "string")
385
+ return textBlock(`${theme.fg("success", "✓")} ${record.id.slice(0, 8)}`);
386
+ return textBlock(resultText(result));
387
+ }
@@ -0,0 +1,34 @@
1
+ import { type WorktreeReference } from "../../src/index.js";
2
+ export interface SubagentWorktreeContext {
3
+ readonly cwd: string;
4
+ readonly sessionId: string;
5
+ readonly runId: string;
6
+ readonly name: string;
7
+ readonly owner: string;
8
+ readonly worktreePostCreateCommand?: readonly string[];
9
+ readonly worktreePostCreateCommandSource?: string;
10
+ }
11
+ export interface SubagentWorktreeRunStore {
12
+ recordSystemPrompt(entry: {
13
+ sessionId: string;
14
+ attempt: number;
15
+ turn: number;
16
+ prompt: string;
17
+ }): Promise<void>;
18
+ validateWorktree(owner: string, cwd?: string): Promise<WorktreeReference>;
19
+ worktree(owner: string): Promise<WorktreeReference>;
20
+ snapshotWorktree(owner: string): Promise<string>;
21
+ }
22
+ export interface SubagentWorktreeHandle {
23
+ readonly path: string;
24
+ readonly branch: string;
25
+ readonly cwd: string;
26
+ readonly runStore: SubagentWorktreeRunStore;
27
+ cleanup(): Promise<void>;
28
+ }
29
+ export interface SubagentWorktreeAdapter {
30
+ create(context: Readonly<SubagentWorktreeContext>): Promise<SubagentWorktreeHandle>;
31
+ cleanup?(context: Readonly<SubagentWorktreeContext>): Promise<void>;
32
+ }
33
+ export declare function createRunStoreWorktreeAdapter(home: string): SubagentWorktreeAdapter;
34
+ export declare function defaultWorktreeHome(storageDirectory: string): string;
@@ -0,0 +1,68 @@
1
+ import { join } from "node:path";
2
+ import { LAUNCH_SNAPSHOT_IDENTITY_VERSION, RunStore } from "../../src/index.js";
3
+ function syntheticRun(context) {
4
+ return {
5
+ id: context.runId,
6
+ workflowName: "subagents",
7
+ cwd: context.cwd,
8
+ sessionId: context.sessionId,
9
+ state: "running",
10
+ agentSessions: [],
11
+ agents: [],
12
+ };
13
+ }
14
+ function syntheticSnapshot(context) {
15
+ return {
16
+ identityVersion: LAUNCH_SNAPSHOT_IDENTITY_VERSION,
17
+ launchMode: "background",
18
+ script: "return null;",
19
+ args: null,
20
+ metadata: { name: "subagents" },
21
+ settings: { concurrency: 1, ...(context.worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand: context.worktreePostCreateCommand }) },
22
+ ...(context.worktreePostCreateCommandSource === undefined ? {} : { settingsSources: { concurrency: "subagents", modelAliases: "subagents", worktreePostCreateCommand: context.worktreePostCreateCommandSource } }),
23
+ models: [],
24
+ tools: [],
25
+ agentTypes: [],
26
+ schemas: [],
27
+ };
28
+ }
29
+ export function createRunStoreWorktreeAdapter(home) {
30
+ let createQueue = Promise.resolve();
31
+ return {
32
+ create(context) {
33
+ const operation = createQueue.then(async () => {
34
+ const store = new RunStore(context.cwd, context.sessionId, context.runId, home);
35
+ await store.create(syntheticRun(context), syntheticSnapshot(context));
36
+ try {
37
+ const reference = await store.worktree(context.owner);
38
+ let cleaned = false;
39
+ return {
40
+ path: reference.path,
41
+ branch: reference.branch,
42
+ cwd: reference.cwd,
43
+ runStore: store,
44
+ async cleanup() {
45
+ if (cleaned)
46
+ return;
47
+ cleaned = true;
48
+ await store.delete(true);
49
+ },
50
+ };
51
+ }
52
+ catch (error) {
53
+ await store.delete(true).catch(() => undefined);
54
+ throw error;
55
+ }
56
+ });
57
+ // Git worktree metadata is shared by all RunStore instances for this adapter.
58
+ createQueue = operation.then(() => undefined, () => undefined);
59
+ return operation;
60
+ },
61
+ async cleanup(context) {
62
+ await new RunStore(context.cwd, context.sessionId, context.runId, home).delete(true);
63
+ },
64
+ };
65
+ }
66
+ export function defaultWorktreeHome(storageDirectory) {
67
+ return join(storageDirectory, ".worktrees");
68
+ }