@synergenius/flow-weaver-pack-weaver 0.9.199 → 0.9.201

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 (181) hide show
  1. package/dist/ai-chat-provider.js +5 -5
  2. package/dist/ai-chat-provider.js.map +1 -1
  3. package/dist/bot/acceptance-merge.d.ts +21 -0
  4. package/dist/bot/acceptance-merge.d.ts.map +1 -0
  5. package/dist/bot/acceptance-merge.js +46 -0
  6. package/dist/bot/acceptance-merge.js.map +1 -0
  7. package/dist/bot/ai-client.d.ts +14 -2
  8. package/dist/bot/ai-client.d.ts.map +1 -1
  9. package/dist/bot/ai-client.js +71 -24
  10. package/dist/bot/ai-client.js.map +1 -1
  11. package/dist/bot/assistant-tools.js +3 -3
  12. package/dist/bot/assistant-tools.js.map +1 -1
  13. package/dist/bot/audit-logger.d.ts.map +1 -1
  14. package/dist/bot/audit-logger.js +34 -14
  15. package/dist/bot/audit-logger.js.map +1 -1
  16. package/dist/bot/audit-trail.d.ts +67 -0
  17. package/dist/bot/audit-trail.d.ts.map +1 -0
  18. package/dist/bot/audit-trail.js +153 -0
  19. package/dist/bot/audit-trail.js.map +1 -0
  20. package/dist/bot/behavior-defaults.d.ts +1 -1
  21. package/dist/bot/behavior-defaults.d.ts.map +1 -1
  22. package/dist/bot/behavior-defaults.js +7 -3
  23. package/dist/bot/behavior-defaults.js.map +1 -1
  24. package/dist/bot/capability-registry.d.ts +9 -0
  25. package/dist/bot/capability-registry.d.ts.map +1 -1
  26. package/dist/bot/capability-registry.js +81 -27
  27. package/dist/bot/capability-registry.js.map +1 -1
  28. package/dist/bot/capability-types.d.ts +10 -0
  29. package/dist/bot/capability-types.d.ts.map +1 -1
  30. package/dist/bot/cli-provider.d.ts.map +1 -1
  31. package/dist/bot/cli-provider.js +8 -7
  32. package/dist/bot/cli-provider.js.map +1 -1
  33. package/dist/bot/preflight.d.ts +48 -0
  34. package/dist/bot/preflight.d.ts.map +1 -0
  35. package/dist/bot/preflight.js +247 -0
  36. package/dist/bot/preflight.js.map +1 -0
  37. package/dist/bot/provider-shim.d.ts +74 -0
  38. package/dist/bot/provider-shim.d.ts.map +1 -0
  39. package/dist/bot/provider-shim.js +176 -0
  40. package/dist/bot/provider-shim.js.map +1 -0
  41. package/dist/bot/runner.d.ts +2 -0
  42. package/dist/bot/runner.d.ts.map +1 -1
  43. package/dist/bot/runner.js +60 -17
  44. package/dist/bot/runner.js.map +1 -1
  45. package/dist/bot/step-executor.d.ts.map +1 -1
  46. package/dist/bot/step-executor.js +72 -115
  47. package/dist/bot/step-executor.js.map +1 -1
  48. package/dist/bot/swarm-controller.d.ts +2 -0
  49. package/dist/bot/swarm-controller.d.ts.map +1 -1
  50. package/dist/bot/swarm-controller.js +92 -20
  51. package/dist/bot/swarm-controller.js.map +1 -1
  52. package/dist/bot/task-create-handler.d.ts +37 -0
  53. package/dist/bot/task-create-handler.d.ts.map +1 -0
  54. package/dist/bot/task-create-handler.js +124 -0
  55. package/dist/bot/task-create-handler.js.map +1 -0
  56. package/dist/bot/task-store.d.ts +1 -0
  57. package/dist/bot/task-store.d.ts.map +1 -1
  58. package/dist/bot/task-store.js +67 -0
  59. package/dist/bot/task-store.js.map +1 -1
  60. package/dist/bot/types.d.ts +1 -1
  61. package/dist/bot/types.d.ts.map +1 -1
  62. package/dist/bot/weaver-tools.d.ts.map +1 -1
  63. package/dist/bot/weaver-tools.js +7 -39
  64. package/dist/bot/weaver-tools.js.map +1 -1
  65. package/dist/node-types/agent-execute.d.ts +25 -8
  66. package/dist/node-types/agent-execute.d.ts.map +1 -1
  67. package/dist/node-types/agent-execute.js +89 -23
  68. package/dist/node-types/agent-execute.js.map +1 -1
  69. package/dist/node-types/bot-report.d.ts.map +1 -1
  70. package/dist/node-types/bot-report.js +24 -3
  71. package/dist/node-types/bot-report.js.map +1 -1
  72. package/dist/node-types/plan-task.d.ts +8 -17
  73. package/dist/node-types/plan-task.d.ts.map +1 -1
  74. package/dist/node-types/plan-task.js +217 -256
  75. package/dist/node-types/plan-task.js.map +1 -1
  76. package/dist/node-types/review-result.js +8 -6
  77. package/dist/node-types/review-result.js.map +1 -1
  78. package/dist/palindrome.d.ts +9 -0
  79. package/dist/palindrome.d.ts.map +1 -0
  80. package/dist/palindrome.js +14 -0
  81. package/dist/palindrome.js.map +1 -0
  82. package/dist/ui/approval-card.js +91 -82
  83. package/dist/ui/bot-activity.js +73 -56
  84. package/dist/ui/bot-config.js +48 -31
  85. package/dist/ui/bot-dashboard.js +52 -36
  86. package/dist/ui/bot-panel.js +230 -228
  87. package/dist/ui/bot-slot-card.js +100 -90
  88. package/dist/ui/bot-status.js +37 -15
  89. package/dist/ui/budget-bar.js +57 -31
  90. package/dist/ui/capability-editor.js +447 -378
  91. package/dist/ui/chat-task-result.js +78 -71
  92. package/dist/ui/decision-log.js +68 -81
  93. package/dist/ui/genesis-block.js +86 -95
  94. package/dist/ui/instance-stream-view.js +722 -0
  95. package/dist/ui/profile-card.js +96 -221
  96. package/dist/ui/profile-editor.js +532 -575
  97. package/dist/ui/settings-section.js +41 -45
  98. package/dist/ui/swarm-controls.js +212 -135
  99. package/dist/ui/swarm-dashboard.js +3992 -2715
  100. package/dist/ui/task-detail-view.js +415 -521
  101. package/dist/ui/task-editor.js +339 -390
  102. package/dist/ui/task-pool-list.js +60 -55
  103. package/dist/workflows/src/palindrome.d.ts +11 -0
  104. package/dist/workflows/src/palindrome.d.ts.map +1 -0
  105. package/dist/workflows/src/palindrome.js +16 -0
  106. package/dist/workflows/src/palindrome.js.map +1 -0
  107. package/dist/workflows/tests/palindrome.test.d.ts +2 -0
  108. package/dist/workflows/tests/palindrome.test.d.ts.map +1 -0
  109. package/dist/workflows/tests/palindrome.test.js +41 -0
  110. package/dist/workflows/tests/palindrome.test.js.map +1 -0
  111. package/dist/workflows/weaver-bot-batch.js +1 -1
  112. package/dist/workflows/weaver-bot-batch.js.map +1 -1
  113. package/dist/workflows/weaver-bot.js +1 -1
  114. package/dist/workflows/weaver-bot.js.map +1 -1
  115. package/flowweaver.manifest.json +1 -1
  116. package/package.json +8 -2
  117. package/src/ai-chat-provider.ts +5 -5
  118. package/src/bot/acceptance-merge.ts +62 -0
  119. package/src/bot/ai-client.ts +77 -21
  120. package/src/bot/assistant-tools.ts +3 -3
  121. package/src/bot/audit-logger.ts +42 -14
  122. package/src/bot/audit-trail.ts +211 -0
  123. package/src/bot/behavior-defaults.ts +7 -2
  124. package/src/bot/capability-registry.ts +84 -28
  125. package/src/bot/capability-types.ts +11 -0
  126. package/src/bot/cli-provider.ts +8 -7
  127. package/src/bot/preflight.ts +285 -0
  128. package/src/bot/provider-shim.ts +218 -0
  129. package/src/bot/runner.ts +68 -20
  130. package/src/bot/step-executor.ts +69 -127
  131. package/src/bot/swarm-controller.ts +94 -20
  132. package/src/bot/task-create-handler.ts +164 -0
  133. package/src/bot/task-store.ts +83 -0
  134. package/src/bot/types.ts +4 -1
  135. package/src/bot/weaver-tools.ts +7 -45
  136. package/src/node-types/agent-execute.ts +102 -16
  137. package/src/node-types/bot-report.ts +24 -3
  138. package/src/node-types/plan-task.ts +238 -280
  139. package/src/node-types/review-result.ts +8 -6
  140. package/src/palindrome.ts +14 -0
  141. package/src/ui/approval-card.tsx +78 -62
  142. package/src/ui/bot-activity.tsx +12 -10
  143. package/src/ui/bot-config.tsx +12 -10
  144. package/src/ui/bot-dashboard.tsx +13 -11
  145. package/src/ui/bot-panel.tsx +189 -171
  146. package/src/ui/bot-slot-card.tsx +125 -70
  147. package/src/ui/bot-status.tsx +4 -4
  148. package/src/ui/budget-bar.tsx +86 -25
  149. package/src/ui/capability-editor.tsx +392 -257
  150. package/src/ui/chat-task-result.tsx +81 -78
  151. package/src/ui/decision-log.tsx +76 -73
  152. package/src/ui/genesis-block.tsx +91 -61
  153. package/src/ui/instance-stream-view.tsx +861 -0
  154. package/src/ui/profile-card.tsx +195 -168
  155. package/src/ui/profile-editor.tsx +453 -370
  156. package/src/ui/settings-section.tsx +46 -39
  157. package/src/ui/swarm-controls.tsx +252 -123
  158. package/src/ui/swarm-dashboard.tsx +999 -466
  159. package/src/ui/task-detail-view.tsx +485 -428
  160. package/src/ui/task-editor.tsx +329 -271
  161. package/src/ui/task-pool-list.tsx +68 -62
  162. package/src/workflows/src/palindrome.ts +16 -0
  163. package/src/workflows/tests/palindrome.test.ts +49 -0
  164. package/src/workflows/weaver-bot-batch.ts +1 -1
  165. package/src/workflows/weaver-bot.ts +1 -1
  166. package/dist/ui/bot-constants.d.ts +0 -14
  167. package/dist/ui/bot-constants.d.ts.map +0 -1
  168. package/dist/ui/bot-constants.js +0 -189
  169. package/dist/ui/bot-constants.js.map +0 -1
  170. package/dist/ui/steer-api.d.ts +0 -7
  171. package/dist/ui/steer-api.d.ts.map +0 -1
  172. package/dist/ui/steer-api.js +0 -11
  173. package/dist/ui/steer-api.js.map +0 -1
  174. package/dist/ui/trace-to-timeline.d.ts +0 -91
  175. package/dist/ui/trace-to-timeline.d.ts.map +0 -1
  176. package/dist/ui/trace-to-timeline.js +0 -116
  177. package/dist/ui/trace-to-timeline.js.map +0 -1
  178. package/dist/ui/use-stream-timeline.d.ts +0 -50
  179. package/dist/ui/use-stream-timeline.d.ts.map +0 -1
  180. package/dist/ui/use-stream-timeline.js +0 -245
  181. package/dist/ui/use-stream-timeline.js.map +0 -1
@@ -1,9 +1,16 @@
1
1
  import type { WeaverContext, StepLogEntry } from '../bot/types.js';
2
- import { callPlatformWithMessages, callCapabilityTriage } from '../bot/ai-client.js';
3
- import type { AiTool, AiCallResult, ChatMessage } from '../bot/ai-client.js';
2
+ import { callCapabilityTriage } from '../bot/ai-client.js';
3
+ import type { AiTool } from '../bot/ai-client.js';
4
4
  import { auditEmit } from '../bot/audit-logger.js';
5
- import { executeStep } from '../bot/step-executor.js';
6
5
  import { getCapabilitiesByNames, BUILT_IN_CAPABILITIES, PROFILE_CAPABILITIES } from '../bot/capability-registry.js';
6
+ import { ALL_TOOLS, BOT_TOOLS, resolveToolsForTask } from '../bot/tool-registry.js';
7
+ import { createWeaverExecutor, WEAVER_TOOLS } from '../bot/weaver-tools.js';
8
+ import {
9
+ runAgentLoop,
10
+ stripMcpToolPrefix,
11
+ type ToolEvent,
12
+ type StreamEvent,
13
+ } from '@synergenius/flow-weaver/agent';
7
14
  import { readFileSync } from 'node:fs';
8
15
  import { fileURLToPath } from 'node:url';
9
16
  import * as nodePath from 'node:path';
@@ -19,7 +26,7 @@ try {
19
26
  // ---------------------------------------------------------------------------
20
27
 
21
28
  /** All known tool schemas, keyed by operation name. */
22
- const TOOL_SCHEMAS: Record<string, AiTool> = {
29
+ export const TOOL_SCHEMAS: Record<string, AiTool> = {
23
30
  read_file: {
24
31
  name: 'read_file',
25
32
  description: 'Read a file and return its content',
@@ -88,6 +95,55 @@ const TOOL_SCHEMAS: Record<string, AiTool> = {
88
95
  description: 'Validate a Flow Weaver workflow file',
89
96
  parameters: { type: 'object', properties: { file: { type: 'string' } }, required: ['file'] },
90
97
  },
98
+ tsc_check: {
99
+ name: 'tsc_check',
100
+ description: 'Run TypeScript compiler check (npx tsc --noEmit). Returns errors if any.',
101
+ parameters: { type: 'object', properties: {}, required: [] },
102
+ },
103
+ run_tests: {
104
+ name: 'run_tests',
105
+ description: 'Run project tests (npx vitest run). Returns structured results with pass/fail counts.',
106
+ parameters: { type: 'object', properties: { pattern: { type: 'string', description: 'Test file pattern (optional)' } } },
107
+ },
108
+ learn: {
109
+ name: 'learn',
110
+ description: 'Store a fact for future tasks. Key should be descriptive.',
111
+ parameters: { type: 'object', properties: { key: { type: 'string' }, value: { type: 'string' } }, required: ['key', 'value'] },
112
+ },
113
+ web_fetch: {
114
+ name: 'web_fetch',
115
+ description: 'Fetch HTTP content from a URL. Returns text body (max 10KB).',
116
+ parameters: { type: 'object', properties: { url: { type: 'string' } }, required: ['url'] },
117
+ },
118
+ task_list: {
119
+ name: 'task_list',
120
+ description: 'List tasks in the swarm task pool. Optionally filter by status.',
121
+ parameters: { type: 'object', properties: { status: { type: 'string', description: 'Filter: pending, in-progress, done, failed' } } },
122
+ },
123
+ task_get: {
124
+ name: 'task_get',
125
+ description: 'Get full details of a specific task by ID.',
126
+ parameters: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
127
+ },
128
+ task_update: {
129
+ name: 'task_update',
130
+ description: 'Update a task — add notes, change files list, or update description.',
131
+ parameters: {
132
+ type: 'object',
133
+ properties: {
134
+ id: { type: 'string' },
135
+ notes: { type: 'string' },
136
+ files: { type: 'array', items: { type: 'string' } },
137
+ description: { type: 'string' },
138
+ },
139
+ required: ['id'],
140
+ },
141
+ },
142
+ ask_user: {
143
+ name: 'ask_user',
144
+ description: 'Ask the user a question and wait for response.',
145
+ parameters: { type: 'object', properties: { question: { type: 'string' } }, required: ['question'] },
146
+ },
91
147
  respond: {
92
148
  name: 'respond',
93
149
  description: 'Send a text response to the user',
@@ -95,7 +151,38 @@ const TOOL_SCHEMAS: Record<string, AiTool> = {
95
151
  },
96
152
  };
97
153
 
98
- /** Build tool definitions from capability-granted operation names. */
154
+ // ---------------------------------------------------------------------------
155
+ // Classification utility (exported for tests)
156
+ // ---------------------------------------------------------------------------
157
+
158
+ export function classifyToolRejection(toolName: string): { code: 'MISSING_SCHEMA' | 'NOT_GRANTED' | 'HALLUCINATED'; detail: string } {
159
+ const normalized = stripMcpToolPrefix(toolName);
160
+ const allToolNames = new Set(ALL_TOOLS.map(t => t.name));
161
+ const hasRegistry = allToolNames.has(normalized);
162
+ const hasSchema = !!TOOL_SCHEMAS[normalized];
163
+
164
+ if (hasRegistry && !hasSchema) {
165
+ return {
166
+ code: 'MISSING_SCHEMA',
167
+ detail: `Tool '${normalized}' exists in tool-registry but has no TOOL_SCHEMAS entry in plan-task. The model sees it in the prompt but cannot call it. Add it to TOOL_SCHEMAS.`,
168
+ };
169
+ }
170
+ if (hasRegistry && hasSchema) {
171
+ return {
172
+ code: 'NOT_GRANTED',
173
+ detail: `Tool '${normalized}' exists and has a schema but was not granted for this task's mode/capabilities. Check resolveToolsForTask and capability config.`,
174
+ };
175
+ }
176
+ return {
177
+ code: 'HALLUCINATED',
178
+ detail: `Tool '${normalized}' does not exist in tool-registry. The model invented a tool name.`,
179
+ };
180
+ }
181
+
182
+ // ---------------------------------------------------------------------------
183
+ // Prompt building helpers
184
+ // ---------------------------------------------------------------------------
185
+
99
186
  function buildToolDefinitions(grantedTools: string[]): AiTool[] {
100
187
  const tools: AiTool[] = [];
101
188
  const seen = new Set<string>();
@@ -104,10 +191,14 @@ function buildToolDefinitions(grantedTools: string[]): AiTool[] {
104
191
  if (seen.has(name)) continue;
105
192
  seen.add(name);
106
193
  const schema = TOOL_SCHEMAS[name];
107
- if (schema) tools.push(schema);
194
+ if (schema) {
195
+ tools.push(schema);
196
+ } else {
197
+ console.error(`\x1b[33m ⚠ Tool '${name}' granted by capabilities but has no TOOL_SCHEMAS entry — model cannot call it\x1b[0m`);
198
+ auditEmit('tool-schema-missing', { tool: name, grantedTools });
199
+ }
108
200
  }
109
201
 
110
- // Always include 'done' so the AI can signal completion
111
202
  if (!seen.has('done')) {
112
203
  tools.push(TOOL_SCHEMAS.done);
113
204
  }
@@ -116,49 +207,20 @@ function buildToolDefinitions(grantedTools: string[]): AiTool[] {
116
207
  }
117
208
 
118
209
  // ---------------------------------------------------------------------------
119
- // Safety limits
210
+ // Main entry point
120
211
  // ---------------------------------------------------------------------------
121
212
 
122
- const MAX_TOOL_CALLS = 30;
123
- const AGENT_TIMEOUT_MS = 180_000; // 3 minutes
213
+ const AGENT_TIMEOUT_MS = 10 * 60 * 1000; // 10 min
124
214
 
125
- // ---------------------------------------------------------------------------
126
- // Agent Loop — replaces Plan Task + Execute & Validate
127
- // ---------------------------------------------------------------------------
128
-
129
- /**
130
- * Agent loop: sends task + tools to the AI, executes tool calls iteratively
131
- * until the AI signals completion or a safety limit is reached.
132
- *
133
- * Replaces the old Plan Task → Execute & Validate two-phase workflow.
134
- *
135
- * @flowWeaver nodeType
136
- * @label Agent Loop
137
- * @icon psychology
138
- * @color blue
139
- * @input ctx [order:0] - Weaver context (JSON)
140
- * @input [modelOverride] [order:1] - Model ID override from profile behavior
141
- * @output ctx [order:0] - Weaver context with results (JSON)
142
- * @output onSuccess [order:-2] - On Success
143
- * @output onFailure [order:-1] [hidden] - On Failure
144
- */
145
215
  export async function weaverPlanTask(
146
- execute: boolean,
147
216
  ctx: string,
217
+ execute: boolean,
148
218
  modelOverride?: string,
149
- ): Promise<{
150
- onSuccess: boolean; onFailure: boolean;
151
- ctx: string;
152
- }> {
219
+ ): Promise<{ onSuccess: boolean; onFailure: boolean; ctx: string }> {
153
220
  const context = JSON.parse(ctx) as WeaverContext;
154
221
  const { env } = context;
155
222
 
156
223
  if (!execute) {
157
- context.planJson = '{"steps":[],"summary":"dry run"}';
158
- context.resultJson = JSON.stringify({ success: true, toolCallCount: 0 });
159
- context.filesModified = '[]';
160
- context.stepLogJson = '[]';
161
- context.allValid = true;
162
224
  return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
163
225
  }
164
226
 
@@ -166,299 +228,195 @@ export async function weaverPlanTask(
166
228
  ? { ...env.providerInfo, model: modelOverride }
167
229
  : env.providerInfo;
168
230
  const { projectDir } = env;
231
+ const task = context.taskJson ? JSON.parse(context.taskJson) : { instruction: '', mode: 'create' };
169
232
 
170
- if (!context.taskJson) {
171
- context.planJson = JSON.stringify({ steps: [], summary: 'Agent loop failed: missing taskJson' });
172
- context.resultJson = JSON.stringify({ success: false, error: 'missing taskJson' });
173
- context.filesModified = '[]';
174
- context.stepLogJson = '[]';
175
- context.allValid = false;
176
- return { onSuccess: false, onFailure: true, ctx: JSON.stringify(context) };
177
- }
178
- const task = JSON.parse(context.taskJson);
179
-
180
- // Resolve available capabilities: profile pool > behavior config > all defaults
181
- const behavior = context.behaviorJson ? JSON.parse(context.behaviorJson) : undefined;
182
- const profilePool = task.assignedProfile ? PROFILE_CAPABILITIES[task.assignedProfile] : undefined;
183
- const availableCapNames: string[] = profilePool ?? behavior?.capabilities ?? BUILT_IN_CAPABILITIES.map(c => c.name);
184
- const availableCaps = getCapabilitiesByNames(availableCapNames);
185
-
186
- // Capability triage: only run when using the default (all) pool.
187
- // Profile pools are already scoped — triage would just add latency + cost.
188
- let selectedCaps = availableCaps;
189
- if (!profilePool) {
190
- const triageResult = await callCapabilityTriage(pInfo, task.instruction, availableCaps);
191
- if (triageResult) {
192
- selectedCaps = getCapabilitiesByNames(triageResult);
233
+ // -----------------------------------------------------------------------
234
+ // Build system prompt from capabilities
235
+ // -----------------------------------------------------------------------
236
+
237
+ const selectedCaps = (() => {
238
+ const profilePool = task.assignedProfile ? PROFILE_CAPABILITIES[task.assignedProfile] : undefined;
239
+ if (profilePool) {
240
+ return getCapabilitiesByNames(profilePool);
193
241
  }
194
- }
242
+ // Fallback: triage
243
+ try {
244
+ return getCapabilitiesByNames(['core', 'file-ops', 'shell', 'context']);
245
+ } catch {
246
+ return BUILT_IN_CAPABILITIES.slice(0, 4);
247
+ }
248
+ })();
249
+ const selectedCapNames = new Set(selectedCaps.map(c => c.name));
195
250
 
196
- // Build system prompt from capabilities
197
251
  let systemPrompt: string;
252
+ let tools: AiTool[];
198
253
  try {
199
254
  const mod = await import('../bot/system-prompt.js');
255
+ const mod2 = await import('../bot/system-prompt.js');
200
256
  const basePrompt = mod.buildPromptFromCapabilities(selectedCaps);
201
- const selectedCapNames = new Set(selectedCaps.map(c => c.name));
202
257
  const contextBundle = selectedCapNames.has('context') ? context.contextBundle : undefined;
203
258
  const botPrompt = mod.buildBotSystemPrompt(contextBundle, undefined, context.env?.projectDir);
204
259
  systemPrompt = basePrompt + '\n\n' + botPrompt;
205
- } catch (err) {
206
- if (process.env.WEAVER_VERBOSE) console.error('[agent-loop] system prompt build failed:', err);
207
- systemPrompt = 'You are Weaver, an AI workflow bot. Use the provided tools to complete tasks.';
208
- }
209
260
 
210
- // Build tool definitions from capability-granted operations
211
- let tools: AiTool[];
212
- try {
213
- const mod2 = await import('../bot/system-prompt.js');
214
261
  const grantedTools = mod2.collectToolsFromCapabilities(selectedCaps);
215
262
  tools = buildToolDefinitions(grantedTools);
216
263
  } catch (err) {
217
264
  console.warn('[plan-task] capability resolution failed, using worker defaults:', err);
218
- // Fallback: worker tools only — NO task_create (that's orchestrator-only)
219
265
  tools = buildToolDefinitions(['read_file', 'write_file', 'patch_file', 'run_shell', 'list_files']);
266
+ systemPrompt = 'You are Weaver. Execute tasks by calling tools.';
220
267
  }
221
268
 
222
- // Build initial user prompt
223
- // Only mention task_create if the tool is actually available (orchestrator only)
269
+ // Build user prompt
224
270
  const hasTaskCreate = tools.some(t => t.name === 'task_create');
225
- let userPrompt = `Task: ${task.instruction}\nMode: ${task.mode ?? 'create'}\n${task.id && hasTaskCreate ? `Your Task ID is ${task.id}. Use parentId: "@self" in task_create to create subtasks under this task.` : ''}\n${task.targets ? 'Targets: ' + task.targets.join(', ') : ''}
271
+ const maxConcurrent = (context as any).maxConcurrent ?? 2;
272
+ let userPrompt = `Task: ${task.instruction}\nMode: ${task.mode ?? 'create'}\n${task.id && hasTaskCreate ? `Your Task ID is ${task.id}. Use parentId: "@self" in task_create to create subtasks under this task.\nWorkers available: ${maxConcurrent} (plan task grouping to maximize parallel utilization)` : ''}\n${task.targets ? 'Targets: ' + task.targets.join(', ') : ''}
273
+
274
+ ${context.contextBundle || ''}
226
275
 
227
276
  Execute this task step by step using the available tools.
228
277
  When you are done, call the "done" tool with a summary of what you accomplished.
229
278
  Rules:
230
279
  1. Read files before modifying them (use read_file to get exact content for patches).
231
280
  2. Use patch_file for modifications, write_file only for new files.
232
- 3. Verify your work by running tests or tsc when appropriate.`;
233
-
234
- // Append retry context when this is a convergent re-run (runHistory has prior entries)
235
- const runCount = task.runHistory?.length ?? 0;
236
- if (runCount > 0) {
237
- const retryParts: string[] = ['\n\n--- RETRY CONTEXT (run ' + (runCount + 1) + ') ---'];
238
- const summaries: Array<{ outcome?: string; filesModified?: string[]; summary?: string; error?: string }> = task.runHistory ?? [];
239
- const lastSummary = summaries.length > 0 ? summaries[summaries.length - 1] : undefined;
240
- if (lastSummary?.error) {
241
- retryParts.push('Previous attempt failed with error: ' + lastSummary.error);
242
- }
243
- if (lastSummary) {
244
- if (lastSummary.outcome) retryParts.push('Last outcome: ' + lastSummary.outcome);
245
- if (lastSummary.summary) retryParts.push('Last summary: ' + lastSummary.summary);
246
- if (lastSummary.filesModified && lastSummary.filesModified.length > 0) {
247
- retryParts.push('Files already created/modified: ' + lastSummary.filesModified.join(', '));
248
- }
249
- }
250
- retryParts.push('Do NOT recreate files that already exist. Read them first and continue from where the previous attempt left off.');
251
- retryParts.push('---');
252
- userPrompt += retryParts.join('\n');
253
- }
254
-
255
- // Seed symbolic ID map for task references
256
- const symbolicIdMap: Record<string, string> = {};
257
- if (task.id) symbolicIdMap['@self'] = task.id;
258
- if (task.parentId) symbolicIdMap['@parent'] = task.parentId;
281
+ 3. Verify your work by running tests or tsc when appropriate.`.trim();
282
+
283
+ // -----------------------------------------------------------------------
284
+ // Create provider + executor, run via runAgentLoop
285
+ // -----------------------------------------------------------------------
286
+
287
+ const { createProvider } = await import('./agent-execute.js');
288
+ const provider = await createProvider(pInfo, projectDir);
289
+ const providerStats = (provider as unknown as { stats?: { bridgeActive: boolean; bridgeConfigPath?: string; toolUseFiltered: number; toolResultPassthrough: number; textToolCallDetected: number; streamCalls: number; setHandlersCalls: number } }).stats;
290
+ auditEmit('bridge-created', {
291
+ providerType: pInfo.type,
292
+ providerClass: provider.constructor?.name ?? 'unknown',
293
+ bridgeActive: providerStats?.bridgeActive ?? false,
294
+ bridgeConfigPath: providerStats?.bridgeConfigPath ?? null,
295
+ });
296
+ const executor = createWeaverExecutor(projectDir);
259
297
 
260
- // State tracking
298
+ const filesCreated: string[] = [];
261
299
  const filesModified: string[] = [];
262
300
  const stepLog: StepLogEntry[] = [];
263
301
  let toolCallCount = 0;
264
- const deadline = Date.now() + AGENT_TIMEOUT_MS;
265
302
 
266
- // Messages array for multi-turn conversation
267
- const messages: ChatMessage[] = [
268
- { role: 'system', content: systemPrompt },
269
- { role: 'user', content: userPrompt },
270
- ];
303
+ const onToolEvent = (event: ToolEvent) => {
304
+ if (event.type === 'tool_call_start') {
305
+ toolCallCount++;
306
+ const name = stripMcpToolPrefix(event.name);
307
+ console.log(`\x1b[32m + ${toolCallCount}: ${name}\x1b[0m`);
308
+ auditEmit('tool-call', {
309
+ turn: toolCallCount,
310
+ tool: name,
311
+ args: JSON.stringify(event.args ?? {}).slice(0, 1000),
312
+ });
313
+ }
314
+ if (event.type === 'tool_call_result') {
315
+ const name = stripMcpToolPrefix(event.name);
316
+ auditEmit('tool-result', { tool: name, isError: event.isError, result: (event.result ?? '').slice(0, 500) });
317
+ stepLog.push({
318
+ step: `${toolCallCount}:${name}`,
319
+ status: event.isError ? 'error' : 'ok',
320
+ detail: event.isError ? (event.result ?? '').slice(0, 200) : name,
321
+ });
271
322
 
272
- // Build allowed tool name set for validation (always includes 'done' and 'complete')
273
- const allowedToolNames = new Set(tools.map(t => t.name));
274
- allowedToolNames.add('done');
275
- allowedToolNames.add('complete');
323
+ // Track file changes
324
+ if (!event.isError && event.args?.file) {
325
+ if (name === 'write_file') filesCreated.push(event.args.file as string);
326
+ if (name === 'patch_file') filesModified.push(event.args.file as string);
327
+ }
328
+ }
329
+ };
330
+
331
+ const onStreamEvent = (event: StreamEvent) => {
332
+ if (event.type === 'text_delta') {
333
+ process.stderr.write(`\x1b[36m${event.text}\x1b[0m`);
334
+ }
335
+ if (event.type === 'thinking_delta') {
336
+ process.stderr.write('\x1b[90m thinking...\x1b[0m');
337
+ }
338
+ };
339
+
340
+ // Convert WEAVER_TOOLS to ToolDefinition format for runAgentLoop
341
+ const grantedToolNames = new Set(tools.map(t => t.name));
342
+ grantedToolNames.add('done');
343
+ grantedToolNames.add('complete');
344
+ const loopTools = WEAVER_TOOLS.filter(t => grantedToolNames.has(t.name));
276
345
 
277
346
  auditEmit('run-start', { task: task.instruction, mode: 'agent-loop', packVersion: PACK_VERSION, profile: task.assignedProfile });
278
347
  auditEmit('ai-request', {
279
- systemPrompt: systemPrompt.slice(0, 2000),
280
- userPrompt: userPrompt.slice(0, 2000),
281
- tools: tools.map(t => t.name),
348
+ systemPrompt,
349
+ userPrompt,
350
+ tools,
351
+ capabilities: selectedCaps.map(c => c.name),
352
+ model: pInfo.model,
353
+ providerType: pInfo.type,
282
354
  });
283
355
 
284
356
  try {
285
- // Agent loop
286
- let done = false;
287
- while (!done && toolCallCount < MAX_TOOL_CALLS && Date.now() < deadline) {
288
- const result: AiCallResult = await callPlatformWithMessages(messages, tools, pInfo.model, pInfo.maxTokens ?? 8192);
289
-
290
- auditEmit('ai-response', {
291
- turn: toolCallCount,
292
- content: result.content?.slice(0, 2000) ?? null,
293
- thinking: (result as any).thinking?.slice(0, 2000) ?? null,
294
- toolCallCount: result.toolCalls?.length ?? 0,
295
- toolNames: result.toolCalls?.map(tc => tc.name) ?? [],
296
- });
297
-
298
- // If AI returns text with no tool calls, we're done
299
- if (!result.toolCalls || result.toolCalls.length === 0) {
300
- if (result.content) {
301
- console.log(`\x1b[36m→ Agent: ${result.content.slice(0, 200)}\x1b[0m`);
302
- }
303
- done = true;
304
- break;
305
- }
306
-
307
- // Process each tool call
308
- for (const tc of result.toolCalls) {
309
- if (toolCallCount >= MAX_TOOL_CALLS || Date.now() >= deadline) {
310
- done = true;
311
- break;
312
- }
313
-
314
- toolCallCount++;
315
- const toolName = tc.name;
316
- const toolArgs = tc.arguments;
317
-
318
- auditEmit('tool-call', {
319
- turn: toolCallCount,
320
- tool: toolName,
321
- args: JSON.stringify(toolArgs).slice(0, 1000),
322
- });
323
-
324
- // Check for done signal
325
- if (toolName === 'done' || toolName === 'complete') {
326
- console.log(`\x1b[36m→ Agent done: ${(toolArgs as Record<string, string>).summary ?? 'completed'}\x1b[0m`);
327
-
328
- // Execute through step-executor for consistent handling
329
- try {
330
- const stepResult = await executeStep(
331
- { operation: toolName, args: toolArgs },
332
- projectDir,
333
- symbolicIdMap,
334
- );
335
- stepLog.push({ step: `${toolCallCount}:${toolName}`, status: 'ok', detail: stepResult.output ?? 'done' });
336
- } catch {
337
- stepLog.push({ step: `${toolCallCount}:${toolName}`, status: 'ok', detail: 'done' });
338
- }
339
- auditEmit('tool-result', { turn: toolCallCount, tool: 'done', status: 'ok', output: 'completed' });
340
-
341
- // Add assistant message with tool use and tool result to messages
342
- messages.push({
343
- role: 'assistant',
344
- content: result.content || undefined,
345
- tool_use: { id: tc.id, name: toolName, input: toolArgs },
346
- });
347
- messages.push({
348
- role: 'user',
349
- tool_use_id: tc.id,
350
- content: 'Task completed.',
351
- });
352
-
353
- done = true;
354
- break;
355
- }
356
-
357
- // Validate tool name against allowed list to prevent hallucinated tool execution
358
- if (!allowedToolNames.has(toolName)) {
359
- const available = [...allowedToolNames].sort().join(', ');
360
- const toolOutput = `Unknown tool: ${toolName}. Available tools: ${available}`;
361
- stepLog.push({ step: `${toolCallCount}:${toolName}`, status: 'error', detail: toolOutput });
362
- console.error(`\x1b[31m x ${toolCallCount}: ${toolName}: hallucinated tool\x1b[0m`);
363
- auditEmit('tool-result', {
364
- turn: toolCallCount,
365
- tool: toolName,
366
- status: 'rejected',
367
- output: toolOutput.slice(0, 500),
368
- });
369
-
370
- messages.push({
371
- role: 'assistant',
372
- content: result.content || undefined,
373
- tool_use: { id: tc.id, name: toolName, input: toolArgs },
374
- });
375
- messages.push({
376
- role: 'user',
377
- tool_use_id: tc.id,
378
- content: toolOutput,
379
- });
380
- continue;
381
- }
382
-
383
- // Execute the tool via step-executor
384
- let toolOutput: string;
385
- try {
386
- const stepResult = await executeStep(
387
- { operation: toolName, args: toolArgs },
388
- projectDir,
389
- symbolicIdMap,
390
- );
391
-
392
- // Track files modified — only from write/patch operations, not reads/listings
393
- const isWriteOp = toolName === 'write_file' || toolName === 'patch_file' || toolName === 'create_workflow' || toolName === 'modify_source' || toolName === 'implement_node';
394
- if (isWriteOp && stepResult.file) filesModified.push(stepResult.file);
395
-
396
- // Build output for AI
397
- if (stepResult.blocked) {
398
- toolOutput = `BLOCKED: ${stepResult.blockReason}`;
399
- stepLog.push({ step: `${toolCallCount}:${toolName}`, status: 'blocked', detail: stepResult.blockReason });
400
- } else {
401
- toolOutput = stepResult.output ?? 'OK';
402
- stepLog.push({ step: `${toolCallCount}:${toolName}`, status: 'ok', detail: toolName });
403
- }
404
-
405
- console.log(`\x1b[32m + ${toolCallCount}: ${toolName}\x1b[0m`);
406
- auditEmit('tool-result', {
407
- turn: toolCallCount,
408
- tool: toolName,
409
- status: stepResult.blocked ? 'blocked' : 'ok',
410
- output: toolOutput.slice(0, 1000),
411
- });
412
- } catch (err: unknown) {
413
- const msg = err instanceof Error ? err.message : String(err);
414
- toolOutput = `ERROR: ${msg}`;
415
- stepLog.push({ step: `${toolCallCount}:${toolName}`, status: 'error', detail: msg });
416
- console.error(`\x1b[31m x ${toolCallCount}: ${toolName}: ${msg}\x1b[0m`);
417
- auditEmit('tool-result', {
418
- turn: toolCallCount,
419
- tool: toolName,
420
- status: 'error',
421
- output: msg.slice(0, 1000),
422
- });
423
- }
424
-
425
- // Append assistant tool_use + tool result to messages for next iteration
426
- messages.push({
427
- role: 'assistant',
428
- content: result.content || undefined,
429
- tool_use: { id: tc.id, name: toolName, input: toolArgs },
430
- });
431
- messages.push({
432
- role: 'user',
433
- tool_use_id: tc.id,
434
- content: toolOutput,
435
- });
436
- }
437
- }
357
+ const result = await runAgentLoop(
358
+ provider,
359
+ loopTools,
360
+ executor,
361
+ [{ role: 'user', content: userPrompt }],
362
+ {
363
+ systemPrompt: { prefix: systemPrompt, suffix: '' },
364
+ maxIterations: 30,
365
+ onToolEvent,
366
+ onStreamEvent,
367
+ },
368
+ );
438
369
 
439
- // Deduplicate files
440
- const uniqueFiles = [...new Set(filesModified)];
370
+ const usage = result.usage;
371
+ const uniqueCreated = [...new Set(filesCreated)];
372
+ const uniqueModified = [...new Set(filesModified)];
441
373
 
442
- // Store results in context (compatible with exec-validate-retry output format)
443
374
  context.resultJson = JSON.stringify({
444
375
  success: true,
445
376
  toolCallCount,
446
- filesModified: uniqueFiles,
377
+ filesCreated: uniqueCreated,
378
+ filesModified: uniqueModified,
447
379
  stepsCompleted: toolCallCount,
448
380
  stepsTotal: toolCallCount,
449
381
  });
450
382
  context.planJson = JSON.stringify({ steps: [], summary: `Agent loop: ${toolCallCount} tool calls` });
451
- context.filesModified = JSON.stringify(uniqueFiles);
383
+ context.filesModified = JSON.stringify([...uniqueCreated, ...uniqueModified]);
452
384
  context.stepLogJson = JSON.stringify(stepLog);
453
385
  context.allValid = true;
454
386
 
387
+ // Re-read provider stats after loop (they accumulate during streaming)
388
+ const finalStats = (provider as unknown as { stats?: typeof providerStats }).stats;
389
+
455
390
  auditEmit('run-complete', {
456
391
  success: true,
457
392
  toolCalls: toolCallCount,
458
- filesModified: uniqueFiles.length,
393
+ filesCreated: uniqueCreated.length,
394
+ filesModified: uniqueModified.length,
395
+ usage,
396
+ bridge: finalStats ? {
397
+ active: finalStats.bridgeActive,
398
+ toolUseFiltered: finalStats.toolUseFiltered,
399
+ toolResultPassthrough: finalStats.toolResultPassthrough,
400
+ textToolCallDetected: finalStats.textToolCallDetected,
401
+ streamCalls: finalStats.streamCalls,
402
+ setHandlersCalls: finalStats.setHandlersCalls,
403
+ } : undefined,
459
404
  });
460
405
 
461
- console.log(`\x1b[36m→ Agent loop: ${toolCallCount} tool calls, ${uniqueFiles.length} files modified\x1b[0m`);
406
+ // Surface bridge issues immediately in console output
407
+ if (finalStats?.bridgeActive) {
408
+ if (finalStats.textToolCallDetected > 0) {
409
+ console.error(`\x1b[31m ✗ BRIDGE ISSUE: Model output ${finalStats.textToolCallDetected} tool calls as TEXT — MCP tools not connected to CLI session\x1b[0m`);
410
+ }
411
+ if (finalStats.toolUseFiltered === 0 && finalStats.toolResultPassthrough === 0) {
412
+ console.warn(`\x1b[33m ⚠ Bridge active but zero structured tool events (filtered=0, passthrough=0)\x1b[0m`);
413
+ } else {
414
+ console.log(`\x1b[32m ✓ Bridge: ${finalStats.toolUseFiltered} tool_use filtered, ${finalStats.toolResultPassthrough} tool_result passed through\x1b[0m`);
415
+ }
416
+ }
417
+
418
+ const costStr = usage.costUsd > 0 ? ` | $${usage.costUsd.toFixed(4)}` : '';
419
+ console.log(`\x1b[36m→ Agent loop: ${toolCallCount} tool calls, ${uniqueCreated.length + uniqueModified.length} files changed${costStr}\x1b[0m`);
462
420
  return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
463
421
  } catch (err: unknown) {
464
422
  const msg = err instanceof Error ? err.message : String(err);
@@ -466,7 +424,7 @@ Rules:
466
424
 
467
425
  context.resultJson = JSON.stringify({ success: false, error: msg });
468
426
  context.planJson = JSON.stringify({ steps: [], summary: `Agent loop failed: ${msg}` });
469
- context.filesModified = JSON.stringify([...new Set(filesModified)]);
427
+ context.filesModified = JSON.stringify([...new Set([...filesCreated, ...filesModified])]);
470
428
  context.stepLogJson = JSON.stringify(stepLog);
471
429
  context.allValid = false;
472
430
 
@@ -233,11 +233,12 @@ async function createReviewProvider(
233
233
 
234
234
  if (type === 'claude-cli') {
235
235
  const key = projectDir ?? process.cwd();
236
- const session = getOrCreateCliSession(`${key}:reviewer`, {
237
- binPath: 'claude',
236
+ const { getCliSessionConfig } = await import('@synergenius/flow-weaver/agent');
237
+ const session = getOrCreateCliSession(`${key}:reviewer`, getCliSessionConfig({
238
238
  cwd: key,
239
239
  model: pInfo.model ?? 'claude-sonnet-4-6',
240
- });
240
+ appendSystemPrompt: 'You are a Weaver reviewer bot. Evaluate task results using only the provided tools.',
241
+ }));
241
242
  return new CliReviewProvider(session);
242
243
  }
243
244
 
@@ -250,11 +251,12 @@ async function createReviewProvider(
250
251
  });
251
252
  }
252
253
  const key = projectDir ?? process.cwd();
253
- const session = getOrCreateCliSession(`${key}:reviewer`, {
254
- binPath: 'claude',
254
+ const { getCliSessionConfig } = await import('@synergenius/flow-weaver/agent');
255
+ const session = getOrCreateCliSession(`${key}:reviewer`, getCliSessionConfig({
255
256
  cwd: key,
256
257
  model: pInfo.model ?? 'claude-sonnet-4-6',
257
- });
258
+ appendSystemPrompt: 'You are a Weaver reviewer bot. Evaluate task results using only the provided tools.',
259
+ }));
258
260
  return new CliReviewProvider(session);
259
261
  }
260
262