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