@synergenius/flow-weaver-pack-weaver 0.9.59 → 0.9.77

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 (217) hide show
  1. package/dist/ai-chat-provider.d.ts +12 -0
  2. package/dist/ai-chat-provider.d.ts.map +1 -1
  3. package/dist/ai-chat-provider.js +351 -335
  4. package/dist/ai-chat-provider.js.map +1 -1
  5. package/dist/bot/agent-loop.d.ts +20 -0
  6. package/dist/bot/agent-loop.d.ts.map +1 -0
  7. package/dist/bot/agent-loop.js +331 -0
  8. package/dist/bot/agent-loop.js.map +1 -0
  9. package/dist/bot/ai-router.d.ts +19 -0
  10. package/dist/bot/ai-router.d.ts.map +1 -0
  11. package/dist/bot/ai-router.js +104 -0
  12. package/dist/bot/ai-router.js.map +1 -0
  13. package/dist/bot/assistant-tools.d.ts.map +1 -1
  14. package/dist/bot/assistant-tools.js +49 -33
  15. package/dist/bot/assistant-tools.js.map +1 -1
  16. package/dist/bot/async-mutex.d.ts +13 -0
  17. package/dist/bot/async-mutex.d.ts.map +1 -0
  18. package/dist/bot/async-mutex.js +37 -0
  19. package/dist/bot/async-mutex.js.map +1 -0
  20. package/dist/bot/bot-manager.d.ts +2 -2
  21. package/dist/bot/bot-manager.d.ts.map +1 -1
  22. package/dist/bot/bot-manager.js +3 -3
  23. package/dist/bot/bot-manager.js.map +1 -1
  24. package/dist/bot/bot-registry.js +2 -2
  25. package/dist/bot/bot-registry.js.map +1 -1
  26. package/dist/bot/conversation-store.d.ts +1 -0
  27. package/dist/bot/conversation-store.d.ts.map +1 -1
  28. package/dist/bot/conversation-store.js.map +1 -1
  29. package/dist/bot/dashboard.d.ts.map +1 -1
  30. package/dist/bot/dashboard.js +17 -8
  31. package/dist/bot/dashboard.js.map +1 -1
  32. package/dist/bot/improve-loop.js.map +1 -1
  33. package/dist/bot/index.d.ts +2 -4
  34. package/dist/bot/index.d.ts.map +1 -1
  35. package/dist/bot/index.js +1 -2
  36. package/dist/bot/index.js.map +1 -1
  37. package/dist/bot/instance-manager.d.ts +31 -0
  38. package/dist/bot/instance-manager.d.ts.map +1 -0
  39. package/dist/bot/instance-manager.js +115 -0
  40. package/dist/bot/instance-manager.js.map +1 -0
  41. package/dist/bot/orchestrator.d.ts +36 -0
  42. package/dist/bot/orchestrator.d.ts.map +1 -0
  43. package/dist/bot/orchestrator.js +176 -0
  44. package/dist/bot/orchestrator.js.map +1 -0
  45. package/dist/bot/profile-store.d.ts +36 -0
  46. package/dist/bot/profile-store.d.ts.map +1 -0
  47. package/dist/bot/profile-store.js +208 -0
  48. package/dist/bot/profile-store.js.map +1 -0
  49. package/dist/bot/profile-types.d.ts +126 -0
  50. package/dist/bot/profile-types.d.ts.map +1 -0
  51. package/dist/bot/profile-types.js +7 -0
  52. package/dist/bot/profile-types.js.map +1 -0
  53. package/dist/bot/run-store.d.ts.map +1 -1
  54. package/dist/bot/run-store.js +8 -0
  55. package/dist/bot/run-store.js.map +1 -1
  56. package/dist/bot/runner.d.ts +4 -0
  57. package/dist/bot/runner.d.ts.map +1 -1
  58. package/dist/bot/runner.js +5 -1
  59. package/dist/bot/runner.js.map +1 -1
  60. package/dist/bot/swarm-controller.d.ts +109 -0
  61. package/dist/bot/swarm-controller.d.ts.map +1 -0
  62. package/dist/bot/swarm-controller.js +640 -0
  63. package/dist/bot/swarm-controller.js.map +1 -0
  64. package/dist/bot/swarm-event-log.d.ts +28 -0
  65. package/dist/bot/swarm-event-log.d.ts.map +1 -0
  66. package/dist/bot/swarm-event-log.js +54 -0
  67. package/dist/bot/swarm-event-log.js.map +1 -0
  68. package/dist/bot/task-prompt-builder.d.ts +22 -0
  69. package/dist/bot/task-prompt-builder.d.ts.map +1 -0
  70. package/dist/bot/task-prompt-builder.js +240 -0
  71. package/dist/bot/task-prompt-builder.js.map +1 -0
  72. package/dist/bot/task-store.d.ts +21 -0
  73. package/dist/bot/task-store.d.ts.map +1 -0
  74. package/dist/bot/task-store.js +364 -0
  75. package/dist/bot/task-store.js.map +1 -0
  76. package/dist/bot/task-types.d.ts +79 -0
  77. package/dist/bot/task-types.d.ts.map +1 -0
  78. package/dist/bot/task-types.js +6 -0
  79. package/dist/bot/task-types.js.map +1 -0
  80. package/dist/bot/types.d.ts +8 -0
  81. package/dist/bot/types.d.ts.map +1 -1
  82. package/dist/cli-handlers.d.ts.map +1 -1
  83. package/dist/cli-handlers.js +79 -54
  84. package/dist/cli-handlers.js.map +1 -1
  85. package/dist/cli.d.ts +3 -0
  86. package/dist/cli.d.ts.map +1 -0
  87. package/dist/cli.js +749 -0
  88. package/dist/cli.js.map +1 -0
  89. package/dist/docs/docs/weaver-bot-usage.md +35 -18
  90. package/dist/docs/docs/weaver-config.md +20 -0
  91. package/dist/docs/docs/weaver-task-queue.md +31 -19
  92. package/dist/docs/weaver-config.md +15 -9
  93. package/dist/index.d.ts +2 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +1 -1
  96. package/dist/index.js.map +1 -1
  97. package/dist/mcp-tools.d.ts +17 -0
  98. package/dist/mcp-tools.d.ts.map +1 -1
  99. package/dist/mcp-tools.js +98 -279
  100. package/dist/mcp-tools.js.map +1 -1
  101. package/dist/node-types/bot-report.d.ts.map +1 -1
  102. package/dist/node-types/bot-report.js +6 -24
  103. package/dist/node-types/bot-report.js.map +1 -1
  104. package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
  105. package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
  106. package/dist/node-types/orchestrator-dispatch.js +63 -0
  107. package/dist/node-types/orchestrator-dispatch.js.map +1 -0
  108. package/dist/node-types/orchestrator-load-state.d.ts +16 -0
  109. package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
  110. package/dist/node-types/orchestrator-load-state.js +60 -0
  111. package/dist/node-types/orchestrator-load-state.js.map +1 -0
  112. package/dist/node-types/orchestrator-route.d.ts +16 -0
  113. package/dist/node-types/orchestrator-route.d.ts.map +1 -0
  114. package/dist/node-types/orchestrator-route.js +28 -0
  115. package/dist/node-types/orchestrator-route.js.map +1 -0
  116. package/dist/node-types/receive-task.d.ts +2 -3
  117. package/dist/node-types/receive-task.d.ts.map +1 -1
  118. package/dist/node-types/receive-task.js +3 -48
  119. package/dist/node-types/receive-task.js.map +1 -1
  120. package/dist/templates/weaver-template.d.ts +11 -0
  121. package/dist/templates/weaver-template.d.ts.map +1 -0
  122. package/dist/templates/weaver-template.js +53 -0
  123. package/dist/templates/weaver-template.js.map +1 -0
  124. package/dist/ui/bot-activity.js +2 -2
  125. package/dist/ui/bot-constants.d.ts +14 -0
  126. package/dist/ui/bot-constants.d.ts.map +1 -0
  127. package/dist/ui/bot-constants.js +189 -0
  128. package/dist/ui/bot-constants.js.map +1 -0
  129. package/dist/ui/bot-panel.js +207 -245
  130. package/dist/ui/bot-slot-card.js +141 -0
  131. package/dist/ui/budget-bar.js +59 -0
  132. package/dist/ui/chat-task-result.js +178 -0
  133. package/dist/ui/decision-log.js +136 -0
  134. package/dist/ui/profile-card.js +158 -0
  135. package/dist/ui/profile-editor.js +597 -0
  136. package/dist/ui/swarm-controls.js +245 -0
  137. package/dist/ui/swarm-dashboard.js +3012 -0
  138. package/dist/ui/task-create-form.js +98 -0
  139. package/dist/ui/task-detail-view.js +1044 -0
  140. package/dist/ui/task-pool-list.js +156 -0
  141. package/dist/workflows/orchestrator.d.ts +21 -0
  142. package/dist/workflows/orchestrator.d.ts.map +1 -0
  143. package/dist/workflows/orchestrator.js +281 -0
  144. package/dist/workflows/orchestrator.js.map +1 -0
  145. package/dist/workflows/weaver-bot-session.d.ts +65 -0
  146. package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
  147. package/dist/workflows/weaver-bot-session.js +68 -0
  148. package/dist/workflows/weaver-bot-session.js.map +1 -0
  149. package/dist/workflows/weaver.d.ts +24 -0
  150. package/dist/workflows/weaver.d.ts.map +1 -0
  151. package/dist/workflows/weaver.js +28 -0
  152. package/dist/workflows/weaver.js.map +1 -0
  153. package/flowweaver.manifest.json +547 -133
  154. package/package.json +1 -1
  155. package/src/ai-chat-provider.ts +378 -371
  156. package/src/bot/ai-router.ts +132 -0
  157. package/src/bot/assistant-tools.ts +47 -29
  158. package/src/bot/async-mutex.ts +37 -0
  159. package/src/bot/bot-manager.ts +3 -3
  160. package/src/bot/bot-registry.ts +2 -2
  161. package/src/bot/conversation-store.ts +2 -1
  162. package/src/bot/dashboard.ts +17 -8
  163. package/src/bot/improve-loop.ts +6 -6
  164. package/src/bot/index.ts +2 -4
  165. package/src/bot/instance-manager.ts +128 -0
  166. package/src/bot/orchestrator.ts +244 -0
  167. package/src/bot/profile-store.ts +225 -0
  168. package/src/bot/profile-types.ts +141 -0
  169. package/src/bot/run-store.ts +8 -0
  170. package/src/bot/runner.ts +9 -1
  171. package/src/bot/swarm-controller.ts +780 -0
  172. package/src/bot/swarm-event-log.ts +57 -0
  173. package/src/bot/task-prompt-builder.ts +309 -0
  174. package/src/bot/task-store.ts +407 -0
  175. package/src/bot/task-types.ts +100 -0
  176. package/src/bot/types.ts +8 -0
  177. package/src/cli-handlers.ts +78 -53
  178. package/src/docs/weaver-bot-usage.md +35 -18
  179. package/src/docs/weaver-config.md +20 -0
  180. package/src/docs/weaver-task-queue.md +31 -19
  181. package/src/index.ts +5 -4
  182. package/src/mcp-tools.ts +129 -372
  183. package/src/node-types/bot-report.ts +6 -24
  184. package/src/node-types/orchestrator-dispatch.ts +71 -0
  185. package/src/node-types/orchestrator-load-state.ts +66 -0
  186. package/src/node-types/orchestrator-route.ts +33 -0
  187. package/src/node-types/receive-task.ts +3 -57
  188. package/src/ui/bot-activity.tsx +2 -2
  189. package/src/ui/bot-constants.ts +192 -0
  190. package/src/ui/bot-panel.tsx +213 -247
  191. package/src/ui/bot-slot-card.tsx +139 -0
  192. package/src/ui/budget-bar.tsx +30 -0
  193. package/src/ui/chat-task-result.tsx +236 -0
  194. package/src/ui/decision-log.tsx +148 -0
  195. package/src/ui/profile-card.tsx +157 -0
  196. package/src/ui/profile-editor.tsx +384 -0
  197. package/src/ui/swarm-controls.tsx +260 -0
  198. package/src/ui/swarm-dashboard.tsx +647 -0
  199. package/src/ui/task-create-form.tsx +87 -0
  200. package/src/ui/task-detail-view.tsx +841 -0
  201. package/src/ui/task-pool-list.tsx +187 -0
  202. package/src/workflows/orchestrator.ts +302 -0
  203. package/dist/docs/weaver-bot-usage.md +0 -34
  204. package/dist/docs/weaver-genesis.md +0 -32
  205. package/dist/docs/weaver-task-queue.md +0 -34
  206. package/dist/ui/bot-workspace.js +0 -1015
  207. package/dist/ui/chat-bot-result.js +0 -71
  208. package/dist/ui/queue-input.js +0 -82
  209. package/dist/ui/session-bar.js +0 -174
  210. package/src/bot/error-guide.ts +0 -4
  211. package/src/bot/retry-utils.ts +0 -4
  212. package/src/bot/session-state.ts +0 -116
  213. package/src/bot/task-queue.ts +0 -262
  214. package/src/ui/bot-workspace.tsx +0 -442
  215. package/src/ui/chat-bot-result.tsx +0 -81
  216. package/src/ui/queue-input.tsx +0 -56
  217. package/src/ui/session-bar.tsx +0 -157
@@ -0,0 +1,57 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+
4
+ export interface SwarmEvent {
5
+ id: number;
6
+ type: string;
7
+ timestamp: number;
8
+ data?: Record<string, unknown>;
9
+ }
10
+
11
+ /**
12
+ * Append-only event log for swarm-level events.
13
+ *
14
+ * Writes SwarmEvent objects to `.weaver/swarm-events.ndjson`.
15
+ * The platform tails this file via SSE to stream live dashboard updates.
16
+ * Decoupled from execution: the swarm controller writes, the UI reads, independently.
17
+ */
18
+ export class SwarmEventLog {
19
+ private readonly filePath: string;
20
+ private nextId = 0;
21
+
22
+ constructor(projectDir: string) {
23
+ const dir = path.join(projectDir, '.weaver');
24
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
25
+ this.filePath = path.join(dir, 'swarm-events.ndjson');
26
+ }
27
+
28
+ /** Append a swarm event with auto-assigned sequential ID. */
29
+ emit(event: Omit<SwarmEvent, 'id'>): void {
30
+ const full: SwarmEvent = { ...event, id: this.nextId++ };
31
+ fs.appendFileSync(this.filePath, JSON.stringify(full) + '\n', 'utf-8');
32
+ }
33
+
34
+ /**
35
+ * Read events starting from offset (0-based event ID).
36
+ * For repeated polling, pass the last received id + 1 as offset.
37
+ */
38
+ tail(offset: number): SwarmEvent[] {
39
+ if (!fs.existsSync(this.filePath)) return [];
40
+ const events: SwarmEvent[] = [];
41
+ const lines = fs.readFileSync(this.filePath, 'utf-8').split('\n');
42
+ for (const line of lines) {
43
+ if (!line.trim()) continue;
44
+ try {
45
+ const event = JSON.parse(line) as SwarmEvent;
46
+ if (event.id >= offset) events.push(event);
47
+ } catch { /* skip corrupt line */ }
48
+ }
49
+ return events;
50
+ }
51
+
52
+ /** Remove the event log file and reset the ID counter. */
53
+ clear(): void {
54
+ try { fs.unlinkSync(this.filePath); } catch { /* ok */ }
55
+ this.nextId = 0;
56
+ }
57
+ }
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Build AI prompt from task context with sliding window and 4,000 token cap.
3
+ *
4
+ * Prompt structure:
5
+ * ## Task: {title}
6
+ * {description}
7
+ * ### Notes
8
+ * ### Relevant Files
9
+ * ### Previous Attempts (last 3 full, older condensed)
10
+ * ### Last Error
11
+ * ### Parent Context (title + description + sibling status)
12
+ *
13
+ * Truncation cascade when over budget:
14
+ * 1. Older summaries → one-liners only
15
+ * 2. File list → only files from last 2 runs
16
+ * 3. Parent context → title only
17
+ * 4. Notes → first 500 chars
18
+ */
19
+
20
+ import type { Task, RunSummary } from './task-types.js';
21
+ import type { ProfilePreferences } from './profile-types.js';
22
+
23
+ const MAX_CONTEXT_TOKENS = 4000;
24
+ const CHARS_PER_TOKEN = 4; // rough estimate
25
+ const MAX_CONTEXT_CHARS = MAX_CONTEXT_TOKENS * CHARS_PER_TOKEN;
26
+ const FULL_SUMMARY_COUNT = 3;
27
+
28
+ export function buildTaskPrompt(
29
+ task: Task,
30
+ parentTask?: Task | null,
31
+ siblingTasks?: Task[],
32
+ preferences?: ProfilePreferences,
33
+ ): string {
34
+ // Build the full (un-truncated) prompt first
35
+ const prompt = buildFull(task, parentTask, siblingTasks, preferences);
36
+
37
+ if (prompt.length <= MAX_CONTEXT_CHARS) {
38
+ return prompt;
39
+ }
40
+
41
+ // Over budget — apply truncation cascade progressively
42
+ return truncatePrompt(task, parentTask, siblingTasks, preferences);
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Full prompt builder (no truncation)
47
+ // ---------------------------------------------------------------------------
48
+
49
+ function buildFull(
50
+ task: Task,
51
+ parentTask?: Task | null,
52
+ siblingTasks?: Task[],
53
+ preferences?: ProfilePreferences,
54
+ ): string {
55
+ const sections: string[] = [];
56
+
57
+ // Always: title + description
58
+ sections.push(`## Task: ${task.title}\n\n${task.description}`);
59
+
60
+ // Notes
61
+ if (task.context.notes) {
62
+ sections.push(`### Notes\n${task.context.notes}`);
63
+ }
64
+
65
+ // Files
66
+ if (task.context.files.length > 0) {
67
+ sections.push(`### Relevant Files\n${task.context.files.join('\n')}`);
68
+ }
69
+
70
+ // Run summaries: last 3 in full, older condensed
71
+ const summaries = task.context.runSummaries;
72
+ if (summaries.length > 0) {
73
+ const lines: string[] = [];
74
+ const recentStart = Math.max(0, summaries.length - FULL_SUMMARY_COUNT);
75
+
76
+ // Condensed older summaries
77
+ for (let i = 0; i < recentStart; i++) {
78
+ lines.push(condenseSummary(summaries[i]!));
79
+ }
80
+
81
+ // Full recent summaries
82
+ for (let i = recentStart; i < summaries.length; i++) {
83
+ lines.push(fullSummary(summaries[i]!));
84
+ }
85
+
86
+ sections.push(`### Previous Attempts\n${lines.join('\n')}`);
87
+ }
88
+
89
+ // Last error
90
+ if (task.context.lastError) {
91
+ sections.push(`### Last Error\n${task.context.lastError}`);
92
+ }
93
+
94
+ // Execution preferences
95
+ if (preferences) {
96
+ sections.push(buildPreferencesSection(preferences));
97
+ }
98
+
99
+ // Parent context
100
+ if (parentTask) {
101
+ sections.push(buildParentSection(parentTask, siblingTasks));
102
+ }
103
+
104
+ return sections.join('\n\n');
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Truncation cascade
109
+ // ---------------------------------------------------------------------------
110
+
111
+ function truncatePrompt(
112
+ task: Task,
113
+ parentTask?: Task | null,
114
+ siblingTasks?: Task[],
115
+ preferences?: ProfilePreferences,
116
+ ): string {
117
+ // Level 1: All summaries condensed to one-liners
118
+ let result = buildWithTruncation(task, parentTask, siblingTasks, {
119
+ allSummariesCondensed: true,
120
+ filesFromRecentRunsOnly: false,
121
+ parentTitleOnly: false,
122
+ notesCapped: false,
123
+ }, preferences);
124
+ if (result.length <= MAX_CONTEXT_CHARS) return result;
125
+
126
+ // Level 2: File list → only files from last 2 runs
127
+ result = buildWithTruncation(task, parentTask, siblingTasks, {
128
+ allSummariesCondensed: true,
129
+ filesFromRecentRunsOnly: true,
130
+ parentTitleOnly: false,
131
+ notesCapped: false,
132
+ }, preferences);
133
+ if (result.length <= MAX_CONTEXT_CHARS) return result;
134
+
135
+ // Level 3: Parent context → title only
136
+ result = buildWithTruncation(task, parentTask, siblingTasks, {
137
+ allSummariesCondensed: true,
138
+ filesFromRecentRunsOnly: true,
139
+ parentTitleOnly: true,
140
+ notesCapped: false,
141
+ }, preferences);
142
+ if (result.length <= MAX_CONTEXT_CHARS) return result;
143
+
144
+ // Level 4: Notes → first 500 chars
145
+ result = buildWithTruncation(task, parentTask, siblingTasks, {
146
+ allSummariesCondensed: true,
147
+ filesFromRecentRunsOnly: true,
148
+ parentTitleOnly: true,
149
+ notesCapped: true,
150
+ }, preferences);
151
+
152
+ // Hard-truncate if still over (shouldn't happen with reasonable data)
153
+ if (result.length > MAX_CONTEXT_CHARS) {
154
+ return result.slice(0, MAX_CONTEXT_CHARS);
155
+ }
156
+
157
+ return result;
158
+ }
159
+
160
+ interface TruncationFlags {
161
+ allSummariesCondensed: boolean;
162
+ filesFromRecentRunsOnly: boolean;
163
+ parentTitleOnly: boolean;
164
+ notesCapped: boolean;
165
+ }
166
+
167
+ function buildWithTruncation(
168
+ task: Task,
169
+ parentTask: Task | null | undefined,
170
+ siblingTasks: Task[] | undefined,
171
+ flags: TruncationFlags,
172
+ preferences?: ProfilePreferences,
173
+ ): string {
174
+ const sections: string[] = [];
175
+
176
+ // Always: title + description
177
+ sections.push(`## Task: ${task.title}\n\n${task.description}`);
178
+
179
+ // Notes (potentially capped)
180
+ if (task.context.notes) {
181
+ const notes =
182
+ flags.notesCapped && task.context.notes.length > 500
183
+ ? task.context.notes.slice(0, 500) + '...'
184
+ : task.context.notes;
185
+ sections.push(`### Notes\n${notes}`);
186
+ }
187
+
188
+ // Files (potentially only from recent runs)
189
+ if (flags.filesFromRecentRunsOnly) {
190
+ const recentFiles = new Set<string>();
191
+ for (const s of task.context.runSummaries.slice(-2)) {
192
+ for (const f of s.filesModified) recentFiles.add(f);
193
+ }
194
+ if (recentFiles.size > 0) {
195
+ sections.push(`### Relevant Files\n${[...recentFiles].join('\n')}`);
196
+ }
197
+ } else if (task.context.files.length > 0) {
198
+ sections.push(`### Relevant Files\n${task.context.files.join('\n')}`);
199
+ }
200
+
201
+ // All summaries condensed
202
+ if (task.context.runSummaries.length > 0) {
203
+ if (flags.allSummariesCondensed) {
204
+ const lines = task.context.runSummaries.map((s) => condenseSummary(s));
205
+ sections.push(`### Previous Attempts\n${lines.join('\n')}`);
206
+ } else {
207
+ // This path isn't used in truncation, but included for completeness
208
+ const lines: string[] = [];
209
+ const recentStart = Math.max(0, task.context.runSummaries.length - FULL_SUMMARY_COUNT);
210
+ for (let i = 0; i < recentStart; i++) {
211
+ lines.push(condenseSummary(task.context.runSummaries[i]!));
212
+ }
213
+ for (let i = recentStart; i < task.context.runSummaries.length; i++) {
214
+ lines.push(fullSummary(task.context.runSummaries[i]!));
215
+ }
216
+ sections.push(`### Previous Attempts\n${lines.join('\n')}`);
217
+ }
218
+ }
219
+
220
+ // Last error (truncated in aggressive mode)
221
+ if (task.context.lastError) {
222
+ const errorText = flags.notesCapped
223
+ ? task.context.lastError.slice(0, 300)
224
+ : task.context.lastError;
225
+ sections.push(`### Last Error\n${errorText}`);
226
+ }
227
+
228
+ // Execution preferences
229
+ if (preferences) {
230
+ sections.push(buildPreferencesSection(preferences));
231
+ }
232
+
233
+ // Parent context
234
+ if (parentTask) {
235
+ if (flags.parentTitleOnly) {
236
+ sections.push(`### Parent Context\nPart of: "${parentTask.title}"`);
237
+ } else {
238
+ sections.push(buildParentSection(parentTask, siblingTasks));
239
+ }
240
+ }
241
+
242
+ return sections.join('\n\n');
243
+ }
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // Formatting helpers
247
+ // ---------------------------------------------------------------------------
248
+
249
+ function condenseSummary(s: RunSummary): string {
250
+ return `- Run ${s.runId.slice(0, 8)} (${s.botId}): ${s.outcome} — ${s.summary.slice(0, 80)} (${Math.round(s.durationMs / 1000)}s, ${s.tokensUsed} tok)`;
251
+ }
252
+
253
+ function fullSummary(s: RunSummary): string {
254
+ const lines: string[] = [];
255
+ lines.push(`\n**Run ${s.runId.slice(0, 8)} (${s.botId}) — ${s.outcome}**`);
256
+ lines.push(s.summary);
257
+ if (s.filesModified.length > 0) lines.push(`Files: ${s.filesModified.join(', ')}`);
258
+ if (s.error) lines.push(`Error: ${s.error}`);
259
+ lines.push(
260
+ `Duration: ${Math.round(s.durationMs / 1000)}s · Tokens: ${s.tokensUsed} · Cost: $${s.cost.toFixed(3)}`,
261
+ );
262
+ return lines.join('\n');
263
+ }
264
+
265
+ function buildParentSection(
266
+ parentTask: Task,
267
+ siblingTasks?: Task[],
268
+ ): string {
269
+ let section = `### Parent Context\nPart of: "${parentTask.title}" — ${parentTask.description}`;
270
+ if (siblingTasks && siblingTasks.length > 0) {
271
+ const siblingLines = siblingTasks
272
+ .map(
273
+ (s) =>
274
+ `- ${statusIcon(s.status)} ${s.title} (${s.status})`,
275
+ )
276
+ .join('\n');
277
+ section += `\n\nSibling tasks:\n${siblingLines}`;
278
+ }
279
+ return section;
280
+ }
281
+
282
+ function buildPreferencesSection(preferences: ProfilePreferences): string {
283
+ const lines = ['## Execution Preferences'];
284
+ lines.push(`Cost strategy: ${preferences.costStrategy}`);
285
+ if (preferences.maxCostPerRun !== undefined) {
286
+ lines.push(`Max cost per run: $${preferences.maxCostPerRun.toFixed(2)}`);
287
+ }
288
+ if (preferences.maxCostPerTask !== undefined) {
289
+ lines.push(`Max cost per task: $${preferences.maxCostPerTask.toFixed(2)}`);
290
+ }
291
+ if (preferences.instructions) {
292
+ lines.push(`Instructions: ${preferences.instructions}`);
293
+ }
294
+ lines.push(`Approval required: ${preferences.requireApproval ? 'yes' : 'no'}`);
295
+ return lines.join('\n');
296
+ }
297
+
298
+ function statusIcon(status: string): string {
299
+ switch (status) {
300
+ case 'done':
301
+ return '\u2705';
302
+ case 'failed':
303
+ return '\u274C';
304
+ case 'in-progress':
305
+ return '\u25CF';
306
+ default:
307
+ return '\u25CB';
308
+ }
309
+ }