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