@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.
- package/dist/ai-chat-provider.js +5 -5
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/acceptance-merge.d.ts +21 -0
- package/dist/bot/acceptance-merge.d.ts.map +1 -0
- package/dist/bot/acceptance-merge.js +46 -0
- package/dist/bot/acceptance-merge.js.map +1 -0
- package/dist/bot/ai-client.d.ts +14 -2
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +71 -24
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/assistant-tools.js +3 -3
- package/dist/bot/assistant-tools.js.map +1 -1
- package/dist/bot/audit-logger.d.ts.map +1 -1
- package/dist/bot/audit-logger.js +34 -14
- package/dist/bot/audit-logger.js.map +1 -1
- package/dist/bot/audit-trail.d.ts +67 -0
- package/dist/bot/audit-trail.d.ts.map +1 -0
- package/dist/bot/audit-trail.js +153 -0
- package/dist/bot/audit-trail.js.map +1 -0
- package/dist/bot/behavior-defaults.d.ts +1 -1
- package/dist/bot/behavior-defaults.d.ts.map +1 -1
- package/dist/bot/behavior-defaults.js +7 -3
- package/dist/bot/behavior-defaults.js.map +1 -1
- package/dist/bot/capability-registry.d.ts +9 -0
- package/dist/bot/capability-registry.d.ts.map +1 -1
- package/dist/bot/capability-registry.js +81 -27
- package/dist/bot/capability-registry.js.map +1 -1
- package/dist/bot/capability-types.d.ts +10 -0
- package/dist/bot/capability-types.d.ts.map +1 -1
- package/dist/bot/cli-provider.d.ts.map +1 -1
- package/dist/bot/cli-provider.js +8 -7
- package/dist/bot/cli-provider.js.map +1 -1
- package/dist/bot/preflight.d.ts +48 -0
- package/dist/bot/preflight.d.ts.map +1 -0
- package/dist/bot/preflight.js +247 -0
- package/dist/bot/preflight.js.map +1 -0
- package/dist/bot/provider-shim.d.ts +74 -0
- package/dist/bot/provider-shim.d.ts.map +1 -0
- package/dist/bot/provider-shim.js +176 -0
- package/dist/bot/provider-shim.js.map +1 -0
- package/dist/bot/runner.d.ts +2 -0
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +60 -17
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +72 -115
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/swarm-controller.d.ts +2 -0
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +92 -20
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-create-handler.d.ts +37 -0
- package/dist/bot/task-create-handler.d.ts.map +1 -0
- package/dist/bot/task-create-handler.js +124 -0
- package/dist/bot/task-create-handler.js.map +1 -0
- package/dist/bot/task-store.d.ts +1 -0
- package/dist/bot/task-store.d.ts.map +1 -1
- package/dist/bot/task-store.js +67 -0
- package/dist/bot/task-store.js.map +1 -1
- package/dist/bot/types.d.ts +1 -1
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts.map +1 -1
- package/dist/bot/weaver-tools.js +7 -39
- package/dist/bot/weaver-tools.js.map +1 -1
- package/dist/node-types/agent-execute.d.ts +25 -8
- package/dist/node-types/agent-execute.d.ts.map +1 -1
- package/dist/node-types/agent-execute.js +89 -23
- package/dist/node-types/agent-execute.js.map +1 -1
- package/dist/node-types/bot-report.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +24 -3
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/plan-task.d.ts +8 -17
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +217 -256
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/node-types/review-result.js +8 -6
- package/dist/node-types/review-result.js.map +1 -1
- package/dist/palindrome.d.ts +9 -0
- package/dist/palindrome.d.ts.map +1 -0
- package/dist/palindrome.js +14 -0
- package/dist/palindrome.js.map +1 -0
- package/dist/ui/approval-card.js +91 -82
- package/dist/ui/bot-activity.js +73 -56
- package/dist/ui/bot-config.js +48 -31
- package/dist/ui/bot-dashboard.js +52 -36
- package/dist/ui/bot-panel.js +230 -228
- package/dist/ui/bot-slot-card.js +100 -90
- package/dist/ui/bot-status.js +37 -15
- package/dist/ui/budget-bar.js +57 -31
- package/dist/ui/capability-editor.js +447 -378
- package/dist/ui/chat-task-result.js +78 -71
- package/dist/ui/decision-log.js +68 -81
- package/dist/ui/genesis-block.js +86 -95
- package/dist/ui/instance-stream-view.js +722 -0
- package/dist/ui/profile-card.js +96 -221
- package/dist/ui/profile-editor.js +532 -575
- package/dist/ui/settings-section.js +41 -45
- package/dist/ui/swarm-controls.js +212 -135
- package/dist/ui/swarm-dashboard.js +3992 -2715
- package/dist/ui/task-detail-view.js +415 -521
- package/dist/ui/task-editor.js +339 -390
- package/dist/ui/task-pool-list.js +60 -55
- package/dist/workflows/src/palindrome.d.ts +11 -0
- package/dist/workflows/src/palindrome.d.ts.map +1 -0
- package/dist/workflows/src/palindrome.js +16 -0
- package/dist/workflows/src/palindrome.js.map +1 -0
- package/dist/workflows/tests/palindrome.test.d.ts +2 -0
- package/dist/workflows/tests/palindrome.test.d.ts.map +1 -0
- package/dist/workflows/tests/palindrome.test.js +41 -0
- package/dist/workflows/tests/palindrome.test.js.map +1 -0
- package/dist/workflows/weaver-bot-batch.js +1 -1
- package/dist/workflows/weaver-bot-batch.js.map +1 -1
- package/dist/workflows/weaver-bot.js +1 -1
- package/dist/workflows/weaver-bot.js.map +1 -1
- package/flowweaver.manifest.json +1 -1
- package/package.json +8 -2
- package/src/ai-chat-provider.ts +5 -5
- package/src/bot/acceptance-merge.ts +62 -0
- package/src/bot/ai-client.ts +77 -21
- package/src/bot/assistant-tools.ts +3 -3
- package/src/bot/audit-logger.ts +42 -14
- package/src/bot/audit-trail.ts +211 -0
- package/src/bot/behavior-defaults.ts +7 -2
- package/src/bot/capability-registry.ts +84 -28
- package/src/bot/capability-types.ts +11 -0
- package/src/bot/cli-provider.ts +8 -7
- package/src/bot/preflight.ts +285 -0
- package/src/bot/provider-shim.ts +218 -0
- package/src/bot/runner.ts +68 -20
- package/src/bot/step-executor.ts +69 -127
- package/src/bot/swarm-controller.ts +94 -20
- package/src/bot/task-create-handler.ts +164 -0
- package/src/bot/task-store.ts +83 -0
- package/src/bot/types.ts +4 -1
- package/src/bot/weaver-tools.ts +7 -45
- package/src/node-types/agent-execute.ts +102 -16
- package/src/node-types/bot-report.ts +24 -3
- package/src/node-types/plan-task.ts +238 -280
- package/src/node-types/review-result.ts +8 -6
- package/src/palindrome.ts +14 -0
- package/src/ui/approval-card.tsx +78 -62
- package/src/ui/bot-activity.tsx +12 -10
- package/src/ui/bot-config.tsx +12 -10
- package/src/ui/bot-dashboard.tsx +13 -11
- package/src/ui/bot-panel.tsx +189 -171
- package/src/ui/bot-slot-card.tsx +125 -70
- package/src/ui/bot-status.tsx +4 -4
- package/src/ui/budget-bar.tsx +86 -25
- package/src/ui/capability-editor.tsx +392 -257
- package/src/ui/chat-task-result.tsx +81 -78
- package/src/ui/decision-log.tsx +76 -73
- package/src/ui/genesis-block.tsx +91 -61
- package/src/ui/instance-stream-view.tsx +861 -0
- package/src/ui/profile-card.tsx +195 -168
- package/src/ui/profile-editor.tsx +453 -370
- package/src/ui/settings-section.tsx +46 -39
- package/src/ui/swarm-controls.tsx +252 -123
- package/src/ui/swarm-dashboard.tsx +999 -466
- package/src/ui/task-detail-view.tsx +485 -428
- package/src/ui/task-editor.tsx +329 -271
- package/src/ui/task-pool-list.tsx +68 -62
- package/src/workflows/src/palindrome.ts +16 -0
- package/src/workflows/tests/palindrome.test.ts +49 -0
- package/src/workflows/weaver-bot-batch.ts +1 -1
- package/src/workflows/weaver-bot.ts +1 -1
- package/dist/ui/bot-constants.d.ts +0 -14
- package/dist/ui/bot-constants.d.ts.map +0 -1
- package/dist/ui/bot-constants.js +0 -189
- package/dist/ui/bot-constants.js.map +0 -1
- package/dist/ui/steer-api.d.ts +0 -7
- package/dist/ui/steer-api.d.ts.map +0 -1
- package/dist/ui/steer-api.js +0 -11
- package/dist/ui/steer-api.js.map +0 -1
- package/dist/ui/trace-to-timeline.d.ts +0 -91
- package/dist/ui/trace-to-timeline.d.ts.map +0 -1
- package/dist/ui/trace-to-timeline.js +0 -116
- package/dist/ui/trace-to-timeline.js.map +0 -1
- package/dist/ui/use-stream-timeline.d.ts +0 -50
- package/dist/ui/use-stream-timeline.d.ts.map +0 -1
- package/dist/ui/use-stream-timeline.js +0 -245
- package/dist/ui/use-stream-timeline.js.map +0 -1
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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":"
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
267
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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) };
|