openhorse 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +681 -6
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/types.d.ts +3 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/core/checkpoint.d.ts +1 -0
- package/dist/core/checkpoint.d.ts.map +1 -1
- package/dist/core/checkpoint.js +59 -10
- package/dist/core/checkpoint.js.map +1 -1
- package/dist/core/tool-artifacts.d.ts +10 -0
- package/dist/core/tool-artifacts.d.ts.map +1 -1
- package/dist/core/tool-artifacts.js +81 -3
- package/dist/core/tool-artifacts.js.map +1 -1
- package/dist/framework/index.d.ts +2 -2
- package/dist/framework/index.d.ts.map +1 -1
- package/dist/framework/index.js +5 -1
- package/dist/framework/index.js.map +1 -1
- package/dist/framework/query.d.ts +104 -1
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +454 -18
- package/dist/framework/query.js.map +1 -1
- package/dist/framework/store.d.ts +5 -0
- package/dist/framework/store.d.ts.map +1 -1
- package/dist/framework/store.js +4 -0
- package/dist/framework/store.js.map +1 -1
- package/dist/framework/tool-scheduler.d.ts +9 -0
- package/dist/framework/tool-scheduler.d.ts.map +1 -1
- package/dist/framework/tool-scheduler.js +34 -0
- package/dist/framework/tool-scheduler.js.map +1 -1
- package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
- package/dist/ink-ui/components/StatusLine.js +7 -1
- package/dist/ink-ui/components/StatusLine.js.map +1 -1
- package/dist/runtime/agent-runtime-protocol.d.ts +13 -1
- package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-protocol.js +24 -0
- package/dist/runtime/agent-runtime-protocol.js.map +1 -1
- package/dist/runtime/chat-controller.d.ts +3 -0
- package/dist/runtime/chat-controller.d.ts.map +1 -1
- package/dist/runtime/chat-controller.js +930 -5
- package/dist/runtime/chat-controller.js.map +1 -1
- package/dist/runtime/loop-budget.d.ts +5 -0
- package/dist/runtime/loop-budget.d.ts.map +1 -0
- package/dist/runtime/loop-budget.js +77 -0
- package/dist/runtime/loop-budget.js.map +1 -0
- package/dist/runtime/ui-events.d.ts +39 -1
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/services/config-dir.d.ts +2 -0
- package/dist/services/config-dir.d.ts.map +1 -1
- package/dist/services/config-dir.js +5 -0
- package/dist/services/config-dir.js.map +1 -1
- package/dist/services/config.d.ts +4 -2
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +19 -0
- package/dist/services/config.js.map +1 -1
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +34 -4
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/global-config.d.ts +17 -0
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/global-config.js.map +1 -1
- package/dist/services/llm.d.ts +18 -0
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +175 -163
- package/dist/services/llm.js.map +1 -1
- package/dist/services/provider-diagnostics.d.ts +30 -0
- package/dist/services/provider-diagnostics.d.ts.map +1 -0
- package/dist/services/provider-diagnostics.js +308 -0
- package/dist/services/provider-diagnostics.js.map +1 -0
- package/dist/services/redaction.d.ts +2 -0
- package/dist/services/redaction.d.ts.map +1 -0
- package/dist/services/redaction.js +27 -0
- package/dist/services/redaction.js.map +1 -0
- package/dist/services/session-index.d.ts.map +1 -1
- package/dist/services/session-index.js +7 -3
- package/dist/services/session-index.js.map +1 -1
- package/dist/services/session-storage.d.ts +100 -0
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +154 -3
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/verification-profile.d.ts +36 -0
- package/dist/services/verification-profile.d.ts.map +1 -0
- package/dist/services/verification-profile.js +239 -0
- package/dist/services/verification-profile.js.map +1 -0
- package/dist/services/workspace-state.d.ts +25 -0
- package/dist/services/workspace-state.d.ts.map +1 -0
- package/dist/services/workspace-state.js +115 -0
- package/dist/services/workspace-state.js.map +1 -0
- package/dist/skills/loader.d.ts +5 -1
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +34 -0
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/registry.d.ts +5 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +23 -0
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/runtime.d.ts +6 -0
- package/dist/skills/runtime.d.ts.map +1 -1
- package/dist/skills/runtime.js +88 -4
- package/dist/skills/runtime.js.map +1 -1
- package/dist/skills/types.d.ts +19 -0
- package/dist/skills/types.d.ts.map +1 -1
- package/dist/skills/types.js.map +1 -1
- package/package.json +1 -1
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',
|
|
@@ -199,6 +308,14 @@ function showStatus(ctx) {
|
|
|
199
308
|
console.log(` Prompt rules ${snapshot.projectInstructionsContent ? SUCCESS(`${snapshot.projectInstructionsContent.length} chars`) : DIM('none')}`);
|
|
200
309
|
console.log(` Project memory ${snapshot.memoryContent ? SUCCESS(`${snapshot.memoryContent.length} chars`) : DIM('none')}`);
|
|
201
310
|
console.log(` Skills index ${snapshot.skillsContent ? SUCCESS(`${snapshot.skillsContent.length} chars`) : DIM('none')}`);
|
|
311
|
+
if (snapshot.lastLoopStats) {
|
|
312
|
+
const stats = snapshot.lastLoopStats;
|
|
313
|
+
console.log();
|
|
314
|
+
console.log(` Last loop:`);
|
|
315
|
+
for (const line of formatLoopStatsLines(stats)) {
|
|
316
|
+
console.log(` ${line}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
202
319
|
const harnessState = snapshot.harnessState;
|
|
203
320
|
if (harnessState?.contract || harnessState?.capsule) {
|
|
204
321
|
console.log();
|
|
@@ -217,6 +334,413 @@ function showStatus(ctx) {
|
|
|
217
334
|
console.log();
|
|
218
335
|
return { success: true };
|
|
219
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
|
+
}
|
|
220
744
|
function snapshotCurrentModel(ctx) {
|
|
221
745
|
return ctx.store.getSnapshot().currentModel || ctx.config.model;
|
|
222
746
|
}
|
|
@@ -902,6 +1426,7 @@ async function handleChat(ctx, input) {
|
|
|
902
1426
|
abortSignal: ctx.abortSignal,
|
|
903
1427
|
harness,
|
|
904
1428
|
input,
|
|
1429
|
+
loopBudget: (0, loop_budget_1.resolveRuntimeLoopBudget)(input, ctx.config, harness.toJSON()),
|
|
905
1430
|
})) {
|
|
906
1431
|
switch (event.type) {
|
|
907
1432
|
case 'request_start':
|
|
@@ -952,6 +1477,7 @@ async function handleChat(ctx, input) {
|
|
|
952
1477
|
sessionMessagesToRecord.push({
|
|
953
1478
|
role: 'tool',
|
|
954
1479
|
content: event.result,
|
|
1480
|
+
modelVisibleContent: event.modelVisibleResult,
|
|
955
1481
|
timestamp: Date.now(),
|
|
956
1482
|
toolCallId: event.callId,
|
|
957
1483
|
});
|
|
@@ -972,6 +1498,9 @@ async function handleChat(ctx, input) {
|
|
|
972
1498
|
finalContent = event.content;
|
|
973
1499
|
finalModel = event.model;
|
|
974
1500
|
finalUsage = event.usage;
|
|
1501
|
+
if (event.stats) {
|
|
1502
|
+
ctx.store.setLastLoopStats(event.stats);
|
|
1503
|
+
}
|
|
975
1504
|
break;
|
|
976
1505
|
}
|
|
977
1506
|
}
|
|
@@ -1102,12 +1631,11 @@ function handleCost(ctx) {
|
|
|
1102
1631
|
return { success: true };
|
|
1103
1632
|
}
|
|
1104
1633
|
function handleSkills(ctx) {
|
|
1105
|
-
const { getSkillsRegistry } = require('../skills');
|
|
1106
1634
|
console.log();
|
|
1107
1635
|
console.log(HEADER('Loaded Skills'));
|
|
1108
1636
|
console.log(DIM('─'.repeat(40)));
|
|
1109
1637
|
try {
|
|
1110
|
-
const registry = getSkillsRegistry();
|
|
1638
|
+
const registry = (0, skills_1.getSkillsRegistry)();
|
|
1111
1639
|
const summary = registry.getSummary();
|
|
1112
1640
|
if (summary.count === 0) {
|
|
1113
1641
|
console.log();
|
|
@@ -1120,10 +1648,18 @@ function handleSkills(ctx) {
|
|
|
1120
1648
|
console.log(` Total ${SUCCESS(summary.count)} skills (${WARN(summary.autoCount)} auto-trigger)`);
|
|
1121
1649
|
console.log();
|
|
1122
1650
|
for (const skill of registry.getAllSkills()) {
|
|
1123
|
-
const source = registry
|
|
1651
|
+
const source = registry.getSource(skill.name);
|
|
1124
1652
|
const sourceType = source?.type || 'unknown';
|
|
1653
|
+
const resourceRoot = skill.resourceRoot || skill.source || source?.path;
|
|
1654
|
+
const skillFile = resourceRoot
|
|
1655
|
+
? `${resourceRoot.replace(/\/SKILL\.md$/i, '')}/SKILL.md`
|
|
1656
|
+
: source?.skillFile;
|
|
1125
1657
|
console.log(` ${ACCENT(skill.name)} ${DIM(`(${sourceType})`)}`);
|
|
1126
1658
|
console.log(` ${DIM(skill.description || '(no description)')}`);
|
|
1659
|
+
if (skillFile)
|
|
1660
|
+
console.log(` ${DIM(`SKILL.md ${skillFile}`)}`);
|
|
1661
|
+
if (resourceRoot)
|
|
1662
|
+
console.log(` ${DIM(`Root ${resourceRoot}`)}`);
|
|
1127
1663
|
}
|
|
1128
1664
|
console.log();
|
|
1129
1665
|
}
|
|
@@ -1133,6 +1669,60 @@ function handleSkills(ctx) {
|
|
|
1133
1669
|
}
|
|
1134
1670
|
return { success: true };
|
|
1135
1671
|
}
|
|
1672
|
+
function handleSkill(args) {
|
|
1673
|
+
const trimmed = args.trim();
|
|
1674
|
+
const registry = (0, skills_1.getSkillsRegistry)();
|
|
1675
|
+
registry.initialize();
|
|
1676
|
+
if (!trimmed) {
|
|
1677
|
+
const names = registry.getAllSkills().map(skill => skill.name).sort();
|
|
1678
|
+
return {
|
|
1679
|
+
success: false,
|
|
1680
|
+
error: [
|
|
1681
|
+
'Usage: /skill <name> <task>',
|
|
1682
|
+
`Loaded skills: ${names.join(', ') || 'none'}`,
|
|
1683
|
+
].join('\n'),
|
|
1684
|
+
};
|
|
1685
|
+
}
|
|
1686
|
+
const [rawName, ...rest] = trimmed.split(/\s+/);
|
|
1687
|
+
const requestedName = (0, skills_1.normalizeRequestedSkillName)(rawName);
|
|
1688
|
+
const skill = registry.getAllSkills()
|
|
1689
|
+
.find(candidate => (0, skills_1.skillActivationNames)(candidate)
|
|
1690
|
+
.some(name => (0, skills_1.normalizeRequestedSkillName)(name) === requestedName));
|
|
1691
|
+
if (!skill) {
|
|
1692
|
+
const suggestions = registry.getAllSkills()
|
|
1693
|
+
.map(candidate => candidate.name)
|
|
1694
|
+
.filter(name => name.includes(requestedName) || requestedName.includes(name))
|
|
1695
|
+
.slice(0, 5);
|
|
1696
|
+
return {
|
|
1697
|
+
success: false,
|
|
1698
|
+
error: suggestions.length > 0
|
|
1699
|
+
? `Unknown skill: ${rawName}\nDid you mean: ${suggestions.join(', ')}?`
|
|
1700
|
+
: `Unknown skill: ${rawName}`,
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
const task = rest.join(' ').trim();
|
|
1704
|
+
const source = registry.getSource(skill.name);
|
|
1705
|
+
const resourceRoot = skill.resourceRoot || skill.source || source?.path;
|
|
1706
|
+
const skillFile = resourceRoot
|
|
1707
|
+
? `${resourceRoot.replace(/\/SKILL\.md$/i, '')}/SKILL.md`
|
|
1708
|
+
: source?.skillFile;
|
|
1709
|
+
if (!task) {
|
|
1710
|
+
return {
|
|
1711
|
+
success: true,
|
|
1712
|
+
output: [
|
|
1713
|
+
`Skill ${skill.name} is loaded.`,
|
|
1714
|
+
skillFile ? `SKILL.md ${skillFile}` : '',
|
|
1715
|
+
resourceRoot ? `Root ${resourceRoot}` : '',
|
|
1716
|
+
`Use: /skill ${skill.name} <task>`,
|
|
1717
|
+
].filter(Boolean).join('\n'),
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
return {
|
|
1721
|
+
success: true,
|
|
1722
|
+
continueAsChat: true,
|
|
1723
|
+
chatInput: `/skill ${skill.name} ${task}`,
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1136
1726
|
function handleMcp(ctx) {
|
|
1137
1727
|
console.log();
|
|
1138
1728
|
console.log(HEADER('MCP Servers'));
|
|
@@ -1318,7 +1908,7 @@ async function handleCompact(ctx, args) {
|
|
|
1318
1908
|
const percent = Math.round((reduction / history.length) * 100);
|
|
1319
1909
|
const sessionId = ctx.getSession?.()?.id ?? ctx.sessionId;
|
|
1320
1910
|
if (sessionId) {
|
|
1321
|
-
(0, session_storage_1.
|
|
1911
|
+
(0, session_storage_1.persistSessionCompactHistory)(sessionId, compacted);
|
|
1322
1912
|
}
|
|
1323
1913
|
console.log(SUCCESS(`✔ Compacted ${history.length} → ${compacted.length} messages`));
|
|
1324
1914
|
console.log(DIM(` Reduced by ${reduction} messages (${percent}%)`));
|
|
@@ -1596,16 +2186,32 @@ function restoreSession(ctx, session, isLast) {
|
|
|
1596
2186
|
const history = (0, session_storage_1.loadSessionHistory)(resumed.id);
|
|
1597
2187
|
if (history.length > 0) {
|
|
1598
2188
|
const summary = generateHistorySummary(history);
|
|
2189
|
+
const eventSummary = generateRestoredSessionEventSummary(history);
|
|
1599
2190
|
console.log(DIM(` Summary: ${summary}`));
|
|
1600
2191
|
console.log();
|
|
1601
2192
|
ctx.store.setState({ conversationHistory: history });
|
|
1602
2193
|
ctx.store.setState({ harnessState: (0, session_storage_1.loadSessionHarnessState)(resumed.id) ?? resumed.harnessState });
|
|
1603
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
|
+
});
|
|
1604
2203
|
console.log(SUCCESS(`✔ Restored ${history.length} messages`));
|
|
1605
2204
|
}
|
|
1606
2205
|
else {
|
|
1607
2206
|
console.log();
|
|
1608
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
|
+
});
|
|
1609
2215
|
}
|
|
1610
2216
|
console.log();
|
|
1611
2217
|
return { success: true };
|
|
@@ -1729,13 +2335,26 @@ async function handleEditPreview(ctx) {
|
|
|
1729
2335
|
return { success: parsed.success === true };
|
|
1730
2336
|
}
|
|
1731
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
|
+
}
|
|
1732
2352
|
function generateHistorySummary(messages) {
|
|
1733
2353
|
const userMsgs = messages.filter(m => m.role === 'user' && m.content);
|
|
1734
2354
|
const assistantMsgsWithTools = messages.filter(m => m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0);
|
|
1735
2355
|
// Extract topics from first few user messages
|
|
1736
2356
|
const topics = userMsgs.slice(0, 3).map(m => {
|
|
1737
|
-
|
|
1738
|
-
return content.length > 40 ? content.slice(0, 40) + '...' : content;
|
|
2357
|
+
return truncateRedactedSummary(m.content || '', 40);
|
|
1739
2358
|
});
|
|
1740
2359
|
// Extract tools used
|
|
1741
2360
|
const toolsUsed = assistantMsgsWithTools.flatMap(m => m.tool_calls?.map(tc => tc.function.name) || []);
|
|
@@ -1753,6 +2372,14 @@ function generateHistorySummary(messages) {
|
|
|
1753
2372
|
}
|
|
1754
2373
|
return parts.join('. ');
|
|
1755
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
|
+
}
|
|
1756
2383
|
function continueAsSlashChat(name, args) {
|
|
1757
2384
|
const trimmed = args.trim();
|
|
1758
2385
|
return {
|
|
@@ -1887,6 +2514,16 @@ const COMMANDS = [
|
|
|
1887
2514
|
type: 'builtin',
|
|
1888
2515
|
execute: (ctx) => handleSkills(ctx),
|
|
1889
2516
|
},
|
|
2517
|
+
{
|
|
2518
|
+
name: 'skill',
|
|
2519
|
+
aliases: ['use-skill', 'activate-skill'],
|
|
2520
|
+
description: 'Activate a loaded skill for one chat turn',
|
|
2521
|
+
argumentHint: '<name> <task>',
|
|
2522
|
+
category: 'context',
|
|
2523
|
+
priority: 21,
|
|
2524
|
+
type: 'chat',
|
|
2525
|
+
execute: (_ctx, args) => handleSkill(args),
|
|
2526
|
+
},
|
|
1890
2527
|
{
|
|
1891
2528
|
name: 'memory',
|
|
1892
2529
|
description: 'Show memory status, or `/memory reindex` to rebuild semantic index',
|
|
@@ -2025,6 +2662,44 @@ const COMMANDS = [
|
|
|
2025
2662
|
type: 'builtin',
|
|
2026
2663
|
execute: (ctx) => handleUsage(ctx),
|
|
2027
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
|
+
},
|
|
2028
2703
|
{
|
|
2029
2704
|
name: 'cost',
|
|
2030
2705
|
description: 'Show session token usage',
|