@nghyane/arcane 0.1.28 → 0.1.30
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/CHANGELOG.md +7 -0
- package/package.json +4 -4
- package/src/cli/config-cli.ts +1 -1
- package/src/config/settings-schema.ts +19 -27
- package/src/config/settings.ts +3 -4
- package/src/extensibility/custom-tools/types.ts +0 -12
- package/src/extensibility/extensions/index.ts +0 -5
- package/src/extensibility/extensions/runner.ts +6 -26
- package/src/extensibility/extensions/types.ts +1 -77
- package/src/extensibility/hooks/runner.ts +5 -24
- package/src/extensibility/hooks/types.ts +1 -77
- package/src/index.ts +2 -13
- package/src/modes/components/footer.ts +4 -11
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/status-line/segments.ts +1 -2
- package/src/modes/components/status-line/types.ts +0 -1
- package/src/modes/components/status-line.ts +0 -6
- package/src/modes/components/tree-selector.ts +0 -8
- package/src/modes/controllers/command-controller.ts +2 -98
- package/src/modes/controllers/event-controller.ts +46 -52
- package/src/modes/controllers/extension-ui-controller.ts +0 -42
- package/src/modes/controllers/input-controller.ts +0 -23
- package/src/modes/controllers/selector-controller.ts +0 -5
- package/src/modes/interactive-mode.ts +3 -24
- package/src/modes/print-mode.ts +0 -16
- package/src/modes/rpc/rpc-client.ts +0 -16
- package/src/modes/rpc/rpc-mode.ts +0 -32
- package/src/modes/rpc/rpc-types.ts +0 -9
- package/src/modes/types.ts +1 -13
- package/src/modes/utils/ui-helpers.ts +2 -118
- package/src/prompts/agents/librarian.md +7 -12
- package/src/sdk.ts +0 -15
- package/src/session/agent-session.ts +89 -650
- package/src/session/compaction/branch-summarization.ts +5 -13
- package/src/session/compaction/index.ts +0 -1
- package/src/session/compaction/utils.ts +94 -2
- package/src/session/messages.ts +0 -37
- package/src/session/retry-utils.ts +1 -1
- package/src/session/session-manager.ts +8 -108
- package/src/session/session-types.ts +4 -25
- package/src/session/stats.ts +2 -39
- package/src/slash-commands/builtin-registry.ts +0 -11
- package/src/task/executor.ts +0 -8
- package/src/tools/create-tools.ts +3 -0
- package/src/tools/github-fs.ts +195 -0
- package/src/tools/github-utils.ts +35 -0
- package/src/tools/github.ts +35 -123
- package/src/tools/index.ts +1 -0
- package/examples/hooks/custom-compaction.ts +0 -116
- package/src/modes/components/compaction-summary-message.ts +0 -59
- package/src/prompts/compaction/compaction-short-summary.md +0 -9
- package/src/prompts/compaction/compaction-summary-context.md +0 -5
- package/src/prompts/compaction/compaction-summary.md +0 -41
- package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
- package/src/prompts/compaction/compaction-update-summary.md +0 -45
- package/src/session/compaction/compaction.ts +0 -864
- package/src/session/compaction/pruning.ts +0 -91
|
@@ -11,11 +11,9 @@ import type { Rule } from "../../capability/rule";
|
|
|
11
11
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
12
12
|
import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
13
13
|
import type { EditToolDetails } from "../../patch";
|
|
14
|
-
import type { CompactionPreparation, CompactionResult } from "../../session/compaction";
|
|
15
14
|
import type { HookMessage } from "../../session/messages";
|
|
16
15
|
import type {
|
|
17
16
|
BranchSummaryEntry,
|
|
18
|
-
CompactionEntry,
|
|
19
17
|
ReadonlySessionManager,
|
|
20
18
|
SessionEntry,
|
|
21
19
|
SessionManager,
|
|
@@ -249,34 +247,6 @@ export interface SessionBranchEvent {
|
|
|
249
247
|
previousSessionFile: string | undefined;
|
|
250
248
|
}
|
|
251
249
|
|
|
252
|
-
/** Fired before context compaction (can be cancelled) */
|
|
253
|
-
export interface SessionBeforeCompactEvent {
|
|
254
|
-
type: "session_before_compact";
|
|
255
|
-
/** Compaction preparation with messages to summarize, file ops, previous summary, etc. */
|
|
256
|
-
preparation: CompactionPreparation;
|
|
257
|
-
/** Branch entries (root to current leaf). Use to inspect custom state or previous compactions. */
|
|
258
|
-
branchEntries: SessionEntry[];
|
|
259
|
-
/** Optional user-provided instructions for the summary */
|
|
260
|
-
customInstructions?: string;
|
|
261
|
-
/** Abort signal - hooks should pass this to LLM calls and check it periodically */
|
|
262
|
-
signal: AbortSignal;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/** Fired before compaction summarization to customize prompts/context */
|
|
266
|
-
export interface SessionCompactingEvent {
|
|
267
|
-
type: "session.compacting";
|
|
268
|
-
sessionId: string;
|
|
269
|
-
messages: AgentMessage[];
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/** Fired after context compaction */
|
|
273
|
-
export interface SessionCompactEvent {
|
|
274
|
-
type: "session_compact";
|
|
275
|
-
compactionEntry: CompactionEntry;
|
|
276
|
-
/** Whether the compaction entry was provided by a hook */
|
|
277
|
-
fromExtension: boolean;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
250
|
/** Fired on process exit (SIGINT/SIGTERM) */
|
|
281
251
|
export interface SessionShutdownEvent {
|
|
282
252
|
type: "session_shutdown";
|
|
@@ -290,7 +260,7 @@ export interface TreePreparation {
|
|
|
290
260
|
oldLeafId: string | null;
|
|
291
261
|
/** Common ancestor of target and old leaf, null if no common ancestor */
|
|
292
262
|
commonAncestorId: string | null;
|
|
293
|
-
/** Entries to summarize (old leaf back to common ancestor
|
|
263
|
+
/** Entries to summarize (old leaf back to common ancestor) */
|
|
294
264
|
entriesToSummarize: SessionEntry[];
|
|
295
265
|
/** Whether user chose to summarize */
|
|
296
266
|
userWantsSummary: boolean;
|
|
@@ -325,9 +295,6 @@ export type SessionEvent =
|
|
|
325
295
|
| SessionSwitchEvent
|
|
326
296
|
| SessionBeforeBranchEvent
|
|
327
297
|
| SessionBranchEvent
|
|
328
|
-
| SessionBeforeCompactEvent
|
|
329
|
-
| SessionCompactingEvent
|
|
330
|
-
| SessionCompactEvent
|
|
331
298
|
| SessionShutdownEvent
|
|
332
299
|
| SessionBeforeTreeEvent
|
|
333
300
|
| SessionTreeEvent;
|
|
@@ -391,21 +358,6 @@ export interface TurnEndEvent {
|
|
|
391
358
|
toolResults: ToolResultMessage[];
|
|
392
359
|
}
|
|
393
360
|
|
|
394
|
-
/** Event data for auto_compaction_start event. */
|
|
395
|
-
export interface AutoCompactionStartEvent {
|
|
396
|
-
type: "auto_compaction_start";
|
|
397
|
-
reason: "threshold" | "overflow";
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/** Event data for auto_compaction_end event. */
|
|
401
|
-
export interface AutoCompactionEndEvent {
|
|
402
|
-
type: "auto_compaction_end";
|
|
403
|
-
result: CompactionResult | undefined;
|
|
404
|
-
aborted: boolean;
|
|
405
|
-
willRetry: boolean;
|
|
406
|
-
errorMessage?: string;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
361
|
/** Event data for auto_retry_start event. */
|
|
410
362
|
export interface AutoRetryStartEvent {
|
|
411
363
|
type: "auto_retry_start";
|
|
@@ -533,8 +485,6 @@ export type HookEvent =
|
|
|
533
485
|
| AgentEndEvent
|
|
534
486
|
| TurnStartEvent
|
|
535
487
|
| TurnEndEvent
|
|
536
|
-
| AutoCompactionStartEvent
|
|
537
|
-
| AutoCompactionEndEvent
|
|
538
488
|
| AutoRetryStartEvent
|
|
539
489
|
| AutoRetryEndEvent
|
|
540
490
|
| TtsrTriggeredEvent
|
|
@@ -616,24 +566,6 @@ export interface SessionBeforeBranchResult {
|
|
|
616
566
|
skipConversationRestore?: boolean;
|
|
617
567
|
}
|
|
618
568
|
|
|
619
|
-
/** Return type for session_before_compact handlers */
|
|
620
|
-
export interface SessionBeforeCompactResult {
|
|
621
|
-
/** If true, cancel the compaction */
|
|
622
|
-
cancel?: boolean;
|
|
623
|
-
/** Custom compaction result - SessionManager adds id/parentId */
|
|
624
|
-
compaction?: CompactionResult;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/** Return type for session.compacting handlers */
|
|
628
|
-
export interface SessionCompactingResult {
|
|
629
|
-
/** Additional context lines to include in summary */
|
|
630
|
-
context?: string[];
|
|
631
|
-
/** Override the default compaction prompt */
|
|
632
|
-
prompt?: string;
|
|
633
|
-
/** Custom data to store in compaction entry */
|
|
634
|
-
preserveData?: Record<string, unknown>;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
569
|
/** Return type for session_before_tree handlers */
|
|
638
570
|
export interface SessionBeforeTreeResult {
|
|
639
571
|
/** If true, cancel the navigation entirely */
|
|
@@ -695,12 +627,6 @@ export interface HookAPI {
|
|
|
695
627
|
on(event: "session_switch", handler: HookHandler<SessionSwitchEvent>): void;
|
|
696
628
|
on(event: "session_before_branch", handler: HookHandler<SessionBeforeBranchEvent, SessionBeforeBranchResult>): void;
|
|
697
629
|
on(event: "session_branch", handler: HookHandler<SessionBranchEvent>): void;
|
|
698
|
-
on(
|
|
699
|
-
event: "session_before_compact",
|
|
700
|
-
handler: HookHandler<SessionBeforeCompactEvent, SessionBeforeCompactResult>,
|
|
701
|
-
): void;
|
|
702
|
-
on(event: "session.compacting", handler: HookHandler<SessionCompactingEvent, SessionCompactingResult>): void;
|
|
703
|
-
on(event: "session_compact", handler: HookHandler<SessionCompactEvent>): void;
|
|
704
630
|
on(event: "session_shutdown", handler: HookHandler<SessionShutdownEvent>): void;
|
|
705
631
|
on(event: "session_before_tree", handler: HookHandler<SessionBeforeTreeEvent, SessionBeforeTreeResult>): void;
|
|
706
632
|
on(event: "session_tree", handler: HookHandler<SessionTreeEvent>): void;
|
|
@@ -712,8 +638,6 @@ export interface HookAPI {
|
|
|
712
638
|
on(event: "agent_end", handler: HookHandler<AgentEndEvent>): void;
|
|
713
639
|
on(event: "turn_start", handler: HookHandler<TurnStartEvent>): void;
|
|
714
640
|
on(event: "turn_end", handler: HookHandler<TurnEndEvent>): void;
|
|
715
|
-
on(event: "auto_compaction_start", handler: HookHandler<AutoCompactionStartEvent>): void;
|
|
716
|
-
on(event: "auto_compaction_end", handler: HookHandler<AutoCompactionEndEvent>): void;
|
|
717
641
|
on(event: "auto_retry_start", handler: HookHandler<AutoRetryStartEvent>): void;
|
|
718
642
|
on(event: "auto_retry_end", handler: HookHandler<AutoRetryEndEvent>): void;
|
|
719
643
|
on(event: "ttsr_triggered", handler: HookHandler<TtsrTriggeredEvent>): void;
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { ModelRegistry } from "./config/model-registry";
|
|
|
13
13
|
// Prompt templates
|
|
14
14
|
export type { PromptTemplate } from "./config/prompt-templates";
|
|
15
15
|
export { renderPromptTemplate } from "./config/prompt-templates";
|
|
16
|
-
export type {
|
|
16
|
+
export type { RetrySettings, SkillsSettings } from "./config/settings";
|
|
17
17
|
export { Settings, settings } from "./config/settings";
|
|
18
18
|
// Custom commands
|
|
19
19
|
export type {
|
|
@@ -114,7 +114,6 @@ export {
|
|
|
114
114
|
BashExecutionComponent,
|
|
115
115
|
BorderedLoader,
|
|
116
116
|
BranchSummaryMessageComponent,
|
|
117
|
-
CompactionSummaryMessageComponent,
|
|
118
117
|
CustomEditor,
|
|
119
118
|
CustomMessageComponent,
|
|
120
119
|
DynamicBorder,
|
|
@@ -181,39 +180,29 @@ export {
|
|
|
181
180
|
} from "./session/agent-session";
|
|
182
181
|
// Auth and model registry
|
|
183
182
|
export { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from "./session/auth-storage";
|
|
184
|
-
//
|
|
183
|
+
// Branch summarization and utilities
|
|
185
184
|
export {
|
|
186
185
|
type BranchPreparation,
|
|
187
186
|
type BranchSummaryResult,
|
|
188
187
|
type CollectEntriesResult,
|
|
189
|
-
type CompactionResult,
|
|
190
|
-
type CutPointResult,
|
|
191
188
|
calculateContextTokens,
|
|
192
189
|
collectEntriesForBranchSummary,
|
|
193
|
-
compact,
|
|
194
|
-
DEFAULT_COMPACTION_SETTINGS,
|
|
195
190
|
estimateTokens,
|
|
196
191
|
type FileOperations,
|
|
197
|
-
findCutPoint,
|
|
198
|
-
findTurnStartIndex,
|
|
199
192
|
type GenerateBranchSummaryOptions,
|
|
200
193
|
generateBranchSummary,
|
|
201
|
-
generateSummary,
|
|
202
194
|
getLastAssistantUsage,
|
|
203
195
|
prepareBranchEntries,
|
|
204
196
|
serializeConversation,
|
|
205
|
-
shouldCompact,
|
|
206
197
|
} from "./session/compaction";
|
|
207
198
|
export { convertToLlm } from "./session/messages";
|
|
208
199
|
export {
|
|
209
200
|
type BranchSummaryEntry,
|
|
210
201
|
buildSessionContext,
|
|
211
|
-
type CompactionEntry,
|
|
212
202
|
CURRENT_SESSION_VERSION,
|
|
213
203
|
type CustomEntry,
|
|
214
204
|
type CustomMessageEntry,
|
|
215
205
|
type FileEntry,
|
|
216
|
-
getLatestCompactionEntry,
|
|
217
206
|
type ModeChangeEntry,
|
|
218
207
|
type ModelChangeEntry,
|
|
219
208
|
migrateSessionEntries,
|
|
@@ -45,15 +45,10 @@ export class FooterComponent implements Component {
|
|
|
45
45
|
#cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name
|
|
46
46
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
47
47
|
#onBranchChange: (() => void) | null = null;
|
|
48
|
-
#autoCompactEnabled: boolean = true;
|
|
49
48
|
#extensionStatuses: Map<string, string> = new Map();
|
|
50
49
|
|
|
51
50
|
constructor(private readonly session: AgentSession) {}
|
|
52
51
|
|
|
53
|
-
setAutoCompactEnabled(enabled: boolean): void {
|
|
54
|
-
this.#autoCompactEnabled = enabled;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
52
|
/**
|
|
58
53
|
* Set extension status text to display in the footer.
|
|
59
54
|
* Text is sanitized (newlines/tabs replaced with spaces) and truncated to terminal width.
|
|
@@ -157,7 +152,7 @@ export class FooterComponent implements Component {
|
|
|
157
152
|
render(width: number): string[] {
|
|
158
153
|
const state = this.session.state;
|
|
159
154
|
|
|
160
|
-
// Calculate cumulative usage from ALL session entries
|
|
155
|
+
// Calculate cumulative usage from ALL session entries
|
|
161
156
|
let totalInput = 0;
|
|
162
157
|
let totalOutput = 0;
|
|
163
158
|
let totalCacheRead = 0;
|
|
@@ -174,8 +169,7 @@ export class FooterComponent implements Component {
|
|
|
174
169
|
}
|
|
175
170
|
}
|
|
176
171
|
|
|
177
|
-
// Calculate context usage from session
|
|
178
|
-
// After compaction, tokens are unknown until the next LLM response.
|
|
172
|
+
// Calculate context usage from session.
|
|
179
173
|
const contextUsage = this.session.getContextUsage();
|
|
180
174
|
const contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;
|
|
181
175
|
const contextPercentValue = contextUsage?.percent ?? 0;
|
|
@@ -227,11 +221,10 @@ export class FooterComponent implements Component {
|
|
|
227
221
|
|
|
228
222
|
// Colorize context percentage based on usage
|
|
229
223
|
let contextPercentStr: string;
|
|
230
|
-
const autoIndicator = this.#autoCompactEnabled ? " (auto)" : "";
|
|
231
224
|
const contextPercentDisplay =
|
|
232
225
|
contextPercent === "?"
|
|
233
|
-
? `?/${formatTokens(contextWindow)}
|
|
234
|
-
: `${contextPercent}%/${formatTokens(contextWindow)}
|
|
226
|
+
? `?/${formatTokens(contextWindow)}`
|
|
227
|
+
: `${contextPercent}%/${formatTokens(contextWindow)}`;
|
|
235
228
|
if (contextPercentValue > 90) {
|
|
236
229
|
contextPercentStr = theme.fg("error", contextPercentDisplay);
|
|
237
230
|
} else if (contextPercentValue > 70) {
|
|
@@ -3,7 +3,6 @@ export { AssistantMessageComponent } from "./assistant-message";
|
|
|
3
3
|
export { BashExecutionComponent } from "./bash-execution";
|
|
4
4
|
export { BorderedLoader } from "./bordered-loader";
|
|
5
5
|
export { BranchSummaryMessageComponent } from "./branch-summary-message";
|
|
6
|
-
export { CompactionSummaryMessageComponent } from "./compaction-summary-message";
|
|
7
6
|
export { CountdownTimer } from "./countdown-timer";
|
|
8
7
|
export { CustomEditor } from "./custom-editor";
|
|
9
8
|
export { CustomMessageComponent } from "./custom-message";
|
|
@@ -211,8 +211,7 @@ const contextPctSegment: StatusLineSegment = {
|
|
|
211
211
|
const pct = ctx.contextPercent;
|
|
212
212
|
const window = ctx.contextWindow;
|
|
213
213
|
|
|
214
|
-
const
|
|
215
|
-
const text = `${pct.toFixed(1)}%/${formatTokens(window)}${autoIcon}`;
|
|
214
|
+
const text = `${pct.toFixed(1)}%/${formatTokens(window)}`;
|
|
216
215
|
|
|
217
216
|
let content: string;
|
|
218
217
|
if (pct > 90) {
|
|
@@ -65,7 +65,6 @@ export class StatusLineComponent implements Component {
|
|
|
65
65
|
#cachedBranch: string | null | undefined = undefined;
|
|
66
66
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
67
67
|
#onBranchChange: (() => void) | null = null;
|
|
68
|
-
#autoCompactEnabled: boolean = true;
|
|
69
68
|
#hookStatuses: Map<string, string> = new Map();
|
|
70
69
|
#subagentCount: number = 0;
|
|
71
70
|
#sessionStartTime: number = Date.now();
|
|
@@ -89,10 +88,6 @@ export class StatusLineComponent implements Component {
|
|
|
89
88
|
this.#settings = settings;
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
setAutoCompactEnabled(enabled: boolean): void {
|
|
93
|
-
this.#autoCompactEnabled = enabled;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
91
|
setSubagentCount(count: number): void {
|
|
97
92
|
this.#subagentCount = count;
|
|
98
93
|
}
|
|
@@ -258,7 +253,6 @@ export class StatusLineComponent implements Component {
|
|
|
258
253
|
usageStats,
|
|
259
254
|
contextPercent,
|
|
260
255
|
contextWindow,
|
|
261
|
-
autoCompactEnabled: this.#autoCompactEnabled,
|
|
262
256
|
subagentCount: this.#subagentCount,
|
|
263
257
|
sessionStartTime: this.#sessionStartTime,
|
|
264
258
|
git: {
|
|
@@ -372,9 +372,6 @@ class TreeList implements Component {
|
|
|
372
372
|
}
|
|
373
373
|
break;
|
|
374
374
|
}
|
|
375
|
-
case "compaction":
|
|
376
|
-
parts.push("compaction");
|
|
377
|
-
break;
|
|
378
375
|
case "branch_summary":
|
|
379
376
|
parts.push("branch summary", entry.summary);
|
|
380
377
|
break;
|
|
@@ -573,11 +570,6 @@ class TreeList implements Component {
|
|
|
573
570
|
result = theme.fg("customMessageLabel", `[${entry.customType}]: `) + normalize(content);
|
|
574
571
|
break;
|
|
575
572
|
}
|
|
576
|
-
case "compaction": {
|
|
577
|
-
const tokens = Math.round(entry.tokensBefore / 1000);
|
|
578
|
-
result = theme.fg("borderAccent", `[compaction: ${tokens}k tokens]`);
|
|
579
|
-
break;
|
|
580
|
-
}
|
|
581
573
|
case "branch_summary":
|
|
582
574
|
result = theme.fg("warning", `[branch summary]: `) + normalize(entry.summary);
|
|
583
575
|
break;
|
|
@@ -9,14 +9,13 @@ import {
|
|
|
9
9
|
type UsageReport,
|
|
10
10
|
} from "@nghyane/arcane-ai";
|
|
11
11
|
import { copyToClipboard } from "@nghyane/arcane-natives";
|
|
12
|
-
import {
|
|
12
|
+
import { Markdown, padding, Spacer, Text, visibleWidth } from "@nghyane/arcane-tui";
|
|
13
13
|
import { Snowflake } from "@nghyane/arcane-utils";
|
|
14
14
|
import { setProjectDir } from "@nghyane/arcane-utils/dirs";
|
|
15
15
|
import { $ } from "bun";
|
|
16
16
|
import { reset as resetCapabilities } from "../../capability";
|
|
17
17
|
import { formatKeyHint, type KeyId } from "../../config/keybindings";
|
|
18
18
|
import { loadCustomShare } from "../../export/custom-share";
|
|
19
|
-
import type { CompactOptions } from "../../extensibility/extensions/types";
|
|
20
19
|
import { getGatewayStatus } from "../../ipy/gateway-coordinator";
|
|
21
20
|
import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
22
21
|
import { BorderedLoader } from "../../modes/components/bordered-loader";
|
|
@@ -24,8 +23,7 @@ import { DynamicBorder } from "../../modes/components/dynamic-border";
|
|
|
24
23
|
import { PythonExecutionComponent } from "../../modes/components/python-execution";
|
|
25
24
|
import type { InteractiveModeContext } from "../../modes/types";
|
|
26
25
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
27
|
-
import {
|
|
28
|
-
import { getMarkdownTheme, getSymbolTheme, theme } from "../../theme/theme";
|
|
26
|
+
import { getMarkdownTheme, theme } from "../../theme/theme";
|
|
29
27
|
import { outputMeta } from "../../tools/output-meta";
|
|
30
28
|
import { resolveToCwd } from "../../tools/path-utils";
|
|
31
29
|
import { getChangelogPath, parseChangelog } from "../../utils/changelog";
|
|
@@ -425,12 +423,6 @@ export class CommandController {
|
|
|
425
423
|
}
|
|
426
424
|
this.ctx.statusContainer.clear();
|
|
427
425
|
|
|
428
|
-
if (this.ctx.session.isCompacting) {
|
|
429
|
-
this.ctx.session.abortCompaction();
|
|
430
|
-
while (this.ctx.session.isCompacting) {
|
|
431
|
-
await Bun.sleep(10);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
426
|
await this.ctx.session.newSession();
|
|
435
427
|
|
|
436
428
|
this.ctx.statusLine.invalidate();
|
|
@@ -438,7 +430,6 @@ export class CommandController {
|
|
|
438
430
|
|
|
439
431
|
this.ctx.chatContainer.clear();
|
|
440
432
|
this.ctx.pendingMessagesContainer.clear();
|
|
441
|
-
this.ctx.compactionQueuedMessages = [];
|
|
442
433
|
this.ctx.streamingComponent = undefined;
|
|
443
434
|
this.ctx.streamingMessage = undefined;
|
|
444
435
|
this.ctx.pendingTools.clear();
|
|
@@ -604,92 +595,6 @@ export class CommandController {
|
|
|
604
595
|
this.ctx.ui.requestRender();
|
|
605
596
|
}
|
|
606
597
|
|
|
607
|
-
async handleCompactCommand(customInstructions?: string): Promise<void> {
|
|
608
|
-
const entries = this.ctx.sessionManager.getEntries();
|
|
609
|
-
const messageCount = entries.filter(e => e.type === "message").length;
|
|
610
|
-
|
|
611
|
-
if (messageCount < 2) {
|
|
612
|
-
this.ctx.showWarning("Nothing to compact (no messages yet)");
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
await this.executeCompaction(customInstructions, false);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
async handleSkillCommand(skillPath: string, args: string): Promise<void> {
|
|
620
|
-
try {
|
|
621
|
-
const content = await Bun.file(skillPath).text();
|
|
622
|
-
const body = content.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
|
|
623
|
-
const metaLines = [`Skill: ${skillPath}`];
|
|
624
|
-
if (args) {
|
|
625
|
-
metaLines.push(`User: ${args}`);
|
|
626
|
-
}
|
|
627
|
-
const message = `${body}\n\n---\n\n${metaLines.join("\n")}`;
|
|
628
|
-
await this.ctx.session.prompt(message);
|
|
629
|
-
} catch (err) {
|
|
630
|
-
this.ctx.showError(`Failed to load skill: ${err instanceof Error ? err.message : String(err)}`);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
async executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto = false): Promise<void> {
|
|
635
|
-
if (this.ctx.loadingAnimation) {
|
|
636
|
-
this.ctx.loadingAnimation.stop();
|
|
637
|
-
this.ctx.loadingAnimation = undefined;
|
|
638
|
-
}
|
|
639
|
-
this.ctx.statusContainer.clear();
|
|
640
|
-
|
|
641
|
-
const originalOnEscape = this.ctx.editor.onEscape;
|
|
642
|
-
this.ctx.editor.onEscape = () => {
|
|
643
|
-
this.ctx.session.abortCompaction();
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
647
|
-
const label = isAuto ? "Auto-compacting context... (esc to cancel)" : "Compacting context... (esc to cancel)";
|
|
648
|
-
const compactingLoader = new Loader(
|
|
649
|
-
this.ctx.ui,
|
|
650
|
-
spinner => theme.fg("accent", spinner),
|
|
651
|
-
text => theme.fg("muted", text),
|
|
652
|
-
label,
|
|
653
|
-
getSymbolTheme().spinnerFrames,
|
|
654
|
-
);
|
|
655
|
-
this.ctx.statusContainer.addChild(compactingLoader);
|
|
656
|
-
this.ctx.ui.requestRender();
|
|
657
|
-
|
|
658
|
-
try {
|
|
659
|
-
const instructions = typeof customInstructionsOrOptions === "string" ? customInstructionsOrOptions : undefined;
|
|
660
|
-
const options =
|
|
661
|
-
customInstructionsOrOptions && typeof customInstructionsOrOptions === "object"
|
|
662
|
-
? customInstructionsOrOptions
|
|
663
|
-
: undefined;
|
|
664
|
-
const result = await this.ctx.session.compact(instructions, options);
|
|
665
|
-
|
|
666
|
-
this.ctx.rebuildChatFromMessages();
|
|
667
|
-
|
|
668
|
-
const msg = createCompactionSummaryMessage(
|
|
669
|
-
result.summary,
|
|
670
|
-
result.tokensBefore,
|
|
671
|
-
new Date().toISOString(),
|
|
672
|
-
result.shortSummary,
|
|
673
|
-
);
|
|
674
|
-
this.ctx.addMessageToChat(msg);
|
|
675
|
-
|
|
676
|
-
this.ctx.statusLine.invalidate();
|
|
677
|
-
this.ctx.updateEditorTopBorder();
|
|
678
|
-
} catch (error) {
|
|
679
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
680
|
-
if (message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError")) {
|
|
681
|
-
this.ctx.showError("Compaction cancelled");
|
|
682
|
-
} else {
|
|
683
|
-
this.ctx.showError(`Compaction failed: ${message}`);
|
|
684
|
-
}
|
|
685
|
-
} finally {
|
|
686
|
-
compactingLoader.stop();
|
|
687
|
-
this.ctx.statusContainer.clear();
|
|
688
|
-
this.ctx.editor.onEscape = originalOnEscape;
|
|
689
|
-
}
|
|
690
|
-
await this.ctx.flushCompactionQueue({ willRetry: false });
|
|
691
|
-
}
|
|
692
|
-
|
|
693
598
|
async handleHandoffCommand(customInstructions?: string): Promise<void> {
|
|
694
599
|
const entries = this.ctx.sessionManager.getEntries();
|
|
695
600
|
const messageCount = entries.filter(e => e.type === "message").length;
|
|
@@ -772,7 +677,6 @@ function formatDurationShort(ms: number): string {
|
|
|
772
677
|
const hrs = hours % 24;
|
|
773
678
|
if (days > 0) return `${days}d${hrs > 0 ? ` ${hrs}h` : ""}`;
|
|
774
679
|
if (hours > 0) return `${hours}h${mins > 0 ? ` ${mins}m` : ""}`;
|
|
775
|
-
if (minutes > 0) return `${minutes}m`;
|
|
776
680
|
return `${totalSeconds}s`;
|
|
777
681
|
}
|
|
778
682
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AgentTool, toolDetails } from "@nghyane/arcane-agent";
|
|
2
|
-
import { Loader, TERMINAL } from "@nghyane/arcane-tui";
|
|
2
|
+
import { Loader, TERMINAL, Text } from "@nghyane/arcane-tui";
|
|
3
3
|
import { settings } from "../../config/settings";
|
|
4
4
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
5
5
|
import { ContextGroupComponent } from "../../modes/components/context-group";
|
|
@@ -259,57 +259,6 @@ export class EventController {
|
|
|
259
259
|
this.sendCompletionNotification();
|
|
260
260
|
break;
|
|
261
261
|
|
|
262
|
-
case "auto_compaction_start": {
|
|
263
|
-
this.ctx.autoCompactionEscapeHandler = this.ctx.editor.onEscape;
|
|
264
|
-
this.ctx.editor.onEscape = () => {
|
|
265
|
-
this.ctx.session.abortCompaction();
|
|
266
|
-
};
|
|
267
|
-
this.ctx.statusContainer.clear();
|
|
268
|
-
const reasonText = event.reason === "overflow" ? "Context overflow detected, " : "";
|
|
269
|
-
this.ctx.autoCompactionLoader = new Loader(
|
|
270
|
-
this.ctx.ui,
|
|
271
|
-
spinner => theme.fg("accent", spinner),
|
|
272
|
-
text => theme.fg("muted", text),
|
|
273
|
-
`${reasonText}Auto-compacting… (esc to cancel)`,
|
|
274
|
-
getSymbolTheme().spinnerFrames,
|
|
275
|
-
);
|
|
276
|
-
this.ctx.statusContainer.addChild(this.ctx.autoCompactionLoader);
|
|
277
|
-
this.ctx.ui.requestRender();
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
case "auto_compaction_end": {
|
|
282
|
-
if (this.ctx.autoCompactionEscapeHandler) {
|
|
283
|
-
this.ctx.editor.onEscape = this.ctx.autoCompactionEscapeHandler;
|
|
284
|
-
this.ctx.autoCompactionEscapeHandler = undefined;
|
|
285
|
-
}
|
|
286
|
-
if (this.ctx.autoCompactionLoader) {
|
|
287
|
-
this.ctx.autoCompactionLoader.stop();
|
|
288
|
-
this.ctx.autoCompactionLoader = undefined;
|
|
289
|
-
this.ctx.statusContainer.clear();
|
|
290
|
-
}
|
|
291
|
-
if (event.aborted) {
|
|
292
|
-
this.ctx.showStatus("Auto-compaction cancelled");
|
|
293
|
-
} else if (event.result) {
|
|
294
|
-
this.ctx.chatContainer.clear();
|
|
295
|
-
this.ctx.rebuildChatFromMessages();
|
|
296
|
-
this.ctx.addMessageToChat({
|
|
297
|
-
role: "compactionSummary",
|
|
298
|
-
tokensBefore: event.result.tokensBefore,
|
|
299
|
-
summary: event.result.summary,
|
|
300
|
-
shortSummary: event.result.shortSummary,
|
|
301
|
-
timestamp: Date.now(),
|
|
302
|
-
});
|
|
303
|
-
this.ctx.statusLine.invalidate();
|
|
304
|
-
this.ctx.updateEditorTopBorder();
|
|
305
|
-
} else {
|
|
306
|
-
this.ctx.showWarning("Auto-compaction failed; continuing without compaction");
|
|
307
|
-
}
|
|
308
|
-
await this.ctx.flushCompactionQueue({ willRetry: event.willRetry });
|
|
309
|
-
this.ctx.ui.requestRender();
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
262
|
case "auto_retry_start": {
|
|
314
263
|
this.ctx.retryEscapeHandler = this.ctx.editor.onEscape;
|
|
315
264
|
this.ctx.editor.onEscape = () => {
|
|
@@ -348,6 +297,51 @@ export class EventController {
|
|
|
348
297
|
break;
|
|
349
298
|
}
|
|
350
299
|
|
|
300
|
+
case "context_warning": {
|
|
301
|
+
this.ctx.statusContainer.clear();
|
|
302
|
+
const warningMsg = `Context usage at ${event.percent}% — session will auto-handoff soon`;
|
|
303
|
+
this.ctx.statusContainer.addChild(
|
|
304
|
+
new Text(theme.fg("warning", `${theme.status.warning} ${warningMsg}`), 0, 0),
|
|
305
|
+
);
|
|
306
|
+
this.ctx.ui.requestRender();
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
case "auto_handoff_start": {
|
|
311
|
+
this.ctx.statusContainer.clear();
|
|
312
|
+
this.ctx.handoffLoader = new Loader(
|
|
313
|
+
this.ctx.ui,
|
|
314
|
+
spinner => theme.fg("warning", spinner),
|
|
315
|
+
text => theme.fg("muted", text),
|
|
316
|
+
"Auto-handoff in progress…",
|
|
317
|
+
getSymbolTheme().spinnerFrames,
|
|
318
|
+
);
|
|
319
|
+
this.ctx.statusContainer.addChild(this.ctx.handoffLoader);
|
|
320
|
+
this.ctx.ui.requestRender();
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
case "auto_handoff_end": {
|
|
325
|
+
if (this.ctx.handoffLoader) {
|
|
326
|
+
this.ctx.handoffLoader.stop();
|
|
327
|
+
this.ctx.handoffLoader = undefined;
|
|
328
|
+
this.ctx.statusContainer.clear();
|
|
329
|
+
}
|
|
330
|
+
if (event.success) {
|
|
331
|
+
this.ctx.statusContainer.addChild(
|
|
332
|
+
new Text(
|
|
333
|
+
theme.fg("success", `${theme.status.success} Session handed off — continuing in new session`),
|
|
334
|
+
0,
|
|
335
|
+
0,
|
|
336
|
+
),
|
|
337
|
+
);
|
|
338
|
+
} else if (event.error) {
|
|
339
|
+
this.ctx.showError(`Auto-handoff failed: ${event.error}`);
|
|
340
|
+
}
|
|
341
|
+
this.ctx.ui.requestRender();
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
|
|
351
345
|
case "ttsr_triggered": {
|
|
352
346
|
this.#finalizeContextGroup();
|
|
353
347
|
const component = new TtsrNotificationComponent(event.rules);
|
|
@@ -133,12 +133,6 @@ export class ExtensionUiController {
|
|
|
133
133
|
// Signal shutdown request (will be handled by main loop)
|
|
134
134
|
},
|
|
135
135
|
getContextUsage: () => this.ctx.session.getContextUsage(),
|
|
136
|
-
compact: async instructionsOrOptions => {
|
|
137
|
-
const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
|
|
138
|
-
const options =
|
|
139
|
-
instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined;
|
|
140
|
-
await this.ctx.session.compact(instructions, options);
|
|
141
|
-
},
|
|
142
136
|
getSystemPrompt: () => this.ctx.session.systemPrompt,
|
|
143
137
|
};
|
|
144
138
|
const commandActions: ExtensionCommandContextActions = {
|
|
@@ -174,7 +168,6 @@ export class ExtensionUiController {
|
|
|
174
168
|
// Clear UI state
|
|
175
169
|
this.ctx.chatContainer.clear();
|
|
176
170
|
this.ctx.pendingMessagesContainer.clear();
|
|
177
|
-
this.ctx.compactionQueuedMessages = [];
|
|
178
171
|
this.ctx.streamingComponent = undefined;
|
|
179
172
|
this.ctx.streamingMessage = undefined;
|
|
180
173
|
this.ctx.pendingTools.clear();
|
|
@@ -220,16 +213,6 @@ export class ExtensionUiController {
|
|
|
220
213
|
|
|
221
214
|
return { cancelled: false };
|
|
222
215
|
},
|
|
223
|
-
compact: async instructionsOrOptions => {
|
|
224
|
-
const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
|
|
225
|
-
const options =
|
|
226
|
-
instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined;
|
|
227
|
-
if (this.ctx.isBackgrounded) {
|
|
228
|
-
await this.ctx.session.compact(instructions, options);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
await this.ctx.executeCompaction(instructionsOrOptions, false);
|
|
232
|
-
},
|
|
233
216
|
switchSession: async sessionPath => {
|
|
234
217
|
const result = await this.ctx.session.switchSession(sessionPath);
|
|
235
218
|
if (!result) {
|
|
@@ -322,12 +305,6 @@ export class ExtensionUiController {
|
|
|
322
305
|
// Signal shutdown request (will be handled by main loop)
|
|
323
306
|
},
|
|
324
307
|
getContextUsage: () => this.ctx.session.getContextUsage(),
|
|
325
|
-
compact: async instructionsOrOptions => {
|
|
326
|
-
const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
|
|
327
|
-
const options =
|
|
328
|
-
instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined;
|
|
329
|
-
await this.ctx.session.compact(instructions, options);
|
|
330
|
-
},
|
|
331
308
|
getSystemPrompt: () => this.ctx.session.systemPrompt,
|
|
332
309
|
};
|
|
333
310
|
const commandActions: ExtensionCommandContextActions = {
|
|
@@ -369,7 +346,6 @@ export class ExtensionUiController {
|
|
|
369
346
|
// Clear UI state
|
|
370
347
|
this.ctx.chatContainer.clear();
|
|
371
348
|
this.ctx.pendingMessagesContainer.clear();
|
|
372
|
-
this.ctx.compactionQueuedMessages = [];
|
|
373
349
|
this.ctx.streamingComponent = undefined;
|
|
374
350
|
this.ctx.streamingMessage = undefined;
|
|
375
351
|
this.ctx.pendingTools.clear();
|
|
@@ -421,16 +397,6 @@ export class ExtensionUiController {
|
|
|
421
397
|
|
|
422
398
|
return { cancelled: false };
|
|
423
399
|
},
|
|
424
|
-
compact: async instructionsOrOptions => {
|
|
425
|
-
const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
|
|
426
|
-
const options =
|
|
427
|
-
instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined;
|
|
428
|
-
if (this.ctx.isBackgrounded) {
|
|
429
|
-
await this.ctx.session.compact(instructions, options);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
await this.ctx.executeCompaction(instructionsOrOptions, false);
|
|
433
|
-
},
|
|
434
400
|
switchSession: async sessionPath => {
|
|
435
401
|
if (this.ctx.isBackgrounded) {
|
|
436
402
|
return { cancelled: true };
|
|
@@ -497,14 +463,6 @@ export class ExtensionUiController {
|
|
|
497
463
|
await registeredTool.definition.onSession(event, {
|
|
498
464
|
ui: uiContext,
|
|
499
465
|
getContextUsage: () => this.ctx.session.getContextUsage(),
|
|
500
|
-
compact: async instructionsOrOptions => {
|
|
501
|
-
const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
|
|
502
|
-
const options =
|
|
503
|
-
instructionsOrOptions && typeof instructionsOrOptions === "object"
|
|
504
|
-
? instructionsOrOptions
|
|
505
|
-
: undefined;
|
|
506
|
-
await this.ctx.session.compact(instructions, options);
|
|
507
|
-
},
|
|
508
466
|
hasUI: !this.ctx.isBackgrounded,
|
|
509
467
|
cwd: this.ctx.sessionManager.getCwd(),
|
|
510
468
|
sessionManager: this.ctx.session.sessionManager,
|