@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.
- package/README.md +94 -14
- package/cli/commands/providers.js +8 -9
- package/cli/index.js +3032 -2409
- package/cli/message-formatters-normal.js +28 -6
- package/cluster-templates/base-templates/debug-workflow.json +1 -1
- package/cluster-templates/base-templates/full-workflow.json +72 -188
- package/cluster-templates/base-templates/worker-validator.json +59 -3
- package/cluster-templates/conductor-bootstrap.json +4 -4
- package/lib/docker-config.js +8 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +10 -7
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-names.js +2 -1
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +161 -63
- package/package.json +7 -2
- package/scripts/setup-merge-queue.sh +170 -0
- package/src/agent/agent-config.js +135 -82
- package/src/agent/agent-context-builder.js +297 -188
- package/src/agent/agent-hook-executor.js +310 -113
- package/src/agent/agent-lifecycle.js +385 -325
- package/src/agent/agent-stuck-detector.js +7 -7
- package/src/agent/agent-task-executor.js +824 -565
- package/src/agent/output-extraction.js +41 -24
- package/src/agent/output-reformatter.js +1 -1
- package/src/agent/schema-utils.js +108 -73
- package/src/agent-wrapper.js +10 -1
- package/src/agents/git-pusher-template.js +285 -0
- package/src/claude-task-runner.js +85 -34
- package/src/config-validator.js +922 -657
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +289 -199
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +273 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +179 -0
- package/src/issue-providers/gitlab-provider.js +241 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +239 -0
- package/src/ledger.js +22 -5
- package/src/lib/safe-exec.js +88 -0
- package/src/orchestrator.js +1107 -811
- package/src/preflight.js +313 -159
- package/src/process-metrics.js +98 -56
- package/src/providers/anthropic/cli-builder.js +53 -25
- package/src/providers/anthropic/index.js +72 -2
- package/src/providers/anthropic/output-parser.js +32 -14
- package/src/providers/base-provider.js +70 -0
- package/src/providers/capabilities.js +9 -0
- package/src/providers/google/output-parser.js +47 -38
- package/src/providers/index.js +19 -3
- package/src/providers/openai/cli-builder.js +14 -3
- package/src/providers/openai/index.js +1 -0
- package/src/providers/openai/output-parser.js +44 -30
- package/src/providers/opencode/cli-builder.js +42 -0
- package/src/providers/opencode/index.js +103 -0
- package/src/providers/opencode/models.js +55 -0
- package/src/providers/opencode/output-parser.js +122 -0
- package/src/schemas/sub-cluster.js +68 -39
- package/src/status-footer.js +94 -48
- package/src/sub-cluster-wrapper.js +76 -35
- package/src/template-resolver.js +12 -9
- package/src/tui/data-poller.js +123 -99
- package/src/tui/formatters.js +4 -3
- package/src/tui/keybindings.js +259 -318
- package/src/tui/renderer.js +11 -21
- package/task-lib/attachable-watcher.js +118 -81
- package/task-lib/claude-recovery.js +61 -24
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/list.js +2 -2
- package/task-lib/commands/logs.js +231 -189
- package/task-lib/commands/schedules.js +111 -61
- package/task-lib/config.js +0 -2
- package/task-lib/runner.js +84 -27
- package/task-lib/scheduler.js +1 -1
- package/task-lib/store.js +467 -168
- package/task-lib/tui/formatters.js +94 -45
- package/task-lib/tui/renderer.js +13 -3
- package/task-lib/tui.js +73 -32
- package/task-lib/watcher.js +128 -90
- package/src/agents/git-pusher-agent.json +0 -20
- package/src/github.js +0 -139
|
@@ -51,42 +51,8 @@ function generateExampleFromSchema(schema) {
|
|
|
51
51
|
return example;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* @param {object} params - Context building parameters
|
|
57
|
-
* @param {string} params.id - Agent ID
|
|
58
|
-
* @param {string} params.role - Agent role
|
|
59
|
-
* @param {number} params.iteration - Current iteration number
|
|
60
|
-
* @param {any} params.config - Agent configuration
|
|
61
|
-
* @param {any} params.messageBus - Message bus for querying ledger
|
|
62
|
-
* @param {any} params.cluster - Cluster object
|
|
63
|
-
* @param {number} [params.lastTaskEndTime] - Timestamp of last task completion
|
|
64
|
-
* @param {any} params.triggeringMessage - Message that triggered this execution
|
|
65
|
-
* @param {string} [params.selectedPrompt] - Pre-selected prompt from _selectPrompt() (iteration-based)
|
|
66
|
-
* @param {object} [params.worktree] - Worktree isolation state (if running in worktree mode)
|
|
67
|
-
* @param {object} [params.isolation] - Docker isolation state (if running in Docker mode)
|
|
68
|
-
* @returns {string} Assembled context string
|
|
69
|
-
*/
|
|
70
|
-
function buildContext({
|
|
71
|
-
id,
|
|
72
|
-
role,
|
|
73
|
-
iteration,
|
|
74
|
-
config,
|
|
75
|
-
messageBus,
|
|
76
|
-
cluster,
|
|
77
|
-
lastTaskEndTime,
|
|
78
|
-
triggeringMessage,
|
|
79
|
-
selectedPrompt,
|
|
80
|
-
worktree,
|
|
81
|
-
isolation,
|
|
82
|
-
}) {
|
|
83
|
-
const strategy = config.contextStrategy || { sources: [] };
|
|
84
|
-
|
|
85
|
-
let context = `You are agent "${id}" with role "${role}".\n\n`;
|
|
86
|
-
context += `Iteration: ${iteration}\n\n`;
|
|
87
|
-
|
|
88
|
-
// GLOBAL RULE: NEVER ASK QUESTIONS - Vibe agents run non-interactively
|
|
89
|
-
context += `## 🔴 CRITICAL: AUTONOMOUS EXECUTION REQUIRED\n\n`;
|
|
54
|
+
function buildAutonomousSection() {
|
|
55
|
+
let context = `## 🔴 CRITICAL: AUTONOMOUS EXECUTION REQUIRED\n\n`;
|
|
90
56
|
context += `You are running in a NON-INTERACTIVE cluster environment.\n\n`;
|
|
91
57
|
context += `**NEVER** use AskUserQuestion or ask for user input - there is NO user to respond.\n`;
|
|
92
58
|
context += `**NEVER** ask "Would you like me to..." or "Should I..." - JUST DO IT.\n`;
|
|
@@ -95,9 +61,11 @@ function buildContext({
|
|
|
95
61
|
context += `- Choose the option that maintains code quality and correctness\n`;
|
|
96
62
|
context += `- If unsure between "fix the code" vs "relax the rules" → ALWAYS fix the code\n`;
|
|
97
63
|
context += `- If unsure between "do more" vs "do less" → ALWAYS do what's required, nothing more\n\n`;
|
|
64
|
+
return context;
|
|
65
|
+
}
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
context
|
|
67
|
+
function buildOutputStyleSection() {
|
|
68
|
+
let context = `## 🔴 OUTPUT STYLE - NON-NEGOTIABLE\n\n`;
|
|
101
69
|
context += `**ALL OUTPUT: Maximum informativeness, minimum verbosity. NO EXCEPTIONS.**\n\n`;
|
|
102
70
|
context += `This applies to EVERYTHING you output:\n`;
|
|
103
71
|
context += `- Text responses\n`;
|
|
@@ -112,83 +80,137 @@ function buildContext({
|
|
|
112
80
|
context += `- Errors: DETAILED (stack traces, repro). NEVER compress errors.\n`;
|
|
113
81
|
context += `- FORBIDDEN: "I'll help...", "Let me...", "I'm going to...", "Sure!", "Great!", "Certainly!"\n\n`;
|
|
114
82
|
context += `Every token costs money. Waste nothing.\n\n`;
|
|
83
|
+
return context;
|
|
84
|
+
}
|
|
115
85
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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();
|
|
120
101
|
if (!isIsolated) {
|
|
121
|
-
context +=
|
|
122
|
-
context += `NEVER commit, push, or create PRs. You only modify files.\n`;
|
|
123
|
-
context += `The git-pusher agent handles ALL git operations AFTER validators approve.\n\n`;
|
|
124
|
-
context += `- ❌ NEVER run: git add, git commit, git push, gh pr create\n`;
|
|
125
|
-
context += `- ❌ NEVER suggest committing changes\n`;
|
|
126
|
-
context += `- ✅ Only modify files and publish your completion message when done\n\n`;
|
|
102
|
+
context += buildGitOperationsSection();
|
|
127
103
|
}
|
|
104
|
+
return context;
|
|
105
|
+
}
|
|
128
106
|
|
|
129
|
-
|
|
130
|
-
// If selectedPrompt is provided (iteration-based), use it directly
|
|
131
|
-
// Otherwise fall back to legacy config.prompt handling
|
|
107
|
+
function buildInstructionsSection({ config, selectedPrompt, id }) {
|
|
132
108
|
const promptText =
|
|
133
109
|
selectedPrompt || (typeof config.prompt === 'string' ? config.prompt : config.prompt?.system);
|
|
134
110
|
|
|
135
111
|
if (promptText) {
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
112
|
+
return `## Instructions\n\n${promptText}\n\n`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (config.prompt && typeof config.prompt !== 'string' && !config.prompt?.system) {
|
|
139
116
|
throw new Error(
|
|
140
117
|
`Agent "${id}" has invalid prompt format. ` +
|
|
141
118
|
`Expected string or object with .system property, got: ${JSON.stringify(config.prompt).slice(0, 100)}...`
|
|
142
119
|
);
|
|
143
120
|
}
|
|
144
121
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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`;
|
|
154
134
|
}
|
|
155
|
-
context += '\n';
|
|
156
135
|
}
|
|
136
|
+
context += '\n';
|
|
137
|
+
return context;
|
|
138
|
+
}
|
|
157
139
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Critical for less capable models (Codex, Gemini) that output prose without explicit instructions
|
|
161
|
-
if (config.jsonSchema && config.outputFormat === 'json') {
|
|
162
|
-
context += `## 🔴 OUTPUT FORMAT - JSON ONLY\n\n`;
|
|
163
|
-
context += `Your response must be ONLY valid JSON. No other text before or after.\n`;
|
|
164
|
-
context += `Start with { and end with }. Nothing else.\n\n`;
|
|
165
|
-
context += `Required schema:\n`;
|
|
166
|
-
context += `\`\`\`json\n${JSON.stringify(config.jsonSchema, null, 2)}\n\`\`\`\n\n`;
|
|
167
|
-
|
|
168
|
-
// Generate example from schema
|
|
169
|
-
const example = generateExampleFromSchema(config.jsonSchema);
|
|
170
|
-
if (example) {
|
|
171
|
-
context += `Example output:\n`;
|
|
172
|
-
context += `\`\`\`json\n${JSON.stringify(example, null, 2)}\n\`\`\`\n\n`;
|
|
173
|
-
}
|
|
140
|
+
function buildJsonSchemaSection(config) {
|
|
141
|
+
if (!config.jsonSchema || config.outputFormat !== 'json') return '';
|
|
174
142
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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`;
|
|
179
153
|
}
|
|
180
154
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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`;
|
|
190
198
|
}
|
|
199
|
+
context += '\n';
|
|
200
|
+
}
|
|
201
|
+
return context;
|
|
202
|
+
}
|
|
191
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);
|
|
192
214
|
const messages = messageBus.query({
|
|
193
215
|
cluster_id: cluster.id,
|
|
194
216
|
topic: source.topic,
|
|
@@ -198,134 +220,221 @@ function buildContext({
|
|
|
198
220
|
});
|
|
199
221
|
|
|
200
222
|
if (messages.length > 0) {
|
|
201
|
-
context +=
|
|
202
|
-
for (const msg of messages) {
|
|
203
|
-
context += `[${new Date(msg.timestamp).toISOString()}] ${msg.sender}:\n`;
|
|
204
|
-
if (msg.content?.text) {
|
|
205
|
-
context += `${msg.content.text}\n`;
|
|
206
|
-
}
|
|
207
|
-
if (msg.content?.data) {
|
|
208
|
-
context += `Data: ${JSON.stringify(msg.content.data, null, 2)}\n`;
|
|
209
|
-
}
|
|
210
|
-
context += '\n';
|
|
211
|
-
}
|
|
223
|
+
context += formatSourceMessagesSection(source, messages);
|
|
212
224
|
}
|
|
213
225
|
}
|
|
226
|
+
return context;
|
|
227
|
+
}
|
|
214
228
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
+
}
|
|
221
242
|
}
|
|
243
|
+
return cannotValidateCriteria;
|
|
244
|
+
}
|
|
222
245
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
// Truncate from MIDDLE (oldest context messages) if too long
|
|
226
|
-
const originalLength = context.length;
|
|
246
|
+
function buildCannotValidateSection(cannotValidateCriteria) {
|
|
247
|
+
if (cannotValidateCriteria.length === 0) return '';
|
|
227
248
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
+
}
|
|
232
259
|
|
|
233
|
-
|
|
234
|
-
|
|
260
|
+
function buildValidatorSkipSection({ role, messageBus, cluster }) {
|
|
261
|
+
if (role !== 'validator') return '';
|
|
235
262
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
263
|
+
const prevValidations = messageBus.query({
|
|
264
|
+
cluster_id: cluster.id,
|
|
265
|
+
topic: 'VALIDATION_RESULT',
|
|
266
|
+
since: cluster.createdAt,
|
|
267
|
+
limit: 50,
|
|
268
|
+
});
|
|
240
269
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
if (issueOpenedStart !== -1 && issueOpenedEnd === -1 && lines[i].startsWith('## ')) {
|
|
246
|
-
issueOpenedEnd = i;
|
|
247
|
-
}
|
|
248
|
-
if (lines[i].includes('## Triggering Message')) {
|
|
249
|
-
triggeringStart = i;
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
270
|
+
const cannotValidateCriteria = collectCannotValidateCriteria(prevValidations);
|
|
271
|
+
return buildCannotValidateSection(cannotValidateCriteria);
|
|
272
|
+
}
|
|
253
273
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
+
}
|
|
259
283
|
|
|
260
|
-
|
|
261
|
-
|
|
284
|
+
function findContextSectionIndices(lines) {
|
|
285
|
+
let issueOpenedStart = -1;
|
|
286
|
+
let issueOpenedEnd = -1;
|
|
287
|
+
let triggeringStart = -1;
|
|
262
288
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
+
}
|
|
267
301
|
|
|
268
|
-
|
|
302
|
+
return { issueOpenedStart, issueOpenedEnd, triggeringStart };
|
|
303
|
+
}
|
|
269
304
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
305
|
+
function collectRecentLines(middleLines, budgetForRecent) {
|
|
306
|
+
const recentLines = [];
|
|
307
|
+
let recentSize = 0;
|
|
273
308
|
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
309
|
+
for (let i = middleLines.length - 1; i >= 0; i--) {
|
|
310
|
+
const line = middleLines[i];
|
|
311
|
+
const lineSize = line.length + 1;
|
|
277
312
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
313
|
+
if (recentSize + lineSize > budgetForRecent) {
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
281
316
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
317
|
+
recentLines.unshift(line);
|
|
318
|
+
recentSize += lineSize;
|
|
319
|
+
}
|
|
285
320
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
321
|
+
return recentLines;
|
|
322
|
+
}
|
|
289
323
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
324
|
+
function truncateContextIfNeeded(context) {
|
|
325
|
+
const originalLength = context.length;
|
|
326
|
+
if (originalLength <= MAX_CONTEXT_CHARS) {
|
|
327
|
+
return context;
|
|
328
|
+
}
|
|
293
329
|
|
|
294
|
-
|
|
295
|
-
|
|
330
|
+
console.log(
|
|
331
|
+
`[Context] Context too large (${originalLength} chars), truncating to prevent overflow...`
|
|
332
|
+
);
|
|
296
333
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
334
|
+
const lines = context.split('\n');
|
|
335
|
+
const { issueOpenedStart, issueOpenedEnd, triggeringStart } = findContextSectionIndices(lines);
|
|
300
336
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const truncatedCount = middleLines.length - recentLines.length;
|
|
304
|
-
parts.push(
|
|
305
|
-
`\n[...${truncatedCount} earlier context messages truncated to prevent overflow...]\n`
|
|
306
|
-
);
|
|
307
|
-
}
|
|
337
|
+
const headerEnd = issueOpenedStart !== -1 ? issueOpenedStart : triggeringStart;
|
|
338
|
+
const header = lines.slice(0, headerEnd).join('\n');
|
|
308
339
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
340
|
+
const issueOpened =
|
|
341
|
+
issueOpenedStart !== -1 && issueOpenedEnd !== -1
|
|
342
|
+
? lines.slice(issueOpenedStart, issueOpenedEnd).join('\n')
|
|
343
|
+
: '';
|
|
312
344
|
|
|
313
|
-
|
|
345
|
+
const triggeringMsg = lines.slice(triggeringStart).join('\n');
|
|
314
346
|
|
|
315
|
-
|
|
347
|
+
const fixedSize = header.length + issueOpened.length + triggeringMsg.length;
|
|
348
|
+
const budgetForRecent = MAX_CONTEXT_CHARS - fixedSize - 200;
|
|
316
349
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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`
|
|
320
363
|
);
|
|
321
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
|
+
}
|
|
322
378
|
|
|
323
|
-
|
|
379
|
+
function applyLegacyMaxTokens(context, strategy) {
|
|
324
380
|
const maxTokens = strategy.maxTokens || 100000;
|
|
325
381
|
const maxChars = maxTokens * 4;
|
|
326
382
|
if (context.length > maxChars) {
|
|
327
|
-
|
|
383
|
+
return context.slice(0, maxChars) + '\n\n[Context truncated...]';
|
|
328
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);
|
|
329
438
|
|
|
330
439
|
return context;
|
|
331
440
|
}
|