@cuongtran001/kanna 1.2.0 → 1.3.1

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 (196) hide show
  1. package/dist/client/assets/{arc-BLk1CZKB.js → arc-DXtN3EWJ.js} +1 -1
  2. package/dist/client/assets/{architectureDiagram-3BPJPVTR-B01ZQYte.js → architectureDiagram-3BPJPVTR-CIGqW1z-.js} +1 -1
  3. package/dist/client/assets/{blockDiagram-GPEHLZMM-diL6a3cq.js → blockDiagram-GPEHLZMM-BafNS_H6.js} +1 -1
  4. package/dist/client/assets/{c4Diagram-AAUBKEIU-BY1verL8.js → c4Diagram-AAUBKEIU-YdAgRSis.js} +1 -1
  5. package/dist/client/assets/channel-BEYTrUAm.js +1 -0
  6. package/dist/client/assets/{chunk-2J33WTMH-psdZuajw.js → chunk-2J33WTMH-eiP5y5G-.js} +1 -1
  7. package/dist/client/assets/{chunk-4BX2VUAB-CrwQVBc5.js → chunk-4BX2VUAB-CvLGzPMF.js} +1 -1
  8. package/dist/client/assets/{chunk-55IACEB6-9-aj4jKO.js → chunk-55IACEB6-Y-S9Fb8A.js} +1 -1
  9. package/dist/client/assets/{chunk-727SXJPM-1FNK9_nB.js → chunk-727SXJPM-6sJH1RI9.js} +1 -1
  10. package/dist/client/assets/{chunk-AQP2D5EJ-DFhvd_Ek.js → chunk-AQP2D5EJ-hV1r_IXO.js} +1 -1
  11. package/dist/client/assets/{chunk-FMBD7UC4-B7MLrDa7.js → chunk-FMBD7UC4-CC3L28S5.js} +1 -1
  12. package/dist/client/assets/{chunk-ND2GUHAM-D4Df05QE.js → chunk-ND2GUHAM-BZ3czhdH.js} +1 -1
  13. package/dist/client/assets/{chunk-QZHKN3VN-Iq17Dznm.js → chunk-QZHKN3VN-Bjyd6Yr-.js} +1 -1
  14. package/dist/client/assets/classDiagram-4FO5ZUOK-CvnF5Ud6.js +1 -0
  15. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CvnF5Ud6.js +1 -0
  16. package/dist/client/assets/{cose-bilkent-S5V4N54A-BmaZhtXy.js → cose-bilkent-S5V4N54A-CfxdDghE.js} +1 -1
  17. package/dist/client/assets/{dagre-BM42HDAG-BCmpfnfP.js → dagre-BM42HDAG-B8CddsbB.js} +1 -1
  18. package/dist/client/assets/{diagram-2AECGRRQ-BIEcKowS.js → diagram-2AECGRRQ-F0yS0fVB.js} +1 -1
  19. package/dist/client/assets/{diagram-5GNKFQAL-CRBABBfv.js → diagram-5GNKFQAL-cW0i0qlh.js} +1 -1
  20. package/dist/client/assets/{diagram-KO2AKTUF-BRxUk16N.js → diagram-KO2AKTUF-BzAKMeJT.js} +1 -1
  21. package/dist/client/assets/{diagram-LMA3HP47-DPBs4xl1.js → diagram-LMA3HP47-qdC4XqIl.js} +1 -1
  22. package/dist/client/assets/{diagram-OG6HWLK6-BE9rgVv8.js → diagram-OG6HWLK6-CGcpi6Dh.js} +1 -1
  23. package/dist/client/assets/{erDiagram-TEJ5UH35-D3vgA1xJ.js → erDiagram-TEJ5UH35-CxNTDicM.js} +1 -1
  24. package/dist/client/assets/{flowDiagram-I6XJVG4X-Cgu-8XXC.js → flowDiagram-I6XJVG4X-qkGa5TqY.js} +1 -1
  25. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CgjXVyTR.js → ganttDiagram-6RSMTGT7-D9mVPoQi.js} +1 -1
  26. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zD5D8B-i.js → gitGraphDiagram-PVQCEYII-DA0grAXe.js} +1 -1
  27. package/dist/client/assets/{index-DBTdhMbJ.js → index-BqUY1Bm4.js} +1 -1
  28. package/dist/client/assets/index-CLsOWXff.css +32 -0
  29. package/dist/client/assets/{index-zQLLeAfl.js → index-TU1k6ov6.js} +253 -243
  30. package/dist/client/assets/{infoDiagram-5YYISTIA-BaFHqT4u.js → infoDiagram-5YYISTIA-CGljtLs3.js} +1 -1
  31. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-2yAC1br2.js → ishikawaDiagram-YF4QCWOH-CtC0xrmG.js} +1 -1
  32. package/dist/client/assets/{journeyDiagram-JHISSGLW-DacQ-ND5.js → journeyDiagram-JHISSGLW-URLnVZeN.js} +1 -1
  33. package/dist/client/assets/{kanban-definition-UN3LZRKU-Qlbwm3z4.js → kanban-definition-UN3LZRKU-C2Q6aqQh.js} +1 -1
  34. package/dist/client/assets/{linear-DeafqpgO.js → linear-ByKHtsIx.js} +1 -1
  35. package/dist/client/assets/{mermaid.core-KYiCPJDj.js → mermaid.core-Dl_f2KLc.js} +4 -4
  36. package/dist/client/assets/{mindmap-definition-RKZ34NQL-B1V-T6d3.js → mindmap-definition-RKZ34NQL-CaJ9IDLI.js} +1 -1
  37. package/dist/client/assets/{pieDiagram-4H26LBE5-DOF9OFRu.js → pieDiagram-4H26LBE5-mmYaf2Oa.js} +1 -1
  38. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-lH1mB7ko.js → quadrantDiagram-W4KKPZXB-B_PO6OCC.js} +1 -1
  39. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BaTsJ_sS.js → requirementDiagram-4Y6WPE33-M4huwJt1.js} +1 -1
  40. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Pfxib5iI.js → sankeyDiagram-5OEKKPKP-B0hb1sWh.js} +1 -1
  41. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BQMEKkBC.js → sequenceDiagram-3UESZ5HK-BAqSptnA.js} +1 -1
  42. package/dist/client/assets/{stateDiagram-AJRCARHV-CuldNnzp.js → stateDiagram-AJRCARHV-DTNkvPi5.js} +1 -1
  43. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CVN3oGTF.js +1 -0
  44. package/dist/client/assets/{timeline-definition-PNZ67QCA-DP7u0YAg.js → timeline-definition-PNZ67QCA-MgAjHz8Q.js} +1 -1
  45. package/dist/client/assets/{vennDiagram-CIIHVFJN-QlrjpNeQ.js → vennDiagram-CIIHVFJN-DmKoO51W.js} +1 -1
  46. package/dist/client/assets/{wardley-L42UT6IY-BqdJ_sAM.js → wardley-L42UT6IY-CWcyHnYs.js} +1 -1
  47. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Wi3kaQ5X.js → wardleyDiagram-YWT4CUSO-BGIGmFP9.js} +1 -1
  48. package/dist/client/assets/{xychartDiagram-2RQKCTM6-D-KXMyCG.js → xychartDiagram-2RQKCTM6-D20YIvEl.js} +1 -1
  49. package/dist/client/index.html +2 -2
  50. package/package.json +1 -1
  51. package/src/server/agent-coordinator-types.ts +158 -0
  52. package/src/server/agent-coordinator.ts +919 -0
  53. package/src/server/agent-deps-builders.ts +425 -0
  54. package/src/server/agent.oauth-rotation.test.ts +101 -0
  55. package/src/server/agent.test.ts +24 -8
  56. package/src/server/agent.ts +35 -4963
  57. package/src/server/auto-continue/read-model.test.ts +2 -2
  58. package/src/server/auto-continue/read-model.ts +4 -1
  59. package/src/server/claude-autocontinue-commands.test.ts +494 -0
  60. package/src/server/claude-autocontinue-commands.ts +256 -0
  61. package/src/server/claude-cancel-handler.test.ts +472 -0
  62. package/src/server/claude-cancel-handler.ts +241 -0
  63. package/src/server/claude-chat-management.test.ts +395 -0
  64. package/src/server/claude-chat-management.ts +251 -0
  65. package/src/server/claude-harness-stream.test.ts +273 -0
  66. package/src/server/claude-harness-stream.ts +158 -0
  67. package/src/server/claude-loop-orch-commands.test.ts +579 -0
  68. package/src/server/claude-loop-orch-commands.ts +466 -0
  69. package/src/server/claude-message-normalizer.test.ts +213 -0
  70. package/src/server/claude-message-normalizer.ts +436 -0
  71. package/src/server/claude-prompt-helpers.test.ts +248 -0
  72. package/src/server/claude-prompt-helpers.ts +139 -0
  73. package/src/server/claude-sdk-queue.test.ts +169 -0
  74. package/src/server/claude-sdk-queue.ts +82 -0
  75. package/src/server/claude-send-command.test.ts +414 -0
  76. package/src/server/claude-send-command.ts +420 -0
  77. package/src/server/claude-session-config-helpers.test.ts +279 -0
  78. package/src/server/claude-session-config-helpers.ts +151 -0
  79. package/src/server/claude-session-config.test.ts +250 -0
  80. package/src/server/claude-session-config.ts +159 -0
  81. package/src/server/claude-session-error-handler.test.ts +498 -0
  82. package/src/server/claude-session-error-handler.ts +361 -0
  83. package/src/server/claude-session-lifecycle.test.ts +669 -0
  84. package/src/server/claude-session-lifecycle.ts +283 -0
  85. package/src/server/claude-session-rebuild.test.ts +224 -0
  86. package/src/server/claude-session-rebuild.ts +101 -0
  87. package/src/server/claude-session-runner.test.ts +523 -0
  88. package/src/server/claude-session-runner.ts +521 -0
  89. package/src/server/claude-session-spawner.test.ts +401 -0
  90. package/src/server/claude-session-spawner.ts +401 -0
  91. package/src/server/claude-session-start.test.ts +205 -0
  92. package/src/server/claude-session-start.ts +277 -0
  93. package/src/server/claude-session-state-queries.test.ts +344 -0
  94. package/src/server/claude-session-state-queries.ts +160 -0
  95. package/src/server/claude-session-state.ts +104 -0
  96. package/src/server/claude-slash-commands.test.ts +277 -0
  97. package/src/server/claude-slash-commands.ts +234 -0
  98. package/src/server/claude-spawn-helpers.test.ts +221 -0
  99. package/src/server/claude-spawn-helpers.ts +179 -0
  100. package/src/server/claude-steer-log.test.ts +243 -0
  101. package/src/server/claude-steer-log.ts +58 -0
  102. package/src/server/claude-subagent-tool-response.test.ts +213 -0
  103. package/src/server/claude-subagent-tool-response.ts +165 -0
  104. package/src/server/claude-subagent-wiring.test.ts +263 -0
  105. package/src/server/claude-subagent-wiring.ts +324 -0
  106. package/src/server/claude-tool-respond.test.ts +321 -0
  107. package/src/server/claude-tool-respond.ts +142 -0
  108. package/src/server/claude-turn-runner.test.ts +310 -0
  109. package/src/server/claude-turn-runner.ts +228 -0
  110. package/src/server/claude-turn-starter.test.ts +266 -0
  111. package/src/server/claude-turn-starter.ts +566 -0
  112. package/src/server/claude-usage-math.test.ts +169 -0
  113. package/src/server/claude-usage-math.ts +132 -0
  114. package/src/server/diff-store-branch-ops.adapter.test.ts +332 -0
  115. package/src/server/diff-store-branch-ops.adapter.ts +602 -0
  116. package/src/server/diff-store-commit-ops.adapter.test.ts +358 -0
  117. package/src/server/diff-store-commit-ops.adapter.ts +258 -0
  118. package/src/server/diff-store-errors.ts +85 -0
  119. package/src/server/diff-store-file-ops.adapter.ts +212 -0
  120. package/src/server/diff-store-git-branch.adapter.ts +686 -0
  121. package/src/server/diff-store-io.adapter.ts +27 -0
  122. package/src/server/diff-store-parse.test.ts +212 -0
  123. package/src/server/diff-store-parse.ts +122 -0
  124. package/src/server/diff-store-state.ts +82 -0
  125. package/src/server/diff-store.ts +76 -1854
  126. package/src/server/event-store-apply.test.ts +280 -0
  127. package/src/server/event-store-apply.ts +132 -0
  128. package/src/server/event-store-chat-lifecycle.test.ts +408 -0
  129. package/src/server/event-store-chat-lifecycle.ts +441 -0
  130. package/src/server/event-store-entity-write.ts +356 -0
  131. package/src/server/event-store-helpers.test.ts +230 -0
  132. package/src/server/event-store-helpers.ts +235 -0
  133. package/src/server/event-store-init.ts +276 -0
  134. package/src/server/event-store-messages.adapter.test.ts +365 -0
  135. package/src/server/event-store-messages.adapter.ts +213 -0
  136. package/src/server/event-store-orch-subscription.ts +124 -0
  137. package/src/server/event-store-orch.test.ts +462 -0
  138. package/src/server/event-store-orch.ts +312 -0
  139. package/src/server/event-store-peripheral-events.adapter.test.ts +254 -0
  140. package/src/server/event-store-peripheral-events.adapter.ts +105 -0
  141. package/src/server/event-store-snapshot.test.ts +597 -0
  142. package/src/server/event-store-snapshot.ts +658 -0
  143. package/src/server/event-store-subagent.test.ts +408 -0
  144. package/src/server/event-store-subagent.ts +198 -0
  145. package/src/server/event-store-tool-requests.test.ts +210 -0
  146. package/src/server/event-store-tool-requests.ts +131 -0
  147. package/src/server/event-store-transcript-write.adapter.ts +285 -0
  148. package/src/server/event-store-write-ops.test.ts +554 -0
  149. package/src/server/event-store-write-ops.ts +529 -0
  150. package/src/server/event-store.test.ts +35 -0
  151. package/src/server/event-store.ts +251 -2284
  152. package/src/server/events.ts +3 -0
  153. package/src/server/harness-types.ts +15 -1
  154. package/src/server/oauth-errors.ts +19 -0
  155. package/src/server/read-models.ts +26 -1
  156. package/src/server/subagent-orchestrator.ts +3 -0
  157. package/src/server/ws-router-agent-ctrl.test.ts +187 -0
  158. package/src/server/ws-router-agent-ctrl.ts +137 -0
  159. package/src/server/ws-router-broadcast.ts +488 -0
  160. package/src/server/ws-router-chat.test.ts +461 -0
  161. package/src/server/ws-router-chat.ts +279 -0
  162. package/src/server/ws-router-defaults.test.ts +283 -0
  163. package/src/server/ws-router-defaults.ts +434 -0
  164. package/src/server/ws-router-diff.test.ts +167 -0
  165. package/src/server/ws-router-diff.ts +250 -0
  166. package/src/server/ws-router-envelope.ts +329 -0
  167. package/src/server/ws-router-misc.test.ts +422 -0
  168. package/src/server/ws-router-misc.ts +265 -0
  169. package/src/server/ws-router-orch.test.ts +190 -0
  170. package/src/server/ws-router-orch.ts +93 -0
  171. package/src/server/ws-router-project.test.ts +403 -0
  172. package/src/server/ws-router-project.ts +256 -0
  173. package/src/server/ws-router-push.test.ts +193 -0
  174. package/src/server/ws-router-push.ts +120 -0
  175. package/src/server/ws-router-settings.test.ts +173 -0
  176. package/src/server/ws-router-settings.ts +416 -0
  177. package/src/server/ws-router-skills.test.ts +194 -0
  178. package/src/server/ws-router-skills.ts +204 -0
  179. package/src/server/ws-router-utils.ts +224 -0
  180. package/src/server/ws-router.ts +339 -2254
  181. package/src/shared/app-settings-types.ts +365 -0
  182. package/src/shared/core-types.ts +27 -0
  183. package/src/shared/git-diff-types.ts +165 -0
  184. package/src/shared/loop-progress.test.ts +106 -0
  185. package/src/shared/loop-progress.ts +88 -0
  186. package/src/shared/mcp-types.ts +105 -0
  187. package/src/shared/provider-model-types.ts +437 -0
  188. package/src/shared/subagent-types.ts +198 -0
  189. package/src/shared/tool-call-types.ts +314 -0
  190. package/src/shared/transcript-types.ts +270 -0
  191. package/src/shared/types.ts +27 -1668
  192. package/dist/client/assets/channel-BMkhjiLI.js +0 -1
  193. package/dist/client/assets/classDiagram-4FO5ZUOK-C5Kg1XOD.js +0 -1
  194. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C5Kg1XOD.js +0 -1
  195. package/dist/client/assets/index-H6C-f2Vp.css +0 -32
  196. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ca2U11af.js +0 -1
@@ -0,0 +1,365 @@
1
+ // Application settings domain types.
2
+ // Extracted from types.ts to keep the barrel lean.
3
+ // All external consumers importing from "../shared/types" continue to work unchanged.
4
+
5
+ import type {
6
+ AppThemePreference,
7
+ ChatSoundPreference,
8
+ ChatSoundId,
9
+ EditorPreset,
10
+ DefaultProviderPreference,
11
+ LlmProviderKind,
12
+ } from "./core-types"
13
+ import type {
14
+ ChatProviderPreferences,
15
+ ProviderPreference,
16
+ ClaudeModelOptions,
17
+ CodexModelOptions,
18
+ OpenRouterModelOptions,
19
+ CustomModelEntry,
20
+ CustomModelInput,
21
+ CustomModelPatch,
22
+ TextSnippet,
23
+ TextSnippetInput,
24
+ TextSnippetPatch,
25
+ } from "./provider-model-types"
26
+ import type {
27
+ McpServerConfig,
28
+ McpServerInput,
29
+ McpServerPatch,
30
+ McpServerTestResult,
31
+ McpOAuthState,
32
+ } from "./mcp-types"
33
+ import type { Subagent, SubagentInput, SubagentPatch } from "./subagent-types"
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Auth
37
+ // ---------------------------------------------------------------------------
38
+
39
+ export interface AuthSettings {
40
+ sessionMaxAgeDays: number
41
+ }
42
+
43
+ export const AUTH_DEFAULTS: AuthSettings = {
44
+ sessionMaxAgeDays: 30,
45
+ }
46
+
47
+ export const AUTH_SESSION_MAX_AGE_DAYS_MIN = 1
48
+ export const AUTH_SESSION_MAX_AGE_DAYS_MAX = 365
49
+
50
+ export type OAuthTokenStatus = "active" | "limited" | "error" | "disabled"
51
+
52
+ export interface OAuthTokenEntry {
53
+ id: string
54
+ label: string
55
+ token: string
56
+ status: OAuthTokenStatus
57
+ limitedUntil: number | null
58
+ lastUsedAt: number | null
59
+ lastErrorAt: number | null
60
+ lastErrorMessage: string | null
61
+ addedAt: number
62
+ // Per-token concurrent-chat cap. When omitted, the pool falls back to
63
+ // ClaudeAuthSettings.concurrencyDefault. Default 1 preserves the
64
+ // historical 1-token-per-chat invariant. Range
65
+ // [OAUTH_TOKEN_MAX_CONCURRENT_MIN, OAUTH_TOKEN_MAX_CONCURRENT_MAX].
66
+ maxConcurrent?: number
67
+ }
68
+
69
+ export interface ClaudeAuthSettings {
70
+ tokens: OAuthTokenEntry[]
71
+ // Pool-wide default applied to tokens whose maxConcurrent is omitted.
72
+ concurrencyDefault: number
73
+ }
74
+
75
+ export const OAUTH_TOKEN_MAX_CONCURRENT_MIN = 1
76
+ export const OAUTH_TOKEN_MAX_CONCURRENT_MAX = 5
77
+ export const OAUTH_TOKEN_CONCURRENCY_DEFAULT = 1
78
+
79
+ export const CLAUDE_AUTH_DEFAULTS: ClaudeAuthSettings = {
80
+ tokens: [],
81
+ concurrencyDefault: OAUTH_TOKEN_CONCURRENCY_DEFAULT,
82
+ }
83
+
84
+ export const OAUTH_TOKEN_LABEL_MAX = 64
85
+ export const OAUTH_TOKEN_VALUE_MAX = 1024
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Upload
89
+ // ---------------------------------------------------------------------------
90
+
91
+ export interface UploadSettings {
92
+ maxFileSizeMb: number
93
+ }
94
+
95
+ export const UPLOAD_DEFAULTS: UploadSettings = {
96
+ maxFileSizeMb: 100,
97
+ }
98
+
99
+ export const UPLOAD_MAX_FILE_SIZE_MB_MIN = 1
100
+ export const UPLOAD_MAX_FILE_SIZE_MB_MAX = 2048
101
+
102
+ export const GLOBAL_PROMPT_APPEND_MAX_CHARS = 8_000
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Claude driver
106
+ // ---------------------------------------------------------------------------
107
+
108
+ export type ClaudeDriverPreference = "sdk" | "pty"
109
+
110
+ export const CLAUDE_DRIVER_VALUES: readonly ClaudeDriverPreference[] = ["sdk", "pty"]
111
+
112
+ export function isClaudeDriverPreference(value: string | null | undefined): value is ClaudeDriverPreference {
113
+ return value === "sdk" || value === "pty"
114
+ }
115
+
116
+ export interface ClaudePtyLifecycleSettings {
117
+ idleTimeoutMs: number
118
+ maxConcurrent: number
119
+ }
120
+
121
+ export const CLAUDE_PTY_LIFECYCLE_DEFAULTS: ClaudePtyLifecycleSettings = {
122
+ idleTimeoutMs: 600_000,
123
+ maxConcurrent: 4,
124
+ }
125
+
126
+ export const CLAUDE_PTY_IDLE_TIMEOUT_MS_MIN = 60_000
127
+ export const CLAUDE_PTY_IDLE_TIMEOUT_MS_MAX = 3_600_000
128
+ export const CLAUDE_PTY_MAX_CONCURRENT_MIN = 1
129
+ export const CLAUDE_PTY_MAX_CONCURRENT_MAX = 16
130
+
131
+ export interface ClaudeDriverSettings {
132
+ preference: ClaudeDriverPreference
133
+ lifecycle: ClaudePtyLifecycleSettings
134
+ }
135
+
136
+ export const CLAUDE_DRIVER_DEFAULTS: ClaudeDriverSettings = {
137
+ preference: "sdk",
138
+ lifecycle: { ...CLAUDE_PTY_LIFECYCLE_DEFAULTS },
139
+ }
140
+
141
+ export type ClaudeSessionLifecycleStatus = "cold" | "warming" | "active" | "idle" | "cooling"
142
+
143
+ export interface ChatSessionStateSnapshot {
144
+ chatId: string
145
+ state: ClaudeSessionLifecycleStatus
146
+ updatedAt: number
147
+ }
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Cloudflare tunnel
151
+ // ---------------------------------------------------------------------------
152
+
153
+ export type CloudflareTunnelMode = "always-ask" | "auto-expose"
154
+
155
+ export interface CloudflareTunnelSettings {
156
+ enabled: boolean
157
+ cloudflaredPath: string
158
+ mode: CloudflareTunnelMode
159
+ }
160
+
161
+ export const CLOUDFLARE_TUNNEL_DEFAULTS: CloudflareTunnelSettings = {
162
+ enabled: false,
163
+ cloudflaredPath: "cloudflared",
164
+ mode: "always-ask",
165
+ }
166
+
167
+ export type CloudflareTunnelState = "proposed" | "active" | "stopped" | "failed"
168
+
169
+ export interface CloudflareTunnelRecord {
170
+ tunnelId: string
171
+ chatId: string
172
+ port: number
173
+ state: CloudflareTunnelState
174
+ url: string | null
175
+ error: string | null
176
+ proposedAt: number
177
+ activatedAt: number | null
178
+ stoppedAt: number | null
179
+ }
180
+
181
+ // ---------------------------------------------------------------------------
182
+ // Keybindings
183
+ // ---------------------------------------------------------------------------
184
+
185
+ export type KeybindingAction =
186
+ | "toggleEmbeddedTerminal"
187
+ | "toggleRightSidebar"
188
+ | "openInFinder"
189
+ | "openInEditor"
190
+ | "addSplitTerminal"
191
+ | "jumpToSidebarChat"
192
+ | "createChatInCurrentProject"
193
+ | "openAddProject"
194
+ | "newStack"
195
+ | "newStackChat"
196
+ | "jumpToStacks"
197
+
198
+ export const DEFAULT_KEYBINDINGS: Record<KeybindingAction, string[]> = {
199
+ toggleEmbeddedTerminal: ["cmd+j", "ctrl+`"],
200
+ toggleRightSidebar: ["cmd+b", "ctrl+b"],
201
+ openInFinder: ["cmd+alt+f", "ctrl+alt+f"],
202
+ openInEditor: ["cmd+shift+o", "ctrl+shift+o"],
203
+ addSplitTerminal: ["cmd+/", "ctrl+/"],
204
+ jumpToSidebarChat: ["cmd+alt"],
205
+ createChatInCurrentProject: ["cmd+alt+n"],
206
+ openAddProject: ["cmd+alt+o"],
207
+ newStack: ["cmd+alt+w"],
208
+ newStackChat: ["cmd+alt+shift+n"],
209
+ jumpToStacks: ["g s"],
210
+ }
211
+
212
+ export const KEYBINDING_ACTIONS: readonly KeybindingAction[] = [
213
+ "toggleEmbeddedTerminal",
214
+ "toggleRightSidebar",
215
+ "openInFinder",
216
+ "openInEditor",
217
+ "addSplitTerminal",
218
+ "jumpToSidebarChat",
219
+ "createChatInCurrentProject",
220
+ "openAddProject",
221
+ "newStack",
222
+ "newStackChat",
223
+ "jumpToStacks",
224
+ ] satisfies KeybindingAction[]
225
+
226
+ export interface KeybindingsSnapshot {
227
+ bindings: Record<KeybindingAction, string[]>
228
+ warning: string | null
229
+ filePathDisplay: string
230
+ }
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // App settings snapshot & patch
234
+ // ---------------------------------------------------------------------------
235
+
236
+ export interface AppSettingsSnapshot {
237
+ analyticsEnabled: boolean
238
+ browserSettingsMigrated: boolean
239
+ theme: AppThemePreference
240
+ chatSoundPreference: ChatSoundPreference
241
+ chatSoundId: ChatSoundId
242
+ terminal: {
243
+ scrollbackLines: number
244
+ minColumnWidth: number
245
+ }
246
+ editor: {
247
+ preset: EditorPreset
248
+ commandTemplate: string
249
+ }
250
+ defaultProvider: DefaultProviderPreference
251
+ providerDefaults: ChatProviderPreferences
252
+ warning: string | null
253
+ filePathDisplay: string
254
+ cloudflareTunnel: CloudflareTunnelSettings
255
+ auth: AuthSettings
256
+ claudeAuth: ClaudeAuthSettings
257
+ uploads: UploadSettings
258
+ subagents: Subagent[]
259
+ customMcpServers: McpServerConfig[]
260
+ customModels: CustomModelEntry[]
261
+ textSnippets: TextSnippet[]
262
+ claudeDriver: ClaudeDriverSettings
263
+ globalPromptAppend: string
264
+ shareDefaultTtlHours: number
265
+ subagentRuntime: SubagentRuntimeSettings
266
+ }
267
+
268
+ /**
269
+ * Runtime knobs for delegated subagent runs (delegate_subagent) and the
270
+ * autonomous loop (setup_loop). `runTimeoutMs` is the stall/idle watchdog
271
+ * window — a run is aborted only after this long with NO streamed activity,
272
+ * not a total wall-clock cap. `defaultLoopSubagentId` is the subagent
273
+ * setup_loop delegates to when the caller omits an explicit id.
274
+ */
275
+ export interface SubagentRuntimeSettings {
276
+ runTimeoutMs: number
277
+ defaultLoopSubagentId: string | null
278
+ /**
279
+ * Subagent an orchestration run (orch_run) delegates each phase to when the
280
+ * caller omits an explicit id. Optional: absent = no default (the caller must
281
+ * pass `subagentId`). Full settings CRUD/UI is a later phase.
282
+ */
283
+ defaultOrchSubagentId?: string | null
284
+ }
285
+
286
+ export interface AppSettingsPatch {
287
+ analyticsEnabled?: boolean
288
+ browserSettingsMigrated?: boolean
289
+ theme?: AppThemePreference
290
+ chatSoundPreference?: ChatSoundPreference
291
+ chatSoundId?: ChatSoundId
292
+ terminal?: Partial<AppSettingsSnapshot["terminal"]>
293
+ editor?: Partial<AppSettingsSnapshot["editor"]>
294
+ defaultProvider?: DefaultProviderPreference
295
+ providerDefaults?: {
296
+ claude?: Partial<ProviderPreference<ClaudeModelOptions>>
297
+ codex?: Partial<ProviderPreference<CodexModelOptions>>
298
+ openrouter?: Partial<ProviderPreference<OpenRouterModelOptions>>
299
+ }
300
+ cloudflareTunnel?: Partial<CloudflareTunnelSettings>
301
+ auth?: Partial<AuthSettings>
302
+ claudeAuth?: Partial<ClaudeAuthSettings>
303
+ uploads?: Partial<UploadSettings>
304
+ subagents?: {
305
+ create?: SubagentInput
306
+ update?: { id: string; patch: SubagentPatch }
307
+ delete?: { id: string }
308
+ }
309
+ customMcpServers?: {
310
+ create?: McpServerInput
311
+ update?: { id: string; patch: McpServerPatch }
312
+ delete?: { id: string }
313
+ setEnabled?: { id: string; enabled: boolean }
314
+ setTestResult?: { id: string; result: McpServerTestResult }
315
+ setOAuthState?: { id: string; oauth: McpOAuthState }
316
+ }
317
+ customModels?: {
318
+ create?: CustomModelInput
319
+ update?: { id: string; patch: CustomModelPatch }
320
+ delete?: { id: string }
321
+ }
322
+ textSnippets?: {
323
+ create?: TextSnippetInput
324
+ update?: { id: string; patch: TextSnippetPatch }
325
+ delete?: { id: string }
326
+ }
327
+ claudeDriver?: {
328
+ preference?: ClaudeDriverPreference
329
+ lifecycle?: Partial<ClaudePtyLifecycleSettings>
330
+ }
331
+ globalPromptAppend?: string
332
+ shareDefaultTtlHours?: number
333
+ subagentRuntime?: Partial<SubagentRuntimeSettings>
334
+ }
335
+
336
+ // ---------------------------------------------------------------------------
337
+ // Type guards
338
+ // ---------------------------------------------------------------------------
339
+
340
+ export function isEditorPreset(value: string): value is EditorPreset {
341
+ return value === "cursor" || value === "vscode" || value === "xcode" || value === "windsurf" || value === "custom"
342
+ }
343
+
344
+ export function isChatSoundPreference(value: string): value is ChatSoundPreference {
345
+ return value === "never" || value === "unfocused" || value === "always"
346
+ }
347
+
348
+ export function isChatSoundId(value: string): value is ChatSoundId {
349
+ return (
350
+ value === "blow" || value === "bottle" || value === "frog" || value === "funk" ||
351
+ value === "glass" || value === "ping" || value === "pop" || value === "purr" || value === "tink"
352
+ )
353
+ }
354
+
355
+ export function isCloudFlareTunnelMode(value: string): value is CloudflareTunnelMode {
356
+ return value === "always-ask" || value === "auto-expose"
357
+ }
358
+
359
+ export function isLlmProviderKind(value: string): value is LlmProviderKind {
360
+ return value === "openai" || value === "openrouter" || value === "custom"
361
+ }
362
+
363
+ export function isAppThemePreference(value: string): value is AppThemePreference {
364
+ return value === "light" || value === "dark" || value === "system"
365
+ }
@@ -0,0 +1,27 @@
1
+ // Core primitive types shared across all domain modules.
2
+ // Extracted from types.ts to break circular-import chains between
3
+ // domain-specific type files (e.g. app-settings-types, subagent-types).
4
+ //
5
+ // Do NOT add complex interfaces here — keep this file to simple
6
+ // union types, string-literal unions, and small constants.
7
+
8
+ export const STORE_VERSION = 3 as const
9
+ export const PROTOCOL_VERSION = 1 as const
10
+
11
+ export type AgentProvider = "claude" | "codex" | "openrouter"
12
+ export type LlmProviderKind = "openai" | "openrouter" | "custom"
13
+ export type AppThemePreference = "light" | "dark" | "system"
14
+ export type ChatSoundPreference = "never" | "unfocused" | "always"
15
+ export type ChatSoundId = "blow" | "bottle" | "frog" | "funk" | "glass" | "ping" | "pop" | "purr" | "tink"
16
+ export type DefaultProviderPreference = "last_used" | AgentProvider
17
+ export type EditorPreset = "cursor" | "vscode" | "xcode" | "windsurf" | "custom"
18
+ export const DEFAULT_OPENAI_SDK_MODEL = "gpt-5.4-mini"
19
+
20
+ export type AttachmentKind = "image" | "file" | "mention"
21
+
22
+ export type KannaStatus =
23
+ | "idle"
24
+ | "starting"
25
+ | "running"
26
+ | "waiting_for_user"
27
+ | "failed"
@@ -0,0 +1,165 @@
1
+ // Git diff, branch, and worktree types.
2
+ // Extracted from types.ts to keep the barrel lean.
3
+ // All external consumers importing from "../shared/types" continue to work unchanged.
4
+
5
+ export interface ChatDiffFile {
6
+ path: string
7
+ changeType: "added" | "deleted" | "modified" | "renamed"
8
+ isUntracked: boolean
9
+ additions: number
10
+ deletions: number
11
+ patchDigest: string
12
+ mimeType?: string
13
+ size?: number
14
+ }
15
+
16
+ export interface ChatBranchHistoryEntry {
17
+ sha: string
18
+ summary: string
19
+ description: string
20
+ authorName?: string
21
+ authoredAt: string
22
+ tags: string[]
23
+ githubUrl?: string
24
+ }
25
+
26
+ export interface ChatBranchHistorySnapshot {
27
+ entries: ChatBranchHistoryEntry[]
28
+ }
29
+
30
+ export type ChatBranchListEntryKind = "local" | "remote" | "pull_request"
31
+
32
+ export interface ChatBranchListEntry {
33
+ id: string
34
+ kind: ChatBranchListEntryKind
35
+ name: string
36
+ displayName: string
37
+ updatedAt?: string
38
+ description?: string
39
+ remoteRef?: string
40
+ prNumber?: number
41
+ prTitle?: string
42
+ headRefName?: string
43
+ headLabel?: string
44
+ headRepoCloneUrl?: string
45
+ isCrossRepository?: boolean
46
+ }
47
+
48
+ export interface ChatBranchListResult {
49
+ currentBranchName?: string
50
+ defaultBranchName?: string
51
+ recent: ChatBranchListEntry[]
52
+ local: ChatBranchListEntry[]
53
+ remote: ChatBranchListEntry[]
54
+ pullRequests: ChatBranchListEntry[]
55
+ pullRequestsStatus: "available" | "unavailable" | "error"
56
+ pullRequestsError?: string
57
+ }
58
+
59
+ export interface GitHubPublishInfo {
60
+ ghInstalled: boolean
61
+ authenticated: boolean
62
+ activeAccountLogin?: string
63
+ owners: string[]
64
+ suggestedRepoName: string
65
+ }
66
+
67
+ export interface GitHubRepoAvailabilityResult {
68
+ available: boolean
69
+ message: string
70
+ }
71
+
72
+ export interface BranchMetadata {
73
+ branchName?: string
74
+ defaultBranchName?: string
75
+ hasOriginRemote?: boolean
76
+ originRepoSlug?: string
77
+ hasUpstream?: boolean
78
+ }
79
+
80
+ export interface UpstreamStatus {
81
+ aheadCount?: number
82
+ behindCount?: number
83
+ lastFetchedAt?: string
84
+ }
85
+
86
+ export interface ChatDiffSnapshot extends BranchMetadata, UpstreamStatus {
87
+ status: "unknown" | "ready" | "no_repo"
88
+ files: ChatDiffFile[]
89
+ branchHistory?: ChatBranchHistorySnapshot
90
+ }
91
+
92
+ export interface BranchActionSuccess {
93
+ ok: true
94
+ branchName?: string
95
+ snapshotChanged: boolean
96
+ }
97
+
98
+ export interface BranchActionFailure {
99
+ ok: false
100
+ title: string
101
+ message: string
102
+ detail?: string
103
+ cancelled?: boolean
104
+ snapshotChanged?: boolean
105
+ }
106
+
107
+ export type ChatSyncSuccess = BranchActionSuccess & {
108
+ action: "fetch" | "pull" | "push" | "publish"
109
+ aheadCount?: number
110
+ behindCount?: number
111
+ }
112
+
113
+ export type ChatSyncFailure = BranchActionFailure & {
114
+ action: "fetch" | "pull" | "push" | "publish"
115
+ }
116
+
117
+ export type ChatSyncResult = ChatSyncSuccess | ChatSyncFailure
118
+
119
+ export type DiffCommitMode = "commit_and_push" | "commit_only"
120
+
121
+ export type ChatCheckoutBranchSuccess = BranchActionSuccess
122
+ export type ChatCheckoutBranchFailure = BranchActionFailure
123
+ export type ChatCheckoutBranchResult = ChatCheckoutBranchSuccess | ChatCheckoutBranchFailure
124
+
125
+ export type ChatCreateBranchSuccess = BranchActionSuccess & { branchName: string }
126
+ export type ChatCreateBranchFailure = BranchActionFailure
127
+ export type ChatCreateBranchResult = ChatCreateBranchSuccess | ChatCreateBranchFailure
128
+
129
+ export type ChatMergePreviewStatus = "up_to_date" | "mergeable" | "conflicts" | "error"
130
+
131
+ export interface ChatMergePreviewResult {
132
+ currentBranchName?: string
133
+ targetBranchName: string
134
+ targetDisplayName: string
135
+ status: ChatMergePreviewStatus
136
+ commitCount: number
137
+ hasConflicts: boolean
138
+ message: string
139
+ detail?: string
140
+ }
141
+
142
+ export type ChatMergeBranchSuccess = BranchActionSuccess
143
+ export type ChatMergeBranchFailure = BranchActionFailure
144
+ export type ChatMergeBranchResult = ChatMergeBranchSuccess | ChatMergeBranchFailure
145
+
146
+ export type DiffCommitSuccess = BranchActionSuccess & {
147
+ mode: DiffCommitMode
148
+ pushed: boolean
149
+ }
150
+
151
+ export type DiffCommitFailure = BranchActionFailure & {
152
+ mode: DiffCommitMode
153
+ phase: "commit" | "push"
154
+ localCommitCreated?: boolean
155
+ }
156
+
157
+ export type DiffCommitResult = DiffCommitSuccess | DiffCommitFailure
158
+
159
+ export interface GitWorktree {
160
+ path: string // absolute
161
+ branch: string // e.g. "main", "feat/x", "(detached)"
162
+ sha: string // HEAD commit sha
163
+ isPrimary: boolean
164
+ isLocked: boolean // git has flagged this worktree as locked (pruning inhibited)
165
+ }
@@ -0,0 +1,106 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { buildLoopProgress, deriveChunkLabel } from "./loop-progress"
3
+ import type { SubagentRunSnapshot } from "./types"
4
+
5
+ function run(overrides: Partial<SubagentRunSnapshot>): SubagentRunSnapshot {
6
+ return {
7
+ runId: "run-1",
8
+ chatId: "c1",
9
+ subagentId: "sa-1",
10
+ subagentName: "subagent-general",
11
+ label: null,
12
+ provider: "claude",
13
+ model: "claude-sonnet-4-6",
14
+ status: "running",
15
+ parentUserMessageId: "m1",
16
+ parentRunId: null,
17
+ depth: 0,
18
+ startedAt: 1_000,
19
+ finishedAt: null,
20
+ finalText: null,
21
+ error: null,
22
+ usage: null,
23
+ entries: [],
24
+ pendingTool: null,
25
+ ...overrides,
26
+ }
27
+ }
28
+
29
+ describe("deriveChunkLabel", () => {
30
+ test("first non-blank line, trimmed", () => {
31
+ expect(deriveChunkLabel("\n\n Migrate useKannaState.ts \nmore detail")).toBe(
32
+ "Migrate useKannaState.ts",
33
+ )
34
+ })
35
+
36
+ test("strips a single leading markdown marker", () => {
37
+ expect(deriveChunkLabel("- Fix Dockerfiles for Berry")).toBe("Fix Dockerfiles for Berry")
38
+ expect(deriveChunkLabel("## Build + start full stack")).toBe("Build + start full stack")
39
+ expect(deriveChunkLabel("3. Verify service health")).toBe("Verify service health")
40
+ expect(deriveChunkLabel("> quoted chunk")).toBe("quoted chunk")
41
+ })
42
+
43
+ test("does not mistake mid-line content for a marker", () => {
44
+ expect(deriveChunkLabel("3D rendering pipeline")).toBe("3D rendering pipeline")
45
+ })
46
+
47
+ test("caps overlong labels with an ellipsis", () => {
48
+ const long = "x".repeat(200)
49
+ const label = deriveChunkLabel(long)
50
+ expect(label.length).toBe(80)
51
+ expect(label.endsWith("…")).toBe(true)
52
+ })
53
+
54
+ test("empty prompt → empty string", () => {
55
+ expect(deriveChunkLabel(" \n ")).toBe("")
56
+ })
57
+ })
58
+
59
+ describe("buildLoopProgress", () => {
60
+ test("maps run status → row status and prefers label over subagentName", () => {
61
+ const snapshot = buildLoopProgress({
62
+ chatId: "c1",
63
+ armed: true,
64
+ loopArmedAt: 0,
65
+ rateLimit: null,
66
+ runs: [
67
+ run({ runId: "r1", status: "completed", label: "chunk one", startedAt: 10, finishedAt: 20 }),
68
+ run({ runId: "r2", status: "running", label: null, subagentName: "worker", startedAt: 30 }),
69
+ run({ runId: "r3", status: "failed", label: "chunk three", startedAt: 40 }),
70
+ ],
71
+ })
72
+ expect(snapshot.rows.map((r) => [r.runId, r.status, r.label])).toEqual([
73
+ ["r3", "failed", "chunk three"],
74
+ ["r2", "running", "worker"], // label fallback to subagentName
75
+ ["r1", "done", "chunk one"],
76
+ ])
77
+ })
78
+
79
+ test("excludes nested sub-spawns and runs started before the loop armed", () => {
80
+ const snapshot = buildLoopProgress({
81
+ chatId: "c1",
82
+ armed: true,
83
+ loopArmedAt: 100,
84
+ rateLimit: null,
85
+ runs: [
86
+ run({ runId: "pre", startedAt: 50 }), // before arm → excluded
87
+ run({ runId: "nested", startedAt: 150, depth: 1 }), // sub-spawn → excluded
88
+ run({ runId: "keep", startedAt: 200 }),
89
+ ],
90
+ })
91
+ expect(snapshot.rows.map((r) => r.runId)).toEqual(["keep"])
92
+ })
93
+
94
+ test("passes rate-limit through and reflects armed flag", () => {
95
+ const snapshot = buildLoopProgress({
96
+ chatId: "c1",
97
+ armed: false,
98
+ loopArmedAt: null,
99
+ rateLimit: { scheduleId: "s1", resetAt: 123, tz: "Asia/Saigon", scheduled: false },
100
+ runs: [],
101
+ })
102
+ expect(snapshot.armed).toBe(false)
103
+ expect(snapshot.rateLimit).toEqual({ scheduleId: "s1", resetAt: 123, tz: "Asia/Saigon", scheduled: false })
104
+ expect(snapshot.rows).toEqual([])
105
+ })
106
+ })