t3code-cli 0.10.0 → 0.12.0

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 (328) hide show
  1. package/README.md +75 -11
  2. package/dist/application.js +2 -2
  3. package/dist/auth.js +1 -1
  4. package/dist/bin.js +86754 -2675
  5. package/dist/cli.js +15 -2
  6. package/dist/config.js +120 -2
  7. package/dist/connection.js +2 -12
  8. package/dist/index.js +1 -1
  9. package/dist/node.js +6 -0
  10. package/dist/orchestration.js +2 -2
  11. package/dist/rpc.js +2 -2
  12. package/dist/runtime.js +2 -2
  13. package/dist/shared.js +43850 -38040
  14. package/dist/src/application/actions.d.ts +25 -0
  15. package/dist/src/application/index.d.ts +3 -2
  16. package/dist/src/application/layer.d.ts +99 -842
  17. package/dist/src/application/model-selection.d.ts +17 -2
  18. package/dist/src/application/models.d.ts +1 -87
  19. package/dist/src/application/project-commands.d.ts +31 -2
  20. package/dist/src/application/projects.d.ts +15 -137
  21. package/dist/src/application/service.d.ts +147 -5
  22. package/dist/src/application/shell-sequence.d.ts +5 -4
  23. package/dist/src/application/terminals.d.ts +66 -0
  24. package/dist/src/application/thread-commands.d.ts +48 -1
  25. package/dist/src/application/thread-update.d.ts +8 -0
  26. package/dist/src/application/thread-wait.d.ts +4 -6
  27. package/dist/src/application/threads.d.ts +36 -14
  28. package/dist/src/auth/error.d.ts +2 -1
  29. package/dist/src/auth/index.d.ts +1 -1
  30. package/dist/src/auth/layer.d.ts +34 -6
  31. package/dist/src/auth/local-base-dir.d.ts +5 -2
  32. package/dist/src/auth/local-origin.d.ts +3 -3
  33. package/dist/src/auth/local-token.d.ts +3 -3
  34. package/dist/src/auth/service.d.ts +15 -3
  35. package/dist/src/auth/type.d.ts +40 -1
  36. package/dist/src/cli/env/flag.d.ts +2 -0
  37. package/dist/src/cli/env/index.d.ts +2 -0
  38. package/dist/src/cli/env/selection-layer.d.ts +3 -0
  39. package/dist/src/cli/flags.d.ts +5 -0
  40. package/dist/src/cli/format/index.d.ts +1 -0
  41. package/dist/src/cli/format/output.d.ts +12 -0
  42. package/dist/src/cli/index.d.ts +4 -0
  43. package/dist/src/cli/runtime/index.d.ts +1 -0
  44. package/dist/src/cli/runtime/service.d.ts +16 -0
  45. package/dist/src/{scope → cli/scope}/resolve.d.ts +5 -4
  46. package/dist/src/config/config.d.ts +49 -0
  47. package/dist/src/config/credential/cipher-node.d.ts +3 -0
  48. package/dist/src/config/credential/cipher-web.d.ts +3 -0
  49. package/dist/src/config/credential/cipher.d.ts +29 -0
  50. package/dist/src/config/credential/env.d.ts +3 -0
  51. package/dist/src/config/credential/error.d.ts +8 -0
  52. package/dist/src/config/credential/index.d.ts +6 -0
  53. package/dist/src/config/credential/service.d.ts +34 -0
  54. package/dist/src/config/env/env.d.ts +55 -0
  55. package/dist/src/config/env/index.d.ts +2 -0
  56. package/dist/src/{layout/base-dir.d.ts → config/env/layout.d.ts} +0 -1
  57. package/dist/src/config/environment-name/index.d.ts +1 -0
  58. package/dist/src/config/environment-name/name.d.ts +10 -0
  59. package/dist/src/config/error.d.ts +4 -11
  60. package/dist/src/config/index.d.ts +10 -5
  61. package/dist/src/config/keystore/error.d.ts +8 -0
  62. package/dist/src/config/keystore/file.d.ts +9 -0
  63. package/dist/src/config/keystore/index.d.ts +4 -0
  64. package/dist/src/config/keystore/keyring-node.d.ts +4 -0
  65. package/dist/src/config/keystore/service.d.ts +33 -0
  66. package/dist/src/config/paths/index.d.ts +1 -0
  67. package/dist/src/config/paths/paths.d.ts +6 -0
  68. package/dist/src/config/persist/file-mode.d.ts +4 -0
  69. package/dist/src/config/persist/migration.d.ts +68 -0
  70. package/dist/src/config/persist/persist.d.ts +40 -0
  71. package/dist/src/config/persist/schema.d.ts +68 -0
  72. package/dist/src/config/resolve/resolve.d.ts +40 -0
  73. package/dist/src/config/selection/index.d.ts +1 -0
  74. package/dist/src/config/selection/resolve.d.ts +4 -0
  75. package/dist/src/config/selection/service.d.ts +10 -0
  76. package/dist/src/config/types.d.ts +36 -0
  77. package/dist/src/config/url/error.d.ts +10 -0
  78. package/dist/src/config/url/index.d.ts +2 -0
  79. package/dist/src/connection/index.d.ts +0 -1
  80. package/dist/src/contracts/index.d.ts +1 -1
  81. package/dist/src/domain/error.d.ts +27 -1
  82. package/dist/src/domain/helpers.d.ts +8 -2
  83. package/dist/src/domain/model-config.d.ts +30 -1
  84. package/dist/src/domain/thread-activities.d.ts +1 -1
  85. package/dist/src/domain/thread-lifecycle.d.ts +3 -2
  86. package/dist/src/index.d.ts +1 -2
  87. package/dist/src/{connection/node.d.ts → node/connection.d.ts} +1 -1
  88. package/dist/src/node/index.d.ts +1 -0
  89. package/dist/src/orchestration/index.d.ts +1 -1
  90. package/dist/src/orchestration/layer.d.ts +120 -829
  91. package/dist/src/orchestration/service.d.ts +2 -1
  92. package/dist/src/rpc/error.d.ts +5 -3
  93. package/dist/src/rpc/index.d.ts +5 -1
  94. package/dist/src/rpc/layer.d.ts +1166 -40
  95. package/dist/src/rpc/operation.d.ts +6280 -0
  96. package/dist/src/rpc/ws-group.d.ts +372 -27
  97. package/dist/src/runtime/index.d.ts +1 -2
  98. package/dist/src/runtime/layer.d.ts +12 -8
  99. package/dist/src/t3tools/index.d.ts +1 -1
  100. package/dist/t3tools.js +2 -2
  101. package/package.json +27 -19
  102. package/src/application/actions.test.ts +328 -0
  103. package/src/application/actions.ts +475 -0
  104. package/src/application/index.ts +32 -2
  105. package/src/application/layer.ts +43 -5
  106. package/src/application/model-selection.ts +70 -4
  107. package/src/application/models.ts +14 -14
  108. package/src/application/project-commands.ts +38 -5
  109. package/src/application/projects.ts +49 -20
  110. package/src/application/service.ts +275 -78
  111. package/src/application/shell-sequence.ts +6 -8
  112. package/src/application/terminals.ts +208 -0
  113. package/src/application/thread-commands.test.ts +43 -0
  114. package/src/application/thread-commands.ts +82 -1
  115. package/src/application/thread-update.ts +72 -0
  116. package/src/application/thread-wait.ts +47 -48
  117. package/src/application/threads.test.ts +172 -0
  118. package/src/application/threads.ts +128 -45
  119. package/src/auth/error.ts +2 -1
  120. package/src/auth/index.ts +7 -0
  121. package/src/auth/layer.test-utils.ts +34 -0
  122. package/src/auth/layer.ts +116 -34
  123. package/src/auth/local-base-dir.ts +13 -6
  124. package/src/auth/local-origin.ts +10 -7
  125. package/src/auth/local-token.ts +28 -23
  126. package/src/auth/service.test.ts +200 -0
  127. package/src/auth/service.ts +27 -3
  128. package/src/auth/transport.ts +2 -2
  129. package/src/auth/type.ts +47 -1
  130. package/src/bin.ts +26 -6
  131. package/src/cli/action-format.ts +110 -0
  132. package/src/cli/action.ts +398 -0
  133. package/src/cli/app.ts +8 -0
  134. package/src/cli/auth.ts +76 -23
  135. package/src/cli/env/flag.ts +8 -0
  136. package/src/cli/env/index.ts +2 -0
  137. package/src/cli/env/selection-layer.test.ts +49 -0
  138. package/src/cli/env/selection-layer.ts +27 -0
  139. package/src/cli/env.ts +107 -0
  140. package/src/cli/error.ts +26 -3
  141. package/src/cli/extra-args.test.ts +32 -0
  142. package/src/cli/extra-args.ts +22 -0
  143. package/src/cli/flags.ts +26 -1
  144. package/src/cli/format/auth.ts +92 -0
  145. package/src/cli/format/human.ts +131 -0
  146. package/src/cli/format/index.ts +12 -0
  147. package/src/cli/format/model.ts +29 -0
  148. package/src/cli/format/output.ts +51 -0
  149. package/src/cli/format/project.ts +32 -0
  150. package/src/cli/format/terminal.ts +87 -0
  151. package/src/cli/format/thread.test.ts +34 -0
  152. package/src/cli/format/thread.ts +185 -0
  153. package/src/cli/index.ts +20 -0
  154. package/src/cli/input/layer.ts +20 -0
  155. package/src/cli/input/service.ts +1 -0
  156. package/src/cli/interaction/confirm.ts +64 -0
  157. package/src/cli/interaction/self-action.ts +36 -0
  158. package/src/cli/message-input.ts +1 -1
  159. package/src/cli/model.ts +9 -5
  160. package/src/cli/project.ts +15 -8
  161. package/src/cli/projects/delete.ts +51 -0
  162. package/src/cli/require.ts +9 -7
  163. package/src/cli/runtime/index.ts +1 -0
  164. package/src/cli/runtime/service.ts +28 -0
  165. package/src/{scope → cli/scope}/resolve.ts +11 -9
  166. package/src/cli/terminal/attach.ts +29 -0
  167. package/src/cli/terminal/create.ts +55 -0
  168. package/src/cli/terminal/destroy.ts +62 -0
  169. package/src/cli/terminal/encoding.test.ts +63 -0
  170. package/src/cli/terminal/encoding.ts +23 -0
  171. package/src/cli/terminal/error.ts +14 -0
  172. package/src/cli/terminal/io-node-layer.ts +82 -0
  173. package/src/cli/terminal/io-service.ts +25 -0
  174. package/src/cli/terminal/list.ts +36 -0
  175. package/src/cli/terminal/read.test-utils.ts +48 -0
  176. package/src/cli/terminal/read.test.ts +35 -0
  177. package/src/cli/terminal/read.ts +99 -0
  178. package/src/cli/terminal/scope.ts +35 -0
  179. package/src/cli/terminal/shared.ts +250 -0
  180. package/src/cli/terminal/stream.ts +61 -0
  181. package/src/cli/terminal/wait.test.ts +148 -0
  182. package/src/cli/terminal/wait.ts +223 -0
  183. package/src/cli/terminal/write.test.ts +34 -0
  184. package/src/cli/terminal/write.ts +133 -0
  185. package/src/cli/terminal.ts +26 -0
  186. package/src/cli/thread.ts +8 -0
  187. package/src/cli/threads/approve.ts +11 -7
  188. package/src/cli/threads/archive.ts +23 -25
  189. package/src/cli/threads/callback.ts +6 -4
  190. package/src/cli/threads/delete.ts +67 -0
  191. package/src/cli/threads/interrupt.ts +57 -0
  192. package/src/cli/threads/list.test.ts +71 -0
  193. package/src/cli/threads/list.ts +25 -13
  194. package/src/cli/threads/messages.ts +11 -7
  195. package/src/cli/threads/respond.ts +11 -7
  196. package/src/cli/threads/send.ts +39 -9
  197. package/src/cli/threads/show.ts +11 -7
  198. package/src/cli/threads/start.ts +18 -13
  199. package/src/cli/threads/unarchive.ts +48 -0
  200. package/src/cli/threads/update.ts +155 -0
  201. package/src/cli/threads/wait.ts +11 -7
  202. package/src/cli/wait-events.ts +1 -1
  203. package/src/config/config.ts +260 -0
  204. package/src/config/credential/cipher-node.ts +40 -0
  205. package/src/config/credential/cipher-web.ts +82 -0
  206. package/src/config/credential/cipher.ts +39 -0
  207. package/src/config/credential/env.ts +13 -0
  208. package/src/config/credential/error.ts +9 -0
  209. package/src/config/credential/index.ts +6 -0
  210. package/src/config/credential/service.test-utils.ts +25 -0
  211. package/src/config/credential/service.test.ts +116 -0
  212. package/src/config/credential/service.ts +205 -0
  213. package/src/config/env/env.test-utils.ts +14 -0
  214. package/src/config/env/env.ts +81 -0
  215. package/src/config/env/index.ts +2 -0
  216. package/src/{layout/base-dir.ts → config/env/layout.ts} +0 -9
  217. package/src/config/environment-name/index.ts +7 -0
  218. package/src/config/environment-name/name.test.ts +66 -0
  219. package/src/config/environment-name/name.ts +49 -0
  220. package/src/config/error.ts +6 -8
  221. package/src/config/index.ts +16 -5
  222. package/src/config/keystore/error.ts +9 -0
  223. package/src/config/keystore/file.ts +96 -0
  224. package/src/config/keystore/index.ts +4 -0
  225. package/src/config/keystore/keyring-node.test.ts +12 -0
  226. package/src/config/keystore/keyring-node.ts +100 -0
  227. package/src/config/keystore/service.test-utils.ts +15 -0
  228. package/src/config/keystore/service.test.ts +18 -0
  229. package/src/config/keystore/service.ts +29 -0
  230. package/src/config/layer.test-utils.ts +45 -0
  231. package/src/config/layer.test.ts +209 -0
  232. package/src/config/paths/index.ts +1 -0
  233. package/src/config/paths/paths.ts +36 -0
  234. package/src/config/persist/file-mode.ts +21 -0
  235. package/src/config/persist/migration.test.ts +71 -0
  236. package/src/config/persist/migration.ts +82 -0
  237. package/src/config/persist/persist.test.ts +113 -0
  238. package/src/config/persist/persist.ts +65 -0
  239. package/src/config/persist/schema.ts +37 -0
  240. package/src/config/platform.test-utils.ts +20 -0
  241. package/src/config/resolve/resolve.test-utils.ts +20 -0
  242. package/src/config/resolve/resolve.test.ts +137 -0
  243. package/src/config/resolve/resolve.ts +104 -0
  244. package/src/config/selection/index.ts +1 -0
  245. package/src/config/selection/resolve.ts +14 -0
  246. package/src/config/selection/service.ts +29 -0
  247. package/src/config/temp-home.test-utils.ts +7 -0
  248. package/src/config/types.ts +49 -0
  249. package/src/config/url/error.ts +8 -0
  250. package/src/config/url/index.ts +7 -0
  251. package/src/config/url/url.test.ts +31 -0
  252. package/src/connection/index.ts +0 -1
  253. package/src/contracts/index.ts +1 -1
  254. package/src/domain/error.ts +39 -1
  255. package/src/domain/helpers.test.ts +1 -1
  256. package/src/domain/helpers.ts +1 -1
  257. package/src/domain/model-config.ts +5 -1
  258. package/src/domain/thread-activities.test.ts +1 -1
  259. package/src/domain/thread-activities.ts +1 -1
  260. package/src/domain/thread-lifecycle.test.ts +1 -1
  261. package/src/domain/thread-lifecycle.ts +15 -1
  262. package/src/effect.test-utils.ts +25 -0
  263. package/src/index.ts +1 -1
  264. package/src/{connection/node.ts → node/connection.ts} +1 -1
  265. package/src/node/index.ts +1 -0
  266. package/src/orchestration/index.ts +1 -1
  267. package/src/orchestration/layer.ts +34 -86
  268. package/src/orchestration/service.ts +5 -1
  269. package/src/rpc/error.ts +12 -2
  270. package/src/rpc/index.ts +5 -1
  271. package/src/rpc/layer.ts +1 -1
  272. package/src/rpc/operation.ts +83 -0
  273. package/src/rpc/ws-group.ts +25 -1
  274. package/src/runtime/index.ts +2 -1
  275. package/src/runtime/layer.test-utils.ts +32 -0
  276. package/src/runtime/layer.test.ts +66 -0
  277. package/src/runtime/layer.ts +22 -11
  278. package/src/t3tools/index.ts +1 -1
  279. package/src/types/marked-terminal.d.ts +11 -0
  280. package/dist/layout.js +0 -2
  281. package/dist/scope.js +0 -2
  282. package/dist/src/cli/output-format.d.ts +0 -9
  283. package/dist/src/config/layer.d.ts +0 -22
  284. package/dist/src/config/service.d.ts +0 -22
  285. package/dist/src/environment/layer.d.ts +0 -3
  286. package/dist/src/environment/service.d.ts +0 -12
  287. package/dist/src/layout/index.d.ts +0 -1
  288. package/dist/upstream-t3code/packages/contracts/src/auth.d.ts +0 -441
  289. package/dist/upstream-t3code/packages/contracts/src/baseSchemas.d.ts +0 -38
  290. package/dist/upstream-t3code/packages/contracts/src/desktopBootstrap.d.ts +0 -14
  291. package/dist/upstream-t3code/packages/contracts/src/editor.d.ts +0 -124
  292. package/dist/upstream-t3code/packages/contracts/src/environment.d.ts +0 -64
  293. package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +0 -785
  294. package/dist/upstream-t3code/packages/contracts/src/filesystem.d.ts +0 -26
  295. package/dist/upstream-t3code/packages/contracts/src/git.d.ts +0 -500
  296. package/dist/upstream-t3code/packages/contracts/src/index.d.ts +0 -25
  297. package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +0 -496
  298. package/dist/upstream-t3code/packages/contracts/src/keybindings.d.ts +0 -133
  299. package/dist/upstream-t3code/packages/contracts/src/model.d.ts +0 -112
  300. package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +0 -7338
  301. package/dist/upstream-t3code/packages/contracts/src/project.d.ts +0 -45
  302. package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +0 -130
  303. package/dist/upstream-t3code/packages/contracts/src/providerInstance.d.ts +0 -99
  304. package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +0 -4450
  305. package/dist/upstream-t3code/packages/contracts/src/relay.d.ts +0 -1262
  306. package/dist/upstream-t3code/packages/contracts/src/relayClient.d.ts +0 -48
  307. package/dist/upstream-t3code/packages/contracts/src/remoteAccess.d.ts +0 -45
  308. package/dist/upstream-t3code/packages/contracts/src/review.d.ts +0 -37
  309. package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +0 -8710
  310. package/dist/upstream-t3code/packages/contracts/src/server.d.ts +0 -2336
  311. package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +0 -292
  312. package/dist/upstream-t3code/packages/contracts/src/sourceControl.d.ts +0 -177
  313. package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +0 -333
  314. package/dist/upstream-t3code/packages/contracts/src/vcs.d.ts +0 -148
  315. package/src/cli/auth-format.ts +0 -45
  316. package/src/cli/model-format.ts +0 -18
  317. package/src/cli/output-format.ts +0 -41
  318. package/src/cli/project-format.ts +0 -11
  319. package/src/cli/thread-format.ts +0 -121
  320. package/src/config/layer.ts +0 -105
  321. package/src/config/service.ts +0 -26
  322. package/src/environment/layer.ts +0 -12
  323. package/src/environment/service.ts +0 -13
  324. package/src/layout/index.ts +0 -1
  325. /package/dist/src/{scope → cli/scope}/index.d.ts +0 -0
  326. /package/dist/src/config/{url.d.ts → url/url.d.ts} +0 -0
  327. /package/src/{scope → cli/scope}/index.ts +0 -0
  328. /package/src/config/{url.ts → url/url.ts} +0 -0
@@ -0,0 +1,172 @@
1
+ import "vite-plus/test/config";
2
+
3
+ import * as Effect from "effect/Effect";
4
+ import * as Layer from "effect/Layer";
5
+ import * as NodeServices from "@effect/platform-node/NodeServices";
6
+ import { assert, describe, it } from "@effect/vitest";
7
+ import { fromPartial } from "@total-typescript/shoehorn";
8
+ import type {
9
+ ClientOrchestrationCommand,
10
+ OrchestrationShellSnapshot,
11
+ OrchestrationThread,
12
+ OrchestrationThreadShell,
13
+ } from "@t3tools/contracts";
14
+
15
+ import * as CliRuntime from "../cli/runtime/service.ts";
16
+ import { t3CliEnvConfigLayer } from "../config/env/env.test-utils.ts";
17
+ import { T3Orchestration, type Orchestration } from "../orchestration/service.ts";
18
+ import { makeThreadApplication } from "./threads.ts";
19
+
20
+ function makeThread(
21
+ id: string,
22
+ projectId: string,
23
+ archivedAt: string | null,
24
+ ): OrchestrationThreadShell {
25
+ return fromPartial({
26
+ id,
27
+ projectId,
28
+ archivedAt,
29
+ title: `thread-${id}`,
30
+ updatedAt: "2026-01-01T00:00:00.000Z",
31
+ });
32
+ }
33
+
34
+ function makeSnapshot(threads: OrchestrationThreadShell[]): OrchestrationShellSnapshot {
35
+ return fromPartial({
36
+ projects: [{ id: "proj-1", workspaceRoot: "/workspace" }],
37
+ threads,
38
+ });
39
+ }
40
+
41
+ function makeOrchestrationLayer(input: {
42
+ readonly active?: OrchestrationShellSnapshot;
43
+ readonly archived?: OrchestrationShellSnapshot;
44
+ readonly threadSnapshot?: OrchestrationThread;
45
+ readonly onDispatch?: (command: ClientOrchestrationCommand) => void;
46
+ }) {
47
+ return Layer.succeed(
48
+ T3Orchestration,
49
+ fromPartial<Orchestration>({
50
+ getShellSnapshot: () => Effect.succeed(input.active ?? makeSnapshot([])),
51
+ getArchivedShellSnapshot: () => Effect.succeed(input.archived ?? makeSnapshot([])),
52
+ getThreadSnapshot: () => Effect.succeed(input.threadSnapshot ?? fromPartial({})),
53
+ dispatch: (command: ClientOrchestrationCommand) => {
54
+ input.onDispatch?.(command);
55
+ return Effect.succeed({ sequence: 42 });
56
+ },
57
+ }),
58
+ );
59
+ }
60
+
61
+ const testLayer = (orchestration: Layer.Layer<T3Orchestration>) =>
62
+ Layer.mergeAll(
63
+ orchestration,
64
+ NodeServices.layer,
65
+ CliRuntime.layer,
66
+ t3CliEnvConfigLayer("/tmp/t3cli-test"),
67
+ );
68
+
69
+ describe("interruptThread", () => {
70
+ it.layer(NodeServices.layer)("interruptThread", (t) => {
71
+ t.effect("dispatches thread.turn.interrupt with activeTurnId from snapshot", () =>
72
+ Effect.gen(function* () {
73
+ let dispatched: ClientOrchestrationCommand | undefined;
74
+ const threadSnapshot = fromPartial<OrchestrationThread>({
75
+ id: "thread-1",
76
+ session: {
77
+ activeTurnId: "turn-1",
78
+ },
79
+ });
80
+ const app = yield* makeThreadApplication().pipe(
81
+ Effect.provide(
82
+ testLayer(
83
+ makeOrchestrationLayer({
84
+ threadSnapshot,
85
+ onDispatch: (command) => {
86
+ dispatched = command;
87
+ },
88
+ }),
89
+ ),
90
+ ),
91
+ );
92
+
93
+ const dispatch = yield* app.interruptThread("thread-1");
94
+ assert.equal(dispatch.sequence, 42);
95
+ assert.equal(dispatched?.type, "thread.turn.interrupt");
96
+ if (dispatched?.type === "thread.turn.interrupt") {
97
+ assert.equal(dispatched.threadId, "thread-1");
98
+ assert.equal(dispatched.turnId, "turn-1");
99
+ }
100
+ }),
101
+ );
102
+ });
103
+ });
104
+
105
+ describe("listThreads", () => {
106
+ it.layer(NodeServices.layer)("listThreads", (t) => {
107
+ t.effect("returns active threads by default", () =>
108
+ Effect.gen(function* () {
109
+ const active = makeSnapshot([
110
+ makeThread("active-1", "proj-1", null),
111
+ makeThread("active-2", "proj-1", null),
112
+ ]);
113
+ const archived = makeSnapshot([
114
+ makeThread("archived-1", "proj-1", "2026-01-01T00:00:00.000Z"),
115
+ ]);
116
+ const app = yield* makeThreadApplication().pipe(
117
+ Effect.provide(testLayer(makeOrchestrationLayer({ active, archived }))),
118
+ );
119
+
120
+ const result = yield* app.listThreads("proj-1");
121
+ assert.deepEqual(
122
+ result.threads.map((thread) => thread.id),
123
+ ["active-1", "active-2"],
124
+ );
125
+ }),
126
+ );
127
+
128
+ t.effect("returns archived threads when include is archived", () =>
129
+ Effect.gen(function* () {
130
+ const active = makeSnapshot([makeThread("active-1", "proj-1", null)]);
131
+ const archived = makeSnapshot([
132
+ makeThread("archived-1", "proj-1", "2026-01-01T00:00:00.000Z"),
133
+ makeThread("archived-2", "proj-1", "2026-01-02T00:00:00.000Z"),
134
+ ]);
135
+ const app = yield* makeThreadApplication().pipe(
136
+ Effect.provide(testLayer(makeOrchestrationLayer({ active, archived }))),
137
+ );
138
+
139
+ const result = yield* app.listThreads("proj-1", { include: "archived" });
140
+ assert.deepEqual(
141
+ result.threads.map((thread) => thread.id),
142
+ ["archived-1", "archived-2"],
143
+ );
144
+ assert.isTrue(result.threads.every((thread) => thread.archivedAt !== null));
145
+ }),
146
+ );
147
+
148
+ t.effect("returns merged threads without duplicates when include is all", () =>
149
+ Effect.gen(function* () {
150
+ const active = makeSnapshot([
151
+ makeThread("active-1", "proj-1", null),
152
+ makeThread("shared", "proj-1", null),
153
+ ]);
154
+ const archived = makeSnapshot([
155
+ makeThread("archived-1", "proj-1", "2026-01-01T00:00:00.000Z"),
156
+ makeThread("shared", "proj-1", "2026-01-02T00:00:00.000Z"),
157
+ ]);
158
+ const app = yield* makeThreadApplication().pipe(
159
+ Effect.provide(testLayer(makeOrchestrationLayer({ active, archived }))),
160
+ );
161
+
162
+ const result = yield* app.listThreads("proj-1", { include: "all" });
163
+ assert.deepEqual(
164
+ result.threads.map((thread) => thread.id).toSorted(),
165
+ ["active-1", "archived-1", "shared"].toSorted(),
166
+ );
167
+ const shared = result.threads.find((thread) => thread.id === "shared");
168
+ assert.equal(shared?.archivedAt, "2026-01-02T00:00:00.000Z");
169
+ }),
170
+ );
171
+ });
172
+ });
@@ -1,25 +1,36 @@
1
1
  import * as Crypto from "effect/Crypto";
2
2
  import * as Effect from "effect/Effect";
3
3
  import * as Path from "effect/Path";
4
-
5
- import { Environment } from "../environment/service.ts";
4
+ import * as Stream from "effect/Stream";
5
+ import { CliRuntime } from "../cli/runtime/service.ts";
6
6
  import { T3Orchestration } from "../orchestration/service.ts";
7
- import { ProjectLookupError, ThreadSessionError } from "../domain/error.ts";
7
+ import { ProjectLookupError, ThreadLookupError, ThreadSessionError } from "../domain/error.ts";
8
8
  import { resolveProjectScope } from "../domain/helpers.ts";
9
- import { type StartThreadInput } from "./service.ts";
10
- import type { SendThreadInput, CallbackThreadInput } from "./service.ts";
9
+ import { type ListThreadsInclude, type StartThreadInput } from "./service.ts";
10
+ import type { CallbackThreadInput, SendThreadInput } from "./service.ts";
11
+ import type { T3ThreadApplicationService } from "./service.ts";
12
+ import type { OrchestrationThreadShell } from "@t3tools/contracts";
11
13
  import { mergeModelOptions } from "./model-selection.ts";
12
14
  import { derivePendingApprovals, derivePendingUserInputs } from "../domain/thread-activities.ts";
13
- import { threadStatus, type ThreadLifecycleStatus } from "../domain/thread-lifecycle.ts";
14
- import type { OrchestrationThread } from "#t3tools/contracts";
15
- import type { ProviderApprovalDecision, ProviderUserInputAnswers } from "#t3tools/contracts";
15
+ import {
16
+ sessionNeedsStopBeforeDelete,
17
+ threadStatus,
18
+ type ThreadLifecycleStatus,
19
+ } from "../domain/thread-lifecycle.ts";
20
+ import type { OrchestrationThread } from "@t3tools/contracts";
21
+ import type { ProviderApprovalDecision, ProviderUserInputAnswers } from "@t3tools/contracts";
16
22
  import {
17
23
  makeThreadApprovalRespondCommand,
18
24
  makeThreadArchiveCommand,
25
+ makeThreadDeleteCommand,
26
+ makeThreadInterruptCommand,
27
+ makeThreadSessionStopCommand,
19
28
  makeThreadStartCommands,
20
29
  makeThreadTurnContinueCommand,
30
+ makeThreadUnarchiveCommand,
21
31
  makeThreadUserInputRespondCommand,
22
32
  } from "./thread-commands.ts";
33
+ import { makeUpdateThread } from "./thread-update.ts";
23
34
  import {
24
35
  waitForThread as waitForThreadUntilComplete,
25
36
  watchThread as watchThreadEvents,
@@ -30,9 +41,25 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
30
41
  const orchestration = yield* T3Orchestration;
31
42
  const crypto = yield* Crypto.Crypto;
32
43
  const path = yield* Path.Path;
33
- const environment = yield* Environment;
34
- const listThreads = Effect.fn("T3ApplicationLive.listThreads")(function* (projectRef: string) {
35
- const snapshot = yield* orchestration.getShellSnapshot();
44
+ const cliRuntime = yield* CliRuntime;
45
+ const awaitShellSequence = (sequence: number) =>
46
+ waitForShellSequence({ sequence }).pipe(Effect.provideService(T3Orchestration, orchestration));
47
+ const awaitThreadCompletion = (threadId: string) =>
48
+ waitForThreadUntilComplete({ threadId }).pipe(
49
+ Effect.provideService(T3Orchestration, orchestration),
50
+ );
51
+ const streamThreadEvents = (threadId: string) =>
52
+ watchThreadEvents({ threadId }).pipe(Stream.provideService(T3Orchestration, orchestration));
53
+ const listThreads = Effect.fn("T3ApplicationLive.listThreads")(function* (
54
+ projectRef: string,
55
+ options?: {
56
+ readonly include?: ListThreadsInclude;
57
+ },
58
+ ) {
59
+ const include = options?.include ?? "active";
60
+ const snapshot = yield* loadThreadsSnapshot(include).pipe(
61
+ Effect.provideService(T3Orchestration, orchestration),
62
+ );
36
63
  const scope = yield* resolveProjectScope(snapshot, {
37
64
  ref: projectRef,
38
65
  }).pipe(Effect.provideService(Path.Path, path));
@@ -61,6 +88,55 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
61
88
  );
62
89
  return yield* orchestration.dispatch(command);
63
90
  });
91
+ const unarchiveThread = Effect.fn("T3ApplicationLive.unarchiveThread")(function* (
92
+ threadId: string,
93
+ ) {
94
+ const command = yield* makeThreadUnarchiveCommand(threadId).pipe(
95
+ Effect.provideService(Crypto.Crypto, crypto),
96
+ );
97
+ return yield* orchestration.dispatch(command);
98
+ });
99
+ const interruptThread = Effect.fn("T3ApplicationLive.interruptThread")(function* (
100
+ threadId: string,
101
+ ) {
102
+ const snapshot = yield* orchestration.getThreadSnapshot(threadId);
103
+ const activeTurnId = snapshot.session?.activeTurnId ?? undefined;
104
+ const command = yield* makeThreadInterruptCommand({
105
+ threadId,
106
+ ...(activeTurnId !== undefined ? { turnId: activeTurnId } : {}),
107
+ }).pipe(Effect.provideService(Crypto.Crypto, crypto));
108
+ return yield* orchestration.dispatch(command);
109
+ });
110
+ const deleteThread = Effect.fn("T3ApplicationLive.deleteThread")(function* (threadId: string) {
111
+ const snapshot = yield* loadThreadsSnapshot("all").pipe(
112
+ Effect.provideService(T3Orchestration, orchestration),
113
+ );
114
+ const thread = snapshot.threads.find((item) => item.id === threadId);
115
+ if (thread === undefined) {
116
+ return yield* Effect.fail(
117
+ new ThreadLookupError({
118
+ message: `thread not found: ${threadId}`,
119
+ threadId,
120
+ }),
121
+ );
122
+ }
123
+ if (sessionNeedsStopBeforeDelete(thread.session)) {
124
+ const stopCommand = yield* makeThreadSessionStopCommand(threadId).pipe(
125
+ Effect.provideService(Crypto.Crypto, crypto),
126
+ );
127
+ yield* orchestration.dispatch(stopCommand);
128
+ }
129
+ const command = yield* makeThreadDeleteCommand(threadId).pipe(
130
+ Effect.provideService(Crypto.Crypto, crypto),
131
+ );
132
+ const dispatch = yield* orchestration.dispatch(command);
133
+ return { threadId, dispatch };
134
+ });
135
+ const updateThread: T3ThreadApplicationService["updateThread"] = (input) =>
136
+ makeUpdateThread()(input).pipe(
137
+ Effect.provideService(T3Orchestration, orchestration),
138
+ Effect.provideService(Crypto.Crypto, crypto),
139
+ );
64
140
  const startThread = Effect.fn("T3ApplicationLive.startThread")(function* (
65
141
  startInput: StartThreadInput,
66
142
  policy?: {
@@ -73,7 +149,7 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
73
149
  return yield* Effect.fail(
74
150
  new ProjectLookupError({
75
151
  message: "project is required",
76
- ref: environment.cwd,
152
+ ref: cliRuntime.cwd,
77
153
  }),
78
154
  );
79
155
  }
@@ -96,20 +172,14 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
96
172
  serverConfig,
97
173
  }).pipe(Effect.provideService(Crypto.Crypto, crypto));
98
174
  const createDispatch = yield* orchestration.dispatch(commands.createCommand);
99
- yield* waitForShellSequence({
100
- orchestration,
101
- sequence: createDispatch.sequence,
102
- });
175
+ yield* awaitShellSequence(createDispatch.sequence);
103
176
  const dispatch = yield* orchestration.dispatch(commands.turnCommand);
104
177
  const threadId = commands.threadId;
105
178
  const until = policy?.until ?? "dispatch";
106
179
  if (until === "dispatch") {
107
180
  return { dispatch, project: scope.project, threadId };
108
181
  }
109
- yield* waitForShellSequence({
110
- orchestration,
111
- sequence: dispatch.sequence,
112
- });
182
+ yield* awaitShellSequence(dispatch.sequence);
113
183
  if (until === "visible") {
114
184
  const thread = yield* Effect.scoped(
115
185
  Effect.gen(function* () {
@@ -119,10 +189,7 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
119
189
  );
120
190
  return { dispatch, project: scope.project, threadId, thread };
121
191
  }
122
- const thread = yield* waitForThreadUntilComplete({
123
- orchestration,
124
- threadId,
125
- });
192
+ const thread = yield* awaitThreadCompletion(threadId);
126
193
  yield* failIfThreadError(thread);
127
194
  return { dispatch, project: scope.project, threadId, thread };
128
195
  });
@@ -148,10 +215,7 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
148
215
  if (until === "dispatch") {
149
216
  return { dispatch, threadId: input.threadId };
150
217
  }
151
- yield* waitForShellSequence({
152
- orchestration,
153
- sequence: dispatch.sequence,
154
- });
218
+ yield* awaitShellSequence(dispatch.sequence);
155
219
  if (until === "visible") {
156
220
  const thread = yield* Effect.scoped(
157
221
  Effect.gen(function* () {
@@ -161,33 +225,20 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
161
225
  );
162
226
  return { dispatch, threadId: input.threadId, thread };
163
227
  }
164
- const thread = yield* waitForThreadUntilComplete({
165
- orchestration,
166
- threadId: input.threadId,
167
- });
228
+ const thread = yield* awaitThreadCompletion(input.threadId);
168
229
  yield* failIfThreadError(thread);
169
230
  return { dispatch, threadId: input.threadId, thread };
170
231
  });
171
- const watchThread = (threadId: string) =>
172
- watchThreadEvents({
173
- orchestration,
174
- threadId,
175
- });
232
+ const watchThread = (threadId: string) => streamThreadEvents(threadId);
176
233
  const waitForThread = Effect.fn("T3ApplicationLive.waitForThread")(function* (threadId: string) {
177
- const thread = yield* waitForThreadUntilComplete({
178
- orchestration,
179
- threadId,
180
- });
234
+ const thread = yield* awaitThreadCompletion(threadId);
181
235
  yield* failIfThreadError(thread);
182
236
  return thread;
183
237
  });
184
238
  const callbackThread = Effect.fn("T3ApplicationLive.callbackThread")(function* (
185
239
  input: CallbackThreadInput,
186
240
  ) {
187
- yield* waitForThreadUntilComplete({
188
- orchestration,
189
- threadId: input.fromThreadId,
190
- });
241
+ yield* awaitThreadCompletion(input.fromThreadId);
191
242
  const result = yield* sendThread(
192
243
  { threadId: input.targetThreadId, message: input.prompt },
193
244
  { until: "dispatch" },
@@ -220,6 +271,10 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
220
271
  return {
221
272
  approveThread,
222
273
  archiveThread,
274
+ deleteThread,
275
+ interruptThread,
276
+ updateThread,
277
+ unarchiveThread,
223
278
  listThreads,
224
279
  getThreadMessages,
225
280
  respondToThread,
@@ -229,9 +284,37 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
229
284
  watchThread,
230
285
  waitForThread,
231
286
  callbackThread,
287
+ } satisfies T3ThreadApplicationService;
288
+ });
289
+
290
+ const loadThreadsSnapshot = Effect.fn("loadThreadsSnapshot")(function* (
291
+ include: ListThreadsInclude,
292
+ ) {
293
+ const orchestration = yield* T3Orchestration;
294
+ if (include === "active") {
295
+ return yield* orchestration.getShellSnapshot();
296
+ }
297
+ if (include === "archived") {
298
+ return yield* orchestration.getArchivedShellSnapshot();
299
+ }
300
+ const [activeSnapshot, archivedSnapshot] = yield* Effect.all([
301
+ orchestration.getShellSnapshot(),
302
+ orchestration.getArchivedShellSnapshot(),
303
+ ]);
304
+ return {
305
+ ...activeSnapshot,
306
+ threads: dedupeThreadsById([...activeSnapshot.threads, ...archivedSnapshot.threads]),
232
307
  };
233
308
  });
234
309
 
310
+ function dedupeThreadsById(threads: ReadonlyArray<OrchestrationThreadShell>) {
311
+ const byId = new Map<string, OrchestrationThreadShell>();
312
+ for (const thread of threads) {
313
+ byId.set(thread.id, thread);
314
+ }
315
+ return [...byId.values()];
316
+ }
317
+
235
318
  export type ThreadShow = {
236
319
  readonly id: string;
237
320
  readonly projectId: string;
package/src/auth/error.ts CHANGED
@@ -3,7 +3,8 @@ import * as Schema from "effect/Schema";
3
3
  import { PlatformError } from "effect/PlatformError";
4
4
  import { HttpClientError } from "effect/unstable/http";
5
5
 
6
- import { ConfigError, UrlError } from "../config/error.ts";
6
+ import { ConfigError } from "../config/error.ts";
7
+ import { UrlError } from "../config/url/error.ts";
7
8
 
8
9
  export class AuthPairingUrlError extends Schema.TaggedErrorClass<AuthPairingUrlError>()(
9
10
  "AuthPairingUrlError",
package/src/auth/index.ts CHANGED
@@ -17,7 +17,13 @@ export type { AuthError } from "./error.ts";
17
17
  export type { AuthSessionState, AuthWebSocketTicketResult } from "./schema.ts";
18
18
  export type {
19
19
  AuthConfigInput,
20
+ AuthEnvironmentListItem,
21
+ AuthEnvironmentSummary,
22
+ AuthResolvedConfig,
20
23
  AuthSessionRole,
24
+ AuthStatusResult,
25
+ AuthUnpairResult,
26
+ AuthUseResult,
21
27
  LocalAuthInput,
22
28
  LocalAuthOriginInput,
23
29
  LocalAuthResult,
@@ -25,4 +31,5 @@ export type {
25
31
  LocalAuthTokenResult,
26
32
  PairingUrl,
27
33
  PairResult,
34
+ PersistEnvironmentInput,
28
35
  } from "./type.ts";
@@ -0,0 +1,34 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+
4
+ import { T3AuthLive } from "./layer.ts";
5
+ import { T3LocalAuth } from "./local.ts";
6
+ import { T3AuthPairing } from "./pairing.ts";
7
+ import { T3AuthTransport } from "./transport.ts";
8
+ import { t3ConfigDepsLayer } from "../config/layer.test-utils.ts";
9
+ import { ConfigPlatformLayer } from "../config/platform.test-utils.ts";
10
+
11
+ export function t3AuthDepsLayer(homeDir: string) {
12
+ return T3AuthLive.pipe(
13
+ Layer.provide(
14
+ Layer.mergeAll(
15
+ t3ConfigDepsLayer(homeDir),
16
+ Layer.succeed(T3AuthTransport)({
17
+ bootstrapBearer: () => Effect.die("unused in test"),
18
+ getSession: () => Effect.succeed({ authenticated: false }),
19
+ issueWebSocketTicket: () => Effect.die("unused in test"),
20
+ }),
21
+ Layer.succeed(T3LocalAuth)({
22
+ local: () => Effect.die("unused in test"),
23
+ }),
24
+ Layer.succeed(T3AuthPairing)({
25
+ pair: () => Effect.die("unused in test"),
26
+ }),
27
+ ),
28
+ ),
29
+ );
30
+ }
31
+
32
+ export function t3AuthLayerTest(homeDir: string) {
33
+ return Layer.mergeAll(ConfigPlatformLayer, t3AuthDepsLayer(homeDir));
34
+ }