@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.
Files changed (57) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +4 -4
  3. package/src/cli/config-cli.ts +1 -1
  4. package/src/config/settings-schema.ts +19 -27
  5. package/src/config/settings.ts +3 -4
  6. package/src/extensibility/custom-tools/types.ts +0 -12
  7. package/src/extensibility/extensions/index.ts +0 -5
  8. package/src/extensibility/extensions/runner.ts +6 -26
  9. package/src/extensibility/extensions/types.ts +1 -77
  10. package/src/extensibility/hooks/runner.ts +5 -24
  11. package/src/extensibility/hooks/types.ts +1 -77
  12. package/src/index.ts +2 -13
  13. package/src/modes/components/footer.ts +4 -11
  14. package/src/modes/components/index.ts +0 -1
  15. package/src/modes/components/status-line/segments.ts +1 -2
  16. package/src/modes/components/status-line/types.ts +0 -1
  17. package/src/modes/components/status-line.ts +0 -6
  18. package/src/modes/components/tree-selector.ts +0 -8
  19. package/src/modes/controllers/command-controller.ts +2 -98
  20. package/src/modes/controllers/event-controller.ts +46 -52
  21. package/src/modes/controllers/extension-ui-controller.ts +0 -42
  22. package/src/modes/controllers/input-controller.ts +0 -23
  23. package/src/modes/controllers/selector-controller.ts +0 -5
  24. package/src/modes/interactive-mode.ts +3 -24
  25. package/src/modes/print-mode.ts +0 -16
  26. package/src/modes/rpc/rpc-client.ts +0 -16
  27. package/src/modes/rpc/rpc-mode.ts +0 -32
  28. package/src/modes/rpc/rpc-types.ts +0 -9
  29. package/src/modes/types.ts +1 -13
  30. package/src/modes/utils/ui-helpers.ts +2 -118
  31. package/src/prompts/agents/librarian.md +7 -12
  32. package/src/sdk.ts +0 -15
  33. package/src/session/agent-session.ts +89 -650
  34. package/src/session/compaction/branch-summarization.ts +5 -13
  35. package/src/session/compaction/index.ts +0 -1
  36. package/src/session/compaction/utils.ts +94 -2
  37. package/src/session/messages.ts +0 -37
  38. package/src/session/retry-utils.ts +1 -1
  39. package/src/session/session-manager.ts +8 -108
  40. package/src/session/session-types.ts +4 -25
  41. package/src/session/stats.ts +2 -39
  42. package/src/slash-commands/builtin-registry.ts +0 -11
  43. package/src/task/executor.ts +0 -8
  44. package/src/tools/create-tools.ts +3 -0
  45. package/src/tools/github-fs.ts +195 -0
  46. package/src/tools/github-utils.ts +35 -0
  47. package/src/tools/github.ts +35 -123
  48. package/src/tools/index.ts +1 -0
  49. package/examples/hooks/custom-compaction.ts +0 -116
  50. package/src/modes/components/compaction-summary-message.ts +0 -59
  51. package/src/prompts/compaction/compaction-short-summary.md +0 -9
  52. package/src/prompts/compaction/compaction-summary-context.md +0 -5
  53. package/src/prompts/compaction/compaction-summary.md +0 -41
  54. package/src/prompts/compaction/compaction-turn-prefix.md +0 -17
  55. package/src/prompts/compaction/compaction-update-summary.md +0 -45
  56. package/src/session/compaction/compaction.ts +0 -864
  57. package/src/session/compaction/pruning.ts +0 -91
@@ -30,13 +30,9 @@ export class InputController {
30
30
  Boolean(
31
31
  this.ctx.loadingAnimation ||
32
32
  this.ctx.session.isStreaming ||
33
- this.ctx.session.isCompacting ||
34
33
  this.ctx.session.isGeneratingHandoff ||
35
34
  this.ctx.session.isBashRunning ||
36
35
  this.ctx.session.isPythonRunning ||
37
- this.ctx.autoCompactionLoader ||
38
- this.ctx.retryLoader ||
39
- this.ctx.autoCompactionEscapeHandler ||
40
36
  this.ctx.retryEscapeHandler,
41
37
  );
42
38
  this.ctx.editor.onEscape = () => {
@@ -270,16 +266,6 @@ export class InputController {
270
266
  }
271
267
  }
272
268
 
273
- // Queue input during compaction
274
- if (this.ctx.session.isCompacting) {
275
- if (this.ctx.pendingImages.length > 0) {
276
- this.ctx.showStatus("Compaction in progress. Retry after it completes to send images.");
277
- return;
278
- }
279
- this.ctx.queueCompactionMessage(text, "steer");
280
- return;
281
- }
282
-
283
269
  // If streaming, use prompt() with steer behavior
284
270
  // This handles extension commands (execute immediately), prompt template expansion, and queueing
285
271
  if (this.ctx.session.isStreaming) {
@@ -365,11 +351,6 @@ export class InputController {
365
351
  const text = this.ctx.editor.getText().trim();
366
352
  if (!text) return;
367
353
 
368
- if (this.ctx.session.isCompacting) {
369
- this.ctx.queueCompactionMessage(text, "followUp");
370
- return;
371
- }
372
-
373
354
  if (this.ctx.session.isStreaming) {
374
355
  this.ctx.editor.addToHistory(text);
375
356
  this.ctx.editor.setText("");
@@ -427,10 +408,6 @@ export class InputController {
427
408
  this.ctx.loadingAnimation.stop();
428
409
  this.ctx.loadingAnimation = undefined;
429
410
  }
430
- if (this.ctx.autoCompactionLoader) {
431
- this.ctx.autoCompactionLoader.stop();
432
- this.ctx.autoCompactionLoader = undefined;
433
- }
434
411
  if (this.ctx.retryLoader) {
435
412
  this.ctx.retryLoader.stop();
436
413
  this.ctx.retryLoader = undefined;
@@ -179,10 +179,6 @@ export class SelectorController {
179
179
 
180
180
  switch (id) {
181
181
  // Session-managed settings (not in SettingsManager)
182
- case "autoCompact":
183
- this.ctx.session.setAutoCompactionEnabled(value as boolean);
184
- this.ctx.statusLine.setAutoCompactEnabled(value as boolean);
185
- break;
186
182
  case "steeringMode":
187
183
  this.ctx.session.setSteeringMode(value as "all" | "one-at-a-time");
188
184
  break;
@@ -536,7 +532,6 @@ export class SelectorController {
536
532
 
537
533
  // Clear UI state
538
534
  this.ctx.pendingMessagesContainer.clear();
539
- this.ctx.compactionQueuedMessages = [];
540
535
  this.ctx.streamingComponent = undefined;
541
536
  this.ctx.streamingMessage = undefined;
542
537
  this.ctx.pendingTools.clear();
@@ -21,7 +21,6 @@ import chalk from "chalk";
21
21
  import { KeybindingsManager } from "../config/keybindings";
22
22
  import { type Settings, settings } from "../config/settings";
23
23
  import type { ExtensionUIContext, ExtensionUIDialogOptions } from "../extensibility/extensions";
24
- import type { CompactOptions } from "../extensibility/extensions/types";
25
24
  import { BUILTIN_SLASH_COMMANDS, loadSlashCommands } from "../extensibility/slash-commands";
26
25
  import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
27
26
  import { HistoryStorage } from "../session/history-storage";
@@ -50,7 +49,7 @@ import { InputController } from "./controllers/input-controller";
50
49
  import { MCPCommandController } from "./controllers/mcp-command-controller";
51
50
  import { SelectorController } from "./controllers/selector-controller";
52
51
  import { SSHCommandController } from "./controllers/ssh-command-controller";
53
- import type { CompactionQueuedMessage, InteractiveModeContext, TodoItem } from "./types";
52
+ import type { InteractiveModeContext, TodoItem } from "./types";
54
53
  import { UiHelpers } from "./utils/ui-helpers";
55
54
 
56
55
  const TODO_FILE_NAME = "todos.json";
@@ -98,7 +97,6 @@ export class InteractiveMode implements InteractiveModeContext {
98
97
  todoItems: TodoItem[] = [];
99
98
  hideThinkingBlock = false;
100
99
  pendingImages: ImageContent[] = [];
101
- compactionQueuedMessages: CompactionQueuedMessage[] = [];
102
100
  pendingTools = new Map<string, ToolExecutionHandle>();
103
101
  pendingBashComponents: BashExecutionComponent[] = [];
104
102
  bashComponent: BashExecutionComponent | undefined = undefined;
@@ -108,11 +106,7 @@ export class InteractiveMode implements InteractiveModeContext {
108
106
  streamingComponent: AssistantMessageComponent | undefined = undefined;
109
107
  streamingMessage: AssistantMessage | undefined = undefined;
110
108
  loadingAnimation: Loader | undefined = undefined;
111
- autoCompactionLoader: Loader | undefined = undefined;
112
109
  retryLoader: Loader | undefined = undefined;
113
- #pendingWorkingMessage: string | undefined;
114
- readonly #defaultWorkingMessage = `Working… (esc to interrupt)`;
115
- autoCompactionEscapeHandler?: () => void;
116
110
  retryEscapeHandler?: () => void;
117
111
  unsubscribe?: () => void;
118
112
  onInputCallback?: (input: { text: string; images?: ImageContent[] }) => void;
@@ -130,6 +124,8 @@ export class InteractiveMode implements InteractiveModeContext {
130
124
 
131
125
  #pendingSlashCommands: SlashCommand[] = [];
132
126
  #cleanupUnsubscribe?: () => void;
127
+ #pendingWorkingMessage: string | undefined = undefined;
128
+ #defaultWorkingMessage = "Working…";
133
129
  readonly #version: string;
134
130
  readonly #changelogMarkdown: string | undefined;
135
131
  readonly lspServers:
@@ -198,7 +194,6 @@ export class InteractiveMode implements InteractiveModeContext {
198
194
  this.editorContainer = new Container();
199
195
  this.editorContainer.addChild(this.editor);
200
196
  this.statusLine = new StatusLineComponent(session);
201
- this.statusLine.setAutoCompactEnabled(session.autoCompactionEnabled);
202
197
 
203
198
  this.hideThinkingBlock = settings.get("hideThinkingBlock");
204
199
 
@@ -622,14 +617,6 @@ export class InteractiveMode implements InteractiveModeContext {
622
617
  this.#uiHelpers.updatePendingMessagesDisplay();
623
618
  }
624
619
 
625
- queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
626
- this.#uiHelpers.queueCompactionMessage(text, mode);
627
- }
628
-
629
- flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void> {
630
- return this.#uiHelpers.flushCompactionQueue(options);
631
- }
632
-
633
620
  flushPendingBashComponents(): void {
634
621
  this.#uiHelpers.flushPendingBashComponents();
635
622
  }
@@ -737,18 +724,10 @@ export class InteractiveMode implements InteractiveModeContext {
737
724
  await controller.handle(text);
738
725
  }
739
726
 
740
- handleCompactCommand(customInstructions?: string): Promise<void> {
741
- return this.#commandController.handleCompactCommand(customInstructions);
742
- }
743
-
744
727
  handleHandoffCommand(customInstructions?: string): Promise<void> {
745
728
  return this.#commandController.handleHandoffCommand(customInstructions);
746
729
  }
747
730
 
748
- executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<void> {
749
- return this.#commandController.executeCompaction(customInstructionsOrOptions, isAuto);
750
- }
751
-
752
731
  openInBrowser(urlOrPath: string): void {
753
732
  this.#commandController.openInBrowser(urlOrPath);
754
733
  }
@@ -82,14 +82,6 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
82
82
  shutdown: () => {},
83
83
  getContextUsage: () => session.getContextUsage(),
84
84
  getSystemPrompt: () => session.systemPrompt,
85
- compact: async instructionsOrOptions => {
86
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
87
- const options =
88
- instructionsOrOptions && typeof instructionsOrOptions === "object"
89
- ? instructionsOrOptions
90
- : undefined;
91
- await session.compact(instructions, options);
92
- },
93
85
  },
94
86
  // ExtensionCommandContextActions - commands invokable via prompt("/command")
95
87
  {
@@ -117,14 +109,6 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
117
109
  reload: async () => {
118
110
  await session.reload();
119
111
  },
120
- compact: async instructionsOrOptions => {
121
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
122
- const options =
123
- instructionsOrOptions && typeof instructionsOrOptions === "object"
124
- ? instructionsOrOptions
125
- : undefined;
126
- await session.compact(instructions, options);
127
- },
128
112
  },
129
113
  // No UI context
130
114
  );
@@ -8,7 +8,6 @@ import type { ImageContent } from "@nghyane/arcane-ai";
8
8
  import { ptree, readJsonl } from "@nghyane/arcane-utils";
9
9
  import type { BashResult } from "../../exec/bash-executor";
10
10
  import type { SessionStats } from "../../session/agent-session";
11
- import type { CompactionResult } from "../../session/compaction";
12
11
  import type { RpcCommand, RpcResponse, RpcSessionState } from "./rpc-types";
13
12
 
14
13
  /** Distributive Omit that works with union types */
@@ -332,21 +331,6 @@ export class RpcClient {
332
331
  await this.#send({ type: "set_follow_up_mode", mode });
333
332
  }
334
333
 
335
- /**
336
- * Compact session context.
337
- */
338
- async compact(customInstructions?: string): Promise<CompactionResult> {
339
- const response = await this.#send({ type: "compact", customInstructions });
340
- return this.#getData(response);
341
- }
342
-
343
- /**
344
- * Set auto-compaction enabled/disabled.
345
- */
346
- async setAutoCompaction(enabled: boolean): Promise<void> {
347
- await this.#send({ type: "set_auto_compaction", enabled });
348
- }
349
-
350
334
  /**
351
335
  * Set auto-retry enabled/disabled.
352
336
  */
@@ -358,14 +358,6 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
358
358
  },
359
359
  getContextUsage: () => session.getContextUsage(),
360
360
  getSystemPrompt: () => session.systemPrompt,
361
- compact: async instructionsOrOptions => {
362
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
363
- const options =
364
- instructionsOrOptions && typeof instructionsOrOptions === "object"
365
- ? instructionsOrOptions
366
- : undefined;
367
- await session.compact(instructions, options);
368
- },
369
361
  },
370
362
  // ExtensionCommandContextActions - commands invokable via prompt("/command")
371
363
  {
@@ -394,14 +386,6 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
394
386
  reload: async () => {
395
387
  await session.reload();
396
388
  },
397
- compact: async instructionsOrOptions => {
398
- const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined;
399
- const options =
400
- instructionsOrOptions && typeof instructionsOrOptions === "object"
401
- ? instructionsOrOptions
402
- : undefined;
403
- await session.compact(instructions, options);
404
- },
405
389
  },
406
390
  new RpcExtensionUIContext(pendingExtensionRequests, output),
407
391
  );
@@ -479,14 +463,12 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
479
463
  model: session.model,
480
464
  thinkingLevel: session.thinkingLevel,
481
465
  isStreaming: session.isStreaming,
482
- isCompacting: session.isCompacting,
483
466
  steeringMode: session.steeringMode,
484
467
  followUpMode: session.followUpMode,
485
468
  interruptMode: session.interruptMode,
486
469
  sessionFile: session.sessionFile,
487
470
  sessionId: session.sessionId,
488
471
  sessionName: session.sessionName,
489
- autoCompactionEnabled: session.autoCompactionEnabled,
490
472
  messageCount: session.messages.length,
491
473
  queuedMessageCount: session.queuedMessageCount,
492
474
  };
@@ -556,20 +538,6 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
556
538
  return success(id, "set_interrupt_mode");
557
539
  }
558
540
 
559
- // =================================================================
560
- // Compaction
561
- // =================================================================
562
-
563
- case "compact": {
564
- const result = await session.compact(command.customInstructions);
565
- return success(id, "compact", result);
566
- }
567
-
568
- case "set_auto_compaction": {
569
- session.setAutoCompactionEnabled(command.enabled);
570
- return success(id, "set_auto_compaction");
571
- }
572
-
573
541
  // =================================================================
574
542
  // Retry
575
543
  // =================================================================
@@ -8,7 +8,6 @@ import type { AgentMessage, ThinkingLevel } from "@nghyane/arcane-agent";
8
8
  import type { ImageContent, Model } from "@nghyane/arcane-ai";
9
9
  import type { BashResult } from "../../exec/bash-executor";
10
10
  import type { SessionStats } from "../../session/agent-session";
11
- import type { CompactionResult } from "../../session/compaction";
12
11
 
13
12
  // ============================================================================
14
13
  // RPC Commands (stdin)
@@ -40,10 +39,6 @@ export type RpcCommand =
40
39
  | { id?: string; type: "set_follow_up_mode"; mode: "all" | "one-at-a-time" }
41
40
  | { id?: string; type: "set_interrupt_mode"; mode: "immediate" | "wait" }
42
41
 
43
- // Compaction
44
- | { id?: string; type: "compact"; customInstructions?: string }
45
- | { id?: string; type: "set_auto_compaction"; enabled: boolean }
46
-
47
42
  // Retry
48
43
  | { id?: string; type: "set_auto_retry"; enabled: boolean }
49
44
  | { id?: string; type: "abort_retry" }
@@ -72,14 +67,12 @@ export interface RpcSessionState {
72
67
  model?: Model;
73
68
  thinkingLevel: ThinkingLevel;
74
69
  isStreaming: boolean;
75
- isCompacting: boolean;
76
70
  steeringMode: "all" | "one-at-a-time";
77
71
  followUpMode: "all" | "one-at-a-time";
78
72
  interruptMode: "immediate" | "wait";
79
73
  sessionFile?: string;
80
74
  sessionId: string;
81
75
  sessionName?: string;
82
- autoCompactionEnabled: boolean;
83
76
  messageCount: number;
84
77
  queuedMessageCount: number;
85
78
  }
@@ -140,8 +133,6 @@ export type RpcResponse =
140
133
  | { id?: string; type: "response"; command: "set_interrupt_mode"; success: true }
141
134
 
142
135
  // Compaction
143
- | { id?: string; type: "response"; command: "compact"; success: true; data: CompactionResult }
144
- | { id?: string; type: "response"; command: "set_auto_compaction"; success: true }
145
136
 
146
137
  // Retry
147
138
  | { id?: string; type: "response"; command: "set_auto_retry"; success: true }
@@ -4,7 +4,6 @@ import type { Component, Container, Loader, Spacer, Text, TUI } from "@nghyane/a
4
4
  import type { KeybindingsManager } from "../config/keybindings";
5
5
  import type { Settings } from "../config/settings";
6
6
  import type { ExtensionUIContext, ExtensionUIDialogOptions } from "../extensibility/extensions";
7
- import type { CompactOptions } from "../extensibility/extensions/types";
8
7
  import type { MCPManager } from "../mcp";
9
8
  import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
10
9
  import type { HistoryStorage } from "../session/history-storage";
@@ -20,11 +19,6 @@ import type { PythonExecutionComponent } from "./components/python-execution";
20
19
  import type { StatusLineComponent } from "./components/status-line";
21
20
  import type { ToolExecutionHandle } from "./components/tool-execution";
22
21
 
23
- export type CompactionQueuedMessage = {
24
- text: string;
25
- mode: "steer" | "followUp";
26
- };
27
-
28
22
  export type TodoItem = {
29
23
  id: string;
30
24
  content: string;
@@ -60,7 +54,6 @@ export interface InteractiveModeContext {
60
54
  todoExpanded: boolean;
61
55
  hideThinkingBlock: boolean;
62
56
  pendingImages: ImageContent[];
63
- compactionQueuedMessages: CompactionQueuedMessage[];
64
57
  pendingTools: Map<string, ToolExecutionHandle>;
65
58
  pendingBashComponents: BashExecutionComponent[];
66
59
  bashComponent: BashExecutionComponent | undefined;
@@ -70,9 +63,8 @@ export interface InteractiveModeContext {
70
63
  streamingComponent: AssistantMessageComponent | undefined;
71
64
  streamingMessage: AssistantMessage | undefined;
72
65
  loadingAnimation: Loader | undefined;
73
- autoCompactionLoader: Loader | undefined;
74
66
  retryLoader: Loader | undefined;
75
- autoCompactionEscapeHandler?: () => void;
67
+ handoffLoader?: Loader;
76
68
  retryEscapeHandler?: () => void;
77
69
  unsubscribe?: () => void;
78
70
  onInputCallback?: (input: { text: string; images?: ImageContent[] }) => void;
@@ -108,8 +100,6 @@ export interface InteractiveModeContext {
108
100
  showNewVersionNotification(newVersion: string): void;
109
101
  clearEditor(): void;
110
102
  updatePendingMessagesDisplay(): void;
111
- queueCompactionMessage(text: string, mode: "steer" | "followUp"): void;
112
- flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void>;
113
103
  flushPendingBashComponents(): void;
114
104
  setWorkingMessage(message?: string): void;
115
105
  applyPendingWorkingMessage(): void;
@@ -145,11 +135,9 @@ export interface InteractiveModeContext {
145
135
  handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void>;
146
136
  handleMCPCommand(text: string): Promise<void>;
147
137
  handleSSHCommand(text: string): Promise<void>;
148
- handleCompactCommand(customInstructions?: string): Promise<void>;
149
138
  handleHandoffCommand(customInstructions?: string): Promise<void>;
150
139
  handleMoveCommand(targetPath: string): Promise<void>;
151
140
  handleMemoryCommand(text: string): Promise<void>;
152
- executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<void>;
153
141
  openInBrowser(urlOrPath: string): void;
154
142
  refreshSlashCommandState(cwd?: string): Promise<void>;
155
143
 
@@ -6,7 +6,6 @@ import { settings } from "../../config/settings";
6
6
  import { AssistantMessageComponent } from "../../modes/components/assistant-message";
7
7
  import { BashExecutionComponent } from "../../modes/components/bash-execution";
8
8
  import { BranchSummaryMessageComponent } from "../../modes/components/branch-summary-message";
9
- import { CompactionSummaryMessageComponent } from "../../modes/components/compaction-summary-message";
10
9
  import { ContextGroupComponent } from "../../modes/components/context-group";
11
10
  import { CustomMessageComponent } from "../../modes/components/custom-message";
12
11
  import { DynamicBorder } from "../../modes/components/dynamic-border";
@@ -14,7 +13,7 @@ import { PythonExecutionComponent } from "../../modes/components/python-executio
14
13
  import { SkillMessageComponent } from "../../modes/components/skill-message";
15
14
  import { ToolExecutionComponent } from "../../modes/components/tool-execution";
16
15
  import { UserMessageComponent } from "../../modes/components/user-message";
17
- import type { CompactionQueuedMessage, InteractiveModeContext } from "../../modes/types";
16
+ import type { InteractiveModeContext } from "../../modes/types";
18
17
  import { type CustomMessage, SKILL_PROMPT_MESSAGE_TYPE, type SkillPromptDetails } from "../../session/messages";
19
18
  import type { SessionContext } from "../../session/session-manager";
20
19
  import { formatBytes } from "../../session/streaming-output";
@@ -113,13 +112,6 @@ export class UiHelpers {
113
112
  }
114
113
  break;
115
114
  }
116
- case "compactionSummary": {
117
- this.ctx.chatContainer.addChild(new Spacer(1));
118
- const component = new CompactionSummaryMessageComponent(message);
119
- component.setExpanded(this.ctx.toolOutputExpanded);
120
- this.ctx.chatContainer.addChild(component);
121
- break;
122
- }
123
115
  case "branchSummary": {
124
116
  this.ctx.chatContainer.addChild(new Spacer(1));
125
117
  const component = new BranchSummaryMessageComponent(message);
@@ -176,7 +168,7 @@ export class UiHelpers {
176
168
  }
177
169
 
178
170
  /**
179
- * Render session context to chat. Used for initial load and rebuild after compaction.
171
+ * Render session context to chat. Used for initial load and rebuild after handoff.
180
172
  * @param sessionContext Session context to render
181
173
  * @param options.updateFooter Update footer state
182
174
  * @param options.populateHistory Add user messages to editor history
@@ -294,19 +286,6 @@ export class UiHelpers {
294
286
  updateFooter: true,
295
287
  populateHistory: true,
296
288
  });
297
-
298
- // Show compaction info if session was compacted
299
- const allEntries = this.ctx.sessionManager.getEntries();
300
- let compactionCount = 0;
301
- for (const entry of allEntries) {
302
- if (entry.type === "compaction") {
303
- compactionCount++;
304
- }
305
- }
306
- if (compactionCount > 0) {
307
- const times = compactionCount === 1 ? "1 time" : `${compactionCount} times`;
308
- this.ctx.showStatus(`Session compacted ${times}`);
309
- }
310
289
  }
311
290
 
312
291
  clearEditor(): void {
@@ -363,21 +342,11 @@ export class UiHelpers {
363
342
  for (const message of queuedMessages.steering) {
364
343
  steeringMessages.push({ message, label: "Steer" });
365
344
  }
366
- for (const entry of this.ctx.compactionQueuedMessages as CompactionQueuedMessage[]) {
367
- if (entry.mode === "steer") {
368
- steeringMessages.push({ message: entry.text, label: "Steer" });
369
- }
370
- }
371
345
 
372
346
  const followUpMessages: Array<{ message: string; label: string }> = [];
373
347
  for (const message of queuedMessages.followUp) {
374
348
  followUpMessages.push({ message, label: "Follow-up" });
375
349
  }
376
- for (const entry of this.ctx.compactionQueuedMessages as CompactionQueuedMessage[]) {
377
- if (entry.mode === "followUp") {
378
- followUpMessages.push({ message: entry.text, label: "Follow-up" });
379
- }
380
- }
381
350
 
382
351
  const allMessages = [...steeringMessages, ...followUpMessages];
383
352
  if (allMessages.length > 0) {
@@ -392,14 +361,6 @@ export class UiHelpers {
392
361
  }
393
362
  }
394
363
 
395
- queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
396
- this.ctx.compactionQueuedMessages.push({ text, mode } as CompactionQueuedMessage);
397
- this.ctx.editor.addToHistory(text);
398
- this.ctx.editor.setText("");
399
- this.ctx.updatePendingMessagesDisplay();
400
- this.ctx.showStatus("Queued message for after compaction");
401
- }
402
-
403
364
  isKnownSlashCommand(text: string): boolean {
404
365
  if (!text.startsWith("/")) return false;
405
366
  const spaceIndex = text.indexOf(" ");
@@ -419,83 +380,6 @@ export class UiHelpers {
419
380
  return this.ctx.fileSlashCommands.has(commandName);
420
381
  }
421
382
 
422
- async flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void> {
423
- if (this.ctx.compactionQueuedMessages.length === 0) {
424
- return;
425
- }
426
-
427
- const queuedMessages = [...(this.ctx.compactionQueuedMessages as CompactionQueuedMessage[])];
428
- this.ctx.compactionQueuedMessages = [] as CompactionQueuedMessage[];
429
- this.ctx.updatePendingMessagesDisplay();
430
-
431
- const restoreQueue = (error: unknown) => {
432
- this.ctx.session.clearQueue();
433
- this.ctx.compactionQueuedMessages = queuedMessages;
434
- this.ctx.updatePendingMessagesDisplay();
435
- this.ctx.showError(
436
- `Failed to send queued message${queuedMessages.length > 1 ? "s" : ""}: ${
437
- error instanceof Error ? error.message : String(error)
438
- }`,
439
- );
440
- };
441
-
442
- try {
443
- if (options?.willRetry) {
444
- for (const message of queuedMessages) {
445
- if (this.ctx.isKnownSlashCommand(message.text)) {
446
- await this.ctx.session.prompt(message.text);
447
- } else if (message.mode === "followUp") {
448
- await this.ctx.session.followUp(message.text);
449
- } else {
450
- await this.ctx.session.steer(message.text);
451
- }
452
- }
453
- this.ctx.updatePendingMessagesDisplay();
454
- return;
455
- }
456
-
457
- let firstPromptIndex = -1;
458
- for (let i = 0; i < queuedMessages.length; i++) {
459
- if (!this.ctx.isKnownSlashCommand(queuedMessages[i].text)) {
460
- firstPromptIndex = i;
461
- break;
462
- }
463
- }
464
- if (firstPromptIndex === -1) {
465
- for (const message of queuedMessages) {
466
- await this.ctx.session.prompt(message.text);
467
- }
468
- return;
469
- }
470
-
471
- const preCommands = queuedMessages.slice(0, firstPromptIndex);
472
- const firstPrompt = queuedMessages[firstPromptIndex];
473
- const rest = queuedMessages.slice(firstPromptIndex + 1);
474
-
475
- for (const message of preCommands) {
476
- await this.ctx.session.prompt(message.text);
477
- }
478
-
479
- const promptPromise = this.ctx.session.prompt(firstPrompt.text).catch((error: unknown) => {
480
- restoreQueue(error);
481
- });
482
-
483
- for (const message of rest) {
484
- if (this.ctx.isKnownSlashCommand(message.text)) {
485
- await this.ctx.session.prompt(message.text);
486
- } else if (message.mode === "followUp") {
487
- await this.ctx.session.followUp(message.text);
488
- } else {
489
- await this.ctx.session.steer(message.text);
490
- }
491
- }
492
- this.ctx.updatePendingMessagesDisplay();
493
- void promptPromise;
494
- } catch (error) {
495
- restoreQueue(error);
496
- }
497
- }
498
-
499
383
  /** Move pending bash components from pending area to chat */
500
384
  flushPendingBashComponents(): void {
501
385
  for (const component of this.ctx.pendingBashComponents) {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: librarian
3
3
  description: "Repository exploration agent for cross-repo codebase understanding"
4
- tools: github, fetch, web_search, search_code
4
+ tools: github_fs, fetch, web_search, search_code
5
5
  model: arcane/fast
6
6
  thinking-level: minimal
7
7
  ---
@@ -9,7 +9,7 @@ thinking-level: minimal
9
9
  <role>You are the Librarian, a specialized codebase understanding agent that helps answer questions about large, complex codebases across repositories. You are running as a subagent inside an AI coding system — your output goes directly to the main coding agent, not the end user. The main agent invokes you when it needs deep, multi-repository codebase understanding: architecture analysis, cross-repo code tracing, implementation discovery, and history exploration.</role>
10
10
 
11
11
  <directives>
12
- - Use the github tool for all repository operations — it handles auth, rate limits, and caching
12
+ - Use the github_fs tool for all repository file and directory operations — it handles auth, rate limits, and caching
13
13
  - Parallelize tool calls when investigating multiple repos or files
14
14
  - Read files thoroughly — skim causes missed context
15
15
  - Use web_search or fetch only when GitHub API is insufficient
@@ -17,12 +17,9 @@ thinking-level: minimal
17
17
  </directives>
18
18
 
19
19
  <instruction>
20
- Use the github tool for all GitHub API operations:
21
- - `github({ action: "get_file", ... })` for reading remote files
22
- - `github({ action: "get_tree", ... })` for listing directories
23
- - `github({ action: "get_issue", ... })` for reading issues with all comments
24
- - `github({ action: "get_pull", ... })` for PR details and diffs
25
- - `github({ action: "list_commits", ... })` for commit history
20
+ Use the github_fs tool for all GitHub file/tree operations:
21
+ - `github_fs({ action: "get_file", ... })` for reading remote files
22
+ - `github_fs({ action: "get_tree", ... })` for listing directories
26
23
 
27
24
  Use search_code to find code across public GitHub repositories via grep.app:
28
25
  - `search_code({ query: "pattern" })` for broad cross-repo search
@@ -36,10 +33,9 @@ Use search_code to find code across public GitHub repositories via grep.app:
36
33
  <procedure>
37
34
  1. Identify target repositories
38
35
  2. Map structure — get_tree for layout, get_file for README
39
- 3. Locate targets — search_code for patterns across repos, github get_file for specific files
36
+ 3. Locate targets — search_code for patterns across repos, github_fs get_file for specific files
40
37
  4. Read relevant code — follow imports, trace call chains
41
- 5. Check history if needed list_commits for evolution context
42
- 6. Synthesize findings into a comprehensive answer
38
+ 5. Synthesize findings into a comprehensive answer
43
39
  </procedure>
44
40
 
45
41
  <output>
@@ -48,7 +44,6 @@ Format as markdown. Include:
48
44
  - Key files and their roles (with paths)
49
45
  - Code flow / call chains (if tracing)
50
46
  - Relevant code snippets (brief, targeted)
51
- - Commit history / evolution (if relevant)
52
47
 
53
48
  Be comprehensive and direct. No filler.
54
49
  </output>
package/src/sdk.ts CHANGED
@@ -442,21 +442,6 @@ function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory {
442
442
  api.on("session_shutdown", async (_event, ctx) =>
443
443
  runOnSession({ reason: "shutdown", previousSessionFile: undefined }, ctx),
444
444
  );
445
- api.on("auto_compaction_start", async (event, ctx) =>
446
- runOnSession({ reason: "auto_compaction_start", trigger: event.reason }, ctx),
447
- );
448
- api.on("auto_compaction_end", async (event, ctx) =>
449
- runOnSession(
450
- {
451
- reason: "auto_compaction_end",
452
- result: event.result,
453
- aborted: event.aborted,
454
- willRetry: event.willRetry,
455
- errorMessage: event.errorMessage,
456
- },
457
- ctx,
458
- ),
459
- );
460
445
  api.on("auto_retry_start", async (event, ctx) =>
461
446
  runOnSession(
462
447
  {