@neurocode-ai/tui 1.18.8

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 (241) hide show
  1. package/bunfig.toml +4 -0
  2. package/package.json +72 -0
  3. package/src/app.tsx +1134 -0
  4. package/src/attention.ts +260 -0
  5. package/src/audio.d.ts +9 -0
  6. package/src/audio.ts +53 -0
  7. package/src/clipboard.ts +124 -0
  8. package/src/component/bg-pulse-render.ts +436 -0
  9. package/src/component/bg-pulse.tsx +99 -0
  10. package/src/component/command-palette.tsx +79 -0
  11. package/src/component/dialog-agent.tsx +31 -0
  12. package/src/component/dialog-console-org.tsx +135 -0
  13. package/src/component/dialog-debug.tsx +90 -0
  14. package/src/component/dialog-mcp.tsx +85 -0
  15. package/src/component/dialog-model.tsx +197 -0
  16. package/src/component/dialog-move-session.tsx +353 -0
  17. package/src/component/dialog-provider.tsx +469 -0
  18. package/src/component/dialog-retry-action.tsx +160 -0
  19. package/src/component/dialog-session-delete-failed.tsx +99 -0
  20. package/src/component/dialog-session-list.tsx +364 -0
  21. package/src/component/dialog-session-rename.tsx +31 -0
  22. package/src/component/dialog-skill.tsx +70 -0
  23. package/src/component/dialog-stash.tsx +87 -0
  24. package/src/component/dialog-status.tsx +168 -0
  25. package/src/component/dialog-tag.tsx +47 -0
  26. package/src/component/dialog-theme-list.tsx +50 -0
  27. package/src/component/dialog-variant.tsx +39 -0
  28. package/src/component/dialog-workspace-create.tsx +308 -0
  29. package/src/component/dialog-workspace-file-changes.tsx +144 -0
  30. package/src/component/dialog-workspace-list.tsx +112 -0
  31. package/src/component/dialog-workspace-unavailable.tsx +69 -0
  32. package/src/component/error-component.tsx +240 -0
  33. package/src/component/logo.tsx +61 -0
  34. package/src/component/plugin-route-missing.tsx +14 -0
  35. package/src/component/prompt/autocomplete.tsx +781 -0
  36. package/src/component/prompt/cwd.ts +0 -0
  37. package/src/component/prompt/frecency.tsx +1 -0
  38. package/src/component/prompt/history.tsx +1 -0
  39. package/src/component/prompt/index.tsx +1713 -0
  40. package/src/component/prompt/local-attachment.ts +48 -0
  41. package/src/component/prompt/move.tsx +205 -0
  42. package/src/component/prompt/stash.tsx +1 -0
  43. package/src/component/prompt/workspace.tsx +137 -0
  44. package/src/component/register-spinner.ts +6 -0
  45. package/src/component/spinner.tsx +26 -0
  46. package/src/component/startup-loading.tsx +63 -0
  47. package/src/component/todo-item.tsx +32 -0
  48. package/src/component/use-connected.tsx +12 -0
  49. package/src/component/workspace-label.tsx +19 -0
  50. package/src/config/index.tsx +129 -0
  51. package/src/config/keybind.ts +471 -0
  52. package/src/context/args.tsx +16 -0
  53. package/src/context/clipboard.tsx +18 -0
  54. package/src/context/data.tsx +569 -0
  55. package/src/context/directory.ts +17 -0
  56. package/src/context/editor.ts +408 -0
  57. package/src/context/epilogue.tsx +6 -0
  58. package/src/context/event.ts +36 -0
  59. package/src/context/exit.tsx +8 -0
  60. package/src/context/helper.tsx +26 -0
  61. package/src/context/kv.tsx +66 -0
  62. package/src/context/local.tsx +542 -0
  63. package/src/context/location.tsx +14 -0
  64. package/src/context/path-format.tsx +24 -0
  65. package/src/context/permission.tsx +26 -0
  66. package/src/context/project.tsx +115 -0
  67. package/src/context/prompt.tsx +18 -0
  68. package/src/context/route.tsx +60 -0
  69. package/src/context/runtime.tsx +62 -0
  70. package/src/context/sdk.tsx +151 -0
  71. package/src/context/sync.tsx +666 -0
  72. package/src/context/theme.tsx +332 -0
  73. package/src/context/thinking.ts +67 -0
  74. package/src/editor-zed.ts +286 -0
  75. package/src/editor.ts +101 -0
  76. package/src/feature-plugins/builtins.ts +36 -0
  77. package/src/feature-plugins/home/footer.tsx +100 -0
  78. package/src/feature-plugins/home/tips-view.tsx +287 -0
  79. package/src/feature-plugins/home/tips.tsx +59 -0
  80. package/src/feature-plugins/sidebar/context.tsx +65 -0
  81. package/src/feature-plugins/sidebar/files.tsx +70 -0
  82. package/src/feature-plugins/sidebar/footer.tsx +98 -0
  83. package/src/feature-plugins/sidebar/lsp.tsx +65 -0
  84. package/src/feature-plugins/sidebar/mcp.tsx +97 -0
  85. package/src/feature-plugins/sidebar/todo.tsx +49 -0
  86. package/src/feature-plugins/system/diff-viewer-file-tree-utils.ts +232 -0
  87. package/src/feature-plugins/system/diff-viewer-file-tree.tsx +162 -0
  88. package/src/feature-plugins/system/diff-viewer-ui.tsx +103 -0
  89. package/src/feature-plugins/system/diff-viewer.tsx +1077 -0
  90. package/src/feature-plugins/system/notifications.ts +94 -0
  91. package/src/feature-plugins/system/plugins.tsx +269 -0
  92. package/src/feature-plugins/system/which-key.tsx +608 -0
  93. package/src/index.tsx +1 -0
  94. package/src/keymap.tsx +290 -0
  95. package/src/logo.ts +11 -0
  96. package/src/parsers-config.ts +386 -0
  97. package/src/plugin/adapters.tsx +355 -0
  98. package/src/plugin/api.ts +52 -0
  99. package/src/plugin/command-shim.ts +109 -0
  100. package/src/plugin/runtime.tsx +81 -0
  101. package/src/plugin/slots.tsx +65 -0
  102. package/src/prompt/display.ts +48 -0
  103. package/src/prompt/frecency.tsx +80 -0
  104. package/src/prompt/history.tsx +111 -0
  105. package/src/prompt/part.ts +29 -0
  106. package/src/prompt/stash.tsx +89 -0
  107. package/src/prompt/traits.ts +29 -0
  108. package/src/routes/home/session-destination.tsx +41 -0
  109. package/src/routes/home.tsx +95 -0
  110. package/src/routes/session/dialog-fork-from-timeline.tsx +76 -0
  111. package/src/routes/session/dialog-message.tsx +109 -0
  112. package/src/routes/session/dialog-subagent.tsx +26 -0
  113. package/src/routes/session/dialog-timeline.tsx +47 -0
  114. package/src/routes/session/footer.tsx +91 -0
  115. package/src/routes/session/index.tsx +2710 -0
  116. package/src/routes/session/permission.tsx +718 -0
  117. package/src/routes/session/question.tsx +514 -0
  118. package/src/routes/session/sidebar.tsx +103 -0
  119. package/src/routes/session/subagent-footer.tsx +132 -0
  120. package/src/runtime.tsx +9 -0
  121. package/src/terminal-win32.ts +130 -0
  122. package/src/theme/assets/aura.json +69 -0
  123. package/src/theme/assets/ayu.json +80 -0
  124. package/src/theme/assets/carbonfox.json +248 -0
  125. package/src/theme/assets/catppuccin-frappe.json +230 -0
  126. package/src/theme/assets/catppuccin-macchiato.json +230 -0
  127. package/src/theme/assets/catppuccin.json +112 -0
  128. package/src/theme/assets/cobalt2.json +225 -0
  129. package/src/theme/assets/cursor.json +249 -0
  130. package/src/theme/assets/dracula.json +219 -0
  131. package/src/theme/assets/everforest.json +241 -0
  132. package/src/theme/assets/flexoki.json +237 -0
  133. package/src/theme/assets/github.json +233 -0
  134. package/src/theme/assets/gruvbox.json +242 -0
  135. package/src/theme/assets/kanagawa.json +77 -0
  136. package/src/theme/assets/lucent-orng.json +234 -0
  137. package/src/theme/assets/material.json +235 -0
  138. package/src/theme/assets/matrix.json +77 -0
  139. package/src/theme/assets/mercury.json +252 -0
  140. package/src/theme/assets/monokai.json +221 -0
  141. package/src/theme/assets/nightowl.json +221 -0
  142. package/src/theme/assets/nord.json +223 -0
  143. package/src/theme/assets/one-dark.json +84 -0
  144. package/src/theme/assets/opencode.json +245 -0
  145. package/src/theme/assets/orng.json +249 -0
  146. package/src/theme/assets/osaka-jade.json +93 -0
  147. package/src/theme/assets/palenight.json +222 -0
  148. package/src/theme/assets/rosepine.json +234 -0
  149. package/src/theme/assets/solarized.json +223 -0
  150. package/src/theme/assets/synthwave84.json +226 -0
  151. package/src/theme/assets/tokyonight.json +243 -0
  152. package/src/theme/assets/vercel.json +245 -0
  153. package/src/theme/assets/vesper.json +218 -0
  154. package/src/theme/assets/zenburn.json +223 -0
  155. package/src/theme/index.ts +1089 -0
  156. package/src/ui/border.ts +21 -0
  157. package/src/ui/dialog-alert.tsx +66 -0
  158. package/src/ui/dialog-confirm.tsx +108 -0
  159. package/src/ui/dialog-export-options.tsx +217 -0
  160. package/src/ui/dialog-help.tsx +40 -0
  161. package/src/ui/dialog-prompt.tsx +126 -0
  162. package/src/ui/dialog-select.tsx +790 -0
  163. package/src/ui/dialog.tsx +231 -0
  164. package/src/ui/link.tsx +34 -0
  165. package/src/ui/spinner.ts +368 -0
  166. package/src/ui/toast.tsx +102 -0
  167. package/src/util/collapse-tool-output.ts +19 -0
  168. package/src/util/error.ts +182 -0
  169. package/src/util/filetype.ts +130 -0
  170. package/src/util/format.ts +20 -0
  171. package/src/util/layout.ts +25 -0
  172. package/src/util/locale.ts +86 -0
  173. package/src/util/model.ts +28 -0
  174. package/src/util/path.ts +12 -0
  175. package/src/util/persistence.ts +33 -0
  176. package/src/util/presentation.ts +38 -0
  177. package/src/util/provider-origin.ts +7 -0
  178. package/src/util/record.ts +3 -0
  179. package/src/util/renderer.ts +7 -0
  180. package/src/util/revert-diff.ts +18 -0
  181. package/src/util/scroll.ts +27 -0
  182. package/src/util/selection.ts +79 -0
  183. package/src/util/session.ts +3 -0
  184. package/src/util/signal.ts +51 -0
  185. package/src/util/system.ts +20 -0
  186. package/src/util/tool-display.ts +13 -0
  187. package/src/util/transcript.ts +112 -0
  188. package/sst-env.d.ts +10 -0
  189. package/test/app-lifecycle.test.tsx +128 -0
  190. package/test/cli/cmd/tui/dialog-workspace-create.test.ts +28 -0
  191. package/test/cli/cmd/tui/model-options.test.ts +32 -0
  192. package/test/cli/cmd/tui/notifications.test.ts +267 -0
  193. package/test/cli/cmd/tui/provider-options.test.ts +41 -0
  194. package/test/cli/cmd/tui/sync-fixture.tsx +70 -0
  195. package/test/cli/cmd/tui/sync-live-hydration.test.tsx +262 -0
  196. package/test/cli/cmd/tui/sync-undefined-messages.test.tsx +43 -0
  197. package/test/cli/cmd/tui/sync.test.tsx +65 -0
  198. package/test/cli/tui/__snapshots__/inline-tool-wrap-snapshot.test.tsx.snap +92 -0
  199. package/test/cli/tui/data.test.tsx +486 -0
  200. package/test/cli/tui/dialog-prompt.test.tsx +147 -0
  201. package/test/cli/tui/diff-viewer-file-tree.test.tsx +200 -0
  202. package/test/cli/tui/diff-viewer.test.tsx +268 -0
  203. package/test/cli/tui/inline-tool-wrap-snapshot.test.tsx +351 -0
  204. package/test/cli/tui/prompt-submit-race.test.ts +98 -0
  205. package/test/cli/tui/thinking.test.ts +36 -0
  206. package/test/cli/tui/use-event.test.tsx +148 -0
  207. package/test/clipboard.test.ts +19 -0
  208. package/test/component/dialog-session-list.test.ts +46 -0
  209. package/test/config.test.tsx +127 -0
  210. package/test/context/local.test.ts +22 -0
  211. package/test/editor.test.ts +32 -0
  212. package/test/feature-plugins/diff-viewer-file-tree-utils.test.ts +323 -0
  213. package/test/fixture/fixture.ts +13 -0
  214. package/test/fixture/tui-environment.tsx +32 -0
  215. package/test/fixture/tui-plugin.ts +36 -0
  216. package/test/fixture/tui-runtime.ts +12 -0
  217. package/test/fixture/tui-sdk.ts +109 -0
  218. package/test/index.test.tsx +6 -0
  219. package/test/keymap.test.tsx +141 -0
  220. package/test/plugin/runtime.test.ts +50 -0
  221. package/test/plugin/slots.test.tsx +38 -0
  222. package/test/prompt/display.test.ts +33 -0
  223. package/test/prompt/history.test.ts +39 -0
  224. package/test/prompt/jsonl.test.ts +24 -0
  225. package/test/prompt/local-attachment.test.ts +43 -0
  226. package/test/prompt/part.test.ts +53 -0
  227. package/test/prompt/persistence.test.ts +23 -0
  228. package/test/prompt/traits.test.ts +25 -0
  229. package/test/runtime.test.tsx +37 -0
  230. package/test/theme.test.ts +81 -0
  231. package/test/util/error.test.ts +49 -0
  232. package/test/util/filetype.test.ts +16 -0
  233. package/test/util/format.test.ts +59 -0
  234. package/test/util/model.test.ts +9 -0
  235. package/test/util/presentation.test.ts +8 -0
  236. package/test/util/renderer.test.ts +30 -0
  237. package/test/util/revert-diff.test.ts +35 -0
  238. package/test/util/session.test.ts +10 -0
  239. package/test/util/tool-display.test.ts +40 -0
  240. package/test/util/transcript.test.ts +421 -0
  241. package/tsconfig.json +10 -0
@@ -0,0 +1,569 @@
1
+ import type {
2
+ AgentV2Info,
3
+ CommandV2Info,
4
+ IntegrationInfo,
5
+ LocationRef,
6
+ ModelV2Info,
7
+ PermissionSavedInfo,
8
+ PermissionV2Request,
9
+ ProviderV2Info,
10
+ QuestionV2Request,
11
+ ReferenceInfo,
12
+ SessionMessage,
13
+ SessionMessageAssistant,
14
+ SessionMessageAssistantReasoning,
15
+ SessionMessageAssistantText,
16
+ SessionMessageAssistantTool,
17
+ SessionV2Info,
18
+ SkillV2Info,
19
+ V2Event,
20
+ } from "@neurocode-ai/sdk/v2"
21
+ import { createStore, produce } from "solid-js/store"
22
+ import { createSimpleContext } from "./helper"
23
+ import { useSDK } from "./sdk"
24
+ import { useEvent } from "./event"
25
+ import { createSignal, onCleanup, onMount } from "solid-js"
26
+
27
+ type LocationData = {
28
+ agent?: AgentV2Info[]
29
+ command?: CommandV2Info[]
30
+ integration?: IntegrationInfo[]
31
+ model?: ModelV2Info[]
32
+ provider?: ProviderV2Info[]
33
+ reference?: ReferenceInfo[]
34
+ skill?: SkillV2Info[]
35
+ }
36
+
37
+ type Data = {
38
+ session: {
39
+ info: Record<string, SessionV2Info>
40
+ message: Record<string, SessionMessage[]>
41
+ permission: Record<string, PermissionV2Request[]>
42
+ question: Record<string, QuestionV2Request[]>
43
+ }
44
+ project: {
45
+ permission: Record<string, PermissionSavedInfo[]>
46
+ }
47
+ location: Record<string, LocationData>
48
+ }
49
+
50
+ function locationKey(location: LocationRef) {
51
+ return JSON.stringify([location.directory, location.workspaceID])
52
+ }
53
+
54
+ function locationQuery(ref?: LocationRef) {
55
+ return ref ? { directory: ref.directory, workspace: ref.workspaceID } : undefined
56
+ }
57
+
58
+ export const { use: useData, provider: DataProvider } = createSimpleContext({
59
+ name: "Data",
60
+ init: () => {
61
+ const [store, setStore] = createStore<Data>({
62
+ session: {
63
+ info: {},
64
+ message: {},
65
+ permission: {},
66
+ question: {},
67
+ },
68
+ project: {
69
+ permission: {},
70
+ },
71
+ location: {},
72
+ })
73
+
74
+ const sdk = useSDK()
75
+ const events = useEvent()
76
+ const [defaultLocation, setDefaultLocation] = createSignal<LocationRef>({
77
+ directory: sdk.directory ?? process.cwd(),
78
+ })
79
+
80
+ const message = {
81
+ update(sessionID: string, fn: (messages: SessionMessage[]) => void) {
82
+ setStore(
83
+ "session",
84
+ "message",
85
+ produce((draft) => {
86
+ fn((draft[sessionID] ??= []))
87
+ }),
88
+ )
89
+ },
90
+ prepend(messages: SessionMessage[], item: SessionMessage) {
91
+ if (messages.some((existing) => existing.id === item.id)) return
92
+ messages.unshift(item)
93
+ },
94
+ activeAssistant(messages: SessionMessage[]) {
95
+ const item = messages.find((item) => item.type === "assistant" && !item.time.completed)
96
+ return item?.type === "assistant" ? item : undefined
97
+ },
98
+ assistant(messages: SessionMessage[], messageID: string) {
99
+ const item = messages.find((item) => item.type === "assistant" && item.id === messageID)
100
+ return item?.type === "assistant" ? item : undefined
101
+ },
102
+ activeShell(messages: SessionMessage[], callID: string) {
103
+ const item = messages.find((item) => item.type === "shell" && item.callID === callID)
104
+ return item?.type === "shell" ? item : undefined
105
+ },
106
+ latestTool(assistant: SessionMessageAssistant | undefined, callID?: string) {
107
+ return assistant?.content.findLast(
108
+ (item): item is SessionMessageAssistantTool =>
109
+ item.type === "tool" && (callID === undefined || item.id === callID),
110
+ )
111
+ },
112
+ latestText(assistant: SessionMessageAssistant | undefined, textID: string) {
113
+ return assistant?.content.findLast(
114
+ (item): item is SessionMessageAssistantText => item.type === "text" && item.id === textID,
115
+ )
116
+ },
117
+ latestReasoning(assistant: SessionMessageAssistant | undefined, reasoningID: string) {
118
+ return assistant?.content.findLast(
119
+ (item): item is SessionMessageAssistantReasoning => item.type === "reasoning" && item.id === reasoningID,
120
+ )
121
+ },
122
+ }
123
+
124
+ function handleEvent(event: V2Event) {
125
+ switch (event.type) {
126
+ case "catalog.updated":
127
+ void Promise.all([
128
+ result.location.model.refresh(event.location),
129
+ result.location.provider.refresh(event.location),
130
+ ])
131
+ break
132
+ case "session.next.agent.switched":
133
+ message.update(event.data.sessionID, (draft) => {
134
+ message.prepend(draft, {
135
+ id: event.data.messageID,
136
+ type: "agent-switched",
137
+ agent: event.data.agent,
138
+ time: { created: event.data.timestamp },
139
+ })
140
+ })
141
+ break
142
+ case "session.next.model.switched":
143
+ message.update(event.data.sessionID, (draft) => {
144
+ message.prepend(draft, {
145
+ id: event.data.messageID,
146
+ type: "model-switched",
147
+ model: event.data.model,
148
+ time: { created: event.data.timestamp },
149
+ })
150
+ })
151
+ break
152
+ case "session.next.prompted": {
153
+ message.update(event.data.sessionID, (draft) => {
154
+ message.prepend(draft, {
155
+ id: event.data.messageID,
156
+ type: "user",
157
+ text: event.data.prompt.text,
158
+ files: event.data.prompt.files,
159
+ agents: event.data.prompt.agents,
160
+ time: { created: event.data.timestamp },
161
+ })
162
+ })
163
+ break
164
+ }
165
+ case "session.next.prompt.admitted":
166
+ break
167
+ case "session.next.context.updated":
168
+ message.update(event.data.sessionID, (draft) => {
169
+ message.prepend(draft, {
170
+ id: event.data.messageID,
171
+ type: "system",
172
+ text: event.data.text,
173
+ time: { created: event.data.timestamp },
174
+ })
175
+ })
176
+ break
177
+ case "session.next.synthetic":
178
+ message.update(event.data.sessionID, (draft) => {
179
+ message.prepend(draft, {
180
+ id: event.data.messageID,
181
+ type: "synthetic",
182
+ sessionID: event.data.sessionID,
183
+ text: event.data.text,
184
+ time: { created: event.data.timestamp },
185
+ })
186
+ })
187
+ break
188
+ case "session.next.shell.started":
189
+ message.update(event.data.sessionID, (draft) => {
190
+ message.prepend(draft, {
191
+ id: event.data.messageID,
192
+ type: "shell",
193
+ callID: event.data.callID,
194
+ command: event.data.command,
195
+ output: "",
196
+ time: { created: event.data.timestamp },
197
+ })
198
+ })
199
+ break
200
+ case "session.next.shell.ended":
201
+ message.update(event.data.sessionID, (draft) => {
202
+ const match = message.activeShell(draft, event.data.callID)
203
+ if (!match) return
204
+ match.output = event.data.output
205
+ match.time.completed = event.data.timestamp
206
+ })
207
+ break
208
+ case "session.next.step.started":
209
+ message.update(event.data.sessionID, (draft) => {
210
+ if (draft.some((message) => message.id === event.data.assistantMessageID)) return
211
+ const currentAssistant = message.activeAssistant(draft)
212
+ if (currentAssistant) currentAssistant.time.completed = event.data.timestamp
213
+ message.prepend(draft, {
214
+ id: event.data.assistantMessageID,
215
+ type: "assistant",
216
+ agent: event.data.agent,
217
+ model: event.data.model,
218
+ content: [],
219
+ snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
220
+ time: { created: event.data.timestamp },
221
+ })
222
+ })
223
+ break
224
+ case "session.next.step.ended":
225
+ message.update(event.data.sessionID, (draft) => {
226
+ const currentAssistant = message.assistant(draft, event.data.assistantMessageID)
227
+ if (!currentAssistant) return
228
+ currentAssistant.time.completed = event.data.timestamp
229
+ currentAssistant.finish = event.data.finish
230
+ currentAssistant.cost = event.data.cost
231
+ currentAssistant.tokens = event.data.tokens
232
+ if (event.data.snapshot)
233
+ currentAssistant.snapshot = { ...currentAssistant.snapshot, end: event.data.snapshot }
234
+ })
235
+ break
236
+ case "session.next.step.failed":
237
+ message.update(event.data.sessionID, (draft) => {
238
+ const currentAssistant = message.assistant(draft, event.data.assistantMessageID)
239
+ if (!currentAssistant) return
240
+ currentAssistant.time.completed = event.data.timestamp
241
+ currentAssistant.finish = "error"
242
+ currentAssistant.error = event.data.error
243
+ })
244
+ break
245
+ case "session.next.text.started":
246
+ message.update(event.data.sessionID, (draft) => {
247
+ message.assistant(draft, event.data.assistantMessageID)?.content.push({
248
+ type: "text",
249
+ id: event.data.textID,
250
+ text: "",
251
+ })
252
+ })
253
+ break
254
+ case "session.next.text.delta":
255
+ message.update(event.data.sessionID, (draft) => {
256
+ const match = message.latestText(message.assistant(draft, event.data.assistantMessageID), event.data.textID)
257
+ if (match) match.text += event.data.delta
258
+ })
259
+ break
260
+ case "session.next.text.ended":
261
+ message.update(event.data.sessionID, (draft) => {
262
+ const match = message.latestText(message.assistant(draft, event.data.assistantMessageID), event.data.textID)
263
+ if (match) match.text = event.data.text
264
+ })
265
+ break
266
+ case "session.next.tool.input.started":
267
+ message.update(event.data.sessionID, (draft) => {
268
+ message.assistant(draft, event.data.assistantMessageID)?.content.push({
269
+ type: "tool",
270
+ id: event.data.callID,
271
+ name: event.data.name,
272
+ time: { created: event.data.timestamp },
273
+ state: { status: "pending", input: "" },
274
+ })
275
+ })
276
+ break
277
+ case "session.next.tool.input.delta":
278
+ message.update(event.data.sessionID, (draft) => {
279
+ const match = message.latestTool(message.assistant(draft, event.data.assistantMessageID), event.data.callID)
280
+ if (match?.state.status === "pending") match.state.input += event.data.delta
281
+ })
282
+ break
283
+ case "session.next.tool.input.ended":
284
+ message.update(event.data.sessionID, (draft) => {
285
+ const match = message.latestTool(message.assistant(draft, event.data.assistantMessageID), event.data.callID)
286
+ if (match?.state.status === "pending") match.state.input = event.data.text
287
+ })
288
+ break
289
+ case "session.next.tool.called":
290
+ message.update(event.data.sessionID, (draft) => {
291
+ const match = message.latestTool(message.assistant(draft, event.data.assistantMessageID), event.data.callID)
292
+ if (!match) return
293
+ match.time.ran = event.data.timestamp
294
+ match.provider = event.data.provider
295
+ match.state = { status: "running", input: event.data.input, structured: {}, content: [] }
296
+ })
297
+ break
298
+ case "session.next.tool.progress":
299
+ message.update(event.data.sessionID, (draft) => {
300
+ const match = message.latestTool(message.assistant(draft, event.data.assistantMessageID), event.data.callID)
301
+ if (match?.state.status !== "running") return
302
+ match.state.structured = event.data.structured
303
+ match.state.content = [...event.data.content]
304
+ })
305
+ break
306
+ case "session.next.tool.success":
307
+ message.update(event.data.sessionID, (draft) => {
308
+ const match = message.latestTool(message.assistant(draft, event.data.assistantMessageID), event.data.callID)
309
+ if (match?.state.status !== "running") return
310
+ match.state = {
311
+ status: "completed",
312
+ input: match.state.input,
313
+ structured: event.data.structured,
314
+ content: [...event.data.content],
315
+ result: event.data.result,
316
+ }
317
+ match.provider = {
318
+ executed: event.data.provider.executed || match.provider?.executed === true,
319
+ metadata: match.provider?.metadata,
320
+ resultMetadata: event.data.provider.metadata,
321
+ }
322
+ match.time.completed = event.data.timestamp
323
+ })
324
+ break
325
+ case "session.next.tool.failed":
326
+ message.update(event.data.sessionID, (draft) => {
327
+ const match = message.latestTool(message.assistant(draft, event.data.assistantMessageID), event.data.callID)
328
+ if (!match || (match.state.status !== "pending" && match.state.status !== "running")) return
329
+ match.state = {
330
+ status: "error",
331
+ error: event.data.error,
332
+ input: typeof match.state.input === "string" ? {} : match.state.input,
333
+ structured: match.state.status === "running" ? match.state.structured : {},
334
+ content: match.state.status === "running" ? match.state.content : [],
335
+ result: event.data.result,
336
+ }
337
+ match.provider = {
338
+ executed: event.data.provider.executed || match.provider?.executed === true,
339
+ metadata: match.provider?.metadata,
340
+ resultMetadata: event.data.provider.metadata,
341
+ }
342
+ match.time.completed = event.data.timestamp
343
+ })
344
+ break
345
+ case "session.next.reasoning.started":
346
+ message.update(event.data.sessionID, (draft) => {
347
+ message.assistant(draft, event.data.assistantMessageID)?.content.push({
348
+ type: "reasoning",
349
+ id: event.data.reasoningID,
350
+ text: "",
351
+ providerMetadata: event.data.providerMetadata,
352
+ })
353
+ })
354
+ break
355
+ case "session.next.reasoning.delta":
356
+ message.update(event.data.sessionID, (draft) => {
357
+ const match = message.latestReasoning(
358
+ message.assistant(draft, event.data.assistantMessageID),
359
+ event.data.reasoningID,
360
+ )
361
+ if (match) match.text += event.data.delta
362
+ })
363
+ break
364
+ case "session.next.reasoning.ended":
365
+ message.update(event.data.sessionID, (draft) => {
366
+ const match = message.latestReasoning(
367
+ message.assistant(draft, event.data.assistantMessageID),
368
+ event.data.reasoningID,
369
+ )
370
+ if (match) {
371
+ match.text = event.data.text
372
+ if (event.data.providerMetadata !== undefined) match.providerMetadata = event.data.providerMetadata
373
+ }
374
+ })
375
+ break
376
+ case "session.next.retried":
377
+ case "session.next.compaction.started":
378
+ case "session.next.compaction.delta":
379
+ break
380
+ case "session.next.compaction.ended":
381
+ message.update(event.data.sessionID, (draft) => {
382
+ message.prepend(draft, {
383
+ id: event.data.messageID,
384
+ type: "compaction",
385
+ reason: event.data.reason,
386
+ summary: event.data.text,
387
+ recent: event.data.recent,
388
+ time: { created: event.data.timestamp },
389
+ })
390
+ })
391
+ break
392
+ case "reference.updated":
393
+ void result.location.reference.refresh()
394
+ break
395
+ case "integration.updated":
396
+ void Promise.all([
397
+ result.location.integration.refresh(event.location),
398
+ result.location.model.refresh(event.location),
399
+ result.location.provider.refresh(event.location),
400
+ ])
401
+ break
402
+ }
403
+ }
404
+
405
+ onMount(() => {
406
+ const unsub = events.subscribe((event, metadata) => {
407
+ handleEvent({
408
+ ...event,
409
+ data: event.properties,
410
+ location: { directory: metadata.directory, workspaceID: metadata.workspace },
411
+ } as V2Event)
412
+ })
413
+ onCleanup(unsub)
414
+ })
415
+
416
+ const result = {
417
+ session: {
418
+ get(sessionID: string) {
419
+ return store.session.info[sessionID]
420
+ },
421
+ async refresh(sessionID: string) {
422
+ const result = await sdk.client.v2.session.get({ sessionID }, { throwOnError: true })
423
+ setStore("session", "info", sessionID, result.data.data)
424
+ },
425
+ message: {
426
+ list(sessionID: string) {
427
+ return store.session.message[sessionID]
428
+ },
429
+ async refresh(sessionID: string) {
430
+ const result = await sdk.client.v2.session.messages({ sessionID }, { throwOnError: true })
431
+ setStore("session", "message", sessionID, result.data.data)
432
+ },
433
+ },
434
+ permission: {
435
+ list(sessionID: string) {
436
+ return store.session.permission[sessionID]
437
+ },
438
+ async refresh(sessionID: string) {
439
+ const result = await sdk.client.v2.session.permission.list({ sessionID }, { throwOnError: true })
440
+ setStore("session", "permission", sessionID, result.data.data)
441
+ },
442
+ },
443
+ question: {
444
+ list(sessionID: string) {
445
+ return store.session.question[sessionID]
446
+ },
447
+ async refresh(sessionID: string) {
448
+ const result = await sdk.client.v2.session.question.list({ sessionID }, { throwOnError: true })
449
+ setStore("session", "question", sessionID, result.data.data)
450
+ },
451
+ },
452
+ },
453
+ project: {
454
+ permission: {
455
+ list(projectID: string) {
456
+ return store.project.permission[projectID]
457
+ },
458
+ async refresh(projectID: string) {
459
+ const result = await sdk.client.v2.permission.saved.list({ projectID }, { throwOnError: true })
460
+ setStore("project", "permission", projectID, result.data.data)
461
+ },
462
+ },
463
+ },
464
+ location: {
465
+ default() {
466
+ return defaultLocation()
467
+ },
468
+ async refresh(ref?: LocationRef) {
469
+ const response = await sdk.client.v2.location.get({ location: locationQuery(ref) }, { throwOnError: true })
470
+ const location = response.data
471
+ const key = locationKey(location)
472
+ if (!store.location[key]) setStore("location", key, {})
473
+ if (!ref) setDefaultLocation({ directory: location.directory, workspaceID: location.workspaceID })
474
+ },
475
+ agent: {
476
+ list(location?: LocationRef) {
477
+ return store.location[locationKey(location ?? defaultLocation())]?.agent
478
+ },
479
+ async refresh(ref?: LocationRef) {
480
+ const result = await sdk.client.v2.agent.list({ location: locationQuery(ref) }, { throwOnError: true })
481
+ const key = locationKey(result.data.location)
482
+ setStore("location", key, "agent", result.data.data)
483
+ },
484
+ },
485
+ command: {
486
+ list(location?: LocationRef) {
487
+ return store.location[locationKey(location ?? defaultLocation())]?.command
488
+ },
489
+ async refresh(ref?: LocationRef) {
490
+ const result = await sdk.client.v2.command.list({ location: locationQuery(ref) }, { throwOnError: true })
491
+ const key = locationKey(result.data.location)
492
+ setStore("location", key, "command", result.data.data)
493
+ },
494
+ },
495
+ integration: {
496
+ list(location?: LocationRef) {
497
+ return store.location[locationKey(location ?? defaultLocation())]?.integration
498
+ },
499
+ async refresh(ref?: LocationRef) {
500
+ const result = await sdk.client.v2.integration.list(
501
+ { location: locationQuery(ref) },
502
+ { throwOnError: true },
503
+ )
504
+ const key = locationKey(result.data.location)
505
+ setStore("location", key, "integration", result.data.data)
506
+ },
507
+ },
508
+ model: {
509
+ list(location?: LocationRef) {
510
+ return store.location[locationKey(location ?? defaultLocation())]?.model
511
+ },
512
+ async refresh(ref?: LocationRef) {
513
+ const result = await sdk.client.v2.model.list({ location: locationQuery(ref) }, { throwOnError: true })
514
+ const key = locationKey(result.data.location)
515
+ setStore("location", key, "model", result.data.data)
516
+ },
517
+ },
518
+ provider: {
519
+ list(location?: LocationRef) {
520
+ return store.location[locationKey(location ?? defaultLocation())]?.provider
521
+ },
522
+ async refresh(ref?: LocationRef) {
523
+ const result = await sdk.client.v2.provider.list({ location: locationQuery(ref) }, { throwOnError: true })
524
+ const key = locationKey(result.data.location)
525
+ setStore("location", key, "provider", result.data.data)
526
+ },
527
+ },
528
+ reference: {
529
+ list(location?: LocationRef) {
530
+ return store.location[locationKey(location ?? defaultLocation())]?.reference
531
+ },
532
+ async refresh(ref?: LocationRef) {
533
+ const result = await sdk.client.v2.reference.list({ location: locationQuery(ref) }, { throwOnError: true })
534
+ const key = locationKey(result.data.location)
535
+ setStore("location", key, "reference", result.data.data)
536
+ },
537
+ },
538
+ skill: {
539
+ list(location?: LocationRef) {
540
+ return store.location[locationKey(location ?? defaultLocation())]?.skill
541
+ },
542
+ async refresh(ref?: LocationRef) {
543
+ const result = await sdk.client.v2.skill.list({ location: locationQuery(ref) }, { throwOnError: true })
544
+ const key = locationKey(result.data.location)
545
+ setStore("location", key, "skill", result.data.data)
546
+ },
547
+ },
548
+ },
549
+ }
550
+
551
+ onMount(() => {
552
+ void Promise.allSettled([
553
+ result.location.refresh(),
554
+ result.location.agent.refresh(),
555
+ result.location.integration.refresh(),
556
+ result.location.model.refresh(),
557
+ result.location.provider.refresh(),
558
+ result.location.reference.refresh(),
559
+ result.location.command.refresh(),
560
+ result.location.skill.refresh(),
561
+ ]).then((settled) => {
562
+ for (const failure of settled.filter((item) => item.status === "rejected"))
563
+ console.error("Failed to refresh default location data", failure.reason)
564
+ })
565
+ })
566
+
567
+ return result
568
+ },
569
+ })
@@ -0,0 +1,17 @@
1
+ import { createMemo } from "solid-js"
2
+ import { useProject } from "./project"
3
+ import { useSync } from "./sync"
4
+ import { abbreviateHome } from "../runtime"
5
+ import { useTuiPaths } from "./runtime"
6
+
7
+ export function useDirectory() {
8
+ const project = useProject()
9
+ const sync = useSync()
10
+ const paths = useTuiPaths()
11
+ return createMemo(() => {
12
+ const directory = project.instance.path().directory || paths.cwd
13
+ const result = abbreviateHome(directory, paths.home)
14
+ if (sync.data.vcs?.branch) return result + ":" + sync.data.vcs.branch
15
+ return result
16
+ })
17
+ }