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.
- package/README.md +37 -1
- package/dist/application.d.ts +2 -2
- package/dist/application.js +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +1 -1
- package/dist/bin.js +1305 -424
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/client-runtime/authorization.d.ts +1 -1
- package/dist/client-runtime/authorization.js +1 -1
- package/dist/client-runtime/connection.d.ts +1 -1
- package/dist/client-runtime/connection.js +1 -1
- package/dist/client-runtime/environment.d.ts +7 -1
- package/dist/client-runtime/environment.js +1 -1
- package/dist/client-runtime/errors.js +1 -1
- package/dist/client-runtime/operations/projects.d.ts +2 -2
- package/dist/client-runtime/operations/projects.js +2 -2
- package/dist/client-runtime/operations.d.ts +2 -2
- package/dist/client-runtime/operations.js +2 -2
- package/dist/client-runtime/platform.d.ts +1 -1
- package/dist/client-runtime/platform.js +1 -1
- package/dist/client-runtime/relay.d.ts +13 -2
- package/dist/client-runtime/relay.js +12 -2
- package/dist/client-runtime/rpc.d.ts +2 -2
- package/dist/client-runtime/rpc.js +2 -2
- package/dist/client-runtime/state/assets.d.ts +17 -11
- package/dist/client-runtime/state/assets.js +1 -1
- package/dist/client-runtime/state/auth.d.ts +1 -1
- package/dist/client-runtime/state/auth.js +1 -1
- package/dist/client-runtime/state/connections.js +1 -1
- package/dist/client-runtime/state/entities.d.ts +1 -1
- package/dist/client-runtime/state/entities.js +1 -1
- package/dist/client-runtime/state/filesystem.d.ts +1 -1
- package/dist/client-runtime/state/filesystem.js +1 -1
- package/dist/client-runtime/state/git.d.ts +6 -5
- package/dist/client-runtime/state/git.js +1 -1
- package/dist/client-runtime/state/models.d.ts +1 -1
- package/dist/client-runtime/state/models.js +1 -1
- package/dist/client-runtime/state/orchestration.d.ts +42 -2
- package/dist/client-runtime/state/orchestration.js +13 -1
- package/dist/client-runtime/state/presentation.d.ts +15 -3
- package/dist/client-runtime/state/presentation.js +1 -1
- package/dist/client-runtime/state/preview.d.ts +58 -48
- package/dist/client-runtime/state/preview.js +3 -2
- package/dist/client-runtime/state/project-grouping.d.ts +26 -2
- package/dist/client-runtime/state/project-grouping.js +83 -3
- package/dist/client-runtime/state/projects.d.ts +24 -18
- package/dist/client-runtime/state/projects.js +1 -1
- package/dist/client-runtime/state/pull-requests.d.ts +478 -0
- package/dist/client-runtime/state/pull-requests.js +172 -0
- package/dist/client-runtime/state/relay.d.ts +1 -1
- package/dist/client-runtime/state/relay.js +1 -1
- package/dist/client-runtime/state/review.d.ts +16 -1
- package/dist/client-runtime/state/review.js +27 -6
- package/dist/client-runtime/state/runtime.d.ts +2 -2
- package/dist/client-runtime/state/runtime.js +2 -2
- package/dist/client-runtime/state/server.d.ts +311 -172
- package/dist/client-runtime/state/server.js +192 -16
- package/dist/client-runtime/state/session.d.ts +77 -7
- package/dist/client-runtime/state/session.js +32 -3
- package/dist/client-runtime/state/shell.d.ts +47 -7
- package/dist/client-runtime/state/shell.js +1 -1
- package/dist/client-runtime/state/source-control.d.ts +2 -2
- package/dist/client-runtime/state/source-control.js +1 -1
- package/dist/client-runtime/state/subagentRuntime.d.ts +145 -0
- package/dist/client-runtime/state/subagentRuntime.js +521 -0
- package/dist/client-runtime/state/terminal.d.ts +17 -17
- package/dist/client-runtime/state/terminal.js +1 -1
- package/dist/client-runtime/state/thread-search.d.ts +23 -0
- package/dist/client-runtime/state/thread-search.js +38 -0
- package/dist/client-runtime/state/thread-settled.d.ts +32 -4
- package/dist/client-runtime/state/thread-settled.js +2 -157
- package/dist/client-runtime/state/thread-sort.d.ts +59 -2
- package/dist/client-runtime/state/thread-sort.js +128 -2
- package/dist/client-runtime/state/threads.d.ts +115 -3
- package/dist/client-runtime/state/threads.js +309 -28
- package/dist/client-runtime/state/vcs.d.ts +14 -14
- package/dist/client-runtime/state/vcs.js +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connection.d.ts +1 -1
- package/dist/connection.js +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/orchestration.d.ts +155 -6
- package/dist/orchestration.js +2 -2
- package/dist/preview.d.ts +1 -1
- package/dist/preview.js +1 -1
- package/dist/rpc.d.ts +887 -360
- package/dist/rpc.js +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +1 -1
- package/dist/shared/DrainableWorker.js +1 -1
- package/dist/shared/KeyedCoalescingWorker.js +1 -1
- package/dist/shared/Net.d.ts +6 -0
- package/dist/shared/Net.js +2 -1
- package/dist/shared/advertisedEndpoint.d.ts +1 -1
- package/dist/shared/advertisedEndpoint.js +1 -1
- package/dist/shared/agentAwareness.d.ts +1 -1
- package/dist/shared/backgroundActivitySettings.d.ts +1 -1
- package/dist/shared/composerInlineTokens.js +1 -1
- package/dist/shared/connectAuth.d.ts +23 -4
- package/dist/shared/connectAuth.js +40 -7
- package/dist/shared/devHome.js +1 -1
- package/dist/shared/dpop.js +1 -1
- package/dist/shared/git.d.ts +1 -1
- package/dist/shared/httpObservability.js +1 -1
- package/dist/shared/httpReadiness.js +1 -1
- package/dist/shared/keybindings.d.ts +2 -2
- package/dist/shared/keybindings.js +15 -1
- package/dist/shared/model.d.ts +1 -1
- package/dist/shared/oauthScope.js +1 -1
- package/dist/shared/observability.d.ts +8 -1
- package/dist/shared/observability.js +49 -5
- package/dist/shared/path.js +1 -1
- package/dist/shared/preview.js +1 -1
- package/dist/shared/previewViewport.d.ts +1 -1
- package/dist/shared/previewViewport.js +1 -1
- package/dist/shared/projectFavicon.d.ts +2 -1
- package/dist/shared/projectFavicon.js +13 -1
- package/dist/shared/projectScripts.d.ts +1 -1
- package/dist/shared/relayAuth.js +1 -1
- package/dist/shared/relayClient.d.ts +1 -1
- package/dist/shared/relayClient.js +1 -1
- package/dist/shared/relayJwt.js +1 -1
- package/dist/shared/relayTracing.js +1 -1
- package/dist/shared/relayUrl.js +1 -1
- package/dist/shared/remote.js +1 -1
- package/dist/shared/schemaYaml.js +3 -3
- package/dist/shared/semver.js +1 -1
- package/dist/shared/serverSettings.d.ts +1 -1
- package/dist/shared/serverSettings.js +1 -1
- package/dist/shared/shell.d.ts +6 -1
- package/dist/shared/shell.js +42 -4
- package/dist/shared/sourceControl.d.ts +1 -1
- package/dist/shared/t3ProjectFile.d.ts +9 -1
- package/dist/shared/t3ProjectFile.js +12 -2
- package/dist/shared/terminalLabels.d.ts +1 -1
- package/dist/shared/threadEnvMode.d.ts +29 -0
- package/dist/shared/threadEnvMode.js +24 -0
- package/dist/shared/toolActivity.d.ts +1 -1
- package/dist/shared/usageFormat.d.ts +34 -0
- package/dist/shared/usageFormat.js +176 -0
- package/dist/shared/usageMerge.d.ts +78 -0
- package/dist/shared/usageMerge.js +241 -0
- package/dist/shared.d.ts +8268 -1372
- package/dist/shared.js +22185 -19774
- package/dist/t3tools.d.ts +2 -2
- package/dist/t3tools.js +2 -2
- package/package.json +5 -5
- package/src/application/error.ts +2 -2
- package/src/application/index.ts +2 -0
- package/src/application/service.ts +43 -5
- package/src/application/thread-commands.ts +78 -0
- package/src/application/threads.ts +156 -17
- package/src/cli/app.ts +4 -0
- package/src/cli/ask-lifecycle.ts +331 -0
- package/src/cli/ask.ts +390 -0
- package/src/cli/error.ts +70 -0
- package/src/cli/format/thread.ts +49 -7
- package/src/cli/search.ts +48 -0
- package/src/cli/thread.ts +12 -0
- package/src/cli/threads/messages.ts +33 -3
- package/src/cli/threads/pin.ts +46 -0
- package/src/cli/threads/send.ts +5 -1
- package/src/cli/threads/settle.ts +46 -0
- package/src/cli/threads/snooze.ts +117 -0
- package/src/cli/threads/start.ts +6 -1
- package/src/cli/threads/unpin.ts +48 -0
- package/src/cli/threads/unsettle.ts +48 -0
- package/src/cli/threads/unsnooze.ts +48 -0
- package/src/config/persist/schema.ts +1 -1
- package/src/connection/prepared.ts +12 -2
- package/src/orchestration/error.ts +10 -0
- package/src/orchestration/index.ts +1 -0
- package/src/orchestration/layer.ts +78 -0
- package/src/orchestration/service.ts +16 -3
- package/src/rpc/error.ts +2 -0
- package/src/rpc/operation.ts +2 -0
- package/src/rpc/ws-group.ts +3 -0
- package/src/runtime/layer.ts +9 -4
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import * as Stream from "effect/Stream";
|
|
4
|
+
import type {
|
|
5
|
+
OrchestrationMessage,
|
|
6
|
+
OrchestrationThread,
|
|
7
|
+
OrchestrationThreadShell,
|
|
8
|
+
} from "@t3tools/contracts";
|
|
9
|
+
|
|
10
|
+
import type { T3ApplicationService } from "../application/service.ts";
|
|
11
|
+
import type { T3CliEnvShape } from "../config/env/env.ts";
|
|
12
|
+
import { ThreadSessionError } from "../domain/error.ts";
|
|
13
|
+
import { derivePendingApprovals, derivePendingUserInputs } from "../domain/thread-activities.ts";
|
|
14
|
+
import { AskThreadArchivedError, AskThreadPendingRequestError } from "./error.ts";
|
|
15
|
+
import { formatWaitEventNdjson } from "./format/thread.ts";
|
|
16
|
+
import { isInteractiveHumanTerminal } from "./format/output.ts";
|
|
17
|
+
import { T3Output } from "./output/service.ts";
|
|
18
|
+
import { CliRuntime } from "./runtime/service.ts";
|
|
19
|
+
|
|
20
|
+
export const archivePolicyChoices = ["never", "always", "on-success", "on-failure"] as const;
|
|
21
|
+
|
|
22
|
+
export type ArchivePolicy = (typeof archivePolicyChoices)[number];
|
|
23
|
+
export type AskFormat = "human" | "json" | "ndjson";
|
|
24
|
+
|
|
25
|
+
export type AskArchiveResult =
|
|
26
|
+
| {
|
|
27
|
+
readonly policy: ArchivePolicy;
|
|
28
|
+
readonly status: "skipped";
|
|
29
|
+
}
|
|
30
|
+
| {
|
|
31
|
+
readonly policy: ArchivePolicy;
|
|
32
|
+
readonly status: "archived";
|
|
33
|
+
readonly sequence: number;
|
|
34
|
+
}
|
|
35
|
+
| {
|
|
36
|
+
readonly policy: ArchivePolicy;
|
|
37
|
+
readonly status: "failed";
|
|
38
|
+
readonly error: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export interface AskExecutionState {
|
|
42
|
+
readonly archivePolicy: ArchivePolicy;
|
|
43
|
+
threadId: string | undefined;
|
|
44
|
+
createdThread: boolean;
|
|
45
|
+
dispatched: boolean;
|
|
46
|
+
askTurnId: string | null;
|
|
47
|
+
archiveResult: AskArchiveResult | undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resolveAskFormat(
|
|
51
|
+
format: "auto" | AskFormat,
|
|
52
|
+
cliRuntime: CliRuntime["Service"],
|
|
53
|
+
t3CliEnv: T3CliEnvShape,
|
|
54
|
+
): AskFormat {
|
|
55
|
+
if (format !== "auto") {
|
|
56
|
+
return format;
|
|
57
|
+
}
|
|
58
|
+
return isInteractiveHumanTerminal(cliRuntime, t3CliEnv) ? "human" : "json";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function ensureAskTargetAvailable(thread: OrchestrationThreadShell) {
|
|
62
|
+
if (thread.archivedAt !== null) {
|
|
63
|
+
return Effect.fail(
|
|
64
|
+
new AskThreadArchivedError({
|
|
65
|
+
message: `thread is archived: ${thread.id}`,
|
|
66
|
+
threadId: thread.id,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (thread.hasPendingApprovals || thread.hasPendingUserInput) {
|
|
71
|
+
return Effect.fail(
|
|
72
|
+
new AskThreadPendingRequestError({
|
|
73
|
+
message: `thread has a pending approval or user-input request: ${thread.id}`,
|
|
74
|
+
threadId: thread.id,
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return Effect.void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function waitForAskThread(
|
|
82
|
+
application: T3ApplicationService,
|
|
83
|
+
output: T3Output["Service"],
|
|
84
|
+
input: {
|
|
85
|
+
readonly threadId: string;
|
|
86
|
+
readonly format: AskFormat;
|
|
87
|
+
readonly messageId: string;
|
|
88
|
+
readonly state: AskExecutionState;
|
|
89
|
+
},
|
|
90
|
+
) {
|
|
91
|
+
let lastStatus = "";
|
|
92
|
+
let turnComplete = false;
|
|
93
|
+
const observeAskThread = (thread: OrchestrationThread) => {
|
|
94
|
+
const observation = inspectAskTurn(thread, input.messageId, input.state.askTurnId);
|
|
95
|
+
input.state.askTurnId = observation.turnId;
|
|
96
|
+
if (observation.status === "failed") {
|
|
97
|
+
return Effect.fail(
|
|
98
|
+
new ThreadSessionError({
|
|
99
|
+
threadId: input.threadId,
|
|
100
|
+
message: observation.message,
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
turnComplete = observation.status === "complete";
|
|
105
|
+
return Effect.void;
|
|
106
|
+
};
|
|
107
|
+
return Effect.gen(function* () {
|
|
108
|
+
if (input.format === "human") {
|
|
109
|
+
yield* output.writeStderr(`waiting for ${input.threadId}...\n`);
|
|
110
|
+
}
|
|
111
|
+
const last = yield* application.watchThread(input.threadId).pipe(
|
|
112
|
+
Stream.tap((event) =>
|
|
113
|
+
Effect.gen(function* () {
|
|
114
|
+
if (event.type === "thread" || event.type === "done") {
|
|
115
|
+
yield* observeAskThread(event.thread);
|
|
116
|
+
} else if (event.type === "status") {
|
|
117
|
+
const thread = (yield* application.getThreadMessages({ threadId: input.threadId }))
|
|
118
|
+
.thread;
|
|
119
|
+
yield* observeAskThread(thread);
|
|
120
|
+
if (!turnComplete) {
|
|
121
|
+
yield* ensureNoPendingRequest({
|
|
122
|
+
id: thread.id,
|
|
123
|
+
hasPendingApprovals: derivePendingApprovals(thread.activities).length > 0,
|
|
124
|
+
hasPendingUserInput: derivePendingUserInputs(thread.activities).length > 0,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (input.format === "ndjson") {
|
|
129
|
+
yield* output.printNdjson(formatWaitEventNdjson(event));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (input.format === "human" && event.type === "status" && event.status !== lastStatus) {
|
|
133
|
+
lastStatus = event.status;
|
|
134
|
+
yield* output.writeStderr(`${input.threadId}: ${event.status}\n`);
|
|
135
|
+
}
|
|
136
|
+
}),
|
|
137
|
+
),
|
|
138
|
+
Stream.takeUntil((event) => turnComplete || event.type === "done"),
|
|
139
|
+
Stream.runLast,
|
|
140
|
+
);
|
|
141
|
+
const event = Option.getOrUndefined(last);
|
|
142
|
+
if (!turnComplete && event?.type !== "done") {
|
|
143
|
+
return yield* Effect.fail(
|
|
144
|
+
new ThreadSessionError({
|
|
145
|
+
message: `thread wait ended without a terminal event: ${input.threadId}`,
|
|
146
|
+
threadId: input.threadId,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return yield* Effect.void;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function selectAskAnswer(
|
|
155
|
+
thread: OrchestrationThread,
|
|
156
|
+
messageId: string,
|
|
157
|
+
turnId: string | null,
|
|
158
|
+
): OrchestrationMessage | undefined {
|
|
159
|
+
const userIndex = thread.messages.findIndex(
|
|
160
|
+
(message) => message.id === messageId && message.role === "user",
|
|
161
|
+
);
|
|
162
|
+
if (userIndex === -1) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
const following = thread.messages.slice(userIndex + 1);
|
|
166
|
+
const nextUserIndex = following.findIndex((message) => message.role === "user");
|
|
167
|
+
const askMessages = nextUserIndex === -1 ? following : following.slice(0, nextUserIndex);
|
|
168
|
+
const candidates = askMessages.filter(
|
|
169
|
+
(message) =>
|
|
170
|
+
message.role === "assistant" &&
|
|
171
|
+
!message.streaming &&
|
|
172
|
+
message.text.trim().length > 0 &&
|
|
173
|
+
(turnId === null || message.turnId === turnId),
|
|
174
|
+
);
|
|
175
|
+
return candidates.at(-1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
type AskTurnObservation =
|
|
179
|
+
| { readonly status: "waiting"; readonly turnId: string | null }
|
|
180
|
+
| { readonly status: "complete"; readonly turnId: string | null }
|
|
181
|
+
| { readonly status: "failed"; readonly turnId: string; readonly message: string };
|
|
182
|
+
|
|
183
|
+
export function inspectAskTurn(
|
|
184
|
+
thread: OrchestrationThread,
|
|
185
|
+
messageId: string,
|
|
186
|
+
knownTurnId: string | null,
|
|
187
|
+
): AskTurnObservation {
|
|
188
|
+
const userIndex = thread.messages.findIndex(
|
|
189
|
+
(message) => message.id === messageId && message.role === "user",
|
|
190
|
+
);
|
|
191
|
+
const userMessage = userIndex === -1 ? undefined : thread.messages[userIndex];
|
|
192
|
+
if (userMessage === undefined) {
|
|
193
|
+
return { status: "waiting", turnId: knownTurnId };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const following = thread.messages.slice(userIndex + 1);
|
|
197
|
+
const nextUserIndex = following.findIndex((message) => message.role === "user");
|
|
198
|
+
const askMessages = nextUserIndex === -1 ? following : following.slice(0, nextUserIndex);
|
|
199
|
+
const messageTurnId =
|
|
200
|
+
askMessages.findLast((message) => message.role === "assistant" && message.turnId !== null)
|
|
201
|
+
?.turnId ?? null;
|
|
202
|
+
const latestTurn = thread.latestTurn;
|
|
203
|
+
const requestedTurnId =
|
|
204
|
+
latestTurn?.requestedAt === userMessage.createdAt ? latestTurn.turnId : null;
|
|
205
|
+
const turnId =
|
|
206
|
+
knownTurnId ?? requestedTurnId ?? messageTurnId ?? thread.session?.activeTurnId ?? null;
|
|
207
|
+
|
|
208
|
+
if (turnId === null) {
|
|
209
|
+
return {
|
|
210
|
+
status: nextUserIndex === -1 ? "waiting" : "complete",
|
|
211
|
+
turnId,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const askIsLatest = latestTurn?.turnId === turnId;
|
|
216
|
+
if (askIsLatest && (latestTurn.state === "error" || thread.session?.status === "error")) {
|
|
217
|
+
return {
|
|
218
|
+
status: "failed",
|
|
219
|
+
turnId,
|
|
220
|
+
message: thread.session?.lastError ?? "thread ended with error",
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const sessionRunning =
|
|
225
|
+
thread.session?.status === "starting" || thread.session?.status === "running";
|
|
226
|
+
const complete =
|
|
227
|
+
nextUserIndex !== -1 ||
|
|
228
|
+
(askIsLatest &&
|
|
229
|
+
(latestTurn.state !== "running" ||
|
|
230
|
+
(thread.session?.activeTurnId !== turnId && !sessionRunning)));
|
|
231
|
+
return { status: complete ? "complete" : "waiting", turnId };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function finalizeArchive(
|
|
235
|
+
application: T3ApplicationService,
|
|
236
|
+
output: T3Output["Service"],
|
|
237
|
+
state: AskExecutionState,
|
|
238
|
+
succeeded: boolean,
|
|
239
|
+
): Effect.Effect<AskArchiveResult> {
|
|
240
|
+
if (state.archiveResult !== undefined) {
|
|
241
|
+
return Effect.succeed(state.archiveResult);
|
|
242
|
+
}
|
|
243
|
+
const shouldArchive =
|
|
244
|
+
state.archivePolicy === "always" ||
|
|
245
|
+
(state.archivePolicy === "on-success" && succeeded) ||
|
|
246
|
+
(state.archivePolicy === "on-failure" && !succeeded);
|
|
247
|
+
if (
|
|
248
|
+
(!state.createdThread && !state.dispatched) ||
|
|
249
|
+
state.threadId === undefined ||
|
|
250
|
+
!shouldArchive
|
|
251
|
+
) {
|
|
252
|
+
const result = {
|
|
253
|
+
policy: state.archivePolicy,
|
|
254
|
+
status: "skipped",
|
|
255
|
+
} satisfies AskArchiveResult;
|
|
256
|
+
state.archiveResult = result;
|
|
257
|
+
return Effect.succeed(result);
|
|
258
|
+
}
|
|
259
|
+
const threadId = state.threadId;
|
|
260
|
+
return application.archiveThread(threadId).pipe(
|
|
261
|
+
Effect.matchEffect({
|
|
262
|
+
onFailure: (error) =>
|
|
263
|
+
Effect.gen(function* () {
|
|
264
|
+
const result = {
|
|
265
|
+
policy: state.archivePolicy,
|
|
266
|
+
status: "failed",
|
|
267
|
+
error: error.message,
|
|
268
|
+
} satisfies AskArchiveResult;
|
|
269
|
+
state.archiveResult = result;
|
|
270
|
+
yield* output
|
|
271
|
+
.writeStderr(`warning: failed to archive thread ${threadId}: ${error.message}\n`)
|
|
272
|
+
.pipe(Effect.ignore);
|
|
273
|
+
return result;
|
|
274
|
+
}),
|
|
275
|
+
onSuccess: (dispatch) => {
|
|
276
|
+
const result = {
|
|
277
|
+
policy: state.archivePolicy,
|
|
278
|
+
status: "archived",
|
|
279
|
+
sequence: dispatch.sequence,
|
|
280
|
+
} satisfies AskArchiveResult;
|
|
281
|
+
state.archiveResult = result;
|
|
282
|
+
return Effect.succeed(result);
|
|
283
|
+
},
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function cleanupInterruptedAsk(
|
|
289
|
+
application: T3ApplicationService,
|
|
290
|
+
output: T3Output["Service"],
|
|
291
|
+
state: AskExecutionState,
|
|
292
|
+
) {
|
|
293
|
+
if (state.threadId === undefined) {
|
|
294
|
+
return Effect.void;
|
|
295
|
+
}
|
|
296
|
+
const threadId = state.threadId;
|
|
297
|
+
return Effect.gen(function* () {
|
|
298
|
+
if (state.dispatched && state.askTurnId !== null) {
|
|
299
|
+
yield* application.interruptThreadTurn(threadId, state.askTurnId).pipe(
|
|
300
|
+
Effect.matchEffect({
|
|
301
|
+
onFailure: (error) =>
|
|
302
|
+
output
|
|
303
|
+
.writeStderr(`warning: failed to interrupt thread ${threadId}: ${error.message}\n`)
|
|
304
|
+
.pipe(Effect.ignore),
|
|
305
|
+
onSuccess: () => Effect.void,
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
yield* finalizeArchive(application, output, state, false);
|
|
310
|
+
}).pipe(Effect.asVoid);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function ensureTrailingNewline(text: string) {
|
|
314
|
+
return text.endsWith("\n") ? text : `${text}\n`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function ensureNoPendingRequest(thread: {
|
|
318
|
+
readonly id: string;
|
|
319
|
+
readonly hasPendingApprovals: boolean;
|
|
320
|
+
readonly hasPendingUserInput: boolean;
|
|
321
|
+
}) {
|
|
322
|
+
if (!thread.hasPendingApprovals && !thread.hasPendingUserInput) {
|
|
323
|
+
return Effect.void;
|
|
324
|
+
}
|
|
325
|
+
return Effect.fail(
|
|
326
|
+
new AskThreadPendingRequestError({
|
|
327
|
+
message: `thread requested approval or user input instead of answering: ${thread.id}`,
|
|
328
|
+
threadId: thread.id,
|
|
329
|
+
}),
|
|
330
|
+
);
|
|
331
|
+
}
|