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