openhorse 0.2.12 → 0.2.15
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 +814 -61
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/types.d.ts +7 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/core/checkpoint.d.ts +8 -0
- package/dist/core/checkpoint.d.ts.map +1 -1
- package/dist/core/checkpoint.js +69 -10
- package/dist/core/checkpoint.js.map +1 -1
- package/dist/core/cost-tracker.d.ts.map +1 -1
- package/dist/core/cost-tracker.js +11 -0
- package/dist/core/cost-tracker.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/PromptInput.d.ts.map +1 -1
- package/dist/ink-ui/components/PromptInput.js +5 -3
- package/dist/ink-ui/components/PromptInput.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/ink-ui/screens/ReplScreen.d.ts +3 -1
- package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.js +40 -93
- package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
- package/dist/print-ui/launch.d.ts.map +1 -1
- package/dist/print-ui/launch.js +1 -0
- package/dist/print-ui/launch.js.map +1 -1
- package/dist/runtime/agent-runtime-controller.d.ts +5 -0
- package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-controller.js +15 -3
- package/dist/runtime/agent-runtime-controller.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/agent-status.d.ts +5 -0
- package/dist/runtime/agent-status.d.ts.map +1 -1
- package/dist/runtime/agent-status.js +28 -2
- package/dist/runtime/agent-status.js.map +1 -1
- package/dist/runtime/chat-controller.d.ts +7 -0
- package/dist/runtime/chat-controller.d.ts.map +1 -1
- package/dist/runtime/chat-controller.js +1084 -47
- 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/turn-controller.d.ts +4 -0
- package/dist/runtime/turn-controller.d.ts.map +1 -1
- package/dist/runtime/turn-controller.js +8 -0
- package/dist/runtime/turn-controller.js.map +1 -1
- package/dist/runtime/ui-events.d.ts +41 -1
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/runtime/ui-view-model.d.ts +287 -0
- package/dist/runtime/ui-view-model.d.ts.map +1 -0
- package/dist/runtime/ui-view-model.js +642 -0
- package/dist/runtime/ui-view-model.js.map +1 -0
- 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/file-glob.d.ts +6 -2
- package/dist/services/file-glob.d.ts.map +1 -1
- package/dist/services/file-glob.js +10 -4
- package/dist/services/file-glob.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/model-catalog.d.ts +14 -0
- package/dist/services/model-catalog.d.ts.map +1 -0
- package/dist/services/model-catalog.js +73 -0
- package/dist/services/model-catalog.js.map +1 -0
- package/dist/services/model-context.d.ts.map +1 -1
- package/dist/services/model-context.js +3 -0
- package/dist/services/model-context.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 +99 -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 +51 -0
- package/dist/services/verification-profile.d.ts.map +1 -0
- package/dist/services/verification-profile.js +306 -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/terminal-ui/completion.d.ts.map +1 -1
- package/dist/terminal-ui/completion.js +9 -6
- package/dist/terminal-ui/completion.js.map +1 -1
- package/dist/terminal-ui/launch.d.ts +15 -1
- package/dist/terminal-ui/launch.d.ts.map +1 -1
- package/dist/terminal-ui/launch.js +300 -56
- package/dist/terminal-ui/launch.js.map +1 -1
- package/dist/terminal-ui/raw-editor.d.ts +4 -0
- package/dist/terminal-ui/raw-editor.d.ts.map +1 -1
- package/dist/terminal-ui/raw-editor.js +29 -0
- package/dist/terminal-ui/raw-editor.js.map +1 -1
- package/dist/tui-ui/launch.d.ts.map +1 -1
- package/dist/tui-ui/launch.js +1 -0
- package/dist/tui-ui/launch.js.map +1 -1
- package/dist/tui-ui/layout.d.ts.map +1 -1
- package/dist/tui-ui/layout.js +21 -28
- package/dist/tui-ui/layout.js.map +1 -1
- package/dist/tui-ui/pickers.d.ts.map +1 -1
- package/dist/tui-ui/pickers.js +21 -62
- package/dist/tui-ui/pickers.js.map +1 -1
- package/dist/tui-ui/state.d.ts +2 -4
- package/dist/tui-ui/state.d.ts.map +1 -1
- package/dist/tui-ui/state.js +13 -11
- package/dist/tui-ui/state.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/index.js
CHANGED
|
@@ -17,7 +17,7 @@ exports.getCommandNames = getCommandNames;
|
|
|
17
17
|
exports.executeChat = handleChat;
|
|
18
18
|
const chalk_1 = __importDefault(require("chalk"));
|
|
19
19
|
const types_1 = require("./types");
|
|
20
|
-
const
|
|
20
|
+
const ui_view_model_1 = require("../runtime/ui-view-model");
|
|
21
21
|
const task_manager_1 = require("../services/task-manager");
|
|
22
22
|
const agent_runner_1 = require("../services/agent-runner");
|
|
23
23
|
const config_1 = require("../services/config");
|
|
@@ -38,10 +38,14 @@ const project_instructions_1 = require("../services/project-instructions");
|
|
|
38
38
|
const prompt_context_1 = require("../services/prompt-context");
|
|
39
39
|
const doctor_1 = require("../services/doctor");
|
|
40
40
|
const model_context_1 = require("../services/model-context");
|
|
41
|
+
const model_catalog_1 = require("../services/model-catalog");
|
|
41
42
|
const workspace_diff_1 = require("../services/workspace-diff");
|
|
42
43
|
const commit_plan_1 = require("../services/commit-plan");
|
|
44
|
+
const tool_artifacts_1 = require("../core/tool-artifacts");
|
|
45
|
+
const checkpoint_1 = require("../core/checkpoint");
|
|
43
46
|
const storage_maintenance_1 = require("../services/storage-maintenance");
|
|
44
47
|
const agent_status_1 = require("../runtime/agent-status");
|
|
48
|
+
const loop_budget_1 = require("../runtime/loop-budget");
|
|
45
49
|
// ============================================================================
|
|
46
50
|
// 颜色常量
|
|
47
51
|
// ============================================================================
|
|
@@ -63,7 +67,10 @@ const CATEGORY_ORDER = [
|
|
|
63
67
|
'legacy',
|
|
64
68
|
];
|
|
65
69
|
function commandUICapabilities(ctx) {
|
|
66
|
-
return (0,
|
|
70
|
+
return (0, ui_view_model_1.createStatusSnapshot)({
|
|
71
|
+
renderer: ctx.uiRenderer ?? ctx.config.ui?.renderer ?? 'terminal',
|
|
72
|
+
capabilities: ctx.uiCapabilities,
|
|
73
|
+
}).renderer.capabilities;
|
|
67
74
|
}
|
|
68
75
|
function formatTokenCount(tokens) {
|
|
69
76
|
if (tokens >= 1000000)
|
|
@@ -75,6 +82,129 @@ function formatTokenCount(tokens) {
|
|
|
75
82
|
function formatThreshold(value) {
|
|
76
83
|
return `${Math.round(value * 100)}%`;
|
|
77
84
|
}
|
|
85
|
+
function formatDurationMs(ms) {
|
|
86
|
+
if (ms >= 1000)
|
|
87
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
88
|
+
return `${Math.round(ms)}ms`;
|
|
89
|
+
}
|
|
90
|
+
function formatRendererStatus(ctx) {
|
|
91
|
+
const snapshot = (0, ui_view_model_1.createStatusSnapshot)({
|
|
92
|
+
renderer: ctx.uiRenderer ?? ctx.config.ui?.renderer ?? 'terminal',
|
|
93
|
+
capabilities: ctx.uiCapabilities,
|
|
94
|
+
});
|
|
95
|
+
const status = snapshot.renderer.status === 'beta' || (0, config_1.isBetaUIRenderer)(snapshot.renderer.name)
|
|
96
|
+
? WARN('beta')
|
|
97
|
+
: snapshot.renderer.status === 'stable'
|
|
98
|
+
? SUCCESS('stable')
|
|
99
|
+
: snapshot.renderer.status === 'non-interactive'
|
|
100
|
+
? DIM('non-interactive')
|
|
101
|
+
: DIM('custom');
|
|
102
|
+
return `${BRAND(snapshot.renderer.name)} ${status} ${DIM(snapshot.renderer.capabilityLabels.join(', '))}`;
|
|
103
|
+
}
|
|
104
|
+
function formatModelAliasHelp() {
|
|
105
|
+
return Object.keys((0, model_catalog_1.getModelCatalogAliases)()).sort().join(', ');
|
|
106
|
+
}
|
|
107
|
+
function formatLoopBudgetSource(stats) {
|
|
108
|
+
const source = stats.loopBudgetSource ?? 'unknown';
|
|
109
|
+
if (source === 'config' && stats.loopBudgetBaseProfile) {
|
|
110
|
+
return `config over ${stats.loopBudgetBaseProfile}`;
|
|
111
|
+
}
|
|
112
|
+
return source;
|
|
113
|
+
}
|
|
114
|
+
function formatLoopStatsLines(stats, detail = false) {
|
|
115
|
+
const lines = [
|
|
116
|
+
`Finish ${stats.finishReason}`,
|
|
117
|
+
`Requests ${stats.llmRequests} LLM / ${stats.turnsStarted} turns`,
|
|
118
|
+
`Tools ${stats.toolCalls} total (${stats.readOnlyToolCalls} read-only, ${stats.unsafeToolCalls} unsafe)`,
|
|
119
|
+
`Tool bytes ${(0, format_1.formatBytes)(stats.modelVisibleToolBytes)} model-visible / ${(0, format_1.formatBytes)(stats.toolResultBytes)} total`,
|
|
120
|
+
];
|
|
121
|
+
if (stats.summarizedBytes > 0) {
|
|
122
|
+
lines.push(`Saved ${(0, format_1.formatBytes)(stats.summarizedBytes)} from model context`);
|
|
123
|
+
}
|
|
124
|
+
if (stats.compactTrigger) {
|
|
125
|
+
lines.push(`Compact ${stats.compactTrigger}`);
|
|
126
|
+
}
|
|
127
|
+
if (stats.localFastPathUsed) {
|
|
128
|
+
lines.push('Fast path yes');
|
|
129
|
+
}
|
|
130
|
+
if (stats.budgetExceededReason) {
|
|
131
|
+
lines.push(`Budget ${stats.budgetExceededReason}`);
|
|
132
|
+
}
|
|
133
|
+
if (stats.continuationActions && stats.continuationActions.length > 0) {
|
|
134
|
+
lines.push(`Next ${stats.continuationActions.join(', ')}`);
|
|
135
|
+
}
|
|
136
|
+
if ((stats.providerRetryCount ?? 0) > 0) {
|
|
137
|
+
const retryParts = [
|
|
138
|
+
`${stats.providerRetryCount} retries`,
|
|
139
|
+
`delay ${formatDurationMs(stats.providerRetryDelayMs ?? 0)}`,
|
|
140
|
+
stats.providerLastRetryErrorType
|
|
141
|
+
? `last ${stats.providerLastRetryErrorType}${stats.providerLastRetryStatus ? `/${stats.providerLastRetryStatus}` : ''}`
|
|
142
|
+
: undefined,
|
|
143
|
+
].filter(Boolean);
|
|
144
|
+
lines.push(`Provider ${retryParts.join(', ')}`);
|
|
145
|
+
}
|
|
146
|
+
if ((stats.providerFallbackCount ?? 0) > 0 || stats.providerUsingFallback) {
|
|
147
|
+
const fallbackPath = stats.providerFallbackFromModel && stats.providerFallbackToModel
|
|
148
|
+
? `${stats.providerFallbackFromModel} -> ${stats.providerFallbackToModel}`
|
|
149
|
+
: stats.providerFinalModel
|
|
150
|
+
? `final ${stats.providerFinalModel}`
|
|
151
|
+
: 'active';
|
|
152
|
+
lines.push(`Fallback ${fallbackPath}`);
|
|
153
|
+
}
|
|
154
|
+
if (typeof stats.verificationClaimAllowed === 'boolean') {
|
|
155
|
+
const verificationParts = [
|
|
156
|
+
stats.verificationProfile ?? 'unknown',
|
|
157
|
+
`required=${stats.verificationRequired ? 'yes' : 'no'}`,
|
|
158
|
+
`passed=${stats.verificationPassedCommands?.length ?? 0}`,
|
|
159
|
+
`failed=${stats.verificationFailedCommands?.length ?? 0}`,
|
|
160
|
+
`missing=${stats.verificationMissingCommands?.length ?? 0}`,
|
|
161
|
+
`claim=${stats.verificationClaimAllowed ? 'yes' : 'no'}`,
|
|
162
|
+
];
|
|
163
|
+
lines.push(`Verify ${verificationParts.join(' ')}`);
|
|
164
|
+
}
|
|
165
|
+
if (typeof stats.loopBudgetMaxLlmRequests === 'number'
|
|
166
|
+
|| typeof stats.loopBudgetMaxToolCalls === 'number'
|
|
167
|
+
|| typeof stats.loopBudgetMaxModelVisibleBytes === 'number') {
|
|
168
|
+
const caps = [
|
|
169
|
+
typeof stats.loopBudgetMaxLlmRequests === 'number'
|
|
170
|
+
? `${stats.llmRequests}/${stats.loopBudgetMaxLlmRequests} LLM`
|
|
171
|
+
: undefined,
|
|
172
|
+
typeof stats.loopBudgetMaxToolCalls === 'number'
|
|
173
|
+
? `${stats.toolCalls}/${stats.loopBudgetMaxToolCalls} tools`
|
|
174
|
+
: undefined,
|
|
175
|
+
typeof stats.loopBudgetMaxModelVisibleBytes === 'number'
|
|
176
|
+
? `${(0, format_1.formatBytes)(stats.modelVisibleToolBytes)}/${(0, format_1.formatBytes)(stats.loopBudgetMaxModelVisibleBytes)} visible`
|
|
177
|
+
: undefined,
|
|
178
|
+
typeof stats.loopBudgetMaxReadOnlyFragmentation === 'number'
|
|
179
|
+
? `fragment ${stats.singleReadOnlyStreak}/${stats.loopBudgetMaxReadOnlyFragmentation}`
|
|
180
|
+
: undefined,
|
|
181
|
+
].filter(Boolean);
|
|
182
|
+
lines.push(`Budget cap ${caps.join(', ')} (${formatLoopBudgetSource(stats)})`);
|
|
183
|
+
}
|
|
184
|
+
if (stats.singleReadOnlyStreak > 0 || stats.batchReadSuggestionCount > 0) {
|
|
185
|
+
lines.push(`Read-only streak ${stats.singleReadOnlyStreak}, batch_read hints ${stats.batchReadSuggestionCount}`);
|
|
186
|
+
}
|
|
187
|
+
if (detail) {
|
|
188
|
+
lines.push(`Unsafe ${stats.unsafeToolCalls}`);
|
|
189
|
+
if (stats.providerRetryErrorTypes && stats.providerRetryErrorTypes.length > 0) {
|
|
190
|
+
lines.push(`Retry type ${stats.providerRetryErrorTypes.join(', ')}`);
|
|
191
|
+
}
|
|
192
|
+
if (stats.verificationFailedCommands && stats.verificationFailedCommands.length > 0) {
|
|
193
|
+
lines.push(`Failed ${stats.verificationFailedCommands.join(' && ')}`);
|
|
194
|
+
}
|
|
195
|
+
if (stats.verificationMissingCommands && stats.verificationMissingCommands.length > 0) {
|
|
196
|
+
lines.push(`Missing ${stats.verificationMissingCommands.join(' && ')}`);
|
|
197
|
+
}
|
|
198
|
+
if (stats.verificationSkippedReason) {
|
|
199
|
+
lines.push(`Verify why ${stats.verificationSkippedReason}`);
|
|
200
|
+
}
|
|
201
|
+
if (stats.continuationHint) {
|
|
202
|
+
lines.push(`Next why ${stats.continuationHint}`);
|
|
203
|
+
}
|
|
204
|
+
lines.push(`Budget hit ${stats.finishReason === 'budget_exceeded' ? 'yes' : 'no'}`);
|
|
205
|
+
}
|
|
206
|
+
return lines;
|
|
207
|
+
}
|
|
78
208
|
const CATEGORY_LABELS = {
|
|
79
209
|
workflow: 'Workflow',
|
|
80
210
|
session: 'Session',
|
|
@@ -171,6 +301,7 @@ function showStatus(ctx) {
|
|
|
171
301
|
console.log(` Model ${BRAND(modelId)}`);
|
|
172
302
|
console.log(` Context ${DIM(`${formatTokenCount(modelContext.contextWindow)} tokens (${modelContext.source}${modelContext.source === 'fuzzy' ? `:${modelContext.matchedId}` : ''})`)}`);
|
|
173
303
|
console.log(` Compact ${compactStats.enabled ? SUCCESS('auto') : WARN('off')} ${DIM(`predict ${formatThreshold(compactStats.predictiveCompactThreshold)}, hard ${formatThreshold(compactStats.threshold)}, used ${compactStats.ctxPercent}%`)}`);
|
|
304
|
+
console.log(` Renderer ${formatRendererStatus(ctx)}`);
|
|
174
305
|
console.log();
|
|
175
306
|
console.log(` Agents ${SUCCESS(brainStatus.agents.length)} registered`);
|
|
176
307
|
console.log(` Tasks ${brainStatus.pendingTasks} pending (${brainStatus.strategy} strategy)`);
|
|
@@ -203,15 +334,8 @@ function showStatus(ctx) {
|
|
|
203
334
|
const stats = snapshot.lastLoopStats;
|
|
204
335
|
console.log();
|
|
205
336
|
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}`);
|
|
337
|
+
for (const line of formatLoopStatsLines(stats)) {
|
|
338
|
+
console.log(` ${line}`);
|
|
215
339
|
}
|
|
216
340
|
}
|
|
217
341
|
const harnessState = snapshot.harnessState;
|
|
@@ -232,6 +356,533 @@ function showStatus(ctx) {
|
|
|
232
356
|
console.log();
|
|
233
357
|
return { success: true };
|
|
234
358
|
}
|
|
359
|
+
function handleLoopStats(ctx) {
|
|
360
|
+
const stats = ctx.store.getSnapshot().lastLoopStats;
|
|
361
|
+
if (!stats) {
|
|
362
|
+
console.log('No agent-loop stats recorded yet.');
|
|
363
|
+
return { success: true };
|
|
364
|
+
}
|
|
365
|
+
console.log(HEADER('Agent Loop Stats'));
|
|
366
|
+
console.log();
|
|
367
|
+
for (const line of formatLoopStatsLines(stats, true)) {
|
|
368
|
+
console.log(` ${line}`);
|
|
369
|
+
}
|
|
370
|
+
console.log();
|
|
371
|
+
console.log(DIM('Use this to spot excessive LLM requests, fragmented read-only tool calls, and local fast-path hits.'));
|
|
372
|
+
return { success: true };
|
|
373
|
+
}
|
|
374
|
+
function formatTraceEventLine(event) {
|
|
375
|
+
const time = new Date(event.timestamp).toLocaleTimeString();
|
|
376
|
+
const prefix = `${DIM(time)} ${ACCENT(event.type)}`;
|
|
377
|
+
switch (event.type) {
|
|
378
|
+
case 'turn_start':
|
|
379
|
+
return `${prefix} input=${(0, format_1.formatBytes)(event.inputBytes ?? 0)}${event.localFastPathUsed ? ' fast-path' : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
380
|
+
case 'request_start':
|
|
381
|
+
return `${prefix} model=${event.model ?? 'unknown'} iteration=${event.turn ?? '?'}`;
|
|
382
|
+
case 'provider_retry': {
|
|
383
|
+
const parts = [
|
|
384
|
+
`count=${event.providerRetryCount ?? 0}`,
|
|
385
|
+
`delay=${formatDurationMs(event.providerRetryDelayMs ?? 0)}`,
|
|
386
|
+
event.providerLastRetryErrorType
|
|
387
|
+
? `last=${event.providerLastRetryErrorType}${event.providerLastRetryStatus ? `/${event.providerLastRetryStatus}` : ''}`
|
|
388
|
+
: '',
|
|
389
|
+
event.providerRetryErrorTypes?.length ? `types=${event.providerRetryErrorTypes.join(',')}` : '',
|
|
390
|
+
event.providerFinalModel ? `final=${event.providerFinalModel}` : '',
|
|
391
|
+
].filter(Boolean);
|
|
392
|
+
return `${prefix} ${parts.join(' ')}`;
|
|
393
|
+
}
|
|
394
|
+
case 'provider_fallback': {
|
|
395
|
+
const path = event.providerFallbackFromModel && event.providerFallbackToModel
|
|
396
|
+
? `${event.providerFallbackFromModel}->${event.providerFallbackToModel}`
|
|
397
|
+
: event.providerFinalModel ?? 'active';
|
|
398
|
+
const parts = [
|
|
399
|
+
`count=${event.providerFallbackCount ?? 0}`,
|
|
400
|
+
`path=${path}`,
|
|
401
|
+
`using=${event.providerUsingFallback ? 'yes' : 'no'}`,
|
|
402
|
+
];
|
|
403
|
+
return `${prefix} ${parts.join(' ')}`;
|
|
404
|
+
}
|
|
405
|
+
case 'prompt_assembly': {
|
|
406
|
+
const parts = [
|
|
407
|
+
`model=${event.promptModelId ?? 'unknown'}`,
|
|
408
|
+
`tokens=${event.promptEstimatedTokens ?? 0}/${event.promptBudgetTokens ?? 0}`,
|
|
409
|
+
`core=${event.promptCoreTokens ?? 0}`,
|
|
410
|
+
`evidenceBudget=${event.promptEvidenceBudgetTokens ?? 0}`,
|
|
411
|
+
`recentBudget=${event.promptRecentTurnBudgetTokens ?? 0}`,
|
|
412
|
+
`included=${event.promptIncludedEvidenceCount ?? 0}`,
|
|
413
|
+
`omitted=${event.promptOmittedEvidenceCount ?? 0}`,
|
|
414
|
+
];
|
|
415
|
+
const sections = event.promptSections?.length
|
|
416
|
+
? ` sections=${event.promptSections.join(',')}`
|
|
417
|
+
: '';
|
|
418
|
+
const evidence = event.promptIncludedEvidence?.length
|
|
419
|
+
? ` evidence=${event.promptIncludedEvidence.slice(0, 6).join(', ')}${event.promptIncludedEvidence.length > 6 ? ', ...' : ''}`
|
|
420
|
+
: '';
|
|
421
|
+
return `${prefix} ${parts.join(' ')}${sections ? DIM(sections) : ''}${evidence ? ` ${DIM(evidence)}` : ''}`;
|
|
422
|
+
}
|
|
423
|
+
case 'assistant_tool_calls':
|
|
424
|
+
return `${prefix} calls=${event.toolCallCount ?? 0} assistant=${(0, format_1.formatBytes)(event.contentBytes ?? 0)}`;
|
|
425
|
+
case 'checkpoint': {
|
|
426
|
+
const files = event.checkpointFiles?.length
|
|
427
|
+
? ` files=${event.checkpointFiles.slice(0, 6).join(', ')}${event.checkpointFiles.length > 6 ? ', ...' : ''}`
|
|
428
|
+
: '';
|
|
429
|
+
const targets = event.workspaceFiles?.length && !files
|
|
430
|
+
? ` targets=${event.workspaceFiles.slice(0, 6).join(', ')}${event.workspaceFiles.length > 6 ? ', ...' : ''}`
|
|
431
|
+
: '';
|
|
432
|
+
return `${prefix} id=${event.checkpointId ?? 'unknown'} saved=${event.checkpointFileCount ?? 0}${files}${targets}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
433
|
+
}
|
|
434
|
+
case 'tool_call':
|
|
435
|
+
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)}` : ''}`;
|
|
436
|
+
case 'permission_decision': {
|
|
437
|
+
const status = event.permissionApproved ? SUCCESS('approved') : ERROR('denied');
|
|
438
|
+
const parts = [
|
|
439
|
+
`source=${event.permissionSource ?? 'unknown'}`,
|
|
440
|
+
`behavior=${event.permissionBehavior ?? 'unknown'}`,
|
|
441
|
+
];
|
|
442
|
+
if (typeof event.permissionDuration === 'number') {
|
|
443
|
+
parts.push(`${event.permissionDuration}ms`);
|
|
444
|
+
}
|
|
445
|
+
return `${prefix} ${status} ${event.name ?? 'tool'} ${DIM(parts.join(' '))}${event.permissionReason ? ` ${DIM(event.permissionReason)}` : ''}`;
|
|
446
|
+
}
|
|
447
|
+
case 'tool_result': {
|
|
448
|
+
const status = event.success === false ? ERROR('error') : SUCCESS('ok');
|
|
449
|
+
const bytes = [
|
|
450
|
+
`output=${(0, format_1.formatBytes)(event.outputBytes ?? 0)}`,
|
|
451
|
+
`model=${(0, format_1.formatBytes)(event.modelVisibleBytes ?? 0)}`,
|
|
452
|
+
];
|
|
453
|
+
if (event.artifactId)
|
|
454
|
+
bytes.push(`artifact=${event.artifactId}`);
|
|
455
|
+
return `${prefix} ${status} ${event.name ?? 'tool'} ${DIM(`${event.duration ?? 0}ms`)} ${DIM(bytes.join(' '))}${event.error ? ` ${ERROR(event.error)}` : ''}`;
|
|
456
|
+
}
|
|
457
|
+
case 'message':
|
|
458
|
+
return `${prefix} assistant=${(0, format_1.formatBytes)(event.contentBytes ?? 0)}`;
|
|
459
|
+
case 'strategy_exhausted':
|
|
460
|
+
return `${prefix}${event.note ? ` ${WARN(event.note)}` : ''}`;
|
|
461
|
+
case 'complete': {
|
|
462
|
+
const stats = [
|
|
463
|
+
`finish=${event.finishReason ?? 'unknown'}`,
|
|
464
|
+
`llm=${typeof event.llmRequests === 'number' ? event.llmRequests : 'unknown'}`,
|
|
465
|
+
`tools=${typeof event.toolCalls === 'number' ? event.toolCalls : 'unknown'}`,
|
|
466
|
+
];
|
|
467
|
+
if (event.loopBudgetSource
|
|
468
|
+
|| typeof event.loopBudgetMaxLlmRequests === 'number'
|
|
469
|
+
|| typeof event.loopBudgetMaxToolCalls === 'number'
|
|
470
|
+
|| typeof event.loopBudgetMaxModelVisibleBytes === 'number') {
|
|
471
|
+
const source = event.loopBudgetSource === 'config' && event.loopBudgetBaseProfile
|
|
472
|
+
? `config/${event.loopBudgetBaseProfile}`
|
|
473
|
+
: event.loopBudgetSource ?? 'unknown';
|
|
474
|
+
const caps = [
|
|
475
|
+
typeof event.loopBudgetMaxLlmRequests === 'number'
|
|
476
|
+
? `${typeof event.llmRequests === 'number' ? event.llmRequests : '?'}/${event.loopBudgetMaxLlmRequests}llm`
|
|
477
|
+
: undefined,
|
|
478
|
+
typeof event.loopBudgetMaxToolCalls === 'number'
|
|
479
|
+
? `${typeof event.toolCalls === 'number' ? event.toolCalls : '?'}/${event.loopBudgetMaxToolCalls}tools`
|
|
480
|
+
: undefined,
|
|
481
|
+
typeof event.loopBudgetMaxModelVisibleBytes === 'number'
|
|
482
|
+
? `${(0, format_1.formatBytes)(event.loopBudgetMaxModelVisibleBytes)}visible`
|
|
483
|
+
: undefined,
|
|
484
|
+
typeof event.loopBudgetMaxReadOnlyFragmentation === 'number'
|
|
485
|
+
? `frag=${event.loopBudgetMaxReadOnlyFragmentation}`
|
|
486
|
+
: undefined,
|
|
487
|
+
event.loopBudgetConfigOverride ? 'override=yes' : undefined,
|
|
488
|
+
].filter(Boolean);
|
|
489
|
+
stats.push(`budgetProfile=${source}${caps.length ? `(${caps.join(',')})` : ''}`);
|
|
490
|
+
}
|
|
491
|
+
if (event.budgetExceededReason)
|
|
492
|
+
stats.push(`budget=${event.budgetExceededReason}`);
|
|
493
|
+
if (event.continuationActions?.length)
|
|
494
|
+
stats.push(`next=${event.continuationActions.join(',')}`);
|
|
495
|
+
if (event.continuationHint)
|
|
496
|
+
stats.push(`hint=${event.continuationHint}`);
|
|
497
|
+
if (event.localFastPathUsed)
|
|
498
|
+
stats.push('fast-path=yes');
|
|
499
|
+
return `${prefix} ${stats.join(' ')}`;
|
|
500
|
+
}
|
|
501
|
+
case 'local_fast_path':
|
|
502
|
+
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)}` : ''}`;
|
|
503
|
+
case 'workspace_snapshot': {
|
|
504
|
+
const state = event.workspaceGitAvailable === false
|
|
505
|
+
? WARN('not-git')
|
|
506
|
+
: event.workspaceDirty ? WARN('dirty') : SUCCESS('clean');
|
|
507
|
+
const files = event.workspaceFiles?.length
|
|
508
|
+
? ` files=${event.workspaceFiles.slice(0, 6).join(', ')}${event.workspaceFiles.length > 6 ? ', ...' : ''}`
|
|
509
|
+
: '';
|
|
510
|
+
return `${prefix} ${event.workspacePhase ?? 'unknown'} ${state} count=${event.workspaceFileCount ?? 0}${event.workspaceBranch ? ` branch=${event.workspaceBranch}` : ''}${files}${event.error ? ` ${ERROR(event.error)}` : ''}`;
|
|
511
|
+
}
|
|
512
|
+
case 'workspace_delta': {
|
|
513
|
+
const added = event.workspaceNewByTurn ?? [];
|
|
514
|
+
const changed = event.workspaceChangedByTurn ?? [];
|
|
515
|
+
const modifiedPreExisting = event.workspaceModifiedPreExistingByTurn ?? [];
|
|
516
|
+
const resolved = event.workspaceResolvedByTurn ?? [];
|
|
517
|
+
const parts = [
|
|
518
|
+
`after=${event.workspaceFileCount ?? 0}`,
|
|
519
|
+
`new=${added.length}`,
|
|
520
|
+
`changed=${changed.length}`,
|
|
521
|
+
`pre-existing-modified=${modifiedPreExisting.length}`,
|
|
522
|
+
`resolved=${resolved.length}`,
|
|
523
|
+
];
|
|
524
|
+
const details = [
|
|
525
|
+
added.length ? `new: ${added.slice(0, 6).join(', ')}${added.length > 6 ? ', ...' : ''}` : '',
|
|
526
|
+
changed.length ? `changed: ${changed.slice(0, 6).join(', ')}${changed.length > 6 ? ', ...' : ''}` : '',
|
|
527
|
+
modifiedPreExisting.length ? `pre-existing modified: ${modifiedPreExisting.slice(0, 6).join(', ')}${modifiedPreExisting.length > 6 ? ', ...' : ''}` : '',
|
|
528
|
+
resolved.length ? `resolved: ${resolved.slice(0, 6).join(', ')}${resolved.length > 6 ? ', ...' : ''}` : '',
|
|
529
|
+
].filter(Boolean).join(' | ');
|
|
530
|
+
return `${prefix} ${parts.join(' ')}${details ? ` ${DIM(details)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
531
|
+
}
|
|
532
|
+
case 'verification_profile': {
|
|
533
|
+
const commands = event.verificationCommands ?? [];
|
|
534
|
+
const files = event.verificationChangedFiles ?? [];
|
|
535
|
+
const details = [
|
|
536
|
+
`profile=${event.verificationProfile ?? 'unknown'}`,
|
|
537
|
+
`required=${event.verificationRequired === false ? 'no' : 'yes'}`,
|
|
538
|
+
`commands=${commands.length}`,
|
|
539
|
+
`files=${files.length}`,
|
|
540
|
+
];
|
|
541
|
+
if (event.verificationRisky) {
|
|
542
|
+
details.push(`risk=${WARN('high')}(${files.length} files)`);
|
|
543
|
+
}
|
|
544
|
+
const commandPreview = commands.length
|
|
545
|
+
? ` cmds: ${commands.slice(0, 4).join(' && ')}${commands.length > 4 ? ' && ...' : ''}`
|
|
546
|
+
: '';
|
|
547
|
+
return `${prefix} ${details.join(' ')}${commandPreview ? ` ${DIM(commandPreview)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
548
|
+
}
|
|
549
|
+
case 'verification_result': {
|
|
550
|
+
const status = event.verificationPassed === true ? SUCCESS('passed') : ERROR('failed');
|
|
551
|
+
const command = event.verificationCommand ?? 'unknown';
|
|
552
|
+
return `${prefix} ${status} ${command}${typeof event.outputBytes === 'number' ? ` ${DIM((0, format_1.formatBytes)(event.outputBytes))}` : ''}${event.error ? ` ${ERROR(event.error)}` : ''}`;
|
|
553
|
+
}
|
|
554
|
+
case 'verification_summary': {
|
|
555
|
+
const passed = event.verificationPassedCommands?.length ?? 0;
|
|
556
|
+
const failed = event.verificationFailedCommands?.length ?? 0;
|
|
557
|
+
const missing = event.verificationMissingCommands?.length ?? 0;
|
|
558
|
+
const parts = [
|
|
559
|
+
`profile=${event.verificationProfile ?? 'unknown'}`,
|
|
560
|
+
`required=${event.verificationRequired === false ? 'no' : 'yes'}`,
|
|
561
|
+
`passed=${passed}`,
|
|
562
|
+
`failed=${failed}`,
|
|
563
|
+
`missing=${missing}`,
|
|
564
|
+
`claimAllowed=${event.verificationClaimAllowed ? 'yes' : 'no'}`,
|
|
565
|
+
];
|
|
566
|
+
const missingPreview = missing > 0
|
|
567
|
+
? ` missing: ${(event.verificationMissingCommands ?? []).slice(0, 4).join(' && ')}${missing > 4 ? ' && ...' : ''}`
|
|
568
|
+
: '';
|
|
569
|
+
return `${prefix} ${parts.join(' ')}${missingPreview ? ` ${DIM(missingPreview)}` : ''}${event.note ? ` ${DIM(event.note)}` : ''}`;
|
|
570
|
+
}
|
|
571
|
+
case 'aborted':
|
|
572
|
+
return `${prefix}${event.note ? ` ${WARN(event.note)}` : ''}`;
|
|
573
|
+
case 'error':
|
|
574
|
+
return `${prefix} ${ERROR(event.error ?? 'unknown error')}`;
|
|
575
|
+
default:
|
|
576
|
+
return prefix;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
function handleTrace(ctx, args = '') {
|
|
580
|
+
const session = ctx.getSession?.() ?? (ctx.sessionId ? (0, session_storage_1.loadSessionMeta)(ctx.sessionId) : null);
|
|
581
|
+
if (!session) {
|
|
582
|
+
console.log(ERROR('No active session.'));
|
|
583
|
+
console.log(DIM('Use /resume <session-id> first, or start a chat turn to create a session.'));
|
|
584
|
+
return { success: false };
|
|
585
|
+
}
|
|
586
|
+
const events = (0, session_storage_1.readSessionTraceEvents)(session.id);
|
|
587
|
+
if (events.length === 0) {
|
|
588
|
+
console.log(DIM(`No trace events recorded for session ${session.id.slice(0, 8)} yet.`));
|
|
589
|
+
return { success: true };
|
|
590
|
+
}
|
|
591
|
+
const ref = args.trim();
|
|
592
|
+
const requestedTurnId = ref && ref !== 'latest' ? ref : events[events.length - 1].turnId;
|
|
593
|
+
const turnEvents = events.filter(event => event.turnId === requestedTurnId);
|
|
594
|
+
if (turnEvents.length === 0) {
|
|
595
|
+
const recentTurnIds = [...new Set(events.map(event => event.turnId))].slice(-8).reverse();
|
|
596
|
+
console.log(ERROR(`Trace turn not found: ${requestedTurnId}`));
|
|
597
|
+
console.log(DIM(`Recent turns: ${recentTurnIds.join(', ')}`));
|
|
598
|
+
return { success: false };
|
|
599
|
+
}
|
|
600
|
+
console.log(HEADER(`Trace ${requestedTurnId}`));
|
|
601
|
+
console.log(DIM(`Session ${session.id} Events ${turnEvents.length}`));
|
|
602
|
+
console.log(DIM('─'.repeat(40)));
|
|
603
|
+
for (const event of turnEvents) {
|
|
604
|
+
console.log(` ${formatTraceEventLine(event)}`);
|
|
605
|
+
}
|
|
606
|
+
console.log();
|
|
607
|
+
console.log(DIM('Trace stores metadata only; full transcript and tool output stay in session/artifacts.'));
|
|
608
|
+
return { success: true };
|
|
609
|
+
}
|
|
610
|
+
function latestToolTrace(events) {
|
|
611
|
+
for (let index = events.length - 1; index >= 0; index--) {
|
|
612
|
+
const event = events[index];
|
|
613
|
+
if (event.type !== 'tool_result' && event.type !== 'tool_call')
|
|
614
|
+
continue;
|
|
615
|
+
const callId = event.callId;
|
|
616
|
+
const result = event.type === 'tool_result' ? event : undefined;
|
|
617
|
+
const call = event.type === 'tool_call'
|
|
618
|
+
? event
|
|
619
|
+
: callId
|
|
620
|
+
? events.slice(0, index).reverse().find(candidate => candidate.type === 'tool_call' && candidate.callId === callId)
|
|
621
|
+
: undefined;
|
|
622
|
+
return { call, result };
|
|
623
|
+
}
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
function parseLastToolArgs(args = '') {
|
|
627
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
628
|
+
return {
|
|
629
|
+
full: parts.includes('--full'),
|
|
630
|
+
preview: !parts.includes('--no-preview'),
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function printLastToolArtifactPreview(projectPath, label, artifactId, full) {
|
|
634
|
+
if (!artifactId)
|
|
635
|
+
return;
|
|
636
|
+
const artifact = (0, tool_artifacts_1.findArtifact)(projectPath, artifactId);
|
|
637
|
+
if (!artifact) {
|
|
638
|
+
console.log(` ${label} preview ${DIM(`artifact not found or ambiguous: ${artifactId}`)}`);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
const content = (0, tool_artifacts_1.retrieveArtifact)(artifact.path);
|
|
642
|
+
if (content == null) {
|
|
643
|
+
console.log(` ${label} preview ${DIM(`artifact cannot be read: ${artifact.id}`)}`);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const redacted = (0, session_storage_1.redactTraceText)(content);
|
|
647
|
+
const maxPreviewBytes = 4 * 1024;
|
|
648
|
+
const shouldTruncate = !full && Buffer.byteLength(redacted, 'utf8') > maxPreviewBytes;
|
|
649
|
+
const preview = shouldTruncate
|
|
650
|
+
? Buffer.from(redacted, 'utf8').subarray(0, maxPreviewBytes).toString('utf8')
|
|
651
|
+
: redacted;
|
|
652
|
+
console.log(` ${label} preview`);
|
|
653
|
+
console.log(` ${DIM('─'.repeat(40))}`);
|
|
654
|
+
for (const line of preview.split('\n')) {
|
|
655
|
+
console.log(` ${line}`);
|
|
656
|
+
}
|
|
657
|
+
if (shouldTruncate) {
|
|
658
|
+
console.log(` ${DIM(`... preview truncated at ${(0, format_1.formatBytes)(maxPreviewBytes)}. Use /last-tool --full or /artifacts show ${artifact.id} --full.`)}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
function lastToolInputLabel(toolName) {
|
|
662
|
+
return toolName === 'exec_command' ? 'Command' : 'Args';
|
|
663
|
+
}
|
|
664
|
+
function lastToolField(label) {
|
|
665
|
+
return label.padEnd(13, ' ');
|
|
666
|
+
}
|
|
667
|
+
function handleLastTool(ctx, args = '') {
|
|
668
|
+
const options = parseLastToolArgs(args);
|
|
669
|
+
const session = ctx.getSession?.() ?? (ctx.sessionId ? (0, session_storage_1.loadSessionMeta)(ctx.sessionId) : null);
|
|
670
|
+
if (!session) {
|
|
671
|
+
console.log(ERROR('No active session.'));
|
|
672
|
+
console.log(DIM('Use /resume <session-id> first, or start a chat turn to create a session.'));
|
|
673
|
+
return { success: false };
|
|
674
|
+
}
|
|
675
|
+
const events = (0, session_storage_1.readSessionTraceEvents)(session.id);
|
|
676
|
+
const latest = latestToolTrace(events);
|
|
677
|
+
if (!latest) {
|
|
678
|
+
console.log(DIM(`No tool trace events recorded for session ${session.id.slice(0, 8)} yet.`));
|
|
679
|
+
return { success: true };
|
|
680
|
+
}
|
|
681
|
+
const call = latest.call;
|
|
682
|
+
const result = latest.result;
|
|
683
|
+
const source = result ?? call;
|
|
684
|
+
const argsSource = call ?? result;
|
|
685
|
+
const name = source?.name ?? 'tool';
|
|
686
|
+
const inputLabel = lastToolInputLabel(name);
|
|
687
|
+
const callId = source?.callId ?? call?.callId;
|
|
688
|
+
const status = result ? (result.success === false ? ERROR('error') : SUCCESS('ok')) : WARN('running');
|
|
689
|
+
console.log(HEADER('Last Tool'));
|
|
690
|
+
console.log(DIM('─'.repeat(40)));
|
|
691
|
+
console.log(` Tool ${ACCENT(name)}`);
|
|
692
|
+
console.log(` Turn ${DIM(source?.turnId ?? 'unknown')}`);
|
|
693
|
+
if (callId)
|
|
694
|
+
console.log(` Call ${DIM(callId)}`);
|
|
695
|
+
console.log(` Status ${status}`);
|
|
696
|
+
if (typeof result?.duration === 'number') {
|
|
697
|
+
console.log(` Time ${DIM(formatDurationMs(result.duration))}`);
|
|
698
|
+
}
|
|
699
|
+
if (argsSource?.argsSummary) {
|
|
700
|
+
console.log(` ${lastToolField(inputLabel)}${DIM((0, session_storage_1.redactTraceText)(argsSource.argsSummary))}`);
|
|
701
|
+
}
|
|
702
|
+
if (argsSource?.argsArtifactId) {
|
|
703
|
+
const size = typeof argsSource.argsBytes === 'number' ? ` (${(0, format_1.formatBytes)(argsSource.argsBytes)})` : '';
|
|
704
|
+
console.log(` ${lastToolField(`${inputLabel} full`)}${DIM(`/artifacts show ${argsSource.argsArtifactId} --full${size}`)}`);
|
|
705
|
+
}
|
|
706
|
+
if (typeof result?.outputBytes === 'number') {
|
|
707
|
+
const modelVisible = typeof result.modelVisibleBytes === 'number'
|
|
708
|
+
? `, model-visible ${(0, format_1.formatBytes)(result.modelVisibleBytes)}`
|
|
709
|
+
: '';
|
|
710
|
+
console.log(` Output ${DIM(`${(0, format_1.formatBytes)(result.outputBytes)}${modelVisible}`)}`);
|
|
711
|
+
}
|
|
712
|
+
if (result?.artifactId) {
|
|
713
|
+
console.log(` Output full ${DIM(`/artifacts show ${result.artifactId} --full`)}`);
|
|
714
|
+
}
|
|
715
|
+
if (result?.error) {
|
|
716
|
+
console.log(` Error ${ERROR((0, session_storage_1.redactTraceText)(result.error))}`);
|
|
717
|
+
}
|
|
718
|
+
if (options.preview) {
|
|
719
|
+
const projectPath = session.projectPath || ctx.cwd;
|
|
720
|
+
printLastToolArtifactPreview(projectPath, inputLabel, argsSource?.argsArtifactId, options.full);
|
|
721
|
+
printLastToolArtifactPreview(projectPath, 'Output', result?.artifactId, options.full);
|
|
722
|
+
}
|
|
723
|
+
console.log();
|
|
724
|
+
console.log(DIM('Use /last-tool --full for redacted full previews, --no-preview for metadata only, or /trace latest for the ordered turn timeline.'));
|
|
725
|
+
return { success: true };
|
|
726
|
+
}
|
|
727
|
+
function formatDateTime(timestamp) {
|
|
728
|
+
return new Date(timestamp).toLocaleString();
|
|
729
|
+
}
|
|
730
|
+
function parseArtifactArgs(args) {
|
|
731
|
+
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
732
|
+
let full = false;
|
|
733
|
+
let limit = 20;
|
|
734
|
+
const positional = [];
|
|
735
|
+
for (let i = 0; i < parts.length; i++) {
|
|
736
|
+
const part = parts[i];
|
|
737
|
+
if (part === '--full') {
|
|
738
|
+
full = true;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if ((part === '--limit' || part === '-n') && parts[i + 1]) {
|
|
742
|
+
const parsed = Number(parts[i + 1]);
|
|
743
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
744
|
+
limit = Math.min(parsed, 200);
|
|
745
|
+
}
|
|
746
|
+
i++;
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
positional.push(part);
|
|
750
|
+
}
|
|
751
|
+
if (positional[0] === 'show' || positional[0] === 'cat') {
|
|
752
|
+
return { action: 'show', ref: positional[1], full, limit };
|
|
753
|
+
}
|
|
754
|
+
if (positional[0]) {
|
|
755
|
+
return { action: 'show', ref: positional[0], full, limit };
|
|
756
|
+
}
|
|
757
|
+
return { action: 'list', full, limit };
|
|
758
|
+
}
|
|
759
|
+
function printArtifactPreview(content, full) {
|
|
760
|
+
const maxPreviewBytes = 16 * 1024;
|
|
761
|
+
if (full || Buffer.byteLength(content, 'utf8') <= maxPreviewBytes) {
|
|
762
|
+
console.log(content);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
const preview = Buffer.from(content, 'utf8').subarray(0, maxPreviewBytes).toString('utf8');
|
|
766
|
+
console.log(preview);
|
|
767
|
+
console.log();
|
|
768
|
+
console.log(DIM(`... preview truncated at ${(0, format_1.formatBytes)(maxPreviewBytes)}. Use /artifacts show <id> --full for full output.`));
|
|
769
|
+
}
|
|
770
|
+
function formatArtifactPathForDisplay(artifactPath) {
|
|
771
|
+
return (0, session_storage_1.redactTraceText)(artifactPath);
|
|
772
|
+
}
|
|
773
|
+
function handleArtifacts(ctx, args = '') {
|
|
774
|
+
const parsed = parseArtifactArgs(args);
|
|
775
|
+
if (parsed.action === 'list') {
|
|
776
|
+
const artifacts = (0, tool_artifacts_1.listArtifacts)(ctx.cwd, parsed.limit);
|
|
777
|
+
console.log(HEADER('Artifacts'));
|
|
778
|
+
console.log(DIM('─'.repeat(40)));
|
|
779
|
+
if (artifacts.length === 0) {
|
|
780
|
+
console.log(DIM('No artifacts found for this project.'));
|
|
781
|
+
return { success: true };
|
|
782
|
+
}
|
|
783
|
+
for (const artifact of artifacts) {
|
|
784
|
+
console.log(`${ACCENT(artifact.id)} ${DIM(artifact.toolName)} ${(0, format_1.formatBytes)(artifact.outputBytes)}`);
|
|
785
|
+
console.log(` ${DIM(formatDateTime(artifact.modifiedAt))}`);
|
|
786
|
+
console.log(` ${DIM(formatArtifactPathForDisplay(artifact.path))}`);
|
|
787
|
+
}
|
|
788
|
+
console.log();
|
|
789
|
+
console.log(DIM('Use /artifacts show <id|prefix> to preview, or add --full for full output.'));
|
|
790
|
+
return { success: true };
|
|
791
|
+
}
|
|
792
|
+
if (!parsed.ref) {
|
|
793
|
+
console.log(ERROR('Usage: /artifacts show <id|prefix> [--full]'));
|
|
794
|
+
return { success: false };
|
|
795
|
+
}
|
|
796
|
+
const artifact = (0, tool_artifacts_1.findArtifact)(ctx.cwd, parsed.ref);
|
|
797
|
+
if (!artifact) {
|
|
798
|
+
console.log(ERROR(`Artifact not found or ambiguous: ${parsed.ref}`));
|
|
799
|
+
console.log(DIM('Run /artifacts to list available artifact ids.'));
|
|
800
|
+
return { success: false };
|
|
801
|
+
}
|
|
802
|
+
const content = (0, tool_artifacts_1.retrieveArtifact)(artifact.path);
|
|
803
|
+
if (content == null) {
|
|
804
|
+
console.log(ERROR(`Artifact exists but cannot be read: ${artifact.id}`));
|
|
805
|
+
console.log(DIM(formatArtifactPathForDisplay(artifact.path)));
|
|
806
|
+
return { success: false };
|
|
807
|
+
}
|
|
808
|
+
console.log(HEADER(`Artifact ${artifact.id}`));
|
|
809
|
+
console.log(DIM(`Tool ${artifact.toolName} Size ${(0, format_1.formatBytes)(artifact.outputBytes)} Modified ${formatDateTime(artifact.modifiedAt)}`));
|
|
810
|
+
console.log(DIM(formatArtifactPathForDisplay(artifact.path)));
|
|
811
|
+
console.log(DIM('─'.repeat(40)));
|
|
812
|
+
printArtifactPreview(content, parsed.full);
|
|
813
|
+
return { success: true };
|
|
814
|
+
}
|
|
815
|
+
function findCheckpointByRef(ctx, ref) {
|
|
816
|
+
const checkpoints = (0, checkpoint_1.listCheckpoints)(ctx.cwd);
|
|
817
|
+
const exact = checkpoints.find(checkpoint => checkpoint.turnId === ref);
|
|
818
|
+
if (exact)
|
|
819
|
+
return { checkpoint: exact, ambiguous: false };
|
|
820
|
+
const matches = checkpoints.filter(checkpoint => checkpoint.turnId.startsWith(ref));
|
|
821
|
+
return {
|
|
822
|
+
checkpoint: matches.length === 1 ? matches[0] : undefined,
|
|
823
|
+
ambiguous: matches.length > 1,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
function handleCheckpoint(ctx, args = '') {
|
|
827
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
828
|
+
const action = tokens[0] ?? 'list';
|
|
829
|
+
if (action === 'list') {
|
|
830
|
+
const checkpoints = (0, checkpoint_1.listCheckpoints)(ctx.cwd);
|
|
831
|
+
console.log(HEADER('Checkpoints'));
|
|
832
|
+
console.log(DIM('─'.repeat(40)));
|
|
833
|
+
if (checkpoints.length === 0) {
|
|
834
|
+
console.log(DIM('No checkpoints found for this project.'));
|
|
835
|
+
return { success: true };
|
|
836
|
+
}
|
|
837
|
+
for (const checkpoint of checkpoints.slice(0, 20)) {
|
|
838
|
+
console.log(`${ACCENT(checkpoint.turnId)} ${DIM(formatDateTime(checkpoint.createdAt))}`);
|
|
839
|
+
const files = checkpoint.files.map(file => file.path).slice(0, 8).join(', ');
|
|
840
|
+
console.log(` ${DIM(`${checkpoint.files.length} file(s)${files ? `: ${files}` : ''}`)}`);
|
|
841
|
+
}
|
|
842
|
+
console.log();
|
|
843
|
+
console.log(DIM('Use /checkpoint restore <turn-id|prefix> to preview, then add --yes to restore.'));
|
|
844
|
+
return { success: true };
|
|
845
|
+
}
|
|
846
|
+
if (action !== 'restore') {
|
|
847
|
+
console.log(ERROR('Usage: /checkpoint [list|restore <turn-id|prefix> [--yes]]'));
|
|
848
|
+
return { success: false };
|
|
849
|
+
}
|
|
850
|
+
const ref = tokens.find(token => token !== 'restore' && token !== '--yes');
|
|
851
|
+
const confirmed = tokens.includes('--yes');
|
|
852
|
+
if (!ref) {
|
|
853
|
+
console.log(ERROR('Usage: /checkpoint restore <turn-id|prefix> [--yes]'));
|
|
854
|
+
return { success: false };
|
|
855
|
+
}
|
|
856
|
+
const { checkpoint, ambiguous } = findCheckpointByRef(ctx, ref);
|
|
857
|
+
if (!checkpoint) {
|
|
858
|
+
console.log(ERROR(ambiguous
|
|
859
|
+
? `Checkpoint prefix is ambiguous: ${ref}`
|
|
860
|
+
: `Checkpoint not found: ${ref}`));
|
|
861
|
+
console.log(DIM('Run /checkpoint to list available checkpoint ids.'));
|
|
862
|
+
return { success: false };
|
|
863
|
+
}
|
|
864
|
+
if (!confirmed) {
|
|
865
|
+
console.log(HEADER(`Checkpoint ${checkpoint.turnId}`));
|
|
866
|
+
console.log(DIM(`${formatDateTime(checkpoint.createdAt)} ${checkpoint.files.length} file(s)`));
|
|
867
|
+
for (const file of checkpoint.files.slice(0, 20)) {
|
|
868
|
+
console.log(` ${file.path} ${DIM((0, format_1.formatBytes)(file.sizeBytes))}`);
|
|
869
|
+
}
|
|
870
|
+
console.log();
|
|
871
|
+
console.log(WARN(`This will overwrite current files from checkpoint ${checkpoint.turnId}.`));
|
|
872
|
+
console.log(DIM(`Run /checkpoint restore ${checkpoint.turnId} --yes to restore.`));
|
|
873
|
+
return { success: true };
|
|
874
|
+
}
|
|
875
|
+
const result = (0, checkpoint_1.restoreCheckpoint)(ctx.cwd, checkpoint.turnId);
|
|
876
|
+
if (result.error) {
|
|
877
|
+
console.log(ERROR(result.error));
|
|
878
|
+
return { success: false, error: result.error };
|
|
879
|
+
}
|
|
880
|
+
console.log(SUCCESS(`Restored ${result.restored.length} file(s) from checkpoint ${checkpoint.turnId}.`));
|
|
881
|
+
for (const file of result.restored.slice(0, 20)) {
|
|
882
|
+
console.log(` ${file}`);
|
|
883
|
+
}
|
|
884
|
+
return { success: true, output: result.restored.join('\n') };
|
|
885
|
+
}
|
|
235
886
|
function snapshotCurrentModel(ctx) {
|
|
236
887
|
return ctx.store.getSnapshot().currentModel || ctx.config.model;
|
|
237
888
|
}
|
|
@@ -367,6 +1018,47 @@ function showHarness(ctx, args = '') {
|
|
|
367
1018
|
console.log(DIM(' (no contract established)'));
|
|
368
1019
|
}
|
|
369
1020
|
console.log();
|
|
1021
|
+
// Context source
|
|
1022
|
+
console.log(HEADER(' Context Source'));
|
|
1023
|
+
const session = ctx.getSession?.() ?? (ctx.sessionId ? (0, session_storage_1.loadSessionMeta)(ctx.sessionId) : null);
|
|
1024
|
+
const isRestored = session?.transcriptDisplayStartTime != null;
|
|
1025
|
+
const isCompactActive = Boolean(state.promptAssemblyStats);
|
|
1026
|
+
console.log(` Root ${ACCENT(state.rootObjective || contract?.objective || '(none)')}`);
|
|
1027
|
+
console.log(` Active ${DIM(state.activeInstruction || contract?.userIntent || '(none)')}`);
|
|
1028
|
+
console.log(` Source ${isRestored ? WARN('restored session') : DIM('live turn')}`);
|
|
1029
|
+
if (isRestored && session) {
|
|
1030
|
+
const restoredTime = session.transcriptDisplayStartTime
|
|
1031
|
+
? new Date(session.transcriptDisplayStartTime).toLocaleString()
|
|
1032
|
+
: 'unknown';
|
|
1033
|
+
console.log(` Restored ${DIM(restoredTime)}`);
|
|
1034
|
+
}
|
|
1035
|
+
if (isCompactActive) {
|
|
1036
|
+
console.log(` Compact ${SUCCESS('active')}`);
|
|
1037
|
+
}
|
|
1038
|
+
console.log();
|
|
1039
|
+
// Evidence index summary
|
|
1040
|
+
console.log(HEADER(' Evidence Index'));
|
|
1041
|
+
const evidenceItems = state.evidenceIndex?.length ?? 0;
|
|
1042
|
+
const evidenceKinds = new Map();
|
|
1043
|
+
if (state.evidenceIndex) {
|
|
1044
|
+
for (const item of state.evidenceIndex) {
|
|
1045
|
+
const kind = item.kind || 'unknown';
|
|
1046
|
+
evidenceKinds.set(kind, (evidenceKinds.get(kind) || 0) + 1);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
console.log(` Total ${ACCENT(String(evidenceItems))} items`);
|
|
1050
|
+
if (evidenceKinds.size > 0) {
|
|
1051
|
+
const kinds = Array.from(evidenceKinds.entries())
|
|
1052
|
+
.sort((a, b) => b[1] - a[1])
|
|
1053
|
+
.slice(0, 10);
|
|
1054
|
+
for (const [kind, count] of kinds) {
|
|
1055
|
+
console.log(` ${DIM(kind.padEnd(16))} ${count}`);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
console.log(DIM(' (no evidence records yet)'));
|
|
1060
|
+
}
|
|
1061
|
+
console.log();
|
|
370
1062
|
// Intent history
|
|
371
1063
|
console.log(HEADER(' Recent Intents'));
|
|
372
1064
|
const intents = state.intentHistory?.slice(-5) ?? [];
|
|
@@ -498,51 +1190,13 @@ function showConfig(ctx) {
|
|
|
498
1190
|
return { success: true };
|
|
499
1191
|
}
|
|
500
1192
|
function handleModel(ctx, args) {
|
|
501
|
-
// 模型别名映射
|
|
502
|
-
const MODEL_ALIASES = {
|
|
503
|
-
'opus': 'claude-opus-4-7',
|
|
504
|
-
'sonnet': 'claude-sonnet-4-6',
|
|
505
|
-
'haiku': 'claude-haiku-4-5-20251001',
|
|
506
|
-
'claude': 'claude-sonnet-4-6',
|
|
507
|
-
'gpt4': 'gpt-4o',
|
|
508
|
-
'gpt4o': 'gpt-4o',
|
|
509
|
-
'gpt35': 'gpt-3.5-turbo',
|
|
510
|
-
// Bailian (coding.dashscope.aliyuncs.com) — OpenAI-compatible
|
|
511
|
-
'qwen': 'qwen3.5-plus',
|
|
512
|
-
'qwenplus': 'qwen3.5-plus',
|
|
513
|
-
'qwen36': 'qwen3.6-plus',
|
|
514
|
-
'qwenmax': 'qwen3-max-2026-01-23',
|
|
515
|
-
'coder': 'qwen3-coder-plus',
|
|
516
|
-
'codernext': 'qwen3-coder-next',
|
|
517
|
-
'glm': 'glm-5',
|
|
518
|
-
'glm47': 'glm-4.7',
|
|
519
|
-
'kimi': 'kimi-k2.5',
|
|
520
|
-
'minimax': 'MiniMax-M2.5',
|
|
521
|
-
};
|
|
522
|
-
// 可用模型列表
|
|
523
|
-
const AVAILABLE_MODELS = [
|
|
524
|
-
{ name: 'claude-opus-4-7', alias: 'opus', provider: 'Anthropic' },
|
|
525
|
-
{ name: 'claude-sonnet-4-6', alias: 'sonnet', provider: 'Anthropic' },
|
|
526
|
-
{ name: 'claude-haiku-4-5-20251001', alias: 'haiku', provider: 'Anthropic' },
|
|
527
|
-
{ name: 'gpt-4o', alias: 'gpt4o', provider: 'OpenAI' },
|
|
528
|
-
{ name: 'gpt-3.5-turbo', alias: 'gpt35', provider: 'OpenAI' },
|
|
529
|
-
{ name: 'glm-5', alias: 'glm', provider: 'Bailian (Zhipu)' },
|
|
530
|
-
{ name: 'glm-4.7', alias: 'glm47', provider: 'Bailian (Zhipu)' },
|
|
531
|
-
{ name: 'qwen3.5-plus', alias: 'qwen', provider: 'Bailian (Alibaba)' },
|
|
532
|
-
{ name: 'qwen3.6-plus', alias: 'qwen36', provider: 'Bailian (Alibaba)' },
|
|
533
|
-
{ name: 'qwen3-max-2026-01-23', alias: 'qwenmax', provider: 'Bailian (Alibaba)' },
|
|
534
|
-
{ name: 'qwen3-coder-plus', alias: 'coder', provider: 'Bailian (Alibaba)' },
|
|
535
|
-
{ name: 'qwen3-coder-next', alias: 'codernext', provider: 'Bailian (Alibaba)' },
|
|
536
|
-
{ name: 'kimi-k2.5', alias: 'kimi', provider: 'Bailian (Moonshot)' },
|
|
537
|
-
{ name: 'MiniMax-M2.5', alias: 'minimax', provider: 'Bailian (MiniMax)' },
|
|
538
|
-
];
|
|
539
1193
|
const trimmedArgs = args.trim().toLowerCase();
|
|
540
1194
|
// 显示当前模型
|
|
541
1195
|
if (!args || trimmedArgs === '?' || trimmedArgs === 'info') {
|
|
542
1196
|
console.log();
|
|
543
1197
|
if (ctx.llm) {
|
|
544
1198
|
const currentModel = ctx.llm.getModel();
|
|
545
|
-
const aliasEntry =
|
|
1199
|
+
const aliasEntry = (0, model_catalog_1.getModelCatalogEntry)(currentModel);
|
|
546
1200
|
const contextInfo = (0, model_context_1.resolveModelContext)(currentModel);
|
|
547
1201
|
const compactStats = (0, auto_compact_1.getAutoCompact)({ modelId: currentModel }).getStats();
|
|
548
1202
|
console.log(HEADER('Current Model'));
|
|
@@ -571,12 +1225,25 @@ function handleModel(ctx, args) {
|
|
|
571
1225
|
console.log(HEADER('Available Models'));
|
|
572
1226
|
console.log(DIM('─'.repeat(40)));
|
|
573
1227
|
const currentModel = ctx.llm?.getModel() || '';
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
1228
|
+
const modelPicker = (0, ui_view_model_1.createModelPickerState)({
|
|
1229
|
+
currentModel,
|
|
1230
|
+
models: (0, model_catalog_1.listModelCatalogEntries)().map(model => {
|
|
1231
|
+
const contextInfo = (0, model_context_1.resolveModelContext)(model.name);
|
|
1232
|
+
return {
|
|
1233
|
+
...model,
|
|
1234
|
+
contextWindow: contextInfo.contextWindow,
|
|
1235
|
+
maxOutputTokens: contextInfo.maxOutputTokens,
|
|
1236
|
+
source: contextInfo.source,
|
|
1237
|
+
};
|
|
1238
|
+
}),
|
|
1239
|
+
});
|
|
1240
|
+
for (const item of modelPicker.visibleItems) {
|
|
1241
|
+
const marker = item.isCurrent ? SUCCESS('●') : DIM('○');
|
|
1242
|
+
const alias = item.alias ? `(${item.alias})` : '';
|
|
1243
|
+
const context = `${formatTokenCount(item.contextWindow ?? 0)} ctx`;
|
|
1244
|
+
const current = item.isCurrent ? BRAND('(current)') : '';
|
|
1245
|
+
console.log(` ${marker} ${ACCENT(item.name)} ${DIM(alias)} ${DIM(context)} ${current}`);
|
|
1246
|
+
console.log(` ${DIM(item.provider ?? 'unknown')}`);
|
|
580
1247
|
}
|
|
581
1248
|
console.log();
|
|
582
1249
|
console.log(DIM('Use /model <name|alias> to switch, e.g. /model sonnet'));
|
|
@@ -594,7 +1261,7 @@ function handleModel(ctx, args) {
|
|
|
594
1261
|
console.log(` ${ACCENT('/model <name>')} Switch to specific model`);
|
|
595
1262
|
console.log(` ${ACCENT('/model <alias>')} Switch using alias (opus, sonnet, haiku)`);
|
|
596
1263
|
console.log();
|
|
597
|
-
console.log(DIM(
|
|
1264
|
+
console.log(DIM(`Aliases: ${formatModelAliasHelp()}`));
|
|
598
1265
|
console.log();
|
|
599
1266
|
return { success: true };
|
|
600
1267
|
}
|
|
@@ -605,7 +1272,7 @@ function handleModel(ctx, args) {
|
|
|
605
1272
|
return { success: false };
|
|
606
1273
|
}
|
|
607
1274
|
// 解析别名
|
|
608
|
-
const resolvedModel =
|
|
1275
|
+
const resolvedModel = (0, model_catalog_1.resolveModelAlias)(args);
|
|
609
1276
|
ctx.llm.setModel(resolvedModel);
|
|
610
1277
|
(0, auto_compact_1.getAutoCompact)({ modelId: resolvedModel });
|
|
611
1278
|
ctx.store.setState({ currentModel: resolvedModel });
|
|
@@ -917,6 +1584,7 @@ async function handleChat(ctx, input) {
|
|
|
917
1584
|
abortSignal: ctx.abortSignal,
|
|
918
1585
|
harness,
|
|
919
1586
|
input,
|
|
1587
|
+
loopBudget: (0, loop_budget_1.resolveRuntimeLoopBudget)(input, ctx.config, harness.toJSON()),
|
|
920
1588
|
})) {
|
|
921
1589
|
switch (event.type) {
|
|
922
1590
|
case 'request_start':
|
|
@@ -1383,6 +2051,7 @@ async function handleCompact(ctx, args) {
|
|
|
1383
2051
|
console.log();
|
|
1384
2052
|
return { success: true };
|
|
1385
2053
|
}
|
|
2054
|
+
console.log(DIM((0, agent_status_1.compactStatus)()));
|
|
1386
2055
|
try {
|
|
1387
2056
|
const autoCompact = (0, auto_compact_1.getAutoCompact)();
|
|
1388
2057
|
autoCompact.configure({
|
|
@@ -1398,7 +2067,7 @@ async function handleCompact(ctx, args) {
|
|
|
1398
2067
|
const percent = Math.round((reduction / history.length) * 100);
|
|
1399
2068
|
const sessionId = ctx.getSession?.()?.id ?? ctx.sessionId;
|
|
1400
2069
|
if (sessionId) {
|
|
1401
|
-
(0, session_storage_1.
|
|
2070
|
+
(0, session_storage_1.persistSessionCompactHistory)(sessionId, compacted);
|
|
1402
2071
|
}
|
|
1403
2072
|
console.log(SUCCESS(`✔ Compacted ${history.length} → ${compacted.length} messages`));
|
|
1404
2073
|
console.log(DIM(` Reduced by ${reduction} messages (${percent}%)`));
|
|
@@ -1676,16 +2345,32 @@ function restoreSession(ctx, session, isLast) {
|
|
|
1676
2345
|
const history = (0, session_storage_1.loadSessionHistory)(resumed.id);
|
|
1677
2346
|
if (history.length > 0) {
|
|
1678
2347
|
const summary = generateHistorySummary(history);
|
|
2348
|
+
const eventSummary = generateRestoredSessionEventSummary(history);
|
|
1679
2349
|
console.log(DIM(` Summary: ${summary}`));
|
|
1680
2350
|
console.log();
|
|
1681
2351
|
ctx.store.setState({ conversationHistory: history });
|
|
1682
2352
|
ctx.store.setState({ harnessState: (0, session_storage_1.loadSessionHarnessState)(resumed.id) ?? resumed.harnessState });
|
|
1683
2353
|
(0, framework_1.resetToolState)();
|
|
2354
|
+
ctx.sessionRestored?.({
|
|
2355
|
+
sessionId: resumed.id,
|
|
2356
|
+
projectPath: resumed.projectPath,
|
|
2357
|
+
model: resumed.model,
|
|
2358
|
+
restoredMessages: history.length,
|
|
2359
|
+
messageCount: resumed.messageCount,
|
|
2360
|
+
summary: eventSummary,
|
|
2361
|
+
});
|
|
1684
2362
|
console.log(SUCCESS(`✔ Restored ${history.length} messages`));
|
|
1685
2363
|
}
|
|
1686
2364
|
else {
|
|
1687
2365
|
console.log();
|
|
1688
2366
|
console.log(DIM(' No messages in session'));
|
|
2367
|
+
ctx.sessionRestored?.({
|
|
2368
|
+
sessionId: resumed.id,
|
|
2369
|
+
projectPath: resumed.projectPath,
|
|
2370
|
+
model: resumed.model,
|
|
2371
|
+
restoredMessages: 0,
|
|
2372
|
+
messageCount: resumed.messageCount,
|
|
2373
|
+
});
|
|
1689
2374
|
}
|
|
1690
2375
|
console.log();
|
|
1691
2376
|
return { success: true };
|
|
@@ -1809,13 +2494,26 @@ async function handleEditPreview(ctx) {
|
|
|
1809
2494
|
return { success: parsed.success === true };
|
|
1810
2495
|
}
|
|
1811
2496
|
/** Generate a brief summary of conversation history */
|
|
2497
|
+
function truncateRedactedSummary(text, maxLength) {
|
|
2498
|
+
const redacted = (0, session_storage_1.redactTraceText)(text);
|
|
2499
|
+
if (redacted.length <= maxLength)
|
|
2500
|
+
return redacted;
|
|
2501
|
+
let truncated = redacted.slice(0, maxLength);
|
|
2502
|
+
for (const marker of ['[REDACTED_SECRET]']) {
|
|
2503
|
+
const markerStart = truncated.indexOf(marker.slice(0, 6));
|
|
2504
|
+
if (markerStart >= 0 && !truncated.includes(marker)) {
|
|
2505
|
+
truncated = `${truncated.slice(0, markerStart)}${marker}`;
|
|
2506
|
+
break;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
return `${truncated}...`;
|
|
2510
|
+
}
|
|
1812
2511
|
function generateHistorySummary(messages) {
|
|
1813
2512
|
const userMsgs = messages.filter(m => m.role === 'user' && m.content);
|
|
1814
2513
|
const assistantMsgsWithTools = messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0);
|
|
1815
2514
|
// Extract topics from first few user messages
|
|
1816
2515
|
const topics = userMsgs.slice(0, 3).map(m => {
|
|
1817
|
-
|
|
1818
|
-
return content.length > 40 ? content.slice(0, 40) + '...' : content;
|
|
2516
|
+
return truncateRedactedSummary(m.content || '', 40);
|
|
1819
2517
|
});
|
|
1820
2518
|
// Extract tools used
|
|
1821
2519
|
const toolsUsed = assistantMsgsWithTools.flatMap(m => m.tool_calls?.map(tc => tc.function.name) || []);
|
|
@@ -1833,6 +2531,14 @@ function generateHistorySummary(messages) {
|
|
|
1833
2531
|
}
|
|
1834
2532
|
return parts.join('. ');
|
|
1835
2533
|
}
|
|
2534
|
+
function generateRestoredSessionEventSummary(messages) {
|
|
2535
|
+
const assistantMsgsWithTools = messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0);
|
|
2536
|
+
const toolsUsed = assistantMsgsWithTools.flatMap(m => m.tool_calls?.map(tc => tc.function.name) || []);
|
|
2537
|
+
const uniqueTools = [...new Set(toolsUsed)].slice(0, 8);
|
|
2538
|
+
if (uniqueTools.length === 0)
|
|
2539
|
+
return undefined;
|
|
2540
|
+
return `Tools: ${uniqueTools.join(', ')}`;
|
|
2541
|
+
}
|
|
1836
2542
|
function continueAsSlashChat(name, args) {
|
|
1837
2543
|
const trimmed = args.trim();
|
|
1838
2544
|
return {
|
|
@@ -2115,6 +2821,53 @@ const COMMANDS = [
|
|
|
2115
2821
|
type: 'builtin',
|
|
2116
2822
|
execute: (ctx) => handleUsage(ctx),
|
|
2117
2823
|
},
|
|
2824
|
+
{
|
|
2825
|
+
name: 'loop-stats',
|
|
2826
|
+
aliases: ['loop'],
|
|
2827
|
+
description: 'Show detailed agent-loop budget and efficiency diagnostics',
|
|
2828
|
+
category: 'diagnostics',
|
|
2829
|
+
priority: 12,
|
|
2830
|
+
type: 'builtin',
|
|
2831
|
+
execute: (ctx) => handleLoopStats(ctx),
|
|
2832
|
+
},
|
|
2833
|
+
{
|
|
2834
|
+
name: 'trace',
|
|
2835
|
+
description: 'Show structured event timeline for the latest or selected turn',
|
|
2836
|
+
argumentHint: '[latest|turn-id]',
|
|
2837
|
+
category: 'diagnostics',
|
|
2838
|
+
priority: 14,
|
|
2839
|
+
type: 'builtin',
|
|
2840
|
+
execute: (ctx, args) => handleTrace(ctx, args),
|
|
2841
|
+
},
|
|
2842
|
+
{
|
|
2843
|
+
name: 'last-tool',
|
|
2844
|
+
aliases: ['tool-last'],
|
|
2845
|
+
description: 'Show the latest tool call/result with full inspection hints',
|
|
2846
|
+
category: 'diagnostics',
|
|
2847
|
+
priority: 15,
|
|
2848
|
+
type: 'builtin',
|
|
2849
|
+
execute: (ctx, args) => handleLastTool(ctx, args),
|
|
2850
|
+
},
|
|
2851
|
+
{
|
|
2852
|
+
name: 'artifacts',
|
|
2853
|
+
aliases: ['artifact'],
|
|
2854
|
+
description: 'List or inspect saved full tool outputs for this project',
|
|
2855
|
+
argumentHint: '[show <id|prefix> --full]',
|
|
2856
|
+
category: 'diagnostics',
|
|
2857
|
+
priority: 16,
|
|
2858
|
+
type: 'builtin',
|
|
2859
|
+
execute: (ctx, args) => handleArtifacts(ctx, args),
|
|
2860
|
+
},
|
|
2861
|
+
{
|
|
2862
|
+
name: 'checkpoint',
|
|
2863
|
+
aliases: ['checkpoints'],
|
|
2864
|
+
description: 'List or restore file checkpoints created before agent edits',
|
|
2865
|
+
argumentHint: '[list|restore <turn-id|prefix> --yes]',
|
|
2866
|
+
category: 'diagnostics',
|
|
2867
|
+
priority: 18,
|
|
2868
|
+
type: 'builtin',
|
|
2869
|
+
execute: (ctx, args) => handleCheckpoint(ctx, args),
|
|
2870
|
+
},
|
|
2118
2871
|
{
|
|
2119
2872
|
name: 'cost',
|
|
2120
2873
|
description: 'Show session token usage',
|