@cuongtran001/kanna 1.3.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 (186) hide show
  1. package/dist/client/assets/{arc-CAysfmX5.js → arc-DXtN3EWJ.js} +1 -1
  2. package/dist/client/assets/{architectureDiagram-3BPJPVTR-BKqRYsm4.js → architectureDiagram-3BPJPVTR-CIGqW1z-.js} +1 -1
  3. package/dist/client/assets/{blockDiagram-GPEHLZMM-Ce7Eorec.js → blockDiagram-GPEHLZMM-BafNS_H6.js} +1 -1
  4. package/dist/client/assets/{c4Diagram-AAUBKEIU-CJ77zauH.js → c4Diagram-AAUBKEIU-YdAgRSis.js} +1 -1
  5. package/dist/client/assets/channel-BEYTrUAm.js +1 -0
  6. package/dist/client/assets/{chunk-2J33WTMH-Rk_3zLxJ.js → chunk-2J33WTMH-eiP5y5G-.js} +1 -1
  7. package/dist/client/assets/{chunk-4BX2VUAB-DPIz6Nvf.js → chunk-4BX2VUAB-CvLGzPMF.js} +1 -1
  8. package/dist/client/assets/{chunk-55IACEB6-D-TkbGh2.js → chunk-55IACEB6-Y-S9Fb8A.js} +1 -1
  9. package/dist/client/assets/{chunk-727SXJPM-B4NOSIZV.js → chunk-727SXJPM-6sJH1RI9.js} +1 -1
  10. package/dist/client/assets/{chunk-AQP2D5EJ-Cm_TGLYm.js → chunk-AQP2D5EJ-hV1r_IXO.js} +1 -1
  11. package/dist/client/assets/{chunk-FMBD7UC4-Bu45BUVh.js → chunk-FMBD7UC4-CC3L28S5.js} +1 -1
  12. package/dist/client/assets/{chunk-ND2GUHAM-C1Bw8_gg.js → chunk-ND2GUHAM-BZ3czhdH.js} +1 -1
  13. package/dist/client/assets/{chunk-QZHKN3VN-DueG15o0.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-YgpMurXf.js → cose-bilkent-S5V4N54A-CfxdDghE.js} +1 -1
  17. package/dist/client/assets/{dagre-BM42HDAG-Dop7Oq67.js → dagre-BM42HDAG-B8CddsbB.js} +1 -1
  18. package/dist/client/assets/{diagram-2AECGRRQ-unMfBEJH.js → diagram-2AECGRRQ-F0yS0fVB.js} +1 -1
  19. package/dist/client/assets/{diagram-5GNKFQAL-D9EJ9qMI.js → diagram-5GNKFQAL-cW0i0qlh.js} +1 -1
  20. package/dist/client/assets/{diagram-KO2AKTUF-C5IykOIR.js → diagram-KO2AKTUF-BzAKMeJT.js} +1 -1
  21. package/dist/client/assets/{diagram-LMA3HP47-DJAuPngz.js → diagram-LMA3HP47-qdC4XqIl.js} +1 -1
  22. package/dist/client/assets/{diagram-OG6HWLK6-B3wHFtS-.js → diagram-OG6HWLK6-CGcpi6Dh.js} +1 -1
  23. package/dist/client/assets/{erDiagram-TEJ5UH35-Dm7kt9o5.js → erDiagram-TEJ5UH35-CxNTDicM.js} +1 -1
  24. package/dist/client/assets/{flowDiagram-I6XJVG4X-s9Lds3ny.js → flowDiagram-I6XJVG4X-qkGa5TqY.js} +1 -1
  25. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CRQNYdAf.js → ganttDiagram-6RSMTGT7-D9mVPoQi.js} +1 -1
  26. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DhYjK4Jb.js → gitGraphDiagram-PVQCEYII-DA0grAXe.js} +1 -1
  27. package/dist/client/assets/{index-CbSY9HfS.js → index-BqUY1Bm4.js} +1 -1
  28. package/dist/client/assets/{index-C0U2kwuB.js → index-TU1k6ov6.js} +5 -5
  29. package/dist/client/assets/{infoDiagram-5YYISTIA-CRKJ0PSH.js → infoDiagram-5YYISTIA-CGljtLs3.js} +1 -1
  30. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CEAkB0IY.js → ishikawaDiagram-YF4QCWOH-CtC0xrmG.js} +1 -1
  31. package/dist/client/assets/{journeyDiagram-JHISSGLW-CyOK5_DG.js → journeyDiagram-JHISSGLW-URLnVZeN.js} +1 -1
  32. package/dist/client/assets/{kanban-definition-UN3LZRKU-BfKjiJSo.js → kanban-definition-UN3LZRKU-C2Q6aqQh.js} +1 -1
  33. package/dist/client/assets/{linear-DLR6miy3.js → linear-ByKHtsIx.js} +1 -1
  34. package/dist/client/assets/{mermaid.core-D7AdC_lE.js → mermaid.core-Dl_f2KLc.js} +4 -4
  35. package/dist/client/assets/{mindmap-definition-RKZ34NQL-zYyJtDSa.js → mindmap-definition-RKZ34NQL-CaJ9IDLI.js} +1 -1
  36. package/dist/client/assets/{pieDiagram-4H26LBE5-BgP8PFlo.js → pieDiagram-4H26LBE5-mmYaf2Oa.js} +1 -1
  37. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Di8h7J96.js → quadrantDiagram-W4KKPZXB-B_PO6OCC.js} +1 -1
  38. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DuTzFl3R.js → requirementDiagram-4Y6WPE33-M4huwJt1.js} +1 -1
  39. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DBRoMm_T.js → sankeyDiagram-5OEKKPKP-B0hb1sWh.js} +1 -1
  40. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Bmb-1HUN.js → sequenceDiagram-3UESZ5HK-BAqSptnA.js} +1 -1
  41. package/dist/client/assets/{stateDiagram-AJRCARHV-fnv5ayn-.js → stateDiagram-AJRCARHV-DTNkvPi5.js} +1 -1
  42. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CVN3oGTF.js +1 -0
  43. package/dist/client/assets/{timeline-definition-PNZ67QCA-ByWRdpQX.js → timeline-definition-PNZ67QCA-MgAjHz8Q.js} +1 -1
  44. package/dist/client/assets/{vennDiagram-CIIHVFJN--2WGpVXW.js → vennDiagram-CIIHVFJN-DmKoO51W.js} +1 -1
  45. package/dist/client/assets/{wardley-L42UT6IY-24zlnzxK.js → wardley-L42UT6IY-CWcyHnYs.js} +1 -1
  46. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Cllvqb3K.js → wardleyDiagram-YWT4CUSO-BGIGmFP9.js} +1 -1
  47. package/dist/client/assets/{xychartDiagram-2RQKCTM6-ZcdC6nPE.js → xychartDiagram-2RQKCTM6-D20YIvEl.js} +1 -1
  48. package/dist/client/index.html +1 -1
  49. package/package.json +1 -1
  50. package/src/server/agent-coordinator-types.ts +158 -0
  51. package/src/server/agent-coordinator.ts +919 -0
  52. package/src/server/agent-deps-builders.ts +425 -0
  53. package/src/server/agent.test.ts +22 -6
  54. package/src/server/agent.ts +35 -4987
  55. package/src/server/claude-autocontinue-commands.test.ts +494 -0
  56. package/src/server/claude-autocontinue-commands.ts +256 -0
  57. package/src/server/claude-cancel-handler.test.ts +472 -0
  58. package/src/server/claude-cancel-handler.ts +241 -0
  59. package/src/server/claude-chat-management.test.ts +395 -0
  60. package/src/server/claude-chat-management.ts +251 -0
  61. package/src/server/claude-harness-stream.test.ts +273 -0
  62. package/src/server/claude-harness-stream.ts +158 -0
  63. package/src/server/claude-loop-orch-commands.test.ts +579 -0
  64. package/src/server/claude-loop-orch-commands.ts +466 -0
  65. package/src/server/claude-message-normalizer.test.ts +213 -0
  66. package/src/server/claude-message-normalizer.ts +436 -0
  67. package/src/server/claude-prompt-helpers.test.ts +248 -0
  68. package/src/server/claude-prompt-helpers.ts +139 -0
  69. package/src/server/claude-sdk-queue.test.ts +169 -0
  70. package/src/server/claude-sdk-queue.ts +82 -0
  71. package/src/server/claude-send-command.test.ts +414 -0
  72. package/src/server/claude-send-command.ts +420 -0
  73. package/src/server/claude-session-config-helpers.test.ts +279 -0
  74. package/src/server/claude-session-config-helpers.ts +151 -0
  75. package/src/server/claude-session-config.test.ts +250 -0
  76. package/src/server/claude-session-config.ts +159 -0
  77. package/src/server/claude-session-error-handler.test.ts +498 -0
  78. package/src/server/claude-session-error-handler.ts +361 -0
  79. package/src/server/claude-session-lifecycle.test.ts +669 -0
  80. package/src/server/claude-session-lifecycle.ts +283 -0
  81. package/src/server/claude-session-rebuild.test.ts +224 -0
  82. package/src/server/claude-session-rebuild.ts +101 -0
  83. package/src/server/claude-session-runner.test.ts +523 -0
  84. package/src/server/claude-session-runner.ts +521 -0
  85. package/src/server/claude-session-spawner.test.ts +401 -0
  86. package/src/server/claude-session-spawner.ts +401 -0
  87. package/src/server/claude-session-start.test.ts +205 -0
  88. package/src/server/claude-session-start.ts +277 -0
  89. package/src/server/claude-session-state-queries.test.ts +344 -0
  90. package/src/server/claude-session-state-queries.ts +160 -0
  91. package/src/server/claude-session-state.ts +104 -0
  92. package/src/server/claude-slash-commands.test.ts +277 -0
  93. package/src/server/claude-slash-commands.ts +234 -0
  94. package/src/server/claude-spawn-helpers.test.ts +221 -0
  95. package/src/server/claude-spawn-helpers.ts +179 -0
  96. package/src/server/claude-steer-log.test.ts +243 -0
  97. package/src/server/claude-steer-log.ts +58 -0
  98. package/src/server/claude-subagent-tool-response.test.ts +213 -0
  99. package/src/server/claude-subagent-tool-response.ts +165 -0
  100. package/src/server/claude-subagent-wiring.test.ts +263 -0
  101. package/src/server/claude-subagent-wiring.ts +324 -0
  102. package/src/server/claude-tool-respond.test.ts +321 -0
  103. package/src/server/claude-tool-respond.ts +142 -0
  104. package/src/server/claude-turn-runner.test.ts +310 -0
  105. package/src/server/claude-turn-runner.ts +228 -0
  106. package/src/server/claude-turn-starter.test.ts +266 -0
  107. package/src/server/claude-turn-starter.ts +566 -0
  108. package/src/server/claude-usage-math.test.ts +169 -0
  109. package/src/server/claude-usage-math.ts +132 -0
  110. package/src/server/diff-store-branch-ops.adapter.test.ts +332 -0
  111. package/src/server/diff-store-branch-ops.adapter.ts +602 -0
  112. package/src/server/diff-store-commit-ops.adapter.test.ts +358 -0
  113. package/src/server/diff-store-commit-ops.adapter.ts +258 -0
  114. package/src/server/diff-store-errors.ts +85 -0
  115. package/src/server/diff-store-file-ops.adapter.ts +212 -0
  116. package/src/server/diff-store-git-branch.adapter.ts +686 -0
  117. package/src/server/diff-store-io.adapter.ts +27 -0
  118. package/src/server/diff-store-parse.test.ts +212 -0
  119. package/src/server/diff-store-parse.ts +122 -0
  120. package/src/server/diff-store-state.ts +82 -0
  121. package/src/server/diff-store.ts +76 -1854
  122. package/src/server/event-store-apply.test.ts +280 -0
  123. package/src/server/event-store-apply.ts +132 -0
  124. package/src/server/event-store-chat-lifecycle.test.ts +408 -0
  125. package/src/server/event-store-chat-lifecycle.ts +441 -0
  126. package/src/server/event-store-entity-write.ts +356 -0
  127. package/src/server/event-store-helpers.test.ts +230 -0
  128. package/src/server/event-store-helpers.ts +235 -0
  129. package/src/server/event-store-init.ts +276 -0
  130. package/src/server/event-store-messages.adapter.test.ts +365 -0
  131. package/src/server/event-store-messages.adapter.ts +213 -0
  132. package/src/server/event-store-orch-subscription.ts +124 -0
  133. package/src/server/event-store-orch.test.ts +462 -0
  134. package/src/server/event-store-orch.ts +312 -0
  135. package/src/server/event-store-peripheral-events.adapter.test.ts +254 -0
  136. package/src/server/event-store-peripheral-events.adapter.ts +105 -0
  137. package/src/server/event-store-snapshot.test.ts +597 -0
  138. package/src/server/event-store-snapshot.ts +658 -0
  139. package/src/server/event-store-subagent.test.ts +408 -0
  140. package/src/server/event-store-subagent.ts +198 -0
  141. package/src/server/event-store-tool-requests.test.ts +210 -0
  142. package/src/server/event-store-tool-requests.ts +131 -0
  143. package/src/server/event-store-transcript-write.adapter.ts +285 -0
  144. package/src/server/event-store-write-ops.test.ts +554 -0
  145. package/src/server/event-store-write-ops.ts +529 -0
  146. package/src/server/event-store.test.ts +35 -0
  147. package/src/server/event-store.ts +251 -2285
  148. package/src/server/harness-types.ts +15 -1
  149. package/src/server/oauth-errors.ts +19 -0
  150. package/src/server/ws-router-agent-ctrl.test.ts +187 -0
  151. package/src/server/ws-router-agent-ctrl.ts +137 -0
  152. package/src/server/ws-router-broadcast.ts +488 -0
  153. package/src/server/ws-router-chat.test.ts +461 -0
  154. package/src/server/ws-router-chat.ts +279 -0
  155. package/src/server/ws-router-defaults.test.ts +283 -0
  156. package/src/server/ws-router-defaults.ts +434 -0
  157. package/src/server/ws-router-diff.test.ts +167 -0
  158. package/src/server/ws-router-diff.ts +250 -0
  159. package/src/server/ws-router-envelope.ts +329 -0
  160. package/src/server/ws-router-misc.test.ts +422 -0
  161. package/src/server/ws-router-misc.ts +265 -0
  162. package/src/server/ws-router-orch.test.ts +190 -0
  163. package/src/server/ws-router-orch.ts +93 -0
  164. package/src/server/ws-router-project.test.ts +403 -0
  165. package/src/server/ws-router-project.ts +256 -0
  166. package/src/server/ws-router-push.test.ts +193 -0
  167. package/src/server/ws-router-push.ts +120 -0
  168. package/src/server/ws-router-settings.test.ts +173 -0
  169. package/src/server/ws-router-settings.ts +416 -0
  170. package/src/server/ws-router-skills.test.ts +194 -0
  171. package/src/server/ws-router-skills.ts +204 -0
  172. package/src/server/ws-router-utils.ts +224 -0
  173. package/src/server/ws-router.ts +339 -2254
  174. package/src/shared/app-settings-types.ts +365 -0
  175. package/src/shared/core-types.ts +27 -0
  176. package/src/shared/git-diff-types.ts +165 -0
  177. package/src/shared/mcp-types.ts +105 -0
  178. package/src/shared/provider-model-types.ts +437 -0
  179. package/src/shared/subagent-types.ts +198 -0
  180. package/src/shared/tool-call-types.ts +314 -0
  181. package/src/shared/transcript-types.ts +270 -0
  182. package/src/shared/types.ts +25 -1711
  183. package/dist/client/assets/channel-KQhRwDzJ.js +0 -1
  184. package/dist/client/assets/classDiagram-4FO5ZUOK-ChxT1GNq.js +0 -1
  185. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ChxT1GNq.js +0 -1
  186. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D4X-ZnLV.js +0 -1
@@ -0,0 +1,437 @@
1
+ /**
2
+ * Provider and model catalog types — extracted from shared/types.ts.
3
+ * Imported via the re-export barrel in types.ts; all external consumers
4
+ * continue to import from "../shared/types" unchanged.
5
+ */
6
+
7
+ // AgentProvider lives in types.ts and is only needed as a type here.
8
+ // import type = erased at compile time → no circular runtime dependency.
9
+ import type { AgentProvider } from "./types"
10
+
11
+ export const DEFAULT_OPENROUTER_SDK_MODEL = "moonshotai/kimi-k2.5:nitro"
12
+
13
+ export const OPENROUTER_BASE_URL = "https://openrouter.ai/api"
14
+ export const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models"
15
+
16
+ export interface OpenRouterModel {
17
+ id: string
18
+ label: string
19
+ contextLength: number
20
+ pricing?: { promptPerTok: number; completionPerTok: number }
21
+ }
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Provider model option primitives
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export interface ProviderModelOption {
28
+ id: string
29
+ label: string
30
+ supportsEffort: boolean
31
+ aliases?: readonly string[]
32
+ contextWindowOptions?: readonly ProviderContextWindowOption[]
33
+ supportsMaxReasoningEffort?: boolean
34
+ }
35
+
36
+ export interface ProviderEffortOption {
37
+ id: string
38
+ label: string
39
+ }
40
+
41
+ export interface ProviderContextWindowOption {
42
+ id: ClaudeContextWindow
43
+ label: string
44
+ }
45
+
46
+ export const CLAUDE_REASONING_OPTIONS = [
47
+ { id: "low", label: "Low" },
48
+ { id: "medium", label: "Medium" },
49
+ { id: "high", label: "High" },
50
+ { id: "max", label: "Max" },
51
+ ] as const satisfies readonly ProviderEffortOption[]
52
+
53
+ export const CODEX_REASONING_OPTIONS = [
54
+ { id: "minimal", label: "Minimal" },
55
+ { id: "low", label: "Low" },
56
+ { id: "medium", label: "Medium" },
57
+ { id: "high", label: "High" },
58
+ { id: "xhigh", label: "XHigh" },
59
+ ] as const satisfies readonly ProviderEffortOption[]
60
+
61
+ export type ClaudeReasoningEffort = (typeof CLAUDE_REASONING_OPTIONS)[number]["id"]
62
+ export type CodexReasoningEffort = (typeof CODEX_REASONING_OPTIONS)[number]["id"]
63
+ export type ClaudeContextWindow = "200k" | "1m"
64
+ export type ServiceTier = "fast"
65
+
66
+ export interface ClaudeModelOptions {
67
+ reasoningEffort: ClaudeReasoningEffort
68
+ contextWindow: ClaudeContextWindow
69
+ }
70
+
71
+ export interface CodexModelOptions {
72
+ reasoningEffort: CodexReasoningEffort
73
+ fastMode: boolean
74
+ }
75
+
76
+ export type OpenRouterModelOptions = Record<string, never>
77
+
78
+ export interface ProviderModelOptionsByProvider {
79
+ claude: ClaudeModelOptions
80
+ codex: CodexModelOptions
81
+ openrouter: OpenRouterModelOptions
82
+ }
83
+
84
+ export interface ProviderPreference<TModelOptions> {
85
+ model: string
86
+ modelOptions: TModelOptions
87
+ planMode: boolean
88
+ }
89
+
90
+ export type ChatProviderPreferences = {
91
+ claude: ProviderPreference<ClaudeModelOptions>
92
+ codex: ProviderPreference<CodexModelOptions>
93
+ openrouter: ProviderPreference<OpenRouterModelOptions>
94
+ }
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // Model option defaults and type-guards
98
+ // ---------------------------------------------------------------------------
99
+
100
+ export type ModelOptions = Partial<{
101
+ [K in AgentProvider]: Partial<ProviderModelOptionsByProvider[K]>
102
+ }>
103
+
104
+ export const DEFAULT_CLAUDE_MODEL_OPTIONS = {
105
+ reasoningEffort: "high",
106
+ contextWindow: "200k",
107
+ } as const satisfies ClaudeModelOptions
108
+
109
+ export const DEFAULT_CODEX_MODEL_OPTIONS = {
110
+ reasoningEffort: "high",
111
+ fastMode: false,
112
+ } as const satisfies CodexModelOptions
113
+
114
+ export function isClaudeReasoningEffort(value: string | null | undefined): value is ClaudeReasoningEffort {
115
+ return CLAUDE_REASONING_OPTIONS.some((option) => option.id === value)
116
+ }
117
+
118
+ export function isCodexReasoningEffort(value: string | null | undefined): value is CodexReasoningEffort {
119
+ return CODEX_REASONING_OPTIONS.some((option) => option.id === value)
120
+ }
121
+
122
+ export const CLAUDE_CONTEXT_WINDOW_OPTIONS = [
123
+ { id: "200k", label: "200k" },
124
+ { id: "1m", label: "1M" },
125
+ ] as const satisfies readonly ProviderContextWindowOption[]
126
+
127
+ export function isClaudeContextWindow(value: string | null | undefined): value is ClaudeContextWindow {
128
+ return CLAUDE_CONTEXT_WINDOW_OPTIONS.some((option) => option.id === value)
129
+ }
130
+
131
+ // ---------------------------------------------------------------------------
132
+ // Provider catalog
133
+ // ---------------------------------------------------------------------------
134
+
135
+ export interface ProviderCatalogEntry {
136
+ id: AgentProvider
137
+ label: string
138
+ defaultModel: string
139
+ defaultEffort?: string
140
+ supportsPlanMode: boolean
141
+ models: ProviderModelOption[]
142
+ efforts: ProviderEffortOption[]
143
+ }
144
+
145
+ export const PROVIDERS: ProviderCatalogEntry[] = [
146
+ {
147
+ id: "claude",
148
+ label: "Claude",
149
+ defaultModel: "claude-sonnet-4-6",
150
+ defaultEffort: "high",
151
+ supportsPlanMode: true,
152
+ models: [
153
+ {
154
+ id: "claude-fable-5",
155
+ label: "Fable 5",
156
+ supportsEffort: true,
157
+ aliases: ["fable"],
158
+ contextWindowOptions: [...CLAUDE_CONTEXT_WINDOW_OPTIONS],
159
+ supportsMaxReasoningEffort: true,
160
+ },
161
+ {
162
+ id: "claude-opus-4-7",
163
+ label: "Opus 4.7",
164
+ supportsEffort: true,
165
+ aliases: ["opus"],
166
+ contextWindowOptions: [...CLAUDE_CONTEXT_WINDOW_OPTIONS],
167
+ supportsMaxReasoningEffort: true,
168
+ },
169
+ {
170
+ id: "claude-sonnet-4-6",
171
+ label: "Sonnet 4.6",
172
+ supportsEffort: true,
173
+ aliases: ["sonnet"],
174
+ contextWindowOptions: [...CLAUDE_CONTEXT_WINDOW_OPTIONS],
175
+ },
176
+ {
177
+ id: "claude-haiku-4-5-20251001",
178
+ label: "Haiku 4.5",
179
+ supportsEffort: true,
180
+ aliases: ["haiku"],
181
+ },
182
+ {
183
+ id: "claude-opus-4-8",
184
+ label: "Opus 4.8",
185
+ supportsEffort: true,
186
+ aliases: ["opus-4-8"],
187
+ contextWindowOptions: [...CLAUDE_CONTEXT_WINDOW_OPTIONS],
188
+ supportsMaxReasoningEffort: true,
189
+ },
190
+ ],
191
+ efforts: [...CLAUDE_REASONING_OPTIONS],
192
+ },
193
+ {
194
+ id: "codex",
195
+ label: "Codex",
196
+ defaultModel: "gpt-5.5",
197
+ supportsPlanMode: true,
198
+ models: [
199
+ { id: "gpt-5.5", label: "GPT-5.5", supportsEffort: false },
200
+ { id: "gpt-5.4", label: "GPT-5.4", supportsEffort: false },
201
+ { id: "gpt-5.3-codex", label: "GPT-5.3 Codex", supportsEffort: false, aliases: ["gpt-5-codex"] },
202
+ { id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark", supportsEffort: false },
203
+ ],
204
+ efforts: [],
205
+ },
206
+ {
207
+ id: "openrouter",
208
+ label: "OpenRouter",
209
+ defaultModel: DEFAULT_OPENROUTER_SDK_MODEL,
210
+ supportsPlanMode: true,
211
+ models: [],
212
+ efforts: [],
213
+ },
214
+ ]
215
+
216
+ export function getProviderCatalog(provider: AgentProvider): ProviderCatalogEntry {
217
+ const entry = PROVIDERS.find((candidate) => candidate.id === provider)
218
+ if (!entry) {
219
+ throw new Error(`Unknown provider: ${provider}`)
220
+ }
221
+ return entry
222
+ }
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // Custom model entries (user-configurable overrides over the catalog)
226
+ // ---------------------------------------------------------------------------
227
+
228
+ export interface CustomModelEntry {
229
+ id: string
230
+ label: string
231
+ provider: "claude" | "codex"
232
+ supportsEffort: boolean
233
+ aliases?: readonly string[]
234
+ contextWindowOptions?: readonly ProviderContextWindowOption[]
235
+ supportsMaxReasoningEffort?: boolean
236
+ createdAt: number
237
+ updatedAt: number
238
+ }
239
+
240
+ export interface CustomModelInput {
241
+ id: string
242
+ label: string
243
+ provider: "claude" | "codex"
244
+ supportsEffort: boolean
245
+ aliases?: readonly string[]
246
+ contextWindowOptions?: readonly ProviderContextWindowOption[]
247
+ supportsMaxReasoningEffort?: boolean
248
+ }
249
+
250
+ export interface CustomModelPatch {
251
+ label?: string
252
+ supportsEffort?: boolean
253
+ aliases?: readonly string[] | null
254
+ contextWindowOptions?: readonly ProviderContextWindowOption[] | null
255
+ supportsMaxReasoningEffort?: boolean
256
+ }
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // Text snippets (keyboard-shortcut text expansions)
260
+ // ---------------------------------------------------------------------------
261
+
262
+ export interface TextSnippet {
263
+ id: string
264
+ shortcut: string
265
+ expansion: string
266
+ createdAt: number
267
+ updatedAt: number
268
+ }
269
+
270
+ export interface TextSnippetInput {
271
+ shortcut: string
272
+ expansion: string
273
+ }
274
+
275
+ export interface TextSnippetPatch {
276
+ shortcut?: string
277
+ expansion?: string
278
+ }
279
+
280
+ // ---------------------------------------------------------------------------
281
+ // mergeCustomModels — folds user overrides into the provider catalog
282
+ // ---------------------------------------------------------------------------
283
+
284
+ function customEntryToModelOption(entry: CustomModelEntry): ProviderModelOption {
285
+ return {
286
+ id: entry.id,
287
+ label: entry.label,
288
+ supportsEffort: entry.supportsEffort,
289
+ ...(entry.aliases ? { aliases: entry.aliases } : {}),
290
+ ...(entry.contextWindowOptions ? { contextWindowOptions: entry.contextWindowOptions } : {}),
291
+ ...(entry.supportsMaxReasoningEffort !== undefined ? { supportsMaxReasoningEffort: entry.supportsMaxReasoningEffort } : {}),
292
+ }
293
+ }
294
+
295
+ export function mergeCustomModels(
296
+ base: ProviderCatalogEntry[],
297
+ customModels: readonly CustomModelEntry[],
298
+ ): ProviderCatalogEntry[] {
299
+ return base.map((entry) => {
300
+ if (entry.id !== "claude" && entry.id !== "codex") return { ...entry, models: [...entry.models] }
301
+ const forProvider = customModels.filter((m) => m.provider === entry.id)
302
+ if (forProvider.length === 0) return { ...entry, models: [...entry.models] }
303
+ const models = [...entry.models]
304
+ for (const custom of forProvider) {
305
+ const option = customEntryToModelOption(custom)
306
+ const idx = models.findIndex((m) => m.id === option.id)
307
+ if (idx >= 0) models[idx] = option
308
+ else models.push(option)
309
+ }
310
+ return { ...entry, models }
311
+ })
312
+ }
313
+
314
+ // ---------------------------------------------------------------------------
315
+ // Provider session helpers and model-id normalization
316
+ // ---------------------------------------------------------------------------
317
+
318
+ /**
319
+ * True when the provider's turns run through the Claude SDK session transport
320
+ * (a live `claudeSessions` entry consumed by `runClaudeSession`, prompts
321
+ * delivered via `session.sendPrompt`) rather than the generic harness-turn
322
+ * transport (`runTurn` over `active.turn.stream`).
323
+ *
324
+ * `claude` and `openrouter` both ride the SDK session — openrouter just points
325
+ * the SDK at OpenRouter's Anthropic-compatible endpoint. Branching on
326
+ * `provider === "claude"` where the real intent is "uses the SDK session" is
327
+ * what silently dropped openrouter's prompt delivery; use this predicate so a
328
+ * new SDK-backed provider can never be forgotten by an `if`-chain again.
329
+ */
330
+ export function providerUsesSdkSession(provider: AgentProvider): boolean {
331
+ return provider === "claude" || provider === "openrouter"
332
+ }
333
+
334
+ function catalogModelsFor(
335
+ provider: AgentProvider,
336
+ customModels?: readonly CustomModelEntry[],
337
+ ): readonly ProviderModelOption[] {
338
+ const catalog = getProviderCatalog(provider)
339
+ if (!customModels || customModels.length === 0) return catalog.models
340
+ const [merged] = mergeCustomModels([{ ...catalog, models: [...catalog.models] }], customModels)
341
+ return merged.models
342
+ }
343
+
344
+ function getProviderModelMatch(
345
+ provider: AgentProvider,
346
+ modelId?: string,
347
+ customModels?: readonly CustomModelEntry[],
348
+ ): ProviderModelOption | undefined {
349
+ if (!modelId) return undefined
350
+
351
+ return catalogModelsFor(provider, customModels).find((candidate) =>
352
+ candidate.id === modelId || candidate.aliases?.includes(modelId)
353
+ )
354
+ }
355
+
356
+ export function normalizeProviderModelId(
357
+ provider: AgentProvider,
358
+ modelId?: string,
359
+ fallbackModelId?: string,
360
+ customModels?: readonly CustomModelEntry[],
361
+ ): string {
362
+ return getProviderModelMatch(provider, modelId, customModels)?.id
363
+ ?? fallbackModelId
364
+ ?? getProviderCatalog(provider).defaultModel
365
+ }
366
+
367
+ export function normalizeClaudeModelId(
368
+ modelId?: string,
369
+ fallbackModelId = "claude-opus-4-7",
370
+ customModels?: readonly CustomModelEntry[],
371
+ ): string {
372
+ return normalizeProviderModelId("claude", modelId, fallbackModelId, customModels)
373
+ }
374
+
375
+ export function normalizeCodexModelId(
376
+ modelId?: string,
377
+ fallbackModelId = "gpt-5.5",
378
+ customModels?: readonly CustomModelEntry[],
379
+ ): string {
380
+ return normalizeProviderModelId("codex", modelId, fallbackModelId, customModels)
381
+ }
382
+
383
+ export function getProviderModelOption(
384
+ provider: AgentProvider,
385
+ modelId: string,
386
+ customModels?: readonly CustomModelEntry[],
387
+ ): ProviderModelOption | undefined {
388
+ const normalizedModelId = normalizeProviderModelId(provider, modelId, undefined, customModels)
389
+ return catalogModelsFor(provider, customModels).find((candidate) => candidate.id === normalizedModelId)
390
+ }
391
+
392
+ export function getClaudeModelOption(
393
+ modelId: string,
394
+ customModels?: readonly CustomModelEntry[],
395
+ ): ProviderModelOption | undefined {
396
+ return getProviderModelOption("claude", modelId, customModels)
397
+ }
398
+
399
+ export function supportsClaudeMaxReasoningEffort(
400
+ modelId: string,
401
+ customModels?: readonly CustomModelEntry[],
402
+ ): boolean {
403
+ return Boolean(getClaudeModelOption(modelId, customModels)?.supportsMaxReasoningEffort)
404
+ }
405
+
406
+ export function getClaudeContextWindowOptions(
407
+ modelId: string,
408
+ customModels?: readonly CustomModelEntry[],
409
+ ): readonly ProviderContextWindowOption[] {
410
+ return getClaudeModelOption(modelId, customModels)?.contextWindowOptions ?? []
411
+ }
412
+
413
+ export function normalizeClaudeContextWindow(
414
+ modelId: string,
415
+ contextWindow?: string,
416
+ customModels?: readonly CustomModelEntry[],
417
+ ): ClaudeContextWindow {
418
+ const options = getClaudeContextWindowOptions(modelId, customModels)
419
+ if (options.length === 0) return DEFAULT_CLAUDE_MODEL_OPTIONS.contextWindow
420
+ return isClaudeContextWindow(contextWindow) && options.some((option) => option.id === contextWindow)
421
+ ? contextWindow
422
+ : DEFAULT_CLAUDE_MODEL_OPTIONS.contextWindow
423
+ }
424
+
425
+ export function resolveClaudeApiModelId(modelId: string, contextWindow?: ClaudeContextWindow): string {
426
+ return contextWindow === "1m" ? `${modelId}[1m]` : modelId
427
+ }
428
+
429
+ export function resolveClaudeContextWindowTokens(contextWindow: ClaudeContextWindow): number {
430
+ switch (contextWindow) {
431
+ case "1m":
432
+ return 1_000_000
433
+ case "200k":
434
+ default:
435
+ return 200_000
436
+ }
437
+ }
@@ -0,0 +1,198 @@
1
+ // Subagent domain types: definitions, runtime snapshots, loop progress.
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 { AgentProvider } from "./core-types"
6
+ import type { ClaudeModelOptions, CodexModelOptions } from "./provider-model-types"
7
+ import type { TranscriptEntry } from "./transcript-types"
8
+
9
+ export type SubagentContextScope = "previous-assistant-reply" | "full-transcript"
10
+
11
+ export type SubagentTriggerMode = "auto" | "manual"
12
+
13
+ export interface SubagentRestriction {
14
+ workingDir?: string
15
+ allowedPaths?: string[]
16
+ }
17
+
18
+ export interface Subagent {
19
+ id: string
20
+ name: string
21
+ description?: string
22
+ provider: AgentProvider
23
+ model: string
24
+ modelOptions: ClaudeModelOptions | CodexModelOptions
25
+ systemPrompt: string
26
+ contextScope: SubagentContextScope
27
+ triggerMode: SubagentTriggerMode
28
+ workingDir?: string
29
+ allowedPaths?: string[]
30
+ // Per-subagent agentic-turn bound — the analog of Claude Code's agent
31
+ // frontmatter `maxTurns`. Unset = unbounded (Claude Code's default).
32
+ // Claude-SDK runs pass it natively to query() (graceful stop, output kept);
33
+ // PTY/Codex runs get a host-side tool-call-count backstop (hard abort).
34
+ maxTurns?: number
35
+ createdAt: number
36
+ updatedAt: number
37
+ }
38
+
39
+ export interface SubagentInput {
40
+ name: string
41
+ description?: string
42
+ provider: AgentProvider
43
+ model: string
44
+ modelOptions: ClaudeModelOptions | CodexModelOptions
45
+ systemPrompt: string
46
+ contextScope: SubagentContextScope
47
+ triggerMode?: SubagentTriggerMode
48
+ workingDir?: string
49
+ allowedPaths?: string[]
50
+ maxTurns?: number
51
+ }
52
+
53
+ export interface SubagentPatch {
54
+ name?: string
55
+ description?: string | null
56
+ provider?: AgentProvider
57
+ model?: string
58
+ modelOptions?: Partial<ClaudeModelOptions> | Partial<CodexModelOptions>
59
+ systemPrompt?: string
60
+ contextScope?: SubagentContextScope
61
+ triggerMode?: SubagentTriggerMode
62
+ workingDir?: string | null
63
+ allowedPaths?: string[] | null
64
+ maxTurns?: number | null
65
+ }
66
+
67
+ export type SubagentValidationErrorCode =
68
+ | "EMPTY_NAME"
69
+ | "INVALID_CHAR"
70
+ | "RESERVED_NAME"
71
+ | "DUPLICATE_NAME"
72
+ | "TOO_LONG"
73
+ | "NOT_FOUND"
74
+ | "RESTRICTION_NOT_SUPPORTED"
75
+ | "INVALID_PATH"
76
+ | "PATH_ESCAPE"
77
+ | "EMPTY_ALLOWED_PATHS"
78
+
79
+ export interface SubagentValidationError {
80
+ code: SubagentValidationErrorCode
81
+ message: string
82
+ }
83
+
84
+ export type SubagentErrorCode =
85
+ | "AUTH_REQUIRED"
86
+ | "UNKNOWN_SUBAGENT"
87
+ | "MANUAL_ONLY"
88
+ | "LOOP_DETECTED"
89
+ | "DEPTH_EXCEEDED"
90
+ | "TIMEOUT"
91
+ | "MAX_TURNS"
92
+ | "PROVIDER_ERROR"
93
+ | "INTERRUPTED"
94
+ | "USER_CANCELLED"
95
+ | "CAP_EXCEEDED"
96
+ | "NO_LIVE_SESSION"
97
+
98
+ export type SubagentRunStatus = "running" | "completed" | "failed" | "cancelled"
99
+
100
+ export interface ProviderUsage {
101
+ inputTokens?: number
102
+ outputTokens?: number
103
+ cachedInputTokens?: number
104
+ costUsd?: number
105
+ }
106
+
107
+ export interface SubagentPendingTool {
108
+ toolUseId: string
109
+ toolKind: "ask_user_question" | "exit_plan_mode"
110
+ input: Record<string, unknown>
111
+ requestedAt: number
112
+ }
113
+
114
+ export interface SubagentRunSnapshot {
115
+ runId: string
116
+ chatId: string
117
+ subagentId: string | null
118
+ subagentName: string
119
+ /**
120
+ * Short human label for this run, derived from the first line of the spawn
121
+ * prompt (e.g. "Migrate useKannaState.ts"). Null for runs started before this
122
+ * field existed or by error paths that never carried a prompt. Drives the
123
+ * Loop Progress panel rows so each round reads as the chunk it worked on
124
+ * rather than an opaque run id.
125
+ */
126
+ label: string | null
127
+ provider: AgentProvider
128
+ model: string
129
+ status: SubagentRunStatus
130
+ parentUserMessageId: string
131
+ parentRunId: string | null
132
+ depth: number
133
+ startedAt: number
134
+ finishedAt: number | null
135
+ finalText: string | null
136
+ error: { code: SubagentErrorCode; message: string } | null
137
+ usage: ProviderUsage | null
138
+ /**
139
+ * Every TranscriptEntry the subagent produced, in arrival order. Includes
140
+ * tool_call, tool_result, system_init, account_info, result. assistant_text
141
+ * entries also live here in addition to being concatenated into finalText
142
+ * via subagent_message_delta — clients should prefer entries[] for rich
143
+ * rendering, finalText only as a quick text-only summary.
144
+ */
145
+ entries: TranscriptEntry[]
146
+ /**
147
+ * Set while the subagent is awaiting a user response to an
148
+ * interactive tool call (AskUserQuestion / ExitPlanMode). Null
149
+ * otherwise. The orchestrator's wall-clock timeout is paused while
150
+ * this is non-null.
151
+ */
152
+ pendingTool: SubagentPendingTool | null
153
+ }
154
+
155
+ export type LoopRowStatus = "pending" | "running" | "done" | "failed"
156
+
157
+ export interface LoopRow {
158
+ runId: string
159
+ label: string
160
+ status: LoopRowStatus
161
+ startedAt: number
162
+ finishedAt: number | null
163
+ }
164
+
165
+ export interface LoopRateLimitInfo {
166
+ /** The live auto-continue schedule id, so the panel's Resume action can accept it. */
167
+ scheduleId: string
168
+ /** epoch ms the usage limit resets (and the resume is/would be scheduled for) */
169
+ resetAt: number
170
+ tz: string
171
+ /**
172
+ * true → an auto-continue is already scheduled to fire at `resetAt`
173
+ * (the loop resumes on its own).
174
+ * false → the resume is only proposed and waits on the user
175
+ * (auto-resume setting off) — render a "Resume" action.
176
+ */
177
+ scheduled: boolean
178
+ }
179
+
180
+ export interface LoopProgressSnapshot {
181
+ chatId: string
182
+ /** Whether a loop is currently armed for this chat. */
183
+ armed: boolean
184
+ /** Latest first — most recent delegation at the top, mirroring PROGRESS.md. */
185
+ rows: LoopRow[]
186
+ /** Non-null while the loop is paused on a Claude usage limit. */
187
+ rateLimit: LoopRateLimitInfo | null
188
+ }
189
+
190
+ // Type guards
191
+
192
+ export function isSubagentContextScope(value: string): value is SubagentContextScope {
193
+ return value === "previous-assistant-reply" || value === "full-transcript"
194
+ }
195
+
196
+ export function isSubagentTriggerMode(value: string): value is SubagentTriggerMode {
197
+ return value === "auto" || value === "manual"
198
+ }