@nghyane/arcane 0.1.12 → 0.1.14

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 (333) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/package.json +21 -70
  3. package/scripts/format-prompts.ts +1 -3
  4. package/src/cli/args.ts +2 -7
  5. package/src/cli/config-cli.ts +1 -1
  6. package/src/cli/plugin-cli.ts +1 -1
  7. package/src/cli/setup-cli.ts +1 -1
  8. package/src/cli/update-cli.ts +1 -1
  9. package/src/cli/web-search-cli.ts +1 -1
  10. package/src/cli.ts +0 -1
  11. package/src/commands/config.ts +1 -1
  12. package/src/commands/grep.ts +1 -1
  13. package/src/commands/jupyter.ts +1 -1
  14. package/src/commands/plugin.ts +1 -1
  15. package/src/commands/setup.ts +1 -1
  16. package/src/commands/shell.ts +1 -1
  17. package/src/commands/ssh.ts +1 -1
  18. package/src/commands/stats.ts +1 -1
  19. package/src/commands/update.ts +1 -1
  20. package/src/config/model-registry.ts +3 -4
  21. package/src/config/model-resolver.ts +36 -9
  22. package/src/config/prompt-templates.ts +1 -9
  23. package/src/config/settings-schema.ts +32 -88
  24. package/src/config/settings.ts +3 -4
  25. package/src/debug/index.ts +1 -1
  26. package/src/debug/log-formatting.ts +1 -1
  27. package/src/debug/log-viewer.ts +2 -2
  28. package/src/discovery/helpers.ts +13 -3
  29. package/src/exa/company.ts +2 -7
  30. package/src/exa/index.ts +1 -35
  31. package/src/exa/linkedin.ts +2 -7
  32. package/src/exa/mcp-client.ts +21 -11
  33. package/src/exa/render.ts +30 -190
  34. package/src/exa/researcher.ts +2 -12
  35. package/src/exa/search.ts +5 -25
  36. package/src/exa/types.ts +3 -3
  37. package/src/exec/bash-executor.ts +2 -1
  38. package/src/exec/non-interactive-env.ts +43 -0
  39. package/src/export/html/index.ts +1 -1
  40. package/src/extensibility/custom-tools/loader.ts +1 -1
  41. package/src/extensibility/custom-tools/types.ts +5 -1
  42. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  43. package/src/extensibility/extensions/runner.ts +1 -1
  44. package/src/extensibility/extensions/types.ts +1 -1
  45. package/src/extensibility/extensions/wrapper.ts +7 -15
  46. package/src/extensibility/hooks/runner.ts +1 -1
  47. package/src/extensibility/hooks/types.ts +1 -1
  48. package/src/extensibility/plugins/doctor.ts +1 -1
  49. package/src/index.ts +13 -13
  50. package/src/lsp/index.ts +77 -24
  51. package/src/lsp/render.ts +34 -583
  52. package/src/lsp/types.ts +3 -3
  53. package/src/lsp/utils.ts +1 -1
  54. package/src/main.ts +1 -1
  55. package/src/mcp/tool-bridge.ts +1 -24
  56. package/src/modes/components/assistant-message.ts +7 -7
  57. package/src/modes/components/bash-execution.ts +48 -113
  58. package/src/modes/components/bordered-loader.ts +1 -1
  59. package/src/modes/components/branch-summary-message.ts +13 -10
  60. package/src/modes/components/compaction-summary-message.ts +14 -13
  61. package/src/modes/components/context-group.ts +106 -0
  62. package/src/modes/components/custom-message.ts +4 -5
  63. package/src/modes/components/diff.ts +2 -2
  64. package/src/modes/components/dynamic-border.ts +1 -1
  65. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  66. package/src/modes/components/extensions/extension-list.ts +1 -1
  67. package/src/modes/components/extensions/inspector-panel.ts +8 -3
  68. package/src/modes/components/footer.ts +2 -2
  69. package/src/modes/components/history-search.ts +1 -1
  70. package/src/modes/components/hook-editor.ts +1 -1
  71. package/src/modes/components/hook-input.ts +1 -1
  72. package/src/modes/components/hook-message.ts +4 -5
  73. package/src/modes/components/hook-selector.ts +1 -1
  74. package/src/modes/components/index.ts +0 -2
  75. package/src/modes/components/keybinding-hints.ts +1 -1
  76. package/src/modes/components/login-dialog.ts +1 -1
  77. package/src/modes/components/mcp-add-wizard.ts +1 -1
  78. package/src/modes/components/model-selector.ts +1 -1
  79. package/src/modes/components/oauth-selector.ts +1 -1
  80. package/src/modes/components/plugin-settings.ts +1 -1
  81. package/src/modes/components/python-execution.ts +49 -92
  82. package/src/modes/components/queue-mode-selector.ts +1 -1
  83. package/src/modes/components/session-selector.ts +1 -1
  84. package/src/modes/components/settings-defs.ts +5 -10
  85. package/src/modes/components/settings-selector.ts +1 -1
  86. package/src/modes/components/show-images-selector.ts +1 -1
  87. package/src/modes/components/skill-message.ts +4 -4
  88. package/src/modes/components/status-line/segments.ts +2 -2
  89. package/src/modes/components/status-line/separators.ts +1 -1
  90. package/src/modes/components/status-line-segment-editor.ts +1 -1
  91. package/src/modes/components/status-line.ts +1 -1
  92. package/src/modes/components/theme-selector.ts +1 -1
  93. package/src/modes/components/thinking-selector.ts +1 -1
  94. package/src/modes/components/todo-display.ts +2 -4
  95. package/src/modes/components/todo-reminder.ts +4 -4
  96. package/src/modes/components/tool-execution.ts +118 -440
  97. package/src/modes/components/tool-image-display.ts +107 -0
  98. package/src/modes/components/tree-selector.ts +2 -2
  99. package/src/modes/components/ttsr-notification.ts +4 -17
  100. package/src/modes/components/user-message-selector.ts +1 -1
  101. package/src/modes/components/user-message.ts +9 -10
  102. package/src/modes/components/welcome.ts +1 -1
  103. package/src/modes/controllers/command-controller.ts +1 -1
  104. package/src/modes/controllers/event-controller.ts +58 -187
  105. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  106. package/src/modes/controllers/input-controller.ts +3 -1
  107. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  108. package/src/modes/controllers/selector-controller.ts +3 -26
  109. package/src/modes/controllers/ssh-command-controller.ts +1 -1
  110. package/src/modes/interactive-mode.ts +3 -7
  111. package/src/modes/print-mode.ts +5 -5
  112. package/src/modes/rpc/rpc-mode.ts +1 -1
  113. package/src/modes/types.ts +1 -2
  114. package/src/modes/utils/ui-helpers.ts +34 -32
  115. package/src/patch/edit-tool.ts +742 -0
  116. package/src/patch/index.ts +32 -898
  117. package/src/patch/schemas.ts +208 -0
  118. package/src/patch/shared.ts +83 -151
  119. package/src/prompts/agents/explore.md +22 -37
  120. package/src/prompts/agents/frontmatter.md +1 -1
  121. package/src/prompts/agents/init.md +2 -2
  122. package/src/prompts/agents/librarian.md +30 -21
  123. package/src/prompts/agents/oracle.md +9 -2
  124. package/src/prompts/agents/reviewer.md +15 -49
  125. package/src/prompts/agents/task.md +17 -9
  126. package/src/prompts/compaction/branch-summary-context.md +1 -1
  127. package/src/prompts/compaction/branch-summary-preamble.md +1 -1
  128. package/src/prompts/compaction/branch-summary.md +4 -1
  129. package/src/prompts/compaction/compaction-short-summary.md +1 -1
  130. package/src/prompts/compaction/compaction-summary-context.md +1 -1
  131. package/src/prompts/compaction/compaction-summary.md +4 -1
  132. package/src/prompts/compaction/compaction-turn-prefix.md +1 -1
  133. package/src/prompts/compaction/compaction-update-summary.md +1 -1
  134. package/src/prompts/memories/consolidation.md +1 -1
  135. package/src/prompts/memories/read_path.md +1 -1
  136. package/src/prompts/memories/stage_one_input.md +1 -1
  137. package/src/prompts/memories/stage_one_system.md +1 -1
  138. package/src/prompts/review-request.md +1 -1
  139. package/src/prompts/system/agent-creation-architect.md +1 -1
  140. package/src/prompts/system/agent-creation-user.md +1 -1
  141. package/src/prompts/system/custom-system-prompt.md +1 -1
  142. package/src/prompts/system/file-operations.md +1 -1
  143. package/src/prompts/system/subagent-system-prompt.md +2 -2
  144. package/src/prompts/system/summarization-system.md +1 -1
  145. package/src/prompts/system/system-prompt.md +163 -178
  146. package/src/prompts/system/title-system.md +1 -1
  147. package/src/prompts/system/ttsr-interrupt.md +1 -1
  148. package/src/prompts/system/verification-reminder.md +6 -0
  149. package/src/prompts/system/web-search.md +1 -1
  150. package/src/sdk.ts +0 -9
  151. package/src/session/agent-session.ts +244 -1459
  152. package/src/session/auth-storage.ts +5 -0
  153. package/src/session/model-controller.ts +406 -0
  154. package/src/session/retry-utils.ts +71 -0
  155. package/src/session/session-manager.ts +22 -186
  156. package/src/session/session-types.ts +312 -0
  157. package/src/session/stats.ts +387 -0
  158. package/src/session/streaming-edit.ts +258 -0
  159. package/src/session/ttsr.ts +213 -0
  160. package/src/slash-commands/builtin-registry.ts +0 -8
  161. package/src/ssh/connection-manager.ts +1 -0
  162. package/src/stt/recorder.ts +2 -2
  163. package/src/system-prompt.ts +1 -14
  164. package/src/task/agents.ts +7 -33
  165. package/src/task/executor.ts +50 -438
  166. package/src/task/index.ts +104 -71
  167. package/src/task/progress-tracker.ts +390 -0
  168. package/src/task/render.ts +371 -187
  169. package/src/task/subprocess-tool-registry.ts +1 -1
  170. package/src/task/types.ts +14 -47
  171. package/src/tools/ask.ts +31 -42
  172. package/src/tools/bash-interactive.ts +4 -47
  173. package/src/tools/bash-interceptor.ts +2 -2
  174. package/src/tools/bash-normalize.ts +1 -1
  175. package/src/tools/bash-skill-urls.ts +2 -2
  176. package/src/tools/bash.ts +87 -136
  177. package/src/tools/browser.ts +54 -84
  178. package/src/tools/create-tools.ts +186 -0
  179. package/src/tools/default-renderer.ts +104 -0
  180. package/src/tools/explore.ts +11 -10
  181. package/src/tools/fetch.ts +24 -114
  182. package/src/tools/find.ts +48 -132
  183. package/src/tools/gemini-image.ts +5 -15
  184. package/src/tools/github.ts +450 -0
  185. package/src/tools/grep.ts +43 -179
  186. package/src/tools/index.ts +35 -198
  187. package/src/tools/json-tree.ts +3 -3
  188. package/src/tools/librarian.ts +18 -18
  189. package/src/tools/list-limit.ts +2 -2
  190. package/src/tools/notebook.ts +35 -87
  191. package/src/tools/oracle.ts +25 -25
  192. package/src/tools/output-meta.ts +89 -4
  193. package/src/tools/output-utils.ts +2 -2
  194. package/src/tools/python.ts +86 -637
  195. package/src/tools/read.ts +36 -119
  196. package/src/tools/reviewer-tool.ts +19 -21
  197. package/src/tools/search-code.ts +128 -0
  198. package/src/tools/ssh.ts +67 -126
  199. package/src/tools/subagent-tool.ts +197 -123
  200. package/src/tools/todo-write.ts +15 -31
  201. package/src/tools/tool-errors.ts +0 -30
  202. package/src/tools/undo-edit.ts +30 -67
  203. package/src/tools/write.ts +78 -127
  204. package/src/tui/code-cell.ts +4 -4
  205. package/src/tui/file-list.ts +2 -2
  206. package/src/tui/output-block.ts +1 -1
  207. package/src/tui/status-line.ts +1 -1
  208. package/src/tui/tree-list.ts +2 -2
  209. package/src/tui/types.ts +1 -1
  210. package/src/tui/utils.ts +1 -1
  211. package/src/{tools → ui}/render-utils.ts +87 -126
  212. package/src/utils/external-editor.ts +4 -4
  213. package/src/utils/file-mentions.ts +1 -1
  214. package/src/utils/index.ts +30 -0
  215. package/src/utils/tools-manager.ts +9 -19
  216. package/src/web/github-client.ts +290 -0
  217. package/src/web/scrapers/github.ts +11 -62
  218. package/src/web/search/auth.ts +1 -3
  219. package/src/web/search/index.ts +85 -49
  220. package/src/web/search/provider.ts +11 -16
  221. package/src/web/search/providers/grep.ts +160 -0
  222. package/src/web/search/render.ts +48 -235
  223. package/src/web/search/types.ts +1 -1
  224. package/src/commands/commit.ts +0 -36
  225. package/src/commit/agentic/agent.ts +0 -311
  226. package/src/commit/agentic/fallback.ts +0 -96
  227. package/src/commit/agentic/index.ts +0 -359
  228. package/src/commit/agentic/prompts/analyze-file.md +0 -22
  229. package/src/commit/agentic/prompts/session-user.md +0 -25
  230. package/src/commit/agentic/prompts/split-confirm.md +0 -1
  231. package/src/commit/agentic/prompts/system.md +0 -38
  232. package/src/commit/agentic/state.ts +0 -69
  233. package/src/commit/agentic/tools/analyze-file.ts +0 -118
  234. package/src/commit/agentic/tools/git-file-diff.ts +0 -194
  235. package/src/commit/agentic/tools/git-hunk.ts +0 -50
  236. package/src/commit/agentic/tools/git-overview.ts +0 -84
  237. package/src/commit/agentic/tools/index.ts +0 -56
  238. package/src/commit/agentic/tools/propose-changelog.ts +0 -128
  239. package/src/commit/agentic/tools/propose-commit.ts +0 -154
  240. package/src/commit/agentic/tools/recent-commits.ts +0 -81
  241. package/src/commit/agentic/tools/split-commit.ts +0 -280
  242. package/src/commit/agentic/topo-sort.ts +0 -44
  243. package/src/commit/agentic/trivial.ts +0 -51
  244. package/src/commit/agentic/validation.ts +0 -200
  245. package/src/commit/analysis/conventional.ts +0 -165
  246. package/src/commit/analysis/index.ts +0 -4
  247. package/src/commit/analysis/scope.ts +0 -242
  248. package/src/commit/analysis/summary.ts +0 -112
  249. package/src/commit/analysis/validation.ts +0 -66
  250. package/src/commit/changelog/detect.ts +0 -37
  251. package/src/commit/changelog/generate.ts +0 -110
  252. package/src/commit/changelog/index.ts +0 -234
  253. package/src/commit/changelog/parse.ts +0 -44
  254. package/src/commit/cli.ts +0 -93
  255. package/src/commit/git/diff.ts +0 -148
  256. package/src/commit/git/errors.ts +0 -9
  257. package/src/commit/git/index.ts +0 -211
  258. package/src/commit/git/operations.ts +0 -54
  259. package/src/commit/index.ts +0 -5
  260. package/src/commit/map-reduce/index.ts +0 -64
  261. package/src/commit/map-reduce/map-phase.ts +0 -178
  262. package/src/commit/map-reduce/reduce-phase.ts +0 -145
  263. package/src/commit/map-reduce/utils.ts +0 -9
  264. package/src/commit/message.ts +0 -11
  265. package/src/commit/model-selection.ts +0 -69
  266. package/src/commit/pipeline.ts +0 -243
  267. package/src/commit/prompts/analysis-system.md +0 -148
  268. package/src/commit/prompts/analysis-user.md +0 -38
  269. package/src/commit/prompts/changelog-system.md +0 -50
  270. package/src/commit/prompts/changelog-user.md +0 -18
  271. package/src/commit/prompts/file-observer-system.md +0 -24
  272. package/src/commit/prompts/file-observer-user.md +0 -8
  273. package/src/commit/prompts/reduce-system.md +0 -50
  274. package/src/commit/prompts/reduce-user.md +0 -17
  275. package/src/commit/prompts/summary-retry.md +0 -3
  276. package/src/commit/prompts/summary-system.md +0 -38
  277. package/src/commit/prompts/summary-user.md +0 -13
  278. package/src/commit/prompts/types-description.md +0 -2
  279. package/src/commit/types.ts +0 -109
  280. package/src/commit/utils/exclusions.ts +0 -42
  281. package/src/mcp/render.ts +0 -123
  282. package/src/modes/components/agent-dashboard.ts +0 -1130
  283. package/src/modes/components/codemode-group.ts +0 -369
  284. package/src/modes/components/read-tool-group.ts +0 -119
  285. package/src/modes/components/visual-truncate.ts +0 -63
  286. package/src/prompts/system/subagent-user-prompt.md +0 -8
  287. package/src/prompts/tools/ask.md +0 -44
  288. package/src/prompts/tools/bash.md +0 -24
  289. package/src/prompts/tools/browser.md +0 -33
  290. package/src/prompts/tools/calculator.md +0 -12
  291. package/src/prompts/tools/explore.md +0 -29
  292. package/src/prompts/tools/fetch.md +0 -16
  293. package/src/prompts/tools/find.md +0 -18
  294. package/src/prompts/tools/gemini-image.md +0 -23
  295. package/src/prompts/tools/grep.md +0 -28
  296. package/src/prompts/tools/hashline.md +0 -232
  297. package/src/prompts/tools/librarian.md +0 -24
  298. package/src/prompts/tools/lsp.md +0 -28
  299. package/src/prompts/tools/oracle.md +0 -26
  300. package/src/prompts/tools/patch.md +0 -74
  301. package/src/prompts/tools/python.md +0 -66
  302. package/src/prompts/tools/read.md +0 -36
  303. package/src/prompts/tools/replace.md +0 -38
  304. package/src/prompts/tools/reviewer.md +0 -41
  305. package/src/prompts/tools/ssh.md +0 -51
  306. package/src/prompts/tools/task-summary.md +0 -28
  307. package/src/prompts/tools/task.md +0 -146
  308. package/src/prompts/tools/todo-write.md +0 -65
  309. package/src/prompts/tools/undo-edit.md +0 -7
  310. package/src/prompts/tools/web-search.md +0 -19
  311. package/src/prompts/tools/write.md +0 -18
  312. package/src/task/batch.ts +0 -102
  313. package/src/task/discovery.ts +0 -126
  314. package/src/task/parallel.ts +0 -84
  315. package/src/task/template.ts +0 -32
  316. package/src/tools/calculator.ts +0 -537
  317. package/src/tools/jtd-to-typescript.ts +0 -198
  318. package/src/tools/renderers.ts +0 -60
  319. package/src/tools/tool-result.ts +0 -86
  320. /package/src/{modes/theme → theme}/dark.json +0 -0
  321. /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
  322. /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
  323. /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
  324. /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
  325. /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  326. /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
  327. /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
  328. /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
  329. /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
  330. /package/src/{modes/theme → theme}/light.json +0 -0
  331. /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
  332. /package/src/{modes/theme → theme}/theme-schema.json +0 -0
  333. /package/src/{modes/theme → theme}/theme.ts +0 -0
@@ -0,0 +1,312 @@
1
+ import type { Agent, AgentEvent, AgentMessage, AgentTool, ThinkingLevel } from "@nghyane/arcane-agent";
2
+ import type { ImageContent, Model, TextContent, ToolChoice } from "@nghyane/arcane-ai";
3
+ import type { Rule } from "../capability/rule";
4
+ import type { ModelRegistry, ModelRole } from "../config/model-registry";
5
+ import type { PromptTemplate } from "../config/prompt-templates";
6
+ import type { Settings, SkillsSettings } from "../config/settings";
7
+ import type { TtsrManager } from "../export/ttsr";
8
+ import type { LoadedCustomCommand } from "../extensibility/custom-commands";
9
+ import type { ExtensionRunner } from "../extensibility/extensions";
10
+ import type { Skill, SkillWarning } from "../extensibility/skills";
11
+ import type { FileSlashCommand } from "../extensibility/slash-commands";
12
+ import type { SecretObfuscator } from "../secrets/obfuscator";
13
+ import type { TodoItem } from "../tools/todo-write";
14
+ import type { CompactionResult } from "./compaction";
15
+ import type { SessionManager } from "./session-manager";
16
+
17
+ export const CURRENT_SESSION_VERSION = 3;
18
+
19
+ export interface SessionHeader {
20
+ type: "session";
21
+ version?: number; // v1 sessions don't have this
22
+ id: string;
23
+ title?: string; // Auto-generated title from first message
24
+ timestamp: string;
25
+ cwd: string;
26
+ parentSession?: string;
27
+ }
28
+
29
+ export interface NewSessionOptions {
30
+ parentSession?: string;
31
+ }
32
+
33
+ export interface SessionEntryBase {
34
+ type: string;
35
+ id: string;
36
+ parentId: string | null;
37
+ timestamp: string;
38
+ }
39
+
40
+ export interface SessionMessageEntry extends SessionEntryBase {
41
+ type: "message";
42
+ message: AgentMessage;
43
+ }
44
+
45
+ export interface ThinkingLevelChangeEntry extends SessionEntryBase {
46
+ type: "thinking_level_change";
47
+ thinkingLevel: string;
48
+ }
49
+
50
+ export interface ModelChangeEntry extends SessionEntryBase {
51
+ type: "model_change";
52
+ /** Model in "provider/modelId" format */
53
+ model: string;
54
+ /** Role: "default", "fast", "oracle", etc. Undefined treated as "default" */
55
+ role?: string;
56
+ }
57
+
58
+ export interface CompactionEntry<T = unknown> extends SessionEntryBase {
59
+ type: "compaction";
60
+ summary: string;
61
+ shortSummary?: string;
62
+ firstKeptEntryId: string;
63
+ tokensBefore: number;
64
+ /** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
65
+ details?: T;
66
+ /** Hook-provided data to persist across compaction */
67
+ preserveData?: Record<string, unknown>;
68
+ /** True if generated by an extension, undefined/false if pi-generated (backward compatible) */
69
+ fromExtension?: boolean;
70
+ }
71
+
72
+ export interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {
73
+ type: "branch_summary";
74
+ fromId: string;
75
+ summary: string;
76
+ /** Extension-specific data (not sent to LLM) */
77
+ details?: T;
78
+ /** True if generated by an extension, false if pi-generated */
79
+ fromExtension?: boolean;
80
+ }
81
+
82
+ /**
83
+ * Custom entry for extensions to store extension-specific data in the session.
84
+ * Use customType to identify your extension's entries.
85
+ *
86
+ * Purpose: Persist extension state across session reloads. On reload, extensions can
87
+ * scan entries for their customType and reconstruct internal state.
88
+ *
89
+ * Does NOT participate in LLM context (ignored by buildSessionContext).
90
+ * For injecting content into context, see CustomMessageEntry.
91
+ */
92
+ export interface CustomEntry<T = unknown> extends SessionEntryBase {
93
+ type: "custom";
94
+ customType: string;
95
+ data?: T;
96
+ }
97
+
98
+ /** Label entry for user-defined bookmarks/markers on entries. */
99
+ export interface LabelEntry extends SessionEntryBase {
100
+ type: "label";
101
+ targetId: string;
102
+ label: string | undefined;
103
+ }
104
+
105
+ /** TTSR injection entry - tracks which time-traveling rules have been injected this session. */
106
+ export interface TtsrInjectionEntry extends SessionEntryBase {
107
+ type: "ttsr_injection";
108
+ /** Names of rules that were injected */
109
+ injectedRules: string[];
110
+ }
111
+
112
+ /** Session init entry - captures initial context for subagent sessions (debugging/replay). */
113
+ export interface SessionInitEntry extends SessionEntryBase {
114
+ type: "session_init";
115
+ /** Full system prompt sent to the model */
116
+ systemPrompt: string;
117
+ /** Initial task/user message */
118
+ task: string;
119
+ /** Tools available to the agent */
120
+ tools: string[];
121
+ }
122
+
123
+ /** Mode change entry - tracks agent mode transitions. */
124
+ export interface ModeChangeEntry extends SessionEntryBase {
125
+ type: "mode_change";
126
+ /** Current mode name, or "none" when exiting a mode */
127
+ mode: string;
128
+ /** Optional mode-specific data (e.g. plan file path) */
129
+ data?: Record<string, unknown>;
130
+ }
131
+
132
+ /**
133
+ * Custom message entry for extensions to inject messages into LLM context.
134
+ * Use customType to identify your extension's entries.
135
+ *
136
+ * Unlike CustomEntry, this DOES participate in LLM context.
137
+ * The content is converted to a user message in buildSessionContext().
138
+ * Use details for extension-specific metadata (not sent to LLM).
139
+ *
140
+ * display controls TUI rendering:
141
+ * - false: hidden entirely
142
+ * - true: rendered with distinct styling (different from user messages)
143
+ */
144
+ export interface CustomMessageEntry<T = unknown> extends SessionEntryBase {
145
+ type: "custom_message";
146
+ customType: string;
147
+ content: string | (TextContent | ImageContent)[];
148
+ details?: T;
149
+ display: boolean;
150
+ }
151
+
152
+ /** Session entry - has id/parentId for tree structure (returned by "read" methods in SessionManager) */
153
+ export type SessionEntry =
154
+ | SessionMessageEntry
155
+ | ThinkingLevelChangeEntry
156
+ | ModelChangeEntry
157
+ | CompactionEntry
158
+ | BranchSummaryEntry
159
+ | CustomEntry
160
+ | CustomMessageEntry
161
+ | LabelEntry
162
+ | TtsrInjectionEntry
163
+ | SessionInitEntry
164
+ | ModeChangeEntry;
165
+
166
+ /** Raw file entry (includes header) */
167
+ export type FileEntry = SessionHeader | SessionEntry;
168
+
169
+ /** Tree node for getTree() - defensive copy of session structure */
170
+ export interface SessionTreeNode {
171
+ entry: SessionEntry;
172
+ children: SessionTreeNode[];
173
+ /** Resolved label for this entry, if any */
174
+ label?: string;
175
+ }
176
+
177
+ export interface SessionContext {
178
+ messages: AgentMessage[];
179
+ thinkingLevel: string;
180
+ /** Model roles: { default: "provider/modelId", small: "provider/modelId", ... } */
181
+ models: Record<string, string>;
182
+ /** Names of TTSR rules that have been injected this session */
183
+ injectedTtsrRules: string[];
184
+ /** Active mode (e.g. "plan") or "none" if no special mode is active */
185
+ mode: string;
186
+ /** Mode-specific data from the last mode_change entry */
187
+ modeData?: Record<string, unknown>;
188
+ }
189
+
190
+ export interface SessionInfo {
191
+ path: string;
192
+ id: string;
193
+ /** Working directory where the session was started. Empty string for old sessions. */
194
+ cwd: string;
195
+ title?: string;
196
+ /** Path to the parent session (if this session was forked). */
197
+ parentSessionPath?: string;
198
+ created: Date;
199
+ modified: Date;
200
+ messageCount: number;
201
+ firstMessage: string;
202
+ allMessagesText: string;
203
+ }
204
+
205
+ // ============================================================================
206
+ // Agent Session Types (moved from agent-session.ts)
207
+ // ============================================================================
208
+
209
+ /** Session-specific events that extend the core AgentEvent */
210
+ export type AgentSessionEvent =
211
+ | AgentEvent
212
+ | { type: "auto_compaction_start"; reason: "threshold" | "overflow" }
213
+ | {
214
+ type: "auto_compaction_end";
215
+ result: CompactionResult | undefined;
216
+ aborted: boolean;
217
+ willRetry: boolean;
218
+ errorMessage?: string;
219
+ }
220
+ | { type: "auto_retry_start"; attempt: number; maxAttempts: number; delayMs: number; errorMessage: string }
221
+ | { type: "auto_retry_end"; success: boolean; attempt: number; finalError?: string }
222
+ | { type: "ttsr_triggered"; rules: Rule[] }
223
+ | { type: "todo_reminder"; todos: TodoItem[]; attempt: number; maxAttempts: number };
224
+
225
+ /** Listener function for agent session events */
226
+ export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
227
+
228
+ export interface AgentSessionConfig {
229
+ agent: Agent;
230
+ sessionManager: SessionManager;
231
+ settings: Settings;
232
+ /** Models to cycle through with Ctrl+P (from --models flag) */
233
+ scopedModels?: Array<{ model: Model; thinkingLevel: ThinkingLevel }>;
234
+ /** Prompt templates for expansion */
235
+ promptTemplates?: PromptTemplate[];
236
+ /** File-based slash commands for expansion */
237
+ slashCommands?: FileSlashCommand[];
238
+ /** Extension runner (created in main.ts with wrapped tools) */
239
+ extensionRunner?: ExtensionRunner;
240
+ /** Loaded skills (already discovered by SDK) */
241
+ skills?: Skill[];
242
+ /** Skill loading warnings (already captured by SDK) */
243
+ skillWarnings?: SkillWarning[];
244
+ /** Custom commands (TypeScript slash commands) */
245
+ customCommands?: LoadedCustomCommand[];
246
+ skillsSettings?: Required<SkillsSettings>;
247
+ /** Model registry for API key resolution and model discovery */
248
+ modelRegistry: ModelRegistry;
249
+ /** Tool registry for LSP and settings */
250
+ toolRegistry?: Map<string, AgentTool>;
251
+ /** System prompt builder that can consider tool availability */
252
+ rebuildSystemPrompt?: (toolNames: string[], tools: Map<string, AgentTool>) => Promise<string>;
253
+ /** TTSR manager for time-traveling stream rules */
254
+ ttsrManager?: TtsrManager;
255
+ /** Force X-Initiator: agent for GitHub Copilot model selections in this session. */
256
+ forceCopilotAgentInitiator?: boolean;
257
+ /** Secret obfuscator for deobfuscating streaming edit content */
258
+ obfuscator?: SecretObfuscator;
259
+ }
260
+
261
+ /** Options for AgentSession.prompt() */
262
+ export interface PromptOptions {
263
+ /** Whether to expand file-based prompt templates (default: true) */
264
+ expandPromptTemplates?: boolean;
265
+ /** Image attachments */
266
+ images?: ImageContent[];
267
+ /** When streaming, how to queue the message: "steer" (interrupt) or "followUp" (wait). */
268
+ streamingBehavior?: "steer" | "followUp";
269
+ /** Optional tool choice override for the next LLM call. */
270
+ toolChoice?: ToolChoice;
271
+ /** Mark the user message as synthetic (system-injected). */
272
+ synthetic?: boolean;
273
+ }
274
+
275
+ /** Result from cycleModel() */
276
+ export interface ModelCycleResult {
277
+ model: Model;
278
+ thinkingLevel: ThinkingLevel;
279
+ /** Whether cycling through scoped models (--models flag) or all available */
280
+ isScoped: boolean;
281
+ }
282
+
283
+ /** Result from cycleRoleModels() */
284
+ export interface RoleModelCycleResult {
285
+ model: Model;
286
+ thinkingLevel: ThinkingLevel;
287
+ role: ModelRole;
288
+ }
289
+
290
+ /** Session statistics for /session command */
291
+ export interface SessionStats {
292
+ sessionFile: string | undefined;
293
+ sessionId: string;
294
+ userMessages: number;
295
+ assistantMessages: number;
296
+ toolCalls: number;
297
+ toolResults: number;
298
+ totalMessages: number;
299
+ tokens: {
300
+ input: number;
301
+ output: number;
302
+ cacheRead: number;
303
+ cacheWrite: number;
304
+ total: number;
305
+ };
306
+ cost: number;
307
+ }
308
+
309
+ /** Result from handoff() */
310
+ export interface HandoffResult {
311
+ document: string;
312
+ }
@@ -0,0 +1,387 @@
1
+ import type { AgentMessage, AgentState } from "@nghyane/arcane-agent";
2
+ import type { AssistantMessage, Model, Usage, UsageReport } from "@nghyane/arcane-ai";
3
+ import type { ModelRegistry } from "../config/model-registry";
4
+ import { exportSessionToHtml } from "../export/html";
5
+ import type { ContextUsage } from "../extensibility/extensions/types";
6
+ import { getCurrentThemeName } from "../theme/theme";
7
+ import { calculateContextTokens, estimateTokens } from "./compaction";
8
+ import type { CompactionSummaryMessage, FileMentionMessage } from "./messages";
9
+ import type { SessionManager } from "./session-manager";
10
+ import { getLatestCompactionEntry } from "./session-manager";
11
+ import type { SessionStats } from "./session-types";
12
+
13
+ /**
14
+ * Compute session statistics from messages.
15
+ */
16
+ export function getSessionStats(
17
+ messages: AgentMessage[],
18
+ sessionFile: string | undefined,
19
+ sessionId: string,
20
+ ): SessionStats {
21
+ const userMessages = messages.filter(m => m.role === "user").length;
22
+ const assistantMessages = messages.filter(m => m.role === "assistant").length;
23
+ const toolResults = messages.filter(m => m.role === "toolResult").length;
24
+
25
+ let toolCalls = 0;
26
+ let totalInput = 0;
27
+ let totalOutput = 0;
28
+ let totalCacheRead = 0;
29
+ let totalCacheWrite = 0;
30
+ let totalCost = 0;
31
+
32
+ const getTaskToolUsage = (details: unknown): Usage | undefined => {
33
+ if (!details || typeof details !== "object") return undefined;
34
+ const record = details as Record<string, unknown>;
35
+ const usage = record.usage;
36
+ if (!usage || typeof usage !== "object") return undefined;
37
+ return usage as Usage;
38
+ };
39
+
40
+ for (const message of messages) {
41
+ if (message.role === "assistant") {
42
+ const assistantMsg = message as AssistantMessage;
43
+ toolCalls += assistantMsg.content.filter(c => c.type === "toolCall").length;
44
+ totalInput += assistantMsg.usage.input;
45
+ totalOutput += assistantMsg.usage.output;
46
+ totalCacheRead += assistantMsg.usage.cacheRead;
47
+ totalCacheWrite += assistantMsg.usage.cacheWrite;
48
+ totalCost += assistantMsg.usage.cost.total;
49
+ }
50
+
51
+ if (message.role === "toolResult" && message.toolName === "task") {
52
+ const usage = getTaskToolUsage(message.details);
53
+ if (usage) {
54
+ totalInput += usage.input;
55
+ totalOutput += usage.output;
56
+ totalCacheRead += usage.cacheRead;
57
+ totalCacheWrite += usage.cacheWrite;
58
+ totalCost += usage.cost.total;
59
+ }
60
+ }
61
+ }
62
+
63
+ return {
64
+ sessionFile,
65
+ sessionId,
66
+ userMessages,
67
+ assistantMessages,
68
+ toolCalls,
69
+ toolResults,
70
+ totalMessages: messages.length,
71
+ tokens: {
72
+ input: totalInput,
73
+ output: totalOutput,
74
+ cacheRead: totalCacheRead,
75
+ cacheWrite: totalCacheWrite,
76
+ total: totalInput + totalOutput + totalCacheRead + totalCacheWrite,
77
+ },
78
+ cost: totalCost,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Get current context usage statistics.
84
+ */
85
+ export function getContextUsage(
86
+ model: Model | undefined,
87
+ messages: AgentMessage[],
88
+ sessionManager: SessionManager,
89
+ ): ContextUsage | undefined {
90
+ if (!model) return undefined;
91
+
92
+ const contextWindow = model.contextWindow ?? 0;
93
+ if (contextWindow <= 0) return undefined;
94
+
95
+ const branchEntries = sessionManager.getBranch();
96
+ const latestCompaction = getLatestCompactionEntry(branchEntries);
97
+
98
+ if (latestCompaction) {
99
+ const compactionIndex = branchEntries.lastIndexOf(latestCompaction);
100
+ let hasPostCompactionUsage = false;
101
+ for (let i = compactionIndex + 1; i < branchEntries.length; i++) {
102
+ const entry = branchEntries[i];
103
+ if (entry.type === "message" && entry.message.role === "assistant") {
104
+ const msg = entry.message as AssistantMessage;
105
+ if (msg.usage) {
106
+ hasPostCompactionUsage = true;
107
+ break;
108
+ }
109
+ }
110
+ }
111
+
112
+ if (!hasPostCompactionUsage) {
113
+ return {
114
+ tokens: 0,
115
+ contextWindow,
116
+ percent: 0,
117
+ };
118
+ }
119
+ }
120
+
121
+ const { tokens } = estimateContextTokensFromMessages(messages);
122
+ const percent = Math.round((tokens / contextWindow) * 100);
123
+
124
+ return {
125
+ tokens,
126
+ contextWindow,
127
+ percent,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Estimate context tokens from messages, using the last assistant usage when available.
133
+ */
134
+ export function estimateContextTokensFromMessages(messages: AgentMessage[]): { tokens: number } {
135
+ let lastUsageIndex: number | null = null;
136
+ let lastUsage: Usage | undefined;
137
+ for (let i = messages.length - 1; i >= 0; i--) {
138
+ const msg = messages[i];
139
+ if (msg.role === "assistant") {
140
+ const assistantMsg = msg as AssistantMessage;
141
+ if (assistantMsg.usage) {
142
+ lastUsage = assistantMsg.usage;
143
+ lastUsageIndex = i;
144
+ break;
145
+ }
146
+ }
147
+ }
148
+
149
+ if (!lastUsage || lastUsageIndex === null) {
150
+ let estimated = 0;
151
+ for (const message of messages) {
152
+ estimated += estimateTokens(message);
153
+ }
154
+ return { tokens: estimated };
155
+ }
156
+
157
+ const usageTokens = calculateContextTokens(lastUsage);
158
+ let trailingTokens = 0;
159
+ for (let i = lastUsageIndex + 1; i < messages.length; i++) {
160
+ trailingTokens += estimateTokens(messages[i]);
161
+ }
162
+
163
+ return { tokens: usageTokens + trailingTokens };
164
+ }
165
+
166
+ /**
167
+ * Fetch usage reports from the auth storage.
168
+ */
169
+ export function fetchUsageReports(modelRegistry: ModelRegistry): Promise<UsageReport[] | null> | null {
170
+ const authStorage = modelRegistry.authStorage;
171
+ if (!authStorage.fetchUsageReports) return null;
172
+ return authStorage.fetchUsageReports({
173
+ baseUrlResolver: provider => modelRegistry.getProviderBaseUrl?.(provider),
174
+ });
175
+ }
176
+
177
+ /**
178
+ * Export session to HTML.
179
+ */
180
+ export async function exportToHtml(
181
+ sessionManager: SessionManager,
182
+ state: AgentState,
183
+ outputPath?: string,
184
+ ): Promise<string> {
185
+ const themeName = getCurrentThemeName();
186
+ return exportSessionToHtml(sessionManager, state, { outputPath, themeName });
187
+ }
188
+
189
+ /**
190
+ * Get text content of last assistant message.
191
+ */
192
+ export function getLastAssistantText(messages: AgentMessage[]): string | undefined {
193
+ const lastAssistant = messages
194
+ .slice()
195
+ .reverse()
196
+ .find(m => {
197
+ if (m.role !== "assistant") return false;
198
+ const msg = m as AssistantMessage;
199
+ if (msg.stopReason === "aborted" && msg.content.length === 0) return false;
200
+ return true;
201
+ });
202
+
203
+ if (!lastAssistant) return undefined;
204
+
205
+ let text = "";
206
+ for (const content of (lastAssistant as AssistantMessage).content) {
207
+ if (content.type === "text") {
208
+ text += content.text;
209
+ }
210
+ }
211
+
212
+ return text.trim() || undefined;
213
+ }
214
+
215
+ /**
216
+ * Format the entire session as plain text for clipboard export.
217
+ */
218
+ export function formatSessionAsText(state: AgentState): string {
219
+ const lines: string[] = [];
220
+
221
+ function formatArgsAsXml(args: Record<string, unknown>, indent = "\t"): string {
222
+ const parts: string[] = [];
223
+ for (const [key, value] of Object.entries(args)) {
224
+ const text = typeof value === "string" ? value : JSON.stringify(value);
225
+ parts.push(`${indent}<parameter name="${key}">${text}</parameter>`);
226
+ }
227
+ return parts.join("\n");
228
+ }
229
+
230
+ const systemPrompt = state.systemPrompt;
231
+ if (systemPrompt) {
232
+ lines.push("## System Prompt\n");
233
+ lines.push(systemPrompt);
234
+ lines.push("\n");
235
+ }
236
+
237
+ const model = state.model;
238
+ const thinkingLevel = state.thinkingLevel;
239
+ lines.push("## Configuration\n");
240
+ lines.push(`Model: ${model.provider}/${model.id}`);
241
+ lines.push(`Thinking Level: ${thinkingLevel}`);
242
+ lines.push("\n");
243
+
244
+ const tools = state.tools;
245
+
246
+ // Recursively strip all fields starting with 'TypeBox.' from an object
247
+ function stripTypeBoxFields(obj: unknown): unknown {
248
+ if (Array.isArray(obj)) {
249
+ return obj.map(stripTypeBoxFields);
250
+ }
251
+ if (obj && typeof obj === "object") {
252
+ const result: Record<string, unknown> = {};
253
+ for (const [k, v] of Object.entries(obj)) {
254
+ if (!k.startsWith("TypeBox.")) {
255
+ result[k] = stripTypeBoxFields(v);
256
+ }
257
+ }
258
+ return result;
259
+ }
260
+ return obj;
261
+ }
262
+
263
+ if (tools.length > 0) {
264
+ lines.push("## Available Tools\n");
265
+ for (const tool of tools) {
266
+ lines.push(`<tool name="${tool.name}">`);
267
+ lines.push(tool.description);
268
+ const parametersClean = stripTypeBoxFields(tool.parameters);
269
+ lines.push(`\nParameters:\n${formatArgsAsXml(parametersClean as Record<string, unknown>)}`);
270
+ lines.push("<" + "/tool>\n");
271
+ }
272
+ lines.push("\n");
273
+ }
274
+
275
+ for (const msg of state.messages) {
276
+ if (msg.role === "user") {
277
+ lines.push("## User\n");
278
+ if (typeof msg.content === "string") {
279
+ lines.push(msg.content);
280
+ } else {
281
+ for (const c of msg.content) {
282
+ if (c.type === "text") {
283
+ lines.push(c.text);
284
+ } else if (c.type === "image") {
285
+ lines.push("[Image]");
286
+ }
287
+ }
288
+ }
289
+ lines.push("\n");
290
+ } else if (msg.role === "assistant") {
291
+ const assistantMsg = msg as AssistantMessage;
292
+ lines.push("## Assistant\n");
293
+
294
+ for (const c of assistantMsg.content) {
295
+ if (c.type === "text") {
296
+ lines.push(c.text);
297
+ } else if (c.type === "thinking") {
298
+ lines.push("<thinking>");
299
+ lines.push(c.thinking);
300
+ lines.push("</thinking>\n");
301
+ } else if (c.type === "toolCall") {
302
+ lines.push(`<tool_call name="${c.name}" id="${c.id}">`);
303
+ if (c.arguments && typeof c.arguments === "object") {
304
+ lines.push(formatArgsAsXml(c.arguments as Record<string, unknown>));
305
+ }
306
+ lines.push("<" + "/tool_call>\n");
307
+ }
308
+ }
309
+ lines.push("\n");
310
+ } else if (msg.role === "toolResult") {
311
+ lines.push(`## Tool Result [${msg.toolName}] (id: ${msg.toolCallId})\n`);
312
+ if (typeof msg.content === "string") {
313
+ lines.push(msg.content);
314
+ } else if (Array.isArray(msg.content)) {
315
+ for (const c of msg.content) {
316
+ if (c.type === "text") {
317
+ lines.push(c.text);
318
+ } else if (c.type === "image") {
319
+ lines.push("[Image]");
320
+ }
321
+ }
322
+ }
323
+ lines.push("\n");
324
+ }
325
+ }
326
+
327
+ return lines.join("\n").trim();
328
+ }
329
+
330
+ /**
331
+ * Format the conversation as compact context for subagents.
332
+ */
333
+ export function formatCompactContext(messages: AgentMessage[]): string {
334
+ const lines: string[] = [];
335
+ lines.push("# Conversation Context");
336
+ lines.push("");
337
+ lines.push(
338
+ "This is a summary of the parent conversation. Read this if you need additional context about what was discussed or decided.",
339
+ );
340
+ lines.push("");
341
+
342
+ for (const msg of messages) {
343
+ if (msg.role === "user") {
344
+ lines.push("## User");
345
+ lines.push("");
346
+ if (typeof msg.content === "string") {
347
+ lines.push(msg.content);
348
+ } else {
349
+ for (const c of msg.content) {
350
+ if (c.type === "text") {
351
+ lines.push(c.text);
352
+ } else if (c.type === "image") {
353
+ lines.push("[Image attached]");
354
+ }
355
+ }
356
+ }
357
+ lines.push("");
358
+ } else if (msg.role === "assistant") {
359
+ const assistantMsg = msg as AssistantMessage;
360
+ const textParts: string[] = [];
361
+ for (const c of assistantMsg.content) {
362
+ if (c.type === "text" && c.text.trim()) {
363
+ textParts.push(c.text);
364
+ }
365
+ }
366
+ if (textParts.length > 0) {
367
+ lines.push("## Assistant");
368
+ lines.push("");
369
+ lines.push(textParts.join("\n\n"));
370
+ lines.push("");
371
+ }
372
+ } else if (msg.role === "fileMention") {
373
+ const fileMsg = msg as FileMentionMessage;
374
+ const paths = fileMsg.files.map(f => f.path).join(", ");
375
+ lines.push(`[Files referenced: ${paths}]`);
376
+ lines.push("");
377
+ } else if (msg.role === "compactionSummary") {
378
+ const compactMsg = msg as CompactionSummaryMessage;
379
+ lines.push("## Earlier Context (Summarized)");
380
+ lines.push("");
381
+ lines.push(compactMsg.summary);
382
+ lines.push("");
383
+ }
384
+ }
385
+
386
+ return lines.join("\n").trim();
387
+ }