openhorse 0.2.11 → 0.2.13
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/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +681 -6
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/types.d.ts +3 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/core/checkpoint.d.ts +1 -0
- package/dist/core/checkpoint.d.ts.map +1 -1
- package/dist/core/checkpoint.js +59 -10
- package/dist/core/checkpoint.js.map +1 -1
- package/dist/core/tool-artifacts.d.ts +10 -0
- package/dist/core/tool-artifacts.d.ts.map +1 -1
- package/dist/core/tool-artifacts.js +81 -3
- package/dist/core/tool-artifacts.js.map +1 -1
- package/dist/framework/index.d.ts +2 -2
- package/dist/framework/index.d.ts.map +1 -1
- package/dist/framework/index.js +5 -1
- package/dist/framework/index.js.map +1 -1
- package/dist/framework/query.d.ts +104 -1
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +454 -18
- package/dist/framework/query.js.map +1 -1
- package/dist/framework/store.d.ts +5 -0
- package/dist/framework/store.d.ts.map +1 -1
- package/dist/framework/store.js +4 -0
- package/dist/framework/store.js.map +1 -1
- package/dist/framework/tool-scheduler.d.ts +9 -0
- package/dist/framework/tool-scheduler.d.ts.map +1 -1
- package/dist/framework/tool-scheduler.js +34 -0
- package/dist/framework/tool-scheduler.js.map +1 -1
- package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
- package/dist/ink-ui/components/StatusLine.js +7 -1
- package/dist/ink-ui/components/StatusLine.js.map +1 -1
- package/dist/runtime/agent-runtime-protocol.d.ts +13 -1
- package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-protocol.js +24 -0
- package/dist/runtime/agent-runtime-protocol.js.map +1 -1
- package/dist/runtime/chat-controller.d.ts +3 -0
- package/dist/runtime/chat-controller.d.ts.map +1 -1
- package/dist/runtime/chat-controller.js +930 -5
- package/dist/runtime/chat-controller.js.map +1 -1
- package/dist/runtime/loop-budget.d.ts +5 -0
- package/dist/runtime/loop-budget.d.ts.map +1 -0
- package/dist/runtime/loop-budget.js +77 -0
- package/dist/runtime/loop-budget.js.map +1 -0
- package/dist/runtime/ui-events.d.ts +39 -1
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/services/config-dir.d.ts +2 -0
- package/dist/services/config-dir.d.ts.map +1 -1
- package/dist/services/config-dir.js +5 -0
- package/dist/services/config-dir.js.map +1 -1
- package/dist/services/config.d.ts +4 -2
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +19 -0
- package/dist/services/config.js.map +1 -1
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +34 -4
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/global-config.d.ts +17 -0
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/global-config.js.map +1 -1
- package/dist/services/llm.d.ts +18 -0
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +175 -163
- package/dist/services/llm.js.map +1 -1
- package/dist/services/provider-diagnostics.d.ts +30 -0
- package/dist/services/provider-diagnostics.d.ts.map +1 -0
- package/dist/services/provider-diagnostics.js +308 -0
- package/dist/services/provider-diagnostics.js.map +1 -0
- package/dist/services/redaction.d.ts +2 -0
- package/dist/services/redaction.d.ts.map +1 -0
- package/dist/services/redaction.js +27 -0
- package/dist/services/redaction.js.map +1 -0
- package/dist/services/session-index.d.ts.map +1 -1
- package/dist/services/session-index.js +7 -3
- package/dist/services/session-index.js.map +1 -1
- package/dist/services/session-storage.d.ts +100 -0
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +154 -3
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/verification-profile.d.ts +36 -0
- package/dist/services/verification-profile.d.ts.map +1 -0
- package/dist/services/verification-profile.js +239 -0
- package/dist/services/verification-profile.js.map +1 -0
- package/dist/services/workspace-state.d.ts +25 -0
- package/dist/services/workspace-state.d.ts.map +1 -0
- package/dist/services/workspace-state.js +115 -0
- package/dist/services/workspace-state.js.map +1 -0
- package/dist/skills/loader.d.ts +5 -1
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +34 -0
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/registry.d.ts +5 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +23 -0
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/runtime.d.ts +6 -0
- package/dist/skills/runtime.d.ts.map +1 -1
- package/dist/skills/runtime.js +88 -4
- package/dist/skills/runtime.js.map +1 -1
- package/dist/skills/types.d.ts +19 -0
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/skills/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.InkChatController = exports.AgentChatController = void 0;
|
|
4
37
|
exports.sessionMessagesToTranscriptEntries = sessionMessagesToTranscriptEntries;
|
|
@@ -8,17 +41,26 @@ exports.loadSessionIntoRuntime = loadSessionIntoRuntime;
|
|
|
8
41
|
exports.closeSession = closeSession;
|
|
9
42
|
const commands_1 = require("../commands");
|
|
10
43
|
const parser_1 = require("../commands/parser");
|
|
44
|
+
const path = __importStar(require("path"));
|
|
11
45
|
const session_storage_1 = require("../services/session-storage");
|
|
12
46
|
const config_1 = require("../services/config");
|
|
13
47
|
const framework_1 = require("../framework");
|
|
14
48
|
const harness_1 = require("../harness");
|
|
15
49
|
const tools_1 = require("../tools");
|
|
50
|
+
const tool_serializer_1 = require("../framework/tool-serializer");
|
|
51
|
+
const tool_artifacts_1 = require("../core/tool-artifacts");
|
|
52
|
+
const checkpoint_1 = require("../core/checkpoint");
|
|
16
53
|
const skills_1 = require("../skills");
|
|
17
54
|
const file_context_1 = require("../services/file-context");
|
|
18
55
|
const prompt_context_1 = require("../services/prompt-context");
|
|
56
|
+
const workspace_state_1 = require("../services/workspace-state");
|
|
57
|
+
const verification_profile_1 = require("../services/verification-profile");
|
|
19
58
|
const ui_events_1 = require("./ui-events");
|
|
20
59
|
const agent_status_1 = require("./agent-status");
|
|
60
|
+
const loop_budget_1 = require("./loop-budget");
|
|
21
61
|
const ANSI_PATTERN = /\x1b\[[0-9;?]*[A-Za-z]/g;
|
|
62
|
+
const LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES = 2048;
|
|
63
|
+
const TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES = 160;
|
|
22
64
|
function stripAnsi(text) {
|
|
23
65
|
return text.replace(ANSI_PATTERN, '');
|
|
24
66
|
}
|
|
@@ -64,6 +106,316 @@ function compactToolArgs(args, maxLength = 160) {
|
|
|
64
106
|
}
|
|
65
107
|
return '';
|
|
66
108
|
}
|
|
109
|
+
function fullToolArgsForTrace(name, args) {
|
|
110
|
+
if (name === 'exec_command' && typeof args.command === 'string') {
|
|
111
|
+
return `$ ${args.command}`;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return JSON.stringify(args, null, 2);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return compactToolArgs(args, 2048);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function buildTraceArgsDetails(projectPath, name, args) {
|
|
121
|
+
const argsSummary = compactToolArgs(args);
|
|
122
|
+
const fullArgs = (0, session_storage_1.redactTraceText)(fullToolArgsForTrace(name, args)).trim();
|
|
123
|
+
const argsBytes = byteLength(fullArgs);
|
|
124
|
+
if (!projectPath
|
|
125
|
+
|| !fullArgs
|
|
126
|
+
|| fullArgs === (0, session_storage_1.redactTraceText)(argsSummary)
|
|
127
|
+
|| argsBytes <= TRACE_ARGS_ARTIFACT_THRESHOLD_BYTES) {
|
|
128
|
+
return { argsSummary };
|
|
129
|
+
}
|
|
130
|
+
const artifact = (0, tool_artifacts_1.storeArtifact)(projectPath, `${name}-args`, fullArgs, argsBytes);
|
|
131
|
+
return artifact
|
|
132
|
+
? { argsSummary, argsArtifactId: artifact.id, argsBytes }
|
|
133
|
+
: { argsSummary };
|
|
134
|
+
}
|
|
135
|
+
function parseToolCallArgsForRuntime(toolCall) {
|
|
136
|
+
try {
|
|
137
|
+
const parsed = JSON.parse(toolCall.function.arguments || '{}');
|
|
138
|
+
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
|
|
139
|
+
? parsed
|
|
140
|
+
: null;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function resolveProjectScopedPath(cwd, filePath) {
|
|
147
|
+
const absolute = path.resolve(cwd, filePath);
|
|
148
|
+
const relative = path.relative(cwd, absolute);
|
|
149
|
+
if (relative.startsWith('..') || path.isAbsolute(relative))
|
|
150
|
+
return null;
|
|
151
|
+
return absolute;
|
|
152
|
+
}
|
|
153
|
+
function checkpointTargetsFromToolCalls(cwd, toolCalls) {
|
|
154
|
+
const targets = new Set();
|
|
155
|
+
for (const toolCall of toolCalls) {
|
|
156
|
+
const name = toolCall.function.name;
|
|
157
|
+
if (name !== 'write_file' && name !== 'edit_file')
|
|
158
|
+
continue;
|
|
159
|
+
const args = parseToolCallArgsForRuntime(toolCall);
|
|
160
|
+
if (!args || typeof args.path !== 'string')
|
|
161
|
+
continue;
|
|
162
|
+
if (name === 'edit_file' && args.preview === true)
|
|
163
|
+
continue;
|
|
164
|
+
const target = resolveProjectScopedPath(cwd, args.path);
|
|
165
|
+
if (target)
|
|
166
|
+
targets.add(target);
|
|
167
|
+
}
|
|
168
|
+
return Array.from(targets);
|
|
169
|
+
}
|
|
170
|
+
function createPreToolCheckpoint(events, sessionId, turnId, checkpointId, cwd, toolCalls) {
|
|
171
|
+
const targets = checkpointTargetsFromToolCalls(cwd, toolCalls);
|
|
172
|
+
if (targets.length === 0)
|
|
173
|
+
return false;
|
|
174
|
+
const checkpoint = (0, checkpoint_1.createCheckpoint)(cwd, checkpointId, targets);
|
|
175
|
+
if (!sessionId)
|
|
176
|
+
return true;
|
|
177
|
+
const relativeTargets = targets.map(target => path.relative(cwd, target));
|
|
178
|
+
recordTraceEvent(events, sessionId, {
|
|
179
|
+
turnId,
|
|
180
|
+
type: 'checkpoint',
|
|
181
|
+
checkpointId,
|
|
182
|
+
checkpointFileCount: checkpoint?.files.length ?? 0,
|
|
183
|
+
checkpointFiles: checkpoint?.files.map(file => file.path) ?? [],
|
|
184
|
+
workspaceFiles: relativeTargets,
|
|
185
|
+
note: checkpoint
|
|
186
|
+
? 'pre_edit_checkpoint'
|
|
187
|
+
: 'pre_edit_checkpoint_skipped',
|
|
188
|
+
});
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
function byteLength(text) {
|
|
192
|
+
return Buffer.byteLength(text, 'utf8');
|
|
193
|
+
}
|
|
194
|
+
function traceTurnId(turnId) {
|
|
195
|
+
return turnId == null ? `turn-${Date.now()}` : String(turnId);
|
|
196
|
+
}
|
|
197
|
+
function compactTraceError(error) {
|
|
198
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
199
|
+
return compactMiddle(message, 240);
|
|
200
|
+
}
|
|
201
|
+
function getLastRequestDiagnostics(llm) {
|
|
202
|
+
if (!llm)
|
|
203
|
+
return undefined;
|
|
204
|
+
const reader = llm.getLastRequestDiagnostics;
|
|
205
|
+
return typeof reader === 'function' ? reader.call(llm) : undefined;
|
|
206
|
+
}
|
|
207
|
+
function compactPathList(paths, maxItems = 40) {
|
|
208
|
+
return paths.slice(0, maxItems);
|
|
209
|
+
}
|
|
210
|
+
function formatWorkspaceFileForTrace(file) {
|
|
211
|
+
const metadata = [
|
|
212
|
+
typeof file.sizeBytes === 'number' ? `${file.sizeBytes}B` : '',
|
|
213
|
+
typeof file.mtimeMs === 'number' ? `mtime=${file.mtimeMs}` : '',
|
|
214
|
+
].filter(Boolean).join(' ');
|
|
215
|
+
return `${file.status} ${file.path}${metadata ? ` (${metadata})` : ''}`;
|
|
216
|
+
}
|
|
217
|
+
function appendWorkspaceSnapshotTrace(events, sessionId, turnId, phase, snapshot) {
|
|
218
|
+
if (!sessionId)
|
|
219
|
+
return;
|
|
220
|
+
recordTraceEvent(events, sessionId, {
|
|
221
|
+
turnId,
|
|
222
|
+
type: 'workspace_snapshot',
|
|
223
|
+
workspacePhase: phase,
|
|
224
|
+
workspaceGitAvailable: snapshot.gitAvailable,
|
|
225
|
+
workspaceDirty: snapshot.dirty,
|
|
226
|
+
workspaceBranch: snapshot.branch,
|
|
227
|
+
workspaceFileCount: snapshot.fileCount,
|
|
228
|
+
workspaceFiles: compactPathList(snapshot.files.map(formatWorkspaceFileForTrace)),
|
|
229
|
+
error: snapshot.error ? compactMiddle(snapshot.error, 240) : undefined,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function appendWorkspaceDeltaTrace(events, sessionId, turnId, before, after) {
|
|
233
|
+
const delta = (0, workspace_state_1.diffWorkspaceSnapshots)(before, after);
|
|
234
|
+
if (sessionId) {
|
|
235
|
+
recordTraceEvent(events, sessionId, {
|
|
236
|
+
turnId,
|
|
237
|
+
type: 'workspace_delta',
|
|
238
|
+
workspaceFileCount: delta.filesAfterTurn.length,
|
|
239
|
+
workspaceFiles: compactPathList(delta.filesAfterTurn),
|
|
240
|
+
workspaceNewByTurn: compactPathList(delta.newFilesByTurn),
|
|
241
|
+
workspaceChangedByTurn: compactPathList(delta.changedByTurn),
|
|
242
|
+
workspaceModifiedPreExistingByTurn: compactPathList(delta.modifiedPreExistingByTurn),
|
|
243
|
+
workspaceResolvedByTurn: compactPathList(delta.resolvedByTurn),
|
|
244
|
+
note: `pre_existing=${delta.preExistingFiles.length}`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return delta;
|
|
248
|
+
}
|
|
249
|
+
function workspaceDeltaHasTurnChanges(delta) {
|
|
250
|
+
return delta.newFilesByTurn.length > 0
|
|
251
|
+
|| delta.changedByTurn.length > 0
|
|
252
|
+
|| delta.resolvedByTurn.length > 0;
|
|
253
|
+
}
|
|
254
|
+
function formatFailureRecoveryNotice(turnId, delta, checkpointIds) {
|
|
255
|
+
const files = compactPathList([
|
|
256
|
+
...delta.newFilesByTurn,
|
|
257
|
+
...delta.changedByTurn,
|
|
258
|
+
...delta.resolvedByTurn,
|
|
259
|
+
], 8);
|
|
260
|
+
const fileText = files.length > 0
|
|
261
|
+
? files.join(', ')
|
|
262
|
+
: 'workspace changes recorded';
|
|
263
|
+
const checkpointText = checkpointIds.length > 0
|
|
264
|
+
? ` Checkpoints: ${checkpointIds.join(', ')}. Preview rollback with /checkpoint restore <id>; restore each listed checkpoint if multiple.`
|
|
265
|
+
: '';
|
|
266
|
+
return `Turn failed after modifying files: ${fileText}. Inspect /trace ${turnId}.${checkpointText}`;
|
|
267
|
+
}
|
|
268
|
+
function appendVerificationProfileTrace(events, sessionId, turnId, profile) {
|
|
269
|
+
if (!sessionId || profile.changedFiles.length === 0)
|
|
270
|
+
return;
|
|
271
|
+
recordTraceEvent(events, sessionId, {
|
|
272
|
+
turnId,
|
|
273
|
+
type: 'verification_profile',
|
|
274
|
+
verificationProfile: profile.profile,
|
|
275
|
+
verificationRequired: profile.required,
|
|
276
|
+
verificationCommands: compactPathList(profile.commands, 8),
|
|
277
|
+
verificationChangedFiles: compactPathList(profile.changedFiles),
|
|
278
|
+
note: profile.reason,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function appendVerificationResultTrace(events, sessionId, turnId, result) {
|
|
282
|
+
if (!sessionId)
|
|
283
|
+
return;
|
|
284
|
+
recordTraceEvent(events, sessionId, {
|
|
285
|
+
turnId,
|
|
286
|
+
type: 'verification_result',
|
|
287
|
+
verificationCommand: result.command,
|
|
288
|
+
verificationPassed: result.success,
|
|
289
|
+
outputBytes: result.outputBytes,
|
|
290
|
+
error: result.error ? compactMiddle(result.error, 240) : undefined,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function appendVerificationSummaryTrace(events, sessionId, turnId, summary, changedFiles) {
|
|
294
|
+
if (!sessionId || changedFiles.length === 0)
|
|
295
|
+
return;
|
|
296
|
+
recordTraceEvent(events, sessionId, {
|
|
297
|
+
turnId,
|
|
298
|
+
type: 'verification_summary',
|
|
299
|
+
verificationProfile: summary.profile,
|
|
300
|
+
verificationRequired: summary.required,
|
|
301
|
+
verificationCommands: compactPathList(summary.commandsRun, 12),
|
|
302
|
+
verificationPassedCommands: compactPathList(summary.passedCommands, 12),
|
|
303
|
+
verificationFailedCommands: compactPathList(summary.failedCommands, 12),
|
|
304
|
+
verificationMissingCommands: compactPathList(summary.missingCommands, 12),
|
|
305
|
+
verificationChangedFiles: compactPathList(changedFiles),
|
|
306
|
+
verificationClaimAllowed: summary.claimAllowed,
|
|
307
|
+
note: summary.skippedReason,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
function compactVerificationCommands(commands, maxItems = 12) {
|
|
311
|
+
return commands.slice(0, maxItems).map(session_storage_1.redactTraceText);
|
|
312
|
+
}
|
|
313
|
+
function withVerificationLoopStats(stats, summary) {
|
|
314
|
+
return {
|
|
315
|
+
...stats,
|
|
316
|
+
verificationProfile: summary.profile,
|
|
317
|
+
verificationRequired: summary.required,
|
|
318
|
+
verificationClaimAllowed: summary.claimAllowed,
|
|
319
|
+
verificationPassedCommands: compactVerificationCommands(summary.passedCommands),
|
|
320
|
+
verificationFailedCommands: compactVerificationCommands(summary.failedCommands),
|
|
321
|
+
verificationMissingCommands: compactVerificationCommands(summary.missingCommands),
|
|
322
|
+
verificationSkippedReason: summary.skippedReason ? (0, session_storage_1.redactTraceText)(summary.skippedReason) : undefined,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function shouldRecordVerificationLoopStats(profile, summary) {
|
|
326
|
+
return profile.changedFiles.length > 0
|
|
327
|
+
|| summary.commandsRun.length > 0
|
|
328
|
+
|| summary.passedCommands.length > 0
|
|
329
|
+
|| summary.failedCommands.length > 0
|
|
330
|
+
|| summary.missingCommands.length > 0;
|
|
331
|
+
}
|
|
332
|
+
function appendPostWorkspaceTrace(events, sessionId, turnId, cwd, before, verificationResults = []) {
|
|
333
|
+
const postWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(cwd);
|
|
334
|
+
appendWorkspaceSnapshotTrace(events, sessionId, turnId, 'post_turn', postWorkspace);
|
|
335
|
+
const delta = appendWorkspaceDeltaTrace(events, sessionId, turnId, before, postWorkspace);
|
|
336
|
+
const profile = (0, verification_profile_1.selectVerificationProfile)(cwd, delta.changedByTurn);
|
|
337
|
+
const summary = (0, verification_profile_1.summarizeVerificationState)(profile, verificationResults);
|
|
338
|
+
appendVerificationProfileTrace(events, sessionId, turnId, profile);
|
|
339
|
+
appendVerificationSummaryTrace(events, sessionId, turnId, summary, profile.changedFiles);
|
|
340
|
+
return { delta, profile, summary };
|
|
341
|
+
}
|
|
342
|
+
function appendAssistantNotice(messages, notice) {
|
|
343
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
344
|
+
const message = messages[index];
|
|
345
|
+
if (message.role === 'assistant' && !message.tool_calls) {
|
|
346
|
+
message.content = message.content ? `${message.content}\n\n${notice}` : notice;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
messages.push({
|
|
351
|
+
role: 'assistant',
|
|
352
|
+
content: notice,
|
|
353
|
+
timestamp: Date.now(),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function recordTraceEvent(events, sessionId, event) {
|
|
357
|
+
if (!sessionId)
|
|
358
|
+
return null;
|
|
359
|
+
const traceEvent = (0, session_storage_1.appendSessionTraceEvent)(sessionId, event);
|
|
360
|
+
if (traceEvent) {
|
|
361
|
+
events.traceEventRecorded?.(traceEvent);
|
|
362
|
+
}
|
|
363
|
+
return traceEvent;
|
|
364
|
+
}
|
|
365
|
+
function recordProviderTraceEvents(events, sessionId, turnId, stats) {
|
|
366
|
+
if ((stats.providerRetryCount ?? 0) > 0) {
|
|
367
|
+
recordTraceEvent(events, sessionId, {
|
|
368
|
+
turnId,
|
|
369
|
+
type: 'provider_retry',
|
|
370
|
+
providerRetryCount: stats.providerRetryCount,
|
|
371
|
+
providerRetryDelayMs: stats.providerRetryDelayMs,
|
|
372
|
+
providerRetryErrorTypes: stats.providerRetryErrorTypes,
|
|
373
|
+
providerLastRetryErrorType: stats.providerLastRetryErrorType,
|
|
374
|
+
providerLastRetryStatus: stats.providerLastRetryStatus,
|
|
375
|
+
providerFinalModel: stats.providerFinalModel,
|
|
376
|
+
providerUsingFallback: stats.providerUsingFallback,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if ((stats.providerFallbackCount ?? 0) > 0 || stats.providerUsingFallback) {
|
|
380
|
+
recordTraceEvent(events, sessionId, {
|
|
381
|
+
turnId,
|
|
382
|
+
type: 'provider_fallback',
|
|
383
|
+
providerFallbackCount: stats.providerFallbackCount,
|
|
384
|
+
providerFallbackFromModel: stats.providerFallbackFromModel,
|
|
385
|
+
providerFallbackToModel: stats.providerFallbackToModel,
|
|
386
|
+
providerFinalModel: stats.providerFinalModel,
|
|
387
|
+
providerUsingFallback: stats.providerUsingFallback,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function toHarnessDiagnostics(state) {
|
|
392
|
+
const stats = state.promptAssemblyStats;
|
|
393
|
+
const redactOptional = (value) => typeof value === 'string' ? (0, session_storage_1.redactTraceText)(value) : undefined;
|
|
394
|
+
const redactList = (values) => values?.slice(0, 6).map(session_storage_1.redactTraceText);
|
|
395
|
+
return {
|
|
396
|
+
taskEpoch: state.taskEpoch,
|
|
397
|
+
rootObjective: redactOptional(state.rootObjective ?? state.contract?.objective),
|
|
398
|
+
activeInstruction: redactOptional(state.activeInstruction ?? state.contract?.userIntent),
|
|
399
|
+
openQuestions: redactList(state.openQuestions),
|
|
400
|
+
diagnostics: redactList(state.diagnostics?.slice(-6)),
|
|
401
|
+
ledgerSize: state.ledger?.length ?? 0,
|
|
402
|
+
evidenceSize: state.evidenceIndex?.length ?? 0,
|
|
403
|
+
turnSummaryCount: state.turnSummaries?.length ?? 0,
|
|
404
|
+
promptAssembly: stats
|
|
405
|
+
? {
|
|
406
|
+
modelId: stats.modelId,
|
|
407
|
+
estimatedTokens: stats.estimatedTokens,
|
|
408
|
+
budgetTokens: stats.budgetTokens,
|
|
409
|
+
sections: stats.sections.slice(0, 12),
|
|
410
|
+
includedEvidence: stats.includedEvidence.length,
|
|
411
|
+
omittedEvidence: stats.omittedEvidence.length,
|
|
412
|
+
}
|
|
413
|
+
: undefined,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function emitHarnessDiagnostics(events, state) {
|
|
417
|
+
events.harnessDiagnosticsUpdated?.(toHarnessDiagnostics(state));
|
|
418
|
+
}
|
|
67
419
|
function toolStartContent(name, args) {
|
|
68
420
|
if (name === 'exec_command' && typeof args.command === 'string') {
|
|
69
421
|
return `Running ${name}\n $ ${args.command}`;
|
|
@@ -76,6 +428,23 @@ function toolSummary(name, args, success, duration) {
|
|
|
76
428
|
const suffix = details ? ` ${details}` : '';
|
|
77
429
|
return `${success ? '✓' : '✗'} ${name}${suffix} (${duration}ms)`;
|
|
78
430
|
}
|
|
431
|
+
function toolFinishContent(event) {
|
|
432
|
+
const summary = event.summary || toolSummary(event.name, event.args, event.success, event.duration);
|
|
433
|
+
const lines = [summary];
|
|
434
|
+
if (event.name === 'exec_command' && typeof event.args.command === 'string') {
|
|
435
|
+
lines.push(` $ ${event.args.command}`);
|
|
436
|
+
}
|
|
437
|
+
if (event.artifactRef) {
|
|
438
|
+
lines.push(` artifact ${event.artifactRef.id} (${event.artifactRef.outputBytes}B full output)`);
|
|
439
|
+
}
|
|
440
|
+
else if (typeof event.outputBytes === 'number') {
|
|
441
|
+
lines.push(` output ${event.outputBytes}B`);
|
|
442
|
+
}
|
|
443
|
+
if (event.error) {
|
|
444
|
+
lines.push(`Error: ${event.error}`);
|
|
445
|
+
}
|
|
446
|
+
return lines.filter(Boolean).join('\n');
|
|
447
|
+
}
|
|
79
448
|
function isSyntheticCompactContext(content) {
|
|
80
449
|
return content.startsWith('[OpenHorse Context State v2]')
|
|
81
450
|
|| content.startsWith('[Context Summary]')
|
|
@@ -237,8 +606,76 @@ function createAssistantStreamPresenter(events, abortSignal) {
|
|
|
237
606
|
activeSegmentText = content;
|
|
238
607
|
ensureLiveEntry();
|
|
239
608
|
},
|
|
609
|
+
replaceMessage(content) {
|
|
610
|
+
if (abortSignal?.aborted || !content)
|
|
611
|
+
return;
|
|
612
|
+
activeSegmentText = content;
|
|
613
|
+
ensureLiveEntry();
|
|
614
|
+
},
|
|
240
615
|
};
|
|
241
616
|
}
|
|
617
|
+
class LocalFastPathBlockedError extends Error {
|
|
618
|
+
constructor(message) {
|
|
619
|
+
super(message);
|
|
620
|
+
this.name = 'LocalFastPathBlockedError';
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function parseLocalFastPath(input) {
|
|
624
|
+
const text = input.trim();
|
|
625
|
+
if (/^git\s+status$/i.test(text)) {
|
|
626
|
+
return { tool: 'git_status', args: {}, label: 'git status' };
|
|
627
|
+
}
|
|
628
|
+
const readMatch = /^(?:read|读取)\s+(.+)$/i.exec(text);
|
|
629
|
+
const readTarget = readMatch?.[1]?.trim();
|
|
630
|
+
const looksLikePath = Boolean(readTarget)
|
|
631
|
+
&& !/\s/.test(readTarget)
|
|
632
|
+
&& (/[/\\.]/.test(readTarget) || readTarget.startsWith('~'));
|
|
633
|
+
if (readTarget && looksLikePath) {
|
|
634
|
+
return { tool: 'read_file', args: { path: readTarget }, label: `read ${readTarget}` };
|
|
635
|
+
}
|
|
636
|
+
const grepMatch = /^(?:grep|搜索)\s+(.+)$/i.exec(text);
|
|
637
|
+
if (grepMatch?.[1]?.trim()) {
|
|
638
|
+
return { tool: 'grep', args: { pattern: grepMatch[1].trim() }, label: `grep ${grepMatch[1].trim()}` };
|
|
639
|
+
}
|
|
640
|
+
const runTestMatch = /^(?:run\s+test|运行测试)\s*[::]\s*(.+)$/i.exec(text);
|
|
641
|
+
if (runTestMatch?.[1]?.trim()) {
|
|
642
|
+
return { tool: 'exec_command', args: { command: runTestMatch[1].trim() }, label: `run test: ${runTestMatch[1].trim()}` };
|
|
643
|
+
}
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
function formatLocalFastPathAssistantContent(action, rawResult, projectPath) {
|
|
647
|
+
const envelope = (0, tool_serializer_1.parseToolResultEnvelope)(rawResult);
|
|
648
|
+
const output = typeof envelope.output === 'string' ? envelope.output.trim() : '';
|
|
649
|
+
const summary = envelope.summary || `${action.tool} ${envelope.success ? 'completed' : 'failed'}`;
|
|
650
|
+
const lines = [
|
|
651
|
+
envelope.success
|
|
652
|
+
? `Local fast path completed ${action.label}.`
|
|
653
|
+
: `Local fast path failed ${action.label}.`,
|
|
654
|
+
'',
|
|
655
|
+
summary,
|
|
656
|
+
];
|
|
657
|
+
if (!envelope.success && envelope.error) {
|
|
658
|
+
lines.push(`Error: ${envelope.error}`);
|
|
659
|
+
}
|
|
660
|
+
if (!output) {
|
|
661
|
+
return lines.join('\n');
|
|
662
|
+
}
|
|
663
|
+
let artifactRef = envelope.artifactRef;
|
|
664
|
+
let preview = output;
|
|
665
|
+
const outputBytes = envelope.outputBytes ?? byteLength(output);
|
|
666
|
+
if (byteLength(output) > LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES) {
|
|
667
|
+
if (!artifactRef) {
|
|
668
|
+
const artifact = (0, tool_artifacts_1.storeArtifact)(projectPath, action.tool, output, outputBytes);
|
|
669
|
+
artifactRef = artifact ? { id: artifact.id, outputBytes: artifact.outputBytes } : undefined;
|
|
670
|
+
}
|
|
671
|
+
preview = (0, tool_artifacts_1.truncateForContext)(output, LOCAL_FAST_PATH_INLINE_OUTPUT_BYTES);
|
|
672
|
+
}
|
|
673
|
+
if (artifactRef) {
|
|
674
|
+
lines.push('', `Full output: /artifacts show ${artifactRef.id} --full (${artifactRef.outputBytes}B)`);
|
|
675
|
+
}
|
|
676
|
+
lines.push('', 'Preview:', preview);
|
|
677
|
+
return lines.join('\n');
|
|
678
|
+
}
|
|
242
679
|
function createToolEventPresenter(events) {
|
|
243
680
|
const runningToolEntries = new Map();
|
|
244
681
|
return {
|
|
@@ -264,11 +701,10 @@ function createToolEventPresenter(events) {
|
|
|
264
701
|
duration: event.duration,
|
|
265
702
|
summary: event.summary,
|
|
266
703
|
error: event.error,
|
|
704
|
+
outputBytes: event.outputBytes,
|
|
705
|
+
artifactRef: event.artifactRef,
|
|
267
706
|
});
|
|
268
|
-
const content =
|
|
269
|
-
event.summary || toolSummary(event.name, event.args, event.success, event.duration),
|
|
270
|
-
event.error ? `Error: ${event.error}` : '',
|
|
271
|
-
].filter(Boolean).join('\n');
|
|
707
|
+
const content = toolFinishContent(event);
|
|
272
708
|
const existingEntryId = runningToolEntries.get(event.callId);
|
|
273
709
|
if (existingEntryId) {
|
|
274
710
|
events.finalize(existingEntryId, {
|
|
@@ -315,12 +751,21 @@ class AgentChatController {
|
|
|
315
751
|
this.events = events;
|
|
316
752
|
this.controllerOptions = controllerOptions;
|
|
317
753
|
}
|
|
754
|
+
setLoopStats(stats) {
|
|
755
|
+
this.runtime.store.setLastLoopStats(stats);
|
|
756
|
+
this.events.loopStatsUpdated?.(stats);
|
|
757
|
+
}
|
|
318
758
|
async runInput(input, options = {}) {
|
|
319
759
|
const text = input.trim();
|
|
320
760
|
if (!text)
|
|
321
761
|
return;
|
|
322
762
|
const parsed = (0, parser_1.parseInput)(text);
|
|
323
763
|
if (!parsed.isCommand) {
|
|
764
|
+
const localFastPath = parseLocalFastPath(text);
|
|
765
|
+
if (localFastPath) {
|
|
766
|
+
await this.runLocalFastPath(text, localFastPath, options);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
324
769
|
await this.runChat(text, options);
|
|
325
770
|
return;
|
|
326
771
|
}
|
|
@@ -384,6 +829,179 @@ class AgentChatController {
|
|
|
384
829
|
await this.runChat(result.chatInput ?? parsed.args, options);
|
|
385
830
|
}
|
|
386
831
|
}
|
|
832
|
+
async runLocalFastPath(input, action, options = {}) {
|
|
833
|
+
const activeSession = this.runtime.getSession() ?? this.runtime.ensureSession() ?? (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
|
|
834
|
+
const sessionId = activeSession?.id;
|
|
835
|
+
const turnId = traceTurnId(options.turnId);
|
|
836
|
+
const localCallId = `local-${turnId}`;
|
|
837
|
+
const start = Date.now();
|
|
838
|
+
const preWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(this.runtime.cwd);
|
|
839
|
+
const traceArgs = buildTraceArgsDetails(this.runtime.cwd, action.tool, action.args);
|
|
840
|
+
if (sessionId) {
|
|
841
|
+
(0, session_storage_1.appendSessionMessage)(sessionId, {
|
|
842
|
+
role: 'user',
|
|
843
|
+
content: input,
|
|
844
|
+
timestamp: Date.now(),
|
|
845
|
+
});
|
|
846
|
+
recordTraceEvent(this.events, sessionId, {
|
|
847
|
+
turnId,
|
|
848
|
+
type: 'turn_start',
|
|
849
|
+
inputBytes: byteLength(input),
|
|
850
|
+
localFastPathUsed: true,
|
|
851
|
+
});
|
|
852
|
+
appendWorkspaceSnapshotTrace(this.events, sessionId, turnId, 'pre_turn', preWorkspace);
|
|
853
|
+
recordTraceEvent(this.events, sessionId, {
|
|
854
|
+
turnId,
|
|
855
|
+
type: 'local_fast_path',
|
|
856
|
+
name: action.tool,
|
|
857
|
+
...traceArgs,
|
|
858
|
+
note: compactMiddle(action.label, 160),
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
this.runtime.store.addMessage({ role: 'user', content: input });
|
|
862
|
+
this.events.setStatus(`Running local ${action.label}...`);
|
|
863
|
+
try {
|
|
864
|
+
const tool = (0, tools_1.getRuntimeTools)().find(candidate => candidate.name === action.tool);
|
|
865
|
+
const toolContext = {
|
|
866
|
+
cwd: this.runtime.cwd,
|
|
867
|
+
config: {
|
|
868
|
+
name: this.runtime.config.name,
|
|
869
|
+
mode: this.runtime.config.mode,
|
|
870
|
+
},
|
|
871
|
+
sessionId,
|
|
872
|
+
turnId,
|
|
873
|
+
};
|
|
874
|
+
const permission = tool?.checkPermissions?.(action.args, toolContext);
|
|
875
|
+
if (permission?.behavior === 'deny' || tool?.isDestructive?.(action.args) === true) {
|
|
876
|
+
const reason = permission?.reason || 'Local fast path blocked a destructive tool request.';
|
|
877
|
+
throw new LocalFastPathBlockedError(reason);
|
|
878
|
+
}
|
|
879
|
+
if (permission?.behavior === 'ask') {
|
|
880
|
+
throw new LocalFastPathBlockedError(permission.reason || 'Local fast path requires an allow-safe command.');
|
|
881
|
+
}
|
|
882
|
+
if (sessionId) {
|
|
883
|
+
recordTraceEvent(this.events, sessionId, {
|
|
884
|
+
turnId,
|
|
885
|
+
type: 'tool_call',
|
|
886
|
+
name: action.tool,
|
|
887
|
+
callId: localCallId,
|
|
888
|
+
...traceArgs,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
const result = await (0, tools_1.executeTool)(action.tool, action.args, options.abortSignal, {
|
|
892
|
+
...toolContext,
|
|
893
|
+
sessionId,
|
|
894
|
+
turnId,
|
|
895
|
+
});
|
|
896
|
+
const duration = Date.now() - start;
|
|
897
|
+
const envelope = (0, tool_serializer_1.parseToolResultEnvelope)(result);
|
|
898
|
+
const outputBytes = typeof envelope.outputBytes === 'number'
|
|
899
|
+
? envelope.outputBytes
|
|
900
|
+
: Buffer.byteLength(result, 'utf8');
|
|
901
|
+
const assistantContent = formatLocalFastPathAssistantContent(action, result, this.runtime.cwd);
|
|
902
|
+
const stats = (0, framework_1.createLocalFastPathLoopStats)({
|
|
903
|
+
finishReason: envelope.success ? 'completed' : 'failed',
|
|
904
|
+
toolCalls: 1,
|
|
905
|
+
readOnlyToolCalls: action.tool === 'exec_command' ? 0 : 1,
|
|
906
|
+
unsafeToolCalls: action.tool === 'exec_command' ? 1 : 0,
|
|
907
|
+
toolResultBytes: outputBytes,
|
|
908
|
+
modelVisibleToolBytes: 0,
|
|
909
|
+
summarizedBytes: outputBytes,
|
|
910
|
+
});
|
|
911
|
+
this.events.append({
|
|
912
|
+
role: envelope.success ? 'tool' : 'error',
|
|
913
|
+
title: 'local',
|
|
914
|
+
content: toolFinishContent({
|
|
915
|
+
type: 'tool_result',
|
|
916
|
+
name: action.tool,
|
|
917
|
+
args: action.args,
|
|
918
|
+
callId: localCallId,
|
|
919
|
+
result,
|
|
920
|
+
modelVisibleResult: result,
|
|
921
|
+
duration,
|
|
922
|
+
success: envelope.success,
|
|
923
|
+
error: envelope.error,
|
|
924
|
+
summary: envelope.summary,
|
|
925
|
+
outputBytes,
|
|
926
|
+
}),
|
|
927
|
+
});
|
|
928
|
+
this.runtime.store.addMessage({ role: 'assistant', content: assistantContent });
|
|
929
|
+
this.setLoopStats(stats);
|
|
930
|
+
if (sessionId) {
|
|
931
|
+
recordTraceEvent(this.events, sessionId, {
|
|
932
|
+
turnId,
|
|
933
|
+
type: 'tool_result',
|
|
934
|
+
name: action.tool,
|
|
935
|
+
callId: localCallId,
|
|
936
|
+
argsSummary: traceArgs.argsSummary,
|
|
937
|
+
argsArtifactId: traceArgs.argsArtifactId,
|
|
938
|
+
argsBytes: traceArgs.argsBytes,
|
|
939
|
+
success: envelope.success,
|
|
940
|
+
duration,
|
|
941
|
+
outputBytes,
|
|
942
|
+
modelVisibleBytes: 0,
|
|
943
|
+
error: envelope.error ? compactMiddle(envelope.error, 240) : undefined,
|
|
944
|
+
});
|
|
945
|
+
appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace);
|
|
946
|
+
recordTraceEvent(this.events, sessionId, {
|
|
947
|
+
turnId,
|
|
948
|
+
type: 'complete',
|
|
949
|
+
finishReason: stats.finishReason,
|
|
950
|
+
llmRequests: stats.llmRequests,
|
|
951
|
+
toolCalls: stats.toolCalls,
|
|
952
|
+
readOnlyToolCalls: stats.readOnlyToolCalls,
|
|
953
|
+
unsafeToolCalls: stats.unsafeToolCalls,
|
|
954
|
+
localFastPathUsed: true,
|
|
955
|
+
});
|
|
956
|
+
(0, session_storage_1.appendSessionMessage)(sessionId, {
|
|
957
|
+
role: 'assistant',
|
|
958
|
+
content: assistantContent,
|
|
959
|
+
timestamp: Date.now(),
|
|
960
|
+
});
|
|
961
|
+
const recordedMessages = (0, session_storage_1.readSessionMessages)(sessionId);
|
|
962
|
+
if (recordedMessages.length > 0) {
|
|
963
|
+
(0, session_storage_1.updateSessionSummary)(sessionId, recordedMessages);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
this.events.setStatus(envelope.success ? `Completed local ${action.label}` : `Failed local ${action.label}`);
|
|
967
|
+
}
|
|
968
|
+
catch (error) {
|
|
969
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
970
|
+
this.events.append({ role: 'error', title: 'local', content: message });
|
|
971
|
+
this.events.setStatus('Local command failed. Ready for the next input.');
|
|
972
|
+
const assistantContent = `Local fast path failed for ${action.label}.\n\n${message}`;
|
|
973
|
+
this.runtime.store.addMessage({ role: 'assistant', content: assistantContent });
|
|
974
|
+
const finishReason = error instanceof LocalFastPathBlockedError ? 'blocked' : 'failed';
|
|
975
|
+
const stats = (0, framework_1.createLocalFastPathLoopStats)({
|
|
976
|
+
finishReason,
|
|
977
|
+
toolCalls: 0,
|
|
978
|
+
});
|
|
979
|
+
this.setLoopStats(stats);
|
|
980
|
+
if (sessionId) {
|
|
981
|
+
recordTraceEvent(this.events, sessionId, {
|
|
982
|
+
turnId,
|
|
983
|
+
type: 'error',
|
|
984
|
+
name: action.tool,
|
|
985
|
+
error: compactTraceError(error),
|
|
986
|
+
});
|
|
987
|
+
appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace);
|
|
988
|
+
recordTraceEvent(this.events, sessionId, {
|
|
989
|
+
turnId,
|
|
990
|
+
type: 'complete',
|
|
991
|
+
finishReason: stats.finishReason,
|
|
992
|
+
llmRequests: stats.llmRequests,
|
|
993
|
+
toolCalls: stats.toolCalls,
|
|
994
|
+
localFastPathUsed: true,
|
|
995
|
+
note: 'local_fast_path_failed',
|
|
996
|
+
});
|
|
997
|
+
(0, session_storage_1.appendSessionMessage)(sessionId, {
|
|
998
|
+
role: 'assistant',
|
|
999
|
+
content: assistantContent,
|
|
1000
|
+
timestamp: Date.now(),
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
387
1005
|
createCommandContext(abortSignal, turnId) {
|
|
388
1006
|
return {
|
|
389
1007
|
cwd: this.runtime.cwd,
|
|
@@ -398,6 +1016,9 @@ class AgentChatController {
|
|
|
398
1016
|
this.runtime.setSession(session);
|
|
399
1017
|
this.events.replaceTranscript(sessionMessagesToTranscriptEntries(session.id));
|
|
400
1018
|
},
|
|
1019
|
+
sessionRestored: event => {
|
|
1020
|
+
this.events.sessionRestored?.(event);
|
|
1021
|
+
},
|
|
401
1022
|
getSession: this.runtime.getSession,
|
|
402
1023
|
abortSignal,
|
|
403
1024
|
writeOutput: text => {
|
|
@@ -426,9 +1047,10 @@ class AgentChatController {
|
|
|
426
1047
|
return;
|
|
427
1048
|
}
|
|
428
1049
|
const abortSignal = options.abortSignal;
|
|
429
|
-
const turnId = options.turnId;
|
|
1050
|
+
const turnId = traceTurnId(options.turnId);
|
|
430
1051
|
const activeSession = this.runtime.getSession() ?? this.runtime.ensureSession() ?? (0, session_storage_1.loadSessionMeta)(this.runtime.getSession()?.id ?? '');
|
|
431
1052
|
const sessionId = activeSession?.id;
|
|
1053
|
+
const preWorkspace = (0, workspace_state_1.captureWorkspaceSnapshot)(this.runtime.cwd);
|
|
432
1054
|
const runtimeTools = (0, tools_1.getRuntimeTools)();
|
|
433
1055
|
const skillResolution = (0, skills_1.resolveSkillsForTurn)({
|
|
434
1056
|
cwd: this.runtime.cwd,
|
|
@@ -445,6 +1067,13 @@ class AgentChatController {
|
|
|
445
1067
|
timestamp: Date.now(),
|
|
446
1068
|
appliedSkills: appliedSkillNames.length > 0 ? appliedSkillNames : undefined,
|
|
447
1069
|
});
|
|
1070
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1071
|
+
turnId,
|
|
1072
|
+
type: 'turn_start',
|
|
1073
|
+
inputBytes: byteLength(input),
|
|
1074
|
+
note: appliedSkillNames.length > 0 ? `skills=${appliedSkillNames.join(',')}` : undefined,
|
|
1075
|
+
});
|
|
1076
|
+
appendWorkspaceSnapshotTrace(this.events, sessionId, turnId, 'pre_turn', preWorkspace);
|
|
448
1077
|
}
|
|
449
1078
|
this.runtime.store.addMessage({ role: 'user', content: input });
|
|
450
1079
|
(0, prompt_context_1.refreshProjectInstructions)(this.runtime.store, this.runtime.cwd);
|
|
@@ -481,9 +1110,14 @@ class AgentChatController {
|
|
|
481
1110
|
let finalContent = '';
|
|
482
1111
|
let finalUsage;
|
|
483
1112
|
let finalModel = '';
|
|
1113
|
+
let pendingCompleteTrace = null;
|
|
1114
|
+
let pendingCompleteStats;
|
|
1115
|
+
const verificationResults = [];
|
|
484
1116
|
const sessionMessagesToRecord = [];
|
|
485
1117
|
const assistantStream = createAssistantStreamPresenter(this.events, abortSignal);
|
|
486
1118
|
const toolEvents = createToolEventPresenter(this.events);
|
|
1119
|
+
let checkpointSequence = 0;
|
|
1120
|
+
const checkpointIds = [];
|
|
487
1121
|
const streamCallbacks = {
|
|
488
1122
|
onChunk: chunk => {
|
|
489
1123
|
assistantStream.appendChunk(chunk);
|
|
@@ -508,6 +1142,14 @@ class AgentChatController {
|
|
|
508
1142
|
turnId,
|
|
509
1143
|
});
|
|
510
1144
|
};
|
|
1145
|
+
const loopBudget = (0, loop_budget_1.resolveRuntimeLoopBudget)(input, this.runtime.config, harness.toJSON());
|
|
1146
|
+
let observedTurnsStarted = 0;
|
|
1147
|
+
let observedLlmRequests = 0;
|
|
1148
|
+
let observedToolCalls = 0;
|
|
1149
|
+
let observedReadOnlyToolCalls = 0;
|
|
1150
|
+
let observedUnsafeToolCalls = 0;
|
|
1151
|
+
let observedToolResultBytes = 0;
|
|
1152
|
+
let observedModelVisibleToolBytes = 0;
|
|
511
1153
|
try {
|
|
512
1154
|
for await (const event of (0, framework_1.query)({
|
|
513
1155
|
messages,
|
|
@@ -531,11 +1173,41 @@ class AgentChatController {
|
|
|
531
1173
|
abortSignal,
|
|
532
1174
|
harness,
|
|
533
1175
|
input,
|
|
1176
|
+
loopBudget,
|
|
534
1177
|
})) {
|
|
535
1178
|
switch (event.type) {
|
|
536
1179
|
case 'request_start':
|
|
1180
|
+
observedTurnsStarted = Math.max(observedTurnsStarted, event.turn);
|
|
1181
|
+
observedLlmRequests++;
|
|
537
1182
|
assistantStream.discardSegment();
|
|
538
1183
|
this.events.setStatus((0, agent_status_1.agentStepStatus)(event.turn));
|
|
1184
|
+
if (sessionId) {
|
|
1185
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1186
|
+
turnId,
|
|
1187
|
+
type: 'request_start',
|
|
1188
|
+
model: event.model,
|
|
1189
|
+
turn: event.turn,
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
break;
|
|
1193
|
+
case 'prompt_assembly':
|
|
1194
|
+
if (sessionId) {
|
|
1195
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1196
|
+
turnId,
|
|
1197
|
+
type: 'prompt_assembly',
|
|
1198
|
+
promptModelId: event.modelId,
|
|
1199
|
+
promptEstimatedTokens: event.estimatedTokens,
|
|
1200
|
+
promptBudgetTokens: event.budgetTokens,
|
|
1201
|
+
promptCoreTokens: event.coreTokens,
|
|
1202
|
+
promptEvidenceBudgetTokens: event.evidenceBudgetTokens,
|
|
1203
|
+
promptRecentTurnBudgetTokens: event.recentTurnBudgetTokens,
|
|
1204
|
+
promptSections: event.sections,
|
|
1205
|
+
promptIncludedEvidence: event.includedEvidence,
|
|
1206
|
+
promptOmittedEvidence: event.omittedEvidence,
|
|
1207
|
+
promptIncludedEvidenceCount: event.includedEvidenceCount,
|
|
1208
|
+
promptOmittedEvidenceCount: event.omittedEvidenceCount,
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
539
1211
|
break;
|
|
540
1212
|
case 'assistant_tool_calls':
|
|
541
1213
|
assistantStream.ensureMessage(event.content || '');
|
|
@@ -543,6 +1215,21 @@ class AgentChatController {
|
|
|
543
1215
|
if (event.toolCalls.length > 1) {
|
|
544
1216
|
this.events.setStatus((0, agent_status_1.runningToolsStatus)(event.toolCalls.length));
|
|
545
1217
|
}
|
|
1218
|
+
const checkpointId = checkpointSequence === 0
|
|
1219
|
+
? turnId
|
|
1220
|
+
: `${turnId}-checkpoint-${checkpointSequence + 1}`;
|
|
1221
|
+
if (createPreToolCheckpoint(this.events, sessionId, turnId, checkpointId, this.runtime.cwd, event.toolCalls)) {
|
|
1222
|
+
checkpointIds.push(checkpointId);
|
|
1223
|
+
checkpointSequence++;
|
|
1224
|
+
}
|
|
1225
|
+
if (sessionId) {
|
|
1226
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1227
|
+
turnId,
|
|
1228
|
+
type: 'assistant_tool_calls',
|
|
1229
|
+
toolCallCount: event.toolCalls.length,
|
|
1230
|
+
contentBytes: byteLength(event.content || ''),
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
546
1233
|
sessionMessagesToRecord.push({
|
|
547
1234
|
role: 'assistant',
|
|
548
1235
|
content: event.content || '',
|
|
@@ -551,14 +1238,87 @@ class AgentChatController {
|
|
|
551
1238
|
});
|
|
552
1239
|
break;
|
|
553
1240
|
case 'tool_call':
|
|
1241
|
+
observedToolCalls++;
|
|
1242
|
+
{
|
|
1243
|
+
const toolDefinition = skillResolution.tools.find(tool => tool.name === event.name);
|
|
1244
|
+
if (toolDefinition?.isReadOnly?.(event.args) === true) {
|
|
1245
|
+
observedReadOnlyToolCalls++;
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
observedUnsafeToolCalls++;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
554
1251
|
assistantStream.closeSegment();
|
|
555
1252
|
toolEvents.start(event);
|
|
1253
|
+
if (sessionId) {
|
|
1254
|
+
const traceArgs = buildTraceArgsDetails(this.runtime.cwd, event.name, event.args);
|
|
1255
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1256
|
+
turnId,
|
|
1257
|
+
type: 'tool_call',
|
|
1258
|
+
name: event.name,
|
|
1259
|
+
callId: event.callId,
|
|
1260
|
+
...traceArgs,
|
|
1261
|
+
batchCount: event.batchCount,
|
|
1262
|
+
batchIndex: event.batchIndex,
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
break;
|
|
1266
|
+
case 'permission_decision':
|
|
1267
|
+
if (sessionId) {
|
|
1268
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1269
|
+
turnId,
|
|
1270
|
+
type: 'permission_decision',
|
|
1271
|
+
name: event.name,
|
|
1272
|
+
callId: event.callId,
|
|
1273
|
+
argsSummary: compactToolArgs(event.args),
|
|
1274
|
+
permissionBehavior: event.decision.behavior,
|
|
1275
|
+
permissionApproved: event.decision.approved,
|
|
1276
|
+
permissionSource: event.decision.source,
|
|
1277
|
+
permissionReason: event.decision.reason
|
|
1278
|
+
? compactMiddle(event.decision.reason, 240)
|
|
1279
|
+
: undefined,
|
|
1280
|
+
permissionDuration: event.decision.duration,
|
|
1281
|
+
batchCount: event.batchCount,
|
|
1282
|
+
batchIndex: event.batchIndex,
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
556
1285
|
break;
|
|
557
1286
|
case 'tool_result': {
|
|
1287
|
+
observedToolResultBytes += event.outputBytes ?? byteLength(event.result);
|
|
1288
|
+
observedModelVisibleToolBytes += byteLength(event.modelVisibleResult);
|
|
558
1289
|
toolEvents.finish(event);
|
|
1290
|
+
if (sessionId) {
|
|
1291
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1292
|
+
turnId,
|
|
1293
|
+
type: 'tool_result',
|
|
1294
|
+
name: event.name,
|
|
1295
|
+
callId: event.callId,
|
|
1296
|
+
argsSummary: compactToolArgs(event.args),
|
|
1297
|
+
success: event.success,
|
|
1298
|
+
duration: event.duration,
|
|
1299
|
+
outputBytes: event.outputBytes,
|
|
1300
|
+
modelVisibleBytes: byteLength(event.modelVisibleResult),
|
|
1301
|
+
artifactId: event.artifactRef?.id,
|
|
1302
|
+
error: event.error ? compactMiddle(event.error, 240) : undefined,
|
|
1303
|
+
batchCount: event.batchCount,
|
|
1304
|
+
batchIndex: event.batchIndex,
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
const verificationResult = (0, verification_profile_1.collectVerificationCommandResult)({
|
|
1308
|
+
toolName: event.name,
|
|
1309
|
+
args: event.args,
|
|
1310
|
+
success: event.success,
|
|
1311
|
+
outputBytes: event.outputBytes,
|
|
1312
|
+
error: event.error,
|
|
1313
|
+
});
|
|
1314
|
+
if (verificationResult) {
|
|
1315
|
+
verificationResults.push(verificationResult);
|
|
1316
|
+
appendVerificationResultTrace(this.events, sessionId, turnId, verificationResult);
|
|
1317
|
+
}
|
|
559
1318
|
sessionMessagesToRecord.push({
|
|
560
1319
|
role: 'tool',
|
|
561
1320
|
content: event.result,
|
|
1321
|
+
modelVisibleContent: event.modelVisibleResult,
|
|
562
1322
|
timestamp: Date.now(),
|
|
563
1323
|
toolCallId: event.callId,
|
|
564
1324
|
});
|
|
@@ -566,10 +1326,24 @@ class AgentChatController {
|
|
|
566
1326
|
}
|
|
567
1327
|
case 'strategy_exhausted':
|
|
568
1328
|
this.events.append({ role: 'status', content: event.suggestion });
|
|
1329
|
+
if (sessionId) {
|
|
1330
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1331
|
+
turnId,
|
|
1332
|
+
type: 'strategy_exhausted',
|
|
1333
|
+
note: compactMiddle(event.suggestion, 240),
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
569
1336
|
break;
|
|
570
1337
|
case 'message':
|
|
571
1338
|
finalContent = event.content;
|
|
572
1339
|
assistantStream.ensureMessage(event.content);
|
|
1340
|
+
if (sessionId) {
|
|
1341
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1342
|
+
turnId,
|
|
1343
|
+
type: 'message',
|
|
1344
|
+
contentBytes: byteLength(event.content),
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
573
1347
|
if (event.content) {
|
|
574
1348
|
sessionMessagesToRecord.push({
|
|
575
1349
|
role: 'assistant',
|
|
@@ -579,9 +1353,56 @@ class AgentChatController {
|
|
|
579
1353
|
}
|
|
580
1354
|
break;
|
|
581
1355
|
case 'complete':
|
|
1356
|
+
if (event.content && !finalContent) {
|
|
1357
|
+
if (event.stats?.finishReason === 'budget_exceeded') {
|
|
1358
|
+
assistantStream.replaceMessage(event.content);
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
assistantStream.ensureMessage(event.content);
|
|
1362
|
+
}
|
|
1363
|
+
sessionMessagesToRecord.push({
|
|
1364
|
+
role: 'assistant',
|
|
1365
|
+
content: event.content,
|
|
1366
|
+
timestamp: Date.now(),
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
582
1369
|
finalContent = event.content;
|
|
583
1370
|
finalUsage = event.usage;
|
|
584
1371
|
finalModel = event.model;
|
|
1372
|
+
if (event.stats) {
|
|
1373
|
+
pendingCompleteStats = event.stats;
|
|
1374
|
+
recordProviderTraceEvents(this.events, sessionId, turnId, event.stats);
|
|
1375
|
+
pendingCompleteTrace = {
|
|
1376
|
+
turnId,
|
|
1377
|
+
type: 'complete',
|
|
1378
|
+
model: event.model,
|
|
1379
|
+
contentBytes: byteLength(event.content || ''),
|
|
1380
|
+
finishReason: event.stats.finishReason,
|
|
1381
|
+
llmRequests: event.stats.llmRequests,
|
|
1382
|
+
toolCalls: event.stats.toolCalls,
|
|
1383
|
+
readOnlyToolCalls: event.stats.readOnlyToolCalls,
|
|
1384
|
+
unsafeToolCalls: event.stats.unsafeToolCalls,
|
|
1385
|
+
loopBudgetSource: event.stats.loopBudgetSource,
|
|
1386
|
+
loopBudgetBaseProfile: event.stats.loopBudgetBaseProfile,
|
|
1387
|
+
loopBudgetMaxLlmRequests: event.stats.loopBudgetMaxLlmRequests,
|
|
1388
|
+
loopBudgetMaxToolCalls: event.stats.loopBudgetMaxToolCalls,
|
|
1389
|
+
loopBudgetMaxReadOnlyFragmentation: event.stats.loopBudgetMaxReadOnlyFragmentation,
|
|
1390
|
+
loopBudgetMaxModelVisibleBytes: event.stats.loopBudgetMaxModelVisibleBytes,
|
|
1391
|
+
loopBudgetConfigOverride: event.stats.loopBudgetConfigOverride,
|
|
1392
|
+
budgetExceededReason: event.stats.budgetExceededReason,
|
|
1393
|
+
continuationActions: event.stats.continuationActions,
|
|
1394
|
+
continuationHint: event.stats.continuationHint,
|
|
1395
|
+
localFastPathUsed: event.stats.localFastPathUsed,
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
else {
|
|
1399
|
+
pendingCompleteTrace = {
|
|
1400
|
+
turnId,
|
|
1401
|
+
type: 'complete',
|
|
1402
|
+
model: event.model,
|
|
1403
|
+
contentBytes: byteLength(event.content || ''),
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
585
1406
|
break;
|
|
586
1407
|
}
|
|
587
1408
|
}
|
|
@@ -591,11 +1412,51 @@ class AgentChatController {
|
|
|
591
1412
|
this.events.setStatus('Interrupted.');
|
|
592
1413
|
removeTrailingUserMessage(this.runtime);
|
|
593
1414
|
if (sessionId) {
|
|
1415
|
+
appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
|
|
1416
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1417
|
+
turnId,
|
|
1418
|
+
type: 'aborted',
|
|
1419
|
+
note: 'aborted_after_query',
|
|
1420
|
+
});
|
|
594
1421
|
(0, session_storage_1.removeLastIncompleteAssistantMessage)(sessionId);
|
|
595
1422
|
}
|
|
596
1423
|
return;
|
|
597
1424
|
}
|
|
598
1425
|
assistantStream.closeSegment();
|
|
1426
|
+
if (sessionId) {
|
|
1427
|
+
const { profile, summary } = appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
|
|
1428
|
+
if (shouldRecordVerificationLoopStats(profile, summary)) {
|
|
1429
|
+
const stats = pendingCompleteStats ?? this.runtime.store.getSnapshot().lastLoopStats;
|
|
1430
|
+
if (stats) {
|
|
1431
|
+
pendingCompleteStats = withVerificationLoopStats(stats, summary);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
if ((0, verification_profile_1.shouldGateCompletion)(summary)) {
|
|
1435
|
+
const notice = (0, verification_profile_1.formatVerificationGateNotice)(summary);
|
|
1436
|
+
this.events.append({
|
|
1437
|
+
role: 'status',
|
|
1438
|
+
title: 'verification',
|
|
1439
|
+
content: notice,
|
|
1440
|
+
});
|
|
1441
|
+
finalContent = finalContent ? `${finalContent}\n\n${notice}` : notice;
|
|
1442
|
+
appendAssistantNotice(sessionMessagesToRecord, notice);
|
|
1443
|
+
if (pendingCompleteTrace) {
|
|
1444
|
+
pendingCompleteTrace.finishReason = 'completion_gate';
|
|
1445
|
+
pendingCompleteTrace.contentBytes = byteLength(finalContent);
|
|
1446
|
+
pendingCompleteTrace.note = 'verification_incomplete';
|
|
1447
|
+
}
|
|
1448
|
+
const stats = pendingCompleteStats ?? this.runtime.store.getSnapshot().lastLoopStats;
|
|
1449
|
+
if (stats) {
|
|
1450
|
+
pendingCompleteStats = {
|
|
1451
|
+
...withVerificationLoopStats(stats, summary),
|
|
1452
|
+
finishReason: 'completion_gate',
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (pendingCompleteStats) {
|
|
1458
|
+
this.setLoopStats(pendingCompleteStats);
|
|
1459
|
+
}
|
|
599
1460
|
if (finalContent) {
|
|
600
1461
|
this.runtime.store.addMessage({ role: 'assistant', content: finalContent });
|
|
601
1462
|
}
|
|
@@ -613,7 +1474,11 @@ class AgentChatController {
|
|
|
613
1474
|
});
|
|
614
1475
|
const harnessState = harness.toJSON();
|
|
615
1476
|
this.runtime.store.setState({ harnessState });
|
|
1477
|
+
emitHarnessDiagnostics(this.events, harnessState);
|
|
616
1478
|
if (sessionId) {
|
|
1479
|
+
if (pendingCompleteTrace) {
|
|
1480
|
+
recordTraceEvent(this.events, sessionId, pendingCompleteTrace);
|
|
1481
|
+
}
|
|
617
1482
|
(0, session_storage_1.updateSessionSkills)(sessionId, appliedSkillNames);
|
|
618
1483
|
(0, session_storage_1.updateSessionHarnessState)(sessionId, harnessState);
|
|
619
1484
|
const recordedMessages = (0, session_storage_1.readSessionMessages)(sessionId);
|
|
@@ -629,6 +1494,12 @@ class AgentChatController {
|
|
|
629
1494
|
this.events.setStatus('Interrupted.');
|
|
630
1495
|
removeTrailingUserMessage(this.runtime);
|
|
631
1496
|
if (sessionId) {
|
|
1497
|
+
appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
|
|
1498
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1499
|
+
turnId,
|
|
1500
|
+
type: 'aborted',
|
|
1501
|
+
note: 'abort_error',
|
|
1502
|
+
});
|
|
632
1503
|
(0, session_storage_1.removeLastIncompleteAssistantMessage)(sessionId);
|
|
633
1504
|
}
|
|
634
1505
|
return;
|
|
@@ -636,6 +1507,60 @@ class AgentChatController {
|
|
|
636
1507
|
assistantStream.discardSegment();
|
|
637
1508
|
this.events.append({ role: 'error', content: formatChatError(error) });
|
|
638
1509
|
this.events.setStatus('Turn failed. Ready for the next input.');
|
|
1510
|
+
const failedStats = error instanceof framework_1.QueryLoopError
|
|
1511
|
+
? error.stats
|
|
1512
|
+
: (0, framework_1.createFailedLoopStats)({
|
|
1513
|
+
loopBudget,
|
|
1514
|
+
diagnostics: observedLlmRequests > 0 ? getLastRequestDiagnostics(this.runtime.llm) : undefined,
|
|
1515
|
+
turnsStarted: observedTurnsStarted,
|
|
1516
|
+
llmRequests: observedLlmRequests,
|
|
1517
|
+
toolCalls: observedToolCalls,
|
|
1518
|
+
readOnlyToolCalls: observedReadOnlyToolCalls,
|
|
1519
|
+
unsafeToolCalls: observedUnsafeToolCalls,
|
|
1520
|
+
toolResultBytes: observedToolResultBytes,
|
|
1521
|
+
modelVisibleToolBytes: observedModelVisibleToolBytes,
|
|
1522
|
+
});
|
|
1523
|
+
this.setLoopStats(failedStats);
|
|
1524
|
+
if (sessionId) {
|
|
1525
|
+
recordProviderTraceEvents(this.events, sessionId, turnId, failedStats);
|
|
1526
|
+
const { delta } = appendPostWorkspaceTrace(this.events, sessionId, turnId, this.runtime.cwd, preWorkspace, verificationResults);
|
|
1527
|
+
const recoveryNotice = workspaceDeltaHasTurnChanges(delta)
|
|
1528
|
+
? formatFailureRecoveryNotice(turnId, delta, checkpointIds)
|
|
1529
|
+
: undefined;
|
|
1530
|
+
if (recoveryNotice) {
|
|
1531
|
+
this.events.append({
|
|
1532
|
+
role: 'status',
|
|
1533
|
+
title: 'recovery',
|
|
1534
|
+
content: recoveryNotice,
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1538
|
+
turnId,
|
|
1539
|
+
type: 'error',
|
|
1540
|
+
error: compactTraceError(error),
|
|
1541
|
+
note: recoveryNotice,
|
|
1542
|
+
});
|
|
1543
|
+
recordTraceEvent(this.events, sessionId, {
|
|
1544
|
+
turnId,
|
|
1545
|
+
type: 'complete',
|
|
1546
|
+
model: failedStats.providerFinalModel ?? this.runtime.llm.getModel(),
|
|
1547
|
+
contentBytes: 0,
|
|
1548
|
+
finishReason: failedStats.finishReason,
|
|
1549
|
+
llmRequests: failedStats.llmRequests,
|
|
1550
|
+
toolCalls: failedStats.toolCalls,
|
|
1551
|
+
readOnlyToolCalls: failedStats.readOnlyToolCalls,
|
|
1552
|
+
unsafeToolCalls: failedStats.unsafeToolCalls,
|
|
1553
|
+
loopBudgetSource: failedStats.loopBudgetSource,
|
|
1554
|
+
loopBudgetBaseProfile: failedStats.loopBudgetBaseProfile,
|
|
1555
|
+
loopBudgetMaxLlmRequests: failedStats.loopBudgetMaxLlmRequests,
|
|
1556
|
+
loopBudgetMaxToolCalls: failedStats.loopBudgetMaxToolCalls,
|
|
1557
|
+
loopBudgetMaxReadOnlyFragmentation: failedStats.loopBudgetMaxReadOnlyFragmentation,
|
|
1558
|
+
loopBudgetMaxModelVisibleBytes: failedStats.loopBudgetMaxModelVisibleBytes,
|
|
1559
|
+
loopBudgetConfigOverride: failedStats.loopBudgetConfigOverride,
|
|
1560
|
+
localFastPathUsed: failedStats.localFastPathUsed,
|
|
1561
|
+
});
|
|
1562
|
+
(0, session_storage_1.removeLastIncompleteAssistantMessage)(sessionId);
|
|
1563
|
+
}
|
|
639
1564
|
const history = this.runtime.store.getSnapshot().conversationHistory;
|
|
640
1565
|
if (history.length > 0) {
|
|
641
1566
|
this.runtime.store.setState({ conversationHistory: history.slice(0, -1) });
|