t3code-cli 0.7.0 → 0.9.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 (82) hide show
  1. package/README.md +90 -35
  2. package/dist/application.js +1 -1
  3. package/dist/auth.js +1 -2
  4. package/dist/bin.js +241 -45
  5. package/dist/cli.js +1 -1
  6. package/dist/config.js +1 -3
  7. package/dist/connection.js +1 -4
  8. package/dist/index.js +1 -4
  9. package/dist/layout.js +1 -1
  10. package/dist/orchestration.js +1 -1
  11. package/dist/rpc.js +1 -1
  12. package/dist/runtime.js +1 -2
  13. package/dist/scope.js +1 -1
  14. package/dist/shared.js +85492 -0
  15. package/dist/src/application/layer.d.ts +86 -2
  16. package/dist/src/application/models.d.ts +1 -0
  17. package/dist/src/application/projects.d.ts +10 -1
  18. package/dist/src/application/service.d.ts +30 -1
  19. package/dist/src/application/shell-sequence.d.ts +9 -0
  20. package/dist/src/application/thread-commands.d.ts +27 -1
  21. package/dist/src/application/thread-wait.d.ts +9 -0
  22. package/dist/src/application/threads.d.ts +102 -2
  23. package/dist/src/cli-path/layer.d.ts +3 -0
  24. package/dist/src/cli-path/service.d.ts +8 -0
  25. package/dist/src/domain/helpers.d.ts +9 -0
  26. package/dist/src/domain/model-config.d.ts +3 -0
  27. package/dist/src/domain/thread-activities.d.ts +14 -0
  28. package/dist/src/domain/thread-lifecycle.d.ts +10 -0
  29. package/dist/src/orchestration/layer.d.ts +215 -0
  30. package/dist/src/rpc/layer.d.ts +466 -0
  31. package/dist/src/rpc/ws-group.d.ts +142 -0
  32. package/dist/src/runtime/layer.d.ts +1 -1
  33. package/dist/t3tools.js +2 -2
  34. package/dist/upstream-t3code/packages/contracts/src/environmentHttp.d.ts +33 -10
  35. package/dist/upstream-t3code/packages/contracts/src/ipc.d.ts +6 -0
  36. package/dist/upstream-t3code/packages/contracts/src/orchestration.d.ts +657 -1
  37. package/dist/upstream-t3code/packages/contracts/src/provider.d.ts +14 -0
  38. package/dist/upstream-t3code/packages/contracts/src/providerRuntime.d.ts +175 -1
  39. package/dist/upstream-t3code/packages/contracts/src/rpc.d.ts +492 -0
  40. package/dist/upstream-t3code/packages/contracts/src/server.d.ts +45 -0
  41. package/dist/upstream-t3code/packages/contracts/src/settings.d.ts +21 -0
  42. package/dist/upstream-t3code/packages/contracts/src/terminal.d.ts +6 -3
  43. package/package.json +2 -2
  44. package/src/application/service.ts +32 -0
  45. package/src/application/thread-commands.ts +45 -0
  46. package/src/application/threads.ts +100 -1
  47. package/src/cli/error.ts +15 -0
  48. package/src/cli/message-input.ts +30 -0
  49. package/src/cli/thread-format.ts +47 -0
  50. package/src/cli/thread.ts +8 -0
  51. package/src/cli/threads/approve.ts +61 -0
  52. package/src/cli/threads/archive.ts +18 -3
  53. package/src/cli/threads/callback.ts +92 -0
  54. package/src/cli/threads/respond.ts +70 -0
  55. package/src/cli/threads/show.ts +43 -0
  56. package/src/cli-path/layer.ts +10 -0
  57. package/src/cli-path/service.ts +7 -0
  58. package/src/domain/thread-activities.test.ts +321 -0
  59. package/src/domain/thread-activities.ts +244 -0
  60. package/src/domain/thread-lifecycle.ts +2 -0
  61. package/src/runtime/layer.ts +2 -0
  62. package/dist/Context-DueQ9iMH.js +0 -4916
  63. package/dist/Path-D8WPdPwR.js +0 -11406
  64. package/dist/Schema-DsQxYh6_.js +0 -13581
  65. package/dist/UrlParams-BA6gBvaY.js +0 -205
  66. package/dist/base-dir-R12OMDso.js +0 -20
  67. package/dist/error-B2t1bAP9.js +0 -169
  68. package/dist/error-BHRnjLux.js +0 -15
  69. package/dist/error-jwMt3VoW.js +0 -54
  70. package/dist/flags-CM7_iGdA.js +0 -13371
  71. package/dist/layer-CfC5qZol.js +0 -1508
  72. package/dist/layer-DHhKS5jd.js +0 -13
  73. package/dist/layer-DUv99vsS.js +0 -72
  74. package/dist/layer-DvHnKBYj.js +0 -29466
  75. package/dist/layer-MqCKkIGD.js +0 -1206
  76. package/dist/scope-GycYiJ54.js +0 -29
  77. package/dist/service-CLmRO2Dp.js +0 -8
  78. package/dist/service-ybOWV9pL.js +0 -5
  79. package/dist/src-KdbHqrex.js +0 -8874
  80. package/dist/transport-D3zBdZ1h.js +0 -539
  81. package/dist/url-SlsaG8nY.js +0 -165
  82. /package/dist/{chunk-B5meny8j.js → rolldown-runtime.js} +0 -0
@@ -0,0 +1,244 @@
1
+ import {
2
+ ApprovalRequestId,
3
+ type OrchestrationThreadActivity,
4
+ type UserInputQuestion,
5
+ } from "#t3tools/contracts";
6
+
7
+ export type PendingApproval = {
8
+ readonly requestId: ApprovalRequestId;
9
+ readonly requestKind: "command" | "file-read" | "file-change";
10
+ readonly createdAt: string;
11
+ readonly detail?: string;
12
+ };
13
+
14
+ export type PendingUserInput = {
15
+ readonly requestId: ApprovalRequestId;
16
+ readonly createdAt: string;
17
+ readonly questions: ReadonlyArray<UserInputQuestion>;
18
+ };
19
+
20
+ function isRecord(value: unknown): value is Record<string, unknown> {
21
+ return value !== null && typeof value === "object";
22
+ }
23
+
24
+ function activityPayload(activity: OrchestrationThreadActivity): Record<string, unknown> | null {
25
+ const payload = activity.payload;
26
+ if (!isRecord(payload)) {
27
+ return null;
28
+ }
29
+ return payload;
30
+ }
31
+
32
+ // Mirrors upstream apps/web/src/session-logic.ts; `.failed` sorts after `.requested`
33
+ // so stale provider failures clear same-timestamp pending requests before id tie-break.
34
+ function compareActivityLifecycleRank(kind: string): number {
35
+ if (kind.endsWith(".started") || kind === "tool.started") {
36
+ return 0;
37
+ }
38
+ if (kind.endsWith(".progress") || kind.endsWith(".updated")) {
39
+ return 1;
40
+ }
41
+ if (kind.endsWith(".completed") || kind.endsWith(".resolved") || kind.endsWith(".failed")) {
42
+ return 2;
43
+ }
44
+ return 1;
45
+ }
46
+
47
+ function compareActivitiesByOrder(
48
+ left: OrchestrationThreadActivity,
49
+ right: OrchestrationThreadActivity,
50
+ ): number {
51
+ if (left.sequence !== undefined && right.sequence !== undefined) {
52
+ if (left.sequence !== right.sequence) {
53
+ return left.sequence - right.sequence;
54
+ }
55
+ } else if (left.sequence !== undefined) {
56
+ return 1;
57
+ } else if (right.sequence !== undefined) {
58
+ return -1;
59
+ }
60
+
61
+ const createdAtComparison = left.createdAt.localeCompare(right.createdAt);
62
+ if (createdAtComparison !== 0) {
63
+ return createdAtComparison;
64
+ }
65
+
66
+ const lifecycleRankComparison =
67
+ compareActivityLifecycleRank(left.kind) - compareActivityLifecycleRank(right.kind);
68
+ if (lifecycleRankComparison !== 0) {
69
+ return lifecycleRankComparison;
70
+ }
71
+
72
+ return left.id.localeCompare(right.id);
73
+ }
74
+
75
+ function requestKindFromRequestType(requestType: unknown): PendingApproval["requestKind"] | null {
76
+ switch (requestType) {
77
+ case "command_execution_approval":
78
+ case "exec_command_approval":
79
+ case "dynamic_tool_call":
80
+ return "command";
81
+ case "file_read_approval":
82
+ return "file-read";
83
+ case "file_change_approval":
84
+ case "apply_patch_approval":
85
+ return "file-change";
86
+ default:
87
+ return null;
88
+ }
89
+ }
90
+
91
+ function isStalePendingRequestFailureDetail(detail: string | undefined): boolean {
92
+ const normalized = detail?.toLowerCase();
93
+ if (normalized === undefined || normalized.length === 0) {
94
+ return false;
95
+ }
96
+ return (
97
+ normalized.includes("stale pending approval request") ||
98
+ normalized.includes("stale pending user-input request") ||
99
+ normalized.includes("unknown pending approval request") ||
100
+ normalized.includes("unknown pending permission request") ||
101
+ normalized.includes("unknown pending user-input request")
102
+ );
103
+ }
104
+
105
+ function requestKindFromPayload(
106
+ payload: Record<string, unknown>,
107
+ ): PendingApproval["requestKind"] | null {
108
+ if (
109
+ payload.requestKind === "command" ||
110
+ payload.requestKind === "file-read" ||
111
+ payload.requestKind === "file-change"
112
+ ) {
113
+ return payload.requestKind;
114
+ }
115
+ return requestKindFromRequestType(payload.requestType);
116
+ }
117
+
118
+ export function derivePendingApprovals(
119
+ activities: ReadonlyArray<OrchestrationThreadActivity>,
120
+ ): ReadonlyArray<PendingApproval> {
121
+ const openByRequestId = new Map<ApprovalRequestId, PendingApproval>();
122
+ const ordered = [...activities].toSorted(compareActivitiesByOrder);
123
+
124
+ for (const activity of ordered) {
125
+ const payload = activityPayload(activity);
126
+ const requestId =
127
+ payload !== null && typeof payload.requestId === "string"
128
+ ? ApprovalRequestId.make(payload.requestId)
129
+ : null;
130
+ const requestKind = payload !== null ? requestKindFromPayload(payload) : null;
131
+ const detail =
132
+ payload !== null && typeof payload.detail === "string" ? payload.detail : undefined;
133
+
134
+ if (activity.kind === "approval.requested" && requestId !== null && requestKind !== null) {
135
+ openByRequestId.set(requestId, {
136
+ requestId,
137
+ requestKind,
138
+ createdAt: activity.createdAt,
139
+ ...(detail !== undefined ? { detail } : {}),
140
+ });
141
+ continue;
142
+ }
143
+ if (activity.kind === "approval.resolved" && requestId !== null) {
144
+ openByRequestId.delete(requestId);
145
+ continue;
146
+ }
147
+ if (
148
+ activity.kind === "provider.approval.respond.failed" &&
149
+ requestId !== null &&
150
+ isStalePendingRequestFailureDetail(detail)
151
+ ) {
152
+ openByRequestId.delete(requestId);
153
+ }
154
+ }
155
+
156
+ return [...openByRequestId.values()].toSorted((a, b) => a.createdAt.localeCompare(b.createdAt));
157
+ }
158
+
159
+ function parseUserInputQuestions(
160
+ payload: Record<string, unknown> | null,
161
+ ): ReadonlyArray<UserInputQuestion> | null {
162
+ const questions = payload?.questions;
163
+ if (!Array.isArray(questions)) {
164
+ return null;
165
+ }
166
+ const parsed = questions
167
+ .map((entry): UserInputQuestion | null => {
168
+ if (!isRecord(entry)) {
169
+ return null;
170
+ }
171
+ const question = entry;
172
+ if (
173
+ typeof question.id !== "string" ||
174
+ typeof question.header !== "string" ||
175
+ typeof question.question !== "string" ||
176
+ !Array.isArray(question.options)
177
+ ) {
178
+ return null;
179
+ }
180
+ const options = question.options
181
+ .map((option): UserInputQuestion["options"][number] | null => {
182
+ if (!isRecord(option)) {
183
+ return null;
184
+ }
185
+ const record = option;
186
+ if (typeof record.label !== "string" || typeof record.description !== "string") {
187
+ return null;
188
+ }
189
+ return { label: record.label, description: record.description };
190
+ })
191
+ .filter((option): option is UserInputQuestion["options"][number] => option !== null);
192
+ if (options.length === 0) {
193
+ return null;
194
+ }
195
+ return {
196
+ id: question.id,
197
+ header: question.header,
198
+ question: question.question,
199
+ options,
200
+ multiSelect: question.multiSelect === true,
201
+ };
202
+ })
203
+ .filter((question): question is UserInputQuestion => question !== null);
204
+ return parsed.length > 0 ? parsed : null;
205
+ }
206
+
207
+ export function derivePendingUserInputs(
208
+ activities: ReadonlyArray<OrchestrationThreadActivity>,
209
+ ): ReadonlyArray<PendingUserInput> {
210
+ const openByRequestId = new Map<ApprovalRequestId, PendingUserInput>();
211
+ const ordered = [...activities].toSorted(compareActivitiesByOrder);
212
+
213
+ for (const activity of ordered) {
214
+ const payload = activityPayload(activity);
215
+ const requestId =
216
+ payload !== null && typeof payload.requestId === "string"
217
+ ? ApprovalRequestId.make(payload.requestId)
218
+ : null;
219
+ const detail =
220
+ payload !== null && typeof payload.detail === "string" ? payload.detail : undefined;
221
+
222
+ if (activity.kind === "user-input.requested" && requestId !== null) {
223
+ const questions = parseUserInputQuestions(payload);
224
+ if (questions === null) {
225
+ continue;
226
+ }
227
+ openByRequestId.set(requestId, { requestId, createdAt: activity.createdAt, questions });
228
+ continue;
229
+ }
230
+ if (activity.kind === "user-input.resolved" && requestId !== null) {
231
+ openByRequestId.delete(requestId);
232
+ continue;
233
+ }
234
+ if (
235
+ activity.kind === "provider.user-input.respond.failed" &&
236
+ requestId !== null &&
237
+ isStalePendingRequestFailureDetail(detail)
238
+ ) {
239
+ openByRequestId.delete(requestId);
240
+ }
241
+ }
242
+
243
+ return [...openByRequestId.values()].toSorted((a, b) => a.createdAt.localeCompare(b.createdAt));
244
+ }
@@ -16,6 +16,8 @@ export function isThreadActive(thread: OrchestrationThreadShell | OrchestrationT
16
16
  );
17
17
  }
18
18
 
19
+ export type ThreadLifecycleStatus = ReturnType<typeof threadStatus>;
20
+
19
21
  export function threadStatus(thread: OrchestrationThreadShell | OrchestrationThread) {
20
22
  if (isPendingStart(thread)) {
21
23
  return "pending";
@@ -17,6 +17,7 @@ import { T3CodeConnectionProvider, makeT3CodeConnectionProvider } from "../conne
17
17
  import { T3OrchestrationLive } from "../orchestration/layer.ts";
18
18
  import { T3RpcLive } from "../rpc/layer.ts";
19
19
  import { NodeSqlClientFactoryLive } from "../sql/node-sqlite-client.ts";
20
+ import { NodeCliPathLayer } from "../cli-path/layer.ts";
20
21
 
21
22
  export const T3AuthTransportLayer = T3AuthTransportLive.pipe(
22
23
  Layer.provide(NodeHttpClient.layerUndici),
@@ -76,4 +77,5 @@ export const AppLayer = Layer.mergeAll(
76
77
  T3RpcLayer,
77
78
  T3OrchestrationLayer,
78
79
  T3ApplicationLayer,
80
+ NodeCliPathLayer,
79
81
  );