@covibes/zeroshot 5.4.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.
- package/LICENSE +1 -1
- package/README.md +8 -465
- package/cli/index.js +101 -5231
- package/package.json +16 -130
- package/CHANGELOG.md +0 -537
- package/cli/commands/providers.js +0 -149
- package/cli/lib/first-run.js +0 -211
- package/cli/lib/update-checker.js +0 -281
- package/cli/message-formatter-utils.js +0 -75
- package/cli/message-formatters-normal.js +0 -275
- package/cli/message-formatters-watch.js +0 -181
- package/cluster-templates/base-templates/debug-workflow.json +0 -375
- package/cluster-templates/base-templates/full-workflow.json +0 -581
- package/cluster-templates/base-templates/single-worker.json +0 -63
- package/cluster-templates/base-templates/worker-validator.json +0 -208
- package/cluster-templates/conductor-bootstrap.json +0 -124
- package/docker/zeroshot-cluster/Dockerfile +0 -152
- package/docker/zeroshot-cluster/pre-baked-deps.json +0 -28
- package/lib/completion.js +0 -174
- package/lib/docker-config.js +0 -220
- package/lib/git-remote-utils.js +0 -165
- package/lib/id-detector.js +0 -56
- package/lib/provider-defaults.js +0 -62
- package/lib/provider-detection.js +0 -59
- package/lib/provider-names.js +0 -57
- package/lib/settings/claude-auth.js +0 -78
- package/lib/settings.js +0 -503
- package/lib/stream-json-parser.js +0 -10
- package/scripts/fix-node-pty-permissions.js +0 -75
- package/scripts/record-demo.sh +0 -279
- package/scripts/setup-merge-queue.sh +0 -170
- package/scripts/test-install.sh +0 -40
- package/scripts/validate-templates.js +0 -100
- package/src/agent/agent-config.js +0 -246
- package/src/agent/agent-context-builder.js +0 -444
- package/src/agent/agent-hook-executor.js +0 -599
- package/src/agent/agent-lifecycle.js +0 -743
- package/src/agent/agent-stuck-detector.js +0 -256
- package/src/agent/agent-task-executor.js +0 -1690
- package/src/agent/agent-trigger-evaluator.js +0 -67
- package/src/agent/output-extraction.js +0 -217
- package/src/agent/output-reformatter.js +0 -175
- package/src/agent/schema-utils.js +0 -146
- package/src/agent-wrapper.js +0 -579
- package/src/agents/git-pusher-template.js +0 -285
- package/src/attach/attach-client.js +0 -438
- package/src/attach/attach-server.js +0 -543
- package/src/attach/index.js +0 -35
- package/src/attach/protocol.js +0 -220
- package/src/attach/ring-buffer.js +0 -121
- package/src/attach/socket-discovery.js +0 -267
- package/src/claude-task-runner.js +0 -574
- package/src/config-router.js +0 -80
- package/src/config-validator.js +0 -1925
- package/src/copy-worker.js +0 -43
- package/src/input-helpers.js +0 -65
- package/src/isolation-manager.js +0 -1471
- package/src/issue-providers/README.md +0 -305
- package/src/issue-providers/azure-devops-provider.js +0 -273
- package/src/issue-providers/base-provider.js +0 -232
- package/src/issue-providers/github-provider.js +0 -179
- package/src/issue-providers/gitlab-provider.js +0 -241
- package/src/issue-providers/index.js +0 -196
- package/src/issue-providers/jira-provider.js +0 -239
- package/src/ledger.js +0 -631
- package/src/lib/safe-exec.js +0 -88
- package/src/logic-engine.js +0 -223
- package/src/message-bus-bridge.js +0 -139
- package/src/message-bus.js +0 -249
- package/src/name-generator.js +0 -232
- package/src/orchestrator.js +0 -2815
- package/src/preflight.js +0 -639
- package/src/process-metrics.js +0 -608
- package/src/providers/anthropic/cli-builder.js +0 -73
- package/src/providers/anthropic/index.js +0 -204
- package/src/providers/anthropic/models.js +0 -23
- package/src/providers/anthropic/output-parser.js +0 -177
- package/src/providers/base-provider.js +0 -251
- package/src/providers/capabilities.js +0 -60
- package/src/providers/google/cli-builder.js +0 -55
- package/src/providers/google/index.js +0 -116
- package/src/providers/google/models.js +0 -24
- package/src/providers/google/output-parser.js +0 -101
- package/src/providers/index.js +0 -91
- package/src/providers/openai/cli-builder.js +0 -133
- package/src/providers/openai/index.js +0 -136
- package/src/providers/openai/models.js +0 -21
- package/src/providers/openai/output-parser.js +0 -143
- package/src/providers/opencode/cli-builder.js +0 -42
- package/src/providers/opencode/index.js +0 -103
- package/src/providers/opencode/models.js +0 -55
- package/src/providers/opencode/output-parser.js +0 -122
- package/src/schemas/sub-cluster.js +0 -185
- package/src/status-footer.js +0 -1026
- package/src/sub-cluster-wrapper.js +0 -601
- package/src/task-runner.js +0 -30
- package/src/template-resolver.js +0 -372
- package/src/tui/CHANGES.txt +0 -133
- package/src/tui/LAYOUT.md +0 -261
- package/src/tui/README.txt +0 -192
- package/src/tui/TWO-LEVEL-NAVIGATION.md +0 -186
- package/src/tui/data-poller.js +0 -349
- package/src/tui/demo.js +0 -208
- package/src/tui/formatters.js +0 -129
- package/src/tui/index.js +0 -193
- package/src/tui/keybindings.js +0 -324
- package/src/tui/layout.js +0 -334
- package/src/tui/renderer.js +0 -184
- package/task-lib/attachable-watcher.js +0 -309
- package/task-lib/claude-recovery.js +0 -156
- package/task-lib/commands/clean.js +0 -50
- package/task-lib/commands/episodes.js +0 -105
- package/task-lib/commands/get-log-path.js +0 -23
- package/task-lib/commands/kill.js +0 -32
- package/task-lib/commands/list.js +0 -105
- package/task-lib/commands/logs.js +0 -453
- package/task-lib/commands/resume.js +0 -42
- package/task-lib/commands/run.js +0 -57
- package/task-lib/commands/schedule.js +0 -105
- package/task-lib/commands/scheduler-cmd.js +0 -96
- package/task-lib/commands/schedules.js +0 -148
- package/task-lib/commands/status.js +0 -44
- package/task-lib/commands/unschedule.js +0 -16
- package/task-lib/completion.js +0 -9
- package/task-lib/config.js +0 -8
- package/task-lib/name-generator.js +0 -230
- package/task-lib/package.json +0 -3
- package/task-lib/runner.js +0 -201
- package/task-lib/scheduler.js +0 -252
- package/task-lib/store.js +0 -529
- package/task-lib/tui/formatters.js +0 -215
- package/task-lib/tui/index.js +0 -197
- package/task-lib/tui/layout.js +0 -111
- package/task-lib/tui/renderer.js +0 -129
- package/task-lib/tui.js +0 -425
- package/task-lib/watcher.js +0 -226
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentConfig - Agent configuration validation and defaults
|
|
3
|
-
*
|
|
4
|
-
* Provides:
|
|
5
|
-
* - Config validation and normalization
|
|
6
|
-
* - Default values for optional fields
|
|
7
|
-
* - Model configuration setup
|
|
8
|
-
* - Safety checks for test mode
|
|
9
|
-
* - maxModel ceiling enforcement at config time
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { loadSettings, validateModelAgainstMax, VALID_MODELS } = require('../../lib/settings');
|
|
13
|
-
|
|
14
|
-
const VALID_LEVELS = ['level1', 'level2', 'level3'];
|
|
15
|
-
|
|
16
|
-
// Default max iterations (high limit - let the user decide when to give up)
|
|
17
|
-
const DEFAULT_MAX_ITERATIONS = 100;
|
|
18
|
-
|
|
19
|
-
// Default timeout: 0 = no timeout (task runs until completion or explicit kill)
|
|
20
|
-
// Use positive number for timeout in milliseconds
|
|
21
|
-
const DEFAULT_TIMEOUT = 0;
|
|
22
|
-
|
|
23
|
-
// Stale detection - ENABLED by default using multi-indicator analysis (safe from false positives)
|
|
24
|
-
// Multi-indicator approach checks: process state, CPU usage, context switches, network I/O
|
|
25
|
-
// Only flags as stuck when ALL indicators show inactivity (score >= 3.5)
|
|
26
|
-
// Single-indicator detection (just output freshness) was too risky - this is safe.
|
|
27
|
-
const DEFAULT_STALE_DURATION_MS = 30 * 60 * 1000; // 30 minutes before triggering analysis
|
|
28
|
-
const DEFAULT_LIVENESS_CHECK_ENABLED = true; // Safe with multi-indicator detection
|
|
29
|
-
|
|
30
|
-
function applyOutputDefaults(config) {
|
|
31
|
-
// CRITICAL: Enforce JSON schema output by default to prevent parse failures and crashes
|
|
32
|
-
if (!config.outputFormat) {
|
|
33
|
-
config.outputFormat = 'json';
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// If outputFormat is json but no schema defined, use a minimal default schema
|
|
37
|
-
if (config.outputFormat === 'json' && !config.jsonSchema) {
|
|
38
|
-
config.jsonSchema = {
|
|
39
|
-
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
summary: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
description: 'Brief summary of what was done',
|
|
44
|
-
},
|
|
45
|
-
result: {
|
|
46
|
-
type: 'string',
|
|
47
|
-
description: 'Detailed result or output',
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
required: ['summary', 'result'],
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function buildModelConfig(config) {
|
|
56
|
-
if (config.modelRules) {
|
|
57
|
-
return { type: 'rules', rules: config.modelRules };
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
type: 'static',
|
|
61
|
-
model: config.model || null,
|
|
62
|
-
modelLevel: config.modelLevel || null,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function applyStrictSchemaDefault(config, settings) {
|
|
67
|
-
if (config.strictSchema === undefined) {
|
|
68
|
-
config.strictSchema = settings.strictSchema !== false; // Default true if not set
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
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}`);
|
|
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
|
-
);
|
|
99
|
-
}
|
|
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') {
|
|
117
|
-
for (const rule of modelConfig.rules) {
|
|
118
|
-
validateModelRule(config.id, rule, maxModel, minModel);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function buildPromptConfig(config) {
|
|
124
|
-
if (config.prompt?.iterations) {
|
|
125
|
-
return { type: 'rules', rules: config.prompt.iterations };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (config.prompt?.initial || config.prompt?.subsequent) {
|
|
129
|
-
const rules = [];
|
|
130
|
-
if (config.prompt.initial) rules.push({ match: '1', system: config.prompt.initial });
|
|
131
|
-
if (config.prompt.subsequent) rules.push({ match: '2+', system: config.prompt.subsequent });
|
|
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) {
|
|
144
|
-
throw new Error(`Agent "${config.id}": invalid prompt format`);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function normalizeTimeout(config) {
|
|
151
|
-
if (config.timeout === undefined || config.timeout === null || config.timeout === '') {
|
|
152
|
-
config.timeout = DEFAULT_TIMEOUT;
|
|
153
|
-
} else {
|
|
154
|
-
config.timeout = Number(config.timeout);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (!Number.isFinite(config.timeout) || config.timeout < 0) {
|
|
158
|
-
throw new Error(
|
|
159
|
-
`Agent "${config.id}": timeout must be a non-negative number (got ${config.timeout}).`
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return config.timeout;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function buildNormalizedConfig(config, modelConfig, promptConfig) {
|
|
167
|
-
return {
|
|
168
|
-
...config,
|
|
169
|
-
modelConfig,
|
|
170
|
-
promptConfig,
|
|
171
|
-
maxIterations: config.maxIterations || DEFAULT_MAX_ITERATIONS,
|
|
172
|
-
timeout: config.timeout,
|
|
173
|
-
staleDuration: config.staleDuration || DEFAULT_STALE_DURATION_MS,
|
|
174
|
-
enableLivenessCheck: config.enableLivenessCheck ?? DEFAULT_LIVENESS_CHECK_ENABLED,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function assertTestModeSafety(config, options) {
|
|
179
|
-
const executesTask = config.triggers?.some(
|
|
180
|
-
(trigger) => !trigger.action || trigger.action === 'execute_task'
|
|
181
|
-
);
|
|
182
|
-
const hasMock = options.mockSpawnFn || options.taskRunner;
|
|
183
|
-
|
|
184
|
-
if (options.testMode && !hasMock && executesTask) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`AgentWrapper: testMode=true but no mockSpawnFn/taskRunner provided for agent '${config.id}'. ` +
|
|
187
|
-
`This would cause real Claude API calls. ABORTING.`
|
|
188
|
-
);
|
|
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);
|
|
237
|
-
|
|
238
|
-
return normalizedConfig;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
module.exports = {
|
|
242
|
-
validateAgentConfig,
|
|
243
|
-
DEFAULT_MAX_ITERATIONS,
|
|
244
|
-
DEFAULT_STALE_DURATION_MS,
|
|
245
|
-
DEFAULT_LIVENESS_CHECK_ENABLED,
|
|
246
|
-
};
|
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentContextBuilder - Build agent execution context from ledger
|
|
3
|
-
*
|
|
4
|
-
* Provides:
|
|
5
|
-
* - Context assembly from multiple message sources
|
|
6
|
-
* - Context strategy evaluation (topics, limits, since timestamps)
|
|
7
|
-
* - Prompt injection and formatting
|
|
8
|
-
* - Token-based truncation
|
|
9
|
-
* - Defensive context overflow prevention
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// Defensive limit: 500,000 chars ≈ 125k tokens (safe buffer below 200k limit)
|
|
13
|
-
// Prevents "Prompt is too long" errors that kill tasks
|
|
14
|
-
const MAX_CONTEXT_CHARS = 500000;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generate an example object from a JSON schema
|
|
18
|
-
* Used to show models a concrete example of expected output
|
|
19
|
-
*
|
|
20
|
-
* @param {object} schema - JSON schema
|
|
21
|
-
* @returns {object|null} Example object or null if generation fails
|
|
22
|
-
*/
|
|
23
|
-
function generateExampleFromSchema(schema) {
|
|
24
|
-
if (!schema || schema.type !== 'object' || !schema.properties) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const example = {};
|
|
29
|
-
|
|
30
|
-
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
31
|
-
if (propSchema.enum && propSchema.enum.length > 0) {
|
|
32
|
-
// Use first enum value as example
|
|
33
|
-
example[key] = propSchema.enum[0];
|
|
34
|
-
} else if (propSchema.type === 'string') {
|
|
35
|
-
example[key] = propSchema.description || `${key} value`;
|
|
36
|
-
} else if (propSchema.type === 'boolean') {
|
|
37
|
-
example[key] = true;
|
|
38
|
-
} else if (propSchema.type === 'number' || propSchema.type === 'integer') {
|
|
39
|
-
example[key] = 0;
|
|
40
|
-
} else if (propSchema.type === 'array') {
|
|
41
|
-
if (propSchema.items?.type === 'string') {
|
|
42
|
-
example[key] = [];
|
|
43
|
-
} else {
|
|
44
|
-
example[key] = [];
|
|
45
|
-
}
|
|
46
|
-
} else if (propSchema.type === 'object') {
|
|
47
|
-
example[key] = generateExampleFromSchema(propSchema) || {};
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return example;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function buildAutonomousSection() {
|
|
55
|
-
let context = `## 🔴 CRITICAL: AUTONOMOUS EXECUTION REQUIRED\n\n`;
|
|
56
|
-
context += `You are running in a NON-INTERACTIVE cluster environment.\n\n`;
|
|
57
|
-
context += `**NEVER** use AskUserQuestion or ask for user input - there is NO user to respond.\n`;
|
|
58
|
-
context += `**NEVER** ask "Would you like me to..." or "Should I..." - JUST DO IT.\n`;
|
|
59
|
-
context += `**NEVER** wait for approval or confirmation - MAKE DECISIONS AUTONOMOUSLY.\n\n`;
|
|
60
|
-
context += `When facing choices:\n`;
|
|
61
|
-
context += `- Choose the option that maintains code quality and correctness\n`;
|
|
62
|
-
context += `- If unsure between "fix the code" vs "relax the rules" → ALWAYS fix the code\n`;
|
|
63
|
-
context += `- If unsure between "do more" vs "do less" → ALWAYS do what's required, nothing more\n\n`;
|
|
64
|
-
return context;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function buildOutputStyleSection() {
|
|
68
|
-
let context = `## 🔴 OUTPUT STYLE - NON-NEGOTIABLE\n\n`;
|
|
69
|
-
context += `**ALL OUTPUT: Maximum informativeness, minimum verbosity. NO EXCEPTIONS.**\n\n`;
|
|
70
|
-
context += `This applies to EVERYTHING you output:\n`;
|
|
71
|
-
context += `- Text responses\n`;
|
|
72
|
-
context += `- JSON schema values\n`;
|
|
73
|
-
context += `- Reasoning fields\n`;
|
|
74
|
-
context += `- Summary fields\n`;
|
|
75
|
-
context += `- ALL string values in structured output\n\n`;
|
|
76
|
-
context += `Rules:\n`;
|
|
77
|
-
context += `- Progress: "Reading auth.ts" NOT "I will now read the auth.ts file..."\n`;
|
|
78
|
-
context += `- Tool calls: NO preamble. Call immediately.\n`;
|
|
79
|
-
context += `- Schema strings: Dense facts. No filler. No fluff.\n`;
|
|
80
|
-
context += `- Errors: DETAILED (stack traces, repro). NEVER compress errors.\n`;
|
|
81
|
-
context += `- FORBIDDEN: "I'll help...", "Let me...", "I'm going to...", "Sure!", "Great!", "Certainly!"\n\n`;
|
|
82
|
-
context += `Every token costs money. Waste nothing.\n\n`;
|
|
83
|
-
return context;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function buildGitOperationsSection() {
|
|
87
|
-
let context = `## 🚫 GIT OPERATIONS - FORBIDDEN\n\n`;
|
|
88
|
-
context += `NEVER commit, push, or create PRs. You only modify files.\n`;
|
|
89
|
-
context += `The git-pusher agent handles ALL git operations AFTER validators approve.\n\n`;
|
|
90
|
-
context += `- ❌ NEVER run: git add, git commit, git push, gh pr create\n`;
|
|
91
|
-
context += `- ❌ NEVER suggest committing changes\n`;
|
|
92
|
-
context += `- ✅ Only modify files and publish your completion message when done\n\n`;
|
|
93
|
-
return context;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function buildHeaderContext({ id, role, iteration, isIsolated }) {
|
|
97
|
-
let context = `You are agent "${id}" with role "${role}".\n\n`;
|
|
98
|
-
context += `Iteration: ${iteration}\n\n`;
|
|
99
|
-
context += buildAutonomousSection();
|
|
100
|
-
context += buildOutputStyleSection();
|
|
101
|
-
if (!isIsolated) {
|
|
102
|
-
context += buildGitOperationsSection();
|
|
103
|
-
}
|
|
104
|
-
return context;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function buildInstructionsSection({ config, selectedPrompt, id }) {
|
|
108
|
-
const promptText =
|
|
109
|
-
selectedPrompt || (typeof config.prompt === 'string' ? config.prompt : config.prompt?.system);
|
|
110
|
-
|
|
111
|
-
if (promptText) {
|
|
112
|
-
return `## Instructions\n\n${promptText}\n\n`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (config.prompt && typeof config.prompt !== 'string' && !config.prompt?.system) {
|
|
116
|
-
throw new Error(
|
|
117
|
-
`Agent "${id}" has invalid prompt format. ` +
|
|
118
|
-
`Expected string or object with .system property, got: ${JSON.stringify(config.prompt).slice(0, 100)}...`
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return '';
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function buildLegacyOutputSchemaSection(config) {
|
|
126
|
-
if (!config.prompt?.outputFormat) return '';
|
|
127
|
-
|
|
128
|
-
let context = `## Output Schema (REQUIRED)\n\n`;
|
|
129
|
-
context += `\`\`\`json\n${JSON.stringify(config.prompt.outputFormat.example, null, 2)}\n\`\`\`\n\n`;
|
|
130
|
-
context += `STRING VALUES IN THIS SCHEMA: Dense. Factual. No filler words. No pleasantries.\n`;
|
|
131
|
-
if (config.prompt.outputFormat.rules) {
|
|
132
|
-
for (const rule of config.prompt.outputFormat.rules) {
|
|
133
|
-
context += `- ${rule}\n`;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
context += '\n';
|
|
137
|
-
return context;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function buildJsonSchemaSection(config) {
|
|
141
|
-
if (!config.jsonSchema || config.outputFormat !== 'json') return '';
|
|
142
|
-
|
|
143
|
-
let context = `## 🔴 OUTPUT FORMAT - JSON ONLY\n\n`;
|
|
144
|
-
context += `Your response must be ONLY valid JSON. No other text before or after.\n`;
|
|
145
|
-
context += `Start with { and end with }. Nothing else.\n\n`;
|
|
146
|
-
context += `Required schema:\n`;
|
|
147
|
-
context += `\`\`\`json\n${JSON.stringify(config.jsonSchema, null, 2)}\n\`\`\`\n\n`;
|
|
148
|
-
|
|
149
|
-
const example = generateExampleFromSchema(config.jsonSchema);
|
|
150
|
-
if (example) {
|
|
151
|
-
context += `Example output:\n`;
|
|
152
|
-
context += `\`\`\`json\n${JSON.stringify(example, null, 2)}\n\`\`\`\n\n`;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
context += `CRITICAL RULES:\n`;
|
|
156
|
-
context += `- Output ONLY the JSON object - no explanation, no thinking, no preamble\n`;
|
|
157
|
-
context += `- Use EXACTLY the enum values specified (case-sensitive)\n`;
|
|
158
|
-
context += `- Include ALL required fields\n\n`;
|
|
159
|
-
return context;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function resolveSourceSince(source, cluster, lastTaskEndTime, lastAgentStartTime) {
|
|
163
|
-
const sinceValue = source.since;
|
|
164
|
-
|
|
165
|
-
if (sinceValue === 'cluster_start') {
|
|
166
|
-
return cluster.createdAt;
|
|
167
|
-
}
|
|
168
|
-
if (sinceValue === 'last_task_end') {
|
|
169
|
-
return lastTaskEndTime || cluster.createdAt;
|
|
170
|
-
}
|
|
171
|
-
if (sinceValue === 'last_agent_start') {
|
|
172
|
-
return lastAgentStartTime || cluster.createdAt;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (typeof sinceValue === 'string') {
|
|
176
|
-
const parsed = Date.parse(sinceValue);
|
|
177
|
-
if (Number.isNaN(parsed)) {
|
|
178
|
-
throw new Error(
|
|
179
|
-
`Agent context source for topic ${source.topic} has invalid since value "${sinceValue}". ` +
|
|
180
|
-
'Use cluster_start, last_task_end, last_agent_start, or an ISO timestamp.'
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
return parsed;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return sinceValue;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function formatSourceMessagesSection(source, messages) {
|
|
190
|
-
let context = `\n## Messages from topic: ${source.topic}\n\n`;
|
|
191
|
-
for (const msg of messages) {
|
|
192
|
-
context += `[${new Date(msg.timestamp).toISOString()}] ${msg.sender}:\n`;
|
|
193
|
-
if (msg.content?.text) {
|
|
194
|
-
context += `${msg.content.text}\n`;
|
|
195
|
-
}
|
|
196
|
-
if (msg.content?.data) {
|
|
197
|
-
context += `Data: ${JSON.stringify(msg.content.data, null, 2)}\n`;
|
|
198
|
-
}
|
|
199
|
-
context += '\n';
|
|
200
|
-
}
|
|
201
|
-
return context;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function buildSourcesSection({
|
|
205
|
-
strategy,
|
|
206
|
-
messageBus,
|
|
207
|
-
cluster,
|
|
208
|
-
lastTaskEndTime,
|
|
209
|
-
lastAgentStartTime,
|
|
210
|
-
}) {
|
|
211
|
-
let context = '';
|
|
212
|
-
for (const source of strategy.sources) {
|
|
213
|
-
const sinceTimestamp = resolveSourceSince(source, cluster, lastTaskEndTime, lastAgentStartTime);
|
|
214
|
-
const messages = messageBus.query({
|
|
215
|
-
cluster_id: cluster.id,
|
|
216
|
-
topic: source.topic,
|
|
217
|
-
sender: source.sender,
|
|
218
|
-
since: sinceTimestamp,
|
|
219
|
-
limit: source.limit,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
if (messages.length > 0) {
|
|
223
|
-
context += formatSourceMessagesSection(source, messages);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return context;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function collectCannotValidateCriteria(prevValidations) {
|
|
230
|
-
const cannotValidateCriteria = [];
|
|
231
|
-
for (const msg of prevValidations) {
|
|
232
|
-
const criteriaResults = msg.content?.data?.criteriaResults;
|
|
233
|
-
if (!Array.isArray(criteriaResults)) continue;
|
|
234
|
-
for (const cr of criteriaResults) {
|
|
235
|
-
if (cr.status !== 'CANNOT_VALIDATE' || !cr.id) continue;
|
|
236
|
-
if (cannotValidateCriteria.find((c) => c.id === cr.id)) continue;
|
|
237
|
-
cannotValidateCriteria.push({
|
|
238
|
-
id: cr.id,
|
|
239
|
-
reason: cr.reason || 'No reason provided',
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return cannotValidateCriteria;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function buildCannotValidateSection(cannotValidateCriteria) {
|
|
247
|
-
if (cannotValidateCriteria.length === 0) return '';
|
|
248
|
-
|
|
249
|
-
let context = `\n## ⚠️ Permanently Unverifiable Criteria (SKIP THESE)\n\n`;
|
|
250
|
-
context += `The following criteria have PERMANENT environmental limitations (missing tools, no access).\n`;
|
|
251
|
-
context += `These limitations have not changed. Do NOT re-attempt verification.\n`;
|
|
252
|
-
context += `Mark these as CANNOT_VALIDATE again with the same reason.\n\n`;
|
|
253
|
-
for (const cv of cannotValidateCriteria) {
|
|
254
|
-
context += `- **${cv.id}**: ${cv.reason}\n`;
|
|
255
|
-
}
|
|
256
|
-
context += `\n`;
|
|
257
|
-
return context;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function buildValidatorSkipSection({ role, messageBus, cluster }) {
|
|
261
|
-
if (role !== 'validator') return '';
|
|
262
|
-
|
|
263
|
-
const prevValidations = messageBus.query({
|
|
264
|
-
cluster_id: cluster.id,
|
|
265
|
-
topic: 'VALIDATION_RESULT',
|
|
266
|
-
since: cluster.createdAt,
|
|
267
|
-
limit: 50,
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const cannotValidateCriteria = collectCannotValidateCriteria(prevValidations);
|
|
271
|
-
return buildCannotValidateSection(cannotValidateCriteria);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function buildTriggeringMessageSection(triggeringMessage) {
|
|
275
|
-
let context = `\n## Triggering Message\n\n`;
|
|
276
|
-
context += `Topic: ${triggeringMessage.topic}\n`;
|
|
277
|
-
context += `Sender: ${triggeringMessage.sender}\n`;
|
|
278
|
-
if (triggeringMessage.content?.text) {
|
|
279
|
-
context += `\n${triggeringMessage.content.text}\n`;
|
|
280
|
-
}
|
|
281
|
-
return context;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function findContextSectionIndices(lines) {
|
|
285
|
-
let issueOpenedStart = -1;
|
|
286
|
-
let issueOpenedEnd = -1;
|
|
287
|
-
let triggeringStart = -1;
|
|
288
|
-
|
|
289
|
-
for (let i = 0; i < lines.length; i++) {
|
|
290
|
-
if (lines[i].includes('## Messages from topic: ISSUE_OPENED')) {
|
|
291
|
-
issueOpenedStart = i;
|
|
292
|
-
}
|
|
293
|
-
if (issueOpenedStart !== -1 && issueOpenedEnd === -1 && lines[i].startsWith('## ')) {
|
|
294
|
-
issueOpenedEnd = i;
|
|
295
|
-
}
|
|
296
|
-
if (lines[i].includes('## Triggering Message')) {
|
|
297
|
-
triggeringStart = i;
|
|
298
|
-
break;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return { issueOpenedStart, issueOpenedEnd, triggeringStart };
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function collectRecentLines(middleLines, budgetForRecent) {
|
|
306
|
-
const recentLines = [];
|
|
307
|
-
let recentSize = 0;
|
|
308
|
-
|
|
309
|
-
for (let i = middleLines.length - 1; i >= 0; i--) {
|
|
310
|
-
const line = middleLines[i];
|
|
311
|
-
const lineSize = line.length + 1;
|
|
312
|
-
|
|
313
|
-
if (recentSize + lineSize > budgetForRecent) {
|
|
314
|
-
break;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
recentLines.unshift(line);
|
|
318
|
-
recentSize += lineSize;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return recentLines;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function truncateContextIfNeeded(context) {
|
|
325
|
-
const originalLength = context.length;
|
|
326
|
-
if (originalLength <= MAX_CONTEXT_CHARS) {
|
|
327
|
-
return context;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
console.log(
|
|
331
|
-
`[Context] Context too large (${originalLength} chars), truncating to prevent overflow...`
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
const lines = context.split('\n');
|
|
335
|
-
const { issueOpenedStart, issueOpenedEnd, triggeringStart } = findContextSectionIndices(lines);
|
|
336
|
-
|
|
337
|
-
const headerEnd = issueOpenedStart !== -1 ? issueOpenedStart : triggeringStart;
|
|
338
|
-
const header = lines.slice(0, headerEnd).join('\n');
|
|
339
|
-
|
|
340
|
-
const issueOpened =
|
|
341
|
-
issueOpenedStart !== -1 && issueOpenedEnd !== -1
|
|
342
|
-
? lines.slice(issueOpenedStart, issueOpenedEnd).join('\n')
|
|
343
|
-
: '';
|
|
344
|
-
|
|
345
|
-
const triggeringMsg = lines.slice(triggeringStart).join('\n');
|
|
346
|
-
|
|
347
|
-
const fixedSize = header.length + issueOpened.length + triggeringMsg.length;
|
|
348
|
-
const budgetForRecent = MAX_CONTEXT_CHARS - fixedSize - 200;
|
|
349
|
-
|
|
350
|
-
const middleStart = issueOpenedEnd !== -1 ? issueOpenedEnd : headerEnd;
|
|
351
|
-
const middleEnd = triggeringStart;
|
|
352
|
-
const middleLines = lines.slice(middleStart, middleEnd);
|
|
353
|
-
const recentLines = collectRecentLines(middleLines, budgetForRecent);
|
|
354
|
-
|
|
355
|
-
const parts = [header];
|
|
356
|
-
if (issueOpened) {
|
|
357
|
-
parts.push(issueOpened);
|
|
358
|
-
}
|
|
359
|
-
if (recentLines.length < middleLines.length) {
|
|
360
|
-
const truncatedCount = middleLines.length - recentLines.length;
|
|
361
|
-
parts.push(
|
|
362
|
-
`\n[...${truncatedCount} earlier context messages truncated to prevent overflow...]\n`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
if (recentLines.length > 0) {
|
|
366
|
-
parts.push(recentLines.join('\n'));
|
|
367
|
-
}
|
|
368
|
-
parts.push(triggeringMsg);
|
|
369
|
-
|
|
370
|
-
const truncatedContext = parts.join('\n');
|
|
371
|
-
const truncatedLength = truncatedContext.length;
|
|
372
|
-
console.log(
|
|
373
|
-
`[Context] Truncated from ${originalLength} to ${truncatedLength} chars (${Math.round((truncatedLength / originalLength) * 100)}% retained)`
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
return truncatedContext;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function applyLegacyMaxTokens(context, strategy) {
|
|
380
|
-
const maxTokens = strategy.maxTokens || 100000;
|
|
381
|
-
const maxChars = maxTokens * 4;
|
|
382
|
-
if (context.length > maxChars) {
|
|
383
|
-
return context.slice(0, maxChars) + '\n\n[Context truncated...]';
|
|
384
|
-
}
|
|
385
|
-
return context;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Build execution context for an agent
|
|
390
|
-
* @param {object} params - Context building parameters
|
|
391
|
-
* @param {string} params.id - Agent ID
|
|
392
|
-
* @param {string} params.role - Agent role
|
|
393
|
-
* @param {number} params.iteration - Current iteration number
|
|
394
|
-
* @param {any} params.config - Agent configuration
|
|
395
|
-
* @param {any} params.messageBus - Message bus for querying ledger
|
|
396
|
-
* @param {any} params.cluster - Cluster object
|
|
397
|
-
* @param {number} [params.lastTaskEndTime] - Timestamp of last task completion
|
|
398
|
-
* @param {number} [params.lastAgentStartTime] - Timestamp when this agent last started work
|
|
399
|
-
* @param {any} params.triggeringMessage - Message that triggered this execution
|
|
400
|
-
* @param {string} [params.selectedPrompt] - Pre-selected prompt from _selectPrompt() (iteration-based)
|
|
401
|
-
* @param {object} [params.worktree] - Worktree isolation state (if running in worktree mode)
|
|
402
|
-
* @param {object} [params.isolation] - Docker isolation state (if running in Docker mode)
|
|
403
|
-
* @returns {string} Assembled context string
|
|
404
|
-
*/
|
|
405
|
-
function buildContext({
|
|
406
|
-
id,
|
|
407
|
-
role,
|
|
408
|
-
iteration,
|
|
409
|
-
config,
|
|
410
|
-
messageBus,
|
|
411
|
-
cluster,
|
|
412
|
-
lastTaskEndTime,
|
|
413
|
-
lastAgentStartTime,
|
|
414
|
-
triggeringMessage,
|
|
415
|
-
selectedPrompt,
|
|
416
|
-
worktree,
|
|
417
|
-
isolation,
|
|
418
|
-
}) {
|
|
419
|
-
const strategy = config.contextStrategy || { sources: [] };
|
|
420
|
-
const isIsolated = !!(worktree?.enabled || isolation?.enabled);
|
|
421
|
-
|
|
422
|
-
let context = buildHeaderContext({ id, role, iteration, isIsolated });
|
|
423
|
-
context += buildInstructionsSection({ config, selectedPrompt, id });
|
|
424
|
-
context += buildLegacyOutputSchemaSection(config);
|
|
425
|
-
context += buildJsonSchemaSection(config);
|
|
426
|
-
context += buildSourcesSection({
|
|
427
|
-
strategy,
|
|
428
|
-
messageBus,
|
|
429
|
-
cluster,
|
|
430
|
-
lastTaskEndTime,
|
|
431
|
-
lastAgentStartTime,
|
|
432
|
-
});
|
|
433
|
-
context += buildValidatorSkipSection({ role, messageBus, cluster });
|
|
434
|
-
context += buildTriggeringMessageSection(triggeringMessage);
|
|
435
|
-
|
|
436
|
-
context = truncateContextIfNeeded(context);
|
|
437
|
-
context = applyLegacyMaxTokens(context, strategy);
|
|
438
|
-
|
|
439
|
-
return context;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
module.exports = {
|
|
443
|
-
buildContext,
|
|
444
|
-
};
|