openhorse 0.2.12 → 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 +597 -12
- 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 +87 -2
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +281 -14
- package/dist/framework/query.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 +907 -2
- 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 +34 -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/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 +16 -0
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +56 -11
- package/dist/services/llm.js.map +1 -1
- 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 +98 -0
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +153 -2
- 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/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -40,8 +40,11 @@ const doctor_1 = require("../services/doctor");
|
|
|
40
40
|
const model_context_1 = require("../services/model-context");
|
|
41
41
|
const workspace_diff_1 = require("../services/workspace-diff");
|
|
42
42
|
const commit_plan_1 = require("../services/commit-plan");
|
|
43
|
+
const tool_artifacts_1 = require("../core/tool-artifacts");
|
|
44
|
+
const checkpoint_1 = require("../core/checkpoint");
|
|
43
45
|
const storage_maintenance_1 = require("../services/storage-maintenance");
|
|
44
46
|
const agent_status_1 = require("../runtime/agent-status");
|
|
47
|
+
const loop_budget_1 = require("../runtime/loop-budget");
|
|
45
48
|
// ============================================================================
|
|
46
49
|
// 颜色常量
|
|
47
50
|
// ============================================================================
|
|
@@ -75,6 +78,112 @@ function formatTokenCount(tokens) {
|
|
|
75
78
|
function formatThreshold(value) {
|
|
76
79
|
return `${Math.round(value * 100)}%`;
|
|
77
80
|
}
|
|
81
|
+
function formatDurationMs(ms) {
|
|
82
|
+
if (ms >= 1000)
|
|
83
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
84
|
+
return `${Math.round(ms)}ms`;
|
|
85
|
+
}
|
|
86
|
+
function formatLoopBudgetSource(stats) {
|
|
87
|
+
const source = stats.loopBudgetSource ?? 'unknown';
|
|
88
|
+
if (source === 'config' && stats.loopBudgetBaseProfile) {
|
|
89
|
+
return `config over ${stats.loopBudgetBaseProfile}`;
|
|
90
|
+
}
|
|
91
|
+
return source;
|
|
92
|
+
}
|
|
93
|
+
function formatLoopStatsLines(stats, detail = false) {
|
|
94
|
+
const lines = [
|
|
95
|
+
`Finish ${stats.finishReason}`,
|
|
96
|
+
`Requests ${stats.llmRequests} LLM / ${stats.turnsStarted} turns`,
|
|
97
|
+
`Tools ${stats.toolCalls} total (${stats.readOnlyToolCalls} read-only, ${stats.unsafeToolCalls} unsafe)`,
|
|
98
|
+
`Tool bytes ${(0, format_1.formatBytes)(stats.modelVisibleToolBytes)} model-visible / ${(0, format_1.formatBytes)(stats.toolResultBytes)} total`,
|
|
99
|
+
];
|
|
100
|
+
if (stats.summarizedBytes > 0) {
|
|
101
|
+
lines.push(`Saved ${(0, format_1.formatBytes)(stats.summarizedBytes)} from model context`);
|
|
102
|
+
}
|
|
103
|
+
if (stats.compactTrigger) {
|
|
104
|
+
lines.push(`Compact ${stats.compactTrigger}`);
|
|
105
|
+
}
|
|
106
|
+
if (stats.localFastPathUsed) {
|
|
107
|
+
lines.push('Fast path yes');
|
|
108
|
+
}
|
|
109
|
+
if (stats.budgetExceededReason) {
|
|
110
|
+
lines.push(`Budget ${stats.budgetExceededReason}`);
|
|
111
|
+
}
|
|
112
|
+
if (stats.continuationActions && stats.continuationActions.length > 0) {
|
|
113
|
+
lines.push(`Next ${stats.continuationActions.join(', ')}`);
|
|
114
|
+
}
|
|
115
|
+
if ((stats.providerRetryCount ?? 0) > 0) {
|
|
116
|
+
const retryParts = [
|
|
117
|
+
`${stats.providerRetryCount} retries`,
|
|
118
|
+
`delay ${formatDurationMs(stats.providerRetryDelayMs ?? 0)}`,
|
|
119
|
+
stats.providerLastRetryErrorType
|
|
120
|
+
? `last ${stats.providerLastRetryErrorType}${stats.providerLastRetryStatus ? `/${stats.providerLastRetryStatus}` : ''}`
|
|
121
|
+
: undefined,
|
|
122
|
+
].filter(Boolean);
|
|
123
|
+
lines.push(`Provider ${retryParts.join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
if ((stats.providerFallbackCount ?? 0) > 0 || stats.providerUsingFallback) {
|
|
126
|
+
const fallbackPath = stats.providerFallbackFromModel && stats.providerFallbackToModel
|
|
127
|
+
? `${stats.providerFallbackFromModel} -> ${stats.providerFallbackToModel}`
|
|
128
|
+
: stats.providerFinalModel
|
|
129
|
+
? `final ${stats.providerFinalModel}`
|
|
130
|
+
: 'active';
|
|
131
|
+
lines.push(`Fallback ${fallbackPath}`);
|
|
132
|
+
}
|
|
133
|
+
if (typeof stats.verificationClaimAllowed === 'boolean') {
|
|
134
|
+
const verificationParts = [
|
|
135
|
+
stats.verificationProfile ?? 'unknown',
|
|
136
|
+
`required=${stats.verificationRequired ? 'yes' : 'no'}`,
|
|
137
|
+
`passed=${stats.verificationPassedCommands?.length ?? 0}`,
|
|
138
|
+
`failed=${stats.verificationFailedCommands?.length ?? 0}`,
|
|
139
|
+
`missing=${stats.verificationMissingCommands?.length ?? 0}`,
|
|
140
|
+
`claim=${stats.verificationClaimAllowed ? 'yes' : 'no'}`,
|
|
141
|
+
];
|
|
142
|
+
lines.push(`Verify ${verificationParts.join(' ')}`);
|
|
143
|
+
}
|
|
144
|
+
if (typeof stats.loopBudgetMaxLlmRequests === 'number'
|
|
145
|
+
|| typeof stats.loopBudgetMaxToolCalls === 'number'
|
|
146
|
+
|| typeof stats.loopBudgetMaxModelVisibleBytes === 'number') {
|
|
147
|
+
const caps = [
|
|
148
|
+
typeof stats.loopBudgetMaxLlmRequests === 'number'
|
|
149
|
+
? `${stats.llmRequests}/${stats.loopBudgetMaxLlmRequests} LLM`
|
|
150
|
+
: undefined,
|
|
151
|
+
typeof stats.loopBudgetMaxToolCalls === 'number'
|
|
152
|
+
? `${stats.toolCalls}/${stats.loopBudgetMaxToolCalls} tools`
|
|
153
|
+
: undefined,
|
|
154
|
+
typeof stats.loopBudgetMaxModelVisibleBytes === 'number'
|
|
155
|
+
? `${(0, format_1.formatBytes)(stats.modelVisibleToolBytes)}/${(0, format_1.formatBytes)(stats.loopBudgetMaxModelVisibleBytes)} visible`
|
|
156
|
+
: undefined,
|
|
157
|
+
typeof stats.loopBudgetMaxReadOnlyFragmentation === 'number'
|
|
158
|
+
? `fragment ${stats.singleReadOnlyStreak}/${stats.loopBudgetMaxReadOnlyFragmentation}`
|
|
159
|
+
: undefined,
|
|
160
|
+
].filter(Boolean);
|
|
161
|
+
lines.push(`Budget cap ${caps.join(', ')} (${formatLoopBudgetSource(stats)})`);
|
|
162
|
+
}
|
|
163
|
+
if (stats.singleReadOnlyStreak > 0 || stats.batchReadSuggestionCount > 0) {
|
|
164
|
+
lines.push(`Read-only streak ${stats.singleReadOnlyStreak}, batch_read hints ${stats.batchReadSuggestionCount}`);
|
|
165
|
+
}
|
|
166
|
+
if (detail) {
|
|
167
|
+
lines.push(`Unsafe ${stats.unsafeToolCalls}`);
|
|
168
|
+
if (stats.providerRetryErrorTypes && stats.providerRetryErrorTypes.length > 0) {
|
|
169
|
+
lines.push(`Retry type ${stats.providerRetryErrorTypes.join(', ')}`);
|
|
170
|
+
}
|
|
171
|
+
if (stats.verificationFailedCommands && stats.verificationFailedCommands.length > 0) {
|
|
172
|
+
lines.push(`Failed ${stats.verificationFailedCommands.join(' && ')}`);
|
|
173
|
+
}
|
|
174
|
+
if (stats.verificationMissingCommands && stats.verificationMissingCommands.length > 0) {
|
|
175
|
+
lines.push(`Missing ${stats.verificationMissingCommands.join(' && ')}`);
|
|
176
|
+
}
|
|
177
|
+
if (stats.verificationSkippedReason) {
|
|
178
|
+
lines.push(`Verify why ${stats.verificationSkippedReason}`);
|
|
179
|
+
}
|
|
180
|
+
if (stats.continuationHint) {
|
|
181
|
+
lines.push(`Next why ${stats.continuationHint}`);
|
|
182
|
+
}
|
|
183
|
+
lines.push(`Budget hit ${stats.finishReason === 'budget_exceeded' ? 'yes' : 'no'}`);
|
|
184
|
+
}
|
|
185
|
+
return lines;
|
|
186
|
+
}
|
|
78
187
|
const CATEGORY_LABELS = {
|
|
79
188
|
workflow: 'Workflow',
|
|
80
189
|
session: 'Session',
|
|
@@ -203,15 +312,8 @@ function showStatus(ctx) {
|
|
|
203
312
|
const stats = snapshot.lastLoopStats;
|
|
204
313
|
console.log();
|
|
205
314
|
console.log(` Last loop:`);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
console.log(` Tools ${stats.toolCalls} total (${stats.readOnlyToolCalls} read-only, ${stats.unsafeToolCalls} unsafe)`);
|
|
209
|
-
console.log(` Tool bytes ${(0, format_1.formatBytes)(stats.modelVisibleToolBytes)} model-visible / ${(0, format_1.formatBytes)(stats.toolResultBytes)} total`);
|
|
210
|
-
if (stats.summarizedBytes > 0) {
|
|
211
|
-
console.log(` Saved ${(0, format_1.formatBytes)(stats.summarizedBytes)} from model context`);
|
|
212
|
-
}
|
|
213
|
-
if (stats.compactTrigger) {
|
|
214
|
-
console.log(` Compact ${stats.compactTrigger}`);
|
|
315
|
+
for (const line of formatLoopStatsLines(stats)) {
|
|
316
|
+
console.log(` ${line}`);
|
|
215
317
|
}
|
|
216
318
|
}
|
|
217
319
|
const harnessState = snapshot.harnessState;
|
|
@@ -232,6 +334,413 @@ function showStatus(ctx) {
|
|
|
232
334
|
console.log();
|
|
233
335
|
return { success: true };
|
|
234
336
|
}
|
|
337
|
+
function handleLoopStats(ctx) {
|
|
338
|
+
const stats = ctx.store.getSnapshot().lastLoopStats;
|
|
339
|
+
if (!stats) {
|
|
340
|
+
console.log('No agent-loop stats recorded yet.');
|
|
341
|
+
return { success: true };
|
|
342
|
+
}
|
|
343
|
+
console.log(HEADER('Agent Loop Stats'));
|
|
344
|
+
console.log();
|
|
345
|
+
for (const line of formatLoopStatsLines(stats, true)) {
|
|
346
|
+
console.log(` ${line}`);
|
|
347
|
+
}
|
|
348
|
+
console.log();
|
|
349
|
+
console.log(DIM('Use this to spot excessive LLM requests, fragmented read-only tool calls, and local fast-path hits.'));
|
|
350
|
+
return { success: true };
|
|
351
|
+
}
|
|
352
|
+
function formatTraceEventLine(event) {
|
|
353
|
+
const time = new Date(event.timestamp).toLocaleTimeString();
|
|
354
|
+
const prefix = `${DIM(time)} ${ACCENT(event.type)}`;
|
|
355
|
+
switch (event.type) {
|
|
356
|
+
case 'turn_start':
|
|
357
|
+
return `${prefix} input=${(0, format_1.formatBytes)(event.inputBytes ?? 0)}${event.localFastPathUsed ? ' fast-path' : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
358
|
+
case 'request_start':
|
|
359
|
+
return `${prefix} model=${event.model ?? 'unknown'} iteration=${event.turn ?? '?'}`;
|
|
360
|
+
case 'provider_retry': {
|
|
361
|
+
const parts = [
|
|
362
|
+
`count=${event.providerRetryCount ?? 0}`,
|
|
363
|
+
`delay=${formatDurationMs(event.providerRetryDelayMs ?? 0)}`,
|
|
364
|
+
event.providerLastRetryErrorType
|
|
365
|
+
? `last=${event.providerLastRetryErrorType}${event.providerLastRetryStatus ? `/${event.providerLastRetryStatus}` : ''}`
|
|
366
|
+
: '',
|
|
367
|
+
event.providerRetryErrorTypes?.length ? `types=${event.providerRetryErrorTypes.join(',')}` : '',
|
|
368
|
+
event.providerFinalModel ? `final=${event.providerFinalModel}` : '',
|
|
369
|
+
].filter(Boolean);
|
|
370
|
+
return `${prefix} ${parts.join(' ')}`;
|
|
371
|
+
}
|
|
372
|
+
case 'provider_fallback': {
|
|
373
|
+
const path = event.providerFallbackFromModel && event.providerFallbackToModel
|
|
374
|
+
? `${event.providerFallbackFromModel}->${event.providerFallbackToModel}`
|
|
375
|
+
: event.providerFinalModel ?? 'active';
|
|
376
|
+
const parts = [
|
|
377
|
+
`count=${event.providerFallbackCount ?? 0}`,
|
|
378
|
+
`path=${path}`,
|
|
379
|
+
`using=${event.providerUsingFallback ? 'yes' : 'no'}`,
|
|
380
|
+
];
|
|
381
|
+
return `${prefix} ${parts.join(' ')}`;
|
|
382
|
+
}
|
|
383
|
+
case 'prompt_assembly': {
|
|
384
|
+
const parts = [
|
|
385
|
+
`model=${event.promptModelId ?? 'unknown'}`,
|
|
386
|
+
`tokens=${event.promptEstimatedTokens ?? 0}/${event.promptBudgetTokens ?? 0}`,
|
|
387
|
+
`core=${event.promptCoreTokens ?? 0}`,
|
|
388
|
+
`evidenceBudget=${event.promptEvidenceBudgetTokens ?? 0}`,
|
|
389
|
+
`recentBudget=${event.promptRecentTurnBudgetTokens ?? 0}`,
|
|
390
|
+
`included=${event.promptIncludedEvidenceCount ?? 0}`,
|
|
391
|
+
`omitted=${event.promptOmittedEvidenceCount ?? 0}`,
|
|
392
|
+
];
|
|
393
|
+
const sections = event.promptSections?.length
|
|
394
|
+
? ` sections=${event.promptSections.join(',')}`
|
|
395
|
+
: '';
|
|
396
|
+
const evidence = event.promptIncludedEvidence?.length
|
|
397
|
+
? ` evidence=${event.promptIncludedEvidence.slice(0, 6).join(', ')}${event.promptIncludedEvidence.length > 6 ? ', ...' : ''}`
|
|
398
|
+
: '';
|
|
399
|
+
return `${prefix} ${parts.join(' ')}${sections ? DIM(sections) : ''}${evidence ? ` ${DIM(evidence)}` : ''}`;
|
|
400
|
+
}
|
|
401
|
+
case 'assistant_tool_calls':
|
|
402
|
+
return `${prefix} calls=${event.toolCallCount ?? 0} assistant=${(0, format_1.formatBytes)(event.contentBytes ?? 0)}`;
|
|
403
|
+
case 'checkpoint': {
|
|
404
|
+
const files = event.checkpointFiles?.length
|
|
405
|
+
? ` files=${event.checkpointFiles.slice(0, 6).join(', ')}${event.checkpointFiles.length > 6 ? ', ...' : ''}`
|
|
406
|
+
: '';
|
|
407
|
+
const targets = event.workspaceFiles?.length && !files
|
|
408
|
+
? ` targets=${event.workspaceFiles.slice(0, 6).join(', ')}${event.workspaceFiles.length > 6 ? ', ...' : ''}`
|
|
409
|
+
: '';
|
|
410
|
+
return `${prefix} id=${event.checkpointId ?? 'unknown'} saved=${event.checkpointFileCount ?? 0}${files}${targets}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
411
|
+
}
|
|
412
|
+
case 'tool_call':
|
|
413
|
+
return `${prefix} ${event.name ?? 'tool'}${event.argsSummary ? ` ${DIM(event.argsSummary)}` : ''}${event.argsArtifactId ? ` ${DIM(`args=/artifacts show ${event.argsArtifactId} --full${event.argsBytes ? ` (${(0, format_1.formatBytes)(event.argsBytes)})` : ''}`)}` : ''}${event.callId ? ` ${DIM(event.callId)}` : ''}`;
|
|
414
|
+
case 'permission_decision': {
|
|
415
|
+
const status = event.permissionApproved ? SUCCESS('approved') : ERROR('denied');
|
|
416
|
+
const parts = [
|
|
417
|
+
`source=${event.permissionSource ?? 'unknown'}`,
|
|
418
|
+
`behavior=${event.permissionBehavior ?? 'unknown'}`,
|
|
419
|
+
];
|
|
420
|
+
if (typeof event.permissionDuration === 'number') {
|
|
421
|
+
parts.push(`${event.permissionDuration}ms`);
|
|
422
|
+
}
|
|
423
|
+
return `${prefix} ${status} ${event.name ?? 'tool'} ${DIM(parts.join(' '))}${event.permissionReason ? ` ${DIM(event.permissionReason)}` : ''}`;
|
|
424
|
+
}
|
|
425
|
+
case 'tool_result': {
|
|
426
|
+
const status = event.success === false ? ERROR('error') : SUCCESS('ok');
|
|
427
|
+
const bytes = [
|
|
428
|
+
`output=${(0, format_1.formatBytes)(event.outputBytes ?? 0)}`,
|
|
429
|
+
`model=${(0, format_1.formatBytes)(event.modelVisibleBytes ?? 0)}`,
|
|
430
|
+
];
|
|
431
|
+
if (event.artifactId)
|
|
432
|
+
bytes.push(`artifact=${event.artifactId}`);
|
|
433
|
+
return `${prefix} ${status} ${event.name ?? 'tool'} ${DIM(`${event.duration ?? 0}ms`)} ${DIM(bytes.join(' '))}${event.error ? ` ${ERROR(event.error)}` : ''}`;
|
|
434
|
+
}
|
|
435
|
+
case 'message':
|
|
436
|
+
return `${prefix} assistant=${(0, format_1.formatBytes)(event.contentBytes ?? 0)}`;
|
|
437
|
+
case 'strategy_exhausted':
|
|
438
|
+
return `${prefix}${event.note ? ` ${WARN(event.note)}` : ''}`;
|
|
439
|
+
case 'complete': {
|
|
440
|
+
const stats = [
|
|
441
|
+
`finish=${event.finishReason ?? 'unknown'}`,
|
|
442
|
+
`llm=${typeof event.llmRequests === 'number' ? event.llmRequests : 'unknown'}`,
|
|
443
|
+
`tools=${typeof event.toolCalls === 'number' ? event.toolCalls : 'unknown'}`,
|
|
444
|
+
];
|
|
445
|
+
if (event.loopBudgetSource
|
|
446
|
+
|| typeof event.loopBudgetMaxLlmRequests === 'number'
|
|
447
|
+
|| typeof event.loopBudgetMaxToolCalls === 'number'
|
|
448
|
+
|| typeof event.loopBudgetMaxModelVisibleBytes === 'number') {
|
|
449
|
+
const source = event.loopBudgetSource === 'config' && event.loopBudgetBaseProfile
|
|
450
|
+
? `config/${event.loopBudgetBaseProfile}`
|
|
451
|
+
: event.loopBudgetSource ?? 'unknown';
|
|
452
|
+
const caps = [
|
|
453
|
+
typeof event.loopBudgetMaxLlmRequests === 'number'
|
|
454
|
+
? `${typeof event.llmRequests === 'number' ? event.llmRequests : '?'}/${event.loopBudgetMaxLlmRequests}llm`
|
|
455
|
+
: undefined,
|
|
456
|
+
typeof event.loopBudgetMaxToolCalls === 'number'
|
|
457
|
+
? `${typeof event.toolCalls === 'number' ? event.toolCalls : '?'}/${event.loopBudgetMaxToolCalls}tools`
|
|
458
|
+
: undefined,
|
|
459
|
+
typeof event.loopBudgetMaxModelVisibleBytes === 'number'
|
|
460
|
+
? `${(0, format_1.formatBytes)(event.loopBudgetMaxModelVisibleBytes)}visible`
|
|
461
|
+
: undefined,
|
|
462
|
+
typeof event.loopBudgetMaxReadOnlyFragmentation === 'number'
|
|
463
|
+
? `frag=${event.loopBudgetMaxReadOnlyFragmentation}`
|
|
464
|
+
: undefined,
|
|
465
|
+
event.loopBudgetConfigOverride ? 'override=yes' : undefined,
|
|
466
|
+
].filter(Boolean);
|
|
467
|
+
stats.push(`budgetProfile=${source}${caps.length ? `(${caps.join(',')})` : ''}`);
|
|
468
|
+
}
|
|
469
|
+
if (event.budgetExceededReason)
|
|
470
|
+
stats.push(`budget=${event.budgetExceededReason}`);
|
|
471
|
+
if (event.continuationActions?.length)
|
|
472
|
+
stats.push(`next=${event.continuationActions.join(',')}`);
|
|
473
|
+
if (event.continuationHint)
|
|
474
|
+
stats.push(`hint=${event.continuationHint}`);
|
|
475
|
+
if (event.localFastPathUsed)
|
|
476
|
+
stats.push('fast-path=yes');
|
|
477
|
+
return `${prefix} ${stats.join(' ')}`;
|
|
478
|
+
}
|
|
479
|
+
case 'local_fast_path':
|
|
480
|
+
return `${prefix} ${event.name ?? 'tool'}${event.argsSummary ? ` ${DIM(event.argsSummary)}` : ''}${event.argsArtifactId ? ` ${DIM(`args=/artifacts show ${event.argsArtifactId} --full${event.argsBytes ? ` (${(0, format_1.formatBytes)(event.argsBytes)})` : ''}`)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
481
|
+
case 'workspace_snapshot': {
|
|
482
|
+
const state = event.workspaceGitAvailable === false
|
|
483
|
+
? WARN('not-git')
|
|
484
|
+
: event.workspaceDirty ? WARN('dirty') : SUCCESS('clean');
|
|
485
|
+
const files = event.workspaceFiles?.length
|
|
486
|
+
? ` files=${event.workspaceFiles.slice(0, 6).join(', ')}${event.workspaceFiles.length > 6 ? ', ...' : ''}`
|
|
487
|
+
: '';
|
|
488
|
+
return `${prefix} ${event.workspacePhase ?? 'unknown'} ${state} count=${event.workspaceFileCount ?? 0}${event.workspaceBranch ? ` branch=${event.workspaceBranch}` : ''}${files}${event.error ? ` ${ERROR(event.error)}` : ''}`;
|
|
489
|
+
}
|
|
490
|
+
case 'workspace_delta': {
|
|
491
|
+
const added = event.workspaceNewByTurn ?? [];
|
|
492
|
+
const changed = event.workspaceChangedByTurn ?? [];
|
|
493
|
+
const modifiedPreExisting = event.workspaceModifiedPreExistingByTurn ?? [];
|
|
494
|
+
const resolved = event.workspaceResolvedByTurn ?? [];
|
|
495
|
+
const parts = [
|
|
496
|
+
`after=${event.workspaceFileCount ?? 0}`,
|
|
497
|
+
`new=${added.length}`,
|
|
498
|
+
`changed=${changed.length}`,
|
|
499
|
+
`pre-existing-modified=${modifiedPreExisting.length}`,
|
|
500
|
+
`resolved=${resolved.length}`,
|
|
501
|
+
];
|
|
502
|
+
const details = [
|
|
503
|
+
added.length ? `new: ${added.slice(0, 6).join(', ')}${added.length > 6 ? ', ...' : ''}` : '',
|
|
504
|
+
changed.length ? `changed: ${changed.slice(0, 6).join(', ')}${changed.length > 6 ? ', ...' : ''}` : '',
|
|
505
|
+
modifiedPreExisting.length ? `pre-existing modified: ${modifiedPreExisting.slice(0, 6).join(', ')}${modifiedPreExisting.length > 6 ? ', ...' : ''}` : '',
|
|
506
|
+
resolved.length ? `resolved: ${resolved.slice(0, 6).join(', ')}${resolved.length > 6 ? ', ...' : ''}` : '',
|
|
507
|
+
].filter(Boolean).join(' | ');
|
|
508
|
+
return `${prefix} ${parts.join(' ')}${details ? ` ${DIM(details)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
509
|
+
}
|
|
510
|
+
case 'verification_profile': {
|
|
511
|
+
const commands = event.verificationCommands ?? [];
|
|
512
|
+
const files = event.verificationChangedFiles ?? [];
|
|
513
|
+
const details = [
|
|
514
|
+
`profile=${event.verificationProfile ?? 'unknown'}`,
|
|
515
|
+
`required=${event.verificationRequired === false ? 'no' : 'yes'}`,
|
|
516
|
+
`commands=${commands.length}`,
|
|
517
|
+
`files=${files.length}`,
|
|
518
|
+
];
|
|
519
|
+
const commandPreview = commands.length
|
|
520
|
+
? ` cmds: ${commands.slice(0, 4).join(' && ')}${commands.length > 4 ? ' && ...' : ''}`
|
|
521
|
+
: '';
|
|
522
|
+
return `${prefix} ${details.join(' ')}${commandPreview ? ` ${DIM(commandPreview)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
523
|
+
}
|
|
524
|
+
case 'verification_result': {
|
|
525
|
+
const status = event.verificationPassed === true ? SUCCESS('passed') : ERROR('failed');
|
|
526
|
+
const command = event.verificationCommand ?? 'unknown';
|
|
527
|
+
return `${prefix} ${status} ${command}${typeof event.outputBytes === 'number' ? ` ${DIM((0, format_1.formatBytes)(event.outputBytes))}` : ''}${event.error ? ` ${ERROR(event.error)}` : ''}`;
|
|
528
|
+
}
|
|
529
|
+
case 'verification_summary': {
|
|
530
|
+
const passed = event.verificationPassedCommands?.length ?? 0;
|
|
531
|
+
const failed = event.verificationFailedCommands?.length ?? 0;
|
|
532
|
+
const missing = event.verificationMissingCommands?.length ?? 0;
|
|
533
|
+
const parts = [
|
|
534
|
+
`profile=${event.verificationProfile ?? 'unknown'}`,
|
|
535
|
+
`required=${event.verificationRequired === false ? 'no' : 'yes'}`,
|
|
536
|
+
`passed=${passed}`,
|
|
537
|
+
`failed=${failed}`,
|
|
538
|
+
`missing=${missing}`,
|
|
539
|
+
`claimAllowed=${event.verificationClaimAllowed ? 'yes' : 'no'}`,
|
|
540
|
+
];
|
|
541
|
+
const missingPreview = missing > 0
|
|
542
|
+
? ` missing: ${(event.verificationMissingCommands ?? []).slice(0, 4).join(' && ')}${missing > 4 ? ' && ...' : ''}`
|
|
543
|
+
: '';
|
|
544
|
+
return `${prefix} ${parts.join(' ')}${missingPreview ? ` ${DIM(missingPreview)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
545
|
+
}
|
|
546
|
+
case 'aborted':
|
|
547
|
+
return `${prefix}${event.note ? ` ${WARN(event.note)}` : ''}`;
|
|
548
|
+
case 'error':
|
|
549
|
+
return `${prefix} ${ERROR(event.error ?? 'unknown error')}`;
|
|
550
|
+
default:
|
|
551
|
+
return prefix;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function handleTrace(ctx, args = '') {
|
|
555
|
+
const session = ctx.getSession?.() ?? (ctx.sessionId ? (0, session_storage_1.loadSessionMeta)(ctx.sessionId) : null);
|
|
556
|
+
if (!session) {
|
|
557
|
+
console.log(ERROR('No active session.'));
|
|
558
|
+
console.log(DIM('Use /resume <session-id> first, or start a chat turn to create a session.'));
|
|
559
|
+
return { success: false };
|
|
560
|
+
}
|
|
561
|
+
const events = (0, session_storage_1.readSessionTraceEvents)(session.id);
|
|
562
|
+
if (events.length === 0) {
|
|
563
|
+
console.log(DIM(`No trace events recorded for session ${session.id.slice(0, 8)} yet.`));
|
|
564
|
+
return { success: true };
|
|
565
|
+
}
|
|
566
|
+
const ref = args.trim();
|
|
567
|
+
const requestedTurnId = ref && ref !== 'latest' ? ref : events[events.length - 1].turnId;
|
|
568
|
+
const turnEvents = events.filter(event => event.turnId === requestedTurnId);
|
|
569
|
+
if (turnEvents.length === 0) {
|
|
570
|
+
const recentTurnIds = [...new Set(events.map(event => event.turnId))].slice(-8).reverse();
|
|
571
|
+
console.log(ERROR(`Trace turn not found: ${requestedTurnId}`));
|
|
572
|
+
console.log(DIM(`Recent turns: ${recentTurnIds.join(', ')}`));
|
|
573
|
+
return { success: false };
|
|
574
|
+
}
|
|
575
|
+
console.log(HEADER(`Trace ${requestedTurnId}`));
|
|
576
|
+
console.log(DIM(`Session ${session.id} Events ${turnEvents.length}`));
|
|
577
|
+
console.log(DIM('─'.repeat(40)));
|
|
578
|
+
for (const event of turnEvents) {
|
|
579
|
+
console.log(` ${formatTraceEventLine(event)}`);
|
|
580
|
+
}
|
|
581
|
+
console.log();
|
|
582
|
+
console.log(DIM('Trace stores metadata only; full transcript and tool output stay in session/artifacts.'));
|
|
583
|
+
return { success: true };
|
|
584
|
+
}
|
|
585
|
+
function formatDateTime(timestamp) {
|
|
586
|
+
return new Date(timestamp).toLocaleString();
|
|
587
|
+
}
|
|
588
|
+
function parseArtifactArgs(args) {
|
|
589
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
590
|
+
let full = false;
|
|
591
|
+
let limit = 20;
|
|
592
|
+
const positional = [];
|
|
593
|
+
for (let i = 0; i < parts.length; i++) {
|
|
594
|
+
const part = parts[i];
|
|
595
|
+
if (part === '--full') {
|
|
596
|
+
full = true;
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if ((part === '--limit' || part === '-n') && parts[i + 1]) {
|
|
600
|
+
const parsed = Number(parts[i + 1]);
|
|
601
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
602
|
+
limit = Math.min(parsed, 200);
|
|
603
|
+
}
|
|
604
|
+
i++;
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
positional.push(part);
|
|
608
|
+
}
|
|
609
|
+
if (positional[0] === 'show' || positional[0] === 'cat') {
|
|
610
|
+
return { action: 'show', ref: positional[1], full, limit };
|
|
611
|
+
}
|
|
612
|
+
if (positional[0]) {
|
|
613
|
+
return { action: 'show', ref: positional[0], full, limit };
|
|
614
|
+
}
|
|
615
|
+
return { action: 'list', full, limit };
|
|
616
|
+
}
|
|
617
|
+
function printArtifactPreview(content, full) {
|
|
618
|
+
const maxPreviewBytes = 16 * 1024;
|
|
619
|
+
if (full || Buffer.byteLength(content, 'utf8') <= maxPreviewBytes) {
|
|
620
|
+
console.log(content);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const preview = Buffer.from(content, 'utf8').subarray(0, maxPreviewBytes).toString('utf8');
|
|
624
|
+
console.log(preview);
|
|
625
|
+
console.log();
|
|
626
|
+
console.log(DIM(`... preview truncated at ${(0, format_1.formatBytes)(maxPreviewBytes)}. Use /artifacts show <id> --full for full output.`));
|
|
627
|
+
}
|
|
628
|
+
function formatArtifactPathForDisplay(artifactPath) {
|
|
629
|
+
return (0, session_storage_1.redactTraceText)(artifactPath);
|
|
630
|
+
}
|
|
631
|
+
function handleArtifacts(ctx, args = '') {
|
|
632
|
+
const parsed = parseArtifactArgs(args);
|
|
633
|
+
if (parsed.action === 'list') {
|
|
634
|
+
const artifacts = (0, tool_artifacts_1.listArtifacts)(ctx.cwd, parsed.limit);
|
|
635
|
+
console.log(HEADER('Artifacts'));
|
|
636
|
+
console.log(DIM('─'.repeat(40)));
|
|
637
|
+
if (artifacts.length === 0) {
|
|
638
|
+
console.log(DIM('No artifacts found for this project.'));
|
|
639
|
+
return { success: true };
|
|
640
|
+
}
|
|
641
|
+
for (const artifact of artifacts) {
|
|
642
|
+
console.log(`${ACCENT(artifact.id)} ${DIM(artifact.toolName)} ${(0, format_1.formatBytes)(artifact.outputBytes)}`);
|
|
643
|
+
console.log(` ${DIM(formatDateTime(artifact.modifiedAt))}`);
|
|
644
|
+
console.log(` ${DIM(formatArtifactPathForDisplay(artifact.path))}`);
|
|
645
|
+
}
|
|
646
|
+
console.log();
|
|
647
|
+
console.log(DIM('Use /artifacts show <id|prefix> to preview, or add --full for full output.'));
|
|
648
|
+
return { success: true };
|
|
649
|
+
}
|
|
650
|
+
if (!parsed.ref) {
|
|
651
|
+
console.log(ERROR('Usage: /artifacts show <id|prefix> [--full]'));
|
|
652
|
+
return { success: false };
|
|
653
|
+
}
|
|
654
|
+
const artifact = (0, tool_artifacts_1.findArtifact)(ctx.cwd, parsed.ref);
|
|
655
|
+
if (!artifact) {
|
|
656
|
+
console.log(ERROR(`Artifact not found or ambiguous: ${parsed.ref}`));
|
|
657
|
+
console.log(DIM('Run /artifacts to list available artifact ids.'));
|
|
658
|
+
return { success: false };
|
|
659
|
+
}
|
|
660
|
+
const content = (0, tool_artifacts_1.retrieveArtifact)(artifact.path);
|
|
661
|
+
if (content == null) {
|
|
662
|
+
console.log(ERROR(`Artifact exists but cannot be read: ${artifact.id}`));
|
|
663
|
+
console.log(DIM(formatArtifactPathForDisplay(artifact.path)));
|
|
664
|
+
return { success: false };
|
|
665
|
+
}
|
|
666
|
+
console.log(HEADER(`Artifact ${artifact.id}`));
|
|
667
|
+
console.log(DIM(`Tool ${artifact.toolName} Size ${(0, format_1.formatBytes)(artifact.outputBytes)} Modified ${formatDateTime(artifact.modifiedAt)}`));
|
|
668
|
+
console.log(DIM(formatArtifactPathForDisplay(artifact.path)));
|
|
669
|
+
console.log(DIM('─'.repeat(40)));
|
|
670
|
+
printArtifactPreview(content, parsed.full);
|
|
671
|
+
return { success: true };
|
|
672
|
+
}
|
|
673
|
+
function findCheckpointByRef(ctx, ref) {
|
|
674
|
+
const checkpoints = (0, checkpoint_1.listCheckpoints)(ctx.cwd);
|
|
675
|
+
const exact = checkpoints.find(checkpoint => checkpoint.turnId === ref);
|
|
676
|
+
if (exact)
|
|
677
|
+
return { checkpoint: exact, ambiguous: false };
|
|
678
|
+
const matches = checkpoints.filter(checkpoint => checkpoint.turnId.startsWith(ref));
|
|
679
|
+
return {
|
|
680
|
+
checkpoint: matches.length === 1 ? matches[0] : undefined,
|
|
681
|
+
ambiguous: matches.length > 1,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function handleCheckpoint(ctx, args = '') {
|
|
685
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
686
|
+
const action = tokens[0] ?? 'list';
|
|
687
|
+
if (action === 'list') {
|
|
688
|
+
const checkpoints = (0, checkpoint_1.listCheckpoints)(ctx.cwd);
|
|
689
|
+
console.log(HEADER('Checkpoints'));
|
|
690
|
+
console.log(DIM('─'.repeat(40)));
|
|
691
|
+
if (checkpoints.length === 0) {
|
|
692
|
+
console.log(DIM('No checkpoints found for this project.'));
|
|
693
|
+
return { success: true };
|
|
694
|
+
}
|
|
695
|
+
for (const checkpoint of checkpoints.slice(0, 20)) {
|
|
696
|
+
console.log(`${ACCENT(checkpoint.turnId)} ${DIM(formatDateTime(checkpoint.createdAt))}`);
|
|
697
|
+
const files = checkpoint.files.map(file => file.path).slice(0, 8).join(', ');
|
|
698
|
+
console.log(` ${DIM(`${checkpoint.files.length} file(s)${files ? `: ${files}` : ''}`)}`);
|
|
699
|
+
}
|
|
700
|
+
console.log();
|
|
701
|
+
console.log(DIM('Use /checkpoint restore <turn-id|prefix> to preview, then add --yes to restore.'));
|
|
702
|
+
return { success: true };
|
|
703
|
+
}
|
|
704
|
+
if (action !== 'restore') {
|
|
705
|
+
console.log(ERROR('Usage: /checkpoint [list|restore <turn-id|prefix> [--yes]]'));
|
|
706
|
+
return { success: false };
|
|
707
|
+
}
|
|
708
|
+
const ref = tokens.find(token => token !== 'restore' && token !== '--yes');
|
|
709
|
+
const confirmed = tokens.includes('--yes');
|
|
710
|
+
if (!ref) {
|
|
711
|
+
console.log(ERROR('Usage: /checkpoint restore <turn-id|prefix> [--yes]'));
|
|
712
|
+
return { success: false };
|
|
713
|
+
}
|
|
714
|
+
const { checkpoint, ambiguous } = findCheckpointByRef(ctx, ref);
|
|
715
|
+
if (!checkpoint) {
|
|
716
|
+
console.log(ERROR(ambiguous
|
|
717
|
+
? `Checkpoint prefix is ambiguous: ${ref}`
|
|
718
|
+
: `Checkpoint not found: ${ref}`));
|
|
719
|
+
console.log(DIM('Run /checkpoint to list available checkpoint ids.'));
|
|
720
|
+
return { success: false };
|
|
721
|
+
}
|
|
722
|
+
if (!confirmed) {
|
|
723
|
+
console.log(HEADER(`Checkpoint ${checkpoint.turnId}`));
|
|
724
|
+
console.log(DIM(`${formatDateTime(checkpoint.createdAt)} ${checkpoint.files.length} file(s)`));
|
|
725
|
+
for (const file of checkpoint.files.slice(0, 20)) {
|
|
726
|
+
console.log(` ${file.path} ${DIM((0, format_1.formatBytes)(file.sizeBytes))}`);
|
|
727
|
+
}
|
|
728
|
+
console.log();
|
|
729
|
+
console.log(WARN(`This will overwrite current files from checkpoint ${checkpoint.turnId}.`));
|
|
730
|
+
console.log(DIM(`Run /checkpoint restore ${checkpoint.turnId} --yes to restore.`));
|
|
731
|
+
return { success: true };
|
|
732
|
+
}
|
|
733
|
+
const result = (0, checkpoint_1.restoreCheckpoint)(ctx.cwd, checkpoint.turnId);
|
|
734
|
+
if (result.error) {
|
|
735
|
+
console.log(ERROR(result.error));
|
|
736
|
+
return { success: false, error: result.error };
|
|
737
|
+
}
|
|
738
|
+
console.log(SUCCESS(`Restored ${result.restored.length} file(s) from checkpoint ${checkpoint.turnId}.`));
|
|
739
|
+
for (const file of result.restored.slice(0, 20)) {
|
|
740
|
+
console.log(` ${file}`);
|
|
741
|
+
}
|
|
742
|
+
return { success: true, output: result.restored.join('\n') };
|
|
743
|
+
}
|
|
235
744
|
function snapshotCurrentModel(ctx) {
|
|
236
745
|
return ctx.store.getSnapshot().currentModel || ctx.config.model;
|
|
237
746
|
}
|
|
@@ -917,6 +1426,7 @@ async function handleChat(ctx, input) {
|
|
|
917
1426
|
abortSignal: ctx.abortSignal,
|
|
918
1427
|
harness,
|
|
919
1428
|
input,
|
|
1429
|
+
loopBudget: (0, loop_budget_1.resolveRuntimeLoopBudget)(input, ctx.config, harness.toJSON()),
|
|
920
1430
|
})) {
|
|
921
1431
|
switch (event.type) {
|
|
922
1432
|
case 'request_start':
|
|
@@ -1398,7 +1908,7 @@ async function handleCompact(ctx, args) {
|
|
|
1398
1908
|
const percent = Math.round((reduction / history.length) * 100);
|
|
1399
1909
|
const sessionId = ctx.getSession?.()?.id ?? ctx.sessionId;
|
|
1400
1910
|
if (sessionId) {
|
|
1401
|
-
(0, session_storage_1.
|
|
1911
|
+
(0, session_storage_1.persistSessionCompactHistory)(sessionId, compacted);
|
|
1402
1912
|
}
|
|
1403
1913
|
console.log(SUCCESS(`✔ Compacted ${history.length} → ${compacted.length} messages`));
|
|
1404
1914
|
console.log(DIM(` Reduced by ${reduction} messages (${percent}%)`));
|
|
@@ -1676,16 +2186,32 @@ function restoreSession(ctx, session, isLast) {
|
|
|
1676
2186
|
const history = (0, session_storage_1.loadSessionHistory)(resumed.id);
|
|
1677
2187
|
if (history.length > 0) {
|
|
1678
2188
|
const summary = generateHistorySummary(history);
|
|
2189
|
+
const eventSummary = generateRestoredSessionEventSummary(history);
|
|
1679
2190
|
console.log(DIM(` Summary: ${summary}`));
|
|
1680
2191
|
console.log();
|
|
1681
2192
|
ctx.store.setState({ conversationHistory: history });
|
|
1682
2193
|
ctx.store.setState({ harnessState: (0, session_storage_1.loadSessionHarnessState)(resumed.id) ?? resumed.harnessState });
|
|
1683
2194
|
(0, framework_1.resetToolState)();
|
|
2195
|
+
ctx.sessionRestored?.({
|
|
2196
|
+
sessionId: resumed.id,
|
|
2197
|
+
projectPath: resumed.projectPath,
|
|
2198
|
+
model: resumed.model,
|
|
2199
|
+
restoredMessages: history.length,
|
|
2200
|
+
messageCount: resumed.messageCount,
|
|
2201
|
+
summary: eventSummary,
|
|
2202
|
+
});
|
|
1684
2203
|
console.log(SUCCESS(`✔ Restored ${history.length} messages`));
|
|
1685
2204
|
}
|
|
1686
2205
|
else {
|
|
1687
2206
|
console.log();
|
|
1688
2207
|
console.log(DIM(' No messages in session'));
|
|
2208
|
+
ctx.sessionRestored?.({
|
|
2209
|
+
sessionId: resumed.id,
|
|
2210
|
+
projectPath: resumed.projectPath,
|
|
2211
|
+
model: resumed.model,
|
|
2212
|
+
restoredMessages: 0,
|
|
2213
|
+
messageCount: resumed.messageCount,
|
|
2214
|
+
});
|
|
1689
2215
|
}
|
|
1690
2216
|
console.log();
|
|
1691
2217
|
return { success: true };
|
|
@@ -1809,13 +2335,26 @@ async function handleEditPreview(ctx) {
|
|
|
1809
2335
|
return { success: parsed.success === true };
|
|
1810
2336
|
}
|
|
1811
2337
|
/** Generate a brief summary of conversation history */
|
|
2338
|
+
function truncateRedactedSummary(text, maxLength) {
|
|
2339
|
+
const redacted = (0, session_storage_1.redactTraceText)(text);
|
|
2340
|
+
if (redacted.length <= maxLength)
|
|
2341
|
+
return redacted;
|
|
2342
|
+
let truncated = redacted.slice(0, maxLength);
|
|
2343
|
+
for (const marker of ['[REDACTED_SECRET]']) {
|
|
2344
|
+
const markerStart = truncated.indexOf(marker.slice(0, 6));
|
|
2345
|
+
if (markerStart >= 0 && !truncated.includes(marker)) {
|
|
2346
|
+
truncated = `${truncated.slice(0, markerStart)}${marker}`;
|
|
2347
|
+
break;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
return `${truncated}...`;
|
|
2351
|
+
}
|
|
1812
2352
|
function generateHistorySummary(messages) {
|
|
1813
2353
|
const userMsgs = messages.filter(m => m.role === 'user' && m.content);
|
|
1814
2354
|
const assistantMsgsWithTools = messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0);
|
|
1815
2355
|
// Extract topics from first few user messages
|
|
1816
2356
|
const topics = userMsgs.slice(0, 3).map(m => {
|
|
1817
|
-
|
|
1818
|
-
return content.length > 40 ? content.slice(0, 40) + '...' : content;
|
|
2357
|
+
return truncateRedactedSummary(m.content || '', 40);
|
|
1819
2358
|
});
|
|
1820
2359
|
// Extract tools used
|
|
1821
2360
|
const toolsUsed = assistantMsgsWithTools.flatMap(m => m.tool_calls?.map(tc => tc.function.name) || []);
|
|
@@ -1833,6 +2372,14 @@ function generateHistorySummary(messages) {
|
|
|
1833
2372
|
}
|
|
1834
2373
|
return parts.join('. ');
|
|
1835
2374
|
}
|
|
2375
|
+
function generateRestoredSessionEventSummary(messages) {
|
|
2376
|
+
const assistantMsgsWithTools = messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0);
|
|
2377
|
+
const toolsUsed = assistantMsgsWithTools.flatMap(m => m.tool_calls?.map(tc => tc.function.name) || []);
|
|
2378
|
+
const uniqueTools = [...new Set(toolsUsed)].slice(0, 8);
|
|
2379
|
+
if (uniqueTools.length === 0)
|
|
2380
|
+
return undefined;
|
|
2381
|
+
return `Tools: ${uniqueTools.join(', ')}`;
|
|
2382
|
+
}
|
|
1836
2383
|
function continueAsSlashChat(name, args) {
|
|
1837
2384
|
const trimmed = args.trim();
|
|
1838
2385
|
return {
|
|
@@ -2115,6 +2662,44 @@ const COMMANDS = [
|
|
|
2115
2662
|
type: 'builtin',
|
|
2116
2663
|
execute: (ctx) => handleUsage(ctx),
|
|
2117
2664
|
},
|
|
2665
|
+
{
|
|
2666
|
+
name: 'loop-stats',
|
|
2667
|
+
aliases: ['loop'],
|
|
2668
|
+
description: 'Show detailed agent-loop budget and efficiency diagnostics',
|
|
2669
|
+
category: 'diagnostics',
|
|
2670
|
+
priority: 12,
|
|
2671
|
+
type: 'builtin',
|
|
2672
|
+
execute: (ctx) => handleLoopStats(ctx),
|
|
2673
|
+
},
|
|
2674
|
+
{
|
|
2675
|
+
name: 'trace',
|
|
2676
|
+
description: 'Show structured event timeline for the latest or selected turn',
|
|
2677
|
+
argumentHint: '[latest|turn-id]',
|
|
2678
|
+
category: 'diagnostics',
|
|
2679
|
+
priority: 14,
|
|
2680
|
+
type: 'builtin',
|
|
2681
|
+
execute: (ctx, args) => handleTrace(ctx, args),
|
|
2682
|
+
},
|
|
2683
|
+
{
|
|
2684
|
+
name: 'artifacts',
|
|
2685
|
+
aliases: ['artifact'],
|
|
2686
|
+
description: 'List or inspect saved full tool outputs for this project',
|
|
2687
|
+
argumentHint: '[show <id|prefix> --full]',
|
|
2688
|
+
category: 'diagnostics',
|
|
2689
|
+
priority: 16,
|
|
2690
|
+
type: 'builtin',
|
|
2691
|
+
execute: (ctx, args) => handleArtifacts(ctx, args),
|
|
2692
|
+
},
|
|
2693
|
+
{
|
|
2694
|
+
name: 'checkpoint',
|
|
2695
|
+
aliases: ['checkpoints'],
|
|
2696
|
+
description: 'List or restore file checkpoints created before agent edits',
|
|
2697
|
+
argumentHint: '[list|restore <turn-id|prefix> --yes]',
|
|
2698
|
+
category: 'diagnostics',
|
|
2699
|
+
priority: 18,
|
|
2700
|
+
type: 'builtin',
|
|
2701
|
+
execute: (ctx, args) => handleCheckpoint(ctx, args),
|
|
2702
|
+
},
|
|
2118
2703
|
{
|
|
2119
2704
|
name: 'cost',
|
|
2120
2705
|
description: 'Show session token usage',
|