@calliopelabs/cli 2.2.0 → 2.5.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 +17 -0
- package/dist/agents/agent-config-loader.js +1 -1
- package/dist/agents/agent-config-presets.js +13 -13
- package/dist/agents/agent-config-presets.js.map +1 -1
- package/dist/agents/agent-config-types.d.ts +1 -1
- package/dist/agents/agent-config-types.d.ts.map +1 -1
- package/dist/agents/council-types.d.ts +2 -0
- package/dist/agents/council-types.d.ts.map +1 -1
- package/dist/agents/council-types.js.map +1 -1
- package/dist/agents/council.d.ts +5 -0
- package/dist/agents/council.d.ts.map +1 -1
- package/dist/agents/council.js +150 -14
- package/dist/agents/council.js.map +1 -1
- package/dist/agents/dynamic-tools.d.ts.map +1 -1
- package/dist/agents/dynamic-tools.js +39 -10
- package/dist/agents/dynamic-tools.js.map +1 -1
- package/dist/agents/orchestrator.d.ts +7 -0
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +48 -7
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/agents/sdk-backend.js +1 -1
- package/dist/agents/sdk-backend.js.map +1 -1
- package/dist/agents/swarm-types.d.ts +1 -0
- package/dist/agents/swarm-types.d.ts.map +1 -1
- package/dist/agents/swarm.d.ts +5 -0
- package/dist/agents/swarm.d.ts.map +1 -1
- package/dist/agents/swarm.js +85 -17
- package/dist/agents/swarm.js.map +1 -1
- package/dist/agents/types.d.ts +1 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/agents/types.js.map +1 -1
- package/dist/api-server.d.ts +9 -0
- package/dist/api-server.d.ts.map +1 -1
- package/dist/api-server.js +75 -4
- package/dist/api-server.js.map +1 -1
- package/dist/auto-checkpoint.d.ts.map +1 -1
- package/dist/auto-checkpoint.js +50 -17
- package/dist/auto-checkpoint.js.map +1 -1
- package/dist/auto-compressor.d.ts +14 -0
- package/dist/auto-compressor.d.ts.map +1 -1
- package/dist/auto-compressor.js +67 -10
- package/dist/auto-compressor.js.map +1 -1
- package/dist/background-jobs.d.ts.map +1 -1
- package/dist/background-jobs.js +8 -3
- package/dist/background-jobs.js.map +1 -1
- package/dist/bin.d.ts +8 -0
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +63 -4
- package/dist/bin.js.map +1 -1
- package/dist/branching.d.ts.map +1 -1
- package/dist/branching.js +14 -1
- package/dist/branching.js.map +1 -1
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +13 -1
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli/agent.d.ts.map +1 -1
- package/dist/cli/agent.js +130 -62
- package/dist/cli/agent.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +218 -11
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +55 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/types.d.ts +3 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js +2 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -3
- package/dist/config.js.map +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +42 -4
- package/dist/diff.js.map +1 -1
- package/dist/env-expansion.d.ts +15 -0
- package/dist/env-expansion.d.ts.map +1 -0
- package/dist/env-expansion.js +43 -0
- package/dist/env-expansion.js.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +30 -3
- package/dist/errors.js.map +1 -1
- package/dist/headless.d.ts.map +1 -1
- package/dist/headless.js +59 -4
- package/dist/headless.js.map +1 -1
- package/dist/hooks.d.ts +8 -2
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +97 -11
- package/dist/hooks.js.map +1 -1
- package/dist/idle-eviction.d.ts.map +1 -1
- package/dist/idle-eviction.js +8 -1
- package/dist/idle-eviction.js.map +1 -1
- package/dist/iteration-ledger.d.ts +111 -2
- package/dist/iteration-ledger.d.ts.map +1 -1
- package/dist/iteration-ledger.js +327 -19
- package/dist/iteration-ledger.js.map +1 -1
- package/dist/iteration-limit.d.ts +5 -0
- package/dist/iteration-limit.d.ts.map +1 -0
- package/dist/iteration-limit.js +17 -0
- package/dist/iteration-limit.js.map +1 -0
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +32 -10
- package/dist/markdown.js.map +1 -1
- package/dist/mcp.d.ts +35 -5
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +191 -12
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +4 -9
- package/dist/memory.js.map +1 -1
- package/dist/model-detection.d.ts +14 -1
- package/dist/model-detection.d.ts.map +1 -1
- package/dist/model-detection.js +307 -114
- package/dist/model-detection.js.map +1 -1
- package/dist/model-router.js +7 -7
- package/dist/model-router.js.map +1 -1
- package/dist/parallel-tools.d.ts +9 -1
- package/dist/parallel-tools.d.ts.map +1 -1
- package/dist/parallel-tools.js +6 -5
- package/dist/parallel-tools.js.map +1 -1
- package/dist/plugins.d.ts +37 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +87 -0
- package/dist/plugins.js.map +1 -1
- package/dist/prevent-sleep.d.ts +10 -0
- package/dist/prevent-sleep.d.ts.map +1 -0
- package/dist/prevent-sleep.js +85 -0
- package/dist/prevent-sleep.js.map +1 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +36 -2
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/bedrock.d.ts.map +1 -1
- package/dist/providers/bedrock.js +81 -17
- package/dist/providers/bedrock.js.map +1 -1
- package/dist/providers/compat.d.ts.map +1 -1
- package/dist/providers/compat.js +21 -6
- package/dist/providers/compat.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai-compat-shims.d.ts +31 -0
- package/dist/providers/openai-compat-shims.d.ts.map +1 -0
- package/dist/providers/openai-compat-shims.js +179 -0
- package/dist/providers/openai-compat-shims.js.map +1 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +19 -10
- package/dist/providers/types.js.map +1 -1
- package/dist/risk.d.ts.map +1 -1
- package/dist/risk.js +15 -5
- package/dist/risk.js.map +1 -1
- package/dist/sandbox-native.d.ts +1 -0
- package/dist/sandbox-native.d.ts.map +1 -1
- package/dist/sandbox-native.js +37 -5
- package/dist/sandbox-native.js.map +1 -1
- package/dist/scope.d.ts +10 -0
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +75 -15
- package/dist/scope.js.map +1 -1
- package/dist/scuttlebot/client.d.ts +83 -0
- package/dist/scuttlebot/client.d.ts.map +1 -0
- package/dist/scuttlebot/client.js +350 -0
- package/dist/scuttlebot/client.js.map +1 -0
- package/dist/scuttlebot/config.d.ts +28 -0
- package/dist/scuttlebot/config.d.ts.map +1 -0
- package/dist/scuttlebot/config.js +91 -0
- package/dist/scuttlebot/config.js.map +1 -0
- package/dist/scuttlebot/http-client.d.ts +63 -0
- package/dist/scuttlebot/http-client.d.ts.map +1 -0
- package/dist/scuttlebot/http-client.js +124 -0
- package/dist/scuttlebot/http-client.js.map +1 -0
- package/dist/scuttlebot/index.d.ts +13 -0
- package/dist/scuttlebot/index.d.ts.map +1 -0
- package/dist/scuttlebot/index.js +10 -0
- package/dist/scuttlebot/index.js.map +1 -0
- package/dist/scuttlebot/irc-client.d.ts +124 -0
- package/dist/scuttlebot/irc-client.d.ts.map +1 -0
- package/dist/scuttlebot/irc-client.js +599 -0
- package/dist/scuttlebot/irc-client.js.map +1 -0
- package/dist/skills.d.ts +19 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +98 -10
- package/dist/skills.js.map +1 -1
- package/dist/smart-router.js +4 -4
- package/dist/smart-router.js.map +1 -1
- package/dist/storage.d.ts +18 -3
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +182 -14
- package/dist/storage.js.map +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +233 -39
- package/dist/tools.js.map +1 -1
- package/dist/trust.d.ts +16 -3
- package/dist/trust.d.ts.map +1 -1
- package/dist/trust.js +23 -4
- package/dist/trust.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -12
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +1 -1
- package/dist/ui/agent.d.ts.map +1 -1
- package/dist/ui/agent.js +175 -121
- package/dist/ui/agent.js.map +1 -1
- package/dist/ui/chat-input.d.ts +3 -1
- package/dist/ui/chat-input.d.ts.map +1 -1
- package/dist/ui/chat-input.js +82 -17
- package/dist/ui/chat-input.js.map +1 -1
- package/dist/ui/commands.d.ts +4 -0
- package/dist/ui/commands.d.ts.map +1 -1
- package/dist/ui/commands.js +562 -39
- package/dist/ui/commands.js.map +1 -1
- package/dist/ui/completions.d.ts.map +1 -1
- package/dist/ui/completions.js +2 -0
- package/dist/ui/completions.js.map +1 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +288 -60
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-utils.d.ts +20 -0
- package/dist/ui/input-utils.d.ts.map +1 -0
- package/dist/ui/input-utils.js +35 -0
- package/dist/ui/input-utils.js.map +1 -0
- package/dist/ui/messages.d.ts +6 -2
- package/dist/ui/messages.d.ts.map +1 -1
- package/dist/ui/messages.js +42 -11
- package/dist/ui/messages.js.map +1 -1
- package/dist/ui/modals.d.ts +21 -1
- package/dist/ui/modals.d.ts.map +1 -1
- package/dist/ui/modals.js +67 -5
- package/dist/ui/modals.js.map +1 -1
- package/dist/ui/status-bar.d.ts +4 -1
- package/dist/ui/status-bar.d.ts.map +1 -1
- package/dist/ui/status-bar.js +12 -1
- package/dist/ui/status-bar.js.map +1 -1
- package/dist/ui/types.d.ts +3 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/package.json +4 -7
- package/dist/completion.d.ts +0 -75
- package/dist/completion.d.ts.map +0 -1
- package/dist/completion.js +0 -234
- package/dist/completion.js.map +0 -1
- package/dist/keyboard.d.ts +0 -57
- package/dist/keyboard.d.ts.map +0 -1
- package/dist/keyboard.js +0 -265
- package/dist/keyboard.js.map +0 -1
package/dist/ui/agent.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Core agent execution loop, tool handling, and message validation.
|
|
5
5
|
* Extracted from TerminalChat using an AgentContext state bag.
|
|
6
6
|
*/
|
|
7
|
-
import { spawn } from 'child_process';
|
|
8
7
|
import * as config from '../config.js';
|
|
9
8
|
import { chat } from '../providers/index.js';
|
|
10
9
|
import { estimateContextUsage, needsSummarization } from '../providers/types.js';
|
|
@@ -17,13 +16,17 @@ import { getAvailableProviders } from '../providers/index.js';
|
|
|
17
16
|
import * as storage from '../storage.js';
|
|
18
17
|
import * as hooks from '../hooks.js';
|
|
19
18
|
import * as modelRouter from '../model-router.js';
|
|
19
|
+
import { scuttlebotClient } from '../scuttlebot/index.js';
|
|
20
20
|
import * as summarization from '../summarization.js';
|
|
21
|
+
import { autoCompress } from '../auto-compressor.js';
|
|
21
22
|
import { executeParallel, getParallelizationStats } from '../parallel-tools.js';
|
|
22
23
|
import { setMood } from '../companions.js';
|
|
23
24
|
import { checkAndWarnContextLimit } from './context.js';
|
|
24
25
|
import { smartRoute } from '../smart-router.js';
|
|
25
26
|
import { shouldCheckpoint, createCheckpoint } from '../auto-checkpoint.js';
|
|
26
27
|
import { recordEvent } from '../terminal-recording.js';
|
|
28
|
+
import { startPreventSleep, stopPreventSleep } from '../prevent-sleep.js';
|
|
29
|
+
import { resolveIterationLimit, formatIterationProgress, isFiniteIterationLimit, } from '../iteration-limit.js';
|
|
27
30
|
// ============================================================================
|
|
28
31
|
// Tool Result Truncation
|
|
29
32
|
// ============================================================================
|
|
@@ -44,6 +47,21 @@ function truncateToolResult(content, modelLimit) {
|
|
|
44
47
|
const trimmed = content.slice(0, half) + `\n\n... [truncated ${content.length - maxChars} chars] ...\n\n` + content.slice(-half);
|
|
45
48
|
return trimmed;
|
|
46
49
|
}
|
|
50
|
+
function summarizeMessageContent(content) {
|
|
51
|
+
if (typeof content === 'string') {
|
|
52
|
+
return content;
|
|
53
|
+
}
|
|
54
|
+
return content
|
|
55
|
+
.map(block => {
|
|
56
|
+
if (block.type === 'text')
|
|
57
|
+
return block.text;
|
|
58
|
+
if (block.type === 'image')
|
|
59
|
+
return '[image]';
|
|
60
|
+
return '[content]';
|
|
61
|
+
})
|
|
62
|
+
.join(' ')
|
|
63
|
+
.trim();
|
|
64
|
+
}
|
|
47
65
|
// ============================================================================
|
|
48
66
|
// Validate and Repair Messages
|
|
49
67
|
// ============================================================================
|
|
@@ -121,8 +139,19 @@ export async function runAgentImpl(ctx, content) {
|
|
|
121
139
|
ctx.addMessage('system', `[Auto-route: ${routeDecision.tier} tier - ${routeDecision.reason}]`);
|
|
122
140
|
}
|
|
123
141
|
}
|
|
124
|
-
const maxIterations = config.get('maxIterations')
|
|
142
|
+
const maxIterations = resolveIterationLimit(config.get('maxIterations'));
|
|
125
143
|
let completedNaturally = false;
|
|
144
|
+
const hasParentRun = Boolean(ctx.ledger?.getActiveRun('loop') ||
|
|
145
|
+
ctx.ledger?.getActiveRun('workflow') ||
|
|
146
|
+
ctx.ledger?.getActiveRun('swarm') ||
|
|
147
|
+
ctx.ledger?.getActiveRun('council'));
|
|
148
|
+
const runId = ctx.ledger && !hasParentRun
|
|
149
|
+
? ctx.ledger.startRun('agent', summarizeMessageContent(content), {
|
|
150
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : null,
|
|
151
|
+
})
|
|
152
|
+
: undefined;
|
|
153
|
+
let runStatus;
|
|
154
|
+
let runErrorSummary;
|
|
126
155
|
// Check context limit and warn if approaching capacity
|
|
127
156
|
// Uses model's actual context length from API when available
|
|
128
157
|
let currentContextTokens = ctx.estimateContextTokens();
|
|
@@ -130,32 +159,14 @@ export async function runAgentImpl(ctx, content) {
|
|
|
130
159
|
let contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
131
160
|
// Adaptive preserveRecent: small models keep fewer messages to leave room for output
|
|
132
161
|
const preserveRecent = modelLimit < 8000 ? 2 : modelLimit < 16000 ? 4 : modelLimit < 32000 ? 6 : modelLimit < 64000 ? 10 : 15;
|
|
133
|
-
// Auto-compact
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
ctx.llmMessages.current = result.messages;
|
|
142
|
-
currentContextTokens = ctx.estimateContextTokens();
|
|
143
|
-
contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
144
|
-
ctx.setContextTokens(currentContextTokens);
|
|
145
|
-
ctx.addMessage('system', `✓ Compacted ${result.summarizedCount} messages. Now at ${Math.round(contextPercentage)}% (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K)`);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
// If compaction didn't help enough, force-trim old messages
|
|
149
|
-
if (contextPercentage > 98) {
|
|
150
|
-
const systemMsgs = ctx.llmMessages.current.filter(m => m.role === 'system');
|
|
151
|
-
const recentMsgs = ctx.llmMessages.current.filter(m => m.role !== 'system').slice(-5);
|
|
152
|
-
ctx.llmMessages.current = [...systemMsgs, ...recentMsgs];
|
|
153
|
-
currentContextTokens = ctx.estimateContextTokens();
|
|
154
|
-
contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
155
|
-
ctx.setContextTokens(currentContextTokens);
|
|
156
|
-
ctx.addMessage('system', `⚠️ Force-trimmed to last 5 messages (${Math.round(contextPercentage)}%). Use /clear for a full reset.`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
162
|
+
// Auto-compact using the new auto-compressor
|
|
163
|
+
const autoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
|
|
164
|
+
if (autoCompressResult.compressed) {
|
|
165
|
+
ctx.llmMessages.current = autoCompressResult.messages;
|
|
166
|
+
currentContextTokens = ctx.estimateContextTokens();
|
|
167
|
+
contextPercentage = (currentContextTokens / modelLimit) * 100;
|
|
168
|
+
ctx.setContextTokens(currentContextTokens);
|
|
169
|
+
ctx.addMessage('system', `🔄 Auto-compressed ${autoCompressResult.summarizedCount} messages using ${autoCompressResult.method} (${Math.round(autoCompressResult.originalTokens / 1000)}K → ${Math.round(autoCompressResult.compressedTokens / 1000)}K tokens)`);
|
|
159
170
|
}
|
|
160
171
|
else if (contextPercentage > 65) {
|
|
161
172
|
ctx.addMessage('system', `⚠️ Context at ${Math.round(contextPercentage)}% capacity (${Math.round(currentContextTokens / 1000)}K/${Math.round(modelLimit / 1000)}K tokens)
|
|
@@ -170,31 +181,26 @@ export async function runAgentImpl(ctx, content) {
|
|
|
170
181
|
}
|
|
171
182
|
for (let i = 0; i < maxIterations; i++) {
|
|
172
183
|
// Start ledger tracking for this iteration
|
|
173
|
-
ctx.ledger
|
|
184
|
+
if (ctx.ledger) {
|
|
185
|
+
ctx.ledger.startIteration(ctx.ledger.getNextIterationNumber());
|
|
186
|
+
}
|
|
174
187
|
// Safety check at start of each iteration - context may have grown from tool results
|
|
175
188
|
if (i > 0) {
|
|
176
189
|
const iterContextTokens = ctx.estimateContextTokens();
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
ctx.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
preserveRecent,
|
|
183
|
-
});
|
|
184
|
-
if (result.summarizedCount > 0) {
|
|
185
|
-
ctx.llmMessages.current = result.messages;
|
|
186
|
-
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
187
|
-
ctx.addMessage('system', `✓ Compacted ${result.summarizedCount} messages during iteration ${i + 1}`);
|
|
188
|
-
}
|
|
190
|
+
const iterAutoCompressResult = await autoCompress(ctx.llmMessages.current, modelLimit, ctx.provider, effectiveModel);
|
|
191
|
+
if (iterAutoCompressResult.compressed) {
|
|
192
|
+
ctx.llmMessages.current = iterAutoCompressResult.messages;
|
|
193
|
+
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
194
|
+
ctx.addMessage('system', `🔄 Auto-compressed ${iterAutoCompressResult.summarizedCount} messages during iteration ${i + 1} (${iterAutoCompressResult.method})`);
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
197
|
try {
|
|
192
198
|
// Update thinking state for LLM call
|
|
193
199
|
ctx.setThinkingState({
|
|
194
200
|
status: i === 0 ? 'Analyzing request...' : 'Processing response...',
|
|
195
|
-
detail: `Iteration ${i + 1
|
|
201
|
+
detail: `Iteration ${formatIterationProgress(i + 1, maxIterations)}`,
|
|
196
202
|
iteration: i + 1,
|
|
197
|
-
maxIterations,
|
|
203
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
198
204
|
});
|
|
199
205
|
ctx.setActivityState({
|
|
200
206
|
action: i === 0 ? 'Analyzing request' : 'Processing',
|
|
@@ -212,7 +218,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
212
218
|
status: `Retrying... (attempt ${attempt + 1})`,
|
|
213
219
|
detail: `${error.message.substring(0, 40)}... Waiting ${Math.round(delayMs / 1000)}s`,
|
|
214
220
|
iteration: i + 1,
|
|
215
|
-
maxIterations,
|
|
221
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
216
222
|
});
|
|
217
223
|
};
|
|
218
224
|
ctx.debugLog('chat', 'WAITING for LLM response', `iteration=${i + 1}`);
|
|
@@ -276,6 +282,8 @@ export async function runAgentImpl(ctx, content) {
|
|
|
276
282
|
ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
|
|
277
283
|
if (breakerResult.tripped) {
|
|
278
284
|
ctx.addMessage('system', `\u26a0\ufe0f Circuit breaker tripped: ${breakerResult.breaker}\n${breakerResult.message}\n\nUse /breaker resume to continue, /breaker status for details.`);
|
|
285
|
+
runStatus = 'stopped';
|
|
286
|
+
runErrorSummary = breakerResult.message;
|
|
279
287
|
completedNaturally = true;
|
|
280
288
|
break;
|
|
281
289
|
}
|
|
@@ -287,6 +295,10 @@ export async function runAgentImpl(ctx, content) {
|
|
|
287
295
|
content: response.content,
|
|
288
296
|
toolCalls: response.toolCalls,
|
|
289
297
|
});
|
|
298
|
+
// Mirror any text the assistant produced alongside the tool calls
|
|
299
|
+
if (scuttlebotClient.isEnabled() && response.content) {
|
|
300
|
+
scuttlebotClient.mirrorAssistant(response.content).catch(() => { });
|
|
301
|
+
}
|
|
290
302
|
const preChecks = [];
|
|
291
303
|
const executableTools = [];
|
|
292
304
|
for (const toolCall of response.toolCalls) {
|
|
@@ -360,7 +372,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
360
372
|
status: `Executing ${executableTools.length} tools in parallel...`,
|
|
361
373
|
detail: `${parallelStats.stages} stages, up to ${parallelStats.maxParallel}x speedup`,
|
|
362
374
|
iteration: i + 1,
|
|
363
|
-
maxIterations,
|
|
375
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
364
376
|
});
|
|
365
377
|
ctx.setActivityState({
|
|
366
378
|
action: `Executing ${executableTools.length} tools`,
|
|
@@ -370,8 +382,8 @@ export async function runAgentImpl(ctx, content) {
|
|
|
370
382
|
// Execute in parallel using dependency-aware staging
|
|
371
383
|
ctx.debugLog('tools', 'PARALLEL exec start', `count=${executableTools.length}`);
|
|
372
384
|
const results = await executeParallel(executableTools, async (call) => {
|
|
373
|
-
const result = await executeTool(call, process.cwd());
|
|
374
|
-
return result.result;
|
|
385
|
+
const result = await executeTool(call, ctx.sessionRef.current?.projectPath ?? process.cwd());
|
|
386
|
+
return { result: result.result, isError: result.isError };
|
|
375
387
|
}, (completed, total, current) => {
|
|
376
388
|
const args = current.arguments;
|
|
377
389
|
const target = args.path || args.command?.substring(0, 30) || current.name;
|
|
@@ -384,7 +396,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
384
396
|
status: `Executing tools... (${completed + 1}/${total})`,
|
|
385
397
|
detail: current.name,
|
|
386
398
|
iteration: i + 1,
|
|
387
|
-
maxIterations,
|
|
399
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
388
400
|
});
|
|
389
401
|
});
|
|
390
402
|
ctx.debugLog('tools', 'PARALLEL exec done', `results=${results.length}`);
|
|
@@ -392,10 +404,14 @@ export async function runAgentImpl(ctx, content) {
|
|
|
392
404
|
for (const result of results) {
|
|
393
405
|
const toolCall = result.toolCall;
|
|
394
406
|
const args = toolCall.arguments;
|
|
407
|
+
// A tool-level failure (result.isError) or a thrown exception (result.error)
|
|
408
|
+
// both count as a failure — mirror the sequential branch.
|
|
409
|
+
const failed = result.isError || !!result.error;
|
|
410
|
+
const failureText = result.error || result.result;
|
|
395
411
|
recordEvent('tool_call', toolCall.name, { name: toolCall.name, arguments: args });
|
|
396
|
-
recordEvent('tool_result', (result.result || result.error || '').slice(0, 1000), { name: toolCall.name, isError:
|
|
412
|
+
recordEvent('tool_result', (result.result || result.error || '').slice(0, 1000), { name: toolCall.name, isError: failed });
|
|
397
413
|
// Record in iteration ledger
|
|
398
|
-
ctx.ledger?.recordAction(toolCall.name, args,
|
|
414
|
+
ctx.ledger?.recordAction(toolCall.name, args, failed ? 'error' : 'ok', failed ? failureText : undefined);
|
|
399
415
|
// Execute post-tool hooks
|
|
400
416
|
hooks.executeHooks('post-tool', {
|
|
401
417
|
tool: toolCall.name,
|
|
@@ -410,11 +426,11 @@ export async function runAgentImpl(ctx, content) {
|
|
|
410
426
|
ctx.addMessage('tool', thought);
|
|
411
427
|
}
|
|
412
428
|
else if (result.error) {
|
|
413
|
-
ctx.addMessage('tool', `Error: ${result.error}
|
|
429
|
+
ctx.addMessage('tool', `Error: ${result.error}`, true);
|
|
414
430
|
}
|
|
415
431
|
else {
|
|
416
432
|
const preview = result.result.split('\n').slice(0, 3).join('\n');
|
|
417
|
-
ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''));
|
|
433
|
+
ctx.addMessage('tool', preview + (result.result.split('\n').length > 3 ? '\n...' : ''), failed);
|
|
418
434
|
}
|
|
419
435
|
ctx.llmMessages.current.push({
|
|
420
436
|
role: 'tool',
|
|
@@ -454,7 +470,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
454
470
|
detail: thought.substring(0, 60) + (thought.length > 60 ? '...' : ''),
|
|
455
471
|
thinking: thought,
|
|
456
472
|
iteration: i + 1,
|
|
457
|
-
maxIterations,
|
|
473
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
458
474
|
});
|
|
459
475
|
}
|
|
460
476
|
else {
|
|
@@ -463,7 +479,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
463
479
|
detail: toolPreview.substring(0, 60),
|
|
464
480
|
thinking: undefined,
|
|
465
481
|
iteration: i + 1,
|
|
466
|
-
maxIterations,
|
|
482
|
+
maxIterations: isFiniteIterationLimit(maxIterations) ? maxIterations : undefined,
|
|
467
483
|
});
|
|
468
484
|
}
|
|
469
485
|
ctx.debugLog('tools', 'EXEC', toolCall.name, toolPreview.substring(0, 30));
|
|
@@ -484,7 +500,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
484
500
|
detail: chunk.trimEnd().split('\n').pop()?.substring(0, 60),
|
|
485
501
|
});
|
|
486
502
|
} : undefined;
|
|
487
|
-
const result = await executeTool(toolCall, process.cwd(), 60000, shellStreamCallback);
|
|
503
|
+
const result = await executeTool(toolCall, ctx.sessionRef.current?.projectPath ?? process.cwd(), 60000, shellStreamCallback);
|
|
488
504
|
ctx.debugLog('tools', 'DONE', toolCall.name);
|
|
489
505
|
recordEvent('tool_result', result.result.slice(0, 1000), { name: toolCall.name, isError: result.isError });
|
|
490
506
|
// Record in iteration ledger
|
|
@@ -545,7 +561,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
545
561
|
else {
|
|
546
562
|
const display = result.displayResult || result.result;
|
|
547
563
|
const preview = display.split('\n').slice(0, 5).join('\n');
|
|
548
|
-
ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''));
|
|
564
|
+
ctx.addMessage('tool', preview + (display.split('\n').length > 5 ? '\n...' : ''), result.isError);
|
|
549
565
|
}
|
|
550
566
|
if (toolCall.name !== 'ask_question') {
|
|
551
567
|
ctx.llmMessages.current.push({
|
|
@@ -567,6 +583,12 @@ export async function runAgentImpl(ctx, content) {
|
|
|
567
583
|
ctx.llmMessages.current.push({ role: 'assistant', content: response.content });
|
|
568
584
|
ctx.addMessage('assistant', response.content);
|
|
569
585
|
recordEvent('output', response.content.slice(0, 5000));
|
|
586
|
+
// Mirror assistant message to scuttlebot
|
|
587
|
+
if (scuttlebotClient.isEnabled()) {
|
|
588
|
+
scuttlebotClient.mirrorAssistant(response.content).catch(() => {
|
|
589
|
+
// Silent fail
|
|
590
|
+
});
|
|
591
|
+
}
|
|
570
592
|
ctx.setStreamingResponse('');
|
|
571
593
|
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
572
594
|
checkAndWarnContextLimit(ctx.actualProvider, ctx.actualModel, ctx.estimateContextTokens(), ctx.addMessage);
|
|
@@ -578,6 +600,7 @@ export async function runAgentImpl(ctx, content) {
|
|
|
578
600
|
continue; // Loop again to get continuation
|
|
579
601
|
}
|
|
580
602
|
completedNaturally = true;
|
|
603
|
+
runStatus = 'completed';
|
|
581
604
|
// End iteration ledger entry
|
|
582
605
|
ctx.ledger?.endIteration('success');
|
|
583
606
|
// Auto-save full message history for session persistence
|
|
@@ -609,6 +632,8 @@ export async function runAgentImpl(ctx, content) {
|
|
|
609
632
|
ctx.setBreakerHealth?.(ctx.circuitBreaker.getHealth());
|
|
610
633
|
if (breakerResult.tripped) {
|
|
611
634
|
ctx.addMessage('system', `\u26a0\ufe0f Circuit breaker tripped: ${breakerResult.breaker}\n${breakerResult.message}\n\nUse /breaker resume to continue.`);
|
|
635
|
+
runStatus = 'stopped';
|
|
636
|
+
runErrorSummary = breakerResult.message;
|
|
612
637
|
completedNaturally = true;
|
|
613
638
|
break;
|
|
614
639
|
}
|
|
@@ -635,18 +660,30 @@ export async function runAgentImpl(ctx, content) {
|
|
|
635
660
|
}
|
|
636
661
|
// Non-retryable errors (auth, etc.) still kill the session
|
|
637
662
|
completedNaturally = true;
|
|
663
|
+
runStatus = 'failed';
|
|
664
|
+
runErrorSummary = errorMsg;
|
|
638
665
|
// On error, clear queued messages to prevent infinite retry loop
|
|
639
666
|
const currentQueuedOnError = ctx.queuedMessagesRef.current;
|
|
640
667
|
if (currentQueuedOnError.length > 0) {
|
|
641
668
|
ctx.addMessage('system', `\u26a0\ufe0f Cleared ${currentQueuedOnError.length} queued message(s) due to error. Use /clear to reset conversation.`);
|
|
642
669
|
ctx.setQueuedMessages([]);
|
|
643
670
|
}
|
|
671
|
+
if (runId && runStatus) {
|
|
672
|
+
ctx.ledger?.finishRun(runId, runStatus, { errorSummary: runErrorSummary });
|
|
673
|
+
}
|
|
644
674
|
return; // Exit early on error - don't process queued messages
|
|
645
675
|
}
|
|
646
676
|
}
|
|
647
677
|
// Only show warning if we actually hit the iteration limit (not errors or natural completion)
|
|
648
|
-
if (!completedNaturally) {
|
|
678
|
+
if (!completedNaturally && isFiniteIterationLimit(maxIterations)) {
|
|
649
679
|
ctx.addMessage('system', `⚠️ Reached ${maxIterations} iterations limit. Task may be incomplete. Adjust with /set maxIterations <number>.`);
|
|
680
|
+
if (!runStatus) {
|
|
681
|
+
runStatus = 'stopped';
|
|
682
|
+
runErrorSummary = `Reached ${maxIterations} iterations limit`;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (runId) {
|
|
686
|
+
ctx.ledger?.finishRun(runId, runStatus || 'completed', { errorSummary: runErrorSummary });
|
|
650
687
|
}
|
|
651
688
|
// Update context tokens after agent run
|
|
652
689
|
ctx.setContextTokens(ctx.estimateContextTokens());
|
|
@@ -684,29 +721,6 @@ export async function runAgentImpl(ctx, content) {
|
|
|
684
721
|
// ============================================================================
|
|
685
722
|
// Run Loop
|
|
686
723
|
// ============================================================================
|
|
687
|
-
/**
|
|
688
|
-
* Start caffeinate to prevent system sleep during long operations (macOS).
|
|
689
|
-
*/
|
|
690
|
-
function startCaffeinate() {
|
|
691
|
-
if (process.platform !== 'darwin')
|
|
692
|
-
return null;
|
|
693
|
-
try {
|
|
694
|
-
const proc = spawn('caffeinate', ['-di'], { stdio: 'ignore', detached: true });
|
|
695
|
-
proc.unref();
|
|
696
|
-
return proc;
|
|
697
|
-
}
|
|
698
|
-
catch {
|
|
699
|
-
return null;
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
function stopCaffeinate(proc) {
|
|
703
|
-
if (proc) {
|
|
704
|
-
try {
|
|
705
|
-
proc.kill('SIGTERM');
|
|
706
|
-
}
|
|
707
|
-
catch { /* already dead */ }
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
724
|
/**
|
|
711
725
|
* Agent loop - runs prompt repeatedly until completion promise or max iterations.
|
|
712
726
|
*/
|
|
@@ -714,55 +728,95 @@ export async function runLoopImpl(ctx, prompt, maxIter, completionPromise) {
|
|
|
714
728
|
ctx.setIsProcessing(true);
|
|
715
729
|
setMood('focused');
|
|
716
730
|
// Prevent system sleep during long agent loops (macOS)
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
731
|
+
startPreventSleep();
|
|
732
|
+
let completedIterations = 0;
|
|
733
|
+
let loopOutcome = 'running';
|
|
734
|
+
let loopErrorSummary;
|
|
735
|
+
const loopRunId = ctx.ledger?.startRun('loop', prompt, {
|
|
736
|
+
completionPromise,
|
|
737
|
+
maxIterations: isFiniteIterationLimit(maxIter) ? maxIter : null,
|
|
738
|
+
});
|
|
739
|
+
try {
|
|
740
|
+
for (let i = 0; i < maxIter; i++) {
|
|
741
|
+
// Check if cancelled
|
|
742
|
+
if (ctx.loopCancelledRef.current) {
|
|
743
|
+
ctx.addMessage('system', '🛑 Loop cancelled by user');
|
|
744
|
+
loopOutcome = 'cancelled';
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
completedIterations = i + 1;
|
|
748
|
+
ctx.setLoopIteration(completedIterations);
|
|
749
|
+
ctx.addMessage('system', `🔄 Loop iteration ${formatIterationProgress(completedIterations, maxIter)}`);
|
|
750
|
+
// First iteration: send original prompt. Subsequent: send continuation.
|
|
751
|
+
const iterationPrompt = i === 0
|
|
752
|
+
? prompt
|
|
753
|
+
: `Continue working on the task: "${prompt}"\n\nThis is iteration ${i + 1}. Review what you've done so far and continue making progress.`;
|
|
754
|
+
try {
|
|
755
|
+
// Run the agent
|
|
756
|
+
await runAgentImpl(ctx, iterationPrompt);
|
|
757
|
+
// Check for completion promise in the last assistant message
|
|
758
|
+
if (completionPromise) {
|
|
759
|
+
const lastMessage = ctx.llmMessages.current[ctx.llmMessages.current.length - 1];
|
|
760
|
+
if (lastMessage?.role === 'assistant') {
|
|
761
|
+
const content = typeof lastMessage.content === 'string'
|
|
762
|
+
? lastMessage.content
|
|
763
|
+
: JSON.stringify(lastMessage.content);
|
|
764
|
+
if (content.includes(completionPromise)) {
|
|
765
|
+
ctx.addMessage('system', `🎉 Completion promise "${completionPromise}" detected! Loop finished.`);
|
|
766
|
+
loopOutcome = 'promise-met';
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
744
769
|
}
|
|
745
770
|
}
|
|
771
|
+
// Check cancelled again after agent run
|
|
772
|
+
if (ctx.loopCancelledRef.current) {
|
|
773
|
+
ctx.addMessage('system', '🛑 Loop cancelled by user');
|
|
774
|
+
loopOutcome = 'cancelled';
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
// Small delay between iterations
|
|
778
|
+
if (i + 1 < maxIter) {
|
|
779
|
+
await new Promise(r => setTimeout(r, 500));
|
|
780
|
+
}
|
|
746
781
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
ctx.addMessage('
|
|
782
|
+
catch (error) {
|
|
783
|
+
loopErrorSummary = error instanceof Error ? error.message : String(error);
|
|
784
|
+
ctx.addMessage('error', `Loop error: ${loopErrorSummary}`);
|
|
785
|
+
loopOutcome = 'error';
|
|
750
786
|
break;
|
|
751
787
|
}
|
|
752
|
-
// Small delay between iterations
|
|
753
|
-
await new Promise(r => setTimeout(r, 500));
|
|
754
788
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
789
|
+
if (loopOutcome === 'running') {
|
|
790
|
+
if (completionPromise) {
|
|
791
|
+
ctx.addMessage('system', `⚠️ Loop stopped after ${completedIterations} iteration${completedIterations === 1 ? '' : 's'} without matching completion promise "${completionPromise}".`);
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
ctx.addMessage('system', `✅ Loop completed ${completedIterations} iteration${completedIterations === 1 ? '' : 's'}.`);
|
|
795
|
+
}
|
|
758
796
|
}
|
|
759
797
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
798
|
+
finally {
|
|
799
|
+
if (loopRunId) {
|
|
800
|
+
const finalStatus = loopOutcome === 'cancelled'
|
|
801
|
+
? 'cancelled'
|
|
802
|
+
: loopOutcome === 'error'
|
|
803
|
+
? 'failed'
|
|
804
|
+
: loopOutcome === 'promise-met'
|
|
805
|
+
? 'completed'
|
|
806
|
+
: completionPromise
|
|
807
|
+
? 'stopped'
|
|
808
|
+
: 'completed';
|
|
809
|
+
ctx.ledger?.finishRun(loopRunId, finalStatus, {
|
|
810
|
+
errorSummary: loopErrorSummary
|
|
811
|
+
|| (finalStatus === 'cancelled' ? 'Stopped by user' : undefined)
|
|
812
|
+
|| (finalStatus === 'stopped' && completionPromise
|
|
813
|
+
? `Stopped after ${completedIterations} iterations without matching completion promise`
|
|
814
|
+
: undefined),
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
ctx.setLoopActive(false);
|
|
818
|
+
ctx.setIsProcessing(false);
|
|
819
|
+
stopPreventSleep();
|
|
763
820
|
}
|
|
764
|
-
ctx.setLoopActive(false);
|
|
765
|
-
ctx.setIsProcessing(false);
|
|
766
|
-
stopCaffeinate(caffeinateProc);
|
|
767
821
|
}
|
|
768
822
|
//# sourceMappingURL=agent.js.map
|