@covibes/zeroshot 5.3.0 → 5.4.1

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 (119) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -385
  3. package/cli/index.js +108 -4615
  4. package/package.json +16 -125
  5. package/CHANGELOG.md +0 -537
  6. package/cli/commands/providers.js +0 -150
  7. package/cli/lib/first-run.js +0 -211
  8. package/cli/lib/update-checker.js +0 -281
  9. package/cli/message-formatter-utils.js +0 -75
  10. package/cli/message-formatters-normal.js +0 -253
  11. package/cli/message-formatters-watch.js +0 -181
  12. package/cluster-templates/base-templates/debug-workflow.json +0 -375
  13. package/cluster-templates/base-templates/full-workflow.json +0 -697
  14. package/cluster-templates/base-templates/single-worker.json +0 -63
  15. package/cluster-templates/base-templates/worker-validator.json +0 -152
  16. package/cluster-templates/conductor-bootstrap.json +0 -124
  17. package/docker/zeroshot-cluster/Dockerfile +0 -152
  18. package/docker/zeroshot-cluster/pre-baked-deps.json +0 -28
  19. package/lib/completion.js +0 -174
  20. package/lib/docker-config.js +0 -212
  21. package/lib/id-detector.js +0 -53
  22. package/lib/provider-detection.js +0 -59
  23. package/lib/provider-names.js +0 -56
  24. package/lib/settings.js +0 -405
  25. package/lib/stream-json-parser.js +0 -10
  26. package/scripts/fix-node-pty-permissions.js +0 -75
  27. package/scripts/record-demo.sh +0 -279
  28. package/scripts/test-install.sh +0 -40
  29. package/scripts/validate-templates.js +0 -100
  30. package/src/agent/agent-config.js +0 -193
  31. package/src/agent/agent-context-builder.js +0 -335
  32. package/src/agent/agent-hook-executor.js +0 -402
  33. package/src/agent/agent-lifecycle.js +0 -683
  34. package/src/agent/agent-stuck-detector.js +0 -256
  35. package/src/agent/agent-task-executor.js +0 -1431
  36. package/src/agent/agent-trigger-evaluator.js +0 -67
  37. package/src/agent/output-extraction.js +0 -200
  38. package/src/agent/output-reformatter.js +0 -175
  39. package/src/agent/schema-utils.js +0 -111
  40. package/src/agent-wrapper.js +0 -570
  41. package/src/agents/git-pusher-agent.json +0 -20
  42. package/src/attach/attach-client.js +0 -438
  43. package/src/attach/attach-server.js +0 -543
  44. package/src/attach/index.js +0 -35
  45. package/src/attach/protocol.js +0 -220
  46. package/src/attach/ring-buffer.js +0 -121
  47. package/src/attach/socket-discovery.js +0 -267
  48. package/src/claude-task-runner.js +0 -523
  49. package/src/config-router.js +0 -80
  50. package/src/config-validator.js +0 -1660
  51. package/src/copy-worker.js +0 -43
  52. package/src/github.js +0 -139
  53. package/src/isolation-manager.js +0 -1381
  54. package/src/ledger.js +0 -614
  55. package/src/logic-engine.js +0 -223
  56. package/src/message-bus-bridge.js +0 -139
  57. package/src/message-bus.js +0 -249
  58. package/src/name-generator.js +0 -232
  59. package/src/orchestrator.js +0 -2519
  60. package/src/preflight.js +0 -485
  61. package/src/process-metrics.js +0 -566
  62. package/src/providers/anthropic/cli-builder.js +0 -45
  63. package/src/providers/anthropic/index.js +0 -134
  64. package/src/providers/anthropic/models.js +0 -23
  65. package/src/providers/anthropic/output-parser.js +0 -159
  66. package/src/providers/base-provider.js +0 -181
  67. package/src/providers/capabilities.js +0 -51
  68. package/src/providers/google/cli-builder.js +0 -55
  69. package/src/providers/google/index.js +0 -116
  70. package/src/providers/google/models.js +0 -24
  71. package/src/providers/google/output-parser.js +0 -92
  72. package/src/providers/index.js +0 -75
  73. package/src/providers/openai/cli-builder.js +0 -122
  74. package/src/providers/openai/index.js +0 -135
  75. package/src/providers/openai/models.js +0 -21
  76. package/src/providers/openai/output-parser.js +0 -129
  77. package/src/schemas/sub-cluster.js +0 -156
  78. package/src/status-footer.js +0 -980
  79. package/src/sub-cluster-wrapper.js +0 -560
  80. package/src/task-runner.js +0 -30
  81. package/src/template-resolver.js +0 -369
  82. package/src/tui/CHANGES.txt +0 -133
  83. package/src/tui/LAYOUT.md +0 -261
  84. package/src/tui/README.txt +0 -192
  85. package/src/tui/TWO-LEVEL-NAVIGATION.md +0 -186
  86. package/src/tui/data-poller.js +0 -325
  87. package/src/tui/demo.js +0 -208
  88. package/src/tui/formatters.js +0 -128
  89. package/src/tui/index.js +0 -193
  90. package/src/tui/keybindings.js +0 -383
  91. package/src/tui/layout.js +0 -334
  92. package/src/tui/renderer.js +0 -194
  93. package/task-lib/attachable-watcher.js +0 -272
  94. package/task-lib/claude-recovery.js +0 -119
  95. package/task-lib/commands/clean.js +0 -50
  96. package/task-lib/commands/get-log-path.js +0 -23
  97. package/task-lib/commands/kill.js +0 -32
  98. package/task-lib/commands/list.js +0 -105
  99. package/task-lib/commands/logs.js +0 -411
  100. package/task-lib/commands/resume.js +0 -42
  101. package/task-lib/commands/run.js +0 -57
  102. package/task-lib/commands/schedule.js +0 -105
  103. package/task-lib/commands/scheduler-cmd.js +0 -96
  104. package/task-lib/commands/schedules.js +0 -98
  105. package/task-lib/commands/status.js +0 -44
  106. package/task-lib/commands/unschedule.js +0 -16
  107. package/task-lib/completion.js +0 -9
  108. package/task-lib/config.js +0 -10
  109. package/task-lib/name-generator.js +0 -230
  110. package/task-lib/package.json +0 -3
  111. package/task-lib/runner.js +0 -144
  112. package/task-lib/scheduler.js +0 -252
  113. package/task-lib/store.js +0 -230
  114. package/task-lib/tui/formatters.js +0 -166
  115. package/task-lib/tui/index.js +0 -197
  116. package/task-lib/tui/layout.js +0 -111
  117. package/task-lib/tui/renderer.js +0 -119
  118. package/task-lib/tui.js +0 -384
  119. package/task-lib/watcher.js +0 -188
@@ -1,402 +0,0 @@
1
- /**
2
- * AgentHookExecutor - Hook transformation and execution
3
- *
4
- * Provides:
5
- * - Hook execution (publish_message, stop_cluster, etc.)
6
- * - Template variable substitution
7
- * - Transform script execution in VM sandbox
8
- */
9
-
10
- const vm = require('vm');
11
-
12
- /**
13
- * Execute a hook
14
- * THROWS on failure - no silent errors
15
- * @param {Object} params - Hook execution parameters
16
- * @param {Object} params.hook - Hook configuration
17
- * @param {Object} params.agent - Agent instance
18
- * @param {Object} params.message - Triggering message
19
- * @param {Object} params.result - Agent execution result
20
- * @param {Object} params.messageBus - Message bus instance
21
- * @param {Object} params.cluster - Cluster object
22
- * @param {Object} params.orchestrator - Orchestrator instance
23
- * @returns {Promise<void>}
24
- */
25
- async function executeHook(params) {
26
- const { hook, agent, message, result, cluster } = params;
27
-
28
- if (!hook) {
29
- return;
30
- }
31
-
32
- // Build context for hook execution
33
- const context = {
34
- result,
35
- triggeringMessage: message,
36
- agent,
37
- cluster,
38
- };
39
-
40
- // NO try/catch - errors must propagate
41
- if (hook.action === 'publish_message') {
42
- let messageToPublish;
43
-
44
- if (hook.transform) {
45
- // NEW: Execute transform script to generate message
46
- messageToPublish = await executeTransform({
47
- transform: hook.transform,
48
- context,
49
- agent,
50
- });
51
- } else {
52
- // Existing: Use template substitution
53
- messageToPublish = await substituteTemplate({
54
- config: hook.config,
55
- context,
56
- agent,
57
- cluster,
58
- });
59
- }
60
-
61
- // Publish via agent's _publish method
62
- agent._publish(messageToPublish);
63
- } else if (hook.action === 'execute_system_command') {
64
- throw new Error('execute_system_command not implemented');
65
- } else {
66
- throw new Error(`Unknown hook action: ${hook.action}`);
67
- }
68
- }
69
-
70
- /**
71
- * Execute a hook transform script
72
- * Transform scripts return the message to publish, with access to:
73
- * - result: parsed agent output
74
- * - triggeringMessage: the message that triggered the agent
75
- * - helpers: { getConfig(complexity, taskType) }
76
- * @param {Object} params - Transform parameters
77
- * @param {Object} params.transform - Transform configuration
78
- * @param {Object} params.context - Execution context
79
- * @param {Object} params.agent - Agent instance
80
- * @returns {Promise<Object>} Message to publish
81
- */
82
- async function executeTransform(params) {
83
- const { transform, context, agent } = params;
84
- const { engine, script } = transform;
85
-
86
- if (engine !== 'javascript') {
87
- throw new Error(`Unsupported transform engine: ${engine}`);
88
- }
89
-
90
- // Parse result output if we have a result
91
- // VALIDATION: Check if script uses result.* variables and fail early if no output
92
- const scriptUsesResult = /\bresult\.[a-zA-Z]/.test(script);
93
- let resultData = null;
94
-
95
- if (context.result?.output) {
96
- try {
97
- resultData = await agent._parseResultOutput(context.result.output);
98
- } catch (parseError) {
99
- // FAIL FAST: Result parsing failed - don't continue with null data
100
- const taskId = context.result?.taskId || agent.currentTaskId || 'UNKNOWN';
101
- console.error(`\n${'='.repeat(80)}`);
102
- console.error(`🔴 TRANSFORM SCRIPT BLOCKED - RESULT PARSING FAILED`);
103
- console.error(`${'='.repeat(80)}`);
104
- console.error(`Agent: ${agent.id}, Role: ${agent.role}`);
105
- console.error(`TaskID: ${taskId}`);
106
- console.error(`Parse error: ${parseError.message}`);
107
- console.error(`Output (last 500 chars): ${(context.result.output || '').slice(-500)}`);
108
- console.error(`${'='.repeat(80)}\n`);
109
- throw new Error(
110
- `Transform script cannot run: result parsing failed. ` +
111
- `Agent: ${agent.id}, Error: ${parseError.message}`
112
- );
113
- }
114
-
115
- // DEFENSIVE: Validate result has expected fields if script accesses them
116
- // Extract field names from script (e.g., result.complexity, result.taskType)
117
- const accessedFields = [...script.matchAll(/result\.([a-zA-Z_]+)/g)].map((m) => m[1]);
118
- const missingFields = accessedFields.filter((f) => resultData[f] === undefined);
119
- if (missingFields.length > 0) {
120
- const taskId = context.result?.taskId || agent.currentTaskId || 'UNKNOWN';
121
- console.error(`\n${'='.repeat(80)}`);
122
- console.error(`🔴 TRANSFORM SCRIPT BLOCKED - MISSING REQUIRED FIELDS`);
123
- console.error(`${'='.repeat(80)}`);
124
- console.error(`Agent: ${agent.id}, Role: ${agent.role}, TaskID: ${taskId}`);
125
- console.error(`Script accesses: ${accessedFields.join(', ')}`);
126
- console.error(`Missing from result: ${missingFields.join(', ')}`);
127
- console.error(`Result keys: ${Object.keys(resultData).join(', ')}`);
128
- console.error(`Result data: ${JSON.stringify(resultData, null, 2)}`);
129
- console.error(`${'='.repeat(80)}\n`);
130
- throw new Error(
131
- `Transform script accesses undefined fields: ${missingFields.join(', ')}. ` +
132
- `Agent ${agent.id} (task ${taskId}) output missing required fields. ` +
133
- `Check agent's jsonSchema and output format.`
134
- );
135
- }
136
- } else if (scriptUsesResult) {
137
- const taskId = context.result?.taskId || agent.currentTaskId || 'UNKNOWN';
138
- const outputLength = (context.result?.output || '').length;
139
- throw new Error(
140
- `Transform script uses result.* variables but no output was captured. ` +
141
- `Agent: ${agent.id}, TaskID: ${taskId}, Iteration: ${agent.iteration}, ` +
142
- `Output length: ${outputLength}. ` +
143
- `Check that the task completed successfully and the get-log-path command exists.`
144
- );
145
- }
146
-
147
- // Helper functions exposed to transform scripts
148
- const helpers = {
149
- /**
150
- * Get cluster config based on complexity and task type
151
- * Returns: { base: 'template-name', params: { ... } }
152
- */
153
- getConfig: require('../config-router').getConfig,
154
- };
155
-
156
- // Build sandbox context
157
- const sandbox = {
158
- result: resultData,
159
- triggeringMessage: context.triggeringMessage,
160
- helpers,
161
- JSON,
162
- console: {
163
- log: (...args) => agent._log('[transform]', ...args),
164
- error: (...args) => console.error('[transform]', ...args),
165
- warn: (...args) => console.warn('[transform]', ...args),
166
- },
167
- };
168
-
169
- // Execute in VM sandbox with timeout
170
- const vmContext = vm.createContext(sandbox);
171
- const wrappedScript = `(function() { ${script} })()`;
172
-
173
- let result;
174
- try {
175
- result = vm.runInContext(wrappedScript, vmContext, { timeout: 5000 });
176
- } catch (err) {
177
- throw new Error(`Transform script error: ${err.message}`);
178
- }
179
-
180
- // Validate result
181
- if (!result || typeof result !== 'object') {
182
- throw new Error(
183
- `Transform script must return an object with topic and content, got: ${typeof result}`
184
- );
185
- }
186
- if (!result.topic) {
187
- throw new Error(`Transform script result must have a 'topic' property`);
188
- }
189
- if (!result.content) {
190
- throw new Error(`Transform script result must have a 'content' property`);
191
- }
192
-
193
- // CRITICAL: Extra validation for CLUSTER_OPERATIONS - this is the make-or-break message
194
- // If this message is malformed, the cluster will hang forever
195
- if (result.topic === 'CLUSTER_OPERATIONS') {
196
- const operations = result.content?.data?.operations;
197
- if (!operations) {
198
- console.error(`\n${'='.repeat(80)}`);
199
- console.error(`🔴 CLUSTER_OPERATIONS MALFORMED - MISSING OPERATIONS ARRAY`);
200
- console.error(`${'='.repeat(80)}`);
201
- console.error(`Agent: ${agent.id}`);
202
- console.error(`Result: ${JSON.stringify(result, null, 2)}`);
203
- console.error(`${'='.repeat(80)}\n`);
204
- throw new Error(
205
- `CLUSTER_OPERATIONS message missing operations array. ` +
206
- `Agent ${agent.id} transform script returned invalid structure.`
207
- );
208
- }
209
- if (!Array.isArray(operations)) {
210
- throw new Error(`CLUSTER_OPERATIONS.operations must be an array, got: ${typeof operations}`);
211
- }
212
- if (operations.length === 0) {
213
- throw new Error(`CLUSTER_OPERATIONS.operations is empty - no operations to execute`);
214
- }
215
-
216
- // Validate each operation has required 'action' field
217
- for (let i = 0; i < operations.length; i++) {
218
- const op = operations[i];
219
- if (!op || !op.action) {
220
- throw new Error(`CLUSTER_OPERATIONS.operations[${i}] missing required 'action' field`);
221
- }
222
- }
223
-
224
- agent._log(`✅ CLUSTER_OPERATIONS validated: ${operations.length} operations`);
225
- }
226
-
227
- return result;
228
- }
229
-
230
- /**
231
- * Substitute template variables in hook config
232
- * ONLY parses result output if result.* variables are used
233
- * THROWS on any error - no silent failures
234
- * @param {Object} params - Substitution parameters
235
- * @param {Object} params.config - Hook configuration
236
- * @param {Object} params.context - Execution context
237
- * @param {Object} params.agent - Agent instance
238
- * @param {Object} params.cluster - Cluster object
239
- * @returns {Promise<Object>} Substituted configuration
240
- */
241
- async function substituteTemplate(params) {
242
- const { config, context, agent, cluster } = params;
243
-
244
- if (!config) {
245
- throw new Error('_substituteTemplate: config is required');
246
- }
247
-
248
- const json = JSON.stringify(config);
249
-
250
- // Check if ANY result.* variables are used BEFORE parsing
251
- // Generic pattern - no hardcoded field names, works with any agent config
252
- const usesResultVars = /\{\{result\.[^}]+\}\}/.test(json);
253
-
254
- let resultData = null;
255
- if (usesResultVars) {
256
- if (!context.result) {
257
- throw new Error(
258
- `Hook uses result.* variables but no result in context. ` +
259
- `Agent: ${agent.id}, TaskID: ${agent.currentTaskId}, Iteration: ${agent.iteration}`
260
- );
261
- }
262
- if (!context.result.output) {
263
- // Log detailed context for debugging
264
- const taskId = context.result.taskId || agent.currentTaskId || 'UNKNOWN';
265
- console.error(`\n${'='.repeat(80)}`);
266
- console.error(`🔴 HOOK FAILURE - EMPTY OUTPUT`);
267
- console.error(`${'='.repeat(80)}`);
268
- console.error(`Agent: ${agent.id}`);
269
- console.error(`Task ID: ${taskId}`);
270
- console.error(`Iteration: ${context.result.iteration || agent.iteration}`);
271
- console.error(`Result success: ${context.result.success}`);
272
- console.error(`Result error: ${context.result.error || 'none'}`);
273
- console.error(`Output length: ${(context.result.output || '').length}`);
274
- console.error(`Hook config: ${JSON.stringify(config, null, 2)}`);
275
-
276
- // Auto-fetch and publish task logs for debugging
277
- let taskLogs = 'Task logs unavailable';
278
- if (taskId !== 'UNKNOWN') {
279
- console.error(`\nFetching task logs for ${taskId}...`);
280
- try {
281
- const { execSync } = require('child_process');
282
- const ctPath = agent._getClaudeTasksPath();
283
- taskLogs = execSync(`${ctPath} logs ${taskId} --lines 100`, {
284
- encoding: 'utf-8',
285
- timeout: 5000,
286
- maxBuffer: 1024 * 1024, // 1MB
287
- }).trim();
288
- console.error(`✓ Retrieved ${taskLogs.split('\n').length} lines of logs`);
289
- } catch (err) {
290
- taskLogs = `Failed to retrieve logs: ${err.message}`;
291
- console.error(`✗ Failed to retrieve logs: ${err.message}`);
292
- }
293
- }
294
- console.error(`${'='.repeat(80)}\n`);
295
-
296
- // Publish task logs to message bus for visibility in zeroshot logs
297
- agent._publish({
298
- topic: 'AGENT_ERROR',
299
- receiver: 'broadcast',
300
- content: {
301
- text: `Task logs for ${taskId} (last 100 lines)`,
302
- data: {
303
- taskId,
304
- logs: taskLogs,
305
- logsPreview: taskLogs.split('\n').slice(-20).join('\n'), // Last 20 lines as preview
306
- },
307
- },
308
- });
309
-
310
- throw new Error(
311
- `Hook uses result.* variables but result.output is empty. ` +
312
- `Agent: ${agent.id}, TaskID: ${taskId}, ` +
313
- `Iteration: ${context.result.iteration || agent.iteration}, ` +
314
- `Success: ${context.result.success}. ` +
315
- `Task logs posted to message bus.`
316
- );
317
- }
318
- // Parse result output - WILL THROW if no JSON block
319
- resultData = await agent._parseResultOutput(context.result.output);
320
- }
321
-
322
- // Helper to escape a value for JSON string substitution
323
- // Uses JSON.stringify for ALL escaping - no manual replace() calls
324
- const escapeForJsonString = (value) => {
325
- if (value === null || value === undefined) {
326
- throw new Error(`Cannot escape null/undefined value for JSON`);
327
- }
328
- // JSON.stringify handles ALL escaping (newlines, quotes, backslashes, control chars)
329
- // .slice(1, -1) strips the outer quotes it adds
330
- // For arrays/objects: stringify twice - once for JSON, once to escape for string embedding
331
- const stringified =
332
- typeof value === 'string' ? JSON.stringify(value) : JSON.stringify(JSON.stringify(value));
333
- return stringified.slice(1, -1);
334
- };
335
-
336
- let substituted = json
337
- .replace(/\{\{cluster\.id\}\}/g, cluster.id)
338
- .replace(/\{\{cluster\.createdAt\}\}/g, String(cluster.createdAt))
339
- .replace(/\{\{iteration\}\}/g, String(agent.iteration))
340
- .replace(/\{\{error\.message\}\}/g, escapeForJsonString(context.error?.message ?? ''))
341
- .replace(/\{\{result\.output\}\}/g, escapeForJsonString(context.result?.output ?? ''));
342
-
343
- // Substitute ALL result.* variables dynamically from parsed resultData
344
- if (resultData) {
345
- // Generic substitution - replace {{result.fieldName}} with resultData[fieldName]
346
- // No hardcoded field names - works with any agent output schema
347
- // CRITICAL: For booleans/nulls/numbers, we need to match and remove surrounding quotes
348
- // to produce valid JSON (e.g., "{{result.approved}}" -> true, not "true")
349
- substituted = substituted.replace(/"?\{\{result\.([^}]+)\}\}"?/g, (match, fieldName) => {
350
- const value = resultData[fieldName];
351
- if (value === undefined) {
352
- // Missing fields should gracefully default to null or empty values
353
- // This allows optional schema fields without hardcoding field names
354
- // If a field is truly required, the schema validation will catch it
355
- console.warn(
356
- `⚠️ Agent ${agent.id}: Template variable {{result.${fieldName}}} not found in output. ` +
357
- `If this field is required by the schema, the agent violated its own schema. ` +
358
- `Defaulting to null. Agent output keys: ${Object.keys(resultData).join(', ')}`
359
- );
360
- return 'null';
361
- }
362
- // Booleans, numbers, and null should be unquoted JSON primitives
363
- if (typeof value === 'boolean' || typeof value === 'number' || value === null) {
364
- return String(value);
365
- }
366
- // Strings need to be quoted and escaped for JSON
367
- return JSON.stringify(value);
368
- });
369
- }
370
-
371
- // Check for unsubstituted KNOWN template variables only
372
- // KNOWN patterns: {{cluster.X}}, {{iteration}}, {{error.X}}, {{result.X}}
373
- // Content may contain arbitrary {{...}} patterns (React dangerouslySetInnerHTML, Mustache, etc.)
374
- // Those are NOT template variables - they're just content that happens to contain braces
375
- const KNOWN_TEMPLATE_PREFIXES = ['cluster', 'iteration', 'error', 'result'];
376
- const knownVariablePattern = new RegExp(
377
- `\\{\\{(${KNOWN_TEMPLATE_PREFIXES.join('|')})(\\.[a-zA-Z_][a-zA-Z0-9_]*)?\\}\\}`,
378
- 'g'
379
- );
380
- const remaining = substituted.match(knownVariablePattern);
381
- if (remaining) {
382
- throw new Error(`Unsubstituted template variables: ${remaining.join(', ')}`);
383
- }
384
-
385
- // Parse and validate result
386
- let result;
387
- try {
388
- result = JSON.parse(substituted);
389
- } catch (e) {
390
- console.error('JSON parse failed. Substituted string:');
391
- console.error(substituted);
392
- throw new Error(`Template substitution produced invalid JSON: ${e.message}`);
393
- }
394
-
395
- return result;
396
- }
397
-
398
- module.exports = {
399
- executeHook,
400
- executeTransform,
401
- substituteTemplate,
402
- };