@meistrari/remy-cli 1.12.0 → 1.14.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 +20 -7
- package/dist/remy.js +2094 -432
- package/package.json +1 -1
package/dist/remy.js
CHANGED
|
@@ -18,8 +18,8 @@ var __export = (target, all) => {
|
|
|
18
18
|
// src/commands.ts
|
|
19
19
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
20
20
|
import { spawn } from "child_process";
|
|
21
|
-
import { mkdir as mkdir5, readFile as readFile5, readdir as
|
|
22
|
-
import { tmpdir } from "os";
|
|
21
|
+
import { mkdir as mkdir5, readFile as readFile5, readdir as readdir3, rename as rename4, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
|
|
22
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
23
23
|
import { dirname as dirname6, join as join6 } from "path";
|
|
24
24
|
|
|
25
25
|
// ../../node_modules/.bun/@meistrari+auth-cli@1.6.1+aff0271b2b55d60c/node_modules/@meistrari/auth-cli/dist/index.mjs
|
|
@@ -31673,6 +31673,31 @@ var repositorySuggestionsInputSchema = exports_external2.strictObject({
|
|
|
31673
31673
|
installationId: exports_external2.string().min(1),
|
|
31674
31674
|
prompt: exports_external2.string().trim().min(1).max(20000)
|
|
31675
31675
|
});
|
|
31676
|
+
var branchSuggestionsInputSchema = exports_external2.strictObject({
|
|
31677
|
+
installationId: exports_external2.string().min(1),
|
|
31678
|
+
prompt: exports_external2.string().trim().min(1).max(20000),
|
|
31679
|
+
repositoryIds: exports_external2.array(exports_external2.string().min(1)).min(1).max(20)
|
|
31680
|
+
});
|
|
31681
|
+
var branchSuggestionsResponseSchema = exports_external2.strictObject({
|
|
31682
|
+
repositories: exports_external2.array(exports_external2.discriminatedUnion("suggestion_kind", [
|
|
31683
|
+
exports_external2.strictObject({
|
|
31684
|
+
repository_id: exports_external2.string().min(1),
|
|
31685
|
+
suggestion_kind: exports_external2.literal("continue_pull_request"),
|
|
31686
|
+
branch_name: exports_external2.string().min(1),
|
|
31687
|
+
pull_request: exports_external2.strictObject({
|
|
31688
|
+
number: exports_external2.number().int().positive(),
|
|
31689
|
+
title: exports_external2.string(),
|
|
31690
|
+
url: exports_external2.url()
|
|
31691
|
+
})
|
|
31692
|
+
}),
|
|
31693
|
+
exports_external2.strictObject({
|
|
31694
|
+
repository_id: exports_external2.string().min(1),
|
|
31695
|
+
suggestion_kind: exports_external2.literal("create_session_branch"),
|
|
31696
|
+
branch_name: exports_external2.null(),
|
|
31697
|
+
pull_request: exports_external2.null()
|
|
31698
|
+
})
|
|
31699
|
+
]))
|
|
31700
|
+
});
|
|
31676
31701
|
var repositorySuggestionsResponseSchema = exports_external2.discriminatedUnion("selection_kind", [
|
|
31677
31702
|
exports_external2.object({
|
|
31678
31703
|
selection_kind: exports_external2.literal("repositories"),
|
|
@@ -31757,19 +31782,95 @@ async function suggestRemoteRepositories({
|
|
|
31757
31782
|
confidence: parsed.data.confidence
|
|
31758
31783
|
};
|
|
31759
31784
|
}
|
|
31785
|
+
async function suggestRemoteBranches({
|
|
31786
|
+
client,
|
|
31787
|
+
input
|
|
31788
|
+
}) {
|
|
31789
|
+
const parsedInput = branchSuggestionsInputSchema.parse(input);
|
|
31790
|
+
const response = await client.request("/v1/repositories/branch-suggestions", {
|
|
31791
|
+
method: "POST",
|
|
31792
|
+
headers: { "content-type": "application/json" },
|
|
31793
|
+
body: JSON.stringify({
|
|
31794
|
+
github_installation_id: parsedInput.installationId,
|
|
31795
|
+
prompt: parsedInput.prompt,
|
|
31796
|
+
repository_ids: parsedInput.repositoryIds
|
|
31797
|
+
})
|
|
31798
|
+
});
|
|
31799
|
+
let body;
|
|
31800
|
+
try {
|
|
31801
|
+
body = await response.json();
|
|
31802
|
+
} catch (error93) {
|
|
31803
|
+
throw new CodingAgentProtocolError("Branch suggestions response was not valid JSON.", { cause: error93 });
|
|
31804
|
+
}
|
|
31805
|
+
const parsed = branchSuggestionsResponseSchema.safeParse(body);
|
|
31806
|
+
if (!parsed.success)
|
|
31807
|
+
throw new CodingAgentProtocolError("Branch suggestions response did not match the public API contract.", { cause: parsed.error });
|
|
31808
|
+
return parsed.data.repositories.map((repository) => repository.suggestion_kind === "continue_pull_request" ? {
|
|
31809
|
+
suggestionKind: "continuePullRequest",
|
|
31810
|
+
repositoryId: repository.repository_id,
|
|
31811
|
+
branchName: repository.branch_name,
|
|
31812
|
+
pullRequest: repository.pull_request
|
|
31813
|
+
} : {
|
|
31814
|
+
suggestionKind: "createSessionBranch",
|
|
31815
|
+
repositoryId: repository.repository_id,
|
|
31816
|
+
branchName: null,
|
|
31817
|
+
pullRequest: null
|
|
31818
|
+
});
|
|
31819
|
+
}
|
|
31820
|
+
|
|
31821
|
+
// ../../packages/coding-agent-client/src/users.ts
|
|
31822
|
+
var remoteUserPageDtoSchema = exports_external2.object({
|
|
31823
|
+
data: exports_external2.array(exports_external2.object({
|
|
31824
|
+
id: exports_external2.string(),
|
|
31825
|
+
name: exports_external2.string(),
|
|
31826
|
+
email: exports_external2.email()
|
|
31827
|
+
})),
|
|
31828
|
+
has_more: exports_external2.boolean(),
|
|
31829
|
+
next_cursor: exports_external2.string().nullable()
|
|
31830
|
+
});
|
|
31831
|
+
async function listRemoteUsers({
|
|
31832
|
+
client,
|
|
31833
|
+
limit,
|
|
31834
|
+
after,
|
|
31835
|
+
signal
|
|
31836
|
+
}) {
|
|
31837
|
+
const searchParams = new URLSearchParams;
|
|
31838
|
+
if (limit !== undefined)
|
|
31839
|
+
searchParams.set("limit", String(limit));
|
|
31840
|
+
if (after)
|
|
31841
|
+
searchParams.set("after", after);
|
|
31842
|
+
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
31843
|
+
const response = await client.request(`/users${suffix}`, { signal });
|
|
31844
|
+
let body;
|
|
31845
|
+
try {
|
|
31846
|
+
body = await response.json();
|
|
31847
|
+
} catch (error93) {
|
|
31848
|
+
throw new CodingAgentProtocolError("User directory response was not valid JSON.", { cause: error93 });
|
|
31849
|
+
}
|
|
31850
|
+
const parsed = remoteUserPageDtoSchema.safeParse(body);
|
|
31851
|
+
if (!parsed.success)
|
|
31852
|
+
throw new CodingAgentProtocolError("User directory response did not match the API contract.", { cause: parsed.error });
|
|
31853
|
+
return {
|
|
31854
|
+
data: parsed.data.data.map((user) => ({ id: user.id, name: user.name, email: user.email })),
|
|
31855
|
+
hasMore: parsed.data.has_more,
|
|
31856
|
+
nextCursor: parsed.data.next_cursor
|
|
31857
|
+
};
|
|
31858
|
+
}
|
|
31760
31859
|
|
|
31761
31860
|
// ../../packages/coding-agent-client/src/files.ts
|
|
31861
|
+
import { Buffer } from "buffer";
|
|
31762
31862
|
import { createHash } from "crypto";
|
|
31763
31863
|
import { open } from "fs/promises";
|
|
31764
31864
|
import { basename } from "path";
|
|
31765
|
-
var
|
|
31865
|
+
var fileUploadMaxBytes = 20 * 1024 * 1024;
|
|
31866
|
+
var fileUploadMaxSizeLabel = `${fileUploadMaxBytes / (1024 * 1024)} MiB`;
|
|
31766
31867
|
var defaultPollIntervalMs = 250;
|
|
31767
31868
|
var sha256Schema = exports_external2.string().regex(/^[0-9a-f]{64}$/);
|
|
31768
31869
|
var fileIdentitySchema = exports_external2.strictObject({
|
|
31769
31870
|
id: exports_external2.string(),
|
|
31770
31871
|
filename: exports_external2.string().min(1).max(255),
|
|
31771
31872
|
media_type: exports_external2.string().min(1).max(255),
|
|
31772
|
-
byte_size: exports_external2.number().int().min(0).max(
|
|
31873
|
+
byte_size: exports_external2.number().int().min(0).max(fileUploadMaxBytes),
|
|
31773
31874
|
sha256: sha256Schema
|
|
31774
31875
|
});
|
|
31775
31876
|
var uploadSignedUrlSchema = exports_external2.strictObject({
|
|
@@ -31820,9 +31921,37 @@ var fileResponseSchema = exports_external2.union([
|
|
|
31820
31921
|
var createFileRequestSchema = exports_external2.strictObject({
|
|
31821
31922
|
filename: exports_external2.string().min(1).max(255),
|
|
31822
31923
|
media_type: exports_external2.string().min(1).max(255),
|
|
31823
|
-
byte_size: exports_external2.number().int().min(0).max(
|
|
31924
|
+
byte_size: exports_external2.number().int().min(0).max(fileUploadMaxBytes),
|
|
31824
31925
|
sha256: sha256Schema
|
|
31825
31926
|
});
|
|
31927
|
+
async function readFileHandleWithLimit({
|
|
31928
|
+
fileHandle,
|
|
31929
|
+
initialByteSize,
|
|
31930
|
+
maxBytes
|
|
31931
|
+
}) {
|
|
31932
|
+
const maximumBufferBytes = maxBytes + 1;
|
|
31933
|
+
let buffer = Buffer.allocUnsafe(Math.min(maximumBufferBytes, Math.max(1, initialByteSize + 1)));
|
|
31934
|
+
let byteLength = 0;
|
|
31935
|
+
while (true) {
|
|
31936
|
+
if (byteLength === buffer.byteLength) {
|
|
31937
|
+
if (byteLength > maxBytes)
|
|
31938
|
+
return { status: "limit_exceeded" };
|
|
31939
|
+
const nextBuffer = Buffer.allocUnsafe(Math.min(maximumBufferBytes, Math.max(buffer.byteLength * 2, byteLength + 1)));
|
|
31940
|
+
buffer.copy(nextBuffer, 0, 0, byteLength);
|
|
31941
|
+
buffer = nextBuffer;
|
|
31942
|
+
}
|
|
31943
|
+
const { bytesRead } = await fileHandle.read(buffer, byteLength, buffer.byteLength - byteLength, null);
|
|
31944
|
+
if (bytesRead === 0) {
|
|
31945
|
+
return {
|
|
31946
|
+
status: "complete",
|
|
31947
|
+
bytes: Buffer.from(buffer.subarray(0, byteLength))
|
|
31948
|
+
};
|
|
31949
|
+
}
|
|
31950
|
+
byteLength += bytesRead;
|
|
31951
|
+
if (byteLength > maxBytes)
|
|
31952
|
+
return { status: "limit_exceeded" };
|
|
31953
|
+
}
|
|
31954
|
+
}
|
|
31826
31955
|
async function reserveAndUploadFile({
|
|
31827
31956
|
client,
|
|
31828
31957
|
filePath,
|
|
@@ -31907,13 +32036,18 @@ async function readLocalUploadFile({ filePath, mediaType }) {
|
|
|
31907
32036
|
throw new LocalFileSelectionError(`Local attachment is not a regular file: ${filePath}`);
|
|
31908
32037
|
if (fileStat.size === 0)
|
|
31909
32038
|
throw new LocalFileSelectionError(`Local attachment is empty: ${filePath}`);
|
|
31910
|
-
if (fileStat.size >
|
|
31911
|
-
throw
|
|
31912
|
-
const
|
|
32039
|
+
if (fileStat.size > fileUploadMaxBytes)
|
|
32040
|
+
throw oversizedLocalFileError(filePath);
|
|
32041
|
+
const readResult = await readFileHandleWithLimit({
|
|
32042
|
+
fileHandle,
|
|
32043
|
+
initialByteSize: fileStat.size,
|
|
32044
|
+
maxBytes: fileUploadMaxBytes
|
|
32045
|
+
});
|
|
32046
|
+
if (readResult.status === "limit_exceeded")
|
|
32047
|
+
throw oversizedLocalFileError(filePath);
|
|
32048
|
+
const { bytes } = readResult;
|
|
31913
32049
|
if (bytes.byteLength === 0)
|
|
31914
32050
|
throw new LocalFileSelectionError(`Local attachment is empty: ${filePath}`);
|
|
31915
|
-
if (bytes.byteLength > maxFileBytes)
|
|
31916
|
-
throw new LocalFileSelectionError(`Local attachment exceeds ${maxFileBytes} bytes: ${filePath}`);
|
|
31917
32051
|
return {
|
|
31918
32052
|
filename: basename(filePath),
|
|
31919
32053
|
mediaType,
|
|
@@ -31925,6 +32059,9 @@ async function readLocalUploadFile({ filePath, mediaType }) {
|
|
|
31925
32059
|
await fileHandle.close();
|
|
31926
32060
|
}
|
|
31927
32061
|
}
|
|
32062
|
+
function oversizedLocalFileError(filePath) {
|
|
32063
|
+
return new LocalFileSelectionError(`Local attachment ${JSON.stringify(filePath)} is larger than ${fileUploadMaxSizeLabel}. Choose a file no larger than ${fileUploadMaxSizeLabel}.`);
|
|
32064
|
+
}
|
|
31928
32065
|
async function parseJson(response, failureMessage) {
|
|
31929
32066
|
try {
|
|
31930
32067
|
return await response.json();
|
|
@@ -32675,11 +32812,17 @@ var sessionTelaPageArtifactEventSchema = exports_external2.strictObject({
|
|
|
32675
32812
|
published_at: exports_external2.iso.datetime()
|
|
32676
32813
|
});
|
|
32677
32814
|
var sessionArtifactEventSchema = exports_external2.discriminatedUnion("kind", [
|
|
32678
|
-
sessionFileArtifactEventSchema
|
|
32815
|
+
sessionFileArtifactEventSchema.extend({
|
|
32816
|
+
download_url: exports_external2.string().optional(),
|
|
32817
|
+
preview_url: exports_external2.url().optional()
|
|
32818
|
+
}),
|
|
32679
32819
|
sessionTelaPageArtifactEventSchema
|
|
32680
32820
|
]);
|
|
32681
32821
|
var sessionArtifactSchema = exports_external2.discriminatedUnion("kind", [
|
|
32682
|
-
sessionFileArtifactEventSchema.extend({
|
|
32822
|
+
sessionFileArtifactEventSchema.extend({
|
|
32823
|
+
download_url: exports_external2.string(),
|
|
32824
|
+
preview_url: exports_external2.url().optional()
|
|
32825
|
+
}),
|
|
32683
32826
|
sessionTelaPageArtifactEventSchema.extend({ preview_url: exports_external2.string() })
|
|
32684
32827
|
]);
|
|
32685
32828
|
var sessionMessageSchema = exports_external2.strictObject({
|
|
@@ -32721,6 +32864,10 @@ var sessionConnectionSchema = exports_external2.discriminatedUnion("status", [
|
|
|
32721
32864
|
var createCodexSessionInputSchema = exports_external2.strictObject({
|
|
32722
32865
|
installationId: exports_external2.string().min(1),
|
|
32723
32866
|
repositoryIds: exports_external2.array(exports_external2.string().min(1)),
|
|
32867
|
+
repositoryBranchOverrides: exports_external2.array(exports_external2.strictObject({
|
|
32868
|
+
repositoryId: exports_external2.string().min(1),
|
|
32869
|
+
branchName: exports_external2.string().min(1)
|
|
32870
|
+
})).optional(),
|
|
32724
32871
|
prompt: exports_external2.string().refine((value) => value.trim().length > 0),
|
|
32725
32872
|
fileIds: exports_external2.array(exports_external2.string().min(1)),
|
|
32726
32873
|
model: codexModelIdSchema.optional(),
|
|
@@ -32762,6 +32909,7 @@ var withdrawSessionMessageResponseSchema = exports_external2.strictObject({
|
|
|
32762
32909
|
});
|
|
32763
32910
|
var sessionDetailResponseSchema = exports_external2.object({
|
|
32764
32911
|
id: exports_external2.string(),
|
|
32912
|
+
title: exports_external2.string().nullable(),
|
|
32765
32913
|
status: sessionStatusSchema,
|
|
32766
32914
|
session_number: exports_external2.number().int().positive(),
|
|
32767
32915
|
agent_status: exports_external2.string().min(1),
|
|
@@ -32852,7 +33000,13 @@ async function createCodexSession({
|
|
|
32852
33000
|
...parsedInput.reasoningEffort ? { reasoning_effort: parsedInput.reasoningEffort } : {}
|
|
32853
33001
|
}
|
|
32854
33002
|
} : {},
|
|
32855
|
-
repositories: parsedInput.repositoryIds.map((repositoryId) =>
|
|
33003
|
+
repositories: parsedInput.repositoryIds.map((repositoryId) => {
|
|
33004
|
+
const override = parsedInput.repositoryBranchOverrides?.find((candidate) => candidate.repositoryId === repositoryId);
|
|
33005
|
+
return {
|
|
33006
|
+
repository_id: repositoryId,
|
|
33007
|
+
...override ? { branch_name: override.branchName } : {}
|
|
33008
|
+
};
|
|
33009
|
+
}),
|
|
32856
33010
|
message: {
|
|
32857
33011
|
text: parsedInput.prompt,
|
|
32858
33012
|
file_ids: parsedInput.fileIds
|
|
@@ -32934,6 +33088,8 @@ async function listSessions({
|
|
|
32934
33088
|
client,
|
|
32935
33089
|
limit,
|
|
32936
33090
|
after,
|
|
33091
|
+
statuses,
|
|
33092
|
+
creatorUserIds,
|
|
32937
33093
|
signal
|
|
32938
33094
|
}) {
|
|
32939
33095
|
const searchParams = new URLSearchParams;
|
|
@@ -32941,6 +33097,10 @@ async function listSessions({
|
|
|
32941
33097
|
searchParams.set("limit", String(limit));
|
|
32942
33098
|
if (after)
|
|
32943
33099
|
searchParams.set("after", after);
|
|
33100
|
+
for (const status of statuses ?? [])
|
|
33101
|
+
searchParams.append("status", status);
|
|
33102
|
+
for (const creatorUserId of creatorUserIds ?? [])
|
|
33103
|
+
searchParams.append("creator_user_id", creatorUserId);
|
|
32944
33104
|
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
32945
33105
|
const response = await client.request(`/v1/sessions${suffix}`, { signal });
|
|
32946
33106
|
const parsed = sessionListResponseSchema.safeParse(await parseJson2(response, "Session list response was not valid JSON."));
|
|
@@ -33027,7 +33187,8 @@ async function listSessionEvents({
|
|
|
33027
33187
|
client,
|
|
33028
33188
|
sessionId,
|
|
33029
33189
|
limit,
|
|
33030
|
-
after
|
|
33190
|
+
after,
|
|
33191
|
+
signal
|
|
33031
33192
|
}) {
|
|
33032
33193
|
const searchParams = new URLSearchParams;
|
|
33033
33194
|
if (limit !== undefined)
|
|
@@ -33035,7 +33196,7 @@ async function listSessionEvents({
|
|
|
33035
33196
|
if (after)
|
|
33036
33197
|
searchParams.set("after", after);
|
|
33037
33198
|
const suffix = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
|
|
33038
|
-
const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/events${suffix}
|
|
33199
|
+
const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/events${suffix}`, { signal });
|
|
33039
33200
|
const parsed = sessionEventsResponseSchema.safeParse(await parseJson3(response, "Session events response was not valid JSON."));
|
|
33040
33201
|
if (!parsed.success)
|
|
33041
33202
|
throw new CodingAgentProtocolError("Session events response did not match the public API contract.", { cause: parsed.error });
|
|
@@ -33925,15 +34086,16 @@ var sessionWorkspaceGitRevisionEventSchema = exports_external2.object({
|
|
|
33925
34086
|
trigger: exports_external2.enum(["turn-ended", "boot-reconcile"])
|
|
33926
34087
|
}).passthrough()
|
|
33927
34088
|
}).passthrough();
|
|
33928
|
-
var agentTurnEndedEventSchema =
|
|
33929
|
-
type:
|
|
33930
|
-
turnId:
|
|
33931
|
-
actor:
|
|
33932
|
-
payload:
|
|
34089
|
+
var agentTurnEndedEventSchema = wrappedTurnEndedAgentEventSchema.pick({
|
|
34090
|
+
type: true,
|
|
34091
|
+
turnId: true,
|
|
34092
|
+
actor: true,
|
|
34093
|
+
payload: true
|
|
33933
34094
|
}).passthrough();
|
|
33934
|
-
var agentMessageDeltaEventSchema =
|
|
33935
|
-
type:
|
|
33936
|
-
|
|
34095
|
+
var agentMessageDeltaEventSchema = wrappedMessageDeltaAgentEventSchema.pick({
|
|
34096
|
+
type: true,
|
|
34097
|
+
actor: true,
|
|
34098
|
+
payload: true
|
|
33937
34099
|
}).passthrough();
|
|
33938
34100
|
var publicMessageContentSegmentSchema = exports_external2.discriminatedUnion("type", [
|
|
33939
34101
|
exports_external2.strictObject({ type: exports_external2.literal("text"), text: exports_external2.string() }),
|
|
@@ -33970,7 +34132,9 @@ function createSessionViewState({ detail, activeMessageId }) {
|
|
|
33970
34132
|
kind: "session-view",
|
|
33971
34133
|
sessionId: detail.id,
|
|
33972
34134
|
...detail.sessionNumber === undefined ? {} : { sessionNumber: detail.sessionNumber },
|
|
34135
|
+
title: detail.title ?? null,
|
|
33973
34136
|
aggregateStatus: detail.status,
|
|
34137
|
+
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
33974
34138
|
connectionStatus: "connected",
|
|
33975
34139
|
pullRequest: toSessionPullRequest(detail),
|
|
33976
34140
|
...activeMessageId !== undefined ? { activeMessageId } : {},
|
|
@@ -33985,6 +34149,13 @@ function createSessionViewState({ detail, activeMessageId }) {
|
|
|
33985
34149
|
messageTurns: {},
|
|
33986
34150
|
retainedTurnStarts: {},
|
|
33987
34151
|
retainedTurnEnds: {},
|
|
34152
|
+
childLineage: {
|
|
34153
|
+
nextOrder: 0,
|
|
34154
|
+
runtimeEpochs: {},
|
|
34155
|
+
seenRuntimeBoundaryEventIds: {},
|
|
34156
|
+
facts: [],
|
|
34157
|
+
owners: []
|
|
34158
|
+
},
|
|
33988
34159
|
context: {
|
|
33989
34160
|
snapshot: { status: "unknown" },
|
|
33990
34161
|
through: 0
|
|
@@ -33996,7 +34167,9 @@ function updateSessionDetail({ state, detail }) {
|
|
|
33996
34167
|
...state,
|
|
33997
34168
|
sessionId: detail.id,
|
|
33998
34169
|
...detail.sessionNumber === undefined ? {} : { sessionNumber: detail.sessionNumber },
|
|
34170
|
+
..."title" in detail ? { title: detail.title ?? null } : {},
|
|
33999
34171
|
aggregateStatus: detail.status,
|
|
34172
|
+
...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
|
|
34000
34173
|
pullRequest: toSessionPullRequest(detail),
|
|
34001
34174
|
connectionPreviews: toConnectionPreviews(detail)
|
|
34002
34175
|
};
|
|
@@ -34038,7 +34211,7 @@ function sessionTurnOutcome({ state, sessionMessageId }) {
|
|
|
34038
34211
|
}
|
|
34039
34212
|
function projectEphemeralEvent({ state, event }) {
|
|
34040
34213
|
const messageDelta = agentMessageDeltaEventSchema.safeParse(event);
|
|
34041
|
-
if (messageDelta.success && messageDelta.data.payload.role === "assistant") {
|
|
34214
|
+
if (messageDelta.success && messageDelta.data.actor.type === "main" && messageDelta.data.payload.role === "assistant") {
|
|
34042
34215
|
return {
|
|
34043
34216
|
...state,
|
|
34044
34217
|
previews: { ...state.previews, assistantText: state.previews.assistantText + messageDelta.data.payload.delta }
|
|
@@ -34059,6 +34232,7 @@ function projectRetainedEvent({
|
|
|
34059
34232
|
const workspaceGitRevision = sessionWorkspaceGitRevisionEventSchema.safeParse(event);
|
|
34060
34233
|
const turnEnded = agentTurnEndedEventSchema.safeParse(event);
|
|
34061
34234
|
let projected = messageCreated.success ? projectSessionMessageCreated({ state, event: messageCreated.data, occurredAt }) : turnAssociated.success ? projectSessionMessageTurnAssociated({ state, event: turnAssociated.data }) : workspaceGitInitialized.success ? projectSessionWorkspaceGitInitialized({ state, event: workspaceGitInitialized.data, occurredAt, retainedEventId }) : workspaceGitRevision.success ? projectSessionWorkspaceGitRevision({ state, event: workspaceGitRevision.data, occurredAt, retainedEventId }) : turnEnded.success ? projectAgentTurnEnded({ state, event: turnEnded.data, occurredAt, retainedEventId }) : projectDurableAgentEvent({ state, event, occurredAt, retainedEventId });
|
|
34235
|
+
projected = recordAgentLineageEvent({ state: projected, event, retainedEventId });
|
|
34062
34236
|
if (artifact) {
|
|
34063
34237
|
const publication = artifact.kind === "file" ? { artifactId: artifact.artifact_id, kind: artifact.kind, title: artifact.filename } : { artifactId: artifact.artifact_id, kind: artifact.kind, title: artifact.title, url: artifact.url };
|
|
34064
34238
|
const timelineArtifact = artifact.kind === "file" ? {
|
|
@@ -34067,7 +34241,9 @@ function projectRetainedEvent({
|
|
|
34067
34241
|
artifactId: artifact.artifact_id,
|
|
34068
34242
|
occurredAt,
|
|
34069
34243
|
title: artifact.filename,
|
|
34070
|
-
mediaType: artifact.media_type
|
|
34244
|
+
mediaType: artifact.media_type,
|
|
34245
|
+
...artifact.download_url === undefined ? {} : { downloadUrl: artifact.download_url },
|
|
34246
|
+
...artifact.preview_url === undefined ? {} : { previewUrl: artifact.preview_url }
|
|
34071
34247
|
} : {
|
|
34072
34248
|
kind: "artifact",
|
|
34073
34249
|
artifactKind: artifact.kind,
|
|
@@ -34171,19 +34347,22 @@ function projectSessionWorkspaceGitRevision({ state, event, occurredAt, retained
|
|
|
34171
34347
|
});
|
|
34172
34348
|
}
|
|
34173
34349
|
function projectAgentTurnEnded({ state, event, occurredAt, retainedEventId }) {
|
|
34174
|
-
const
|
|
34175
|
-
|
|
34176
|
-
|
|
34177
|
-
|
|
34178
|
-
|
|
34179
|
-
|
|
34180
|
-
|
|
34350
|
+
const stateWithTurnOutcome = event.actor.type === "main" ? {
|
|
34351
|
+
...state,
|
|
34352
|
+
retainedTurnEnds: { ...state.retainedTurnEnds, [event.turnId]: { actorType: event.actor.type, outcome: event.payload.status } },
|
|
34353
|
+
messageTurns: Object.fromEntries(Object.entries(state.messageTurns).map(([messageId, turn]) => [messageId, turn.turnId === event.turnId ? { ...turn, outcome: event.payload.status } : turn]))
|
|
34354
|
+
} : state;
|
|
34355
|
+
const card = event.actor.type === "main" ? event.payload.status === "failed" ? { kind: "failure", weight: "signal", title: "Remy turn failed", summary: "Turn failed." } : { kind: "lifecycle", weight: "noise", title: "Remy turn ended", summary: `Turn ${event.payload.status}.` } : createChildActivityCard({
|
|
34356
|
+
identity: event.actor,
|
|
34357
|
+
activityTitle: event.payload.status === "failed" ? "Turn failed" : "Turn ended",
|
|
34358
|
+
card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Turn failed.") } : { kind: "lifecycle", weight: "noise", summary: `Turn ${event.payload.status}.` }
|
|
34181
34359
|
});
|
|
34360
|
+
return appendActivity({ state: stateWithTurnOutcome, retainedEventId, occurredAt, card });
|
|
34182
34361
|
}
|
|
34183
34362
|
function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId }) {
|
|
34184
34363
|
const agentEvent = parseDurableAgentEvent(event);
|
|
34185
34364
|
if (agentEvent.type === "agent.message.ended")
|
|
34186
|
-
return projectAgentMessageEnded({ state, event: agentEvent, occurredAt });
|
|
34365
|
+
return projectAgentMessageEnded({ state, event: agentEvent, occurredAt, retainedEventId });
|
|
34187
34366
|
if (agentEvent.type === "agent.context.updated")
|
|
34188
34367
|
return state;
|
|
34189
34368
|
if (agentEvent.type === "agent.work.observed" && agentEvent.actor.type === "main")
|
|
@@ -34195,6 +34374,9 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId })
|
|
|
34195
34374
|
} : state;
|
|
34196
34375
|
return appendActivity({ state: stateWithTurnStart, retainedEventId, occurredAt, card: toAgentActivityCard(agentEvent) });
|
|
34197
34376
|
}
|
|
34377
|
+
function hasOwnContextIdentity(identities, identity) {
|
|
34378
|
+
return Object.prototype.hasOwnProperty.call(identities, identity);
|
|
34379
|
+
}
|
|
34198
34380
|
function projectAgentWorkObserved({
|
|
34199
34381
|
state,
|
|
34200
34382
|
event,
|
|
@@ -34266,23 +34448,45 @@ function patchedWorkItemId(observation) {
|
|
|
34266
34448
|
return observation.itemId;
|
|
34267
34449
|
return null;
|
|
34268
34450
|
}
|
|
34269
|
-
function projectAgentMessageEnded({ state, event, occurredAt }) {
|
|
34451
|
+
function projectAgentMessageEnded({ state, event, occurredAt, retainedEventId }) {
|
|
34270
34452
|
const messageId = event.payload.messageId;
|
|
34453
|
+
const text = event.payload.content.map(publicMessageContentSegmentText).join("");
|
|
34454
|
+
if (event.actor.type === "subagent") {
|
|
34455
|
+
const childId = terminalSafeSingleLine(event.actor.subagentId);
|
|
34456
|
+
const card = createChildActivityCard({
|
|
34457
|
+
identity: event.actor,
|
|
34458
|
+
activityTitle: event.payload.role === "assistant" ? "Message completed" : `${capitalize(event.payload.role)} message`,
|
|
34459
|
+
titleIncludesActivity: false,
|
|
34460
|
+
card: {
|
|
34461
|
+
kind: event.payload.role === "assistant" ? "command-output" : "progress",
|
|
34462
|
+
weight: "signal",
|
|
34463
|
+
summary: `${event.payload.role === "assistant" ? "Message completed." : `${capitalize(event.payload.role)} message retained.`} \xB7 ${childId}`,
|
|
34464
|
+
detail: stripAnsi(text) || "[Empty message]"
|
|
34465
|
+
}
|
|
34466
|
+
});
|
|
34467
|
+
return appendActivity({ state, retainedEventId, occurredAt, card });
|
|
34468
|
+
}
|
|
34271
34469
|
if (state.transcript.some((item) => item.kind === "message" && item.messageId === messageId))
|
|
34272
34470
|
return state;
|
|
34273
34471
|
const role2 = event.payload.role;
|
|
34274
34472
|
const author = role2 === "assistant" ? { label: "Remy", role: "remy" } : role2 === "system" ? { label: "System", role: "system" } : { label: event.payload.commandId ? state.commandAuthorLabels[event.payload.commandId] ?? "Human" : "Human", role: "human" };
|
|
34275
|
-
const
|
|
34473
|
+
const displayedText = text || (author.role === "remy" ? "[Remy sent an empty message]" : "[Empty message]");
|
|
34276
34474
|
return {
|
|
34277
34475
|
...state,
|
|
34278
34476
|
...author.role === "remy" ? { previews: { ...state.previews, assistantText: "" } } : {},
|
|
34279
|
-
transcript: [...state.transcript, { kind: "message", messageId, occurredAt, author, text, attachments: [] }]
|
|
34477
|
+
transcript: [...state.transcript, { kind: "message", messageId, occurredAt, author, text: displayedText, attachments: [] }]
|
|
34280
34478
|
};
|
|
34281
34479
|
}
|
|
34282
34480
|
function appendActivity({ state, retainedEventId, occurredAt, card }) {
|
|
34283
34481
|
return { ...state, transcript: [...state.transcript, { kind: "activity", activityId: `activity:${retainedEventId}`, occurredAt, card }] };
|
|
34284
34482
|
}
|
|
34285
34483
|
function toAgentActivityCard(event) {
|
|
34484
|
+
const identity = childActivityIdentity(event);
|
|
34485
|
+
if (identity)
|
|
34486
|
+
return toChildAgentActivityCard({ event, identity });
|
|
34487
|
+
return toMainAgentActivityCard(event);
|
|
34488
|
+
}
|
|
34489
|
+
function toMainAgentActivityCard(event) {
|
|
34286
34490
|
switch (event.type) {
|
|
34287
34491
|
case "agent.session.started":
|
|
34288
34492
|
return { kind: "lifecycle", weight: "noise", title: "Remy session started", summary: "Session is ready." };
|
|
@@ -34347,6 +34551,444 @@ function toAgentActivityCard(event) {
|
|
|
34347
34551
|
throw new SessionProjectionProtocolError("Cannot project an unknown retained agent event.");
|
|
34348
34552
|
}
|
|
34349
34553
|
}
|
|
34554
|
+
function toChildAgentActivityCard({ event, identity }) {
|
|
34555
|
+
switch (event.type) {
|
|
34556
|
+
case "agent.turn.started":
|
|
34557
|
+
return createChildActivityCard({ identity, activityTitle: "Turn started", card: { kind: "lifecycle", weight: "noise", summary: "Turn started." } });
|
|
34558
|
+
case "agent.turn.ended":
|
|
34559
|
+
return createChildActivityCard({
|
|
34560
|
+
identity,
|
|
34561
|
+
activityTitle: event.payload.status === "failed" ? "Turn failed" : "Turn ended",
|
|
34562
|
+
card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Turn failed.") } : { kind: "lifecycle", weight: "noise", summary: `Turn ${event.payload.status}.` }
|
|
34563
|
+
});
|
|
34564
|
+
case "agent.work.observed":
|
|
34565
|
+
return createChildActivityCard({ identity, activityTitle: "Work progress", card: { kind: "progress", weight: "noise", summary: "Subagent updated its work plan.", detail: jsonDetail(event.payload.observations) } });
|
|
34566
|
+
case "agent.message.started":
|
|
34567
|
+
return createChildActivityCard({ identity, activityTitle: "Message started", card: { kind: "lifecycle", weight: "noise", summary: "Subagent is composing a message." } });
|
|
34568
|
+
case "agent.reasoning.started":
|
|
34569
|
+
return createChildActivityCard({ identity, activityTitle: "Reasoning", card: { kind: "reasoning", weight: "noise", summary: "Subagent started a reasoning summary." } });
|
|
34570
|
+
case "agent.reasoning.ended": {
|
|
34571
|
+
const summary = stripAnsi(event.payload.summary ?? "").trim();
|
|
34572
|
+
return createChildActivityCard({
|
|
34573
|
+
identity,
|
|
34574
|
+
activityTitle: "Reasoning",
|
|
34575
|
+
card: summary.length > 0 ? { kind: "reasoning", weight: "signal", summary } : { kind: "reasoning", weight: "noise", summary: "Subagent completed a reasoning summary." }
|
|
34576
|
+
});
|
|
34577
|
+
}
|
|
34578
|
+
case "agent.tool.call.started":
|
|
34579
|
+
return createChildActivityCard({
|
|
34580
|
+
identity,
|
|
34581
|
+
activityTitle: terminalSafeSingleLine(event.payload.toolName),
|
|
34582
|
+
card: {
|
|
34583
|
+
kind: "tool",
|
|
34584
|
+
weight: "signal",
|
|
34585
|
+
summary: "Tool started.",
|
|
34586
|
+
...event.payload.input === undefined ? {} : { detail: jsonDetail(event.payload.input), detailFormat: "code" }
|
|
34587
|
+
}
|
|
34588
|
+
});
|
|
34589
|
+
case "agent.tool.call.completed": {
|
|
34590
|
+
const failed = event.payload.status === "failed" || event.payload.status === "cancelled";
|
|
34591
|
+
const detail = event.payload.output === undefined ? undefined : toolOutputDetail(event.payload.output);
|
|
34592
|
+
return createChildActivityCard({
|
|
34593
|
+
identity,
|
|
34594
|
+
activityTitle: failed ? "Tool failed" : "Tool completed",
|
|
34595
|
+
card: failed ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, `Tool ${event.payload.status}.`), ...detail === undefined ? {} : { detail, detailFormat: "code" } } : { kind: "command-output", weight: "signal", summary: "Tool completed.", ...detail === undefined ? {} : { detail, detailFormat: "code" } }
|
|
34596
|
+
});
|
|
34597
|
+
}
|
|
34598
|
+
case "agent.subagent.started":
|
|
34599
|
+
return createChildActivityCard({ identity, activityTitle: "Started", card: { kind: "progress", weight: "signal", summary: providerText(event.payload.name, event.payload.subagentId) } });
|
|
34600
|
+
case "agent.subagent.progress":
|
|
34601
|
+
return createChildActivityCard({ identity, activityTitle: "Progress", card: { kind: "progress", weight: "signal", summary: providerText(event.payload.summary, "Subagent is working.") } });
|
|
34602
|
+
case "agent.subagent.ended":
|
|
34603
|
+
return createChildActivityCard({
|
|
34604
|
+
identity,
|
|
34605
|
+
activityTitle: event.payload.status === "failed" ? "Failed" : "Ended",
|
|
34606
|
+
card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Subagent failed.") } : { kind: "progress", weight: "signal", summary: providerText(event.payload.summary, `Subagent ${event.payload.status}.`) }
|
|
34607
|
+
});
|
|
34608
|
+
case "agent.usage":
|
|
34609
|
+
return createChildActivityCard({ identity, activityTitle: "Usage updated", card: { kind: "progress", weight: "noise", summary: "Subagent reported usage.", detail: jsonDetail(event.payload.usage) } });
|
|
34610
|
+
case "agent.context.compaction.started":
|
|
34611
|
+
return createChildActivityCard({ identity, activityTitle: "Context compaction started", card: { kind: "reasoning", weight: "noise", summary: "Subagent is compacting context." } });
|
|
34612
|
+
case "agent.context.compaction.completed":
|
|
34613
|
+
return createChildActivityCard({
|
|
34614
|
+
identity,
|
|
34615
|
+
activityTitle: event.payload.status === "failed" ? "Context compaction failed" : "Context compaction completed",
|
|
34616
|
+
card: event.payload.status === "failed" ? { kind: "failure", weight: "signal", summary: providerText(event.payload.error?.message, "Context compaction failed.") } : { kind: "reasoning", weight: "noise", summary: "Subagent compacted context." }
|
|
34617
|
+
});
|
|
34618
|
+
case "agent.user-input.requested":
|
|
34619
|
+
return createChildActivityCard({ identity, activityTitle: "Needs input", card: { kind: "approval-question", weight: "signal", summary: providerText(event.payload.prompt, "Subagent needs input."), detail: jsonDetail(event.payload.questions) } });
|
|
34620
|
+
case "agent.session.started":
|
|
34621
|
+
case "agent.session.configured":
|
|
34622
|
+
case "agent.session.skills.updated":
|
|
34623
|
+
case "agent.session.state.changed":
|
|
34624
|
+
case "agent.session.ended":
|
|
34625
|
+
case "agent.user-input.resolved":
|
|
34626
|
+
case "agent.error":
|
|
34627
|
+
throw new SessionProjectionProtocolError(`Cannot attribute non-child session event type ${event.type} to a subagent.`);
|
|
34628
|
+
case "agent.message.delta":
|
|
34629
|
+
case "agent.reasoning.summary.delta":
|
|
34630
|
+
case "agent.tool.output.delta":
|
|
34631
|
+
throw new SessionProjectionProtocolError(`Cannot project ephemeral session event type ${event.type} as retained history.`);
|
|
34632
|
+
default:
|
|
34633
|
+
throw new SessionProjectionProtocolError("Cannot attribute an unknown retained event to a subagent.");
|
|
34634
|
+
}
|
|
34635
|
+
}
|
|
34636
|
+
function createChildActivityCard({
|
|
34637
|
+
identity,
|
|
34638
|
+
activityTitle,
|
|
34639
|
+
titleIncludesActivity = true,
|
|
34640
|
+
card
|
|
34641
|
+
}) {
|
|
34642
|
+
const subagentId = terminalSafeSingleLine(identity.subagentId);
|
|
34643
|
+
const actorId = terminalSafeSingleLine(identity.actorId);
|
|
34644
|
+
const displayName = terminalSafeSingleLine(identity.name ?? "") || subagentId;
|
|
34645
|
+
const identityDetail = [
|
|
34646
|
+
`Subagent ID: ${subagentId}`,
|
|
34647
|
+
...actorId !== subagentId ? [`Actor ID: ${actorId}`] : []
|
|
34648
|
+
].join(`
|
|
34649
|
+
`);
|
|
34650
|
+
const detail = card.detail === undefined ? identityDetail : `${identityDetail}
|
|
34651
|
+
|
|
34652
|
+
${card.detail}`;
|
|
34653
|
+
return {
|
|
34654
|
+
...card,
|
|
34655
|
+
title: `Subagent \xB7 ${displayName}${activityTitle === undefined || !titleIncludesActivity ? "" : ` \xB7 ${activityTitle}`}`,
|
|
34656
|
+
detail,
|
|
34657
|
+
attribution: {
|
|
34658
|
+
status: "unresolved",
|
|
34659
|
+
identity,
|
|
34660
|
+
...activityTitle === undefined ? {} : { activityTitle }
|
|
34661
|
+
}
|
|
34662
|
+
};
|
|
34663
|
+
}
|
|
34664
|
+
function childActivityIdentity(event) {
|
|
34665
|
+
if (event.type === "agent.subagent.started" || event.type === "agent.subagent.progress" || event.type === "agent.subagent.ended") {
|
|
34666
|
+
if (event.actor.type === "subagent") {
|
|
34667
|
+
if (!subagentLifecycleIdentityMatches({ actor: event.actor, payload: event.payload }))
|
|
34668
|
+
throw new SessionProjectionProtocolError(`Retained session event type ${event.type} carried contradictory subagent actor and payload identity.`);
|
|
34669
|
+
return {
|
|
34670
|
+
...event.actor,
|
|
34671
|
+
...event.actor.name === undefined && event.payload.name !== undefined ? { name: event.payload.name } : {}
|
|
34672
|
+
};
|
|
34673
|
+
}
|
|
34674
|
+
return {
|
|
34675
|
+
type: "subagent",
|
|
34676
|
+
actorId: event.payload.actorId,
|
|
34677
|
+
subagentId: event.payload.subagentId,
|
|
34678
|
+
parentActorId: event.payload.parentActorId,
|
|
34679
|
+
origin: event.payload.origin,
|
|
34680
|
+
...event.payload.parentToolCallId === undefined ? {} : { parentToolCallId: event.payload.parentToolCallId },
|
|
34681
|
+
...event.payload.name === undefined ? {} : { name: event.payload.name }
|
|
34682
|
+
};
|
|
34683
|
+
}
|
|
34684
|
+
if ("actor" in event && event.actor.type === "subagent") {
|
|
34685
|
+
return event.actor;
|
|
34686
|
+
}
|
|
34687
|
+
return;
|
|
34688
|
+
}
|
|
34689
|
+
function subagentLifecycleIdentityMatches({ actor, payload }) {
|
|
34690
|
+
return actor.actorId === payload.actorId && actor.subagentId === payload.subagentId && actor.parentActorId === payload.parentActorId && actor.parentToolCallId === payload.parentToolCallId && subagentOriginsMatch(actor.origin, payload.origin);
|
|
34691
|
+
}
|
|
34692
|
+
function subagentOriginsMatch(left, right) {
|
|
34693
|
+
if (left.type === "tool_call")
|
|
34694
|
+
return right.type === "tool_call" && left.toolCallId === right.toolCallId;
|
|
34695
|
+
return right.type === "provider_task" && left.taskId === right.taskId;
|
|
34696
|
+
}
|
|
34697
|
+
function recordAgentLineageEvent({
|
|
34698
|
+
state,
|
|
34699
|
+
event,
|
|
34700
|
+
retainedEventId
|
|
34701
|
+
}) {
|
|
34702
|
+
if (!event.type.startsWith("agent."))
|
|
34703
|
+
return state;
|
|
34704
|
+
let parsed;
|
|
34705
|
+
try {
|
|
34706
|
+
parsed = parseDurableAgentEvent(event);
|
|
34707
|
+
} catch (error93) {
|
|
34708
|
+
if (error93 instanceof SessionProjectionProtocolError)
|
|
34709
|
+
return state;
|
|
34710
|
+
throw error93;
|
|
34711
|
+
}
|
|
34712
|
+
const runtimeBase = lineageRuntimeBase(parsed);
|
|
34713
|
+
let childLineage = state.childLineage;
|
|
34714
|
+
if (parsed.type === "agent.session.started" || parsed.type === "agent.session.ended") {
|
|
34715
|
+
const boundaryKey = `${runtimeBase}:${lengthPrefixed(parsed.eventId)}`;
|
|
34716
|
+
if (hasOwnContextIdentity(childLineage.seenRuntimeBoundaryEventIds, boundaryKey))
|
|
34717
|
+
return state;
|
|
34718
|
+
childLineage = {
|
|
34719
|
+
...childLineage,
|
|
34720
|
+
runtimeEpochs: parsed.type === "agent.session.started" ? { ...childLineage.runtimeEpochs, [runtimeBase]: (childLineage.runtimeEpochs[runtimeBase] ?? 0) + 1 } : childLineage.runtimeEpochs,
|
|
34721
|
+
seenRuntimeBoundaryEventIds: {
|
|
34722
|
+
...childLineage.seenRuntimeBoundaryEventIds,
|
|
34723
|
+
[boundaryKey]: true
|
|
34724
|
+
}
|
|
34725
|
+
};
|
|
34726
|
+
return { ...state, childLineage };
|
|
34727
|
+
}
|
|
34728
|
+
if (!("turnId" in parsed))
|
|
34729
|
+
return state;
|
|
34730
|
+
const runtimeScope = lineageRuntimeScope({
|
|
34731
|
+
runtimeBase,
|
|
34732
|
+
epoch: childLineage.runtimeEpochs[runtimeBase] ?? 0
|
|
34733
|
+
});
|
|
34734
|
+
const order = childLineage.nextOrder;
|
|
34735
|
+
const identity = childActivityIdentity(parsed);
|
|
34736
|
+
const fact = identity ? {
|
|
34737
|
+
retainedEventId,
|
|
34738
|
+
order,
|
|
34739
|
+
runtimeScope,
|
|
34740
|
+
turnId: parsed.turnId,
|
|
34741
|
+
eventType: parsed.type,
|
|
34742
|
+
identity
|
|
34743
|
+
} : parsed.type === "agent.tool.call.started" && parsed.actor.type === "main" ? {
|
|
34744
|
+
retainedEventId,
|
|
34745
|
+
order,
|
|
34746
|
+
runtimeScope,
|
|
34747
|
+
turnId: parsed.turnId,
|
|
34748
|
+
eventType: parsed.type,
|
|
34749
|
+
toolCallId: parsed.payload.toolCallId
|
|
34750
|
+
} : undefined;
|
|
34751
|
+
if (!fact)
|
|
34752
|
+
return state;
|
|
34753
|
+
const nextState = {
|
|
34754
|
+
...state,
|
|
34755
|
+
childLineage: {
|
|
34756
|
+
...childLineage,
|
|
34757
|
+
nextOrder: order + 1,
|
|
34758
|
+
facts: [...childLineage.facts, fact]
|
|
34759
|
+
}
|
|
34760
|
+
};
|
|
34761
|
+
if (!isChildLineageFact(fact) || isSubagentLifecycleType(fact.eventType))
|
|
34762
|
+
return reconcileChildLineage(nextState);
|
|
34763
|
+
const resolution = resolveBodyFactAgainstOwners(childLineage.owners, fact);
|
|
34764
|
+
if (resolution.status === "graph-change")
|
|
34765
|
+
return reconcileChildLineage(nextState);
|
|
34766
|
+
if (resolution.status === "unresolved")
|
|
34767
|
+
return nextState;
|
|
34768
|
+
return stampResolvedAttribution({ state: nextState, retainedEventId, fact, owner: resolution.owner });
|
|
34769
|
+
}
|
|
34770
|
+
function resolveBodyFactAgainstOwners(owners, fact) {
|
|
34771
|
+
const candidates = owners.filter((owner) => owner.runtimeScope === fact.runtimeScope && childIdentityMatches(owner.identity, fact.identity));
|
|
34772
|
+
const turnCandidates = candidates.filter((owner) => owner.childTurnIds.has(fact.turnId));
|
|
34773
|
+
if (turnCandidates.length === 1)
|
|
34774
|
+
return { status: "known", owner: turnCandidates[0] };
|
|
34775
|
+
if (candidates.length === 1)
|
|
34776
|
+
return { status: "graph-change" };
|
|
34777
|
+
return { status: "unresolved" };
|
|
34778
|
+
}
|
|
34779
|
+
function stampResolvedAttribution({ state, retainedEventId, fact, owner }) {
|
|
34780
|
+
const activityId = `activity:${retainedEventId}`;
|
|
34781
|
+
for (let index = state.transcript.length - 1;index >= 0; index -= 1) {
|
|
34782
|
+
const item = state.transcript[index];
|
|
34783
|
+
if (item.kind !== "activity" || item.activityId !== activityId)
|
|
34784
|
+
continue;
|
|
34785
|
+
if (!item.card.attribution)
|
|
34786
|
+
return state;
|
|
34787
|
+
const attribution = {
|
|
34788
|
+
status: "resolved",
|
|
34789
|
+
identity: fact.identity,
|
|
34790
|
+
...item.card.attribution.activityTitle === undefined ? {} : { activityTitle: item.card.attribution.activityTitle },
|
|
34791
|
+
ownerKey: owner.key,
|
|
34792
|
+
owningMainTurnId: owner.owningMainTurnId,
|
|
34793
|
+
path: ownerPath(owner)
|
|
34794
|
+
};
|
|
34795
|
+
if (childAttributionEqual(item.card.attribution, attribution))
|
|
34796
|
+
return state;
|
|
34797
|
+
const transcript = [...state.transcript];
|
|
34798
|
+
transcript[index] = { ...item, card: { ...item.card, attribution } };
|
|
34799
|
+
return { ...state, transcript };
|
|
34800
|
+
}
|
|
34801
|
+
return state;
|
|
34802
|
+
}
|
|
34803
|
+
function lineageRuntimeBase(event) {
|
|
34804
|
+
return `${lengthPrefixed(event.sessionId)}:${lengthPrefixed(event.providerSessionId)}`;
|
|
34805
|
+
}
|
|
34806
|
+
function lineageRuntimeScope({ runtimeBase, epoch }) {
|
|
34807
|
+
return `${runtimeBase}:${epoch}`;
|
|
34808
|
+
}
|
|
34809
|
+
function lengthPrefixed(value) {
|
|
34810
|
+
return `${value.length}:${value}`;
|
|
34811
|
+
}
|
|
34812
|
+
function isChildLineageFact(fact) {
|
|
34813
|
+
return "identity" in fact;
|
|
34814
|
+
}
|
|
34815
|
+
function isSubagentLifecycleType(type) {
|
|
34816
|
+
return type === "agent.subagent.started" || type === "agent.subagent.progress" || type === "agent.subagent.ended";
|
|
34817
|
+
}
|
|
34818
|
+
function childIdentityMatches(left, right) {
|
|
34819
|
+
return left.actorId === right.actorId && left.subagentId === right.subagentId && left.parentActorId === right.parentActorId && left.parentToolCallId === right.parentToolCallId && subagentOriginsMatch(left.origin, right.origin);
|
|
34820
|
+
}
|
|
34821
|
+
function childIdentityCanAnchor(identity) {
|
|
34822
|
+
return identity.actorId !== identity.parentActorId && (identity.origin.type !== "tool_call" || identity.parentToolCallId === undefined || identity.parentToolCallId === identity.origin.toolCallId);
|
|
34823
|
+
}
|
|
34824
|
+
function reconcileChildLineage(state) {
|
|
34825
|
+
const facts = state.childLineage.facts;
|
|
34826
|
+
const childFacts = facts.filter(isChildLineageFact);
|
|
34827
|
+
const mainToolTurns = new Map;
|
|
34828
|
+
for (const fact of facts) {
|
|
34829
|
+
if (isChildLineageFact(fact))
|
|
34830
|
+
continue;
|
|
34831
|
+
const key = `${fact.runtimeScope}:${lengthPrefixed(fact.toolCallId)}`;
|
|
34832
|
+
const turns = mainToolTurns.get(key) ?? new Set;
|
|
34833
|
+
turns.add(fact.turnId);
|
|
34834
|
+
mainToolTurns.set(key, turns);
|
|
34835
|
+
}
|
|
34836
|
+
const owners = [];
|
|
34837
|
+
const ownerByFactOrder = new Map;
|
|
34838
|
+
const mainTurnForToolOrigin = (fact) => {
|
|
34839
|
+
if (fact.identity.origin.type !== "tool_call")
|
|
34840
|
+
return;
|
|
34841
|
+
const turns = mainToolTurns.get(`${fact.runtimeScope}:${lengthPrefixed(fact.identity.origin.toolCallId)}`);
|
|
34842
|
+
return turns?.size === 1 ? [...turns][0] : undefined;
|
|
34843
|
+
};
|
|
34844
|
+
const createOwner = ({
|
|
34845
|
+
fact,
|
|
34846
|
+
owningMainTurnId,
|
|
34847
|
+
parent
|
|
34848
|
+
}) => {
|
|
34849
|
+
if (!childIdentityCanAnchor(fact.identity))
|
|
34850
|
+
return;
|
|
34851
|
+
if (parent && ownerHasActorAncestor(parent, fact.identity.actorId))
|
|
34852
|
+
return;
|
|
34853
|
+
const samePlacement = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && owner2.owningMainTurnId === owningMainTurnId && owner2.parent === parent && owner2.identity.actorId === fact.identity.actorId && owner2.identity.subagentId === fact.identity.subagentId);
|
|
34854
|
+
if (samePlacement.some((owner2) => !childIdentityMatches(owner2.identity, fact.identity)))
|
|
34855
|
+
return;
|
|
34856
|
+
const existing = samePlacement.find((owner2) => childIdentityMatches(owner2.identity, fact.identity));
|
|
34857
|
+
if (existing)
|
|
34858
|
+
return existing;
|
|
34859
|
+
const owner = {
|
|
34860
|
+
key: `${fact.runtimeScope}:${lengthPrefixed(owningMainTurnId)}:${fact.order}`,
|
|
34861
|
+
identity: fact.identity,
|
|
34862
|
+
runtimeScope: fact.runtimeScope,
|
|
34863
|
+
owningMainTurnId,
|
|
34864
|
+
parent,
|
|
34865
|
+
childTurnIds: new Set
|
|
34866
|
+
};
|
|
34867
|
+
owners.push(owner);
|
|
34868
|
+
return owner;
|
|
34869
|
+
};
|
|
34870
|
+
for (const fact of childFacts) {
|
|
34871
|
+
if (fact.eventType !== "agent.subagent.started" || fact.identity.parentActorId !== "main")
|
|
34872
|
+
continue;
|
|
34873
|
+
const toolTurn = mainTurnForToolOrigin(fact);
|
|
34874
|
+
if (fact.identity.origin.type === "tool_call" && toolTurn === undefined) {
|
|
34875
|
+
const turns = mainToolTurns.get(`${fact.runtimeScope}:${lengthPrefixed(fact.identity.origin.toolCallId)}`);
|
|
34876
|
+
if (turns && turns.size > 1)
|
|
34877
|
+
continue;
|
|
34878
|
+
}
|
|
34879
|
+
if (toolTurn !== undefined && toolTurn !== fact.turnId)
|
|
34880
|
+
continue;
|
|
34881
|
+
const owner = createOwner({ fact, owningMainTurnId: fact.turnId, parent: null });
|
|
34882
|
+
if (owner)
|
|
34883
|
+
ownerByFactOrder.set(fact.order, owner);
|
|
34884
|
+
}
|
|
34885
|
+
for (const fact of childFacts) {
|
|
34886
|
+
if (isSubagentLifecycleType(fact.eventType) && fact.eventType !== "agent.subagent.started" && fact.identity.parentActorId === "main") {
|
|
34887
|
+
const toolTurn = mainTurnForToolOrigin(fact);
|
|
34888
|
+
if (toolTurn === undefined)
|
|
34889
|
+
continue;
|
|
34890
|
+
const owner = owners.find((candidate) => candidate.runtimeScope === fact.runtimeScope && candidate.parent === null && candidate.owningMainTurnId === toolTurn && childIdentityMatches(candidate.identity, fact.identity)) ?? createOwner({ fact, owningMainTurnId: toolTurn, parent: null });
|
|
34891
|
+
if (owner) {
|
|
34892
|
+
ownerByFactOrder.set(fact.order, owner);
|
|
34893
|
+
owner.childTurnIds.add(fact.turnId);
|
|
34894
|
+
}
|
|
34895
|
+
}
|
|
34896
|
+
}
|
|
34897
|
+
let changed = true;
|
|
34898
|
+
while (changed) {
|
|
34899
|
+
changed = false;
|
|
34900
|
+
for (const fact of childFacts) {
|
|
34901
|
+
if (ownerByFactOrder.has(fact.order))
|
|
34902
|
+
continue;
|
|
34903
|
+
const candidates = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && childIdentityMatches(owner2.identity, fact.identity));
|
|
34904
|
+
const turnCandidates = candidates.filter((owner2) => owner2.childTurnIds.has(fact.turnId));
|
|
34905
|
+
const owner = turnCandidates.length === 1 ? turnCandidates[0] : candidates.length === 1 ? candidates[0] : undefined;
|
|
34906
|
+
if (!owner || fact.eventType === "agent.subagent.started")
|
|
34907
|
+
continue;
|
|
34908
|
+
ownerByFactOrder.set(fact.order, owner);
|
|
34909
|
+
owner.childTurnIds.add(fact.turnId);
|
|
34910
|
+
changed = true;
|
|
34911
|
+
}
|
|
34912
|
+
for (const fact of childFacts) {
|
|
34913
|
+
if (ownerByFactOrder.has(fact.order) || fact.eventType !== "agent.subagent.started" || fact.identity.parentActorId === "main" || !childIdentityCanAnchor(fact.identity)) {
|
|
34914
|
+
continue;
|
|
34915
|
+
}
|
|
34916
|
+
const parentCandidates = owners.filter((owner2) => owner2.runtimeScope === fact.runtimeScope && owner2.identity.actorId === fact.identity.parentActorId);
|
|
34917
|
+
const turnParents = parentCandidates.filter((owner2) => owner2.childTurnIds.has(fact.turnId));
|
|
34918
|
+
const parent = turnParents.length === 1 ? turnParents[0] : parentCandidates.length === 1 ? parentCandidates[0] : undefined;
|
|
34919
|
+
if (!parent)
|
|
34920
|
+
continue;
|
|
34921
|
+
const owner = createOwner({ fact, owningMainTurnId: parent.owningMainTurnId, parent });
|
|
34922
|
+
if (!owner)
|
|
34923
|
+
continue;
|
|
34924
|
+
ownerByFactOrder.set(fact.order, owner);
|
|
34925
|
+
changed = true;
|
|
34926
|
+
}
|
|
34927
|
+
}
|
|
34928
|
+
const resolvedByRetainedEventId = new Map;
|
|
34929
|
+
for (const fact of childFacts) {
|
|
34930
|
+
const owner = ownerByFactOrder.get(fact.order);
|
|
34931
|
+
if (owner)
|
|
34932
|
+
resolvedByRetainedEventId.set(fact.retainedEventId, { fact, owner });
|
|
34933
|
+
}
|
|
34934
|
+
let transcriptChanged = false;
|
|
34935
|
+
const transcript = state.transcript.map((item) => {
|
|
34936
|
+
if (item.kind !== "activity" || !item.card.attribution)
|
|
34937
|
+
return item;
|
|
34938
|
+
const retainedEventId = item.activityId.slice("activity:".length);
|
|
34939
|
+
const resolved = resolvedByRetainedEventId.get(retainedEventId);
|
|
34940
|
+
const attribution = resolved ? {
|
|
34941
|
+
status: "resolved",
|
|
34942
|
+
identity: resolved.fact.identity,
|
|
34943
|
+
...item.card.attribution.activityTitle === undefined ? {} : { activityTitle: item.card.attribution.activityTitle },
|
|
34944
|
+
ownerKey: resolved.owner.key,
|
|
34945
|
+
owningMainTurnId: resolved.owner.owningMainTurnId,
|
|
34946
|
+
path: ownerPath(resolved.owner)
|
|
34947
|
+
} : {
|
|
34948
|
+
status: "unresolved",
|
|
34949
|
+
identity: item.card.attribution.identity,
|
|
34950
|
+
...item.card.attribution.activityTitle === undefined ? {} : { activityTitle: item.card.attribution.activityTitle }
|
|
34951
|
+
};
|
|
34952
|
+
if (childAttributionEqual(item.card.attribution, attribution))
|
|
34953
|
+
return item;
|
|
34954
|
+
transcriptChanged = true;
|
|
34955
|
+
return { ...item, card: { ...item.card, attribution } };
|
|
34956
|
+
});
|
|
34957
|
+
const nextState = { ...state, childLineage: { ...state.childLineage, owners } };
|
|
34958
|
+
return transcriptChanged ? { ...nextState, transcript } : nextState;
|
|
34959
|
+
}
|
|
34960
|
+
function ownerHasActorAncestor(parent, actorId) {
|
|
34961
|
+
let candidate = parent;
|
|
34962
|
+
while (candidate) {
|
|
34963
|
+
if (candidate.identity.actorId === actorId)
|
|
34964
|
+
return true;
|
|
34965
|
+
candidate = candidate.parent;
|
|
34966
|
+
}
|
|
34967
|
+
return false;
|
|
34968
|
+
}
|
|
34969
|
+
function ownerPath(owner) {
|
|
34970
|
+
const path = [];
|
|
34971
|
+
let candidate = owner;
|
|
34972
|
+
while (candidate) {
|
|
34973
|
+
path.unshift(candidate.identity);
|
|
34974
|
+
candidate = candidate.parent;
|
|
34975
|
+
}
|
|
34976
|
+
return path;
|
|
34977
|
+
}
|
|
34978
|
+
function childAttributionEqual(left, right) {
|
|
34979
|
+
if (left.status !== right.status || left.activityTitle !== right.activityTitle || !childIdentityMatches(left.identity, right.identity)) {
|
|
34980
|
+
return false;
|
|
34981
|
+
}
|
|
34982
|
+
if (left.status === "unresolved" || right.status === "unresolved")
|
|
34983
|
+
return true;
|
|
34984
|
+
return left.ownerKey === right.ownerKey && left.owningMainTurnId === right.owningMainTurnId && left.path.length === right.path.length && left.path.every((identity, index) => childIdentityMatches(identity, right.path[index]));
|
|
34985
|
+
}
|
|
34986
|
+
function terminalSafeSingleLine(value) {
|
|
34987
|
+
return stripAnsi(value).replace(/\s+/gu, " ").trim();
|
|
34988
|
+
}
|
|
34989
|
+
function capitalize(value) {
|
|
34990
|
+
return value.length === 0 ? value : `${value[0].toUpperCase()}${value.slice(1)}`;
|
|
34991
|
+
}
|
|
34350
34992
|
function parseDurableAgentEvent(event) {
|
|
34351
34993
|
if (!event.type.startsWith("agent."))
|
|
34352
34994
|
throw new SessionProjectionProtocolError(`Cannot project retained session event type ${event.type}.`);
|
|
@@ -34383,6 +35025,9 @@ var terminalControlPattern = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
|
|
|
34383
35025
|
function stripAnsi(value) {
|
|
34384
35026
|
return value.replace(ansiEscapePattern, "").replace(terminalControlPattern, "");
|
|
34385
35027
|
}
|
|
35028
|
+
function terminalSafeText(value) {
|
|
35029
|
+
return stripAnsi(value);
|
|
35030
|
+
}
|
|
34386
35031
|
function formatReasoningPreview(item) {
|
|
34387
35032
|
if (item.status === "absent")
|
|
34388
35033
|
return "";
|
|
@@ -34583,12 +35228,12 @@ function createRemoteSessionController(dependencies) {
|
|
|
34583
35228
|
detail: input.detail,
|
|
34584
35229
|
activeMessageId: dependencies.activeMessageId ?? cache?.activeMessageId
|
|
34585
35230
|
});
|
|
34586
|
-
resolveReady();
|
|
34587
|
-
publishState();
|
|
34588
35231
|
if (input.mode === "cold-resume") {
|
|
34589
35232
|
const hydrationGeneration = beginReasoningAttempt();
|
|
34590
35233
|
await hydrateRetainedHistoryFromBeginning(hydrationGeneration);
|
|
34591
35234
|
}
|
|
35235
|
+
resolveReady();
|
|
35236
|
+
publishState();
|
|
34592
35237
|
await streamWithControllerReconnects();
|
|
34593
35238
|
}
|
|
34594
35239
|
function stop() {
|
|
@@ -34624,7 +35269,8 @@ function createRemoteSessionController(dependencies) {
|
|
|
34624
35269
|
async function recordActiveMessage(sessionMessageId) {
|
|
34625
35270
|
state = {
|
|
34626
35271
|
...getState(),
|
|
34627
|
-
activeMessageId: sessionMessageId
|
|
35272
|
+
activeMessageId: sessionMessageId,
|
|
35273
|
+
agentStatus: undefined
|
|
34628
35274
|
};
|
|
34629
35275
|
await writeCache();
|
|
34630
35276
|
publishState();
|
|
@@ -34645,7 +35291,8 @@ function createRemoteSessionController(dependencies) {
|
|
|
34645
35291
|
const page = await dependencies.listSessionEvents({
|
|
34646
35292
|
sessionId: dependencies.sessionId,
|
|
34647
35293
|
limit: sessionHistoryPageSize,
|
|
34648
|
-
after
|
|
35294
|
+
after,
|
|
35295
|
+
signal: abortController.signal
|
|
34649
35296
|
});
|
|
34650
35297
|
for (const item of page.data) {
|
|
34651
35298
|
await reduceFrameAndPersist({
|
|
@@ -34899,7 +35546,10 @@ async function sleepMs(ms, signal) {
|
|
|
34899
35546
|
}
|
|
34900
35547
|
|
|
34901
35548
|
// src/tui/dashboard.ts
|
|
34902
|
-
import { bg, BoxRenderable, bold as bold2, CliRenderEvents as CliRenderEvents2, fg as fg3, ScrollBoxRenderable, stringToStyledText as stringToStyledText2, TextRenderable } from "@opentui/core";
|
|
35549
|
+
import { bg, BoxRenderable as BoxRenderable2, bold as bold2, CliRenderEvents as CliRenderEvents2, fg as fg3, ScrollBoxRenderable, stringToStyledText as stringToStyledText2, TextRenderable as TextRenderable2 } from "@opentui/core";
|
|
35550
|
+
|
|
35551
|
+
// src/tui/composer.ts
|
|
35552
|
+
import { BoxRenderable, TextRenderable, TextareaRenderable } from "@opentui/core";
|
|
34903
35553
|
|
|
34904
35554
|
// src/tui/composer-divider.ts
|
|
34905
35555
|
import { dim as dim2, fg as fg2, StyledText as StyledText2 } from "@opentui/core";
|
|
@@ -35094,6 +35744,98 @@ function renderComposerDivider({ width, tag, leadLabel }) {
|
|
|
35094
35744
|
]);
|
|
35095
35745
|
}
|
|
35096
35746
|
|
|
35747
|
+
// src/tui/composer-height.ts
|
|
35748
|
+
var composerMaxRows = 6;
|
|
35749
|
+
function resizeComposer(composer) {
|
|
35750
|
+
composer.height = Math.min(Math.max(composer.editorView.getTotalVirtualLineCount(), 1), composerMaxRows);
|
|
35751
|
+
}
|
|
35752
|
+
|
|
35753
|
+
// src/tui/composer.ts
|
|
35754
|
+
var defaultPlaceholder = "Message Remy\u2026 Tab paths attach \xB7 Enter/Shift+Enter send \xB7 Option+Enter newline";
|
|
35755
|
+
function createComposer({
|
|
35756
|
+
renderer,
|
|
35757
|
+
parent,
|
|
35758
|
+
tag,
|
|
35759
|
+
topDividerLabel,
|
|
35760
|
+
placeholder = defaultPlaceholder,
|
|
35761
|
+
screenPaddingX = 1,
|
|
35762
|
+
onSubmit,
|
|
35763
|
+
onContentChange
|
|
35764
|
+
}) {
|
|
35765
|
+
const root = new BoxRenderable(renderer, { width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
35766
|
+
const dividerTop = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
35767
|
+
const textarea = new TextareaRenderable(renderer, {
|
|
35768
|
+
width: "100%",
|
|
35769
|
+
height: 1,
|
|
35770
|
+
flexShrink: 0,
|
|
35771
|
+
wrapMode: "word",
|
|
35772
|
+
placeholder,
|
|
35773
|
+
keyBindings: [
|
|
35774
|
+
{ name: "return", action: "submit" },
|
|
35775
|
+
{ name: "return", shift: true, action: "submit" },
|
|
35776
|
+
{ name: "return", meta: true, action: "newline" },
|
|
35777
|
+
{ name: "j", ctrl: true, action: "newline" }
|
|
35778
|
+
],
|
|
35779
|
+
onSubmit
|
|
35780
|
+
});
|
|
35781
|
+
const dividerBottom = new TextRenderable(renderer, { content: "", flexShrink: 0 });
|
|
35782
|
+
let mounted = false;
|
|
35783
|
+
root.add(dividerTop);
|
|
35784
|
+
root.add(textarea);
|
|
35785
|
+
root.add(dividerBottom);
|
|
35786
|
+
textarea.onContentChange = () => {
|
|
35787
|
+
resizeComposer(textarea);
|
|
35788
|
+
onContentChange?.();
|
|
35789
|
+
};
|
|
35790
|
+
function render() {
|
|
35791
|
+
const dividerWidth = renderer.width - screenPaddingX * 2;
|
|
35792
|
+
dividerTop.content = renderComposerDivider({ width: dividerWidth, tag, leadLabel: topDividerLabel?.() });
|
|
35793
|
+
dividerBottom.content = renderComposerDivider({ width: dividerWidth });
|
|
35794
|
+
}
|
|
35795
|
+
return {
|
|
35796
|
+
textarea,
|
|
35797
|
+
mount() {
|
|
35798
|
+
if (mounted)
|
|
35799
|
+
return;
|
|
35800
|
+
mounted = true;
|
|
35801
|
+
parent.add(root);
|
|
35802
|
+
resizeComposer(textarea);
|
|
35803
|
+
render();
|
|
35804
|
+
},
|
|
35805
|
+
unmount() {
|
|
35806
|
+
if (!mounted)
|
|
35807
|
+
return;
|
|
35808
|
+
mounted = false;
|
|
35809
|
+
parent.remove(root);
|
|
35810
|
+
},
|
|
35811
|
+
render,
|
|
35812
|
+
focus() {
|
|
35813
|
+
textarea.focus();
|
|
35814
|
+
},
|
|
35815
|
+
destroy() {
|
|
35816
|
+
if (mounted)
|
|
35817
|
+
parent.remove(root);
|
|
35818
|
+
mounted = false;
|
|
35819
|
+
root.destroyRecursively();
|
|
35820
|
+
}
|
|
35821
|
+
};
|
|
35822
|
+
}
|
|
35823
|
+
|
|
35824
|
+
// src/tui/fuzzy-match.ts
|
|
35825
|
+
function fuzzyMatches(value, query) {
|
|
35826
|
+
const normalizedQuery = query.replaceAll(/\s/g, "").toLowerCase();
|
|
35827
|
+
if (!normalizedQuery)
|
|
35828
|
+
return true;
|
|
35829
|
+
let queryIndex = 0;
|
|
35830
|
+
for (const character of value.toLowerCase()) {
|
|
35831
|
+
if (character === normalizedQuery[queryIndex])
|
|
35832
|
+
queryIndex += 1;
|
|
35833
|
+
if (queryIndex === normalizedQuery.length)
|
|
35834
|
+
return true;
|
|
35835
|
+
}
|
|
35836
|
+
return false;
|
|
35837
|
+
}
|
|
35838
|
+
|
|
35097
35839
|
// src/tui/renderer.ts
|
|
35098
35840
|
import { CliRenderEvents, createClipboard, createCliRenderer, createHostClipboard, createRendererClipboardAdapter } from "@opentui/core";
|
|
35099
35841
|
var rendererDestroyed = new WeakMap;
|
|
@@ -35176,8 +35918,17 @@ async function renderRemyView({
|
|
|
35176
35918
|
|
|
35177
35919
|
// src/tui/dashboard.ts
|
|
35178
35920
|
var DASHBOARD_POLL_INTERVAL_MS = 1e4;
|
|
35921
|
+
var lifecycleStatusOptions = [
|
|
35922
|
+
{ value: "open", label: "Open" },
|
|
35923
|
+
{ value: "completed", label: "Completed" },
|
|
35924
|
+
{ value: "failed", label: "Failed" },
|
|
35925
|
+
{ value: "cancelled", label: "Cancelled" }
|
|
35926
|
+
];
|
|
35179
35927
|
async function createDashboardTui({
|
|
35180
35928
|
initialPage,
|
|
35929
|
+
initialFilters = { statuses: [], creators: [] },
|
|
35930
|
+
authors: initialAuthors = [],
|
|
35931
|
+
loadAuthors,
|
|
35181
35932
|
loadPage,
|
|
35182
35933
|
createRenderer = createDefaultRenderer
|
|
35183
35934
|
}) {
|
|
@@ -35186,6 +35937,7 @@ async function createDashboardTui({
|
|
|
35186
35937
|
let keyHandler;
|
|
35187
35938
|
let resizeHandler;
|
|
35188
35939
|
let refreshTimer;
|
|
35940
|
+
let contentScrollHandler;
|
|
35189
35941
|
let rendererDestroyed2 = false;
|
|
35190
35942
|
const loadPageAbortController = new AbortController;
|
|
35191
35943
|
try {
|
|
@@ -35197,28 +35949,61 @@ async function createDashboardTui({
|
|
|
35197
35949
|
loadPageAbortController.abort();
|
|
35198
35950
|
}, render = function() {
|
|
35199
35951
|
const contentWidth = renderer.width - 4;
|
|
35200
|
-
const
|
|
35952
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
35953
|
+
const rowsBelow = rowsBelowVisibleWindow({ sessionCount: sessions.length, selectedIndex, height: renderer.height });
|
|
35201
35954
|
const isNarrow = renderer.width < 72;
|
|
35202
|
-
const selectedSession =
|
|
35203
|
-
const sessionRows =
|
|
35955
|
+
const selectedSession = filterMode ? undefined : sessions[selectedIndex];
|
|
35956
|
+
const sessionRows = filterMode ? formatFilterMenu({ mode: filterMode, authors, width: contentWidth }) : sessions.length === 0 ? stringToStyledText2(sessionSearchQuery ? "No sessions match the search." : hasActiveFilters(filters) ? "No sessions match the active filters." : "No remote sessions yet.") : isNarrow ? formatSessionCard({
|
|
35204
35957
|
session: selectedSession,
|
|
35205
35958
|
selectedIndex,
|
|
35206
|
-
sessionCount:
|
|
35959
|
+
sessionCount: sessions.length,
|
|
35207
35960
|
width: contentWidth
|
|
35208
35961
|
}) : formatSessionList({
|
|
35209
|
-
sessions
|
|
35962
|
+
sessions,
|
|
35210
35963
|
selectedIndex,
|
|
35211
35964
|
width: contentWidth,
|
|
35212
35965
|
height: renderer.height
|
|
35213
35966
|
});
|
|
35214
|
-
statusBand.content = dashboardStatusBand({ page, pageIndex });
|
|
35967
|
+
statusBand.content = dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery });
|
|
35215
35968
|
content.content = joinStyled([stringToStyledText2(""), sessionRows], `
|
|
35216
35969
|
`);
|
|
35970
|
+
desiredContentScrollY = filterMode ? filterMode.selectedIndex + 2 : 0;
|
|
35971
|
+
if (!contentScrollHandler) {
|
|
35972
|
+
contentScrollHandler = () => {
|
|
35973
|
+
contentScrollHandler = undefined;
|
|
35974
|
+
if (destroyed)
|
|
35975
|
+
return;
|
|
35976
|
+
listRegion.scrollTo({ x: 0, y: desiredContentScrollY });
|
|
35977
|
+
renderer.requestRender();
|
|
35978
|
+
};
|
|
35979
|
+
renderer.once(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
35980
|
+
}
|
|
35217
35981
|
const showPreview = !isNarrow && selectedSession !== undefined;
|
|
35218
35982
|
previewBand.visible = showPreview;
|
|
35219
35983
|
previewBand.content = showPreview ? formatSessionPreview({ session: selectedSession, width: contentWidth }) : stringToStyledText2("");
|
|
35220
|
-
|
|
35221
|
-
|
|
35984
|
+
const activeSearchComposer = filterMode?.searching ? "filter" : sessionSearchOpen ? "session" : undefined;
|
|
35985
|
+
if (activeSearchComposer !== mountedSearchComposer) {
|
|
35986
|
+
if (mountedSearchComposer === "filter")
|
|
35987
|
+
filterSearchComposer.unmount();
|
|
35988
|
+
else if (mountedSearchComposer === "session")
|
|
35989
|
+
sessionSearchComposer.unmount();
|
|
35990
|
+
mountedSearchComposer = activeSearchComposer;
|
|
35991
|
+
if (activeSearchComposer === "filter") {
|
|
35992
|
+
filterSearchComposer.mount();
|
|
35993
|
+
filterSearchComposer.focus();
|
|
35994
|
+
} else if (activeSearchComposer === "session") {
|
|
35995
|
+
sessionSearchComposer.mount();
|
|
35996
|
+
sessionSearchComposer.focus();
|
|
35997
|
+
} else {
|
|
35998
|
+
listRegion.focus();
|
|
35999
|
+
}
|
|
36000
|
+
}
|
|
36001
|
+
if (activeSearchComposer === "filter")
|
|
36002
|
+
filterSearchComposer.render();
|
|
36003
|
+
else if (activeSearchComposer === "session")
|
|
36004
|
+
sessionSearchComposer.render();
|
|
36005
|
+
separator.content = activeSearchComposer ? "" : renderComposerDivider({ width: contentWidth });
|
|
36006
|
+
footer.content = filterMode ? dashboardFilterFooter({ mode: filterMode, loadingPage, loadingAuthors, pageError, authorError }) : dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen });
|
|
35222
36007
|
renderer.requestRender();
|
|
35223
36008
|
}, finish = function(value) {
|
|
35224
36009
|
if (settled)
|
|
@@ -35233,9 +36018,40 @@ async function createDashboardTui({
|
|
|
35233
36018
|
stopPageRefresh();
|
|
35234
36019
|
rejectDraft(error93);
|
|
35235
36020
|
}, moveSelectionTo = function(index) {
|
|
35236
|
-
|
|
36021
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36022
|
+
if (sessions.length === 0)
|
|
36023
|
+
return;
|
|
36024
|
+
selectedIndex = Math.max(0, Math.min(index, sessions.length - 1));
|
|
36025
|
+
render();
|
|
36026
|
+
}, applyFilterSearch = function() {
|
|
36027
|
+
if (!filterMode?.searching)
|
|
36028
|
+
return;
|
|
36029
|
+
filterMode = { ...filterMode, searching: false, selectedIndex: 0 };
|
|
36030
|
+
filterSearchComposer.textarea.setText("");
|
|
36031
|
+
render();
|
|
36032
|
+
}, cancelFilterSearch = function() {
|
|
36033
|
+
if (!filterMode?.searching)
|
|
36034
|
+
return;
|
|
36035
|
+
filterMode = { ...filterMode, searching: false, query: "", selectedIndex: 0 };
|
|
36036
|
+
filterSearchComposer.textarea.setText("");
|
|
36037
|
+
render();
|
|
36038
|
+
}, openSessionSearch = function() {
|
|
36039
|
+
sessionSearchOpen = true;
|
|
36040
|
+
sessionSearchComposer.textarea.setText(sessionSearchQuery);
|
|
36041
|
+
render();
|
|
36042
|
+
}, applySessionSearch = function() {
|
|
36043
|
+
if (!sessionSearchOpen)
|
|
36044
|
+
return;
|
|
36045
|
+
sessionSearchOpen = false;
|
|
36046
|
+
selectedIndex = 0;
|
|
36047
|
+
render();
|
|
36048
|
+
}, cancelSessionSearch = function() {
|
|
36049
|
+
if (!sessionSearchOpen)
|
|
35237
36050
|
return;
|
|
35238
|
-
|
|
36051
|
+
sessionSearchOpen = false;
|
|
36052
|
+
sessionSearchQuery = "";
|
|
36053
|
+
selectedIndex = 0;
|
|
36054
|
+
sessionSearchComposer.textarea.setText("");
|
|
35239
36055
|
render();
|
|
35240
36056
|
}, handleSessionKey = function(key) {
|
|
35241
36057
|
if (logoutConfirmationOpen) {
|
|
@@ -35248,6 +36064,84 @@ async function createDashboardTui({
|
|
|
35248
36064
|
}
|
|
35249
36065
|
return;
|
|
35250
36066
|
}
|
|
36067
|
+
if (sessionSearchOpen) {
|
|
36068
|
+
if (key.name === "escape") {
|
|
36069
|
+
key.preventDefault();
|
|
36070
|
+
cancelSessionSearch();
|
|
36071
|
+
}
|
|
36072
|
+
return;
|
|
36073
|
+
}
|
|
36074
|
+
if (filterMode) {
|
|
36075
|
+
const options = visibleDashboardFilterOptions({ mode: filterMode, authors });
|
|
36076
|
+
if (key.name === "escape") {
|
|
36077
|
+
key.preventDefault();
|
|
36078
|
+
if (filterMode.searching) {
|
|
36079
|
+
cancelFilterSearch();
|
|
36080
|
+
return;
|
|
36081
|
+
}
|
|
36082
|
+
filterMode = undefined;
|
|
36083
|
+
pageError = undefined;
|
|
36084
|
+
authorError = undefined;
|
|
36085
|
+
render();
|
|
36086
|
+
return;
|
|
36087
|
+
}
|
|
36088
|
+
if (filterMode.searching)
|
|
36089
|
+
return;
|
|
36090
|
+
if (loadingAuthors) {
|
|
36091
|
+
key.preventDefault();
|
|
36092
|
+
return;
|
|
36093
|
+
}
|
|
36094
|
+
const filterListFocused = renderer.currentFocusedRenderable === listRegion;
|
|
36095
|
+
if (filterListFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
36096
|
+
key.preventDefault();
|
|
36097
|
+
awaitingVimGoToTop = false;
|
|
36098
|
+
if (options.length > 0)
|
|
36099
|
+
filterMode = { ...filterMode, selectedIndex: options.length - 1 };
|
|
36100
|
+
render();
|
|
36101
|
+
return;
|
|
36102
|
+
}
|
|
36103
|
+
if (filterListFocused && key.name === "g") {
|
|
36104
|
+
key.preventDefault();
|
|
36105
|
+
if (awaitingVimGoToTop && options.length > 0)
|
|
36106
|
+
filterMode = { ...filterMode, selectedIndex: 0 };
|
|
36107
|
+
awaitingVimGoToTop = !awaitingVimGoToTop;
|
|
36108
|
+
render();
|
|
36109
|
+
return;
|
|
36110
|
+
}
|
|
36111
|
+
awaitingVimGoToTop = false;
|
|
36112
|
+
if (key.name === "down" || key.name === "up" || key.name === "j" || key.name === "k") {
|
|
36113
|
+
key.preventDefault();
|
|
36114
|
+
if (options.length > 0) {
|
|
36115
|
+
const backwards = key.name === "up" || key.name === "k";
|
|
36116
|
+
filterMode = { ...filterMode, selectedIndex: (filterMode.selectedIndex + (backwards ? -1 : 1) + options.length) % options.length };
|
|
36117
|
+
}
|
|
36118
|
+
render();
|
|
36119
|
+
return;
|
|
36120
|
+
}
|
|
36121
|
+
if (key.name === "space" || key.name === "left" || key.name === "right" || key.name === "h" || key.name === "l") {
|
|
36122
|
+
key.preventDefault();
|
|
36123
|
+
const option = options[filterMode.selectedIndex];
|
|
36124
|
+
if (!option)
|
|
36125
|
+
return;
|
|
36126
|
+
const selected = key.name === "space" ? !isDashboardFilterOptionSelected({ mode: filterMode, option }) : key.name === "right" || key.name === "l";
|
|
36127
|
+
filterMode = updateDashboardFilterSelection({ mode: filterMode, option, selected, authors });
|
|
36128
|
+
render();
|
|
36129
|
+
return;
|
|
36130
|
+
}
|
|
36131
|
+
if (key.name === "s") {
|
|
36132
|
+
key.preventDefault();
|
|
36133
|
+
filterMode = { ...filterMode, query: "", searching: true, selectedIndex: 0 };
|
|
36134
|
+
filterSearchComposer.textarea.setText("");
|
|
36135
|
+
render();
|
|
36136
|
+
return;
|
|
36137
|
+
}
|
|
36138
|
+
if (key.name === "return" || key.name === "enter") {
|
|
36139
|
+
key.preventDefault();
|
|
36140
|
+
const nextFilters = { statuses: [...filterMode.statuses], creators: [...filterMode.creators] };
|
|
36141
|
+
applyFilters(nextFilters);
|
|
36142
|
+
}
|
|
36143
|
+
return;
|
|
36144
|
+
}
|
|
35251
36145
|
const listFocused = renderer.currentFocusedRenderable === listRegion;
|
|
35252
36146
|
if (key.name === "down") {
|
|
35253
36147
|
key.preventDefault();
|
|
@@ -35276,7 +36170,7 @@ async function createDashboardTui({
|
|
|
35276
36170
|
if (listFocused && (key.name === "G" || key.name === "g" && key.shift)) {
|
|
35277
36171
|
key.preventDefault();
|
|
35278
36172
|
awaitingVimGoToTop = false;
|
|
35279
|
-
moveSelectionTo(page.sessions.length - 1);
|
|
36173
|
+
moveSelectionTo(visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery }).length - 1);
|
|
35280
36174
|
return;
|
|
35281
36175
|
}
|
|
35282
36176
|
if (listFocused && key.name === "g") {
|
|
@@ -35301,7 +36195,7 @@ async function createDashboardTui({
|
|
|
35301
36195
|
awaitingVimGoToTop = false;
|
|
35302
36196
|
if (key.name === "return" || key.name === "enter") {
|
|
35303
36197
|
key.preventDefault();
|
|
35304
|
-
const session = page.sessions[selectedIndex];
|
|
36198
|
+
const session = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery })[selectedIndex];
|
|
35305
36199
|
if (session)
|
|
35306
36200
|
finish({ kind: "session", sessionId: session.id });
|
|
35307
36201
|
return;
|
|
@@ -35311,6 +36205,23 @@ async function createDashboardTui({
|
|
|
35311
36205
|
finish({ kind: "new" });
|
|
35312
36206
|
return;
|
|
35313
36207
|
}
|
|
36208
|
+
if (key.name === "s") {
|
|
36209
|
+
key.preventDefault();
|
|
36210
|
+
openSessionSearch();
|
|
36211
|
+
return;
|
|
36212
|
+
}
|
|
36213
|
+
if (key.name === "f") {
|
|
36214
|
+
key.preventDefault();
|
|
36215
|
+
authorError = undefined;
|
|
36216
|
+
filterMode = createFilterMode({ kind: "status", filters });
|
|
36217
|
+
render();
|
|
36218
|
+
return;
|
|
36219
|
+
}
|
|
36220
|
+
if (key.name === "a") {
|
|
36221
|
+
key.preventDefault();
|
|
36222
|
+
openAuthorFilter();
|
|
36223
|
+
return;
|
|
36224
|
+
}
|
|
35314
36225
|
if (key.name === "L" || key.shift && key.name === "l") {
|
|
35315
36226
|
key.preventDefault();
|
|
35316
36227
|
logoutConfirmationOpen = true;
|
|
@@ -35327,21 +36238,19 @@ async function createDashboardTui({
|
|
|
35327
36238
|
finish(null);
|
|
35328
36239
|
}
|
|
35329
36240
|
};
|
|
35330
|
-
const root = new
|
|
35331
|
-
const statusBand = new
|
|
35332
|
-
const content = new
|
|
36241
|
+
const root = new BoxRenderable2(renderer, { width: "100%", height: "100%", flexDirection: "column", paddingX: 2, overflow: "hidden" });
|
|
36242
|
+
const statusBand = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36243
|
+
const content = new TextRenderable2(renderer, { content: "" });
|
|
35333
36244
|
const listRegion = new ScrollBoxRenderable(renderer, { flexGrow: 1, flexShrink: 1, minHeight: 0, scrollY: true });
|
|
35334
|
-
const previewBand = new
|
|
35335
|
-
const
|
|
35336
|
-
const
|
|
35337
|
-
|
|
35338
|
-
event.preventDefault();
|
|
35339
|
-
listRegion.focus();
|
|
35340
|
-
};
|
|
36245
|
+
const previewBand = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36246
|
+
const filterSearchRegion = new BoxRenderable2(renderer, { width: "100%", flexShrink: 0 });
|
|
36247
|
+
const separator = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
36248
|
+
const footer = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
35341
36249
|
listRegion.add(content);
|
|
35342
36250
|
root.add(statusBand);
|
|
35343
36251
|
root.add(listRegion);
|
|
35344
36252
|
root.add(previewBand);
|
|
36253
|
+
root.add(filterSearchRegion);
|
|
35345
36254
|
root.add(separator);
|
|
35346
36255
|
root.add(footer);
|
|
35347
36256
|
renderer.root.add(root);
|
|
@@ -35351,8 +36260,22 @@ async function createDashboardTui({
|
|
|
35351
36260
|
let loadingPage = false;
|
|
35352
36261
|
let pageRequestInFlight = false;
|
|
35353
36262
|
let pageError;
|
|
36263
|
+
let authorError;
|
|
36264
|
+
let authors = initialAuthors;
|
|
36265
|
+
let authorsLoaded = loadAuthors === undefined || initialAuthors.length > 0;
|
|
36266
|
+
let loadingAuthors = false;
|
|
35354
36267
|
let awaitingVimGoToTop = false;
|
|
35355
36268
|
let logoutConfirmationOpen = false;
|
|
36269
|
+
let filters = initialFilters;
|
|
36270
|
+
let filterMode;
|
|
36271
|
+
let sessionSearchOpen = false;
|
|
36272
|
+
let sessionSearchQuery = "";
|
|
36273
|
+
root.onMouseDown = (event) => {
|
|
36274
|
+
if (filterMode?.searching || sessionSearchOpen)
|
|
36275
|
+
return;
|
|
36276
|
+
event.preventDefault();
|
|
36277
|
+
listRegion.focus();
|
|
36278
|
+
};
|
|
35356
36279
|
let destroyed = false;
|
|
35357
36280
|
let rendererDestroyPromise;
|
|
35358
36281
|
let settled = false;
|
|
@@ -35362,10 +36285,41 @@ async function createDashboardTui({
|
|
|
35362
36285
|
let rejectDraft = () => {
|
|
35363
36286
|
return;
|
|
35364
36287
|
};
|
|
36288
|
+
let desiredContentScrollY = 0;
|
|
35365
36289
|
const action = new Promise((resolve, reject) => {
|
|
35366
36290
|
resolveAction = resolve;
|
|
35367
36291
|
rejectDraft = reject;
|
|
35368
36292
|
});
|
|
36293
|
+
let mountedSearchComposer;
|
|
36294
|
+
const filterSearchComposer = createComposer({
|
|
36295
|
+
renderer,
|
|
36296
|
+
parent: filterSearchRegion,
|
|
36297
|
+
tag: "Filter",
|
|
36298
|
+
placeholder: "Filter options\u2026",
|
|
36299
|
+
screenPaddingX: 2,
|
|
36300
|
+
onSubmit: () => applyFilterSearch(),
|
|
36301
|
+
onContentChange: () => {
|
|
36302
|
+
if (!filterMode?.searching)
|
|
36303
|
+
return;
|
|
36304
|
+
filterMode = { ...filterMode, query: filterSearchComposer.textarea.plainText, selectedIndex: 0 };
|
|
36305
|
+
render();
|
|
36306
|
+
}
|
|
36307
|
+
});
|
|
36308
|
+
const sessionSearchComposer = createComposer({
|
|
36309
|
+
renderer,
|
|
36310
|
+
parent: filterSearchRegion,
|
|
36311
|
+
tag: "Search",
|
|
36312
|
+
placeholder: "Search by ID, title, or number\u2026",
|
|
36313
|
+
screenPaddingX: 2,
|
|
36314
|
+
onSubmit: () => applySessionSearch(),
|
|
36315
|
+
onContentChange: () => {
|
|
36316
|
+
if (!sessionSearchOpen)
|
|
36317
|
+
return;
|
|
36318
|
+
sessionSearchQuery = sessionSearchComposer.textarea.plainText;
|
|
36319
|
+
selectedIndex = 0;
|
|
36320
|
+
render();
|
|
36321
|
+
}
|
|
36322
|
+
});
|
|
35369
36323
|
async function loadAdjacentPage(direction) {
|
|
35370
36324
|
if (pageRequestInFlight)
|
|
35371
36325
|
return;
|
|
@@ -35382,7 +36336,8 @@ async function createDashboardTui({
|
|
|
35382
36336
|
return;
|
|
35383
36337
|
page = loadedPage;
|
|
35384
36338
|
pageIndex = Math.max(1, pageIndex + (direction === "next" ? 1 : -1));
|
|
35385
|
-
|
|
36339
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36340
|
+
selectedIndex = direction === "next" ? 0 : Math.max(0, sessions.length - 1);
|
|
35386
36341
|
} catch (error93) {
|
|
35387
36342
|
if (!destroyed && !settled)
|
|
35388
36343
|
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
@@ -35401,7 +36356,7 @@ async function createDashboardTui({
|
|
|
35401
36356
|
const refreshedPage = await loadPage({ target: "current", signal: loadPageAbortController.signal });
|
|
35402
36357
|
if (destroyed || settled)
|
|
35403
36358
|
return;
|
|
35404
|
-
const selectedSessionId = page.sessions[selectedIndex]?.id;
|
|
36359
|
+
const selectedSessionId = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery })[selectedIndex]?.id;
|
|
35405
36360
|
page = refreshedPage;
|
|
35406
36361
|
const refreshedSelectedIndex = selectedSessionId ? page.sessions.findIndex((session) => session.id === selectedSessionId) : -1;
|
|
35407
36362
|
selectedIndex = refreshedSelectedIndex >= 0 ? refreshedSelectedIndex : Math.max(0, Math.min(selectedIndex, page.sessions.length - 1));
|
|
@@ -35416,10 +36371,53 @@ async function createDashboardTui({
|
|
|
35416
36371
|
pageRequestInFlight = false;
|
|
35417
36372
|
}
|
|
35418
36373
|
}
|
|
36374
|
+
async function applyFilters(nextFilters) {
|
|
36375
|
+
if (loadingPage)
|
|
36376
|
+
return;
|
|
36377
|
+
loadingPage = true;
|
|
36378
|
+
pageError = undefined;
|
|
36379
|
+
authorError = undefined;
|
|
36380
|
+
render();
|
|
36381
|
+
try {
|
|
36382
|
+
page = await loadPage({ filters: nextFilters, signal: loadPageAbortController.signal });
|
|
36383
|
+
filters = nextFilters;
|
|
36384
|
+
pageIndex = 1;
|
|
36385
|
+
selectedIndex = 0;
|
|
36386
|
+
filterMode = undefined;
|
|
36387
|
+
} catch (error93) {
|
|
36388
|
+
pageError = error93 instanceof Error ? error93.message : String(error93);
|
|
36389
|
+
} finally {
|
|
36390
|
+
loadingPage = false;
|
|
36391
|
+
render();
|
|
36392
|
+
}
|
|
36393
|
+
}
|
|
36394
|
+
async function openAuthorFilter() {
|
|
36395
|
+
filterMode = createFilterMode({ kind: "creator", filters });
|
|
36396
|
+
authorError = undefined;
|
|
36397
|
+
render();
|
|
36398
|
+
if (authorsLoaded || loadingAuthors || !loadAuthors)
|
|
36399
|
+
return;
|
|
36400
|
+
loadingAuthors = true;
|
|
36401
|
+
render();
|
|
36402
|
+
try {
|
|
36403
|
+
authors = await loadAuthors();
|
|
36404
|
+
authorsLoaded = true;
|
|
36405
|
+
if (filterMode?.kind === "creator") {
|
|
36406
|
+
const firstSelectedIndex = authors.findIndex((author) => filterMode?.creators.some((creator) => creator.id === author.id));
|
|
36407
|
+
filterMode = { ...filterMode, selectedIndex: Math.max(0, firstSelectedIndex) };
|
|
36408
|
+
}
|
|
36409
|
+
} catch (error93) {
|
|
36410
|
+
authorError = error93 instanceof Error ? error93.message : String(error93);
|
|
36411
|
+
} finally {
|
|
36412
|
+
loadingAuthors = false;
|
|
36413
|
+
render();
|
|
36414
|
+
}
|
|
36415
|
+
}
|
|
35419
36416
|
async function moveSelection(direction) {
|
|
35420
|
-
|
|
36417
|
+
const sessions = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery });
|
|
36418
|
+
if (loadingPage || sessions.length === 0)
|
|
35421
36419
|
return;
|
|
35422
|
-
const isBoundary = direction === "next" ? selectedIndex ===
|
|
36420
|
+
const isBoundary = direction === "next" ? selectedIndex === sessions.length - 1 : selectedIndex === 0;
|
|
35423
36421
|
if (!isBoundary) {
|
|
35424
36422
|
selectedIndex += direction === "next" ? 1 : -1;
|
|
35425
36423
|
render();
|
|
@@ -35434,7 +36432,13 @@ async function createDashboardTui({
|
|
|
35434
36432
|
}
|
|
35435
36433
|
return false;
|
|
35436
36434
|
};
|
|
35437
|
-
|
|
36435
|
+
filterSearchComposer.textarea.onKeyDown = handleSessionKey;
|
|
36436
|
+
sessionSearchComposer.textarea.onKeyDown = handleSessionKey;
|
|
36437
|
+
keyHandler = (key) => {
|
|
36438
|
+
if (mountedSearchComposer && key.name !== "escape" && !(key.ctrl && key.name === "c"))
|
|
36439
|
+
return;
|
|
36440
|
+
handleSessionKey(key);
|
|
36441
|
+
};
|
|
35438
36442
|
renderer.addInputHandler(inputHandler);
|
|
35439
36443
|
renderer.keyInput.on("keypress", keyHandler);
|
|
35440
36444
|
renderer.on(CliRenderEvents2.RENDER_ERROR, (event) => fail(event.error));
|
|
@@ -35461,6 +36465,10 @@ async function createDashboardTui({
|
|
|
35461
36465
|
if (resizeHandler)
|
|
35462
36466
|
renderer.off(CliRenderEvents2.RESIZE, resizeHandler);
|
|
35463
36467
|
stopPageRefresh();
|
|
36468
|
+
if (contentScrollHandler)
|
|
36469
|
+
renderer.off(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
36470
|
+
filterSearchComposer.destroy();
|
|
36471
|
+
sessionSearchComposer.destroy();
|
|
35464
36472
|
rendererDestroyed2 = true;
|
|
35465
36473
|
renderer.destroy();
|
|
35466
36474
|
},
|
|
@@ -35478,6 +36486,8 @@ async function createDashboardTui({
|
|
|
35478
36486
|
if (refreshTimer !== undefined)
|
|
35479
36487
|
clearInterval(refreshTimer);
|
|
35480
36488
|
loadPageAbortController.abort();
|
|
36489
|
+
if (contentScrollHandler)
|
|
36490
|
+
renderer.off(CliRenderEvents2.FRAME, contentScrollHandler);
|
|
35481
36491
|
if (!rendererDestroyed2) {
|
|
35482
36492
|
rendererDestroyed2 = true;
|
|
35483
36493
|
renderer.destroy();
|
|
@@ -35515,6 +36525,63 @@ function rowsBelowVisibleWindow({ sessionCount, selectedIndex, height }) {
|
|
|
35515
36525
|
const { firstIndex, rowCount } = visibleListWindow({ sessionCount, selectedIndex, height });
|
|
35516
36526
|
return Math.max(0, sessionCount - (firstIndex + rowCount));
|
|
35517
36527
|
}
|
|
36528
|
+
function createFilterMode({ kind, filters }) {
|
|
36529
|
+
return {
|
|
36530
|
+
kind,
|
|
36531
|
+
selectedIndex: 0,
|
|
36532
|
+
statuses: [...filters.statuses],
|
|
36533
|
+
creators: [...filters.creators],
|
|
36534
|
+
query: "",
|
|
36535
|
+
searching: false
|
|
36536
|
+
};
|
|
36537
|
+
}
|
|
36538
|
+
function formatFilterMenu({ mode, authors, width }) {
|
|
36539
|
+
const options = visibleDashboardFilterOptions({ mode, authors });
|
|
36540
|
+
const selectedCount = mode.kind === "status" ? mode.statuses.length : mode.creators.length;
|
|
36541
|
+
const heading = `${mode.kind === "status" ? "Filter by status" : "Filter by author"} \xB7 ${selectedCount} selected \xB7 empty shows all`;
|
|
36542
|
+
const rows = options.map((option, index) => {
|
|
36543
|
+
const active = isDashboardFilterOptionSelected({ mode, option });
|
|
36544
|
+
const label = `${active ? "[x]" : "[ ]"} ${truncate(option.label, Math.max(1, width - 4))}`;
|
|
36545
|
+
const chunk = index === mode.selectedIndex ? bg(PALETTE.selectionBg)(fg3(PALETTE.bodyText)(label.padEnd(width))) : active ? bold2(fg3(PALETTE.humanAccent)(label)) : fg3(PALETTE.dimText)(label);
|
|
36546
|
+
return [chunk];
|
|
36547
|
+
});
|
|
36548
|
+
return joinStyled([
|
|
36549
|
+
[bold2(fg3(PALETTE.bodyText)(truncate(heading, width)))],
|
|
36550
|
+
...rows.length > 0 ? rows : [[fg3(PALETTE.dimText)("No matching options.")]]
|
|
36551
|
+
], `
|
|
36552
|
+
`);
|
|
36553
|
+
}
|
|
36554
|
+
function dashboardFilterOptions({ kind, authors }) {
|
|
36555
|
+
return kind === "status" ? lifecycleStatusOptions.map((option) => ({ kind: "status", id: option.value, label: option.label })) : authors.map((author) => ({ kind: "creator", id: author.id, label: author.name }));
|
|
36556
|
+
}
|
|
36557
|
+
function visibleDashboardFilterOptions({ mode, authors }) {
|
|
36558
|
+
return dashboardFilterOptions({ kind: mode.kind, authors }).filter((option) => fuzzyMatches(option.label, mode.query));
|
|
36559
|
+
}
|
|
36560
|
+
function isDashboardFilterOptionSelected({ mode, option }) {
|
|
36561
|
+
return option.kind === "status" ? mode.statuses.includes(option.id) : mode.creators.some((creator) => creator.id === option.id);
|
|
36562
|
+
}
|
|
36563
|
+
function updateDashboardFilterSelection({ mode, option, selected, authors }) {
|
|
36564
|
+
if (option.kind === "status") {
|
|
36565
|
+
const selectedStatuses = new Set(mode.statuses);
|
|
36566
|
+
if (selected)
|
|
36567
|
+
selectedStatuses.add(option.id);
|
|
36568
|
+
else
|
|
36569
|
+
selectedStatuses.delete(option.id);
|
|
36570
|
+
return {
|
|
36571
|
+
...mode,
|
|
36572
|
+
statuses: lifecycleStatusOptions.map((entry) => entry.value).filter((status) => selectedStatuses.has(status))
|
|
36573
|
+
};
|
|
36574
|
+
}
|
|
36575
|
+
const selectedCreatorIds = new Set(mode.creators.map((creator) => creator.id));
|
|
36576
|
+
if (selected)
|
|
36577
|
+
selectedCreatorIds.add(option.id);
|
|
36578
|
+
else
|
|
36579
|
+
selectedCreatorIds.delete(option.id);
|
|
36580
|
+
return {
|
|
36581
|
+
...mode,
|
|
36582
|
+
creators: authors.filter((author) => selectedCreatorIds.has(author.id))
|
|
36583
|
+
};
|
|
36584
|
+
}
|
|
35518
36585
|
function formatSessionList({ sessions, selectedIndex, width, height }) {
|
|
35519
36586
|
const { firstIndex, rowCount } = visibleListWindow({ sessionCount: sessions.length, selectedIndex, height });
|
|
35520
36587
|
const visibleSessions = sessions.slice(firstIndex, firstIndex + rowCount);
|
|
@@ -35601,18 +36668,34 @@ function formatUpdatedAt(value) {
|
|
|
35601
36668
|
return `${Math.floor(seconds / (60 * 60))}h ago`;
|
|
35602
36669
|
return new Date(milliseconds).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
35603
36670
|
}
|
|
35604
|
-
function
|
|
35605
|
-
|
|
36671
|
+
function matchesSessionSearch({ session, query }) {
|
|
36672
|
+
return fuzzyMatches(session.id, query) || fuzzyMatches(session.title ?? "", query) || fuzzyMatches(String(session.sessionNumber), query);
|
|
36673
|
+
}
|
|
36674
|
+
function visibleDashboardSessions({ sessions, query }) {
|
|
36675
|
+
return sessions.filter((session) => matchesSessionSearch({ session, query }));
|
|
36676
|
+
}
|
|
36677
|
+
function dashboardStatusBand({ page, pageIndex, filters, sessionSearchQuery }) {
|
|
36678
|
+
const openCount = visibleDashboardSessions({ sessions: page.sessions, query: sessionSearchQuery }).filter((session) => session.status === "open").length;
|
|
35606
36679
|
const paging = [`page ${pageIndex}`];
|
|
35607
36680
|
if (page.canGoPrevious)
|
|
35608
36681
|
paging.push("\u2190 prev");
|
|
35609
36682
|
if (page.hasMore)
|
|
35610
36683
|
paging.push("more \u2192");
|
|
35611
|
-
|
|
36684
|
+
const activeFilters = [
|
|
36685
|
+
filters.statuses.length > 0 ? `status: ${filters.statuses.map(sessionStatusLabel).join(", ")}` : undefined,
|
|
36686
|
+
filters.creators.length > 0 ? `author: ${filters.creators.map((creator) => creator.name).join(", ")}` : undefined,
|
|
36687
|
+
sessionSearchQuery ? `search: ${sessionSearchQuery}` : undefined
|
|
36688
|
+
].filter((value) => value !== undefined);
|
|
36689
|
+
return `remy \xB7 sessions \xB7 ${openCount} open${activeFilters.length > 0 ? ` \xB7 ${activeFilters.join(" \xB7 ")}` : ""} \xB7 ${paging.join(" \xB7 ")}`;
|
|
35612
36690
|
}
|
|
35613
|
-
function
|
|
36691
|
+
function hasActiveFilters(filters) {
|
|
36692
|
+
return filters.statuses.length > 0 || filters.creators.length > 0;
|
|
36693
|
+
}
|
|
36694
|
+
function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmationOpen, sessionSearchOpen }) {
|
|
35614
36695
|
if (logoutConfirmationOpen)
|
|
35615
36696
|
return "Log out of Remy? y confirms \xB7 Esc cancels";
|
|
36697
|
+
if (sessionSearchOpen)
|
|
36698
|
+
return "type to search \xB7 \u23CE apply search \xB7 Esc clear search";
|
|
35616
36699
|
const parts = [];
|
|
35617
36700
|
if (loadingPage)
|
|
35618
36701
|
parts.push("Loading sessions\u2026");
|
|
@@ -35620,9 +36703,22 @@ function dashboardFooter({ loadingPage, pageError, rowsBelow, logoutConfirmation
|
|
|
35620
36703
|
parts.push(`Could not load sessions: ${pageError}`);
|
|
35621
36704
|
if (rowsBelow > 0)
|
|
35622
36705
|
parts.push(`\u2193 ${rowsBelow} more below`);
|
|
35623
|
-
parts.push("\u2191\u2193 move \xB7 \u23CE open \xB7 n new session \xB7 \u21E7l log out \xB7 q quit");
|
|
36706
|
+
parts.push("\u2191\u2193 move \xB7 \u23CE open \xB7 s search \xB7 f status \xB7 a author \xB7 n new session \xB7 \u21E7l log out \xB7 q quit");
|
|
35624
36707
|
return parts.join(" \xB7 ");
|
|
35625
36708
|
}
|
|
36709
|
+
function dashboardFilterFooter({ mode, loadingPage, loadingAuthors, pageError, authorError }) {
|
|
36710
|
+
if (loadingAuthors)
|
|
36711
|
+
return "Loading authors\u2026 \xB7 Esc cancel";
|
|
36712
|
+
if (loadingPage)
|
|
36713
|
+
return "Applying filter\u2026";
|
|
36714
|
+
if (mode.searching)
|
|
36715
|
+
return "type to filter \xB7 \u23CE apply search \xB7 Esc cancel search";
|
|
36716
|
+
return [
|
|
36717
|
+
authorError ? `Could not load authors: ${authorError}` : undefined,
|
|
36718
|
+
pageError ? `Could not apply filter: ${pageError}` : undefined,
|
|
36719
|
+
"\u2191\u2193/jk move \xB7 space toggle \xB7 \u2190\u2192/hl mark \xB7 s search \xB7 \u23CE apply \xB7 Esc cancel"
|
|
36720
|
+
].filter((value) => value !== undefined).join(" \xB7 ");
|
|
36721
|
+
}
|
|
35626
36722
|
function truncate(value, width) {
|
|
35627
36723
|
if (value.length <= width)
|
|
35628
36724
|
return value;
|
|
@@ -35633,87 +36729,33 @@ async function createDefaultRenderer() {
|
|
|
35633
36729
|
}
|
|
35634
36730
|
|
|
35635
36731
|
// src/tui/new-session-wizard.ts
|
|
35636
|
-
import { bg as bg3, BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as CliRenderEvents3, dim as
|
|
35637
|
-
|
|
35638
|
-
// src/tui/composer.ts
|
|
35639
|
-
import { BoxRenderable as BoxRenderable2, TextRenderable as TextRenderable2, TextareaRenderable } from "@opentui/core";
|
|
36732
|
+
import { bg as bg3, BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as CliRenderEvents3, dim as dim4, fg as fg6, ScrollBoxRenderable as ScrollBoxRenderable2, StyledText as StyledText5, stringToStyledText as stringToStyledText4, TextRenderable as TextRenderable3 } from "@opentui/core";
|
|
35640
36733
|
|
|
35641
|
-
// src/tui/
|
|
35642
|
-
|
|
35643
|
-
function
|
|
35644
|
-
|
|
36734
|
+
// src/tui/attachment-presentation.ts
|
|
36735
|
+
import { dim as dim3, fg as fg4, StyledText as StyledText3 } from "@opentui/core";
|
|
36736
|
+
function attachmentUploadMessage(filenames) {
|
|
36737
|
+
return filenames.length === 1 ? `Attaching ${filenames[0]}\u2026` : `Attaching ${filenames.length} files\u2026`;
|
|
36738
|
+
}
|
|
36739
|
+
function renderAttachmentFeedback(feedback) {
|
|
36740
|
+
const presentation = feedback.kind === "progress" ? { glyph: "\u28FF", color: PALETTE.progress } : feedback.kind === "success" ? { glyph: "\u2713", color: PALETTE.statusCompleted } : { glyph: "\u2717", color: PALETTE.failure };
|
|
36741
|
+
return new StyledText3([
|
|
36742
|
+
fg4(presentation.color)(`${presentation.glyph} `),
|
|
36743
|
+
fg4(feedback.kind === "failure" ? PALETTE.failure : PALETTE.bodyText)(feedback.message)
|
|
36744
|
+
]);
|
|
35645
36745
|
}
|
|
35646
|
-
|
|
35647
|
-
|
|
35648
|
-
|
|
35649
|
-
|
|
35650
|
-
|
|
35651
|
-
|
|
35652
|
-
|
|
35653
|
-
|
|
35654
|
-
|
|
35655
|
-
onSubmit,
|
|
35656
|
-
onContentChange
|
|
35657
|
-
}) {
|
|
35658
|
-
const root = new BoxRenderable2(renderer, { width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
35659
|
-
const dividerTop = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
35660
|
-
const textarea = new TextareaRenderable(renderer, {
|
|
35661
|
-
width: "100%",
|
|
35662
|
-
height: 1,
|
|
35663
|
-
flexShrink: 0,
|
|
35664
|
-
wrapMode: "word",
|
|
35665
|
-
placeholder,
|
|
35666
|
-
keyBindings: [
|
|
35667
|
-
{ name: "return", action: "submit" },
|
|
35668
|
-
{ name: "return", shift: true, action: "newline" },
|
|
35669
|
-
{ name: "j", ctrl: true, action: "newline" }
|
|
35670
|
-
],
|
|
35671
|
-
onSubmit
|
|
35672
|
-
});
|
|
35673
|
-
const dividerBottom = new TextRenderable2(renderer, { content: "", flexShrink: 0 });
|
|
35674
|
-
let mounted = false;
|
|
35675
|
-
root.add(dividerTop);
|
|
35676
|
-
root.add(textarea);
|
|
35677
|
-
root.add(dividerBottom);
|
|
35678
|
-
textarea.onContentChange = () => {
|
|
35679
|
-
resizeComposer(textarea);
|
|
35680
|
-
onContentChange?.();
|
|
35681
|
-
};
|
|
35682
|
-
function render() {
|
|
35683
|
-
dividerTop.content = renderComposerDivider({ width: renderer.width - 2, tag, leadLabel: topDividerLabel?.() });
|
|
35684
|
-
dividerBottom.content = renderComposerDivider({ width: renderer.width - 2 });
|
|
35685
|
-
}
|
|
35686
|
-
return {
|
|
35687
|
-
textarea,
|
|
35688
|
-
mount() {
|
|
35689
|
-
if (mounted)
|
|
35690
|
-
return;
|
|
35691
|
-
mounted = true;
|
|
35692
|
-
parent.add(root);
|
|
35693
|
-
resizeComposer(textarea);
|
|
35694
|
-
render();
|
|
35695
|
-
},
|
|
35696
|
-
unmount() {
|
|
35697
|
-
if (!mounted)
|
|
35698
|
-
return;
|
|
35699
|
-
mounted = false;
|
|
35700
|
-
parent.remove(root);
|
|
35701
|
-
},
|
|
35702
|
-
render,
|
|
35703
|
-
focus() {
|
|
35704
|
-
textarea.focus();
|
|
35705
|
-
},
|
|
35706
|
-
destroy() {
|
|
35707
|
-
if (mounted)
|
|
35708
|
-
parent.remove(root);
|
|
35709
|
-
mounted = false;
|
|
35710
|
-
root.destroyRecursively();
|
|
35711
|
-
}
|
|
35712
|
-
};
|
|
36746
|
+
function renderAttachmentSummary(attachments) {
|
|
36747
|
+
if (attachments.length === 0)
|
|
36748
|
+
return new StyledText3([]);
|
|
36749
|
+
const label = attachments.length === 1 ? "Attachment" : `${attachments.length} attachments`;
|
|
36750
|
+
return new StyledText3([
|
|
36751
|
+
dim3(fg4(PALETTE.dimText)("\u21B3 ")),
|
|
36752
|
+
fg4(PALETTE.tool)(label),
|
|
36753
|
+
dim3(fg4(PALETTE.dimText)(` \xB7 ${attachments.map((attachment) => attachment.filename).join(" \xB7 ")}`))
|
|
36754
|
+
]);
|
|
35713
36755
|
}
|
|
35714
36756
|
|
|
35715
36757
|
// src/tui/path-completion-menu.ts
|
|
35716
|
-
import { bg as bg2, fg as
|
|
36758
|
+
import { bg as bg2, fg as fg5, stringToStyledText as stringToStyledText3, StyledText as StyledText4 } from "@opentui/core";
|
|
35717
36759
|
var maximumVisibleCompletions = 8;
|
|
35718
36760
|
function renderPathCompletionMenu({ completions, selectedIndex }) {
|
|
35719
36761
|
if (completions.length === 0)
|
|
@@ -35724,7 +36766,7 @@ function renderPathCompletionMenu({ completions, selectedIndex }) {
|
|
|
35724
36766
|
const completionIndex = firstIndex + index;
|
|
35725
36767
|
const selected = completionIndex === selectedIndex;
|
|
35726
36768
|
const rowStyle = selected ? bg2(PALETTE.selectionBg) : (chunk) => chunk;
|
|
35727
|
-
return new
|
|
36769
|
+
return new StyledText4([rowStyle(fg5(PALETTE.bodyText)(`${selected ? "\u203A " : " "}${completion.display}`))]);
|
|
35728
36770
|
});
|
|
35729
36771
|
const overflow = completions.length > visibleCompletions.length ? stringToStyledText3(`${firstIndex + 1}-${firstIndex + visibleCompletions.length} of ${completions.length}`) : undefined;
|
|
35730
36772
|
return joinStyled([
|
|
@@ -35764,13 +36806,62 @@ async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
|
|
|
35764
36806
|
const normalized = replacementPath.split(sep).join("/");
|
|
35765
36807
|
const suffix = entry.isDirectory() ? "/" : "";
|
|
35766
36808
|
const completed = `${normalized}${suffix}`;
|
|
35767
|
-
return {
|
|
36809
|
+
return {
|
|
36810
|
+
display: completed,
|
|
36811
|
+
replacement: `${token.startsWith("@") ? "@" : ""}${completed}`,
|
|
36812
|
+
...entry.isFile() ? { attachmentPath: completedPath } : {}
|
|
36813
|
+
};
|
|
35768
36814
|
});
|
|
35769
36815
|
}
|
|
35770
36816
|
function replaceActivePathToken({ text, replacement }) {
|
|
35771
36817
|
const token = activePathToken(text);
|
|
35772
36818
|
return token === undefined ? text : `${text.slice(0, text.length - token.length)}${replacement}`;
|
|
35773
36819
|
}
|
|
36820
|
+
function selectedAttachmentPaths({ text, selections }) {
|
|
36821
|
+
return selections.filter((selection) => text.slice(selection.start, selection.end) === selection.token && hasPathTokenBoundaries({ text, selection })).map((selection) => selection.attachmentPath);
|
|
36822
|
+
}
|
|
36823
|
+
function createSelectedPathCompletion({ attachmentPath, token, end }) {
|
|
36824
|
+
return { attachmentPath, token, start: end - token.length, end };
|
|
36825
|
+
}
|
|
36826
|
+
function reconcileSelectedPathCompletions({
|
|
36827
|
+
previousText,
|
|
36828
|
+
text,
|
|
36829
|
+
selections
|
|
36830
|
+
}) {
|
|
36831
|
+
if (text === previousText)
|
|
36832
|
+
return selections;
|
|
36833
|
+
let changeStart = 0;
|
|
36834
|
+
while (changeStart < previousText.length && changeStart < text.length && previousText[changeStart] === text[changeStart])
|
|
36835
|
+
changeStart += 1;
|
|
36836
|
+
let previousChangeEnd = previousText.length;
|
|
36837
|
+
let nextChangeEnd = text.length;
|
|
36838
|
+
while (previousChangeEnd > changeStart && nextChangeEnd > changeStart && previousText[previousChangeEnd - 1] === text[nextChangeEnd - 1]) {
|
|
36839
|
+
previousChangeEnd -= 1;
|
|
36840
|
+
nextChangeEnd -= 1;
|
|
36841
|
+
}
|
|
36842
|
+
const offsetChange = nextChangeEnd - previousChangeEnd;
|
|
36843
|
+
return selections.flatMap((selection) => {
|
|
36844
|
+
if (selection.end <= changeStart)
|
|
36845
|
+
return [selection];
|
|
36846
|
+
if (selection.start >= previousChangeEnd) {
|
|
36847
|
+
return [{
|
|
36848
|
+
...selection,
|
|
36849
|
+
start: selection.start + offsetChange,
|
|
36850
|
+
end: selection.end + offsetChange
|
|
36851
|
+
}];
|
|
36852
|
+
}
|
|
36853
|
+
return [];
|
|
36854
|
+
}).filter((selection) => hasPathTokenBoundaries({ text, selection }));
|
|
36855
|
+
}
|
|
36856
|
+
function hasPathTokenBoundaries({ text, selection }) {
|
|
36857
|
+
let before = selection.start;
|
|
36858
|
+
while (before > 0 && "([{'\"`".includes(text[before - 1]))
|
|
36859
|
+
before -= 1;
|
|
36860
|
+
let after = selection.end;
|
|
36861
|
+
while (after < text.length && '),.;:!?]}"`'.includes(text[after]))
|
|
36862
|
+
after += 1;
|
|
36863
|
+
return (before === 0 || /\s/.test(text[before - 1])) && (after === text.length || /\s/.test(text[after]));
|
|
36864
|
+
}
|
|
35774
36865
|
function activePathToken(text) {
|
|
35775
36866
|
const match = text.match(/(?:^|\s)(\S*)$/);
|
|
35776
36867
|
return match?.[1];
|
|
@@ -35783,6 +36874,7 @@ async function createNewSessionWizard({
|
|
|
35783
36874
|
repositories,
|
|
35784
36875
|
reloadRepositories,
|
|
35785
36876
|
suggestRepositories,
|
|
36877
|
+
suggestBranches,
|
|
35786
36878
|
readClipboardImage,
|
|
35787
36879
|
savePastedImage,
|
|
35788
36880
|
attachPromptPaths,
|
|
@@ -35819,15 +36911,19 @@ async function createNewSessionWizard({
|
|
|
35819
36911
|
else
|
|
35820
36912
|
overrides.set(repositoryId, isSelected);
|
|
35821
36913
|
}, renderRepositoryLoadingSkeleton = function() {
|
|
35822
|
-
return new
|
|
35823
|
-
|
|
36914
|
+
return new StyledText5([
|
|
36915
|
+
dim4(fg6(PALETTE.dimText)(`\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
35824
36916
|
\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
|
|
35825
36917
|
\u283F \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588`))
|
|
35826
36918
|
]);
|
|
35827
36919
|
}, render = function() {
|
|
35828
36920
|
const repositoryListVisible = step === "repositories" || step === "repositorySearch";
|
|
35829
36921
|
const composerVisible = step === "prompt" || step === "repositorySearch" || step === "recompute";
|
|
35830
|
-
const orient = (body) => joinStyled([renderStepIndicator({
|
|
36922
|
+
const orient = (body) => joinStyled([renderStepIndicator({
|
|
36923
|
+
step,
|
|
36924
|
+
hasInstallationChoice: installationIds.length > 1,
|
|
36925
|
+
hasBranchChoice: step === "loadingBranches" || branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")
|
|
36926
|
+
}), body], `
|
|
35831
36927
|
`);
|
|
35832
36928
|
if (composerVisible && !composerMounted) {
|
|
35833
36929
|
composerMounted = true;
|
|
@@ -35845,19 +36941,22 @@ async function createNewSessionWizard({
|
|
|
35845
36941
|
repositoryRows.content = "";
|
|
35846
36942
|
repositoryDivider.content = "";
|
|
35847
36943
|
repositoryFooter.content = "";
|
|
35848
|
-
const attachmentText =
|
|
36944
|
+
const attachmentText = renderAttachmentSummary(attachments);
|
|
36945
|
+
const attachmentStatus = attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined;
|
|
35849
36946
|
if (step === "prompt") {
|
|
35850
36947
|
const parts = [
|
|
35851
|
-
new
|
|
36948
|
+
new StyledText5([stepHeader("New remote Codex session")]),
|
|
35852
36949
|
stringToStyledText4("Describe the work you want Remy to do. Press Enter to continue; Esc back.")
|
|
35853
36950
|
];
|
|
35854
|
-
if (attachmentText)
|
|
36951
|
+
if (attachmentText.chunks.length > 0)
|
|
35855
36952
|
parts.push(attachmentText);
|
|
35856
36953
|
const completionMenu = pathCompletionMenuOpen ? renderPathCompletionMenu({ completions: pathCompletions, selectedIndex: completionIndex }) : undefined;
|
|
35857
36954
|
if (completionMenu)
|
|
35858
36955
|
parts.push(completionMenu);
|
|
35859
36956
|
if (status)
|
|
35860
36957
|
parts.push(stringToStyledText4(status));
|
|
36958
|
+
if (attachmentStatus)
|
|
36959
|
+
parts.push(attachmentStatus);
|
|
35861
36960
|
content.content = orient(joinStyled(parts, `
|
|
35862
36961
|
|
|
35863
36962
|
`));
|
|
@@ -35865,7 +36964,7 @@ async function createNewSessionWizard({
|
|
|
35865
36964
|
const rows = joinStyled(installationIds.map((id, index) => renderSelectableRow({ label: id, isCursor: index === installationIndex })), `
|
|
35866
36965
|
`);
|
|
35867
36966
|
content.content = orient(joinStyled([
|
|
35868
|
-
new
|
|
36967
|
+
new StyledText5([stepHeader("Select GitHub installation")]),
|
|
35869
36968
|
rows,
|
|
35870
36969
|
stringToStyledText4("\u2191\u2193 move \xB7 \u23CE continue \xB7 esc back")
|
|
35871
36970
|
], `
|
|
@@ -35874,11 +36973,11 @@ async function createNewSessionWizard({
|
|
|
35874
36973
|
} else if (step === "loadingRepositories") {
|
|
35875
36974
|
const failed = repositoryLoadState === "failed";
|
|
35876
36975
|
const parts = [
|
|
35877
|
-
new
|
|
36976
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
35878
36977
|
stringToStyledText4(failed ? "Could not load your repositories." : "Loading your repos\u2026")
|
|
35879
36978
|
];
|
|
35880
36979
|
if (failed) {
|
|
35881
|
-
parts.push(new
|
|
36980
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(`(${status})`))]));
|
|
35882
36981
|
parts.push(stringToStyledText4("r retry \xB7 esc back"));
|
|
35883
36982
|
} else {
|
|
35884
36983
|
parts.push(renderRepositoryLoadingSkeleton());
|
|
@@ -35889,12 +36988,41 @@ async function createNewSessionWizard({
|
|
|
35889
36988
|
`));
|
|
35890
36989
|
} else if (step === "loadingSuggestions") {
|
|
35891
36990
|
content.content = orient(joinStyled([
|
|
35892
|
-
new
|
|
35893
|
-
new
|
|
35894
|
-
new
|
|
36991
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
36992
|
+
new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is matching your prompt to repositories\u2026`)]),
|
|
36993
|
+
new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
|
|
35895
36994
|
stringToStyledText4("s select repositories yourself \xB7 esc back")
|
|
35896
36995
|
], `
|
|
35897
36996
|
|
|
36997
|
+
`));
|
|
36998
|
+
} else if (step === "loadingBranches") {
|
|
36999
|
+
content.content = orient(joinStyled([
|
|
37000
|
+
new StyledText5([stepHeader("Choose branches")]),
|
|
37001
|
+
new StyledText5([fg6(PALETTE.progress)(`${suggestionSpinnerFrames[suggestionSpinnerFrame]} Remy is checking for related open pull requests\u2026`)]),
|
|
37002
|
+
new StyledText5([dim4(fg6(PALETTE.dimText)("This can take a few seconds."))]),
|
|
37003
|
+
stringToStyledText4("esc back")
|
|
37004
|
+
], `
|
|
37005
|
+
|
|
37006
|
+
`));
|
|
37007
|
+
} else if (step === "branches") {
|
|
37008
|
+
const existing = branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
|
|
37009
|
+
const parts = [new StyledText5([stepHeader("Choose branches")])];
|
|
37010
|
+
if (existing.length > 0) {
|
|
37011
|
+
parts.push(stringToStyledText4("Remy found related open pull-request work:"));
|
|
37012
|
+
for (const suggestion of existing) {
|
|
37013
|
+
const repository = installationRepositories().find((candidate) => candidate.id === suggestion.repositoryId);
|
|
37014
|
+
parts.push(stringToStyledText4(`${repository?.fullName ?? suggestion.repositoryId} \u2014 ${suggestion.branchName}${suggestion.pullRequest ? ` (PR #${suggestion.pullRequest.number}: ${suggestion.pullRequest.title})` : ""}`));
|
|
37015
|
+
}
|
|
37016
|
+
parts.push(new StyledText5(renderSelectableRow({ label: "Continue from the suggested branch", isCursor: branchChoice === "existing" })));
|
|
37017
|
+
parts.push(new StyledText5(renderSelectableRow({ label: "Create a new session branch", isCursor: branchChoice === "new" })));
|
|
37018
|
+
parts.push(stringToStyledText4("\u2191\u2193 choose \xB7 \u23CE continue \xB7 esc back"));
|
|
37019
|
+
} else {
|
|
37020
|
+
parts.push(stringToStyledText4("Remy recommends creating a new session branch."));
|
|
37021
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(status || "No related open pull request was identified from your request."))]));
|
|
37022
|
+
parts.push(stringToStyledText4("\u23CE continue \xB7 esc back"));
|
|
37023
|
+
}
|
|
37024
|
+
content.content = orient(joinStyled(parts, `
|
|
37025
|
+
|
|
35898
37026
|
`));
|
|
35899
37027
|
} else if (repositoryListVisible) {
|
|
35900
37028
|
const selectionOverrides = searchSelections ?? manualSelections;
|
|
@@ -35909,15 +37037,17 @@ async function createNewSessionWizard({
|
|
|
35909
37037
|
`) : step === "repositorySearch" ? "No matching repositories." : "No repositories available for this installation.";
|
|
35910
37038
|
repositoryDivider.content = renderComposerDivider({ width: renderer.width - 2 });
|
|
35911
37039
|
repositoryFooter.content = step === "repositorySearch" ? "type to filter \xB7 \u23CE apply \xB7 esc cancel search" : "\u2191\u2193 move \xB7 space toggle \xB7 \u2190\u2192 mark \xB7 s search \xB7 r recompute \xB7 \u23CE continue \xB7 esc back";
|
|
35912
|
-
const parts = step === "repositorySearch" ? [new
|
|
35913
|
-
new
|
|
37040
|
+
const parts = step === "repositorySearch" ? [new StyledText5([stepHeader("Search repositories")]), stringToStyledText4("Fuzzy matching repository names.")] : [
|
|
37041
|
+
new StyledText5([stepHeader("Select repositories")]),
|
|
35914
37042
|
stringToStyledText4(`${selected.size} selected \xB7 ${suggestedIds.size} suggested by Remy${suggestionConfidence ? ` (${suggestionConfidence} confidence)` : ""}`),
|
|
35915
37043
|
stringToStyledText4("Suggestions are based on your prompt and recompute instruction.")
|
|
35916
37044
|
];
|
|
35917
|
-
if (attachmentText)
|
|
37045
|
+
if (attachmentText.chunks.length > 0)
|
|
35918
37046
|
parts.push(attachmentText);
|
|
35919
37047
|
if (status && step === "repositories")
|
|
35920
37048
|
parts.push(stringToStyledText4(status));
|
|
37049
|
+
if (attachmentStatus)
|
|
37050
|
+
parts.push(attachmentStatus);
|
|
35921
37051
|
content.content = orient(joinStyled(parts, `
|
|
35922
37052
|
|
|
35923
37053
|
`));
|
|
@@ -35925,11 +37055,11 @@ async function createNewSessionWizard({
|
|
|
35925
37055
|
editor.placeholder = "Filter repositories\u2026";
|
|
35926
37056
|
} else if (step === "recompute") {
|
|
35927
37057
|
const parts = [
|
|
35928
|
-
new
|
|
37058
|
+
new StyledText5([stepHeader("Recompute suggestions")]),
|
|
35929
37059
|
stringToStyledText4("Suggestions use your prompt and instruction. Existing manual toggles stay selected or unselected."),
|
|
35930
37060
|
stringToStyledText4("Enter instruction, then press Enter to continue. Esc back.")
|
|
35931
37061
|
];
|
|
35932
|
-
if (attachmentText)
|
|
37062
|
+
if (attachmentText.chunks.length > 0)
|
|
35933
37063
|
parts.push(attachmentText);
|
|
35934
37064
|
content.content = orient(joinStyled(parts, `
|
|
35935
37065
|
|
|
@@ -35937,26 +37067,31 @@ async function createNewSessionWizard({
|
|
|
35937
37067
|
editor.placeholder = "e.g. worker, migration, dashboard";
|
|
35938
37068
|
} else {
|
|
35939
37069
|
const selected = installationRepositories().filter((repository) => selectedRepositoryIds().has(repository.id));
|
|
35940
|
-
const
|
|
35941
|
-
|
|
37070
|
+
const branchOverrides = branchChoice === "existing" ? branchSuggestions.filter((suggestion) => suggestion.suggestionKind !== "createSessionBranch") : [];
|
|
37071
|
+
const meta5 = new StyledText5([
|
|
37072
|
+
fg6(PALETTE.bodyText)(`Installation: ${selectedInstallationId() ?? "(missing)"}
|
|
35942
37073
|
`),
|
|
35943
|
-
|
|
37074
|
+
fg6(PALETTE.bodyText)(`Repositories: ${selected.map((repository) => repository.fullName).join(", ") || "(none)"}
|
|
35944
37075
|
`),
|
|
35945
|
-
|
|
35946
|
-
|
|
37076
|
+
fg6(PALETTE.bodyText)(`Branches: ${branchOverrides.length > 0 ? branchOverrides.map((override) => override.branchName).join(", ") : "new session branches"}
|
|
37077
|
+
`),
|
|
37078
|
+
fg6(PALETTE.bodyText)(`Model: ${newSessionModelLabel(model)} \xB7 ${newSessionReasoningEffortLabel(reasoningEffort)} reasoning`),
|
|
37079
|
+
dim4(fg6(PALETTE.dimText)(" \u25B8 m model \xB7 \u2190\u2192 reasoning"))
|
|
35947
37080
|
]);
|
|
35948
37081
|
const parts = [
|
|
35949
|
-
new
|
|
35950
|
-
new
|
|
37082
|
+
new StyledText5([stepHeader("Confirm new session")]),
|
|
37083
|
+
new StyledText5([renderRoleLabel({ label: "You", role: "human" })]),
|
|
35951
37084
|
renderMessageBody({ text: prompt, role: "human" }),
|
|
35952
37085
|
meta5
|
|
35953
37086
|
];
|
|
35954
|
-
if (attachmentText)
|
|
37087
|
+
if (attachmentText.chunks.length > 0)
|
|
35955
37088
|
parts.push(attachmentText);
|
|
35956
|
-
parts.push(new
|
|
35957
|
-
parts.push(stringToStyledText4("\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 esc back"));
|
|
37089
|
+
parts.push(new StyledText5([dim4(fg6(PALETTE.dimText)(branchOverrides.length > 0 ? "Remy will continue on the selected existing branch and update its pull request; you land in the session view." : "Remy will clone the selected repositories, work on new session branches, and open a pull request for each repository; you land in the session view."))]));
|
|
37090
|
+
parts.push(stringToStyledText4(branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") ? "\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 b branches \xB7 esc back" : "\u23CE create \xB7 e edit prompt \xB7 r repositories \xB7 esc back"));
|
|
35958
37091
|
if (status)
|
|
35959
37092
|
parts.push(stringToStyledText4(status));
|
|
37093
|
+
if (attachmentStatus)
|
|
37094
|
+
parts.push(attachmentStatus);
|
|
35960
37095
|
content.content = orient(joinStyled(parts, `
|
|
35961
37096
|
|
|
35962
37097
|
`));
|
|
@@ -35981,7 +37116,7 @@ async function createNewSessionWizard({
|
|
|
35981
37116
|
syncSuggestionLoadingIndicator();
|
|
35982
37117
|
renderer.requestRender();
|
|
35983
37118
|
}, syncSuggestionLoadingIndicator = function() {
|
|
35984
|
-
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
37119
|
+
if (destroyed || settled || step !== "loadingSuggestions" && step !== "loadingBranches") {
|
|
35985
37120
|
if (suggestionSpinnerTimer) {
|
|
35986
37121
|
clearInterval(suggestionSpinnerTimer);
|
|
35987
37122
|
suggestionSpinnerTimer = undefined;
|
|
@@ -35992,22 +37127,28 @@ async function createNewSessionWizard({
|
|
|
35992
37127
|
if (suggestionSpinnerTimer)
|
|
35993
37128
|
return;
|
|
35994
37129
|
suggestionSpinnerTimer = setInterval(() => {
|
|
35995
|
-
if (destroyed || settled || step !== "loadingSuggestions") {
|
|
37130
|
+
if (destroyed || settled || step !== "loadingSuggestions" && step !== "loadingBranches") {
|
|
35996
37131
|
syncSuggestionLoadingIndicator();
|
|
35997
37132
|
return;
|
|
35998
37133
|
}
|
|
35999
37134
|
suggestionSpinnerFrame = (suggestionSpinnerFrame + 1) % suggestionSpinnerFrames.length;
|
|
36000
37135
|
render();
|
|
36001
37136
|
}, suggestionSpinnerIntervalMs);
|
|
37137
|
+
}, invalidatePendingConfirmation = function() {
|
|
37138
|
+
pendingConfirmation = undefined;
|
|
37139
|
+
if (attachmentFeedback?.kind === "progress")
|
|
37140
|
+
attachmentFeedback = undefined;
|
|
36002
37141
|
}, finish = function(value) {
|
|
36003
37142
|
if (settled)
|
|
36004
37143
|
return;
|
|
37144
|
+
invalidatePendingConfirmation();
|
|
36005
37145
|
settled = true;
|
|
36006
37146
|
syncSuggestionLoadingIndicator();
|
|
36007
37147
|
resolveDraft(value);
|
|
36008
37148
|
}, fail = function(error93) {
|
|
36009
37149
|
if (settled)
|
|
36010
37150
|
return;
|
|
37151
|
+
invalidatePendingConfirmation();
|
|
36011
37152
|
settled = true;
|
|
36012
37153
|
syncSuggestionLoadingIndicator();
|
|
36013
37154
|
rejectDraft(error93);
|
|
@@ -36078,7 +37219,15 @@ async function createNewSessionWizard({
|
|
|
36078
37219
|
returnToPromptFromRepositoryLoading();
|
|
36079
37220
|
return;
|
|
36080
37221
|
}
|
|
37222
|
+
if (step === "loadingBranches" || step === "branches") {
|
|
37223
|
+
branchRequestGeneration += 1;
|
|
37224
|
+
step = "repositories";
|
|
37225
|
+
render();
|
|
37226
|
+
return;
|
|
37227
|
+
}
|
|
36081
37228
|
if (step === "recompute" || step === "confirmation") {
|
|
37229
|
+
if (step === "confirmation")
|
|
37230
|
+
invalidatePendingConfirmation();
|
|
36082
37231
|
step = "repositories";
|
|
36083
37232
|
editor.setText("");
|
|
36084
37233
|
render();
|
|
@@ -36106,6 +37255,15 @@ async function createNewSessionWizard({
|
|
|
36106
37255
|
const completedText = replaceActivePathToken({ text: editor.plainText, replacement: completion.replacement });
|
|
36107
37256
|
editor.setText(completedText);
|
|
36108
37257
|
editor.cursorOffset = completedText.length;
|
|
37258
|
+
if (completion.attachmentPath) {
|
|
37259
|
+
const selectedCompletion = createSelectedPathCompletion({
|
|
37260
|
+
attachmentPath: completion.attachmentPath,
|
|
37261
|
+
token: completion.replacement,
|
|
37262
|
+
end: completedText.length
|
|
37263
|
+
});
|
|
37264
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
37265
|
+
}
|
|
37266
|
+
previousPromptEditorText = completedText;
|
|
36109
37267
|
}
|
|
36110
37268
|
pathCompletions = [];
|
|
36111
37269
|
pathCompletionMenuOpen = false;
|
|
@@ -36131,12 +37289,14 @@ async function createNewSessionWizard({
|
|
|
36131
37289
|
if (step === "confirmation") {
|
|
36132
37290
|
if (key.name === "m") {
|
|
36133
37291
|
key.preventDefault();
|
|
37292
|
+
invalidatePendingConfirmation();
|
|
36134
37293
|
model = cycle({ values: newSessionModels, value: model, direction: 1 });
|
|
36135
37294
|
render();
|
|
36136
37295
|
return;
|
|
36137
37296
|
}
|
|
36138
37297
|
if (key.name === "left" || key.name === "right") {
|
|
36139
37298
|
key.preventDefault();
|
|
37299
|
+
invalidatePendingConfirmation();
|
|
36140
37300
|
reasoningEffort = clampedStep({
|
|
36141
37301
|
values: newSessionReasoningEfforts,
|
|
36142
37302
|
value: reasoningEffort,
|
|
@@ -36147,6 +37307,7 @@ async function createNewSessionWizard({
|
|
|
36147
37307
|
}
|
|
36148
37308
|
if (key.name === "e") {
|
|
36149
37309
|
key.preventDefault();
|
|
37310
|
+
invalidatePendingConfirmation();
|
|
36150
37311
|
step = "prompt";
|
|
36151
37312
|
editor.setText(prompt);
|
|
36152
37313
|
render();
|
|
@@ -36154,11 +37315,19 @@ async function createNewSessionWizard({
|
|
|
36154
37315
|
}
|
|
36155
37316
|
if (key.name === "r") {
|
|
36156
37317
|
key.preventDefault();
|
|
37318
|
+
invalidatePendingConfirmation();
|
|
36157
37319
|
step = "repositories";
|
|
36158
37320
|
editor.setText("");
|
|
36159
37321
|
render();
|
|
36160
37322
|
return;
|
|
36161
37323
|
}
|
|
37324
|
+
if (key.name === "b" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch")) {
|
|
37325
|
+
key.preventDefault();
|
|
37326
|
+
invalidatePendingConfirmation();
|
|
37327
|
+
step = "branches";
|
|
37328
|
+
render();
|
|
37329
|
+
return;
|
|
37330
|
+
}
|
|
36162
37331
|
}
|
|
36163
37332
|
if (step === "repositorySearch")
|
|
36164
37333
|
return;
|
|
@@ -36179,8 +37348,16 @@ async function createNewSessionWizard({
|
|
|
36179
37348
|
return;
|
|
36180
37349
|
}
|
|
36181
37350
|
}
|
|
37351
|
+
if (step === "branches" && branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch") && (key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right")) {
|
|
37352
|
+
key.preventDefault();
|
|
37353
|
+
branchChoice = branchChoice === "existing" ? "new" : "existing";
|
|
37354
|
+
render();
|
|
37355
|
+
return;
|
|
37356
|
+
}
|
|
36182
37357
|
if (!composerMounted && (key.name === "return" || key.name === "enter")) {
|
|
36183
37358
|
key.preventDefault();
|
|
37359
|
+
if (step === "loadingBranches")
|
|
37360
|
+
return;
|
|
36184
37361
|
submitEditor();
|
|
36185
37362
|
return;
|
|
36186
37363
|
}
|
|
@@ -36269,6 +37446,7 @@ async function createNewSessionWizard({
|
|
|
36269
37446
|
let repositoryLoadState = Array.isArray(repositories) ? "ready" : "loading";
|
|
36270
37447
|
let repositoryRequestGeneration = 0;
|
|
36271
37448
|
let suggestionRequestGeneration = 0;
|
|
37449
|
+
let branchRequestGeneration = 0;
|
|
36272
37450
|
let installationIds = [...new Set(loadedRepositories.map((repository) => repository.githubInstallationId))].sort((left, right) => left.localeCompare(right));
|
|
36273
37451
|
let step = initialDraft ? "confirmation" : "prompt";
|
|
36274
37452
|
let prompt = initialDraft?.prompt ?? "";
|
|
@@ -36280,7 +37458,9 @@ async function createNewSessionWizard({
|
|
|
36280
37458
|
let recomputeInstruction = "";
|
|
36281
37459
|
let suggestionSpinnerFrame = 0;
|
|
36282
37460
|
let attachments = [...initialDraft?.attachments ?? []];
|
|
37461
|
+
let previousPromptEditorText = "";
|
|
36283
37462
|
let pathCompletions = [];
|
|
37463
|
+
let selectedPathCompletions = [];
|
|
36284
37464
|
let completionIndex = 0;
|
|
36285
37465
|
let pathCompletionMenuOpen = false;
|
|
36286
37466
|
let repositoryHeaderHeight;
|
|
@@ -36289,8 +37469,17 @@ async function createNewSessionWizard({
|
|
|
36289
37469
|
let searchSelections;
|
|
36290
37470
|
let suggestedIds = new Set;
|
|
36291
37471
|
let suggestionConfidence;
|
|
37472
|
+
let branchSuggestions = initialDraft?.repositoryBranchOverrides?.map((override) => ({
|
|
37473
|
+
suggestionKind: "continueExistingBranch",
|
|
37474
|
+
repositoryId: override.repositoryId,
|
|
37475
|
+
branchName: override.branchName,
|
|
37476
|
+
pullRequest: null
|
|
37477
|
+
})) ?? [];
|
|
37478
|
+
let branchChoice = initialDraft?.repositoryBranchOverrides?.length ? "existing" : "new";
|
|
36292
37479
|
let status = initialError ?? "";
|
|
37480
|
+
let attachmentFeedback;
|
|
36293
37481
|
let pendingPastedImageWrites = 0;
|
|
37482
|
+
let pendingConfirmation;
|
|
36294
37483
|
let destroyed = false;
|
|
36295
37484
|
let rendererDestroyPromise;
|
|
36296
37485
|
let settled = false;
|
|
@@ -36313,6 +37502,14 @@ async function createNewSessionWizard({
|
|
|
36313
37502
|
submitEditor();
|
|
36314
37503
|
},
|
|
36315
37504
|
onContentChange: () => {
|
|
37505
|
+
if (step === "prompt") {
|
|
37506
|
+
selectedPathCompletions = reconcileSelectedPathCompletions({
|
|
37507
|
+
previousText: previousPromptEditorText,
|
|
37508
|
+
text: editor.plainText,
|
|
37509
|
+
selections: selectedPathCompletions
|
|
37510
|
+
});
|
|
37511
|
+
previousPromptEditorText = editor.plainText;
|
|
37512
|
+
}
|
|
36316
37513
|
pathCompletionMenuOpen = false;
|
|
36317
37514
|
if (step === "repositorySearch") {
|
|
36318
37515
|
repositoryQuery = editor.plainText;
|
|
@@ -36348,6 +37545,35 @@ async function createNewSessionWizard({
|
|
|
36348
37545
|
status = error93 instanceof Error ? error93.message : String(error93);
|
|
36349
37546
|
}
|
|
36350
37547
|
}
|
|
37548
|
+
async function startBranchReview() {
|
|
37549
|
+
const installationId = selectedInstallationId();
|
|
37550
|
+
if (!installationId)
|
|
37551
|
+
return;
|
|
37552
|
+
const repositoryIds = [...selectedRepositoryIds()];
|
|
37553
|
+
const requestGeneration = ++branchRequestGeneration;
|
|
37554
|
+
branchSuggestions = [];
|
|
37555
|
+
branchChoice = "new";
|
|
37556
|
+
step = "loadingBranches";
|
|
37557
|
+
status = "";
|
|
37558
|
+
render();
|
|
37559
|
+
try {
|
|
37560
|
+
const suggestions = await suggestBranches({ installationId, prompt, repositoryIds });
|
|
37561
|
+
if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
|
|
37562
|
+
return;
|
|
37563
|
+
branchSuggestions = suggestions;
|
|
37564
|
+
const hasExistingBranch = branchSuggestions.some((suggestion) => suggestion.suggestionKind !== "createSessionBranch");
|
|
37565
|
+
branchChoice = hasExistingBranch ? "existing" : "new";
|
|
37566
|
+
step = hasExistingBranch ? "branches" : "confirmation";
|
|
37567
|
+
} catch (error93) {
|
|
37568
|
+
if (destroyed || requestGeneration !== branchRequestGeneration || step !== "loadingBranches")
|
|
37569
|
+
return;
|
|
37570
|
+
branchSuggestions = [];
|
|
37571
|
+
branchChoice = "new";
|
|
37572
|
+
status = error93 instanceof Error ? error93.message : String(error93);
|
|
37573
|
+
step = "confirmation";
|
|
37574
|
+
}
|
|
37575
|
+
render();
|
|
37576
|
+
}
|
|
36351
37577
|
async function openPathCompletionMenu() {
|
|
36352
37578
|
if (destroyed)
|
|
36353
37579
|
return;
|
|
@@ -36379,28 +37605,33 @@ async function createNewSessionWizard({
|
|
|
36379
37605
|
}
|
|
36380
37606
|
async function pasteClipboardImage() {
|
|
36381
37607
|
if (!readClipboardImage || !savePastedImage) {
|
|
36382
|
-
|
|
37608
|
+
attachmentFeedback = { kind: "failure", message: "Clipboard image paste is unavailable." };
|
|
36383
37609
|
render();
|
|
36384
37610
|
return;
|
|
36385
37611
|
}
|
|
36386
37612
|
pendingPastedImageWrites += 1;
|
|
36387
|
-
|
|
37613
|
+
attachmentFeedback = { kind: "progress", message: "Saving clipboard image\u2026" };
|
|
36388
37614
|
render();
|
|
36389
37615
|
try {
|
|
36390
37616
|
const image = await readClipboardImage();
|
|
36391
37617
|
const path = await savePastedImage(image);
|
|
36392
37618
|
if (!destroyed) {
|
|
36393
37619
|
editor.insertText(path);
|
|
36394
|
-
|
|
37620
|
+
const selectedCompletion = createSelectedPathCompletion({ attachmentPath: path, token: path, end: editor.cursorOffset });
|
|
37621
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
37622
|
+
previousPromptEditorText = editor.plainText;
|
|
37623
|
+
attachmentFeedback = { kind: "success", message: "Clipboard image ready to attach." };
|
|
36395
37624
|
}
|
|
36396
37625
|
} catch (error93) {
|
|
36397
|
-
|
|
37626
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
36398
37627
|
} finally {
|
|
36399
37628
|
pendingPastedImageWrites -= 1;
|
|
36400
37629
|
}
|
|
36401
37630
|
render();
|
|
36402
37631
|
}
|
|
36403
37632
|
async function confirm() {
|
|
37633
|
+
if (destroyed || settled || step !== "confirmation" || pendingConfirmation)
|
|
37634
|
+
return;
|
|
36404
37635
|
const installationId = selectedInstallationId();
|
|
36405
37636
|
if (!installationId) {
|
|
36406
37637
|
status = "GitHub installation selection is required.";
|
|
@@ -36412,22 +37643,55 @@ async function createNewSessionWizard({
|
|
|
36412
37643
|
fail(new Error("Selected repositories must belong to the chosen GitHub installation."));
|
|
36413
37644
|
return;
|
|
36414
37645
|
}
|
|
37646
|
+
const repositoryBranchOverrides = branchChoice === "existing" ? branchSuggestions.flatMap((suggestion) => suggestion.suggestionKind === "createSessionBranch" ? [] : [{ repositoryId: suggestion.repositoryId, branchName: suggestion.branchName }]) : [];
|
|
37647
|
+
const confirmation = Symbol("confirmation");
|
|
37648
|
+
const confirmedAttachments = [...attachments];
|
|
37649
|
+
const confirmedPrompt = prompt;
|
|
37650
|
+
const confirmedModel = model;
|
|
37651
|
+
const confirmedReasoningEffort = reasoningEffort;
|
|
37652
|
+
const confirmedSelectedPaths = selectedAttachmentPaths({ text: confirmedPrompt, selections: selectedPathCompletions });
|
|
37653
|
+
pendingConfirmation = confirmation;
|
|
37654
|
+
const ownsConfirmation = () => !destroyed && !settled && step === "confirmation" && pendingConfirmation === confirmation;
|
|
36415
37655
|
if (attachPromptPaths) {
|
|
36416
|
-
|
|
36417
|
-
render();
|
|
37656
|
+
attachmentFeedback = undefined;
|
|
36418
37657
|
try {
|
|
36419
37658
|
const promptAttachments = await attachPromptPaths({
|
|
36420
|
-
text:
|
|
36421
|
-
excludedPaths:
|
|
37659
|
+
text: confirmedPrompt,
|
|
37660
|
+
excludedPaths: confirmedAttachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : []),
|
|
37661
|
+
selectedPaths: confirmedSelectedPaths,
|
|
37662
|
+
onUploadStart: ({ filenames }) => {
|
|
37663
|
+
if (!ownsConfirmation())
|
|
37664
|
+
return;
|
|
37665
|
+
attachmentFeedback = { kind: "progress", message: attachmentUploadMessage(filenames) };
|
|
37666
|
+
render();
|
|
37667
|
+
}
|
|
36422
37668
|
});
|
|
36423
|
-
|
|
37669
|
+
if (!ownsConfirmation())
|
|
37670
|
+
return;
|
|
37671
|
+
confirmedAttachments.push(...promptAttachments);
|
|
37672
|
+
attachments = confirmedAttachments;
|
|
37673
|
+
selectedPathCompletions = [];
|
|
37674
|
+
attachmentFeedback = undefined;
|
|
36424
37675
|
} catch (error93) {
|
|
36425
|
-
|
|
37676
|
+
if (!ownsConfirmation())
|
|
37677
|
+
return;
|
|
37678
|
+
pendingConfirmation = undefined;
|
|
37679
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
36426
37680
|
render();
|
|
36427
37681
|
return;
|
|
36428
37682
|
}
|
|
36429
37683
|
}
|
|
36430
|
-
|
|
37684
|
+
if (!ownsConfirmation())
|
|
37685
|
+
return;
|
|
37686
|
+
finish({
|
|
37687
|
+
prompt: confirmedPrompt,
|
|
37688
|
+
githubInstallationId: installationId,
|
|
37689
|
+
repositories: selected,
|
|
37690
|
+
...repositoryBranchOverrides.length > 0 ? { repositoryBranchOverrides } : {},
|
|
37691
|
+
attachments: confirmedAttachments,
|
|
37692
|
+
model: confirmedModel,
|
|
37693
|
+
reasoningEffort: confirmedReasoningEffort
|
|
37694
|
+
});
|
|
36431
37695
|
}
|
|
36432
37696
|
async function submitEditor() {
|
|
36433
37697
|
const value = editor.plainText.trim();
|
|
@@ -36461,8 +37725,12 @@ async function createNewSessionWizard({
|
|
|
36461
37725
|
return;
|
|
36462
37726
|
}
|
|
36463
37727
|
if (step === "repositories") {
|
|
36464
|
-
step = "confirmation";
|
|
36465
37728
|
editor.setText("");
|
|
37729
|
+
startBranchReview();
|
|
37730
|
+
return;
|
|
37731
|
+
}
|
|
37732
|
+
if (step === "branches") {
|
|
37733
|
+
step = "confirmation";
|
|
36466
37734
|
render();
|
|
36467
37735
|
return;
|
|
36468
37736
|
}
|
|
@@ -36526,6 +37794,7 @@ async function createNewSessionWizard({
|
|
|
36526
37794
|
destroy() {
|
|
36527
37795
|
if (rendererDestroyed2)
|
|
36528
37796
|
return;
|
|
37797
|
+
invalidatePendingConfirmation();
|
|
36529
37798
|
destroyed = true;
|
|
36530
37799
|
if (inputHandler)
|
|
36531
37800
|
renderer.removeInputHandler(inputHandler);
|
|
@@ -36562,12 +37831,13 @@ async function createNewSessionWizard({
|
|
|
36562
37831
|
}
|
|
36563
37832
|
}
|
|
36564
37833
|
function stepHeader(title) {
|
|
36565
|
-
return bold3(
|
|
37834
|
+
return bold3(fg6(PALETTE.remyAccent)(title));
|
|
36566
37835
|
}
|
|
36567
37836
|
var WIZARD_STEPS = [
|
|
36568
37837
|
{ key: "describe", label: "Describe" },
|
|
36569
37838
|
{ key: "installation", label: "Installation" },
|
|
36570
37839
|
{ key: "repositories", label: "Repositories" },
|
|
37840
|
+
{ key: "branches", label: "Branches" },
|
|
36571
37841
|
{ key: "confirm", label: "Confirm" }
|
|
36572
37842
|
];
|
|
36573
37843
|
function wizardStepKey(step) {
|
|
@@ -36575,52 +37845,41 @@ function wizardStepKey(step) {
|
|
|
36575
37845
|
return "describe";
|
|
36576
37846
|
if (step === "installation")
|
|
36577
37847
|
return "installation";
|
|
37848
|
+
if (step === "loadingBranches" || step === "branches")
|
|
37849
|
+
return "branches";
|
|
36578
37850
|
if (step === "confirmation")
|
|
36579
37851
|
return "confirm";
|
|
36580
37852
|
return "repositories";
|
|
36581
37853
|
}
|
|
36582
|
-
function renderStepIndicator({ step, hasInstallationChoice }) {
|
|
36583
|
-
const steps = WIZARD_STEPS.filter((entry) => entry.key !== "installation" || hasInstallationChoice);
|
|
37854
|
+
function renderStepIndicator({ step, hasInstallationChoice, hasBranchChoice = false }) {
|
|
37855
|
+
const steps = WIZARD_STEPS.filter((entry) => (entry.key !== "installation" || hasInstallationChoice) && (entry.key !== "branches" || hasBranchChoice));
|
|
36584
37856
|
const currentKey = wizardStepKey(step);
|
|
36585
37857
|
const currentIndex = steps.findIndex((entry) => entry.key === currentKey);
|
|
36586
37858
|
const position = currentIndex >= 0 ? currentIndex + 1 : steps.length;
|
|
36587
37859
|
const crumbs = [];
|
|
36588
37860
|
steps.forEach((entry, index) => {
|
|
36589
37861
|
if (index > 0)
|
|
36590
|
-
crumbs.push(
|
|
36591
|
-
crumbs.push(entry.key === currentKey ? bold3(
|
|
37862
|
+
crumbs.push(fg6(PALETTE.dimText)(" \u203A "));
|
|
37863
|
+
crumbs.push(entry.key === currentKey ? bold3(fg6(PALETTE.bodyText)(entry.label)) : fg6(PALETTE.dimText)(entry.label));
|
|
36592
37864
|
});
|
|
36593
|
-
return new
|
|
36594
|
-
bold3(
|
|
36595
|
-
|
|
37865
|
+
return new StyledText5([
|
|
37866
|
+
bold3(fg6(PALETTE.remyAccent)(`Step ${position} of ${steps.length}`)),
|
|
37867
|
+
fg6(PALETTE.dimText)(" "),
|
|
36596
37868
|
...crumbs
|
|
36597
37869
|
]);
|
|
36598
37870
|
}
|
|
36599
37871
|
function renderSelectableRow({ label, isCursor }) {
|
|
36600
37872
|
const line = `${isCursor ? "> " : " "}${label}`;
|
|
36601
|
-
const styled =
|
|
37873
|
+
const styled = fg6(PALETTE.bodyText)(line);
|
|
36602
37874
|
return [isCursor ? bg3(PALETTE.selectionBg)(styled) : styled];
|
|
36603
37875
|
}
|
|
36604
37876
|
function renderRepositoryRow({ repository, isCursor, isChecked, source }) {
|
|
36605
37877
|
const marker = isChecked ? "[x]" : "[ ]";
|
|
36606
37878
|
const line = `${isCursor ? "> " : " "}${marker} ${repository.fullName}`;
|
|
36607
|
-
const styled = isChecked ? bold3(
|
|
36608
|
-
const tag = source === "suggested" && isChecked ?
|
|
37879
|
+
const styled = isChecked ? bold3(fg6(PALETTE.humanAccent)(line)) : fg6(PALETTE.dimText)(line);
|
|
37880
|
+
const tag = source === "suggested" && isChecked ? dim4(fg6(PALETTE.tool)(" \u25C6 suggested by Remy")) : undefined;
|
|
36609
37881
|
return [isCursor ? bg3(PALETTE.selectionBg)(styled) : styled, ...tag ? [tag] : []];
|
|
36610
37882
|
}
|
|
36611
|
-
function fuzzyMatches(value, query) {
|
|
36612
|
-
const normalizedQuery = query.replaceAll(/\s/g, "").toLowerCase();
|
|
36613
|
-
if (!normalizedQuery)
|
|
36614
|
-
return true;
|
|
36615
|
-
let queryIndex = 0;
|
|
36616
|
-
for (const character of value.toLowerCase()) {
|
|
36617
|
-
if (character === normalizedQuery[queryIndex])
|
|
36618
|
-
queryIndex += 1;
|
|
36619
|
-
if (queryIndex === normalizedQuery.length)
|
|
36620
|
-
return true;
|
|
36621
|
-
}
|
|
36622
|
-
return false;
|
|
36623
|
-
}
|
|
36624
37883
|
function cycle({ values, value, direction }) {
|
|
36625
37884
|
const currentIndex = values.indexOf(value);
|
|
36626
37885
|
const nextIndex = (currentIndex + direction + values.length) % values.length;
|
|
@@ -36636,7 +37895,7 @@ async function createDefaultRenderer2() {
|
|
|
36636
37895
|
}
|
|
36637
37896
|
|
|
36638
37897
|
// src/tui/session-view.ts
|
|
36639
|
-
import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, bg as bg4, bold as bold4, dim as
|
|
37898
|
+
import { CliRenderEvents as CliRenderEvents4, BoxRenderable as BoxRenderable4, bg as bg4, bold as bold4, dim as dim5, fg as fg7, ScrollBoxRenderable as ScrollBoxRenderable3, StyledText as StyledText6, stringToStyledText as stringToStyledText5, TextRenderable as TextRenderable4 } from "@opentui/core";
|
|
36640
37899
|
var composerSlashCommands = [
|
|
36641
37900
|
{ value: "/sessions", description: "Back to the session list" },
|
|
36642
37901
|
{ value: "/complete", description: "Finish this session" },
|
|
@@ -36717,6 +37976,7 @@ async function createSessionTui({
|
|
|
36717
37976
|
}
|
|
36718
37977
|
promptHistoryIndex = direction === "up" ? Math.max(0, promptHistoryIndex - 1) : Math.min(promptHistory.length, promptHistoryIndex + 1);
|
|
36719
37978
|
const text = promptHistoryIndex === promptHistory.length ? promptHistoryDraft : promptHistory[promptHistoryIndex];
|
|
37979
|
+
selectedPathCompletions = [];
|
|
36720
37980
|
historyReplacement = text;
|
|
36721
37981
|
composer.setText(text);
|
|
36722
37982
|
composer.cursorOffset = text.length;
|
|
@@ -36739,10 +37999,10 @@ async function createSessionTui({
|
|
|
36739
37999
|
syncTerminalSessionControls();
|
|
36740
38000
|
syncWorkingIndicator();
|
|
36741
38001
|
renderRetainedTranscript();
|
|
36742
|
-
const assistantPreview = latestState.previews.assistantText ? new
|
|
36743
|
-
`)),
|
|
36744
|
-
const reasoningPreview = latestState.previews.reasoningText ? new
|
|
36745
|
-
`)),
|
|
38002
|
+
const assistantPreview = latestState.previews.assistantText ? new StyledText6([dim5(fg7(PALETTE.dimText)(`Live assistant preview
|
|
38003
|
+
`)), fg7(PALETTE.remyAccent)(latestState.previews.assistantText)]) : new StyledText6([]);
|
|
38004
|
+
const reasoningPreview = latestState.previews.reasoningText ? new StyledText6([dim5(fg7(PALETTE.dimText)(`Live reasoning preview
|
|
38005
|
+
`)), dim5(fg7(PALETTE.dimText)(latestState.previews.reasoningText))]) : new StyledText6([]);
|
|
36746
38006
|
const working = isRemyWorking(latestState);
|
|
36747
38007
|
const startedAt = workingTurnStartedAt(latestState);
|
|
36748
38008
|
const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now3() - Date.parse(startedAt));
|
|
@@ -36751,7 +38011,7 @@ async function createSessionTui({
|
|
|
36751
38011
|
frame: workingSpinnerFrames[spinnerFrameIndex],
|
|
36752
38012
|
elapsedMs,
|
|
36753
38013
|
mode: stopState.kind === "stopping" ? "stopping" : "working"
|
|
36754
|
-
}) : new
|
|
38014
|
+
}) : new StyledText6([]);
|
|
36755
38015
|
const liveContent = [assistantPreview, reasoningPreview, liveIndicator].filter((part) => part.chunks.length > 0);
|
|
36756
38016
|
liveTranscript.content = liveContent.length === 0 ? "" : joinStyled([stringToStyledText5(`
|
|
36757
38017
|
|
|
@@ -36767,6 +38027,7 @@ async function createSessionTui({
|
|
|
36767
38027
|
const commandMenu = renderSlashCommandMenu({ commands: slashCompletions, selectedIndex: slashCompletionIndex });
|
|
36768
38028
|
const composerStatus = [
|
|
36769
38029
|
helpVisible ? renderHelpPanel() : undefined,
|
|
38030
|
+
attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined,
|
|
36770
38031
|
composerFeedback ? stringToStyledText5(composerFeedback) : undefined,
|
|
36771
38032
|
submissionStatus,
|
|
36772
38033
|
commandMenu,
|
|
@@ -36839,10 +38100,17 @@ async function createSessionTui({
|
|
|
36839
38100
|
}
|
|
36840
38101
|
slashCompletions = composerSlashCommands.filter((command) => command.value.startsWith(value) && command.value !== value && (!["/complete", "/cancel"].includes(command.value) || latestState.aggregateStatus === "open"));
|
|
36841
38102
|
slashCompletionIndex = 0;
|
|
38103
|
+
}, invalidateAttachmentPreparation = function() {
|
|
38104
|
+
attachmentPreparationOwner = undefined;
|
|
38105
|
+
}, ownsAttachmentPreparation = function(owner) {
|
|
38106
|
+
return attachmentPreparationOwner === owner && !destroyed && !settled;
|
|
38107
|
+
}, viewIsActive = function() {
|
|
38108
|
+
return !destroyed && !settled;
|
|
36842
38109
|
}, finish = function(result, error93) {
|
|
36843
38110
|
if (settled)
|
|
36844
38111
|
return;
|
|
36845
38112
|
settled = true;
|
|
38113
|
+
invalidateAttachmentPreparation();
|
|
36846
38114
|
if (error93)
|
|
36847
38115
|
rejectAction(error93);
|
|
36848
38116
|
else
|
|
@@ -36866,17 +38134,21 @@ async function createSessionTui({
|
|
|
36866
38134
|
let settled = false;
|
|
36867
38135
|
let activityExpanded = false;
|
|
36868
38136
|
let composerDraft = initialComposer ?? { text: "", attachments: [] };
|
|
38137
|
+
let previousComposerText = composerDraft.text;
|
|
36869
38138
|
let pathCompletions = [];
|
|
38139
|
+
let selectedPathCompletions = [];
|
|
36870
38140
|
let completionIndex = 0;
|
|
36871
38141
|
let pathCompletionMenuOpen = false;
|
|
36872
38142
|
let slashCompletions = [];
|
|
36873
38143
|
let slashCompletionIndex = 0;
|
|
36874
38144
|
let composerFeedback = initialFeedback;
|
|
38145
|
+
let attachmentFeedback;
|
|
36875
38146
|
const localPromptHistory = [];
|
|
36876
38147
|
let promptHistoryIndex;
|
|
36877
38148
|
let promptHistoryDraft = "";
|
|
36878
38149
|
let historyReplacement;
|
|
36879
38150
|
let pendingPastedImageWrites = 0;
|
|
38151
|
+
let attachmentPreparationOwner;
|
|
36880
38152
|
let admittedSubmissions = [];
|
|
36881
38153
|
let stopState = { kind: "idle" };
|
|
36882
38154
|
let lifecycleRequestState = { kind: "idle" };
|
|
@@ -36915,6 +38187,12 @@ async function createSessionTui({
|
|
|
36915
38187
|
submitComposer();
|
|
36916
38188
|
},
|
|
36917
38189
|
onContentChange: () => {
|
|
38190
|
+
selectedPathCompletions = reconcileSelectedPathCompletions({
|
|
38191
|
+
previousText: previousComposerText,
|
|
38192
|
+
text: composer.plainText,
|
|
38193
|
+
selections: selectedPathCompletions
|
|
38194
|
+
});
|
|
38195
|
+
previousComposerText = composer.plainText;
|
|
36918
38196
|
if (historyReplacement !== composer.plainText)
|
|
36919
38197
|
promptHistoryIndex = undefined;
|
|
36920
38198
|
historyReplacement = undefined;
|
|
@@ -36995,12 +38273,12 @@ async function createSessionTui({
|
|
|
36995
38273
|
};
|
|
36996
38274
|
async function pasteClipboardImage() {
|
|
36997
38275
|
if (!readClipboardImage || !savePastedImage) {
|
|
36998
|
-
|
|
38276
|
+
attachmentFeedback = { kind: "failure", message: "Clipboard image paste is unavailable." };
|
|
36999
38277
|
render();
|
|
37000
38278
|
return;
|
|
37001
38279
|
}
|
|
37002
38280
|
pendingPastedImageWrites += 1;
|
|
37003
|
-
|
|
38281
|
+
attachmentFeedback = { kind: "progress", message: "Saving clipboard image\u2026" };
|
|
37004
38282
|
render();
|
|
37005
38283
|
try {
|
|
37006
38284
|
const image = await readClipboardImage();
|
|
@@ -37008,10 +38286,13 @@ async function createSessionTui({
|
|
|
37008
38286
|
if (destroyed)
|
|
37009
38287
|
return;
|
|
37010
38288
|
composer.insertText(path);
|
|
37011
|
-
|
|
38289
|
+
const selectedCompletion = createSelectedPathCompletion({ attachmentPath: path, token: path, end: composer.cursorOffset });
|
|
38290
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
38291
|
+
previousComposerText = composer.plainText;
|
|
38292
|
+
attachmentFeedback = { kind: "success", message: "Clipboard image ready to attach." };
|
|
37012
38293
|
} catch (error93) {
|
|
37013
38294
|
if (!destroyed)
|
|
37014
|
-
|
|
38295
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
37015
38296
|
} finally {
|
|
37016
38297
|
pendingPastedImageWrites -= 1;
|
|
37017
38298
|
if (!destroyed)
|
|
@@ -37071,6 +38352,15 @@ async function createSessionTui({
|
|
|
37071
38352
|
const completedText = replaceActivePathToken({ text: composer.plainText, replacement: completion.replacement });
|
|
37072
38353
|
composer.setText(completedText);
|
|
37073
38354
|
composer.cursorOffset = completedText.length;
|
|
38355
|
+
if (completion.attachmentPath) {
|
|
38356
|
+
const selectedCompletion = createSelectedPathCompletion({
|
|
38357
|
+
attachmentPath: completion.attachmentPath,
|
|
38358
|
+
token: completion.replacement,
|
|
38359
|
+
end: completedText.length
|
|
38360
|
+
});
|
|
38361
|
+
selectedPathCompletions = [...selectedPathCompletions, selectedCompletion];
|
|
38362
|
+
}
|
|
38363
|
+
previousComposerText = completedText;
|
|
37074
38364
|
}
|
|
37075
38365
|
pathCompletions = [];
|
|
37076
38366
|
pathCompletionMenuOpen = false;
|
|
@@ -37101,8 +38391,10 @@ async function createSessionTui({
|
|
|
37101
38391
|
render();
|
|
37102
38392
|
}
|
|
37103
38393
|
async function submitComposer() {
|
|
38394
|
+
if (attachmentPreparationOwner)
|
|
38395
|
+
return;
|
|
37104
38396
|
if (pendingPastedImageWrites > 0) {
|
|
37105
|
-
|
|
38397
|
+
attachmentFeedback = { kind: "progress", message: "Saving pasted image\u2026" };
|
|
37106
38398
|
render();
|
|
37107
38399
|
return;
|
|
37108
38400
|
}
|
|
@@ -37110,33 +38402,61 @@ async function createSessionTui({
|
|
|
37110
38402
|
const value = composerDraft.text;
|
|
37111
38403
|
if (value.trim() === "")
|
|
37112
38404
|
return;
|
|
37113
|
-
|
|
38405
|
+
const preparationOwner = Symbol("attachment preparation");
|
|
38406
|
+
attachmentPreparationOwner = preparationOwner;
|
|
38407
|
+
if (await handleComposerCommand(value)) {
|
|
38408
|
+
if (attachmentPreparationOwner === preparationOwner)
|
|
38409
|
+
invalidateAttachmentPreparation();
|
|
38410
|
+
return;
|
|
38411
|
+
}
|
|
38412
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
37114
38413
|
return;
|
|
37115
38414
|
if (lifecycleRequestState.kind === "pending") {
|
|
37116
38415
|
composerFeedback = `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.`;
|
|
37117
38416
|
render();
|
|
38417
|
+
invalidateAttachmentPreparation();
|
|
37118
38418
|
return;
|
|
37119
38419
|
}
|
|
37120
38420
|
if (latestState.aggregateStatus !== "open") {
|
|
37121
38421
|
composerFeedback = "This session is already terminal. Run /sessions to return to the session list.";
|
|
37122
38422
|
render();
|
|
38423
|
+
invalidateAttachmentPreparation();
|
|
37123
38424
|
return;
|
|
37124
38425
|
}
|
|
37125
38426
|
if (attachPromptPaths) {
|
|
37126
|
-
|
|
37127
|
-
render();
|
|
38427
|
+
let handedOffToAdmission = false;
|
|
37128
38428
|
try {
|
|
37129
38429
|
const attachments = await attachPromptPaths({
|
|
37130
38430
|
text: composerDraft.text,
|
|
37131
|
-
excludedPaths: composerDraft.attachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : [])
|
|
38431
|
+
excludedPaths: composerDraft.attachments.flatMap((attachment) => attachment.sourcePath ? [attachment.sourcePath] : []),
|
|
38432
|
+
selectedPaths: selectedAttachmentPaths({ text: composerDraft.text, selections: selectedPathCompletions }),
|
|
38433
|
+
onUploadStart: ({ filenames }) => {
|
|
38434
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38435
|
+
return;
|
|
38436
|
+
attachmentFeedback = { kind: "progress", message: attachmentUploadMessage(filenames) };
|
|
38437
|
+
render();
|
|
38438
|
+
}
|
|
37132
38439
|
});
|
|
38440
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38441
|
+
return;
|
|
37133
38442
|
composerDraft = { ...composerDraft, attachments: [...composerDraft.attachments, ...attachments] };
|
|
38443
|
+
selectedPathCompletions = [];
|
|
38444
|
+
attachmentFeedback = undefined;
|
|
38445
|
+
handedOffToAdmission = true;
|
|
37134
38446
|
} catch (error93) {
|
|
37135
|
-
|
|
38447
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38448
|
+
return;
|
|
38449
|
+
attachmentFeedback = { kind: "failure", message: error93 instanceof Error ? error93.message : String(error93) };
|
|
37136
38450
|
render();
|
|
37137
38451
|
return;
|
|
38452
|
+
} finally {
|
|
38453
|
+
if (!handedOffToAdmission && attachmentPreparationOwner === preparationOwner)
|
|
38454
|
+
invalidateAttachmentPreparation();
|
|
37138
38455
|
}
|
|
37139
38456
|
}
|
|
38457
|
+
if (!ownsAttachmentPreparation(preparationOwner))
|
|
38458
|
+
return;
|
|
38459
|
+
invalidateAttachmentPreparation();
|
|
37140
38460
|
const submission = {
|
|
37141
38461
|
id: crypto.randomUUID(),
|
|
37142
38462
|
idempotencyKey: crypto.randomUUID(),
|
|
@@ -37173,13 +38493,13 @@ async function createSessionTui({
|
|
|
37173
38493
|
if (command === "/complete") {
|
|
37174
38494
|
composer.setText("");
|
|
37175
38495
|
composerDraft = { ...composerDraft, text: "" };
|
|
37176
|
-
|
|
38496
|
+
handleLifecycleRequest("complete");
|
|
37177
38497
|
return true;
|
|
37178
38498
|
}
|
|
37179
38499
|
if (command === "/cancel") {
|
|
37180
38500
|
composer.setText("");
|
|
37181
38501
|
composerDraft = { ...composerDraft, text: "" };
|
|
37182
|
-
|
|
38502
|
+
handleLifecycleRequest("cancel");
|
|
37183
38503
|
return true;
|
|
37184
38504
|
}
|
|
37185
38505
|
if (command === "/logout") {
|
|
@@ -37207,16 +38527,22 @@ async function createSessionTui({
|
|
|
37207
38527
|
attachments: submission.attachments,
|
|
37208
38528
|
idempotencyKey: submission.idempotencyKey
|
|
37209
38529
|
});
|
|
38530
|
+
if (!viewIsActive())
|
|
38531
|
+
return;
|
|
37210
38532
|
localPromptHistory.push(submission.text);
|
|
37211
38533
|
const messageId = latestState.activeMessageId;
|
|
37212
38534
|
admittedSubmissions = messageId ? admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, status: "submitted", messageId } : candidate) : admittedSubmissions.filter((candidate) => candidate.id !== submission.id);
|
|
37213
38535
|
composerFeedback = undefined;
|
|
37214
38536
|
} catch (error93) {
|
|
38537
|
+
if (!viewIsActive())
|
|
38538
|
+
return;
|
|
37215
38539
|
admittedSubmissions = admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, status: "failed", error: error93 instanceof Error ? error93.message : String(error93) } : candidate);
|
|
37216
38540
|
}
|
|
37217
38541
|
render();
|
|
37218
38542
|
}
|
|
37219
38543
|
async function retryOldestFailedSubmission() {
|
|
38544
|
+
if (!viewIsActive())
|
|
38545
|
+
return;
|
|
37220
38546
|
const failed = admittedSubmissions.find((submission) => submission.status === "failed");
|
|
37221
38547
|
if (!failed)
|
|
37222
38548
|
return;
|
|
@@ -37332,6 +38658,7 @@ async function createSessionTui({
|
|
|
37332
38658
|
if (destroyed)
|
|
37333
38659
|
return;
|
|
37334
38660
|
destroyed = true;
|
|
38661
|
+
invalidateAttachmentPreparation();
|
|
37335
38662
|
if (workingSpinnerTimer) {
|
|
37336
38663
|
clearInterval(workingSpinnerTimer);
|
|
37337
38664
|
workingSpinnerTimer = undefined;
|
|
@@ -37373,9 +38700,9 @@ function renderSlashCommandMenu({ commands, selectedIndex }) {
|
|
|
37373
38700
|
const rows = commands.map((command, index) => {
|
|
37374
38701
|
const selected = index === selectedIndex;
|
|
37375
38702
|
const rowStyle = selected ? bg4(PALETTE.selectionBg) : (chunk) => chunk;
|
|
37376
|
-
return new
|
|
37377
|
-
rowStyle(
|
|
37378
|
-
rowStyle(
|
|
38703
|
+
return new StyledText6([
|
|
38704
|
+
rowStyle(fg7(PALETTE.remyAccent)(`${selected ? "\u203A " : " "}${command.value.padEnd(12)}`)),
|
|
38705
|
+
rowStyle(fg7(PALETTE.dimText)(command.description))
|
|
37379
38706
|
]);
|
|
37380
38707
|
});
|
|
37381
38708
|
return joinStyled([joinStyled(rows, `
|
|
@@ -37396,15 +38723,15 @@ var helpEntries = [
|
|
|
37396
38723
|
];
|
|
37397
38724
|
function renderHelpPanel() {
|
|
37398
38725
|
const width = helpEntries.reduce((max, entry) => Math.max(max, entry.token.length), 0) + 2;
|
|
37399
|
-
const rows = [new
|
|
38726
|
+
const rows = [new StyledText6([dim5(fg7(PALETTE.dimText)("What you can do here"))])];
|
|
37400
38727
|
let lastGroup;
|
|
37401
38728
|
for (const entry of helpEntries) {
|
|
37402
38729
|
if (lastGroup !== undefined && entry.group !== lastGroup)
|
|
37403
|
-
rows.push(new
|
|
38730
|
+
rows.push(new StyledText6([fg7(PALETTE.dimText)(" ")]));
|
|
37404
38731
|
lastGroup = entry.group;
|
|
37405
|
-
rows.push(new
|
|
37406
|
-
|
|
37407
|
-
|
|
38732
|
+
rows.push(new StyledText6([
|
|
38733
|
+
fg7(PALETTE.remyAccent)(` ${entry.token.padEnd(width)}`),
|
|
38734
|
+
dim5(fg7(PALETTE.dimText)(entry.description))
|
|
37408
38735
|
]));
|
|
37409
38736
|
}
|
|
37410
38737
|
return joinStyled(rows, `
|
|
@@ -37418,23 +38745,22 @@ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleReque
|
|
|
37418
38745
|
const pendingOperation = lifecycleRequestState?.kind === "pending" ? lifecycleRequestState.operation : undefined;
|
|
37419
38746
|
const presentation = pendingOperation ? { label: lifecycleOperationPresentParticiple(pendingOperation), color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
|
|
37420
38747
|
const elapsed = (working || stopping) && elapsedMs !== undefined && elapsedMs > 0 ? ` ${formatElapsed(elapsedMs)}` : "";
|
|
37421
|
-
const connection = state.connectionStatus === "connected" ?
|
|
37422
|
-
const sessionLabel = state.sessionNumber !== undefined ?
|
|
37423
|
-
const context = renderContextStatus(state.context.snapshot);
|
|
38748
|
+
const connection = state.connectionStatus === "connected" ? new StyledText6([fg7(PALETTE.statusCompleted)("\u25CF "), dim5(fg7(PALETTE.dimText)("connected"))]) : new StyledText6([fg7(PALETTE.approvalQuestion)("\u25CF reconnecting\u2026")]);
|
|
38749
|
+
const sessionLabel = state.sessionNumber !== undefined ? `#${state.sessionNumber}` : state.sessionId;
|
|
37424
38750
|
const pullRequest = state.pullRequest;
|
|
37425
|
-
|
|
37426
|
-
|
|
37427
|
-
...repositoryLabel ? [
|
|
37428
|
-
|
|
37429
|
-
bold4(
|
|
37430
|
-
|
|
37431
|
-
|
|
37432
|
-
|
|
37433
|
-
|
|
37434
|
-
|
|
37435
|
-
|
|
37436
|
-
|
|
37437
|
-
|
|
38751
|
+
const contextStatus = renderContextStatus(state.context.snapshot);
|
|
38752
|
+
const headerContext = [
|
|
38753
|
+
...repositoryLabel ? [[dim5(fg7(PALETTE.dimText)(repositoryLabel))]] : [],
|
|
38754
|
+
...pullRequest ? [[bold4(fg7(statusColor(pullRequest.status))(`PR#${pullRequest.number}`))]] : [],
|
|
38755
|
+
[bold4(fg7(presentation.color)(`${presentation.label}${elapsed}`))],
|
|
38756
|
+
connection.chunks,
|
|
38757
|
+
...contextStatus ? [[dim5(fg7(PALETTE.dimText)(contextStatus))]] : []
|
|
38758
|
+
];
|
|
38759
|
+
return joinStyled([
|
|
38760
|
+
[bold4(fg7(PALETTE.bodyText)(`${sessionLabel} ${state.title ?? "Untitled session"}`))],
|
|
38761
|
+
joinStyled(headerContext, " \xB7 ")
|
|
38762
|
+
], `
|
|
38763
|
+
`);
|
|
37438
38764
|
}
|
|
37439
38765
|
function renderContextStatus(snapshot) {
|
|
37440
38766
|
if (snapshot.status === "unknown")
|
|
@@ -37445,33 +38771,39 @@ function renderContextStatus(snapshot) {
|
|
|
37445
38771
|
function renderTerminalSessionBand({ aggregateStatus }) {
|
|
37446
38772
|
const status = sessionStatusLabel(aggregateStatus).toLowerCase();
|
|
37447
38773
|
return joinStyled([
|
|
37448
|
-
new
|
|
37449
|
-
bg4(PALETTE.selectionBg)(bold4(
|
|
37450
|
-
bg4(PALETTE.selectionBg)(
|
|
38774
|
+
new StyledText6([
|
|
38775
|
+
bg4(PALETTE.selectionBg)(bold4(fg7(PALETTE.systemAccent)(` Session ${status} `))),
|
|
38776
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.bodyText)("\u2014 this conversation is closed. "))
|
|
37451
38777
|
]),
|
|
37452
|
-
new
|
|
37453
|
-
bg4(PALETTE.selectionBg)(
|
|
37454
|
-
bg4(PALETTE.selectionBg)(
|
|
37455
|
-
bg4(PALETTE.selectionBg)(
|
|
37456
|
-
bg4(PALETTE.selectionBg)(
|
|
38778
|
+
new StyledText6([
|
|
38779
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.remyAccent)(" esc: ")),
|
|
38780
|
+
bg4(PALETTE.selectionBg)(dim5(fg7(PALETTE.dimText)("return to dashboard \xB7 "))),
|
|
38781
|
+
bg4(PALETTE.selectionBg)(fg7(PALETTE.remyAccent)("n: ")),
|
|
38782
|
+
bg4(PALETTE.selectionBg)(dim5(fg7(PALETTE.dimText)("new session ")))
|
|
37457
38783
|
])
|
|
37458
38784
|
], `
|
|
37459
38785
|
`);
|
|
37460
38786
|
}
|
|
37461
38787
|
function renderActionBar({ state, working, stopState, lifecycleRequestState }) {
|
|
37462
38788
|
if (lifecycleRequestState.kind === "pending")
|
|
37463
|
-
return new
|
|
38789
|
+
return new StyledText6([dim5(fg7(PALETTE.dimText)(`${lifecycleOperationPresentParticiple(lifecycleRequestState.operation).toLowerCase()} the session\u2026`))]);
|
|
37464
38790
|
if (state.aggregateStatus !== "open")
|
|
37465
|
-
return new
|
|
38791
|
+
return new StyledText6([]);
|
|
37466
38792
|
const stopToken = working && stopState.kind === "failed" ? ["esc retry stop"] : [];
|
|
37467
38793
|
const idleGroup = lifecycleRequestState.kind === "failed" ? `/${lifecycleRequestState.operation} retries \xB7 /new \xB7 /sessions` : "/new \xB7 /sessions \xB7 /complete \xB7 /cancel";
|
|
37468
38794
|
const tokens = working ? ["/ commands", ...stopToken, "ctrl+o activity"] : ["/ commands", idleGroup, "ctrl+o activity"];
|
|
37469
|
-
return new
|
|
38795
|
+
return new StyledText6([dim5(fg7(PALETTE.dimText)(tokens.join(" \xB7 ")))]);
|
|
37470
38796
|
}
|
|
37471
38797
|
function isRemyWorking(state) {
|
|
37472
|
-
if (state.aggregateStatus !== "open" || state.connectionStatus !== "connected")
|
|
38798
|
+
if (state.aggregateStatus !== "open" || state.connectionStatus !== "connected") {
|
|
37473
38799
|
return false;
|
|
37474
|
-
|
|
38800
|
+
}
|
|
38801
|
+
if (state.activeMessageId !== undefined && state.messageTurns[state.activeMessageId]?.outcome === undefined)
|
|
38802
|
+
return true;
|
|
38803
|
+
return isWorkingAgentStatus(state.agentStatus) && hasRunningTurn(state);
|
|
38804
|
+
}
|
|
38805
|
+
function isWorkingAgentStatus(agentStatus) {
|
|
38806
|
+
return agentStatus === undefined || agentStatus === "pending" || agentStatus === "queued" || agentStatus === "running";
|
|
37475
38807
|
}
|
|
37476
38808
|
function workingTurnStartedAt(state) {
|
|
37477
38809
|
const activeTurn = state.activeMessageId === undefined ? undefined : state.messageTurns[state.activeMessageId];
|
|
@@ -37512,58 +38844,64 @@ function renderTimeline({ state, activityExpanded }) {
|
|
|
37512
38844
|
}
|
|
37513
38845
|
function renderTimelineItem(item, activityExpanded) {
|
|
37514
38846
|
if (item.kind === "message") {
|
|
37515
|
-
const header = new
|
|
38847
|
+
const header = new StyledText6([
|
|
37516
38848
|
renderTimestampChunk(item.occurredAt),
|
|
37517
38849
|
renderRoleLabel({ label: item.author.label, role: item.author.role }),
|
|
37518
|
-
...item.author.source ? [
|
|
38850
|
+
...item.author.source ? [dim5(fg7(PALETTE.dimText)(` (${item.author.source})`))] : []
|
|
37519
38851
|
]);
|
|
37520
|
-
|
|
38852
|
+
const attachments = renderAttachmentSummary(item.attachments);
|
|
38853
|
+
return joinStyled([
|
|
38854
|
+
header,
|
|
38855
|
+
renderMessageBody({ text: item.text, role: item.author.role }),
|
|
38856
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
38857
|
+
], `
|
|
37521
38858
|
`);
|
|
37522
38859
|
}
|
|
37523
38860
|
if (item.kind === "plan")
|
|
37524
38861
|
return renderPlan({ item, expanded: activityExpanded });
|
|
37525
38862
|
if (item.artifactKind === "tela_page") {
|
|
37526
|
-
return new
|
|
38863
|
+
return new StyledText6([
|
|
37527
38864
|
renderTimestampChunk(item.occurredAt),
|
|
37528
|
-
bold4(
|
|
37529
|
-
|
|
37530
|
-
|
|
38865
|
+
bold4(fg7(PALETTE.tool)("Tela Page: ")),
|
|
38866
|
+
fg7(PALETTE.bodyText)(item.title),
|
|
38867
|
+
dim5(fg7(PALETTE.dimText)(` \xB7 ${item.url}`))
|
|
37531
38868
|
]);
|
|
37532
38869
|
}
|
|
37533
|
-
return new
|
|
38870
|
+
return new StyledText6([
|
|
37534
38871
|
renderTimestampChunk(item.occurredAt),
|
|
37535
|
-
bold4(
|
|
37536
|
-
|
|
38872
|
+
bold4(fg7(PALETTE.tool)("Artifact: ")),
|
|
38873
|
+
fg7(PALETTE.bodyText)(item.title),
|
|
38874
|
+
...item.previewUrl ? [dim5(fg7(PALETTE.dimText)(` (${item.previewUrl})`))] : []
|
|
37537
38875
|
]);
|
|
37538
38876
|
}
|
|
37539
38877
|
var collapsedPlanItemLimit = 5;
|
|
37540
38878
|
function renderPlan({ item, expanded }) {
|
|
37541
38879
|
if (item.items.length === 0)
|
|
37542
|
-
return new
|
|
38880
|
+
return new StyledText6([]);
|
|
37543
38881
|
const completed = item.items.filter((workItem) => workItem.status === "completed").length;
|
|
37544
38882
|
const visibleItems = expanded ? item.items : collapsedPlanItems(item.items);
|
|
37545
38883
|
const rows = visibleItems.map((workItem) => {
|
|
37546
38884
|
if (workItem.status === "in_progress") {
|
|
37547
|
-
return new
|
|
37548
|
-
|
|
37549
|
-
bold4(
|
|
38885
|
+
return new StyledText6([
|
|
38886
|
+
fg7(PALETTE.remyAccent)(" \u25B8 "),
|
|
38887
|
+
bold4(fg7(PALETTE.bodyText)(workItem.title))
|
|
37550
38888
|
]);
|
|
37551
38889
|
}
|
|
37552
38890
|
const glyph = workItem.status === "completed" ? "\u2713" : "\u25CB";
|
|
37553
|
-
return new
|
|
37554
|
-
|
|
37555
|
-
|
|
38891
|
+
return new StyledText6([
|
|
38892
|
+
fg7(PALETTE.dimText)(` ${glyph} `),
|
|
38893
|
+
dim5(fg7(PALETTE.dimText)(workItem.title))
|
|
37556
38894
|
]);
|
|
37557
38895
|
});
|
|
37558
38896
|
const hidden = item.items.length - visibleItems.length;
|
|
37559
38897
|
return joinStyled([
|
|
37560
|
-
new
|
|
38898
|
+
new StyledText6([
|
|
37561
38899
|
renderTimestampChunk(item.occurredAt),
|
|
37562
|
-
bold4(
|
|
37563
|
-
|
|
38900
|
+
bold4(fg7(PALETTE.remyAccent)("Plan")),
|
|
38901
|
+
dim5(fg7(PALETTE.dimText)(` \xB7 ${completed}/${item.items.length} done`))
|
|
37564
38902
|
]),
|
|
37565
38903
|
...rows,
|
|
37566
|
-
...hidden > 0 ? [new
|
|
38904
|
+
...hidden > 0 ? [new StyledText6([dim5(fg7(PALETTE.dimText)(` \u2026 ${hidden} more task${hidden === 1 ? "" : "s"} \xB7 Ctrl+O details`))])] : []
|
|
37567
38905
|
], `
|
|
37568
38906
|
`);
|
|
37569
38907
|
}
|
|
@@ -37593,20 +38931,24 @@ function activityGlyph({ kind, inFlight }) {
|
|
|
37593
38931
|
return { glyph: "\xB7", color: PALETTE.dimText };
|
|
37594
38932
|
return { glyph: "\u2713", color: PALETTE.dimText };
|
|
37595
38933
|
}
|
|
37596
|
-
function
|
|
37597
|
-
|
|
37598
|
-
|
|
37599
|
-
|
|
37600
|
-
|
|
37601
|
-
|
|
37602
|
-
|
|
37603
|
-
|
|
38934
|
+
function activityCardsHaveEqualDisclosure(left, right) {
|
|
38935
|
+
return left.kind === right.kind && left.title === right.title && left.summary === right.summary && left.detail === right.detail && left.detailFormat === right.detailFormat && canonicalValuesEqual(left.attribution, right.attribution);
|
|
38936
|
+
}
|
|
38937
|
+
function canonicalValuesEqual(left, right) {
|
|
38938
|
+
if (Object.is(left, right))
|
|
38939
|
+
return true;
|
|
38940
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
38941
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => canonicalValuesEqual(value, right[index]));
|
|
37604
38942
|
}
|
|
37605
|
-
|
|
38943
|
+
if (typeof left !== "object" || left === null || typeof right !== "object" || right === null)
|
|
38944
|
+
return false;
|
|
38945
|
+
const leftEntries = Object.entries(left);
|
|
38946
|
+
const rightEntries = Object.entries(right);
|
|
38947
|
+
return leftEntries.length === rightEntries.length && leftEntries.every(([key, value], index) => rightEntries[index]?.[0] === key && canonicalValuesEqual(value, rightEntries[index]?.[1]));
|
|
37606
38948
|
}
|
|
37607
38949
|
function renderActivityGroup({ items, activityExpanded }) {
|
|
37608
38950
|
if (items.length === 0)
|
|
37609
|
-
return new
|
|
38951
|
+
return new StyledText6([]);
|
|
37610
38952
|
const summary = renderActivitySummary({ items, includeTimestamp: activityExpanded });
|
|
37611
38953
|
const lastInFlightIndex = items.length - 1;
|
|
37612
38954
|
if (!activityExpanded) {
|
|
@@ -37615,58 +38957,129 @@ function renderActivityGroup({ items, activityExpanded }) {
|
|
|
37615
38957
|
item,
|
|
37616
38958
|
inFlight: item.card.kind === "tool" && tailStart + index === lastInFlightIndex
|
|
37617
38959
|
}));
|
|
37618
|
-
const elision = tailStart > 0 ? new
|
|
38960
|
+
const elision = tailStart > 0 ? new StyledText6([dim5(fg7(PALETTE.dimText)(` \u2026 ${tailStart} earlier step${tailStart === 1 ? "" : "s"}`))]) : undefined;
|
|
37619
38961
|
return joinStyled([
|
|
37620
|
-
new
|
|
38962
|
+
new StyledText6([renderTimestampChunk(items[0].occurredAt), dim5(fg7(PALETTE.dimText)("Worked"))]),
|
|
37621
38963
|
...elision ? [elision] : [],
|
|
37622
38964
|
...tail,
|
|
37623
38965
|
summary
|
|
37624
38966
|
], `
|
|
37625
38967
|
`);
|
|
37626
38968
|
}
|
|
37627
|
-
|
|
37628
|
-
|
|
37629
|
-
|
|
37630
|
-
|
|
37631
|
-
|
|
37632
|
-
|
|
37633
|
-
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
|
|
37637
|
-
|
|
37638
|
-
|
|
37639
|
-
|
|
38969
|
+
return joinStyled([summary, ...renderExpandedActivityHierarchy({ items, lastInFlightIndex })], `
|
|
38970
|
+
`);
|
|
38971
|
+
}
|
|
38972
|
+
function renderExpandedActivityHierarchy({ items, lastInFlightIndex }) {
|
|
38973
|
+
const rendered = [];
|
|
38974
|
+
let activeOwnerKey;
|
|
38975
|
+
let index = 0;
|
|
38976
|
+
while (index < items.length) {
|
|
38977
|
+
const item = items[index];
|
|
38978
|
+
const attribution = item.card.attribution;
|
|
38979
|
+
if (attribution?.status === "resolved") {
|
|
38980
|
+
if (activeOwnerKey !== attribution.ownerKey) {
|
|
38981
|
+
rendered.push(new StyledText6([
|
|
38982
|
+
dim5(fg7(PALETTE.dimText)(" ")),
|
|
38983
|
+
fg7(PALETTE.bodyText)(`Subagent \xB7 ${lineagePathLabel(attribution.path)}`)
|
|
38984
|
+
]));
|
|
38985
|
+
}
|
|
38986
|
+
activeOwnerKey = attribution.ownerKey;
|
|
38987
|
+
} else {
|
|
38988
|
+
activeOwnerKey = undefined;
|
|
38989
|
+
}
|
|
38990
|
+
let count = 1;
|
|
38991
|
+
while (items[index + count] && activityCardsHaveEqualDisclosure(item.card, items[index + count].card)) {
|
|
38992
|
+
count += 1;
|
|
38993
|
+
}
|
|
38994
|
+
rendered.push(renderExpandedActivityStep({
|
|
38995
|
+
item,
|
|
38996
|
+
count,
|
|
38997
|
+
inFlight: item.card.kind === "tool" && index === lastInFlightIndex,
|
|
38998
|
+
nested: attribution?.status === "resolved"
|
|
38999
|
+
}));
|
|
39000
|
+
index += count;
|
|
39001
|
+
}
|
|
39002
|
+
return rendered;
|
|
39003
|
+
}
|
|
39004
|
+
function renderExpandedActivityStep({ item, count, inFlight, nested }) {
|
|
39005
|
+
const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
|
|
39006
|
+
const showSummary = item.card.summary.length > 0 && item.card.summary !== item.card.title && !boilerplateActivitySummaries.has(item.card.summary);
|
|
39007
|
+
const disclosure = renderActivityDisclosure(item.card);
|
|
39008
|
+
const title = expandedActivityTitle(item.card);
|
|
39009
|
+
const indent = nested ? " " : " ";
|
|
39010
|
+
const step = new StyledText6([
|
|
39011
|
+
fg7(color)(`${indent}${glyph} `),
|
|
39012
|
+
fg7(PALETTE.bodyText)(title),
|
|
39013
|
+
...count > 1 ? [dim5(fg7(PALETTE.dimText)(` \xD7${count}`))] : [],
|
|
39014
|
+
...showSummary ? [dim5(fg7(PALETTE.dimText)(`
|
|
39015
|
+
${indent} ${item.card.summary}`))] : []
|
|
39016
|
+
]);
|
|
39017
|
+
return disclosure ? joinStyled([step, disclosure], `
|
|
37640
39018
|
`) : step;
|
|
37641
|
-
|
|
37642
|
-
|
|
39019
|
+
}
|
|
39020
|
+
function expandedActivityTitle(card) {
|
|
39021
|
+
if (!card.attribution)
|
|
39022
|
+
return card.title;
|
|
39023
|
+
if (card.attribution.status === "resolved")
|
|
39024
|
+
return card.attribution.activityTitle ?? "Activity";
|
|
39025
|
+
const name = childIdentityLabel(card.attribution.identity);
|
|
39026
|
+
return `Unattributed subagent \xB7 ${name}${card.attribution.activityTitle ? ` \xB7 ${card.attribution.activityTitle}` : ""}`;
|
|
39027
|
+
}
|
|
39028
|
+
function lineagePathLabel(path) {
|
|
39029
|
+
return path.map(childIdentityLabel).join(" \u203A ");
|
|
39030
|
+
}
|
|
39031
|
+
function childIdentityLabel(identity) {
|
|
39032
|
+
const safeName = terminalSafeText(identity.name ?? "").replace(/\s+/gu, " ").trim();
|
|
39033
|
+
if (safeName.length > 0)
|
|
39034
|
+
return safeName;
|
|
39035
|
+
return terminalSafeText(identity.subagentId).replace(/\s+/gu, " ").trim();
|
|
39036
|
+
}
|
|
39037
|
+
function renderActivityDisclosure(card) {
|
|
39038
|
+
const details = [];
|
|
39039
|
+
if (card.detail !== undefined) {
|
|
39040
|
+
const safeDetail = terminalSafeText(card.detail);
|
|
39041
|
+
details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new StyledText6([dim5(fg7(PALETTE.dimText)(` ${safeDetail}`))]));
|
|
39042
|
+
}
|
|
39043
|
+
return details.length === 0 ? undefined : joinStyled(details, `
|
|
39044
|
+
|
|
37643
39045
|
`);
|
|
37644
39046
|
}
|
|
37645
39047
|
function renderActivitySummary({ items, includeTimestamp }) {
|
|
37646
|
-
return new
|
|
39048
|
+
return new StyledText6([
|
|
37647
39049
|
...includeTimestamp ? [renderTimestampChunk(items[0].occurredAt)] : [],
|
|
37648
|
-
|
|
39050
|
+
dim5(fg7(PALETTE.dimText)(`Worked \xB7 ${items.length} step${items.length === 1 ? "" : "s"} \xB7 Ctrl+O details`))
|
|
37649
39051
|
]);
|
|
37650
39052
|
}
|
|
37651
39053
|
function renderCollapsedActivityStep({ item, inFlight }) {
|
|
37652
39054
|
const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
|
|
37653
|
-
return new
|
|
37654
|
-
|
|
37655
|
-
|
|
39055
|
+
return new StyledText6([
|
|
39056
|
+
fg7(color)(` ${glyph} `),
|
|
39057
|
+
dim5(fg7(PALETTE.dimText)(collapsedActivityTitle(item.card)))
|
|
37656
39058
|
]);
|
|
37657
39059
|
}
|
|
39060
|
+
function collapsedActivityTitle(card) {
|
|
39061
|
+
const attribution = card.attribution;
|
|
39062
|
+
if (!attribution)
|
|
39063
|
+
return card.title;
|
|
39064
|
+
if (attribution.status === "unresolved") {
|
|
39065
|
+
const name = childIdentityLabel(attribution.identity);
|
|
39066
|
+
return `Unattributed subagent \xB7 ${name}${attribution.activityTitle ? ` \xB7 ${attribution.activityTitle}` : ""}`;
|
|
39067
|
+
}
|
|
39068
|
+
const activityTitle = attribution.activityTitle ? ` \xB7 ${attribution.activityTitle}` : "";
|
|
39069
|
+
return `${lineagePathLabel(attribution.path)}${activityTitle}`;
|
|
39070
|
+
}
|
|
37658
39071
|
function renderSubmittedTimeline({ state, admittedSubmissions }) {
|
|
37659
39072
|
const submitted = admittedSubmissions.filter((submission) => submission.status === "submitted" && !state.transcript.some((item) => item.kind === "message" && item.messageId === submission.messageId));
|
|
37660
39073
|
const messages = submitted.map((submission) => {
|
|
37661
|
-
const attachments =
|
|
37662
|
-
const header = new
|
|
39074
|
+
const attachments = renderAttachmentSummary(submission.attachments);
|
|
39075
|
+
const header = new StyledText6([
|
|
37663
39076
|
renderRoleLabel({ label: "You", role: "human" }),
|
|
37664
|
-
|
|
39077
|
+
dim5(fg7(PALETTE.dimText)(" (CLI)"))
|
|
37665
39078
|
]);
|
|
37666
39079
|
return joinStyled([
|
|
37667
39080
|
header,
|
|
37668
39081
|
renderMessageBody({ text: submission.text, role: "human" }),
|
|
37669
|
-
...attachments ? [
|
|
39082
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
37670
39083
|
], `
|
|
37671
39084
|
`);
|
|
37672
39085
|
});
|
|
@@ -37676,20 +39089,20 @@ function renderSubmittedTimeline({ state, admittedSubmissions }) {
|
|
|
37676
39089
|
}
|
|
37677
39090
|
function renderWorkingIndicator({ frame, elapsedMs, mode }) {
|
|
37678
39091
|
if (mode === "stopping") {
|
|
37679
|
-
return new
|
|
37680
|
-
|
|
37681
|
-
|
|
39092
|
+
return new StyledText6([
|
|
39093
|
+
fg7(PALETTE.approvalQuestion)("\u23F9"),
|
|
39094
|
+
dim5(fg7(PALETTE.dimText)(` Stopping\u2026 ${formatElapsed(elapsedMs)}`))
|
|
37682
39095
|
]);
|
|
37683
39096
|
}
|
|
37684
39097
|
if (mode === "complete" || mode === "cancel") {
|
|
37685
|
-
return new
|
|
37686
|
-
|
|
37687
|
-
|
|
39098
|
+
return new StyledText6([
|
|
39099
|
+
fg7(PALETTE.approvalQuestion)(frame),
|
|
39100
|
+
dim5(fg7(PALETTE.dimText)(` ${lifecycleOperationPresentParticiple(mode)} the session\u2026`))
|
|
37688
39101
|
]);
|
|
37689
39102
|
}
|
|
37690
|
-
return new
|
|
37691
|
-
|
|
37692
|
-
|
|
39103
|
+
return new StyledText6([
|
|
39104
|
+
fg7(PALETTE.remyAccent)(frame),
|
|
39105
|
+
dim5(fg7(PALETTE.dimText)(` Working ${formatElapsed(elapsedMs)}`))
|
|
37693
39106
|
]);
|
|
37694
39107
|
}
|
|
37695
39108
|
function lifecycleOperationPresentParticiple(operation) {
|
|
@@ -37711,22 +39124,22 @@ function truncate2(value, width) {
|
|
|
37711
39124
|
}
|
|
37712
39125
|
function renderComposerStatus({ admittedSubmissions }) {
|
|
37713
39126
|
if (admittedSubmissions.length === 0)
|
|
37714
|
-
return new
|
|
39127
|
+
return new StyledText6([]);
|
|
37715
39128
|
const parts = admittedSubmissions.flatMap((submission) => {
|
|
37716
39129
|
if (submission.status === "submitted")
|
|
37717
39130
|
return [];
|
|
37718
|
-
const attachments = submission.attachments
|
|
37719
|
-
|
|
37720
|
-
|
|
37721
|
-
|
|
37722
|
-
|
|
37723
|
-
|
|
37724
|
-
`
|
|
37725
|
-
|
|
37726
|
-
|
|
37727
|
-
|
|
37728
|
-
|
|
37729
|
-
|
|
39131
|
+
const attachments = renderAttachmentSummary(submission.attachments);
|
|
39132
|
+
return [submission.status === "failed" ? joinStyled([
|
|
39133
|
+
new StyledText6([bold4(fg7(PALETTE.failure)("Failed: ")), fg7(PALETTE.bodyText)(submission.error ?? "")]),
|
|
39134
|
+
renderMessageBody({ text: submission.text, role: "human" }),
|
|
39135
|
+
...attachments.chunks.length > 0 ? [attachments] : [],
|
|
39136
|
+
new StyledText6([dim5(fg7(PALETTE.dimText)("Ctrl+R retries oldest failed submission."))])
|
|
39137
|
+
], `
|
|
39138
|
+
`) : joinStyled([
|
|
39139
|
+
new StyledText6([fg7(PALETTE.progress)("Admitting: "), fg7(PALETTE.bodyText)(submission.text)]),
|
|
39140
|
+
...attachments.chunks.length > 0 ? [attachments] : []
|
|
39141
|
+
], `
|
|
39142
|
+
`)];
|
|
37730
39143
|
});
|
|
37731
39144
|
return joinStyled(parts, `
|
|
37732
39145
|
|
|
@@ -37737,10 +39150,15 @@ async function createDefaultRenderer3() {
|
|
|
37737
39150
|
}
|
|
37738
39151
|
|
|
37739
39152
|
// src/tui/attachments.ts
|
|
39153
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
37740
39154
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
37741
|
-
import {
|
|
37742
|
-
import { homedir as homedir2 } from "os";
|
|
39155
|
+
import { mkdtemp, open as open4, readdir as readdir2, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
39156
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
37743
39157
|
import { basename as basename3, isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "path";
|
|
39158
|
+
var directoryArchiveMediaType = "application/gzip";
|
|
39159
|
+
var directoryArchiveSuffix = ".tar.gz";
|
|
39160
|
+
var maxPortableFilenameBytes = 255;
|
|
39161
|
+
var attachmentSizeLimitLabel = `${fileUploadMaxBytes / (1024 * 1024)} MiB`;
|
|
37744
39162
|
async function readClipboardImage() {
|
|
37745
39163
|
if (process.platform !== "darwin")
|
|
37746
39164
|
throw new Error("Clipboard image paste is supported on macOS only.");
|
|
@@ -37766,11 +39184,13 @@ async function attachPromptPaths({
|
|
|
37766
39184
|
text,
|
|
37767
39185
|
cwd,
|
|
37768
39186
|
excludedPaths = [],
|
|
37769
|
-
|
|
39187
|
+
selectedPaths = [],
|
|
39188
|
+
reserveAndUploadFile: reserveAndUploadFile2,
|
|
39189
|
+
onUploadStart
|
|
37770
39190
|
}) {
|
|
37771
39191
|
const excluded = new Set(excludedPaths);
|
|
37772
|
-
const
|
|
37773
|
-
for (const candidate of promptFilePaths({ text, cwd })) {
|
|
39192
|
+
const pathsToUpload = [];
|
|
39193
|
+
for (const candidate of promptFilePaths({ text, cwd, selectedPaths })) {
|
|
37774
39194
|
const { filePath } = candidate;
|
|
37775
39195
|
if (excluded.has(filePath))
|
|
37776
39196
|
continue;
|
|
@@ -37782,27 +39202,168 @@ async function attachPromptPaths({
|
|
|
37782
39202
|
continue;
|
|
37783
39203
|
throw error93;
|
|
37784
39204
|
}
|
|
37785
|
-
if (!file3.isFile() && candidate.explicit)
|
|
37786
|
-
throw new Error(`Prompt attachment path is not a regular file: ${filePath}`);
|
|
37787
|
-
if (!file3.isFile())
|
|
39205
|
+
if (!file3.isFile() && !file3.isDirectory() && candidate.explicit)
|
|
39206
|
+
throw new Error(`Prompt attachment path is not a regular file or directory: ${filePath}`);
|
|
39207
|
+
if (!file3.isFile() && !file3.isDirectory())
|
|
37788
39208
|
continue;
|
|
39209
|
+
pathsToUpload.push(filePath);
|
|
39210
|
+
}
|
|
39211
|
+
if (pathsToUpload.length === 0)
|
|
39212
|
+
return [];
|
|
39213
|
+
onUploadStart?.({ filenames: pathsToUpload.map((filePath) => basename3(filePath)) });
|
|
39214
|
+
const attachments = [];
|
|
39215
|
+
for (const filePath of pathsToUpload) {
|
|
39216
|
+
const uploaded = await uploadLocalAttachment({ filePath, reserveAndUploadFile: reserveAndUploadFile2 });
|
|
37789
39217
|
attachments.push({
|
|
37790
39218
|
id: randomUUID4(),
|
|
37791
|
-
|
|
37792
|
-
filename: basename3(filePath),
|
|
37793
|
-
sourcePath: filePath
|
|
39219
|
+
...uploaded
|
|
37794
39220
|
});
|
|
37795
39221
|
}
|
|
37796
39222
|
return attachments;
|
|
37797
39223
|
}
|
|
37798
|
-
function
|
|
39224
|
+
async function uploadLocalAttachment({
|
|
39225
|
+
filePath,
|
|
39226
|
+
reserveAndUploadFile: reserveAndUploadFile2
|
|
39227
|
+
}) {
|
|
39228
|
+
const sourcePath = resolve2(filePath);
|
|
39229
|
+
const source = await stat(sourcePath);
|
|
39230
|
+
if (source.isFile()) {
|
|
39231
|
+
if (source.size > fileUploadMaxBytes)
|
|
39232
|
+
throw oversizedFileAttachmentError(sourcePath);
|
|
39233
|
+
return {
|
|
39234
|
+
fileId: await reserveAndUploadFile2({ filePath: sourcePath }),
|
|
39235
|
+
filename: basename3(sourcePath),
|
|
39236
|
+
sourcePath
|
|
39237
|
+
};
|
|
39238
|
+
}
|
|
39239
|
+
if (!source.isDirectory())
|
|
39240
|
+
throw new Error(`Attachment path is not a regular file or directory: ${sourcePath}`);
|
|
39241
|
+
const archive = await createDirectoryArchive(sourcePath);
|
|
39242
|
+
try {
|
|
39243
|
+
return {
|
|
39244
|
+
fileId: await reserveAndUploadFile2({ filePath: archive.path, mediaType: directoryArchiveMediaType }),
|
|
39245
|
+
filename: archive.filename,
|
|
39246
|
+
sourcePath
|
|
39247
|
+
};
|
|
39248
|
+
} finally {
|
|
39249
|
+
await rm(archive.temporaryDirectory, { recursive: true, force: true });
|
|
39250
|
+
}
|
|
39251
|
+
}
|
|
39252
|
+
async function createDirectoryArchive(directoryPath) {
|
|
39253
|
+
const rootName = basename3(directoryPath) || "directory";
|
|
39254
|
+
const files = Object.create(null);
|
|
39255
|
+
await collectDirectoryFiles({
|
|
39256
|
+
directoryPath,
|
|
39257
|
+
archivePath: rootName,
|
|
39258
|
+
attachmentDirectoryPath: directoryPath,
|
|
39259
|
+
collectedByteSize: 0,
|
|
39260
|
+
files
|
|
39261
|
+
});
|
|
39262
|
+
if (Object.keys(files).length === 0)
|
|
39263
|
+
throw new Error(`Directory attachment contains no regular files: ${directoryPath}`);
|
|
39264
|
+
const temporaryDirectory = await mkdtemp(join5(tmpdir(), "remy-directory-attachment-"));
|
|
39265
|
+
const filename = directoryArchiveFilename(rootName);
|
|
39266
|
+
const archivePath = join5(temporaryDirectory, filename);
|
|
39267
|
+
try {
|
|
39268
|
+
await Bun.Archive.write(archivePath, files, { compress: "gzip" });
|
|
39269
|
+
if ((await stat(archivePath)).size > fileUploadMaxBytes)
|
|
39270
|
+
throw oversizedDirectoryArchiveError(directoryPath);
|
|
39271
|
+
} catch (error93) {
|
|
39272
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
39273
|
+
throw error93;
|
|
39274
|
+
}
|
|
39275
|
+
return { path: archivePath, filename, temporaryDirectory };
|
|
39276
|
+
}
|
|
39277
|
+
async function collectDirectoryFiles({
|
|
39278
|
+
directoryPath,
|
|
39279
|
+
archivePath,
|
|
39280
|
+
attachmentDirectoryPath,
|
|
39281
|
+
collectedByteSize,
|
|
39282
|
+
files
|
|
39283
|
+
}) {
|
|
39284
|
+
const entries = await readdir2(directoryPath, { withFileTypes: true });
|
|
39285
|
+
entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
39286
|
+
let byteSize = collectedByteSize;
|
|
39287
|
+
for (const entry of entries) {
|
|
39288
|
+
const entryPath = join5(directoryPath, entry.name);
|
|
39289
|
+
const entryArchivePath = `${archivePath}/${entry.name}`;
|
|
39290
|
+
if (entry.isDirectory()) {
|
|
39291
|
+
byteSize = await collectDirectoryFiles({
|
|
39292
|
+
directoryPath: entryPath,
|
|
39293
|
+
archivePath: entryArchivePath,
|
|
39294
|
+
attachmentDirectoryPath,
|
|
39295
|
+
collectedByteSize: byteSize,
|
|
39296
|
+
files
|
|
39297
|
+
});
|
|
39298
|
+
continue;
|
|
39299
|
+
}
|
|
39300
|
+
if (entry.isFile()) {
|
|
39301
|
+
const fileHandle = await open4(entryPath, "r");
|
|
39302
|
+
try {
|
|
39303
|
+
const entryStat = await fileHandle.stat();
|
|
39304
|
+
if (!entryStat.isFile())
|
|
39305
|
+
throw new Error(`Directory attachment contains a non-regular entry: ${entryPath}`);
|
|
39306
|
+
const remainingBytes = fileUploadMaxBytes - byteSize;
|
|
39307
|
+
if (entryStat.size > remainingBytes)
|
|
39308
|
+
throw oversizedDirectoryAttachmentError(attachmentDirectoryPath);
|
|
39309
|
+
const readResult = await readFileHandleWithLimit({
|
|
39310
|
+
fileHandle,
|
|
39311
|
+
initialByteSize: entryStat.size,
|
|
39312
|
+
maxBytes: remainingBytes
|
|
39313
|
+
});
|
|
39314
|
+
if (readResult.status === "limit_exceeded")
|
|
39315
|
+
throw oversizedDirectoryAttachmentError(attachmentDirectoryPath);
|
|
39316
|
+
files[entryArchivePath] = readResult.bytes;
|
|
39317
|
+
byteSize += readResult.bytes.byteLength;
|
|
39318
|
+
} finally {
|
|
39319
|
+
await fileHandle.close();
|
|
39320
|
+
}
|
|
39321
|
+
continue;
|
|
39322
|
+
}
|
|
39323
|
+
if (entry.isSymbolicLink())
|
|
39324
|
+
throw new Error(`Directory attachment contains a symbolic link: ${entryPath}`);
|
|
39325
|
+
throw new Error(`Directory attachment contains a non-regular entry: ${entryPath}`);
|
|
39326
|
+
}
|
|
39327
|
+
return byteSize;
|
|
39328
|
+
}
|
|
39329
|
+
function oversizedFileAttachmentError(filePath) {
|
|
39330
|
+
return new Error(`Attachment file ${JSON.stringify(filePath)} is larger than ${attachmentSizeLimitLabel}. Choose a file no larger than ${attachmentSizeLimitLabel}.`);
|
|
39331
|
+
}
|
|
39332
|
+
function oversizedDirectoryAttachmentError(directoryPath) {
|
|
39333
|
+
return new Error(`Attachment directory ${JSON.stringify(directoryPath)} contains more than ${attachmentSizeLimitLabel} of files. Choose fewer or smaller files.`);
|
|
39334
|
+
}
|
|
39335
|
+
function oversizedDirectoryArchiveError(directoryPath) {
|
|
39336
|
+
return new Error(`Attachment directory ${JSON.stringify(directoryPath)} produces an archive larger than ${attachmentSizeLimitLabel}. Choose fewer or smaller files.`);
|
|
39337
|
+
}
|
|
39338
|
+
function directoryArchiveFilename(directoryName) {
|
|
39339
|
+
const stem = truncateUtf8(directoryName, maxPortableFilenameBytes - Buffer2.byteLength(directoryArchiveSuffix)) || "directory";
|
|
39340
|
+
return `${stem}${directoryArchiveSuffix}`;
|
|
39341
|
+
}
|
|
39342
|
+
function truncateUtf8(value, maxBytes) {
|
|
39343
|
+
let result = "";
|
|
39344
|
+
let resultBytes = 0;
|
|
39345
|
+
for (const character of value) {
|
|
39346
|
+
const characterBytes = Buffer2.byteLength(character);
|
|
39347
|
+
if (resultBytes + characterBytes > maxBytes)
|
|
39348
|
+
break;
|
|
39349
|
+
result += character;
|
|
39350
|
+
resultBytes += characterBytes;
|
|
39351
|
+
}
|
|
39352
|
+
return result;
|
|
39353
|
+
}
|
|
39354
|
+
function promptFilePaths({ text, cwd, selectedPaths }) {
|
|
37799
39355
|
const paths = new Map;
|
|
39356
|
+
for (const selectedPath of selectedPaths)
|
|
39357
|
+
paths.set(isAbsolute2(selectedPath) ? selectedPath : resolve2(cwd, selectedPath), true);
|
|
37800
39358
|
for (const rawToken of text.split(/\s+/)) {
|
|
37801
39359
|
const token = rawToken.replace(/^[([{'"`]+/, "").replace(/[),.;:!?\]}"`]+$/, "");
|
|
37802
39360
|
const pathText = token.startsWith("@") ? token.slice(1) : token;
|
|
37803
39361
|
if (!pathText)
|
|
37804
39362
|
continue;
|
|
37805
39363
|
const explicit = token.startsWith("@") || pathText.startsWith("./") || pathText.startsWith("../") || isAbsolute2(pathText) || pathText.startsWith("~/");
|
|
39364
|
+
const pathShaped = explicit || pathText.includes("/") || pathText.includes("\\") || pathText.includes(".");
|
|
39365
|
+
if (!pathShaped)
|
|
39366
|
+
continue;
|
|
37806
39367
|
const expandedPath = pathText.startsWith("~/") ? join5(homedir2(), pathText.slice(2)) : pathText;
|
|
37807
39368
|
const filePath = isAbsolute2(expandedPath) ? expandedPath : resolve2(cwd, expandedPath);
|
|
37808
39369
|
paths.set(filePath, paths.get(filePath) || explicit);
|
|
@@ -37825,7 +39386,7 @@ function isPng(bytes) {
|
|
|
37825
39386
|
}
|
|
37826
39387
|
|
|
37827
39388
|
// src/tui/remy-splash.ts
|
|
37828
|
-
import { bold as bold5, BoxRenderable as BoxRenderable5, fg as
|
|
39389
|
+
import { bold as bold5, BoxRenderable as BoxRenderable5, fg as fg8, NativeImage, StyledText as StyledText7, TextRenderable as TextRenderable5, bg as bg5 } from "@opentui/core";
|
|
37829
39390
|
// src/tui/remy-mark.ts
|
|
37830
39391
|
var remyPixelFieldSource = new URL("../assets/remy-pixel-field.png", import.meta.url);
|
|
37831
39392
|
function shouldShowRemySplash({ width, height }) {
|
|
@@ -37844,7 +39405,7 @@ var compactMarkRows = 9;
|
|
|
37844
39405
|
var compactMinWidth = 48;
|
|
37845
39406
|
var compactMinHeight = 20;
|
|
37846
39407
|
var markBrightnessGain = 4.2;
|
|
37847
|
-
var remyCliVersion = "1.
|
|
39408
|
+
var remyCliVersion = "1.14.0";
|
|
37848
39409
|
async function showRemySplash({
|
|
37849
39410
|
createRenderer = createRemyRenderer,
|
|
37850
39411
|
durationMs = splashDurationMs,
|
|
@@ -37902,7 +39463,7 @@ async function showRemySplash({
|
|
|
37902
39463
|
});
|
|
37903
39464
|
const approvalCopy = new TextRenderable5(renderer, { content: "" });
|
|
37904
39465
|
const metadata = new TextRenderable5(renderer, {
|
|
37905
|
-
content: new
|
|
39466
|
+
content: new StyledText7([fg8(PALETTE.dimText)(`v${remyCliVersion} \xB7 Tela\xAE`)])
|
|
37906
39467
|
});
|
|
37907
39468
|
const field = await loadPixelFieldUntilAbort({ signal });
|
|
37908
39469
|
if (!field || signal?.aborted)
|
|
@@ -38000,32 +39561,32 @@ function createSplashController({
|
|
|
38000
39561
|
};
|
|
38001
39562
|
}
|
|
38002
39563
|
function renderBootstrapActions({ state, frame }) {
|
|
38003
|
-
return new
|
|
39564
|
+
return new StyledText7([
|
|
38004
39565
|
actionText({ label: state.authenticatedEmail ? `Authenticated as ${state.authenticatedEmail}` : "Checking authentication...", status: state.authentication, frame }),
|
|
38005
|
-
|
|
39566
|
+
fg8(PALETTE.dimText)(`
|
|
38006
39567
|
`),
|
|
38007
39568
|
actionText({ label: "Load sessions", status: state.loadingSessions, frame })
|
|
38008
39569
|
]);
|
|
38009
39570
|
}
|
|
38010
39571
|
function actionText({ label, status, frame }) {
|
|
38011
39572
|
if (status === "complete")
|
|
38012
|
-
return
|
|
39573
|
+
return fg8(PALETTE.statusCompleted)(`\u2713 ${label === "Load sessions" ? "Sessions loaded!" : label}`);
|
|
38013
39574
|
if (status === "needed")
|
|
38014
|
-
return
|
|
39575
|
+
return fg8(PALETTE.approvalQuestion)("\u26A0 Authentication needed");
|
|
38015
39576
|
if (status === "active")
|
|
38016
|
-
return
|
|
38017
|
-
return
|
|
39577
|
+
return fg8(PALETTE.progress)(`${splashSpinnerFrames[frame % splashSpinnerFrames.length]} ${label === "Load sessions" ? "Loading sessions..." : label}`);
|
|
39578
|
+
return fg8(PALETTE.dimText)(`\u25CB ${label}`);
|
|
38018
39579
|
}
|
|
38019
39580
|
function renderAuthenticationBand({ state, frame }) {
|
|
38020
39581
|
if (!state.waitingForAuthentication)
|
|
38021
|
-
return new
|
|
39582
|
+
return new StyledText7([]);
|
|
38022
39583
|
const spinner = splashSpinnerFrames[frame % splashSpinnerFrames.length];
|
|
38023
|
-
const url3 = state.verificationUrl ? [
|
|
38024
|
-
Verification URL: `), bold5(
|
|
38025
|
-
return new
|
|
38026
|
-
|
|
39584
|
+
const url3 = state.verificationUrl ? [fg8(PALETTE.dimText)(`
|
|
39585
|
+
Verification URL: `), bold5(fg8(PALETTE.humanAccent)(state.verificationUrl))] : [];
|
|
39586
|
+
return new StyledText7([
|
|
39587
|
+
fg8(PALETTE.bodyText)("Continue authentication in your browser."),
|
|
38027
39588
|
...url3,
|
|
38028
|
-
|
|
39589
|
+
fg8(PALETTE.dimText)(`
|
|
38029
39590
|
${spinner} Waiting for approval \xB7 q / esc cancel`)
|
|
38030
39591
|
]);
|
|
38031
39592
|
}
|
|
@@ -38100,7 +39661,7 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
38100
39661
|
const flush = () => {
|
|
38101
39662
|
if (runLength === 0)
|
|
38102
39663
|
return;
|
|
38103
|
-
chunks.push(
|
|
39664
|
+
chunks.push(fg8(grey(runTop))(bg5(grey(runBottom))(upperHalfBlock.repeat(runLength))));
|
|
38104
39665
|
runLength = 0;
|
|
38105
39666
|
};
|
|
38106
39667
|
for (let column = 0;column < columns; column++) {
|
|
@@ -38117,10 +39678,10 @@ function renderMark(field, frame, rows = markRows) {
|
|
|
38117
39678
|
}
|
|
38118
39679
|
flush();
|
|
38119
39680
|
if (row < rows - 1)
|
|
38120
|
-
chunks.push(
|
|
39681
|
+
chunks.push(fg8("#000000")(`
|
|
38121
39682
|
`));
|
|
38122
39683
|
}
|
|
38123
|
-
return new
|
|
39684
|
+
return new StyledText7(chunks);
|
|
38124
39685
|
}
|
|
38125
39686
|
function shimmerBrightness(field, column, sampleRow, columns, sampleRows, center) {
|
|
38126
39687
|
const x0 = Math.floor(column / columns * field.width);
|
|
@@ -38140,8 +39701,8 @@ function grey(value) {
|
|
|
38140
39701
|
return `#${hex5}${hex5}${hex5}`;
|
|
38141
39702
|
}
|
|
38142
39703
|
function renderSplashTitle() {
|
|
38143
|
-
return new
|
|
38144
|
-
bold5(
|
|
39704
|
+
return new StyledText7([
|
|
39705
|
+
bold5(fg8(PALETTE.bodyText)("Remy CLI"))
|
|
38145
39706
|
]);
|
|
38146
39707
|
}
|
|
38147
39708
|
|
|
@@ -38476,6 +40037,9 @@ function createRepositoryLister({ client }) {
|
|
|
38476
40037
|
function createRepositorySuggester({ client }) {
|
|
38477
40038
|
return async (input) => await suggestRemoteRepositories({ client, input });
|
|
38478
40039
|
}
|
|
40040
|
+
function createBranchSuggester({ client }) {
|
|
40041
|
+
return async (input) => await suggestRemoteBranches({ client, input });
|
|
40042
|
+
}
|
|
38479
40043
|
function parseOptionalStringFlag(value, name) {
|
|
38480
40044
|
if (value === undefined)
|
|
38481
40045
|
return;
|
|
@@ -38619,48 +40183,98 @@ async function dashboard({
|
|
|
38619
40183
|
const openDashboardTui = dependencies.openDashboardTui ?? createDashboardTui;
|
|
38620
40184
|
const pages = [initialSessions];
|
|
38621
40185
|
let pageIndex = 0;
|
|
38622
|
-
let
|
|
40186
|
+
let filters = { statuses: [], creators: [] };
|
|
40187
|
+
let authors;
|
|
40188
|
+
let activeDashboardGeneration = 0;
|
|
38623
40189
|
function holdTransitionScreen() {
|
|
38624
40190
|
showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
|
|
38625
40191
|
}
|
|
38626
|
-
|
|
38627
|
-
|
|
38628
|
-
|
|
38629
|
-
|
|
38630
|
-
|
|
38631
|
-
|
|
38632
|
-
|
|
40192
|
+
function createDashboardPageLoader({ generation }) {
|
|
40193
|
+
const loadingPages = new Map;
|
|
40194
|
+
let pageLoadTail = Promise.resolve();
|
|
40195
|
+
return async (input) => {
|
|
40196
|
+
const requestKey = "filters" in input ? `filters:${JSON.stringify(input.filters)}` : input.target;
|
|
40197
|
+
const matchingPageLoad = loadingPages.get(requestKey);
|
|
40198
|
+
if (matchingPageLoad)
|
|
40199
|
+
return await matchingPageLoad;
|
|
40200
|
+
const pendingPageLoad = pageLoadTail;
|
|
40201
|
+
const pageLoad = pendingPageLoad.then(async () => {
|
|
40202
|
+
if (generation !== activeDashboardGeneration)
|
|
40203
|
+
return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
|
|
40204
|
+
if ("filters" in input) {
|
|
40205
|
+
const nextFilters = input.filters;
|
|
40206
|
+
const first = toDashboardPage(await operations.listSessions({
|
|
40207
|
+
client: operations.client,
|
|
40208
|
+
limit: 20,
|
|
40209
|
+
...nextFilters.statuses.length > 0 ? { statuses: nextFilters.statuses } : {},
|
|
40210
|
+
...nextFilters.creators.length > 0 ? { creatorUserIds: nextFilters.creators.map((creator) => creator.id) } : {},
|
|
40211
|
+
signal: input.signal
|
|
40212
|
+
}));
|
|
40213
|
+
if (generation !== activeDashboardGeneration)
|
|
40214
|
+
return first;
|
|
40215
|
+
filters = nextFilters;
|
|
40216
|
+
pages.splice(0, pages.length, first);
|
|
40217
|
+
pageIndex = 0;
|
|
40218
|
+
return first;
|
|
40219
|
+
}
|
|
40220
|
+
const { target, signal } = input;
|
|
40221
|
+
if (target === "current") {
|
|
40222
|
+
const after = pageIndex > 0 ? pages[pageIndex - 1]?.nextCursor ?? undefined : undefined;
|
|
40223
|
+
const refreshed = toDashboardPage(await operations.listSessions({
|
|
40224
|
+
client: operations.client,
|
|
40225
|
+
limit: 20,
|
|
40226
|
+
after,
|
|
40227
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40228
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
40229
|
+
signal
|
|
40230
|
+
}));
|
|
40231
|
+
if (generation !== activeDashboardGeneration)
|
|
40232
|
+
return refreshed;
|
|
40233
|
+
pages.splice(pageIndex, pages.length - pageIndex, refreshed);
|
|
40234
|
+
return { ...refreshed, canGoPrevious: pageIndex > 0 };
|
|
40235
|
+
}
|
|
40236
|
+
if (target === "previous") {
|
|
40237
|
+
pageIndex = Math.max(0, pageIndex - 1);
|
|
40238
|
+
return { ...pages[pageIndex], canGoPrevious: pageIndex > 0 };
|
|
40239
|
+
}
|
|
40240
|
+
const current = pages[pageIndex];
|
|
40241
|
+
if (!current.hasMore || !current.nextCursor)
|
|
40242
|
+
return { ...current, canGoPrevious: pageIndex > 0 };
|
|
40243
|
+
const next = toDashboardPage(await operations.listSessions({
|
|
38633
40244
|
client: operations.client,
|
|
38634
40245
|
limit: 20,
|
|
38635
|
-
after,
|
|
40246
|
+
after: current.nextCursor,
|
|
40247
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40248
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
38636
40249
|
signal
|
|
38637
40250
|
}));
|
|
38638
|
-
|
|
38639
|
-
|
|
38640
|
-
|
|
38641
|
-
|
|
38642
|
-
pageIndex
|
|
38643
|
-
return { ...
|
|
40251
|
+
if (generation !== activeDashboardGeneration)
|
|
40252
|
+
return next;
|
|
40253
|
+
pages.splice(pageIndex + 1);
|
|
40254
|
+
pages.push(next);
|
|
40255
|
+
pageIndex += 1;
|
|
40256
|
+
return { ...next, canGoPrevious: pageIndex > 0 };
|
|
40257
|
+
});
|
|
40258
|
+
loadingPages.set(requestKey, pageLoad);
|
|
40259
|
+
pageLoadTail = pageLoad.then(() => {
|
|
40260
|
+
return;
|
|
40261
|
+
}, () => {
|
|
40262
|
+
return;
|
|
40263
|
+
});
|
|
40264
|
+
try {
|
|
40265
|
+
return await pageLoad;
|
|
40266
|
+
} finally {
|
|
40267
|
+
if (loadingPages.get(requestKey) === pageLoad)
|
|
40268
|
+
loadingPages.delete(requestKey);
|
|
38644
40269
|
}
|
|
38645
|
-
|
|
38646
|
-
|
|
38647
|
-
|
|
38648
|
-
|
|
38649
|
-
|
|
38650
|
-
|
|
38651
|
-
|
|
38652
|
-
|
|
38653
|
-
}));
|
|
38654
|
-
pages.splice(pageIndex + 1);
|
|
38655
|
-
pages.push(next);
|
|
38656
|
-
pageIndex += 1;
|
|
38657
|
-
return { ...next, canGoPrevious: pageIndex > 0 };
|
|
38658
|
-
})();
|
|
38659
|
-
try {
|
|
38660
|
-
return await loadingPage;
|
|
38661
|
-
} finally {
|
|
38662
|
-
loadingPage = undefined;
|
|
38663
|
-
}
|
|
40270
|
+
};
|
|
40271
|
+
}
|
|
40272
|
+
async function loadDashboardAuthors() {
|
|
40273
|
+
if (authors)
|
|
40274
|
+
return authors;
|
|
40275
|
+
const loadedAuthors = await collectAllUsers({ listUsers: operations.listUsers, signal: dependencies.abortSignal });
|
|
40276
|
+
authors = loadedAuthors;
|
|
40277
|
+
return loadedAuthors;
|
|
38664
40278
|
}
|
|
38665
40279
|
let firstOpen = true;
|
|
38666
40280
|
holdTransitionScreen();
|
|
@@ -38669,17 +40283,30 @@ async function dashboard({
|
|
|
38669
40283
|
pages.splice(0, pages.length, toDashboardPage(await operations.listSessions({
|
|
38670
40284
|
client: operations.client,
|
|
38671
40285
|
limit: 20,
|
|
40286
|
+
...filters.statuses.length > 0 ? { statuses: filters.statuses } : {},
|
|
40287
|
+
...filters.creators.length > 0 ? { creatorUserIds: filters.creators.map((creator) => creator.id) } : {},
|
|
38672
40288
|
signal: dependencies.abortSignal
|
|
38673
40289
|
})));
|
|
38674
40290
|
pageIndex = 0;
|
|
38675
40291
|
}
|
|
38676
40292
|
firstOpen = false;
|
|
40293
|
+
const dashboardGeneration = ++activeDashboardGeneration;
|
|
40294
|
+
const loadDashboardPage = createDashboardPageLoader({ generation: dashboardGeneration });
|
|
38677
40295
|
const action = await renderRemyView({
|
|
38678
40296
|
open: async () => await openDashboardTui({
|
|
38679
40297
|
initialPage: pages[0],
|
|
40298
|
+
initialFilters: filters,
|
|
40299
|
+
loadAuthors: loadDashboardAuthors,
|
|
38680
40300
|
loadPage: loadDashboardPage
|
|
38681
40301
|
}),
|
|
38682
|
-
waitForAction: async (dashboardTui) =>
|
|
40302
|
+
waitForAction: async (dashboardTui) => {
|
|
40303
|
+
try {
|
|
40304
|
+
return await awaitWithAbort({ value: dashboardTui.waitForAction(), abortSignal: dependencies.abortSignal });
|
|
40305
|
+
} finally {
|
|
40306
|
+
if (activeDashboardGeneration === dashboardGeneration)
|
|
40307
|
+
activeDashboardGeneration += 1;
|
|
40308
|
+
}
|
|
40309
|
+
}
|
|
38683
40310
|
});
|
|
38684
40311
|
if (!action)
|
|
38685
40312
|
return 0;
|
|
@@ -38722,12 +40349,12 @@ async function prepareDashboardBootstrap({
|
|
|
38722
40349
|
return;
|
|
38723
40350
|
});
|
|
38724
40351
|
onLoadingSessions?.();
|
|
38725
|
-
const
|
|
40352
|
+
const initialSessionPage = await resolvedOperations.listSessions({
|
|
38726
40353
|
client: resolvedOperations.client,
|
|
38727
40354
|
limit: 20,
|
|
38728
40355
|
signal: dependencies.abortSignal
|
|
38729
|
-
})
|
|
38730
|
-
return { operations: resolvedOperations, repositories, initialSessions };
|
|
40356
|
+
});
|
|
40357
|
+
return { operations: resolvedOperations, repositories, initialSessions: toDashboardPage(initialSessionPage) };
|
|
38731
40358
|
}
|
|
38732
40359
|
async function prepareDashboardWithStartupSplash({
|
|
38733
40360
|
dependencies,
|
|
@@ -38918,11 +40545,15 @@ async function createNewSession({
|
|
|
38918
40545
|
throw new Error("All selected repositories must belong to the same GitHub installation.");
|
|
38919
40546
|
const fileIds = [];
|
|
38920
40547
|
for (const attachment of command.attachments) {
|
|
38921
|
-
|
|
38922
|
-
client: operations.client,
|
|
40548
|
+
const uploaded = await uploadLocalAttachment({
|
|
38923
40549
|
filePath: attachment,
|
|
38924
|
-
|
|
38925
|
-
|
|
40550
|
+
reserveAndUploadFile: async (input) => await operations.reserveAndUploadFile({
|
|
40551
|
+
...input,
|
|
40552
|
+
client: operations.client,
|
|
40553
|
+
idempotencyKey: randomUUID5()
|
|
40554
|
+
})
|
|
40555
|
+
});
|
|
40556
|
+
fileIds.push(uploaded.fileId);
|
|
38926
40557
|
}
|
|
38927
40558
|
const created = await operations.createCodexSession({
|
|
38928
40559
|
client: operations.client,
|
|
@@ -38996,6 +40627,7 @@ async function createFromTuiDraft({
|
|
|
38996
40627
|
input: {
|
|
38997
40628
|
installationId: draft.githubInstallationId,
|
|
38998
40629
|
repositoryIds: draft.repositories.map((repository) => repository.id),
|
|
40630
|
+
repositoryBranchOverrides: draft.repositoryBranchOverrides ?? [],
|
|
38999
40631
|
prompt: draft.prompt,
|
|
39000
40632
|
fileIds: draft.attachments.map((attachment) => attachment.fileId),
|
|
39001
40633
|
model: draft.model,
|
|
@@ -39018,10 +40650,11 @@ async function openTuiNewSessionWizard({
|
|
|
39018
40650
|
repositories,
|
|
39019
40651
|
reloadRepositories,
|
|
39020
40652
|
suggestRepositories: operations.suggestRepositories,
|
|
40653
|
+
suggestBranches: operations.suggestBranches,
|
|
39021
40654
|
initialDraft,
|
|
39022
40655
|
initialError,
|
|
39023
40656
|
readClipboardImage,
|
|
39024
|
-
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory:
|
|
40657
|
+
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
39025
40658
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
39026
40659
|
}),
|
|
39027
40660
|
waitForAction: async (wizard) => await awaitWithAbort({ value: wizard.waitForDraft(), abortSignal: dependencies.abortSignal })
|
|
@@ -39030,15 +40663,20 @@ async function openTuiNewSessionWizard({
|
|
|
39030
40663
|
async function uploadPromptPaths({
|
|
39031
40664
|
operations,
|
|
39032
40665
|
text,
|
|
39033
|
-
excludedPaths
|
|
40666
|
+
excludedPaths,
|
|
40667
|
+
selectedPaths,
|
|
40668
|
+
onUploadStart
|
|
39034
40669
|
}) {
|
|
39035
40670
|
return await attachPromptPaths({
|
|
39036
40671
|
text,
|
|
39037
40672
|
excludedPaths,
|
|
40673
|
+
selectedPaths,
|
|
40674
|
+
onUploadStart,
|
|
39038
40675
|
cwd: process.cwd(),
|
|
39039
|
-
reserveAndUploadFile: async ({ filePath }) => await operations.reserveAndUploadFile({
|
|
40676
|
+
reserveAndUploadFile: async ({ filePath, mediaType }) => await operations.reserveAndUploadFile({
|
|
39040
40677
|
client: operations.client,
|
|
39041
40678
|
filePath,
|
|
40679
|
+
...mediaType ? { mediaType } : {},
|
|
39042
40680
|
idempotencyKey: randomUUID5()
|
|
39043
40681
|
})
|
|
39044
40682
|
});
|
|
@@ -39096,7 +40734,7 @@ async function runAttachedSession({
|
|
|
39096
40734
|
...activeMessageId ? { activeMessageId } : {},
|
|
39097
40735
|
environment: dependencies.environment,
|
|
39098
40736
|
getSession: async ({ sessionId: id }) => await operations.getSession({ client: operations.client, sessionId: id }),
|
|
39099
|
-
listSessionEvents: async ({ sessionId: id, limit, after }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after }),
|
|
40737
|
+
listSessionEvents: async ({ sessionId: id, limit, after, signal }) => await operations.listSessionEvents({ client: operations.client, sessionId: id, limit, after, signal }),
|
|
39100
40738
|
openEventStream: ({ sessionId: id, lastRetainedEventId, signal, onSynchronized }) => operations.streamSessionEvents({ client: operations.client, sessionId: id, lastRetainedEventId, signal, onSynchronized })
|
|
39101
40739
|
});
|
|
39102
40740
|
const interactive = !noTui && !json3 && isInteractiveTerminal(dependencies);
|
|
@@ -39135,8 +40773,14 @@ async function runAttachedSession({
|
|
|
39135
40773
|
});
|
|
39136
40774
|
}
|
|
39137
40775
|
const startPromise = controller.start(start);
|
|
39138
|
-
await Promise.race([
|
|
40776
|
+
await Promise.race([
|
|
40777
|
+
controller.waitUntilReady(),
|
|
40778
|
+
startPromise,
|
|
40779
|
+
...aborted3 ? [aborted3] : []
|
|
40780
|
+
]);
|
|
39139
40781
|
if (interactive) {
|
|
40782
|
+
if (dependencies.abortSignal?.aborted)
|
|
40783
|
+
throw dependencies.abortSignal.reason ?? new Error("interrupted");
|
|
39140
40784
|
const openSessionTui = dependencies.openSessionTui ?? createSessionTui;
|
|
39141
40785
|
let composerSnapshot;
|
|
39142
40786
|
const holdTransitionScreen = () => showRemyTransitionScreen({ write: (chunk) => dependencies.output.writeStdout(chunk) });
|
|
@@ -39173,7 +40817,7 @@ async function runAttachedSession({
|
|
|
39173
40817
|
controller.updateDetail(detail);
|
|
39174
40818
|
},
|
|
39175
40819
|
readClipboardImage,
|
|
39176
|
-
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory:
|
|
40820
|
+
savePastedImage: async (input) => await writePastedImage({ ...input, temporaryDirectory: tmpdir2() }),
|
|
39177
40821
|
attachPromptPaths: async (input) => await uploadPromptPaths({ operations, ...input })
|
|
39178
40822
|
}),
|
|
39179
40823
|
waitForAction: async (tui) => await Promise.race([tui.waitForAction(), aborted3])
|
|
@@ -39282,7 +40926,9 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
|
|
|
39282
40926
|
return {
|
|
39283
40927
|
client: dependencies.sessionClient,
|
|
39284
40928
|
listRepositories: dependencies.listRepositories ?? (async () => await Promise.reject(new Error("Missing remote repository operation."))),
|
|
40929
|
+
listUsers: dependencies.listUsers ?? (async (input) => await listRemoteUsers({ client: dependencies.sessionClient, ...input })),
|
|
39285
40930
|
suggestRepositories: dependencies.suggestRepositories ?? (async () => await Promise.reject(new Error("Missing remote repository suggestions operation."))),
|
|
40931
|
+
suggestBranches: dependencies.suggestBranches ?? (async () => await Promise.reject(new Error("Missing remote branch suggestions operation."))),
|
|
39286
40932
|
reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
|
|
39287
40933
|
createCodexSession: dependencies.createCodexSession ?? createCodexSession,
|
|
39288
40934
|
appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
|
|
@@ -39300,7 +40946,9 @@ async function createSessionOperations(dependencies, { onAuthenticated } = {}) {
|
|
|
39300
40946
|
return {
|
|
39301
40947
|
client,
|
|
39302
40948
|
listRepositories: dependencies.listRepositories ?? createRepositoryLister({ client }),
|
|
40949
|
+
listUsers: dependencies.listUsers ?? (async (input) => await listRemoteUsers({ client, ...input })),
|
|
39303
40950
|
suggestRepositories: dependencies.suggestRepositories ?? createRepositorySuggester({ client }),
|
|
40951
|
+
suggestBranches: dependencies.suggestBranches ?? createBranchSuggester({ client }),
|
|
39304
40952
|
reserveAndUploadFile: dependencies.reserveAndUploadFile ?? reserveAndUploadFile,
|
|
39305
40953
|
createCodexSession: dependencies.createCodexSession ?? createCodexSession,
|
|
39306
40954
|
appendSessionMessage: dependencies.appendSessionMessage ?? appendSessionMessage,
|
|
@@ -39342,6 +40990,20 @@ async function collectAllRepositories({
|
|
|
39342
40990
|
after = page.nextCursor;
|
|
39343
40991
|
}
|
|
39344
40992
|
}
|
|
40993
|
+
async function collectAllUsers({
|
|
40994
|
+
listUsers,
|
|
40995
|
+
signal
|
|
40996
|
+
}) {
|
|
40997
|
+
const users = [];
|
|
40998
|
+
let after;
|
|
40999
|
+
while (true) {
|
|
41000
|
+
const page = await listUsers({ limit: 100, after, signal });
|
|
41001
|
+
users.push(...page.data);
|
|
41002
|
+
if (!page.hasMore || !page.nextCursor)
|
|
41003
|
+
return users;
|
|
41004
|
+
after = page.nextCursor;
|
|
41005
|
+
}
|
|
41006
|
+
}
|
|
39345
41007
|
function resolveSessionCachePathForCommand({ dependencies, sessionId }) {
|
|
39346
41008
|
const stateBase = dependencies.environment.XDG_STATE_HOME ?? (dependencies.environment.HOME ? join6(dependencies.environment.HOME, ".local/state") : undefined);
|
|
39347
41009
|
if (!stateBase)
|
|
@@ -39468,7 +41130,7 @@ Options:
|
|
|
39468
41130
|
--installation <id> Select a GitHub installation
|
|
39469
41131
|
--model <name> Select an agent model
|
|
39470
41132
|
--reasoning-effort <low|medium|high|xhigh> Select agent reasoning effort
|
|
39471
|
-
--attach <path> Upload a file; repeat
|
|
41133
|
+
--attach <path> Upload a file or directory (20 MiB max); repeat as needed
|
|
39472
41134
|
--no-tui Do not open the interactive terminal view
|
|
39473
41135
|
--json Write session updates as JSON
|
|
39474
41136
|
--help, -h Show this help
|
|
@@ -39617,7 +41279,7 @@ async function removeCommittedTokenRecoveryArtifacts({
|
|
|
39617
41279
|
const directory = dirname6(tokenPath);
|
|
39618
41280
|
let entries;
|
|
39619
41281
|
try {
|
|
39620
|
-
entries = await
|
|
41282
|
+
entries = await readdir3(directory);
|
|
39621
41283
|
} catch (error93) {
|
|
39622
41284
|
if (isMissingFileError3(error93))
|
|
39623
41285
|
return;
|