t3code-cli 0.8.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.
- package/README.md +90 -35
- package/dist/application.js +1 -1
- package/dist/auth.js +1 -2
- package/dist/bin.js +189 -44
- package/dist/cli.js +1 -1
- package/dist/config.js +1 -3
- package/dist/connection.js +1 -4
- package/dist/index.js +1 -4
- package/dist/layout.js +1 -1
- package/dist/orchestration.js +1 -1
- package/dist/rpc.js +1 -1
- package/dist/runtime.js +1 -2
- package/dist/scope.js +1 -1
- package/dist/shared.js +85492 -0
- package/dist/src/application/layer.d.ts +25 -2
- package/dist/src/application/projects.d.ts +1 -1
- package/dist/src/application/service.d.ts +21 -1
- package/dist/src/application/thread-commands.d.ts +27 -1
- package/dist/src/application/threads.d.ts +50 -1
- package/dist/src/domain/thread-activities.d.ts +14 -0
- package/dist/src/domain/thread-lifecycle.d.ts +1 -0
- package/dist/src/runtime/layer.d.ts +1 -1
- package/dist/t3tools.js +1 -1
- package/package.json +1 -1
- package/src/application/service.ts +19 -0
- package/src/application/thread-commands.ts +45 -0
- package/src/application/threads.ts +85 -0
- package/src/cli/error.ts +15 -0
- package/src/cli/message-input.ts +30 -0
- package/src/cli/thread-format.ts +47 -0
- package/src/cli/thread.ts +6 -0
- package/src/cli/threads/approve.ts +61 -0
- package/src/cli/threads/archive.ts +18 -3
- package/src/cli/threads/respond.ts +70 -0
- package/src/cli/threads/show.ts +43 -0
- package/src/domain/thread-activities.test.ts +321 -0
- package/src/domain/thread-activities.ts +244 -0
- package/src/domain/thread-lifecycle.ts +2 -0
- package/dist/Context-DueQ9iMH.js +0 -4916
- package/dist/Path-D8WPdPwR.js +0 -11406
- package/dist/Schema-DsQxYh6_.js +0 -13581
- package/dist/UrlParams-BA6gBvaY.js +0 -205
- package/dist/base-dir-R12OMDso.js +0 -20
- package/dist/error-BHRnjLux.js +0 -15
- package/dist/error-ChTsLQTu.js +0 -169
- package/dist/error-CjADNVm8.js +0 -54
- package/dist/flags-CM7_iGdA.js +0 -13371
- package/dist/layer-CMo36MrX.js +0 -29466
- package/dist/layer-DHhKS5jd.js +0 -13
- package/dist/layer-DUv99vsS.js +0 -72
- package/dist/layer-Do8wK1t6.js +0 -1508
- package/dist/layer-DwI3Skkh.js +0 -1223
- package/dist/scope-GycYiJ54.js +0 -29
- package/dist/service-CLmRO2Dp.js +0 -8
- package/dist/service-ybOWV9pL.js +0 -5
- package/dist/src-aiFPmAG7.js +0 -9030
- package/dist/transport-MI0Z3d2T.js +0 -539
- package/dist/url-SlsaG8nY.js +0 -165
- /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";
|