@synergenius/flow-weaver-pack-weaver 0.8.3 → 0.9.3

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 (265) hide show
  1. package/dist/bot/ai-client.d.ts +22 -2
  2. package/dist/bot/ai-client.d.ts.map +1 -1
  3. package/dist/bot/ai-client.js +168 -20
  4. package/dist/bot/ai-client.js.map +1 -1
  5. package/dist/bot/assistant-core.d.ts +25 -0
  6. package/dist/bot/assistant-core.d.ts.map +1 -0
  7. package/dist/bot/assistant-core.js +265 -0
  8. package/dist/bot/assistant-core.js.map +1 -0
  9. package/dist/bot/assistant-tools.d.ts +9 -0
  10. package/dist/bot/assistant-tools.d.ts.map +1 -0
  11. package/dist/bot/assistant-tools.js +602 -0
  12. package/dist/bot/assistant-tools.js.map +1 -0
  13. package/dist/bot/audit-logger.d.ts.map +1 -1
  14. package/dist/bot/audit-logger.js +9 -5
  15. package/dist/bot/audit-logger.js.map +1 -1
  16. package/dist/bot/audit-store.d.ts.map +1 -1
  17. package/dist/bot/audit-store.js +3 -11
  18. package/dist/bot/audit-store.js.map +1 -1
  19. package/dist/bot/bot-manager.d.ts +49 -0
  20. package/dist/bot/bot-manager.d.ts.map +1 -0
  21. package/dist/bot/bot-manager.js +279 -0
  22. package/dist/bot/bot-manager.js.map +1 -0
  23. package/dist/bot/child-process-tracker.d.ts +6 -0
  24. package/dist/bot/child-process-tracker.d.ts.map +1 -0
  25. package/dist/bot/child-process-tracker.js +35 -0
  26. package/dist/bot/child-process-tracker.js.map +1 -0
  27. package/dist/bot/cli-provider.d.ts.map +1 -1
  28. package/dist/bot/cli-provider.js +13 -8
  29. package/dist/bot/cli-provider.js.map +1 -1
  30. package/dist/bot/conversation-store.d.ts +40 -0
  31. package/dist/bot/conversation-store.d.ts.map +1 -0
  32. package/dist/bot/conversation-store.js +182 -0
  33. package/dist/bot/conversation-store.js.map +1 -0
  34. package/dist/bot/cost-store.d.ts.map +1 -1
  35. package/dist/bot/cost-store.js +10 -14
  36. package/dist/bot/cost-store.js.map +1 -1
  37. package/dist/bot/error-guide.d.ts +10 -0
  38. package/dist/bot/error-guide.d.ts.map +1 -0
  39. package/dist/bot/error-guide.js +34 -0
  40. package/dist/bot/error-guide.js.map +1 -0
  41. package/dist/bot/genesis-store.d.ts.map +1 -1
  42. package/dist/bot/genesis-store.js +11 -20
  43. package/dist/bot/genesis-store.js.map +1 -1
  44. package/dist/bot/index.d.ts +3 -0
  45. package/dist/bot/index.d.ts.map +1 -1
  46. package/dist/bot/index.js +3 -0
  47. package/dist/bot/index.js.map +1 -1
  48. package/dist/bot/knowledge-store.d.ts +17 -0
  49. package/dist/bot/knowledge-store.d.ts.map +1 -0
  50. package/dist/bot/knowledge-store.js +53 -0
  51. package/dist/bot/knowledge-store.js.map +1 -0
  52. package/dist/bot/pipeline-runner.d.ts.map +1 -1
  53. package/dist/bot/pipeline-runner.js +8 -1
  54. package/dist/bot/pipeline-runner.js.map +1 -1
  55. package/dist/bot/retry-utils.d.ts +19 -0
  56. package/dist/bot/retry-utils.d.ts.map +1 -0
  57. package/dist/bot/retry-utils.js +64 -0
  58. package/dist/bot/retry-utils.js.map +1 -0
  59. package/dist/bot/run-store.d.ts.map +1 -1
  60. package/dist/bot/run-store.js +2 -10
  61. package/dist/bot/run-store.js.map +1 -1
  62. package/dist/bot/runner.d.ts.map +1 -1
  63. package/dist/bot/runner.js +24 -3
  64. package/dist/bot/runner.js.map +1 -1
  65. package/dist/bot/safe-json.d.ts +32 -0
  66. package/dist/bot/safe-json.d.ts.map +1 -0
  67. package/dist/bot/safe-json.js +56 -0
  68. package/dist/bot/safe-json.js.map +1 -0
  69. package/dist/bot/safe-path.d.ts +18 -0
  70. package/dist/bot/safe-path.d.ts.map +1 -0
  71. package/dist/bot/safe-path.js +40 -0
  72. package/dist/bot/safe-path.js.map +1 -0
  73. package/dist/bot/session-state.d.ts.map +1 -1
  74. package/dist/bot/session-state.js +3 -1
  75. package/dist/bot/session-state.js.map +1 -1
  76. package/dist/bot/steering.js +1 -1
  77. package/dist/bot/steering.js.map +1 -1
  78. package/dist/bot/step-executor.d.ts +10 -5
  79. package/dist/bot/step-executor.d.ts.map +1 -1
  80. package/dist/bot/step-executor.js +252 -3
  81. package/dist/bot/step-executor.js.map +1 -1
  82. package/dist/bot/system-prompt.d.ts +1 -1
  83. package/dist/bot/system-prompt.d.ts.map +1 -1
  84. package/dist/bot/system-prompt.js +69 -43
  85. package/dist/bot/system-prompt.js.map +1 -1
  86. package/dist/bot/task-decomposer.d.ts +24 -0
  87. package/dist/bot/task-decomposer.d.ts.map +1 -0
  88. package/dist/bot/task-decomposer.js +75 -0
  89. package/dist/bot/task-decomposer.js.map +1 -0
  90. package/dist/bot/task-queue.d.ts +17 -4
  91. package/dist/bot/task-queue.d.ts.map +1 -1
  92. package/dist/bot/task-queue.js +102 -14
  93. package/dist/bot/task-queue.js.map +1 -1
  94. package/dist/bot/terminal-renderer.d.ts +60 -0
  95. package/dist/bot/terminal-renderer.d.ts.map +1 -0
  96. package/dist/bot/terminal-renderer.js +205 -0
  97. package/dist/bot/terminal-renderer.js.map +1 -0
  98. package/dist/bot/types.d.ts +7 -0
  99. package/dist/bot/types.d.ts.map +1 -1
  100. package/dist/bot/weaver-tools.d.ts +18 -0
  101. package/dist/bot/weaver-tools.d.ts.map +1 -0
  102. package/dist/bot/weaver-tools.js +215 -0
  103. package/dist/bot/weaver-tools.js.map +1 -0
  104. package/dist/cli-bridge.d.ts.map +1 -1
  105. package/dist/cli-bridge.js +10 -3
  106. package/dist/cli-bridge.js.map +1 -1
  107. package/dist/cli-handlers.d.ts +15 -1
  108. package/dist/cli-handlers.d.ts.map +1 -1
  109. package/dist/cli-handlers.js +742 -28
  110. package/dist/cli-handlers.js.map +1 -1
  111. package/dist/handlers/on-bot-completed.d.ts +21 -0
  112. package/dist/handlers/on-bot-completed.d.ts.map +1 -0
  113. package/dist/handlers/on-bot-completed.js +28 -0
  114. package/dist/handlers/on-bot-completed.js.map +1 -0
  115. package/dist/handlers/on-execution-failure.d.ts +23 -0
  116. package/dist/handlers/on-execution-failure.d.ts.map +1 -0
  117. package/dist/handlers/on-execution-failure.js +28 -0
  118. package/dist/handlers/on-execution-failure.js.map +1 -0
  119. package/dist/handlers/scheduled-run.d.ts +24 -0
  120. package/dist/handlers/scheduled-run.d.ts.map +1 -0
  121. package/dist/handlers/scheduled-run.js +25 -0
  122. package/dist/handlers/scheduled-run.js.map +1 -0
  123. package/dist/index.d.ts +3 -0
  124. package/dist/index.d.ts.map +1 -1
  125. package/dist/index.js +4 -0
  126. package/dist/index.js.map +1 -1
  127. package/dist/mcp-tools.js +2 -2
  128. package/dist/mcp-tools.js.map +1 -1
  129. package/dist/node-types/abort-task.d.ts.map +1 -1
  130. package/dist/node-types/abort-task.js +4 -3
  131. package/dist/node-types/abort-task.js.map +1 -1
  132. package/dist/node-types/agent-execute.d.ts +38 -0
  133. package/dist/node-types/agent-execute.d.ts.map +1 -0
  134. package/dist/node-types/agent-execute.js +256 -0
  135. package/dist/node-types/agent-execute.js.map +1 -0
  136. package/dist/node-types/bot-report.d.ts +5 -3
  137. package/dist/node-types/bot-report.d.ts.map +1 -1
  138. package/dist/node-types/bot-report.js +39 -7
  139. package/dist/node-types/bot-report.js.map +1 -1
  140. package/dist/node-types/build-context.d.ts +3 -3
  141. package/dist/node-types/build-context.d.ts.map +1 -1
  142. package/dist/node-types/build-context.js +108 -24
  143. package/dist/node-types/build-context.js.map +1 -1
  144. package/dist/node-types/detect-provider.d.ts +2 -2
  145. package/dist/node-types/detect-provider.d.ts.map +1 -1
  146. package/dist/node-types/detect-provider.js +3 -1
  147. package/dist/node-types/detect-provider.js.map +1 -1
  148. package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
  149. package/dist/node-types/exec-validate-retry.js +43 -6
  150. package/dist/node-types/exec-validate-retry.js.map +1 -1
  151. package/dist/node-types/execute-plan.d.ts.map +1 -1
  152. package/dist/node-types/execute-plan.js +31 -8
  153. package/dist/node-types/execute-plan.js.map +1 -1
  154. package/dist/node-types/execute-target.d.ts.map +1 -1
  155. package/dist/node-types/execute-target.js +3 -1
  156. package/dist/node-types/execute-target.js.map +1 -1
  157. package/dist/node-types/fix-errors.d.ts.map +1 -1
  158. package/dist/node-types/fix-errors.js +21 -5
  159. package/dist/node-types/fix-errors.js.map +1 -1
  160. package/dist/node-types/genesis-observe.d.ts.map +1 -1
  161. package/dist/node-types/genesis-observe.js +3 -1
  162. package/dist/node-types/genesis-observe.js.map +1 -1
  163. package/dist/node-types/genesis-report.js +4 -1
  164. package/dist/node-types/genesis-report.js.map +1 -1
  165. package/dist/node-types/git-ops.d.ts.map +1 -1
  166. package/dist/node-types/git-ops.js +98 -4
  167. package/dist/node-types/git-ops.js.map +1 -1
  168. package/dist/node-types/index.d.ts +2 -0
  169. package/dist/node-types/index.d.ts.map +1 -1
  170. package/dist/node-types/index.js +2 -0
  171. package/dist/node-types/index.js.map +1 -1
  172. package/dist/node-types/load-config.d.ts +2 -2
  173. package/dist/node-types/load-config.d.ts.map +1 -1
  174. package/dist/node-types/load-config.js.map +1 -1
  175. package/dist/node-types/plan-task.d.ts.map +1 -1
  176. package/dist/node-types/plan-task.js +14 -2
  177. package/dist/node-types/plan-task.js.map +1 -1
  178. package/dist/node-types/read-workflow.js +8 -2
  179. package/dist/node-types/read-workflow.js.map +1 -1
  180. package/dist/node-types/receive-task.d.ts.map +1 -1
  181. package/dist/node-types/receive-task.js +35 -26
  182. package/dist/node-types/receive-task.js.map +1 -1
  183. package/dist/node-types/send-notify.js +2 -1
  184. package/dist/node-types/send-notify.js.map +1 -1
  185. package/dist/node-types/validate-gate.d.ts +18 -0
  186. package/dist/node-types/validate-gate.d.ts.map +1 -0
  187. package/dist/node-types/validate-gate.js +96 -0
  188. package/dist/node-types/validate-gate.js.map +1 -0
  189. package/dist/workflows/genesis-task.d.ts +20 -12
  190. package/dist/workflows/genesis-task.d.ts.map +1 -1
  191. package/dist/workflows/genesis-task.js +20 -12
  192. package/dist/workflows/genesis-task.js.map +1 -1
  193. package/dist/workflows/weaver-agent.d.ts +35 -0
  194. package/dist/workflows/weaver-agent.d.ts.map +1 -0
  195. package/dist/workflows/weaver-agent.js +777 -0
  196. package/dist/workflows/weaver-agent.js.map +1 -0
  197. package/dist/workflows/weaver-bot-batch.d.ts +19 -26
  198. package/dist/workflows/weaver-bot-batch.d.ts.map +1 -1
  199. package/dist/workflows/weaver-bot-batch.js +1043 -27
  200. package/dist/workflows/weaver-bot-batch.js.map +1 -1
  201. package/dist/workflows/weaver-bot.d.ts +21 -35
  202. package/dist/workflows/weaver-bot.d.ts.map +1 -1
  203. package/dist/workflows/weaver-bot.js +1119 -36
  204. package/dist/workflows/weaver-bot.js.map +1 -1
  205. package/flowweaver.manifest.json +113 -2
  206. package/package.json +5 -2
  207. package/src/bot/ai-client.ts +180 -19
  208. package/src/bot/assistant-core.ts +306 -0
  209. package/src/bot/assistant-tools.ts +605 -0
  210. package/src/bot/audit-logger.ts +6 -5
  211. package/src/bot/audit-store.ts +3 -12
  212. package/src/bot/bot-manager.ts +293 -0
  213. package/src/bot/child-process-tracker.ts +40 -0
  214. package/src/bot/cli-provider.ts +13 -8
  215. package/src/bot/conversation-store.ts +222 -0
  216. package/src/bot/cost-store.ts +11 -12
  217. package/src/bot/error-guide.ts +34 -0
  218. package/src/bot/genesis-store.ts +11 -17
  219. package/src/bot/index.ts +5 -0
  220. package/src/bot/knowledge-store.ts +59 -0
  221. package/src/bot/pipeline-runner.ts +7 -1
  222. package/src/bot/retry-utils.ts +76 -0
  223. package/src/bot/run-store.ts +2 -11
  224. package/src/bot/runner.ts +26 -3
  225. package/src/bot/safe-json.ts +76 -0
  226. package/src/bot/safe-path.ts +44 -0
  227. package/src/bot/session-state.ts +2 -1
  228. package/src/bot/steering.ts +1 -1
  229. package/src/bot/step-executor.ts +313 -5
  230. package/src/bot/system-prompt.ts +70 -47
  231. package/src/bot/task-decomposer.ts +100 -0
  232. package/src/bot/task-queue.ts +119 -15
  233. package/src/bot/terminal-renderer.ts +241 -0
  234. package/src/bot/types.ts +8 -0
  235. package/src/bot/weaver-tools.ts +225 -0
  236. package/src/cli-bridge.ts +14 -3
  237. package/src/cli-handlers.ts +760 -29
  238. package/src/handlers/on-bot-completed.ts +48 -0
  239. package/src/handlers/on-execution-failure.ts +42 -0
  240. package/src/handlers/scheduled-run.ts +42 -0
  241. package/src/index.ts +5 -0
  242. package/src/mcp-tools.ts +2 -2
  243. package/src/node-types/abort-task.ts +5 -4
  244. package/src/node-types/agent-execute.ts +306 -0
  245. package/src/node-types/bot-report.ts +40 -9
  246. package/src/node-types/build-context.ts +112 -25
  247. package/src/node-types/detect-provider.ts +4 -3
  248. package/src/node-types/exec-validate-retry.ts +47 -8
  249. package/src/node-types/execute-plan.ts +32 -8
  250. package/src/node-types/execute-target.ts +2 -1
  251. package/src/node-types/fix-errors.ts +20 -5
  252. package/src/node-types/genesis-observe.ts +2 -1
  253. package/src/node-types/genesis-report.ts +1 -1
  254. package/src/node-types/git-ops.ts +93 -4
  255. package/src/node-types/index.ts +2 -0
  256. package/src/node-types/load-config.ts +3 -3
  257. package/src/node-types/plan-task.ts +15 -3
  258. package/src/node-types/read-workflow.ts +2 -2
  259. package/src/node-types/receive-task.ts +31 -26
  260. package/src/node-types/send-notify.ts +1 -1
  261. package/src/node-types/validate-gate.ts +112 -0
  262. package/src/workflows/genesis-task.ts +20 -12
  263. package/src/workflows/weaver-agent.ts +799 -0
  264. package/src/workflows/weaver-bot-batch.ts +1049 -27
  265. package/src/workflows/weaver-bot.ts +1123 -36
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Assistant Core — provider-agnostic conversational loop.
3
+ * The user types, the assistant responds with text and tool calls.
4
+ * UI-agnostic: terminal is one frontend, platform AI chat is another.
5
+ *
6
+ * Supports two provider modes:
7
+ * - CLI provider: tools handled internally via MCP bridge (tool_result events)
8
+ * - API provider: tools collected and executed manually (tool_use_start/end events)
9
+ */
10
+
11
+ import * as readline from 'node:readline';
12
+ import {
13
+ runAgentLoop,
14
+ type AgentProvider,
15
+ type AgentMessage,
16
+ type ToolDefinition,
17
+ type ToolExecutor,
18
+ type StreamEvent,
19
+ } from '@synergenius/flow-weaver/agent';
20
+
21
+ export interface AssistantOptions {
22
+ provider: AgentProvider;
23
+ tools: ToolDefinition[];
24
+ executor: ToolExecutor;
25
+ projectDir: string;
26
+ systemPrompt?: string;
27
+ /** Override for testing — provide messages instead of reading stdin */
28
+ inputMessages?: string[];
29
+ /** Resume a specific conversation by ID */
30
+ resumeId?: string;
31
+ /** Always start a fresh conversation */
32
+ newConversation?: boolean;
33
+ }
34
+
35
+ const DEFAULT_SYSTEM_PROMPT = `You are Weaver Assistant — a director-level AI that manages bot workers and the flow-weaver ecosystem.
36
+
37
+ You help users:
38
+ 1. Spawn and manage multiple bot sessions (bot_spawn, bot_list, bot_status, bot_pause, bot_resume, bot_stop, bot_logs)
39
+ 2. Queue tasks for bots (queue_add, queue_add_batch, queue_list, queue_retry)
40
+ 3. Inspect workflows (fw_validate, fw_diagram, fw_describe)
41
+ 4. Read and analyze project files (read_file, list_files, run_shell)
42
+ 5. Generate reports on bot progress and costs
43
+
44
+ USE TOOLS to fulfill requests. Don't describe what you'd do — actually do it.
45
+ When the user asks to "start a bot", call bot_spawn.
46
+ When they ask for status, call bot_list or bot_status.
47
+ When they ask to add tasks, call queue_add or queue_add_batch.
48
+
49
+ Be concise. Show results, not explanations.
50
+ The user is a senior engineer — don't over-explain.
51
+
52
+ CRITICAL: You are running in a terminal. Do NOT use markdown formatting.
53
+ - No **bold**, no _italic_, no \`backticks\`, no tables with |pipes|
54
+ - No emoji (✅, 🔴, etc.)
55
+ - Use plain text with indentation for structure
56
+ - Use UPPERCASE or quotes for emphasis instead of markdown
57
+ - For lists, use simple dashes: - item
58
+ - For key-value pairs, use: key: value (one per line)
59
+ - Keep output scannable and clean`;
60
+
61
+ // ANSI helpers
62
+ const c = {
63
+ cyan: (s: string) => `\x1b[36m${s}\x1b[0m`,
64
+ dim: (s: string) => `\x1b[2m${s}\x1b[0m`,
65
+ bold: (s: string) => `\x1b[1m${s}\x1b[0m`,
66
+ green: (s: string) => `\x1b[32m${s}\x1b[0m`,
67
+ red: (s: string) => `\x1b[31m${s}\x1b[0m`,
68
+ };
69
+
70
+ export async function runAssistant(opts: AssistantOptions): Promise<void> {
71
+ const { provider, tools, executor, projectDir } = opts;
72
+
73
+ // Build system prompt — include project plan if it exists
74
+ let systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
75
+ try {
76
+ const fs = await import('node:fs');
77
+ const path = await import('node:path');
78
+ const planPath = path.resolve(projectDir, '.weaver-plan.md');
79
+ if (fs.existsSync(planPath)) {
80
+ const plan = fs.readFileSync(planPath, 'utf-8').trim();
81
+ systemPrompt += '\n\n## Project Plan & Vision\n\nAll bots you spawn and tasks you queue MUST align with this plan.\n\n' + plan;
82
+ }
83
+ } catch { /* plan not available */ }
84
+
85
+ const out = (s: string) => process.stderr.write(s);
86
+
87
+ // Persistent conversation store
88
+ const { ConversationStore } = await import('./conversation-store.js');
89
+ const store = new ConversationStore();
90
+
91
+ // Resolve conversation: resume, new, or auto
92
+ let conversation: { id: string; title: string; messageCount: number };
93
+ const history: AgentMessage[] = [];
94
+
95
+ if (opts.resumeId) {
96
+ const existing = store.get(opts.resumeId);
97
+ if (!existing) {
98
+ out(` ${c.red('Conversation not found:')} ${opts.resumeId}\n`);
99
+ return;
100
+ }
101
+ conversation = existing;
102
+ history.push(...store.loadMessages(existing.id));
103
+ compressHistory(history);
104
+ } else if (opts.newConversation) {
105
+ conversation = store.create(projectDir);
106
+ } else {
107
+ // Auto-resume most recent if within 1 hour, else create new
108
+ const recent = store.getMostRecent();
109
+ if (recent && Date.now() - recent.lastMessageAt < 3600_000) {
110
+ conversation = recent;
111
+ history.push(...store.loadMessages(recent.id));
112
+ compressHistory(history);
113
+ } else {
114
+ conversation = store.create(projectDir);
115
+ }
116
+ }
117
+
118
+ // Welcome
119
+ out(`\n ${c.bold('weaver assistant')}\n`);
120
+ out(` ${c.dim(`Project: ${projectDir}`)}\n`);
121
+ if (conversation.title) {
122
+ out(` ${c.dim(`Resuming: "${conversation.title}" (${conversation.messageCount} messages)`)}\n`);
123
+ } else {
124
+ out(` ${c.dim(`Conversation: ${conversation.id}`)}\n`);
125
+ }
126
+ out(` ${c.dim('Type your request. Ctrl+C to exit.')}\n\n`);
127
+
128
+ // Input source
129
+ const rl = opts.inputMessages
130
+ ? null
131
+ : readline.createInterface({ input: process.stdin, output: process.stderr, prompt: `${c.cyan('❯')} ` });
132
+
133
+ const getNextInput = opts.inputMessages
134
+ ? (() => {
135
+ let i = 0;
136
+ return (): Promise<string | null> => Promise.resolve(opts.inputMessages![i++] ?? null);
137
+ })()
138
+ : (): Promise<string | null> => new Promise<string | null>((resolve) => {
139
+ rl!.prompt();
140
+ rl!.once('line', (line) => resolve(line.trim() || null));
141
+ rl!.once('close', () => resolve(null));
142
+ });
143
+
144
+ const onStreamEvent = (event: StreamEvent) => {
145
+ if (event.type === 'text_delta') {
146
+ out(event.text);
147
+ } else if (event.type === 'thinking_delta') {
148
+ out(c.dim(event.text));
149
+ }
150
+ };
151
+
152
+ const onToolEvent = (event: { type: string; name: string; args?: Record<string, unknown>; result?: string; isError?: boolean }) => {
153
+ if (event.type === 'tool_call_start') {
154
+ const preview = toolPreview(event.name, event.args ?? {});
155
+ out(`\n ${c.cyan('◆')} ${event.name}${preview ? c.dim(`(${preview})`) : ''}\n`);
156
+ }
157
+ if (event.type === 'tool_call_result') {
158
+ const icon = event.isError ? c.red('✗') : c.dim('→');
159
+ const result = (event.result ?? '').replace(/\n/g, ' ').slice(0, 150);
160
+ out(` ${icon} ${result}\n`);
161
+ }
162
+ };
163
+
164
+ // Main conversation loop
165
+ while (true) {
166
+ const input = await getNextInput();
167
+ if (input === null) break;
168
+ if (!input.trim()) continue;
169
+
170
+ out('\n');
171
+
172
+ // Add user message to history
173
+ history.push({ role: 'user', content: input });
174
+
175
+ try {
176
+ const result = await runAgentLoop(
177
+ provider,
178
+ tools,
179
+ executor,
180
+ history, // full conversation history
181
+ {
182
+ systemPrompt,
183
+ maxIterations: 20,
184
+ onStreamEvent,
185
+ onToolEvent,
186
+ },
187
+ );
188
+
189
+ // Collect new messages from the agent loop
190
+ const newMessages: AgentMessage[] = [];
191
+ if (result.messages.length > history.length) {
192
+ for (let i = history.length; i < result.messages.length; i++) {
193
+ history.push(result.messages[i]);
194
+ newMessages.push(result.messages[i]);
195
+ }
196
+ }
197
+
198
+ // Persist to disk
199
+ const tokensUsed = result.usage.promptTokens + result.usage.completionTokens;
200
+ store.appendMessages(conversation.id, [{ role: 'user', content: input }, ...newMessages]);
201
+ store.updateAfterTurn(conversation.id, [{ role: 'user', content: input }, ...newMessages], tokensUsed);
202
+
203
+ // Auto-title from first assistant response
204
+ if (!conversation.title) {
205
+ const firstAssistant = newMessages.find(m => m.role === 'assistant');
206
+ if (firstAssistant && typeof firstAssistant.content === 'string') {
207
+ const title = firstAssistant.content.split('\n')[0].slice(0, 80).trim();
208
+ if (title) {
209
+ conversation.title = title;
210
+ store.setTitle(conversation.id, title);
211
+ }
212
+ }
213
+ }
214
+
215
+ // Token-aware compression
216
+ compressHistory(history);
217
+
218
+ if (!result.success && result.summary) {
219
+ out(`\n ${c.red(result.summary)}\n`);
220
+ }
221
+ } catch (err: unknown) {
222
+ const msg = err instanceof Error ? err.message : String(err);
223
+ out(`\n ${c.red('Error:')} ${msg}\n`);
224
+ }
225
+
226
+ out('\n');
227
+ }
228
+
229
+ rl?.close();
230
+ out(`\n ${c.dim('Goodbye.')}\n\n`);
231
+ }
232
+
233
+ function toolPreview(name: string, args: Record<string, unknown>): string {
234
+ if (args.name) return String(args.name);
235
+ if (args.bot) return String(args.bot);
236
+ if (args.file) return String(args.file).split('/').pop() ?? '';
237
+ if (args.path) return String(args.path).split('/').pop() ?? '';
238
+ if (args.instruction) return String(args.instruction).slice(0, 40);
239
+ if (args.command) return String(args.command).slice(0, 40);
240
+ return '';
241
+ }
242
+
243
+ // --- Token-aware history compression ---
244
+
245
+ // Rough token estimate: ~4 chars per token for English text
246
+ const CHARS_PER_TOKEN = 4;
247
+ // Budget: leave room for system prompt (~2k) + tools (~3k) + response (~4k)
248
+ const MAX_HISTORY_TOKENS = 80_000;
249
+ // When compressing, truncate tool results to this size
250
+ const COMPRESSED_TOOL_RESULT_SIZE = 200;
251
+
252
+ function estimateTokens(msg: AgentMessage): number {
253
+ const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
254
+ return Math.ceil(content.length / CHARS_PER_TOKEN);
255
+ }
256
+
257
+ function totalTokens(history: AgentMessage[]): number {
258
+ return history.reduce((sum, m) => sum + estimateTokens(m), 0);
259
+ }
260
+
261
+ /**
262
+ * Compress conversation history to stay within token budget.
263
+ * Strategy (progressive, stops as soon as under budget):
264
+ * 1. Truncate long tool results to 200 chars (keep tool call structure)
265
+ * 2. Summarize old tool results to just "[tool_name: ok/error]"
266
+ * 3. Drop oldest turns (keep most recent 10 turns)
267
+ */
268
+ function compressHistory(history: AgentMessage[]): void {
269
+ if (totalTokens(history) <= MAX_HISTORY_TOKENS) return;
270
+
271
+ // Phase 1: Truncate tool results older than last 6 messages
272
+ const cutoff = Math.max(0, history.length - 6);
273
+ for (let i = 0; i < cutoff; i++) {
274
+ const msg = history[i];
275
+ if (msg.role === 'tool' && typeof msg.content === 'string' && msg.content.length > COMPRESSED_TOOL_RESULT_SIZE) {
276
+ msg.content = msg.content.slice(0, COMPRESSED_TOOL_RESULT_SIZE) + '... (truncated)';
277
+ }
278
+ }
279
+ if (totalTokens(history) <= MAX_HISTORY_TOKENS) return;
280
+
281
+ // Phase 2: Summarize all tool results older than last 10 messages
282
+ const summaryCutoff = Math.max(0, history.length - 10);
283
+ for (let i = 0; i < summaryCutoff; i++) {
284
+ const msg = history[i];
285
+ if (msg.role === 'tool' && typeof msg.content === 'string') {
286
+ const isError = msg.content.toLowerCase().includes('error') || msg.content.toLowerCase().includes('not found');
287
+ msg.content = isError ? '(error — details truncated)' : '(ok — details truncated)';
288
+ }
289
+ // Also truncate long assistant messages
290
+ if (msg.role === 'assistant' && typeof msg.content === 'string' && msg.content.length > 500) {
291
+ msg.content = msg.content.slice(0, 500) + '... (truncated)';
292
+ }
293
+ }
294
+ if (totalTokens(history) <= MAX_HISTORY_TOKENS) return;
295
+
296
+ // Phase 3: Drop oldest turns, keep last 10 messages
297
+ const keep = 10;
298
+ if (history.length > keep) {
299
+ // Insert a summary of what was dropped
300
+ const dropped = history.length - keep;
301
+ history.splice(0, dropped, {
302
+ role: 'user',
303
+ content: `(${dropped} earlier messages compressed. Key context: this is an ongoing assistant session managing bots and workflows.)`,
304
+ });
305
+ }
306
+ }