@covibes/zeroshot 5.2.1 → 5.4.0

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 (98) hide show
  1. package/CHANGELOG.md +174 -189
  2. package/README.md +226 -195
  3. package/cli/commands/providers.js +149 -0
  4. package/cli/index.js +3145 -2366
  5. package/cli/lib/first-run.js +40 -3
  6. package/cli/message-formatters-normal.js +28 -6
  7. package/cluster-templates/base-templates/debug-workflow.json +24 -78
  8. package/cluster-templates/base-templates/full-workflow.json +99 -316
  9. package/cluster-templates/base-templates/single-worker.json +23 -15
  10. package/cluster-templates/base-templates/worker-validator.json +105 -36
  11. package/cluster-templates/conductor-bootstrap.json +9 -7
  12. package/lib/docker-config.js +14 -1
  13. package/lib/git-remote-utils.js +165 -0
  14. package/lib/id-detector.js +10 -7
  15. package/lib/provider-defaults.js +62 -0
  16. package/lib/provider-detection.js +59 -0
  17. package/lib/provider-names.js +57 -0
  18. package/lib/settings/claude-auth.js +78 -0
  19. package/lib/settings.js +298 -15
  20. package/lib/stream-json-parser.js +4 -238
  21. package/package.json +27 -6
  22. package/scripts/setup-merge-queue.sh +170 -0
  23. package/scripts/validate-templates.js +100 -0
  24. package/src/agent/agent-config.js +140 -63
  25. package/src/agent/agent-context-builder.js +336 -165
  26. package/src/agent/agent-hook-executor.js +337 -67
  27. package/src/agent/agent-lifecycle.js +386 -287
  28. package/src/agent/agent-stuck-detector.js +7 -7
  29. package/src/agent/agent-task-executor.js +944 -683
  30. package/src/agent/output-extraction.js +217 -0
  31. package/src/agent/output-reformatter.js +175 -0
  32. package/src/agent/schema-utils.js +146 -0
  33. package/src/agent-wrapper.js +112 -31
  34. package/src/agents/git-pusher-template.js +285 -0
  35. package/src/claude-task-runner.js +145 -44
  36. package/src/config-router.js +13 -13
  37. package/src/config-validator.js +1049 -563
  38. package/src/input-helpers.js +65 -0
  39. package/src/isolation-manager.js +499 -320
  40. package/src/issue-providers/README.md +305 -0
  41. package/src/issue-providers/azure-devops-provider.js +273 -0
  42. package/src/issue-providers/base-provider.js +232 -0
  43. package/src/issue-providers/github-provider.js +179 -0
  44. package/src/issue-providers/gitlab-provider.js +241 -0
  45. package/src/issue-providers/index.js +196 -0
  46. package/src/issue-providers/jira-provider.js +239 -0
  47. package/src/ledger.js +50 -11
  48. package/src/lib/safe-exec.js +88 -0
  49. package/src/orchestrator.js +1348 -757
  50. package/src/preflight.js +306 -149
  51. package/src/process-metrics.js +98 -56
  52. package/src/providers/anthropic/cli-builder.js +73 -0
  53. package/src/providers/anthropic/index.js +204 -0
  54. package/src/providers/anthropic/models.js +23 -0
  55. package/src/providers/anthropic/output-parser.js +177 -0
  56. package/src/providers/base-provider.js +251 -0
  57. package/src/providers/capabilities.js +60 -0
  58. package/src/providers/google/cli-builder.js +55 -0
  59. package/src/providers/google/index.js +116 -0
  60. package/src/providers/google/models.js +24 -0
  61. package/src/providers/google/output-parser.js +101 -0
  62. package/src/providers/index.js +91 -0
  63. package/src/providers/openai/cli-builder.js +133 -0
  64. package/src/providers/openai/index.js +136 -0
  65. package/src/providers/openai/models.js +21 -0
  66. package/src/providers/openai/output-parser.js +143 -0
  67. package/src/providers/opencode/cli-builder.js +42 -0
  68. package/src/providers/opencode/index.js +103 -0
  69. package/src/providers/opencode/models.js +55 -0
  70. package/src/providers/opencode/output-parser.js +122 -0
  71. package/src/schemas/sub-cluster.js +68 -39
  72. package/src/status-footer.js +94 -48
  73. package/src/sub-cluster-wrapper.js +92 -36
  74. package/src/task-runner.js +8 -6
  75. package/src/template-resolver.js +12 -9
  76. package/src/tui/data-poller.js +123 -99
  77. package/src/tui/formatters.js +4 -3
  78. package/src/tui/keybindings.js +259 -318
  79. package/src/tui/layout.js +20 -3
  80. package/src/tui/renderer.js +11 -21
  81. package/task-lib/attachable-watcher.js +150 -111
  82. package/task-lib/claude-recovery.js +156 -0
  83. package/task-lib/commands/episodes.js +105 -0
  84. package/task-lib/commands/list.js +3 -3
  85. package/task-lib/commands/logs.js +231 -189
  86. package/task-lib/commands/resume.js +3 -2
  87. package/task-lib/commands/run.js +12 -3
  88. package/task-lib/commands/schedules.js +111 -61
  89. package/task-lib/config.js +0 -2
  90. package/task-lib/runner.js +128 -50
  91. package/task-lib/scheduler.js +3 -3
  92. package/task-lib/store.js +464 -152
  93. package/task-lib/tui/formatters.js +94 -45
  94. package/task-lib/tui/renderer.js +13 -3
  95. package/task-lib/tui.js +73 -32
  96. package/task-lib/watcher.js +157 -100
  97. package/src/agents/git-pusher-agent.json +0 -20
  98. package/src/github.js +0 -103
@@ -9,7 +9,9 @@
9
9
  * - maxModel ceiling enforcement at config time
10
10
  */
11
11
 
12
- const { loadSettings, validateModelAgainstMax } = require('../../lib/settings');
12
+ const { loadSettings, validateModelAgainstMax, VALID_MODELS } = require('../../lib/settings');
13
+
14
+ const VALID_LEVELS = ['level1', 'level2', 'level3'];
13
15
 
14
16
  // Default max iterations (high limit - let the user decide when to give up)
15
17
  const DEFAULT_MAX_ITERATIONS = 100;
@@ -25,21 +27,13 @@ const DEFAULT_TIMEOUT = 0;
25
27
  const DEFAULT_STALE_DURATION_MS = 30 * 60 * 1000; // 30 minutes before triggering analysis
26
28
  const DEFAULT_LIVENESS_CHECK_ENABLED = true; // Safe with multi-indicator detection
27
29
 
28
- /**
29
- * Validate and normalize agent configuration
30
- * @param {Object} config - Raw agent configuration
31
- * @param {Object} options - Agent wrapper options
32
- * @returns {Object} Normalized configuration
33
- */
34
- function validateAgentConfig(config, options = {}) {
30
+ function applyOutputDefaults(config) {
35
31
  // CRITICAL: Enforce JSON schema output by default to prevent parse failures and crashes
36
- // Agents MUST return structured output so hooks can safely use {{result.*}} templates
37
32
  if (!config.outputFormat) {
38
33
  config.outputFormat = 'json';
39
34
  }
40
35
 
41
36
  // If outputFormat is json but no schema defined, use a minimal default schema
42
- // This prevents uncaught exceptions when parsing agent output
43
37
  if (config.outputFormat === 'json' && !config.jsonSchema) {
44
38
  config.jsonSchema = {
45
39
  type: 'object',
@@ -56,99 +50,135 @@ function validateAgentConfig(config, options = {}) {
56
50
  required: ['summary', 'result'],
57
51
  };
58
52
  }
53
+ }
59
54
 
60
- // Model configuration: support both static model and dynamic rules
61
- // If no model specified, model is null - _selectModel() will use maxModel as default
62
- let modelConfig;
55
+ function buildModelConfig(config) {
63
56
  if (config.modelRules) {
64
- modelConfig = { type: 'rules', rules: config.modelRules };
65
- } else {
66
- modelConfig = { type: 'static', model: config.model || null };
57
+ return { type: 'rules', rules: config.modelRules };
67
58
  }
59
+ return {
60
+ type: 'static',
61
+ model: config.model || null,
62
+ modelLevel: config.modelLevel || null,
63
+ };
64
+ }
68
65
 
69
- // COST CEILING ENFORCEMENT: Validate model(s) against maxModel at config time
70
- // Catches violations EARLY (config load) instead of at runtime (iteration N)
71
- const settings = loadSettings();
72
- const maxModel = settings.maxModel || 'sonnet';
66
+ function applyStrictSchemaDefault(config, settings) {
67
+ if (config.strictSchema === undefined) {
68
+ config.strictSchema = settings.strictSchema !== false; // Default true if not set
69
+ }
70
+ }
73
71
 
74
- if (modelConfig.type === 'static' && modelConfig.model) {
75
- // Static model: validate once
72
+ function validateStaticModelConfig(configId, modelConfig, maxModel, minModel) {
73
+ if (modelConfig.model && VALID_MODELS.includes(modelConfig.model)) {
76
74
  try {
77
- validateModelAgainstMax(modelConfig.model, maxModel);
75
+ validateModelAgainstMax(modelConfig.model, maxModel, minModel);
78
76
  } catch (error) {
79
- throw new Error(`Agent "${config.id}": ${error.message}`);
77
+ throw new Error(`Agent "${configId}": ${error.message}`);
78
+ }
79
+ }
80
+
81
+ if (modelConfig.modelLevel && !VALID_LEVELS.includes(modelConfig.modelLevel)) {
82
+ throw new Error(
83
+ `Agent "${configId}": invalid modelLevel "${modelConfig.modelLevel}". ` +
84
+ `Valid: ${VALID_LEVELS.join(', ')}`
85
+ );
86
+ }
87
+ }
88
+
89
+ function validateModelRule(configId, rule, maxModel, minModel) {
90
+ if (rule.model && VALID_MODELS.includes(rule.model)) {
91
+ try {
92
+ validateModelAgainstMax(rule.model, maxModel, minModel);
93
+ } catch {
94
+ throw new Error(
95
+ `Agent "${configId}": modelRule "${rule.iterations}" requests "${rule.model}" ` +
96
+ `but maxModel is "${maxModel}"${minModel ? ` and minModel is "${minModel}"` : ''}. ` +
97
+ `Either adjust the rule's model or change maxModel/minModel settings.`
98
+ );
80
99
  }
81
- } else if (modelConfig.type === 'rules') {
82
- // Dynamic rules: validate ALL rules upfront (don't wait until iteration N)
100
+ }
101
+
102
+ if (rule.modelLevel && !VALID_LEVELS.includes(rule.modelLevel)) {
103
+ throw new Error(
104
+ `Agent "${configId}": modelRule "${rule.iterations}" has invalid modelLevel ` +
105
+ `"${rule.modelLevel}". Valid: ${VALID_LEVELS.join(', ')}`
106
+ );
107
+ }
108
+ }
109
+
110
+ function validateModelConfig(config, modelConfig, maxModel, minModel) {
111
+ if (modelConfig.type === 'static') {
112
+ validateStaticModelConfig(config.id, modelConfig, maxModel, minModel);
113
+ return;
114
+ }
115
+
116
+ if (modelConfig.type === 'rules') {
83
117
  for (const rule of modelConfig.rules) {
84
- if (rule.model) {
85
- try {
86
- validateModelAgainstMax(rule.model, maxModel);
87
- } catch {
88
- throw new Error(
89
- `Agent "${config.id}": modelRule "${rule.iterations}" requests "${rule.model}" ` +
90
- `but maxModel is "${maxModel}". Either lower the rule's model or raise maxModel.`
91
- );
92
- }
93
- }
118
+ validateModelRule(config.id, rule, maxModel, minModel);
94
119
  }
95
120
  }
121
+ }
96
122
 
97
- // Prompt configuration: support static prompt OR iteration-based rules
98
- // Formats:
99
- // prompt: "string" -> static
100
- // prompt: { system: "string" } -> static
101
- // prompt: { initial: "...", subsequent: "..." } -> iteration 1 vs 2+
102
- // prompt: { iterations: [...] } -> full control
103
- let promptConfig = null;
123
+ function buildPromptConfig(config) {
104
124
  if (config.prompt?.iterations) {
105
- promptConfig = { type: 'rules', rules: config.prompt.iterations };
106
- } else if (config.prompt?.initial || config.prompt?.subsequent) {
125
+ return { type: 'rules', rules: config.prompt.iterations };
126
+ }
127
+
128
+ if (config.prompt?.initial || config.prompt?.subsequent) {
107
129
  const rules = [];
108
130
  if (config.prompt.initial) rules.push({ match: '1', system: config.prompt.initial });
109
131
  if (config.prompt.subsequent) rules.push({ match: '2+', system: config.prompt.subsequent });
110
- promptConfig = { type: 'rules', rules };
111
- } else if (typeof config.prompt === 'string') {
112
- promptConfig = { type: 'static', system: config.prompt };
113
- } else if (config.prompt?.system) {
114
- promptConfig = { type: 'static', system: config.prompt.system };
115
- } else if (config.prompt) {
132
+ return { type: 'rules', rules };
133
+ }
134
+
135
+ if (typeof config.prompt === 'string') {
136
+ return { type: 'static', system: config.prompt };
137
+ }
138
+
139
+ if (config.prompt?.system) {
140
+ return { type: 'static', system: config.prompt.system };
141
+ }
142
+
143
+ if (config.prompt) {
116
144
  throw new Error(`Agent "${config.id}": invalid prompt format`);
117
145
  }
118
146
 
119
- // Default timeout to 0 (no timeout) if not specified
120
- // Use positive number for timeout in milliseconds
121
- // ROBUST: Handle undefined, null, AND string values from template resolution
147
+ return null;
148
+ }
149
+
150
+ function normalizeTimeout(config) {
122
151
  if (config.timeout === undefined || config.timeout === null || config.timeout === '') {
123
152
  config.timeout = DEFAULT_TIMEOUT;
124
153
  } else {
125
- // Coerce to number (handles string "0" from template resolution)
126
154
  config.timeout = Number(config.timeout);
127
155
  }
156
+
128
157
  if (!Number.isFinite(config.timeout) || config.timeout < 0) {
129
158
  throw new Error(
130
159
  `Agent "${config.id}": timeout must be a non-negative number (got ${config.timeout}).`
131
160
  );
132
161
  }
133
162
 
134
- // Build normalized config
135
- const normalizedConfig = {
163
+ return config.timeout;
164
+ }
165
+
166
+ function buildNormalizedConfig(config, modelConfig, promptConfig) {
167
+ return {
136
168
  ...config,
137
169
  modelConfig,
138
170
  promptConfig,
139
171
  maxIterations: config.maxIterations || DEFAULT_MAX_ITERATIONS,
140
- timeout: config.timeout, // Defaults to 0 (no timeout) if not specified
172
+ timeout: config.timeout,
141
173
  staleDuration: config.staleDuration || DEFAULT_STALE_DURATION_MS,
142
- enableLivenessCheck: config.enableLivenessCheck ?? DEFAULT_LIVENESS_CHECK_ENABLED, // On by default, opt-out with false
174
+ enableLivenessCheck: config.enableLivenessCheck ?? DEFAULT_LIVENESS_CHECK_ENABLED,
143
175
  };
176
+ }
144
177
 
145
- // SAFETY: In test mode, verify mock is provided for agents that execute tasks
146
- // Check if this agent executes tasks (vs orchestrator agents that only publish messages)
178
+ function assertTestModeSafety(config, options) {
147
179
  const executesTask = config.triggers?.some(
148
180
  (trigger) => !trigger.action || trigger.action === 'execute_task'
149
181
  );
150
-
151
- // Accept either mockSpawnFn OR taskRunner as valid mock sources
152
182
  const hasMock = options.mockSpawnFn || options.taskRunner;
153
183
 
154
184
  if (options.testMode && !hasMock && executesTask) {
@@ -157,6 +187,53 @@ function validateAgentConfig(config, options = {}) {
157
187
  `This would cause real Claude API calls. ABORTING.`
158
188
  );
159
189
  }
190
+ }
191
+
192
+ /**
193
+ * Validate and normalize agent configuration
194
+ * @param {Object} config - Raw agent configuration
195
+ * @param {Object} options - Agent wrapper options
196
+ * @returns {Object} Normalized configuration
197
+ */
198
+ function validateAgentConfig(config, options = {}) {
199
+ applyOutputDefaults(config);
200
+
201
+ // Model configuration: support both static model and dynamic rules
202
+ // If no model specified, model is null - _selectModel() will use provider defaults
203
+ const modelConfig = buildModelConfig(config);
204
+
205
+ // COST CEILING/FLOOR ENFORCEMENT: Validate model(s) against maxModel and minModel at config time
206
+ // Catches violations EARLY (config load) instead of at runtime (iteration N)
207
+ const settings = loadSettings();
208
+ const maxModel = settings.maxModel || 'sonnet';
209
+ const minModel = settings.minModel || null;
210
+
211
+ // STRICT SCHEMA PROPAGATION: Issue #52 fix
212
+ // If agent config doesn't explicitly set strictSchema, inherit from global settings
213
+ // This allows `zeroshot settings set strictSchema false` to actually affect agents
214
+ // Default behavior: strictSchema=true (reliable JSON output, no streaming)
215
+ applyStrictSchemaDefault(config, settings);
216
+ validateModelConfig(config, modelConfig, maxModel, minModel);
217
+
218
+ // Prompt configuration: support static prompt OR iteration-based rules
219
+ // Formats:
220
+ // prompt: "string" -> static
221
+ // prompt: { system: "string" } -> static
222
+ // prompt: { initial: "...", subsequent: "..." } -> iteration 1 vs 2+
223
+ // prompt: { iterations: [...] } -> full control
224
+ const promptConfig = buildPromptConfig(config);
225
+
226
+ // Default timeout to 0 (no timeout) if not specified
227
+ // Use positive number for timeout in milliseconds
228
+ // ROBUST: Handle undefined, null, AND string values from template resolution
229
+ normalizeTimeout(config);
230
+
231
+ // Build normalized config
232
+ const normalizedConfig = buildNormalizedConfig(config, modelConfig, promptConfig);
233
+
234
+ // SAFETY: In test mode, verify mock is provided for agents that execute tasks
235
+ // Check if this agent executes tasks (vs orchestrator agents that only publish messages)
236
+ assertTestModeSafety(config, options);
160
237
 
161
238
  return normalizedConfig;
162
239
  }