t3code-cli 0.14.1 → 0.15.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 (185) hide show
  1. package/README.md +37 -1
  2. package/dist/application.d.ts +2 -2
  3. package/dist/application.js +1 -1
  4. package/dist/auth.d.ts +1 -1
  5. package/dist/auth.js +1 -1
  6. package/dist/bin.js +1305 -424
  7. package/dist/cli.d.ts +1 -1
  8. package/dist/cli.js +1 -1
  9. package/dist/client-runtime/authorization.d.ts +1 -1
  10. package/dist/client-runtime/authorization.js +1 -1
  11. package/dist/client-runtime/connection.d.ts +1 -1
  12. package/dist/client-runtime/connection.js +1 -1
  13. package/dist/client-runtime/environment.d.ts +7 -1
  14. package/dist/client-runtime/environment.js +1 -1
  15. package/dist/client-runtime/errors.js +1 -1
  16. package/dist/client-runtime/operations/projects.d.ts +2 -2
  17. package/dist/client-runtime/operations/projects.js +2 -2
  18. package/dist/client-runtime/operations.d.ts +2 -2
  19. package/dist/client-runtime/operations.js +2 -2
  20. package/dist/client-runtime/platform.d.ts +1 -1
  21. package/dist/client-runtime/platform.js +1 -1
  22. package/dist/client-runtime/relay.d.ts +13 -2
  23. package/dist/client-runtime/relay.js +12 -2
  24. package/dist/client-runtime/rpc.d.ts +2 -2
  25. package/dist/client-runtime/rpc.js +2 -2
  26. package/dist/client-runtime/state/assets.d.ts +17 -11
  27. package/dist/client-runtime/state/assets.js +1 -1
  28. package/dist/client-runtime/state/auth.d.ts +1 -1
  29. package/dist/client-runtime/state/auth.js +1 -1
  30. package/dist/client-runtime/state/connections.js +1 -1
  31. package/dist/client-runtime/state/entities.d.ts +1 -1
  32. package/dist/client-runtime/state/entities.js +1 -1
  33. package/dist/client-runtime/state/filesystem.d.ts +1 -1
  34. package/dist/client-runtime/state/filesystem.js +1 -1
  35. package/dist/client-runtime/state/git.d.ts +6 -5
  36. package/dist/client-runtime/state/git.js +1 -1
  37. package/dist/client-runtime/state/models.d.ts +1 -1
  38. package/dist/client-runtime/state/models.js +1 -1
  39. package/dist/client-runtime/state/orchestration.d.ts +42 -2
  40. package/dist/client-runtime/state/orchestration.js +13 -1
  41. package/dist/client-runtime/state/presentation.d.ts +15 -3
  42. package/dist/client-runtime/state/presentation.js +1 -1
  43. package/dist/client-runtime/state/preview.d.ts +58 -48
  44. package/dist/client-runtime/state/preview.js +3 -2
  45. package/dist/client-runtime/state/project-grouping.d.ts +26 -2
  46. package/dist/client-runtime/state/project-grouping.js +83 -3
  47. package/dist/client-runtime/state/projects.d.ts +24 -18
  48. package/dist/client-runtime/state/projects.js +1 -1
  49. package/dist/client-runtime/state/pull-requests.d.ts +478 -0
  50. package/dist/client-runtime/state/pull-requests.js +172 -0
  51. package/dist/client-runtime/state/relay.d.ts +1 -1
  52. package/dist/client-runtime/state/relay.js +1 -1
  53. package/dist/client-runtime/state/review.d.ts +16 -1
  54. package/dist/client-runtime/state/review.js +27 -6
  55. package/dist/client-runtime/state/runtime.d.ts +2 -2
  56. package/dist/client-runtime/state/runtime.js +2 -2
  57. package/dist/client-runtime/state/server.d.ts +311 -172
  58. package/dist/client-runtime/state/server.js +192 -16
  59. package/dist/client-runtime/state/session.d.ts +77 -7
  60. package/dist/client-runtime/state/session.js +32 -3
  61. package/dist/client-runtime/state/shell.d.ts +47 -7
  62. package/dist/client-runtime/state/shell.js +1 -1
  63. package/dist/client-runtime/state/source-control.d.ts +2 -2
  64. package/dist/client-runtime/state/source-control.js +1 -1
  65. package/dist/client-runtime/state/subagentRuntime.d.ts +145 -0
  66. package/dist/client-runtime/state/subagentRuntime.js +521 -0
  67. package/dist/client-runtime/state/terminal.d.ts +17 -17
  68. package/dist/client-runtime/state/terminal.js +1 -1
  69. package/dist/client-runtime/state/thread-search.d.ts +23 -0
  70. package/dist/client-runtime/state/thread-search.js +38 -0
  71. package/dist/client-runtime/state/thread-settled.d.ts +32 -4
  72. package/dist/client-runtime/state/thread-settled.js +2 -157
  73. package/dist/client-runtime/state/thread-sort.d.ts +59 -2
  74. package/dist/client-runtime/state/thread-sort.js +128 -2
  75. package/dist/client-runtime/state/threads.d.ts +115 -3
  76. package/dist/client-runtime/state/threads.js +309 -28
  77. package/dist/client-runtime/state/vcs.d.ts +14 -14
  78. package/dist/client-runtime/state/vcs.js +1 -1
  79. package/dist/config.d.ts +1 -1
  80. package/dist/config.js +1 -1
  81. package/dist/connection.d.ts +1 -1
  82. package/dist/connection.js +1 -1
  83. package/dist/contracts.d.ts +1 -1
  84. package/dist/contracts.js +1 -1
  85. package/dist/index.d.ts +1 -1
  86. package/dist/index.js +1 -1
  87. package/dist/node.d.ts +1 -1
  88. package/dist/node.js +1 -1
  89. package/dist/orchestration.d.ts +155 -6
  90. package/dist/orchestration.js +2 -2
  91. package/dist/preview.d.ts +1 -1
  92. package/dist/preview.js +1 -1
  93. package/dist/rpc.d.ts +887 -360
  94. package/dist/rpc.js +1 -1
  95. package/dist/runtime.d.ts +1 -1
  96. package/dist/runtime.js +1 -1
  97. package/dist/shared/DrainableWorker.js +1 -1
  98. package/dist/shared/KeyedCoalescingWorker.js +1 -1
  99. package/dist/shared/Net.d.ts +6 -0
  100. package/dist/shared/Net.js +2 -1
  101. package/dist/shared/advertisedEndpoint.d.ts +1 -1
  102. package/dist/shared/advertisedEndpoint.js +1 -1
  103. package/dist/shared/agentAwareness.d.ts +1 -1
  104. package/dist/shared/backgroundActivitySettings.d.ts +1 -1
  105. package/dist/shared/composerInlineTokens.js +1 -1
  106. package/dist/shared/connectAuth.d.ts +23 -4
  107. package/dist/shared/connectAuth.js +40 -7
  108. package/dist/shared/devHome.js +1 -1
  109. package/dist/shared/dpop.js +1 -1
  110. package/dist/shared/git.d.ts +1 -1
  111. package/dist/shared/httpObservability.js +1 -1
  112. package/dist/shared/httpReadiness.js +1 -1
  113. package/dist/shared/keybindings.d.ts +2 -2
  114. package/dist/shared/keybindings.js +15 -1
  115. package/dist/shared/model.d.ts +1 -1
  116. package/dist/shared/oauthScope.js +1 -1
  117. package/dist/shared/observability.d.ts +8 -1
  118. package/dist/shared/observability.js +49 -5
  119. package/dist/shared/path.js +1 -1
  120. package/dist/shared/preview.js +1 -1
  121. package/dist/shared/previewViewport.d.ts +1 -1
  122. package/dist/shared/previewViewport.js +1 -1
  123. package/dist/shared/projectFavicon.d.ts +2 -1
  124. package/dist/shared/projectFavicon.js +13 -1
  125. package/dist/shared/projectScripts.d.ts +1 -1
  126. package/dist/shared/relayAuth.js +1 -1
  127. package/dist/shared/relayClient.d.ts +1 -1
  128. package/dist/shared/relayClient.js +1 -1
  129. package/dist/shared/relayJwt.js +1 -1
  130. package/dist/shared/relayTracing.js +1 -1
  131. package/dist/shared/relayUrl.js +1 -1
  132. package/dist/shared/remote.js +1 -1
  133. package/dist/shared/schemaYaml.js +3 -3
  134. package/dist/shared/semver.js +1 -1
  135. package/dist/shared/serverSettings.d.ts +1 -1
  136. package/dist/shared/serverSettings.js +1 -1
  137. package/dist/shared/shell.d.ts +6 -1
  138. package/dist/shared/shell.js +42 -4
  139. package/dist/shared/sourceControl.d.ts +1 -1
  140. package/dist/shared/t3ProjectFile.d.ts +9 -1
  141. package/dist/shared/t3ProjectFile.js +12 -2
  142. package/dist/shared/terminalLabels.d.ts +1 -1
  143. package/dist/shared/threadEnvMode.d.ts +29 -0
  144. package/dist/shared/threadEnvMode.js +24 -0
  145. package/dist/shared/toolActivity.d.ts +1 -1
  146. package/dist/shared/usageFormat.d.ts +34 -0
  147. package/dist/shared/usageFormat.js +176 -0
  148. package/dist/shared/usageMerge.d.ts +78 -0
  149. package/dist/shared/usageMerge.js +241 -0
  150. package/dist/shared.d.ts +8268 -1372
  151. package/dist/shared.js +22185 -19774
  152. package/dist/t3tools.d.ts +2 -2
  153. package/dist/t3tools.js +2 -2
  154. package/package.json +5 -5
  155. package/src/application/error.ts +2 -2
  156. package/src/application/index.ts +2 -0
  157. package/src/application/service.ts +43 -5
  158. package/src/application/thread-commands.ts +78 -0
  159. package/src/application/threads.ts +156 -17
  160. package/src/cli/app.ts +4 -0
  161. package/src/cli/ask-lifecycle.ts +331 -0
  162. package/src/cli/ask.ts +390 -0
  163. package/src/cli/error.ts +70 -0
  164. package/src/cli/format/thread.ts +49 -7
  165. package/src/cli/search.ts +48 -0
  166. package/src/cli/thread.ts +12 -0
  167. package/src/cli/threads/messages.ts +33 -3
  168. package/src/cli/threads/pin.ts +46 -0
  169. package/src/cli/threads/send.ts +5 -1
  170. package/src/cli/threads/settle.ts +46 -0
  171. package/src/cli/threads/snooze.ts +117 -0
  172. package/src/cli/threads/start.ts +6 -1
  173. package/src/cli/threads/unpin.ts +48 -0
  174. package/src/cli/threads/unsettle.ts +48 -0
  175. package/src/cli/threads/unsnooze.ts +48 -0
  176. package/src/config/persist/schema.ts +1 -1
  177. package/src/connection/prepared.ts +12 -2
  178. package/src/orchestration/error.ts +10 -0
  179. package/src/orchestration/index.ts +1 -0
  180. package/src/orchestration/layer.ts +78 -0
  181. package/src/orchestration/service.ts +16 -3
  182. package/src/rpc/error.ts +2 -0
  183. package/src/rpc/operation.ts +2 -0
  184. package/src/rpc/ws-group.ts +3 -0
  185. package/src/runtime/layer.ts +9 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "t3code-cli",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "CLI for t3code",
5
5
  "keywords": [
6
6
  "claude",
@@ -106,9 +106,9 @@
106
106
  "./package.json": "./package.json"
107
107
  },
108
108
  "dependencies": {
109
- "@effect/platform-node": "4.0.0-beta.102",
109
+ "@effect/platform-node": "4.0.0-beta.103",
110
110
  "@napi-rs/keyring": "^1.3.0",
111
- "effect": "4.0.0-beta.102",
111
+ "effect": "4.0.0-beta.103",
112
112
  "marked": "^15.0.12",
113
113
  "marked-terminal": "^7.3.0",
114
114
  "string-width": "^8.2.2",
@@ -116,7 +116,7 @@
116
116
  },
117
117
  "devDependencies": {
118
118
  "@changesets/cli": "^2.31.1",
119
- "@effect/vitest": "4.0.0-beta.102",
119
+ "@effect/vitest": "4.0.0-beta.103",
120
120
  "@t3tools/client-runtime": "link:upstream-t3code/packages/client-runtime",
121
121
  "@t3tools/shared": "link:upstream-t3code/packages/shared",
122
122
  "@total-typescript/shoehorn": "^0.1.2",
@@ -124,7 +124,7 @@
124
124
  "typescript": "^7.0.2",
125
125
  "vite-plus": "^0.2.6",
126
126
  "yaml": "^2.9.0",
127
- "@t3tools/contracts": "0.0.30"
127
+ "@t3tools/contracts": "0.0.33"
128
128
  },
129
129
  "engines": {
130
130
  "node": ">=24.0.0"
@@ -1,4 +1,4 @@
1
1
  import type { DomainError } from "../domain/error.ts";
2
- import type { RpcError } from "../rpc/error.ts";
2
+ import type { OrchestrationError } from "../orchestration/service.ts";
3
3
 
4
- export type ApplicationError = DomainError | RpcError;
4
+ export type ApplicationError = DomainError | OrchestrationError;
@@ -23,6 +23,7 @@ export type {
23
23
  ProjectActionRunResult,
24
24
  ProjectActionSelector,
25
25
  SendThreadInput,
26
+ SnoozeThreadInput,
26
27
  StartThreadInput,
27
28
  StartThreadPolicy,
28
29
  T3ActionApplicationService,
@@ -31,3 +32,4 @@ export type {
31
32
  WaitEvent,
32
33
  } from "./service.ts";
33
34
  export type { ApplicationError } from "./error.ts";
35
+ export type { ThreadSearchResult } from "./threads.ts";
@@ -6,8 +6,11 @@ import type {
6
6
  ModelSelection,
7
7
  OrchestrationMessage,
8
8
  OrchestrationProjectShell,
9
+ OrchestrationSearchThreadsInput,
9
10
  OrchestrationShellSnapshot,
10
11
  OrchestrationThread,
12
+ OrchestrationThreadDetailSnapshot,
13
+ OrchestrationThreadDetailWindow,
11
14
  OrchestrationThreadShell,
12
15
  ProjectScript,
13
16
  ProjectScriptIcon,
@@ -20,7 +23,7 @@ import type {
20
23
  } from "@t3tools/contracts";
21
24
 
22
25
  import type { ApplicationError } from "./error.ts";
23
- import type { ThreadShow } from "./threads.ts";
26
+ import type { ThreadSearchResult, ThreadShow } from "./threads.ts";
24
27
 
25
28
  export type StartThreadInput = {
26
29
  readonly projectRef?: string;
@@ -61,8 +64,18 @@ export type CallbackThreadInput = {
61
64
  readonly prompt: string;
62
65
  };
63
66
 
67
+ export interface SnoozeThreadInput {
68
+ readonly threadId: string;
69
+ readonly snoozedUntil: string;
70
+ }
71
+
64
72
  export type ListThreadsInclude = "active" | "archived" | "all";
65
73
 
74
+ export interface GetThreadMessagesInput {
75
+ readonly threadId: string;
76
+ readonly window?: OrchestrationThreadDetailWindow;
77
+ }
78
+
66
79
  export type UpdateThreadInput = {
67
80
  readonly threadId: string;
68
81
  readonly title?: string;
@@ -73,9 +86,13 @@ export type UpdateThreadInput = {
73
86
  readonly worktreePath?: string | null;
74
87
  };
75
88
 
76
- export type StartThreadPolicy = {
89
+ export interface ThreadDispatchPolicy {
77
90
  readonly until: "dispatch" | "visible" | "complete";
78
- };
91
+ }
92
+
93
+ export interface StartThreadPolicy extends ThreadDispatchPolicy {
94
+ readonly onThreadCreated?: (threadId: string) => Effect.Effect<void>;
95
+ }
79
96
 
80
97
  export type WaitEvent =
81
98
  | { readonly type: "thread"; readonly thread: OrchestrationThread }
@@ -196,6 +213,10 @@ export class T3ProjectApplication extends Context.Service<
196
213
  >()("t3cli/T3ProjectApplication") {}
197
214
 
198
215
  export type T3ThreadApplicationService = {
216
+ readonly awaitShellSequence: (sequence: number) => Effect.Effect<void, ApplicationError>;
217
+ readonly searchThreads: (
218
+ input: OrchestrationSearchThreadsInput,
219
+ ) => Effect.Effect<ReadonlyArray<ThreadSearchResult>, ApplicationError>;
199
220
  readonly listThreads: (
200
221
  projectRef: string,
201
222
  options?: {
@@ -209,8 +230,11 @@ export type T3ThreadApplicationService = {
209
230
  ApplicationError
210
231
  >;
211
232
  readonly getThreadMessages: (
233
+ input: GetThreadMessagesInput,
234
+ ) => Effect.Effect<OrchestrationThreadDetailSnapshot, ApplicationError>;
235
+ readonly getThreadSummary: (
212
236
  threadId: string,
213
- ) => Effect.Effect<OrchestrationThread, ApplicationError>;
237
+ ) => Effect.Effect<OrchestrationThreadShell, ApplicationError>;
214
238
  readonly showThread: (threadId: string) => Effect.Effect<ThreadShow, ApplicationError>;
215
239
  readonly approveThread: (input: {
216
240
  readonly threadId: string;
@@ -230,7 +254,19 @@ export type T3ThreadApplicationService = {
230
254
  >;
231
255
  readonly archiveThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
232
256
  readonly interruptThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
257
+ readonly interruptThreadTurn: (
258
+ threadId: string,
259
+ turnId: string,
260
+ ) => Effect.Effect<DispatchResult | undefined, ApplicationError>;
261
+ readonly pinThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
262
+ readonly settleThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
263
+ readonly snoozeThread: (
264
+ input: SnoozeThreadInput,
265
+ ) => Effect.Effect<DispatchResult, ApplicationError>;
233
266
  readonly unarchiveThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
267
+ readonly unpinThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
268
+ readonly unsnoozeThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
269
+ readonly unsettleThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
234
270
  readonly deleteThread: (
235
271
  threadId: string,
236
272
  ) => Effect.Effect<
@@ -246,6 +282,7 @@ export type T3ThreadApplicationService = {
246
282
  ) => Effect.Effect<
247
283
  {
248
284
  readonly dispatch: DispatchResult;
285
+ readonly messageId: string;
249
286
  readonly project: OrchestrationProjectShell;
250
287
  readonly threadId: string;
251
288
  readonly thread?: OrchestrationThread;
@@ -254,10 +291,11 @@ export type T3ThreadApplicationService = {
254
291
  >;
255
292
  readonly sendThread: (
256
293
  input: SendThreadInput,
257
- policy?: StartThreadPolicy,
294
+ policy?: ThreadDispatchPolicy,
258
295
  ) => Effect.Effect<
259
296
  {
260
297
  readonly dispatch: DispatchResult;
298
+ readonly messageId: string;
261
299
  readonly threadId: string;
262
300
  readonly thread?: OrchestrationThread;
263
301
  },
@@ -115,6 +115,84 @@ export const makeThreadUnarchiveCommand = Effect.fn("makeThreadUnarchiveCommand"
115
115
  } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.unarchive" }>;
116
116
  });
117
117
 
118
+ export const makeThreadSettleCommand = Effect.fn("makeThreadSettleCommand")(function* (
119
+ threadId: string,
120
+ ) {
121
+ const crypto = yield* Crypto.Crypto;
122
+ return {
123
+ type: "thread.settle",
124
+ commandId: CommandId.make(
125
+ `t3cli:thread-settle:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
126
+ ),
127
+ threadId: ThreadId.make(threadId),
128
+ } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.settle" }>;
129
+ });
130
+
131
+ export const makeThreadUnsettleCommand = Effect.fn("makeThreadUnsettleCommand")(function* (
132
+ threadId: string,
133
+ ) {
134
+ const crypto = yield* Crypto.Crypto;
135
+ return {
136
+ type: "thread.unsettle",
137
+ commandId: CommandId.make(
138
+ `t3cli:thread-unsettle:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
139
+ ),
140
+ threadId: ThreadId.make(threadId),
141
+ reason: "user",
142
+ } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.unsettle" }>;
143
+ });
144
+
145
+ export const makeThreadSnoozeCommand = Effect.fn("makeThreadSnoozeCommand")(function* (input: {
146
+ readonly threadId: string;
147
+ readonly snoozedUntil: string;
148
+ }) {
149
+ const crypto = yield* Crypto.Crypto;
150
+ return {
151
+ type: "thread.snooze",
152
+ commandId: CommandId.make(
153
+ `t3cli:thread-snooze:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
154
+ ),
155
+ threadId: ThreadId.make(input.threadId),
156
+ snoozedUntil: input.snoozedUntil,
157
+ } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.snooze" }>;
158
+ });
159
+
160
+ export const makeThreadUnsnoozeCommand = Effect.fn("makeThreadUnsnoozeCommand")(function* (
161
+ threadId: string,
162
+ ) {
163
+ const crypto = yield* Crypto.Crypto;
164
+ return {
165
+ type: "thread.unsnooze",
166
+ commandId: CommandId.make(
167
+ `t3cli:thread-unsnooze:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
168
+ ),
169
+ threadId: ThreadId.make(threadId),
170
+ reason: "user",
171
+ } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.unsnooze" }>;
172
+ });
173
+
174
+ export const makeThreadPinCommand = Effect.fn("makeThreadPinCommand")(function* (threadId: string) {
175
+ const crypto = yield* Crypto.Crypto;
176
+ return {
177
+ type: "thread.pin",
178
+ commandId: CommandId.make(`t3cli:thread-pin:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`),
179
+ threadId: ThreadId.make(threadId),
180
+ } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.pin" }>;
181
+ });
182
+
183
+ export const makeThreadUnpinCommand = Effect.fn("makeThreadUnpinCommand")(function* (
184
+ threadId: string,
185
+ ) {
186
+ const crypto = yield* Crypto.Crypto;
187
+ return {
188
+ type: "thread.unpin",
189
+ commandId: CommandId.make(
190
+ `t3cli:thread-unpin:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
191
+ ),
192
+ threadId: ThreadId.make(threadId),
193
+ } satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.unpin" }>;
194
+ });
195
+
118
196
  export const makeThreadSessionStopCommand = Effect.fn("makeThreadSessionStopCommand")(function* (
119
197
  threadId: string,
120
198
  ) {
@@ -6,10 +6,21 @@ import { CliRuntime } from "../cli/runtime/service.ts";
6
6
  import { T3Orchestration } from "../orchestration/service.ts";
7
7
  import { ProjectLookupError, ThreadLookupError, ThreadSessionError } from "../domain/error.ts";
8
8
  import { resolveProjectScope } from "../domain/helpers.ts";
9
- import { type ListThreadsInclude, type StartThreadInput } from "./service.ts";
9
+ import {
10
+ type GetThreadMessagesInput,
11
+ type ListThreadsInclude,
12
+ type SnoozeThreadInput,
13
+ type StartThreadInput,
14
+ type StartThreadPolicy,
15
+ type ThreadDispatchPolicy,
16
+ } from "./service.ts";
10
17
  import type { CallbackThreadInput, SendThreadInput } from "./service.ts";
11
18
  import type { T3ThreadApplicationService } from "./service.ts";
12
- import type { OrchestrationThreadShell } from "@t3tools/contracts";
19
+ import type {
20
+ OrchestrationSearchThreadsInput,
21
+ OrchestrationThreadSearchMatch,
22
+ OrchestrationThreadShell,
23
+ } from "@t3tools/contracts";
13
24
  import { mergeModelOptions } from "./model-selection.ts";
14
25
  import { derivePendingApprovals, derivePendingUserInputs } from "../domain/thread-activities.ts";
15
26
  import {
@@ -24,10 +35,16 @@ import {
24
35
  makeThreadArchiveCommand,
25
36
  makeThreadDeleteCommand,
26
37
  makeThreadInterruptCommand,
38
+ makeThreadPinCommand,
27
39
  makeThreadSessionStopCommand,
40
+ makeThreadSettleCommand,
41
+ makeThreadSnoozeCommand,
28
42
  makeThreadStartCommands,
29
43
  makeThreadTurnContinueCommand,
30
44
  makeThreadUnarchiveCommand,
45
+ makeThreadUnpinCommand,
46
+ makeThreadUnsnoozeCommand,
47
+ makeThreadUnsettleCommand,
31
48
  makeThreadUserInputRespondCommand,
32
49
  } from "./thread-commands.ts";
33
50
  import { makeUpdateThread } from "./thread-update.ts";
@@ -73,10 +90,51 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
73
90
  threads: snapshot.threads.filter((thread) => thread.projectId === scope.project.id),
74
91
  };
75
92
  });
93
+ const searchThreads = Effect.fn("T3ApplicationLive.searchThreads")(function* (
94
+ input: OrchestrationSearchThreadsInput,
95
+ ) {
96
+ const result = yield* orchestration.searchThreads(input);
97
+ const snapshot = yield* orchestration.getShellSnapshot();
98
+ const threadsById = new Map(snapshot.threads.map((thread) => [thread.id, thread]));
99
+ const projectsById = new Map(snapshot.projects.map((project) => [project.id, project]));
100
+ return result.matches.map((match) => {
101
+ const thread = threadsById.get(match.threadId);
102
+ const project = projectsById.get(match.projectId);
103
+ return {
104
+ threadId: match.threadId,
105
+ threadTitle: thread?.title ?? null,
106
+ projectId: match.projectId,
107
+ projectTitle: project?.title ?? null,
108
+ workspaceRoot: project?.workspaceRoot ?? null,
109
+ branch: thread?.branch ?? null,
110
+ worktreePath: thread?.worktreePath ?? null,
111
+ source: match.source,
112
+ snippet: match.snippet,
113
+ messageCreatedAt: match.messageCreatedAt,
114
+ } satisfies ThreadSearchResult;
115
+ });
116
+ });
76
117
  const getThreadMessages = Effect.fn("T3ApplicationLive.getThreadMessages")(function* (
118
+ input: GetThreadMessagesInput,
119
+ ) {
120
+ return yield* orchestration.getThreadDetailSnapshot(input);
121
+ });
122
+ const getThreadSummary = Effect.fn("T3ApplicationLive.getThreadSummary")(function* (
77
123
  threadId: string,
78
124
  ) {
79
- return yield* orchestration.getThreadSnapshot(threadId);
125
+ const snapshot = yield* loadThreadsSnapshot("all").pipe(
126
+ Effect.provideService(T3Orchestration, orchestration),
127
+ );
128
+ const thread = snapshot.threads.find((item) => item.id === threadId);
129
+ if (thread === undefined) {
130
+ return yield* Effect.fail(
131
+ new ThreadLookupError({
132
+ message: `thread not found: ${threadId}`,
133
+ threadId,
134
+ }),
135
+ );
136
+ }
137
+ return thread;
80
138
  });
81
139
  const showThread = Effect.fn("T3ApplicationLive.showThread")(function* (threadId: string) {
82
140
  const thread = yield* orchestration.getThreadSnapshot(threadId);
@@ -86,7 +144,9 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
86
144
  const command = yield* makeThreadArchiveCommand(threadId).pipe(
87
145
  Effect.provideService(Crypto.Crypto, crypto),
88
146
  );
89
- return yield* orchestration.dispatch(command);
147
+ const dispatch = yield* orchestration.dispatch(command);
148
+ yield* awaitShellSequence(dispatch.sequence);
149
+ return dispatch;
90
150
  });
91
151
  const unarchiveThread = Effect.fn("T3ApplicationLive.unarchiveThread")(function* (
92
152
  threadId: string,
@@ -96,6 +156,48 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
96
156
  );
97
157
  return yield* orchestration.dispatch(command);
98
158
  });
159
+ const settleThread = Effect.fn("T3ApplicationLive.settleThread")(function* (threadId: string) {
160
+ const command = yield* makeThreadSettleCommand(threadId).pipe(
161
+ Effect.provideService(Crypto.Crypto, crypto),
162
+ );
163
+ return yield* orchestration.dispatch(command);
164
+ });
165
+ const unsettleThread = Effect.fn("T3ApplicationLive.unsettleThread")(function* (
166
+ threadId: string,
167
+ ) {
168
+ const command = yield* makeThreadUnsettleCommand(threadId).pipe(
169
+ Effect.provideService(Crypto.Crypto, crypto),
170
+ );
171
+ return yield* orchestration.dispatch(command);
172
+ });
173
+ const snoozeThread = Effect.fn("T3ApplicationLive.snoozeThread")(function* (
174
+ input: SnoozeThreadInput,
175
+ ) {
176
+ const command = yield* makeThreadSnoozeCommand(input).pipe(
177
+ Effect.provideService(Crypto.Crypto, crypto),
178
+ );
179
+ return yield* orchestration.dispatch(command);
180
+ });
181
+ const unsnoozeThread = Effect.fn("T3ApplicationLive.unsnoozeThread")(function* (
182
+ threadId: string,
183
+ ) {
184
+ const command = yield* makeThreadUnsnoozeCommand(threadId).pipe(
185
+ Effect.provideService(Crypto.Crypto, crypto),
186
+ );
187
+ return yield* orchestration.dispatch(command);
188
+ });
189
+ const pinThread = Effect.fn("T3ApplicationLive.pinThread")(function* (threadId: string) {
190
+ const command = yield* makeThreadPinCommand(threadId).pipe(
191
+ Effect.provideService(Crypto.Crypto, crypto),
192
+ );
193
+ return yield* orchestration.dispatch(command);
194
+ });
195
+ const unpinThread = Effect.fn("T3ApplicationLive.unpinThread")(function* (threadId: string) {
196
+ const command = yield* makeThreadUnpinCommand(threadId).pipe(
197
+ Effect.provideService(Crypto.Crypto, crypto),
198
+ );
199
+ return yield* orchestration.dispatch(command);
200
+ });
99
201
  const interruptThread = Effect.fn("T3ApplicationLive.interruptThread")(function* (
100
202
  threadId: string,
101
203
  ) {
@@ -107,6 +209,19 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
107
209
  }).pipe(Effect.provideService(Crypto.Crypto, crypto));
108
210
  return yield* orchestration.dispatch(command);
109
211
  });
212
+ const interruptThreadTurn = Effect.fn("T3ApplicationLive.interruptThreadTurn")(function* (
213
+ threadId: string,
214
+ turnId: string,
215
+ ) {
216
+ const snapshot = yield* orchestration.getThreadSnapshot(threadId);
217
+ if (snapshot.session?.activeTurnId !== turnId) {
218
+ return undefined;
219
+ }
220
+ const command = yield* makeThreadInterruptCommand({ threadId, turnId }).pipe(
221
+ Effect.provideService(Crypto.Crypto, crypto),
222
+ );
223
+ return yield* orchestration.dispatch(command);
224
+ });
110
225
  const deleteThread = Effect.fn("T3ApplicationLive.deleteThread")(function* (threadId: string) {
111
226
  const snapshot = yield* loadThreadsSnapshot("all").pipe(
112
227
  Effect.provideService(T3Orchestration, orchestration),
@@ -139,9 +254,7 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
139
254
  );
140
255
  const startThread = Effect.fn("T3ApplicationLive.startThread")(function* (
141
256
  startInput: StartThreadInput,
142
- policy?: {
143
- readonly until: "dispatch" | "visible" | "complete";
144
- },
257
+ policy?: StartThreadPolicy,
145
258
  ) {
146
259
  const snapshot = yield* orchestration.getShellSnapshot();
147
260
  const projectRef = startInput.projectRef;
@@ -171,13 +284,17 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
171
284
  project: scope.project,
172
285
  serverConfig,
173
286
  }).pipe(Effect.provideService(Crypto.Crypto, crypto));
287
+ const threadId = commands.threadId;
174
288
  const createDispatch = yield* orchestration.dispatch(commands.createCommand);
289
+ if (policy?.onThreadCreated !== undefined) {
290
+ yield* policy.onThreadCreated(threadId);
291
+ }
175
292
  yield* awaitShellSequence(createDispatch.sequence);
176
293
  const dispatch = yield* orchestration.dispatch(commands.turnCommand);
177
- const threadId = commands.threadId;
294
+ const messageId = commands.turnCommand.message.messageId;
178
295
  const until = policy?.until ?? "dispatch";
179
296
  if (until === "dispatch") {
180
- return { dispatch, project: scope.project, threadId };
297
+ return { dispatch, messageId, project: scope.project, threadId };
181
298
  }
182
299
  yield* awaitShellSequence(dispatch.sequence);
183
300
  if (until === "visible") {
@@ -187,17 +304,15 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
187
304
  return opened.snapshot;
188
305
  }),
189
306
  );
190
- return { dispatch, project: scope.project, threadId, thread };
307
+ return { dispatch, messageId, project: scope.project, threadId, thread };
191
308
  }
192
309
  const thread = yield* awaitThreadCompletion(threadId);
193
310
  yield* failIfThreadError(thread);
194
- return { dispatch, project: scope.project, threadId, thread };
311
+ return { dispatch, messageId, project: scope.project, threadId, thread };
195
312
  });
196
313
  const sendThread = Effect.fn("T3ApplicationLive.sendThread")(function* (
197
314
  input: SendThreadInput,
198
- policy?: {
199
- readonly until: "dispatch" | "visible" | "complete";
200
- },
315
+ policy?: ThreadDispatchPolicy,
201
316
  ) {
202
317
  const modelSelection =
203
318
  input.options !== undefined && input.options.length > 0
@@ -211,9 +326,10 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
211
326
  ...(modelSelection !== undefined ? { modelSelection } : {}),
212
327
  }).pipe(Effect.provideService(Crypto.Crypto, crypto));
213
328
  const dispatch = yield* orchestration.dispatch(command);
329
+ const messageId = command.message.messageId;
214
330
  const until = policy?.until ?? "dispatch";
215
331
  if (until === "dispatch") {
216
- return { dispatch, threadId: input.threadId };
332
+ return { dispatch, messageId, threadId: input.threadId };
217
333
  }
218
334
  yield* awaitShellSequence(dispatch.sequence);
219
335
  if (until === "visible") {
@@ -223,11 +339,11 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
223
339
  return opened.snapshot;
224
340
  }),
225
341
  );
226
- return { dispatch, threadId: input.threadId, thread };
342
+ return { dispatch, messageId, threadId: input.threadId, thread };
227
343
  }
228
344
  const thread = yield* awaitThreadCompletion(input.threadId);
229
345
  yield* failIfThreadError(thread);
230
- return { dispatch, threadId: input.threadId, thread };
346
+ return { dispatch, messageId, threadId: input.threadId, thread };
231
347
  });
232
348
  const watchThread = (threadId: string) => streamThreadEvents(threadId);
233
349
  const waitForThread = Effect.fn("T3ApplicationLive.waitForThread")(function* (threadId: string) {
@@ -271,12 +387,22 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
271
387
  return {
272
388
  approveThread,
273
389
  archiveThread,
390
+ awaitShellSequence,
274
391
  deleteThread,
275
392
  interruptThread,
393
+ interruptThreadTurn,
394
+ pinThread,
395
+ settleThread,
396
+ snoozeThread,
276
397
  updateThread,
277
398
  unarchiveThread,
399
+ unpinThread,
400
+ unsnoozeThread,
401
+ unsettleThread,
278
402
  listThreads,
403
+ searchThreads,
279
404
  getThreadMessages,
405
+ getThreadSummary,
280
406
  respondToThread,
281
407
  sendThread,
282
408
  showThread,
@@ -287,6 +413,19 @@ export const makeThreadApplication = Effect.fn("makeThreadApplication")(function
287
413
  } satisfies T3ThreadApplicationService;
288
414
  });
289
415
 
416
+ export type ThreadSearchResult = {
417
+ readonly threadId: OrchestrationThreadSearchMatch["threadId"];
418
+ readonly threadTitle: string | null;
419
+ readonly projectId: OrchestrationThreadSearchMatch["projectId"];
420
+ readonly projectTitle: string | null;
421
+ readonly workspaceRoot: string | null;
422
+ readonly branch: string | null;
423
+ readonly worktreePath: string | null;
424
+ readonly source: OrchestrationThreadSearchMatch["source"];
425
+ readonly snippet: OrchestrationThreadSearchMatch["snippet"];
426
+ readonly messageCreatedAt: OrchestrationThreadSearchMatch["messageCreatedAt"];
427
+ };
428
+
290
429
  const loadThreadsSnapshot = Effect.fn("loadThreadsSnapshot")(function* (
291
430
  include: ListThreadsInclude,
292
431
  ) {
package/src/cli/app.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { Command } from "effect/unstable/cli";
2
2
 
3
3
  import { createActionCommand } from "./action.ts";
4
+ import { askCommand } from "./ask.ts";
4
5
  import { createAuthCommand } from "./auth.ts";
5
6
  import { createEnvCommand } from "./env.ts";
6
7
  import { cliEnvironmentSetting } from "./env/flag.ts";
7
8
  import { createTerminalCommandGroup } from "./terminal.ts";
8
9
  import { createModelCommand } from "./model.ts";
9
10
  import { createProjectCommand } from "./project.ts";
11
+ import { searchThreadsCommand } from "./search.ts";
10
12
  import { createThreadCommand } from "./thread.ts";
11
13
  import { listThreadsCommand } from "./threads/list.ts";
12
14
  import { sendThreadCommand } from "./threads/send.ts";
@@ -21,11 +23,13 @@ export function createCliCommand() {
21
23
  Command.withGlobalFlags([cliEnvironmentSetting]),
22
24
  Command.withSubcommands([
23
25
  createActionCommand(),
26
+ askCommand,
24
27
  createAuthCommand(),
25
28
  createEnvCommand(),
26
29
  listThreadsCommand,
27
30
  createModelCommand(),
28
31
  createProjectCommand(),
32
+ searchThreadsCommand,
29
33
  createTerminalCommandGroup(),
30
34
  startThreadCommand,
31
35
  sendThreadCommand,