@meistrari/remy-cli 1.19.0-rc.0 → 1.19.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -1
  2. package/dist/remy.js +501 -104
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -62,6 +62,10 @@ Subagent work appears as named **Subagent · <name>** activity rather than messa
62
62
 
63
63
  Published files and Tela Pages appear in the timeline. File artifacts include their full Web preview URL; Tela Page rows include their title and canonical URL so you can open them directly from the terminal. Remy does not request or expose the Web-only temporary iframe capability for Tela Pages.
64
64
 
65
+ **Open links:** Ctrl+click an HTTP or HTTPS link in the conversation, including Markdown links and expanded activity links. Ordinary clicks and drag-to-copy keep their existing behavior. Terminals may reserve that gesture or use their own modifier for hyperlinks; `/links` or `Ctrl+L` provides a keyboard alternative. Select with arrows or `j`/`k`, press `Enter` to open the browser, or `c` to copy the complete URL. Browser opening uses the machine running Remy.
66
+
67
+ **Download files:** `/files` or `Ctrl+F` opens a session file picker, newest first, containing Remy’s published artifacts and attachments sent by everyone in that session, with author/source labels. Repeated updates to an artifact appear once, using its newest title and URL. Select with arrows or `j`/`k`; `Enter` (or `d`) downloads to `~/Downloads`, creating that directory if needed. Remy shows progress and the full saved path. Existing files are preserved: another `report.txt` becomes `report (1).txt`. Press `o` for the Web preview or `c` to copy its URL; previews use your browser sign-in. Tela Pages open their canonical URL instead of downloading. The picker also works in closed sessions. `Esc` cancels an unfinished download and returns to the conversation with your draft intact, without stopping Remy. Errors stay visible for an explicit retry, which requests fresh download authorization. Files are saved without executing or extracting them. This picker covers published files and message attachments; embedded tool-output media remains available through **View event in Web**.
68
+
65
69
  System observations appear as **system notifications** with the system actor, source, and a generic JSON data view. Remy preserves unknown actor IDs and notification formats rather than treating them as user messages or commands. When a notification starts agent work, retained history can associate it with a known accepted Remy turn without adding a synthetic prompt. Delivery is at-least-once, so an uncertain retry can produce additional distinct turns and repeat model or tool effects; Remy preserves those turns rather than presenting the association as proof of exactly-once execution. The CLI has no control for creating notifications.
66
70
 
67
71
  When Remy creates a plan, the session shows its checklist in the timeline and keeps `Plan <done>/<total>` with the current item pinned above the composer. Updates change the same checklist instead of producing repeated rows, and an unfinished plan remains visible after reconnecting or between turns. The collapsed timeline shows up to five plan items; press `Ctrl+O` for the complete checklist and activity detail.
@@ -74,7 +78,7 @@ Command and attachment feedback share progress, success, failure, and attention
74
78
 
75
79
  Enter `/compact` to admit a durable main-thread context compaction control without sending a human message. Remy reports request admission immediately and shows the eventual completed, no-op, failed, or unknown provider result in retained activity. Compaction waits for a safe point and does not interrupt active work or jump ahead of an earlier Next message. While admission is pending, you can keep editing or open other commands; repeated `/compact` invocations do not send another request.
76
80
 
77
- Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. Published file artifacts show their filename and muted, full web preview URL; open the URL in a browser to select that artifact in its session. Tela Page rows retain their canonical Page URL. Remy does not fetch or print artifact bytes. When the conversation has focus, press `Tab` to return to the composer.
81
+ Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. Published file artifacts show their filename and muted, full web preview URL; open the URL in a browser to select that artifact in its session. Tela Page rows retain their canonical Page URL. Downloads are explicit through `/files`; artifact bytes are never printed in the terminal. When the conversation has focus, press `Tab` to return to the composer.
78
82
 
79
83
  | Control | Result |
80
84
  | --- | --- |
@@ -85,6 +89,8 @@ Drag to select visible text in any Remy view; Remy copies it to the local clipbo
85
89
  | `/sessions` | Return to the dashboard. |
86
90
  | `/new` | Start another new-session flow. |
87
91
  | `/logout` | Sign out of Remy after confirmation. |
92
+ | `/files` / `Ctrl+F` | Browse and download session artifacts and everyone’s attachments. |
93
+ | `/links` / `Ctrl+L` | Browse, open, or copy conversation links. |
88
94
  | `/help` | Browse the same command picker shown by `/`; arrows select, Enter/Tab inserts, then submit to run. |
89
95
  | `/exit` | Leave Remy. |
90
96
  | `Ctrl+O` | Expand or collapse full plan and activity details. |
package/dist/remy.js CHANGED
@@ -18,9 +18,9 @@ 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 readdir3, rename as rename4, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
22
- import { tmpdir as tmpdir2 } from "os";
23
- import { dirname as dirname6, join as join6 } from "path";
21
+ import { mkdir as mkdir6, readFile as readFile5, readdir as readdir3, rename as rename4, unlink as unlink3, writeFile as writeFile4 } from "fs/promises";
22
+ import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
23
+ import { dirname as dirname6, join as join7 } from "path";
24
24
 
25
25
  // ../../node_modules/.bun/@meistrari+auth-cli@1.6.1+aff0271b2b55d60c/node_modules/@meistrari/auth-cli/dist/index.mjs
26
26
  import { setTimeout as setTimeout2 } from "timers/promises";
@@ -35448,9 +35448,11 @@ function publicMessageContentSegmentText(segment) {
35448
35448
  return segment.subject.kind === "user" ? segment.subject.identity.name ?? "" : segment.subject.kind === "assistant" ? segment.subject.name : "";
35449
35449
  }
35450
35450
  if (segment.type === "link")
35451
- return segment.label ?? segment.url;
35452
- if (segment.type === "context_file")
35453
- return segment.label ?? segment.filename;
35451
+ return segment.label && segment.label !== segment.url ? `${segment.label} (${segment.url})` : segment.url;
35452
+ if (segment.type === "context_file") {
35453
+ const label = segment.label ?? segment.filename;
35454
+ return label === segment.url ? segment.url : `${label} (${segment.url})`;
35455
+ }
35454
35456
  return "";
35455
35457
  }
35456
35458
  function jsonDetail(value) {
@@ -36031,6 +36033,90 @@ async function sleepMs(ms, signal) {
36031
36033
  });
36032
36034
  }
36033
36035
 
36036
+ // src/sessions/downloads.ts
36037
+ import { link, mkdir as mkdir5, mkdtemp, open as open4, rm } from "fs/promises";
36038
+ import { basename as basename2, extname, join as join4, resolve } from "path";
36039
+ var downloadGrantSchema = exports_external2.object({
36040
+ url: exports_external2.url(),
36041
+ method: exports_external2.literal("GET"),
36042
+ headers: exports_external2.record(exports_external2.string(), exports_external2.string()),
36043
+ expires_at: exports_external2.iso.datetime({ offset: true })
36044
+ });
36045
+ async function downloadSessionFile({ client, sessionId, file: file3, directory, signal, fetchClient }) {
36046
+ signal.throwIfAborted();
36047
+ const resource = file3.kind === "artifact" ? "artifacts" : "message-attachments";
36048
+ const response = await client.request(`/v1/sessions/${encodeURIComponent(sessionId)}/${resource}/${encodeURIComponent(file3.id)}/download`, { signal });
36049
+ const grant = downloadGrantSchema.parse(await response.json());
36050
+ const url3 = new URL(grant.url);
36051
+ if (url3.protocol !== "https:" && url3.protocol !== "http:" || url3.username || url3.password)
36052
+ throw new Error("The server returned an unsupported download URL.");
36053
+ const bytes = await fetchClient(grant.url, { method: grant.method, headers: grant.headers, signal }).catch(() => {
36054
+ signal.throwIfAborted();
36055
+ throw new Error("Could not fetch the file. Check your connection and retry.");
36056
+ });
36057
+ if (!bytes.ok || !bytes.body)
36058
+ throw new Error(`File download failed (HTTP ${bytes.status}). Try again to request a fresh download.`);
36059
+ const destination = resolve(directory);
36060
+ await mkdir5(destination, { recursive: true });
36061
+ const staging = await mkdtemp(join4(destination, ".remy-download-"));
36062
+ try {
36063
+ const temporaryPath = join4(staging, "content");
36064
+ await saveDownloadStream({ body: bytes.body, path: temporaryPath, signal });
36065
+ const filename = safeFilename(file3.filename);
36066
+ const extension = extname(filename);
36067
+ const stem = filename.slice(0, filename.length - extension.length);
36068
+ for (let suffix = 0;suffix < 1000; suffix += 1) {
36069
+ signal.throwIfAborted();
36070
+ const path = join4(destination, suffix === 0 ? filename : `${stem} (${suffix})${extension}`);
36071
+ try {
36072
+ await link(temporaryPath, path);
36073
+ return path;
36074
+ } catch (error93) {
36075
+ if (!(error93 instanceof Error && ("code" in error93) && error93.code === "EEXIST"))
36076
+ throw error93;
36077
+ }
36078
+ }
36079
+ throw new Error("Too many files with this name in downloads. Rename or move an existing download and retry.");
36080
+ } finally {
36081
+ await rm(staging, { recursive: true, force: true });
36082
+ }
36083
+ }
36084
+ async function saveDownloadStream({ body, path, signal }) {
36085
+ const file3 = await open4(path, "wx", 384);
36086
+ const reader = body.getReader();
36087
+ const cancel = () => {
36088
+ reader.cancel().catch(() => {
36089
+ return;
36090
+ });
36091
+ };
36092
+ signal.addEventListener("abort", cancel, { once: true });
36093
+ try {
36094
+ while (true) {
36095
+ signal.throwIfAborted();
36096
+ const chunk = await reader.read().catch(() => {
36097
+ throw new Error("Download interrupted. Check your connection and retry.");
36098
+ });
36099
+ signal.throwIfAborted();
36100
+ if (chunk.done)
36101
+ break;
36102
+ await file3.writeFile(chunk.value);
36103
+ }
36104
+ } finally {
36105
+ signal.removeEventListener("abort", cancel);
36106
+ await reader.cancel().catch(() => {
36107
+ return;
36108
+ });
36109
+ reader.releaseLock();
36110
+ await file3.close();
36111
+ }
36112
+ }
36113
+ function safeFilename(value) {
36114
+ const filename = basename2(terminalSafeText(value).replaceAll("\\", "/")).replace(/[<>:"|?*\r\n\t]/gu, "_").replace(/^[. ]+|[. ]+$/g, "");
36115
+ if (!filename)
36116
+ return "download";
36117
+ return /^(?:con|prn|aux|nul|com\d|lpt\d)(?:\.|$)/i.test(filename) ? `_${filename}` : filename;
36118
+ }
36119
+
36034
36120
  // src/tui/dashboard.ts
36035
36121
  import { BoxRenderable as BoxRenderable3, bold as bold3, CliRenderEvents as CliRenderEvents2, fg as fg5, stringToStyledText as stringToStyledText2, TextRenderable as TextRenderable2 } from "@opentui/core";
36036
36122
 
@@ -36415,8 +36501,8 @@ async function createRemyRenderer({
36415
36501
  return;
36416
36502
  };
36417
36503
  let selectionClipboard;
36418
- const destroyed = new Promise((resolve) => {
36419
- resolveDestroyed = resolve;
36504
+ const destroyed = new Promise((resolve2) => {
36505
+ resolveDestroyed = resolve2;
36420
36506
  });
36421
36507
  const renderer = await createRenderer({
36422
36508
  exitOnCtrlC: false,
@@ -36468,10 +36554,10 @@ function restoreRemyTransitionScreen() {
36468
36554
  transitionScreenWriter = undefined;
36469
36555
  }
36470
36556
  async function renderRemyView({
36471
- open: open4,
36557
+ open: open5,
36472
36558
  waitForAction
36473
36559
  }) {
36474
- const view = await open4();
36560
+ const view = await open5();
36475
36561
  try {
36476
36562
  return await waitForAction(view);
36477
36563
  } finally {
@@ -36850,8 +36936,8 @@ async function createDashboardTui({
36850
36936
  return;
36851
36937
  };
36852
36938
  let desiredContentScrollY = 0;
36853
- const action = new Promise((resolve, reject) => {
36854
- resolveAction = resolve;
36939
+ const action = new Promise((resolve2, reject) => {
36940
+ resolveAction = resolve2;
36855
36941
  rejectDraft = reject;
36856
36942
  });
36857
36943
  let mountedSearchComposer;
@@ -37387,7 +37473,7 @@ function renderPathCompletionMenu({ completions, selectedIndex, width, maxRows }
37387
37473
  // src/tui/path-completion.ts
37388
37474
  import { readdir } from "fs/promises";
37389
37475
  import { homedir } from "os";
37390
- import { basename as basename2, dirname as dirname5, isAbsolute, join as join4, relative, resolve, sep } from "path";
37476
+ import { basename as basename3, dirname as dirname5, isAbsolute, join as join5, relative, resolve as resolve2, sep } from "path";
37391
37477
  var maximumCandidates = 50;
37392
37478
  async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
37393
37479
  const token = activePathToken(text);
@@ -37395,10 +37481,10 @@ async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
37395
37481
  return [];
37396
37482
  const pathText = token.startsWith("@") ? token.slice(1) : token;
37397
37483
  const pathUsesTilde = pathText === "~" || pathText.startsWith("~/");
37398
- const expandedPath = pathText === "~" ? homeDirectory : pathText.startsWith("~/") ? join4(homeDirectory, pathText.slice(2)) : pathText;
37399
- const absolutePath = isAbsolute(expandedPath) ? expandedPath : resolve(cwd, expandedPath);
37484
+ const expandedPath = pathText === "~" ? homeDirectory : pathText.startsWith("~/") ? join5(homeDirectory, pathText.slice(2)) : pathText;
37485
+ const absolutePath = isAbsolute(expandedPath) ? expandedPath : resolve2(cwd, expandedPath);
37400
37486
  const directory = pathText === "" || pathText === "~" || pathText.endsWith("/") ? absolutePath : dirname5(absolutePath);
37401
- const prefix = pathText === "" || pathText === "~" || pathText.endsWith("/") ? "" : basename2(absolutePath);
37487
+ const prefix = pathText === "" || pathText === "~" || pathText.endsWith("/") ? "" : basename3(absolutePath);
37402
37488
  let entries;
37403
37489
  try {
37404
37490
  entries = await readdir(directory, { withFileTypes: true, encoding: "utf8" });
@@ -37406,7 +37492,7 @@ async function completePathToken({ text, cwd, homeDirectory = homedir() }) {
37406
37492
  return [];
37407
37493
  }
37408
37494
  return entries.filter((entry) => entry.name.toLowerCase().startsWith(prefix.toLowerCase())).sort((left, right) => left.name.localeCompare(right.name)).slice(0, maximumCandidates).map((entry) => {
37409
- const completedPath = join4(directory, entry.name);
37495
+ const completedPath = join5(directory, entry.name);
37410
37496
  const replacementPath = pathUsesTilde ? `~/${relative(homeDirectory, completedPath)}` : isAbsolute(pathText) ? completedPath : relative(cwd, completedPath) || entry.name;
37411
37497
  const normalized = replacementPath.split(sep).join("/");
37412
37498
  const suffix = entry.isDirectory() ? "/" : "";
@@ -38152,8 +38238,8 @@ async function createNewSessionWizard({
38152
38238
  let rejectDraft = () => {
38153
38239
  return;
38154
38240
  };
38155
- const draft = new Promise((resolve2, reject) => {
38156
- resolveDraft = resolve2;
38241
+ const draft = new Promise((resolve3, reject) => {
38242
+ resolveDraft = resolve3;
38157
38243
  rejectDraft = reject;
38158
38244
  });
38159
38245
  let editor;
@@ -38570,7 +38656,7 @@ async function createDefaultRenderer2() {
38570
38656
  }
38571
38657
 
38572
38658
  // src/tui/session-view.ts
38573
- import { CliRenderEvents as CliRenderEvents4, bg as bg3, bold as bold5, dim as dim7, fg as fg10, link, StyledText as StyledText8, stringToStyledText as stringToStyledText4, TextRenderable as TextRenderable4 } from "@opentui/core";
38659
+ import { CliRenderEvents as CliRenderEvents4, bg as bg3, bold as bold5, dim as dim7, fg as fg10, link as link2, StyledText as StyledText9, stringToStyledText as stringToStyledText4, TextRenderable as TextRenderable4 } from "@opentui/core";
38574
38660
 
38575
38661
  // src/tui/tool-output.ts
38576
38662
  var maxPreviewCharacters = 8000;
@@ -38661,6 +38747,122 @@ function formattedJson(value) {
38661
38747
  return JSON.stringify(presentation(value), null, 2);
38662
38748
  }
38663
38749
 
38750
+ // src/tui/links.ts
38751
+ import { StyledText as StyledText8, TextAttributes } from "@opentui/core";
38752
+ function browserLink(value) {
38753
+ if ([...value].some((character) => character.charCodeAt(0) <= 32 || character.charCodeAt(0) >= 127 && character.charCodeAt(0) <= 159))
38754
+ return;
38755
+ try {
38756
+ const url3 = new URL(value);
38757
+ if ((url3.protocol === "https:" || url3.protocol === "http:") && !url3.username && !url3.password)
38758
+ return value;
38759
+ } catch {}
38760
+ return;
38761
+ }
38762
+ function linkifyText(content) {
38763
+ return new StyledText8(content.chunks.flatMap((chunk) => {
38764
+ const text = terminalSafeText(chunk.text);
38765
+ if (chunk.link)
38766
+ return [{ ...chunk, text, link: browserLink(chunk.link.url) ? chunk.link : undefined }];
38767
+ const chunks = [];
38768
+ let offset = 0;
38769
+ const pattern = /https?:\/\/[^\s<>"`]+/gu;
38770
+ for (const match of text.matchAll(pattern)) {
38771
+ const target = trimUrlPunctuation(match[0]);
38772
+ if (!browserLink(target))
38773
+ continue;
38774
+ const prefix = text.slice(offset, match.index);
38775
+ const label = /\[[^\][\n]+\]\($/u.exec(prefix);
38776
+ const start = label ? offset + label.index : match.index;
38777
+ const urlEnd = match.index + target.length;
38778
+ const end = urlEnd + (label && text[urlEnd] === ")" ? 1 : 0);
38779
+ chunks.push({ ...chunk, text: text.slice(offset, start) });
38780
+ chunks.push({ ...chunk, text: text.slice(start, end), link: { url: target }, attributes: (chunk.attributes ?? 0) | TextAttributes.UNDERLINE });
38781
+ offset = end;
38782
+ }
38783
+ chunks.push({ ...chunk, text: text.slice(offset) });
38784
+ return chunks.filter((part) => part.text.length > 0);
38785
+ }));
38786
+ }
38787
+ function trimUrlPunctuation(text) {
38788
+ const counts = new Map;
38789
+ for (const character of text)
38790
+ counts.set(character, (counts.get(character) ?? 0) + 1);
38791
+ const openings = new Map([[")", "("], ["]", "["], ["}", "{"]]);
38792
+ let end = text.length;
38793
+ while (end > 0) {
38794
+ const last = text[end - 1];
38795
+ const opening = openings.get(last);
38796
+ if (!".,;:!?".includes(last) && !(opening && (counts.get(last) ?? 0) > (counts.get(opening) ?? 0)))
38797
+ break;
38798
+ counts.set(last, (counts.get(last) ?? 0) - 1);
38799
+ end -= 1;
38800
+ }
38801
+ return text.slice(0, end);
38802
+ }
38803
+ function clickedLink({ text, event }) {
38804
+ const row = event.y - text.y + text.scrollY;
38805
+ const column = event.x - text.x;
38806
+ const info = text.lineInfo;
38807
+ const start = info.lineStartCols[row];
38808
+ const width = info.lineWidthCols[row];
38809
+ if (start === undefined || width === undefined || column < 0 || column >= width)
38810
+ return;
38811
+ const position = start + column + text.scrollX;
38812
+ let offset = 0;
38813
+ for (const chunk of text.chunks) {
38814
+ const size = Bun.stringWidth(chunk.text.replaceAll("\t", " ").replaceAll(`
38815
+ `, " "));
38816
+ if (position >= offset && position < offset + size)
38817
+ return chunk.link ? browserLink(chunk.link.url) : undefined;
38818
+ offset += size;
38819
+ }
38820
+ return;
38821
+ }
38822
+
38823
+ // src/tui/session-files.ts
38824
+ function sessionFileChoices(state) {
38825
+ const files = new Map;
38826
+ for (const item of state.transcript.toReversed()) {
38827
+ if (item.kind === "artifact") {
38828
+ const key = `artifact:${item.artifactId}`;
38829
+ if (files.has(key))
38830
+ continue;
38831
+ files.set(key, {
38832
+ key,
38833
+ label: item.title,
38834
+ description: item.artifactKind === "file" ? "Artifact \xB7 Remy" : "Tela Page \xB7 Remy",
38835
+ previewUrl: item.artifactKind === "file" ? item.previewUrl : browserLink(item.url),
38836
+ download: item.artifactKind === "file" ? { kind: "artifact", id: item.artifactId, filename: item.title } : undefined
38837
+ });
38838
+ }
38839
+ if (item.kind === "message") {
38840
+ for (const attachment of item.attachments) {
38841
+ const key = `attachment:${attachment.id}`;
38842
+ if (files.has(key))
38843
+ continue;
38844
+ let previewUrl;
38845
+ if (state.webUrl && browserLink(state.webUrl)) {
38846
+ const url3 = new URL(state.webUrl);
38847
+ url3.searchParams.delete("artifact");
38848
+ url3.searchParams.delete("event_id");
38849
+ url3.searchParams.set("attachment", attachment.id);
38850
+ url3.hash = "";
38851
+ previewUrl = url3.href;
38852
+ }
38853
+ files.set(key, {
38854
+ key,
38855
+ label: attachment.filename,
38856
+ description: `Attachment \xB7 ${item.author.label}${item.author.source ? ` (${item.author.source})` : ""}`,
38857
+ previewUrl,
38858
+ download: { kind: "attachment", id: attachment.id, filename: attachment.filename }
38859
+ });
38860
+ }
38861
+ }
38862
+ }
38863
+ return [...files.values()];
38864
+ }
38865
+
38664
38866
  // src/tui/session-view.ts
38665
38867
  var composerSlashCommands = [
38666
38868
  { value: "/sessions", description: "Back to the session list" },
@@ -38669,6 +38871,8 @@ var composerSlashCommands = [
38669
38871
  { value: "/compact", description: "Compact main-thread context" },
38670
38872
  { value: "/new", description: "Start a new session" },
38671
38873
  { value: "/logout", description: "Sign out of Remy" },
38874
+ { value: "/files", description: "Download artifacts and user attachments" },
38875
+ { value: "/links", description: "Open conversation links" },
38672
38876
  { value: "/help", description: "Browse session commands" },
38673
38877
  { value: "/exit", description: "Leave Remy" }
38674
38878
  ];
@@ -38679,6 +38883,8 @@ async function createSessionTui({
38679
38883
  requestCompact,
38680
38884
  requestComplete,
38681
38885
  requestCancel,
38886
+ openBrowser,
38887
+ downloadFile,
38682
38888
  repositoryLabel,
38683
38889
  readClipboardImage,
38684
38890
  savePastedImage,
@@ -38765,10 +38971,10 @@ async function createSessionTui({
38765
38971
  syncTerminalSessionControls();
38766
38972
  syncWorkingIndicator();
38767
38973
  renderRetainedTranscript();
38768
- const assistantPreview = latestState.previews.assistantText ? new StyledText8([dim7(fg10(PALETTE.dimText)(`Live assistant preview
38769
- `)), fg10(PALETTE.remyAccent)(latestState.previews.assistantText)]) : new StyledText8([]);
38770
- const reasoningPreview = latestState.previews.reasoningText ? new StyledText8([dim7(fg10(PALETTE.dimText)(`Live reasoning preview
38771
- `)), dim7(fg10(PALETTE.dimText)(latestState.previews.reasoningText))]) : new StyledText8([]);
38974
+ const assistantPreview = latestState.previews.assistantText ? new StyledText9([dim7(fg10(PALETTE.dimText)(`Live assistant preview
38975
+ `)), fg10(PALETTE.remyAccent)(latestState.previews.assistantText)]) : new StyledText9([]);
38976
+ const reasoningPreview = latestState.previews.reasoningText ? new StyledText9([dim7(fg10(PALETTE.dimText)(`Live reasoning preview
38977
+ `)), dim7(fg10(PALETTE.dimText)(latestState.previews.reasoningText))]) : new StyledText9([]);
38772
38978
  const working = isRemyWorking(latestState);
38773
38979
  const startedAt = workingTurnStartedAt(latestState);
38774
38980
  const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now3() - Date.parse(startedAt));
@@ -38777,13 +38983,13 @@ async function createSessionTui({
38777
38983
  frame: SPINNER_FRAMES[spinnerFrameIndex],
38778
38984
  elapsedMs,
38779
38985
  mode: stopState.kind === "stopping" ? "stopping" : "working"
38780
- }) : new StyledText8([]);
38986
+ }) : new StyledText9([]);
38781
38987
  const liveContent = [assistantPreview, reasoningPreview, liveIndicator].filter((part) => part.chunks.length > 0);
38782
- liveTranscript.content = liveContent.length === 0 ? "" : joinStyled([stringToStyledText4(`
38988
+ liveTranscript.content = liveContent.length === 0 ? "" : linkifyText(joinStyled([stringToStyledText4(`
38783
38989
 
38784
38990
  `), joinStyled(liveContent, `
38785
38991
 
38786
- `)], "");
38992
+ `)], ""));
38787
38993
  composer.placeholder = lifecycleRequestState.kind === "pending" ? `${lifecycleOperationPresentParticiple(lifecycleRequestState.operation)} the session \u2014 messages are paused.` : idleComposerPlaceholder;
38788
38994
  if (latestState.aggregateStatus === "open")
38789
38995
  composerLayout.render();
@@ -38804,7 +39010,72 @@ async function createSessionTui({
38804
39010
  `) : "";
38805
39011
  hints.visible = logoutConfirmationOpen || renderer.height >= TUI_LAYOUT.actionBarMinHeight;
38806
39012
  hints.content = hints.visible ? logoutConfirmationOpen ? renderKeyHints({ hints: [{ key: "y", action: "confirm" }, { key: "Esc", action: "cancel" }], width: renderer.width - 2 }) : renderActionBar({ state: latestState, working, stopState, lifecycleRequestState, contextCompactionRequestState, hasFailedSubmission: admittedSubmissions.some((submission) => submission.status === "failed"), width: renderer.width - 2 }) : "";
39013
+ if (resourceMode)
39014
+ renderResources();
38807
39015
  renderer.requestRender();
39016
+ }, resourceChoices = function() {
39017
+ if (resourceCache?.transcript === latestState.transcript && resourceCache.webUrl === latestState.webUrl && resourceCache.mode === resourceMode)
39018
+ return resourceCache.choices;
39019
+ const choices = resourceMode === "files" ? sessionFileChoices(latestState) : conversationLinkChoices();
39020
+ resourceCache = { transcript: latestState.transcript, webUrl: latestState.webUrl, mode: resourceMode, choices };
39021
+ return choices;
39022
+ }, conversationLinkChoices = function() {
39023
+ const content = linkifyText(renderTimeline({ state: latestState, activityExpanded: true }));
39024
+ const urls = new Map;
39025
+ for (const chunk of content.chunks) {
39026
+ const url3 = chunk.link?.url;
39027
+ if (url3 && !urls.has(url3))
39028
+ urls.set(url3, { key: url3, label: chunk.text.trim(), description: url3, previewUrl: url3, download: undefined });
39029
+ }
39030
+ return [...urls.values()];
39031
+ }, openResources = function(mode) {
39032
+ resourceMode = mode;
39033
+ resourceKey = resourceChoices()[0]?.key;
39034
+ resourceFeedback = undefined;
39035
+ pathCompletionMenuOpen = false;
39036
+ slashCompletions = [];
39037
+ helpVisible = false;
39038
+ composer.blur();
39039
+ root.visible = false;
39040
+ resourceRoot.visible = true;
39041
+ resourceBody.scrollTo(0);
39042
+ render();
39043
+ }, renderResources = function() {
39044
+ const choices = resourceChoices();
39045
+ const index = Math.max(0, choices.findIndex((choice) => choice.key === resourceKey));
39046
+ resourceKey = choices[index]?.key;
39047
+ const selected = choices[index];
39048
+ const width = Math.max(1, renderer.width - 2);
39049
+ resourceHeading.content = new StyledText9([bold5(fg10(PALETTE.remyAccent)(`${resourceMode === "files" ? "Files" : "Links"} (${choices.length})`))]);
39050
+ const menu = renderCompletionMenu({
39051
+ items: choices.map((choice) => ({ label: terminalSafeText(choice.label), description: terminalSafeText(choice.description) })),
39052
+ selectedIndex: index,
39053
+ hints: [],
39054
+ width,
39055
+ maxRows: Math.max(2, renderer.height - 7)
39056
+ });
39057
+ const detail = selected ? [
39058
+ truncateText(terminalSafeText(selected.description), width),
39059
+ truncateText(terminalSafeText(selected.previewUrl ?? "Web preview unavailable"), width)
39060
+ ].join(`
39061
+ `) : resourceMode === "files" ? "No artifacts or attachments in this session yet." : "No web links in this conversation yet.";
39062
+ resourceContent.content = joinStyled([
39063
+ ...resourceFeedback ? [renderFeedback({ ...resourceFeedback, message: terminalSafeText(resourceFeedback.message) })] : [],
39064
+ ...menu ? [menu] : [],
39065
+ stringToStyledText4(detail),
39066
+ ...resourceMode === "files" ? [stringToStyledText4("Downloads save to ~/Downloads")] : []
39067
+ ], `
39068
+ `);
39069
+ resourceHints.content = renderKeyHints({
39070
+ hints: [
39071
+ { key: "\u2191\u2193", action: "select" },
39072
+ { key: "Enter", action: selected?.download ? "download" : "open" },
39073
+ ...selected?.download ? [{ key: "o", action: "preview" }] : [],
39074
+ { key: "c", action: "copy URL" },
39075
+ { key: "Esc", action: resourceRequest ? "cancel" : "back" }
39076
+ ],
39077
+ width
39078
+ });
38808
39079
  }, syncTerminalSessionControls = function() {
38809
39080
  const terminal = latestState.aggregateStatus !== "open";
38810
39081
  if (terminal && composerMounted) {
@@ -38824,9 +39095,9 @@ async function createSessionTui({
38824
39095
  }
38825
39096
  const timeline = renderTimeline({ state: latestState, activityExpanded });
38826
39097
  const submittedTimeline = renderSubmittedTimeline({ state: latestState, admittedSubmissions });
38827
- transcript.content = joinStyled([timeline, submittedTimeline].filter((part) => part.chunks.length > 0), `
39098
+ transcript.content = linkifyText(joinStyled([timeline, submittedTimeline].filter((part) => part.chunks.length > 0), `
38828
39099
 
38829
- `);
39100
+ `));
38830
39101
  renderedTranscript = latestState.transcript;
38831
39102
  renderedSessionId = latestState.sessionId;
38832
39103
  renderedAggregateStatus = latestState.aggregateStatus;
@@ -38880,6 +39151,7 @@ async function createSessionTui({
38880
39151
  if (settled)
38881
39152
  return;
38882
39153
  settled = true;
39154
+ resourceRequest?.abort();
38883
39155
  invalidateAttachmentPreparation();
38884
39156
  if (error93)
38885
39157
  rejectAction(error93);
@@ -38899,6 +39171,17 @@ async function createSessionTui({
38899
39171
  root.add(transcriptScrollBox);
38900
39172
  root.add(status);
38901
39173
  renderer.root.add(root);
39174
+ const resourceRoot = createScreenRoot({ renderer, gutter: TUI_LAYOUT.conversationGutter });
39175
+ resourceRoot.visible = false;
39176
+ const resourceHeading = new TextRenderable4(renderer, { content: "", flexShrink: 0 });
39177
+ const resourceBody = createBodyViewport({ renderer, followTail: false });
39178
+ const resourceContent = new TextRenderable4(renderer, { content: "" });
39179
+ const resourceHints = new TextRenderable4(renderer, { content: "", flexShrink: 0 });
39180
+ resourceBody.add(resourceContent);
39181
+ resourceRoot.add(resourceHeading);
39182
+ resourceRoot.add(resourceBody);
39183
+ resourceRoot.add(resourceHints);
39184
+ renderer.root.add(resourceRoot);
38902
39185
  let destroyed = false;
38903
39186
  let rendererDestroyPromise;
38904
39187
  let settled = false;
@@ -38925,6 +39208,11 @@ async function createSessionTui({
38925
39208
  let contextCompactionRequestState = { kind: "idle" };
38926
39209
  let helpVisible = false;
38927
39210
  let logoutConfirmationOpen = false;
39211
+ let resourceMode;
39212
+ let resourceKey;
39213
+ let resourceFeedback;
39214
+ let resourceRequest;
39215
+ let resourceCache;
38928
39216
  let latestState = controller.getState();
38929
39217
  let spinnerFrameIndex = 0;
38930
39218
  let renderedTranscript;
@@ -38943,8 +39231,8 @@ async function createSessionTui({
38943
39231
  let rejectAction = () => {
38944
39232
  return;
38945
39233
  };
38946
- const action = new Promise((resolve2, reject) => {
38947
- resolveAction = resolve2;
39234
+ const action = new Promise((resolve3, reject) => {
39235
+ resolveAction = resolve3;
38948
39236
  rejectAction = reject;
38949
39237
  });
38950
39238
  const composerTag = () => latestState.sessionNumber !== undefined ? `Session #${latestState.sessionNumber}` : latestState.sessionId;
@@ -39004,6 +39292,38 @@ async function createSessionTui({
39004
39292
  }
39005
39293
  return;
39006
39294
  }
39295
+ if (resourceMode) {
39296
+ key.preventDefault();
39297
+ const choices = resourceChoices();
39298
+ const index = Math.max(0, choices.findIndex((choice) => choice.key === resourceKey));
39299
+ if (key.name === "escape") {
39300
+ resourceRequest?.abort();
39301
+ resourceRequest = undefined;
39302
+ resourceMode = undefined;
39303
+ root.visible = true;
39304
+ resourceRoot.visible = false;
39305
+ if (latestState.aggregateStatus === "open")
39306
+ composer.focus();
39307
+ } else if (key.name === "up" || key.name === "down" || key.name === "j" || key.name === "k") {
39308
+ const next = key.name === "up" || key.name === "k" ? index - 1 : index + 1;
39309
+ resourceKey = choices[Math.max(0, Math.min(choices.length - 1, next))]?.key;
39310
+ resourceBody.scrollTo(0);
39311
+ } else if (key.name === "return" || key.name === "d" || key.name === "o") {
39312
+ const choice = choices[index];
39313
+ if (choice)
39314
+ actOnResource(choice, key.name === "o");
39315
+ } else if (key.name === "c") {
39316
+ const url3 = choices[index]?.previewUrl;
39317
+ resourceFeedback = url3 && renderer.copyToClipboardOSC52(url3) ? { kind: "success", message: "URL copied." } : { kind: "failure", message: "Could not copy a URL. Clipboard support or a Web preview is unavailable." };
39318
+ }
39319
+ render();
39320
+ return;
39321
+ }
39322
+ if (key.ctrl && (key.name === "f" || key.name === "l")) {
39323
+ key.preventDefault();
39324
+ openResources(key.name === "f" ? "files" : "links");
39325
+ return;
39326
+ }
39007
39327
  if (key.ctrl && key.name === "v" && latestState.aggregateStatus === "open") {
39008
39328
  key.preventDefault();
39009
39329
  pasteClipboardImage();
@@ -39157,6 +39477,67 @@ async function createSessionTui({
39157
39477
  navigatePromptHistory(key.name);
39158
39478
  }
39159
39479
  };
39480
+ async function actOnResource(choice, preview2) {
39481
+ if (resourceRequest)
39482
+ return;
39483
+ const request = new AbortController;
39484
+ resourceRequest = request;
39485
+ resourceFeedback = { kind: "progress", message: `${choice.download && !preview2 ? "Downloading" : "Opening"} ${choice.label}\u2026` };
39486
+ render();
39487
+ try {
39488
+ if (choice.download && !preview2) {
39489
+ const path = await downloadFile({ file: choice.download, signal: request.signal });
39490
+ if (!request.signal.aborted && !destroyed && !settled)
39491
+ resourceFeedback = { kind: "success", message: `Saved to ${path}` };
39492
+ } else {
39493
+ if (!choice.previewUrl || !browserLink(choice.previewUrl))
39494
+ throw new Error("Web preview unavailable from this server. You can still download this file.");
39495
+ await openBrowser(choice.previewUrl);
39496
+ if (!request.signal.aborted && !destroyed && !settled)
39497
+ resourceFeedback = { kind: "success", message: "Opened in your browser." };
39498
+ }
39499
+ } catch (error93) {
39500
+ if (!request.signal.aborted && !destroyed && !settled)
39501
+ resourceFeedback = { kind: "failure", message: `${error93 instanceof Error ? error93.message : String(error93)} Press Enter to retry, or o for preview.` };
39502
+ } finally {
39503
+ if (resourceRequest === request)
39504
+ resourceRequest = undefined;
39505
+ if (!request.signal.aborted && !destroyed && !settled)
39506
+ render();
39507
+ }
39508
+ }
39509
+ for (const text of [transcript, liveTranscript]) {
39510
+ let pressedLink;
39511
+ text.onMouseDown = (event) => {
39512
+ pressedLink = undefined;
39513
+ if (event.button !== 0 || !event.modifiers.ctrl || event.isDragging)
39514
+ return;
39515
+ const url3 = clickedLink({ text, event });
39516
+ if (!url3)
39517
+ return;
39518
+ event.preventDefault();
39519
+ event.stopPropagation();
39520
+ pressedLink = { url: url3, x: event.x, y: event.y };
39521
+ };
39522
+ text.onMouseDrag = () => {
39523
+ pressedLink = undefined;
39524
+ };
39525
+ text.onMouseUp = (event) => {
39526
+ const pressed = pressedLink;
39527
+ pressedLink = undefined;
39528
+ if (!pressed || event.button !== 0 || !event.modifiers.ctrl || event.isDragging || event.x !== pressed.x || event.y !== pressed.y || clickedLink({ text, event }) !== pressed.url) {
39529
+ return;
39530
+ }
39531
+ event.preventDefault();
39532
+ event.stopPropagation();
39533
+ openBrowser(pressed.url).catch(() => {
39534
+ if (destroyed || settled)
39535
+ return;
39536
+ composerFeedback = { kind: "failure", message: "Could not open your browser. Use /links to select and copy the URL." };
39537
+ render();
39538
+ });
39539
+ };
39540
+ }
39160
39541
  async function openPathCompletionMenu() {
39161
39542
  if (destroyed)
39162
39543
  return;
@@ -39257,6 +39638,12 @@ async function createSessionTui({
39257
39638
  finish({ kind: "exit" });
39258
39639
  return true;
39259
39640
  }
39641
+ if (command === "/files" || command === "/links") {
39642
+ composer.setText("");
39643
+ composerDraft = { ...composerDraft, text: "" };
39644
+ openResources(command === "/files" ? "files" : "links");
39645
+ return true;
39646
+ }
39260
39647
  if (command === "/new") {
39261
39648
  composer.setText("");
39262
39649
  composerDraft = { ...composerDraft, text: "" };
@@ -39479,6 +39866,7 @@ async function createSessionTui({
39479
39866
  if (destroyed)
39480
39867
  return;
39481
39868
  destroyed = true;
39869
+ resourceRequest?.abort();
39482
39870
  invalidateAttachmentPreparation();
39483
39871
  if (workingSpinnerTimer) {
39484
39872
  clearInterval(workingSpinnerTimer);
@@ -39532,7 +39920,7 @@ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleReque
39532
39920
  const pendingOperation = lifecycleRequestState?.kind === "pending" ? lifecycleRequestState.operation : undefined;
39533
39921
  const presentation = pendingOperation ? { label: lifecycleOperationPresentParticiple(pendingOperation), color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
39534
39922
  const elapsed = (working || stopping) && elapsedMs !== undefined && elapsedMs > 0 ? ` ${formatElapsed(elapsedMs)}` : "";
39535
- const connection = state.connectionStatus === "connected" ? new StyledText8([fg10(PALETTE.statusCompleted)("\u25CF "), dim7(fg10(PALETTE.dimText)("connected"))]) : new StyledText8([fg10(PALETTE.approvalQuestion)("\u25CF reconnecting\u2026")]);
39923
+ const connection = state.connectionStatus === "connected" ? new StyledText9([fg10(PALETTE.statusCompleted)("\u25CF "), dim7(fg10(PALETTE.dimText)("connected"))]) : new StyledText9([fg10(PALETTE.approvalQuestion)("\u25CF reconnecting\u2026")]);
39536
39924
  const sessionLabel = state.sessionNumber !== undefined ? `#${state.sessionNumber}` : state.sessionId;
39537
39925
  const pullRequest = state.pullRequest;
39538
39926
  const contextStatus = renderContextStatus(state.context.snapshot);
@@ -39558,17 +39946,17 @@ function renderContextStatus(snapshot) {
39558
39946
  function renderTerminalSessionBand({ aggregateStatus, width }) {
39559
39947
  const status = sessionStatusLabel(aggregateStatus).toLowerCase();
39560
39948
  return joinStyled([
39561
- new StyledText8([
39949
+ new StyledText9([
39562
39950
  bg3(PALETTE.selectionBg)(bold5(fg10(PALETTE.systemAccent)(` Session ${status} `))),
39563
39951
  bg3(PALETTE.selectionBg)(fg10(PALETTE.bodyText)("\u2014 this conversation is closed. "))
39564
39952
  ]),
39565
- renderKeyHints({ hints: [{ key: "esc", action: "return to dashboard" }, { key: "n", action: "new session" }], width })
39953
+ renderKeyHints({ hints: [{ key: "esc", action: "return to dashboard" }, { key: "n", action: "new session" }, { key: "ctrl+f", action: "files" }, { key: "ctrl+l", action: "links" }], width })
39566
39954
  ], `
39567
39955
  `);
39568
39956
  }
39569
39957
  function renderActionBar({ state, working, stopState, lifecycleRequestState, contextCompactionRequestState, hasFailedSubmission, width }) {
39570
39958
  if (state.aggregateStatus !== "open")
39571
- return new StyledText8([]);
39959
+ return new StyledText9([]);
39572
39960
  const hints = [{ key: "/", action: "commands" }];
39573
39961
  if (working && stopState.kind === "failed")
39574
39962
  hints.push({ key: "esc", action: "retry stop" });
@@ -39580,7 +39968,7 @@ function renderActionBar({ state, working, stopState, lifecycleRequestState, con
39580
39968
  hints.push({ key: "ctrl+r", action: "retry oldest failed message" });
39581
39969
  if (!working && lifecycleRequestState.kind !== "pending")
39582
39970
  hints.push({ key: "/new", action: "" }, { key: "/sessions", action: "" });
39583
- hints.push({ key: "ctrl+o", action: "activity" });
39971
+ hints.push({ key: "ctrl+o", action: "activity" }, { key: "ctrl+f", action: "files" }, { key: "ctrl+l", action: "links" });
39584
39972
  return renderKeyHints({ hints, width });
39585
39973
  }
39586
39974
  function isRemyWorking(state) {
@@ -39640,20 +40028,20 @@ function renderTimelineItem(item, activityExpanded) {
39640
40028
  const sourceUrl = item.source.url ? terminalSafeText(item.source.url) : null;
39641
40029
  const notificationData = JSON.stringify(item.notifications, null, 2);
39642
40030
  return joinStyled([
39643
- new StyledText8([
40031
+ new StyledText9([
39644
40032
  renderTimestampChunk(item.occurredAt),
39645
40033
  renderRoleLabel({ label: actorName, role: "system" }),
39646
40034
  dim7(fg10(PALETTE.dimText)(` \xB7 System notification (${sourceLabel})`))
39647
40035
  ]),
39648
- new StyledText8([dim7(fg10(PALETTE.dimText)(`Actor ID: ${actorId} \xB7 Source: ${sourceProvider}`))]),
39649
- ...sourceUrl ? [new StyledText8([dim7(fg10(PALETTE.dimText)(`Source URL: ${sourceUrl}`))])] : [],
39650
- new StyledText8([fg10(PALETTE.dimText)(notificationData)]),
39651
- ...item.eventUrl ? [new StyledText8([dim7(fg10(PALETTE.dimText)(`View event in Web: ${item.eventUrl}`))])] : []
40036
+ new StyledText9([dim7(fg10(PALETTE.dimText)(`Actor ID: ${actorId} \xB7 Source: ${sourceProvider}`))]),
40037
+ ...sourceUrl ? [new StyledText9([dim7(fg10(PALETTE.dimText)(`Source URL: ${sourceUrl}`))])] : [],
40038
+ new StyledText9([fg10(PALETTE.dimText)(notificationData)]),
40039
+ ...item.eventUrl ? [new StyledText9([dim7(fg10(PALETTE.dimText)(`View event in Web: ${item.eventUrl}`))])] : []
39652
40040
  ], `
39653
40041
  `);
39654
40042
  }
39655
40043
  if (item.kind === "message") {
39656
- const header = new StyledText8([
40044
+ const header = new StyledText9([
39657
40045
  renderTimestampChunk(item.occurredAt),
39658
40046
  renderRoleLabel({ label: item.author.label, role: item.author.role }),
39659
40047
  ...item.author.source ? [dim7(fg10(PALETTE.dimText)(` (${item.author.source})`))] : []
@@ -39669,14 +40057,14 @@ function renderTimelineItem(item, activityExpanded) {
39669
40057
  if (item.kind === "plan")
39670
40058
  return renderPlan({ item, expanded: activityExpanded });
39671
40059
  if (item.artifactKind === "tela_page") {
39672
- return new StyledText8([
40060
+ return new StyledText9([
39673
40061
  renderTimestampChunk(item.occurredAt),
39674
40062
  bold5(fg10(PALETTE.tool)("Tela Page: ")),
39675
40063
  fg10(PALETTE.bodyText)(item.title),
39676
40064
  dim7(fg10(PALETTE.dimText)(` \xB7 ${item.url}`))
39677
40065
  ]);
39678
40066
  }
39679
- return new StyledText8([
40067
+ return new StyledText9([
39680
40068
  renderTimestampChunk(item.occurredAt),
39681
40069
  bold5(fg10(PALETTE.tool)("Artifact: ")),
39682
40070
  fg10(PALETTE.bodyText)(item.title),
@@ -39686,31 +40074,31 @@ function renderTimelineItem(item, activityExpanded) {
39686
40074
  var collapsedPlanItemLimit = 5;
39687
40075
  function renderPlan({ item, expanded }) {
39688
40076
  if (item.items.length === 0)
39689
- return new StyledText8([]);
40077
+ return new StyledText9([]);
39690
40078
  const completed = item.items.filter((workItem) => workItem.status === "completed").length;
39691
40079
  const visibleItems = expanded ? item.items : collapsedPlanItems(item.items);
39692
40080
  const rows = visibleItems.map((workItem) => {
39693
40081
  if (workItem.status === "in_progress") {
39694
- return new StyledText8([
40082
+ return new StyledText9([
39695
40083
  fg10(PALETTE.remyAccent)(" \u25B8 "),
39696
40084
  bold5(fg10(PALETTE.bodyText)(workItem.title))
39697
40085
  ]);
39698
40086
  }
39699
40087
  const glyph = workItem.status === "completed" ? "\u2713" : "\u25CB";
39700
- return new StyledText8([
40088
+ return new StyledText9([
39701
40089
  fg10(PALETTE.dimText)(` ${glyph} `),
39702
40090
  dim7(fg10(PALETTE.dimText)(workItem.title))
39703
40091
  ]);
39704
40092
  });
39705
40093
  const hidden = item.items.length - visibleItems.length;
39706
40094
  return joinStyled([
39707
- new StyledText8([
40095
+ new StyledText9([
39708
40096
  renderTimestampChunk(item.occurredAt),
39709
40097
  bold5(fg10(PALETTE.remyAccent)("Plan")),
39710
40098
  dim7(fg10(PALETTE.dimText)(` \xB7 ${completed}/${item.items.length} done`))
39711
40099
  ]),
39712
40100
  ...rows,
39713
- ...hidden > 0 ? [new StyledText8([dim7(fg10(PALETTE.dimText)(` \u2026 ${hidden} more task${hidden === 1 ? "" : "s"} \xB7 Ctrl+O details`))])] : []
40101
+ ...hidden > 0 ? [new StyledText9([dim7(fg10(PALETTE.dimText)(` \u2026 ${hidden} more task${hidden === 1 ? "" : "s"} \xB7 Ctrl+O details`))])] : []
39714
40102
  ], `
39715
40103
  `);
39716
40104
  }
@@ -39757,7 +40145,7 @@ function canonicalValuesEqual(left, right) {
39757
40145
  }
39758
40146
  function renderActivityGroup({ items, activityExpanded }) {
39759
40147
  if (items.length === 0)
39760
- return new StyledText8([]);
40148
+ return new StyledText9([]);
39761
40149
  const summary = renderActivitySummary({ items, includeTimestamp: activityExpanded });
39762
40150
  const lastInFlightIndex = items.length - 1;
39763
40151
  if (!activityExpanded) {
@@ -39766,9 +40154,9 @@ function renderActivityGroup({ items, activityExpanded }) {
39766
40154
  item,
39767
40155
  inFlight: item.card.kind === "tool" && tailStart + index === lastInFlightIndex
39768
40156
  }));
39769
- const elision = tailStart > 0 ? new StyledText8([dim7(fg10(PALETTE.dimText)(` \u2026 ${tailStart} earlier step${tailStart === 1 ? "" : "s"}`))]) : undefined;
40157
+ const elision = tailStart > 0 ? new StyledText9([dim7(fg10(PALETTE.dimText)(` \u2026 ${tailStart} earlier step${tailStart === 1 ? "" : "s"}`))]) : undefined;
39770
40158
  return joinStyled([
39771
- new StyledText8([renderTimestampChunk(items[0].occurredAt), dim7(fg10(PALETTE.dimText)("Worked"))]),
40159
+ new StyledText9([renderTimestampChunk(items[0].occurredAt), dim7(fg10(PALETTE.dimText)("Worked"))]),
39772
40160
  ...elision ? [elision] : [],
39773
40161
  ...tail,
39774
40162
  summary
@@ -39787,7 +40175,7 @@ function renderExpandedActivityHierarchy({ items, lastInFlightIndex }) {
39787
40175
  const attribution = item.card.attribution;
39788
40176
  if (attribution?.status === "resolved") {
39789
40177
  if (activeOwnerKey !== attribution.ownerKey) {
39790
- rendered.push(new StyledText8([
40178
+ rendered.push(new StyledText9([
39791
40179
  dim7(fg10(PALETTE.dimText)(" ")),
39792
40180
  fg10(PALETTE.bodyText)(`Subagent \xB7 ${lineagePathLabel(attribution.path)}`)
39793
40181
  ]));
@@ -39817,17 +40205,17 @@ function renderExpandedActivityStep({ item, count, inFlight, nested, eventUrls }
39817
40205
  const disclosure = renderActivityDisclosure(item.card);
39818
40206
  const title = expandedActivityTitle(item.card);
39819
40207
  const indent = nested ? " " : " ";
39820
- const step = new StyledText8([
40208
+ const step = new StyledText9([
39821
40209
  fg10(color)(`${indent}${glyph} `),
39822
40210
  fg10(PALETTE.bodyText)(title),
39823
40211
  ...count > 1 ? [dim7(fg10(PALETTE.dimText)(` \xD7${count}`))] : [],
39824
40212
  ...showSummary ? [dim7(fg10(PALETTE.dimText)(`
39825
40213
  ${indent} ${item.card.summary}`))] : []
39826
40214
  ]);
39827
- const links = eventUrls.map((url3) => new StyledText8([
39828
- fg10(PALETTE.tool)(link(url3)(`${indent} View event in Web: ${url3}`))
40215
+ const links = eventUrls.map((url3) => new StyledText9([
40216
+ fg10(PALETTE.tool)(link2(url3)(`${indent} View event in Web: ${url3}`))
39829
40217
  ]));
39830
- const unavailable = eventUrls.length === 0 && item.card.toolOutput !== undefined ? [new StyledText8([dim7(fg10(PALETTE.dimText)(`${indent} Web event link unavailable from this server.`))])] : [];
40218
+ const unavailable = eventUrls.length === 0 && item.card.toolOutput !== undefined ? [new StyledText9([dim7(fg10(PALETTE.dimText)(`${indent} Web event link unavailable from this server.`))])] : [];
39831
40219
  return joinStyled([step, ...disclosure ? [disclosure] : [], ...links, ...unavailable], `
39832
40220
  `);
39833
40221
  }
@@ -39861,7 +40249,7 @@ ${text}`;
39861
40249
  const details = [];
39862
40250
  if (card.detail !== undefined) {
39863
40251
  const safeDetail = terminalSafeText(card.detail);
39864
- details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new StyledText8([dim7(fg10(PALETTE.dimText)(` ${safeDetail}`))]));
40252
+ details.push(card.detailFormat === "code" ? renderCodeSnippet({ text: safeDetail, indent: " " }) : new StyledText9([dim7(fg10(PALETTE.dimText)(` ${safeDetail}`))]));
39865
40253
  }
39866
40254
  const toolOutput = card.toolOutput !== undefined && card.toolCall?.mcp ? renderToolOutputDetail(card.toolOutput) : undefined;
39867
40255
  if (toolOutput)
@@ -39881,14 +40269,14 @@ function renderToolOutputDetail(output) {
39881
40269
  `);
39882
40270
  }
39883
40271
  function renderActivitySummary({ items, includeTimestamp }) {
39884
- return new StyledText8([
40272
+ return new StyledText9([
39885
40273
  ...includeTimestamp ? [renderTimestampChunk(items[0].occurredAt)] : [],
39886
40274
  dim7(fg10(PALETTE.dimText)(`Worked \xB7 ${items.length} step${items.length === 1 ? "" : "s"} \xB7 Ctrl+O details`))
39887
40275
  ]);
39888
40276
  }
39889
40277
  function renderCollapsedActivityStep({ item, inFlight }) {
39890
40278
  const { glyph, color } = activityGlyph({ kind: item.card.kind, inFlight });
39891
- return new StyledText8([
40279
+ return new StyledText9([
39892
40280
  fg10(color)(` ${glyph} `),
39893
40281
  dim7(fg10(PALETTE.dimText)(collapsedActivityTitle(item.card)))
39894
40282
  ]);
@@ -39908,7 +40296,7 @@ function renderSubmittedTimeline({ state, admittedSubmissions }) {
39908
40296
  const submitted = admittedSubmissions.filter((submission) => submission.status === "submitted" && !state.transcript.some((item) => item.kind === "message" && item.messageId === submission.messageId));
39909
40297
  const messages = submitted.map((submission) => {
39910
40298
  const attachments = renderAttachmentSummary(submission.attachments);
39911
- const header = new StyledText8([
40299
+ const header = new StyledText9([
39912
40300
  renderRoleLabel({ label: "You", role: "human" }),
39913
40301
  dim7(fg10(PALETTE.dimText)(" (CLI)"))
39914
40302
  ]);
@@ -39925,18 +40313,18 @@ function renderSubmittedTimeline({ state, admittedSubmissions }) {
39925
40313
  }
39926
40314
  function renderWorkingIndicator({ frame, elapsedMs, mode }) {
39927
40315
  if (mode === "stopping") {
39928
- return new StyledText8([
40316
+ return new StyledText9([
39929
40317
  fg10(PALETTE.approvalQuestion)("\u23F9"),
39930
40318
  dim7(fg10(PALETTE.dimText)(` Stopping\u2026 ${formatElapsed(elapsedMs)}`))
39931
40319
  ]);
39932
40320
  }
39933
40321
  if (mode === "complete" || mode === "cancel") {
39934
- return new StyledText8([
40322
+ return new StyledText9([
39935
40323
  fg10(PALETTE.progress)(frame),
39936
40324
  dim7(fg10(PALETTE.dimText)(` ${lifecycleOperationPresentParticiple(mode)} the session\u2026`))
39937
40325
  ]);
39938
40326
  }
39939
- return new StyledText8([
40327
+ return new StyledText9([
39940
40328
  fg10(PALETTE.remyAccent)(frame),
39941
40329
  dim7(fg10(PALETTE.dimText)(` Working ${formatElapsed(elapsedMs)}`))
39942
40330
  ]);
@@ -39955,7 +40343,7 @@ function formatElapsed(elapsedMs) {
39955
40343
  }
39956
40344
  function renderComposerStatus({ admittedSubmissions, width }) {
39957
40345
  if (admittedSubmissions.length === 0)
39958
- return new StyledText8([]);
40346
+ return new StyledText9([]);
39959
40347
  const parts = admittedSubmissions.flatMap((submission) => {
39960
40348
  if (submission.status === "submitted")
39961
40349
  return [];
@@ -39989,9 +40377,9 @@ async function createDefaultRenderer3() {
39989
40377
  // src/tui/attachments.ts
39990
40378
  import { Buffer as Buffer2 } from "buffer";
39991
40379
  import { randomUUID as randomUUID4 } from "crypto";
39992
- import { mkdtemp, open as open4, readdir as readdir2, rm, stat, writeFile as writeFile3 } from "fs/promises";
40380
+ import { mkdtemp as mkdtemp2, open as open5, readdir as readdir2, rm as rm2, stat, writeFile as writeFile3 } from "fs/promises";
39993
40381
  import { homedir as homedir2, tmpdir } from "os";
39994
- import { basename as basename3, isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "path";
40382
+ import { basename as basename4, isAbsolute as isAbsolute2, join as join6, resolve as resolve3 } from "path";
39995
40383
  var directoryArchiveMediaType = "application/gzip";
39996
40384
  var directoryArchiveSuffix = ".tar.gz";
39997
40385
  var maxPortableFilenameBytes = 255;
@@ -40035,7 +40423,7 @@ async function writePastedImage({
40035
40423
  const extension = extensionForMediaType(mediaType);
40036
40424
  if (!extension)
40037
40425
  throw new Error(`Unsupported pasted image MIME type: ${mediaType}`);
40038
- const filePath = join5(temporaryDirectory, `remy-paste-${randomUUID4()}.${extension}`);
40426
+ const filePath = join6(temporaryDirectory, `remy-paste-${randomUUID4()}.${extension}`);
40039
40427
  await writeFile3(filePath, bytes, { mode: 384 });
40040
40428
  return filePath;
40041
40429
  }
@@ -40069,7 +40457,7 @@ async function attachPromptPaths({
40069
40457
  }
40070
40458
  if (pathsToUpload.length === 0)
40071
40459
  return [];
40072
- onUploadStart?.({ filenames: pathsToUpload.map((filePath) => basename3(filePath)) });
40460
+ onUploadStart?.({ filenames: pathsToUpload.map((filePath) => basename4(filePath)) });
40073
40461
  const attachments = [];
40074
40462
  for (const filePath of pathsToUpload) {
40075
40463
  const uploaded = await uploadLocalAttachment({ filePath, reserveAndUploadFile: reserveAndUploadFile2 });
@@ -40084,14 +40472,14 @@ async function uploadLocalAttachment({
40084
40472
  filePath,
40085
40473
  reserveAndUploadFile: reserveAndUploadFile2
40086
40474
  }) {
40087
- const sourcePath = resolve2(filePath);
40475
+ const sourcePath = resolve3(filePath);
40088
40476
  const source = await stat(sourcePath);
40089
40477
  if (source.isFile()) {
40090
40478
  if (source.size > fileUploadMaxBytes)
40091
40479
  throw oversizedFileAttachmentError(sourcePath);
40092
40480
  return {
40093
40481
  fileId: await reserveAndUploadFile2({ filePath: sourcePath }),
40094
- filename: basename3(sourcePath),
40482
+ filename: basename4(sourcePath),
40095
40483
  sourcePath
40096
40484
  };
40097
40485
  }
@@ -40105,11 +40493,11 @@ async function uploadLocalAttachment({
40105
40493
  sourcePath
40106
40494
  };
40107
40495
  } finally {
40108
- await rm(archive.temporaryDirectory, { recursive: true, force: true });
40496
+ await rm2(archive.temporaryDirectory, { recursive: true, force: true });
40109
40497
  }
40110
40498
  }
40111
40499
  async function createDirectoryArchive(directoryPath) {
40112
- const rootName = basename3(directoryPath) || "directory";
40500
+ const rootName = basename4(directoryPath) || "directory";
40113
40501
  const files = Object.create(null);
40114
40502
  await collectDirectoryFiles({
40115
40503
  directoryPath,
@@ -40120,15 +40508,15 @@ async function createDirectoryArchive(directoryPath) {
40120
40508
  });
40121
40509
  if (Object.keys(files).length === 0)
40122
40510
  throw new Error(`Directory attachment contains no regular files: ${directoryPath}`);
40123
- const temporaryDirectory = await mkdtemp(join5(tmpdir(), "remy-directory-attachment-"));
40511
+ const temporaryDirectory = await mkdtemp2(join6(tmpdir(), "remy-directory-attachment-"));
40124
40512
  const filename = directoryArchiveFilename(rootName);
40125
- const archivePath = join5(temporaryDirectory, filename);
40513
+ const archivePath = join6(temporaryDirectory, filename);
40126
40514
  try {
40127
40515
  await Bun.Archive.write(archivePath, files, { compress: "gzip" });
40128
40516
  if ((await stat(archivePath)).size > fileUploadMaxBytes)
40129
40517
  throw oversizedDirectoryArchiveError(directoryPath);
40130
40518
  } catch (error93) {
40131
- await rm(temporaryDirectory, { recursive: true, force: true });
40519
+ await rm2(temporaryDirectory, { recursive: true, force: true });
40132
40520
  throw error93;
40133
40521
  }
40134
40522
  return { path: archivePath, filename, temporaryDirectory };
@@ -40144,7 +40532,7 @@ async function collectDirectoryFiles({
40144
40532
  entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
40145
40533
  let byteSize = collectedByteSize;
40146
40534
  for (const entry of entries) {
40147
- const entryPath = join5(directoryPath, entry.name);
40535
+ const entryPath = join6(directoryPath, entry.name);
40148
40536
  const entryArchivePath = `${archivePath}/${entry.name}`;
40149
40537
  if (entry.isDirectory()) {
40150
40538
  byteSize = await collectDirectoryFiles({
@@ -40157,7 +40545,7 @@ async function collectDirectoryFiles({
40157
40545
  continue;
40158
40546
  }
40159
40547
  if (entry.isFile()) {
40160
- const fileHandle = await open4(entryPath, "r");
40548
+ const fileHandle = await open5(entryPath, "r");
40161
40549
  try {
40162
40550
  const entryStat = await fileHandle.stat();
40163
40551
  if (!entryStat.isFile())
@@ -40213,7 +40601,7 @@ function truncateUtf8(value, maxBytes) {
40213
40601
  function promptFilePaths({ text, cwd, selectedPaths }) {
40214
40602
  const paths = new Map;
40215
40603
  for (const selectedPath of selectedPaths)
40216
- paths.set(isAbsolute2(selectedPath) ? selectedPath : resolve2(cwd, selectedPath), true);
40604
+ paths.set(isAbsolute2(selectedPath) ? selectedPath : resolve3(cwd, selectedPath), true);
40217
40605
  const tokens = text.match(/[([{]*@?(?:"[^"]*"|'[^']*'|`[^`]*`)|(?:\\.|[^\s\\])+/g) ?? [];
40218
40606
  for (const quotedToken of tokens) {
40219
40607
  const wrappedToken = quotedToken.replace(/^[([{]+/, "").replace(/[),.;:!?\]}]+$/, "");
@@ -40226,8 +40614,8 @@ function promptFilePaths({ text, cwd, selectedPaths }) {
40226
40614
  const pathShaped = explicit || pathText.includes("/") || pathText.includes("\\") || pathText.includes(".");
40227
40615
  if (!pathShaped)
40228
40616
  continue;
40229
- const expandedPath = pathText.startsWith("~/") ? join5(homedir2(), pathText.slice(2)) : pathText;
40230
- const filePath = isAbsolute2(expandedPath) ? expandedPath : resolve2(cwd, expandedPath);
40617
+ const expandedPath = pathText.startsWith("~/") ? join6(homedir2(), pathText.slice(2)) : pathText;
40618
+ const filePath = isAbsolute2(expandedPath) ? expandedPath : resolve3(cwd, expandedPath);
40231
40619
  paths.set(filePath, paths.get(filePath) || explicit);
40232
40620
  }
40233
40621
  return [...paths].map(([filePath, explicit]) => ({ filePath, explicit }));
@@ -40248,7 +40636,7 @@ function isPng(bytes) {
40248
40636
  }
40249
40637
 
40250
40638
  // src/tui/remy-splash.ts
40251
- import { bold as bold6, BoxRenderable as BoxRenderable4, fg as fg11, NativeImage, StyledText as StyledText9, TextRenderable as TextRenderable5, bg as bg4 } from "@opentui/core";
40639
+ import { bold as bold6, BoxRenderable as BoxRenderable4, fg as fg11, NativeImage, StyledText as StyledText10, TextRenderable as TextRenderable5, bg as bg4 } from "@opentui/core";
40252
40640
  // src/tui/remy-mark.ts
40253
40641
  var remyPixelFieldSource = new URL("../assets/remy-pixel-field.png", import.meta.url);
40254
40642
  function shouldShowRemySplash({ width, height }) {
@@ -40266,7 +40654,7 @@ var compactMarkRows = 9;
40266
40654
  var compactMinWidth = 48;
40267
40655
  var compactMinHeight = 20;
40268
40656
  var markBrightnessGain = 4.2;
40269
- var remyCliVersion = "1.19.0-rc.0";
40657
+ var remyCliVersion = "1.19.0-rc.2";
40270
40658
  async function showRemySplash({
40271
40659
  createRenderer = createRemyRenderer,
40272
40660
  durationMs = splashDurationMs,
@@ -40324,7 +40712,7 @@ async function showRemySplash({
40324
40712
  });
40325
40713
  const approvalCopy = new TextRenderable5(renderer, { content: "" });
40326
40714
  const metadata = new TextRenderable5(renderer, {
40327
- content: new StyledText9([fg11(PALETTE.dimText)(`v${remyCliVersion} \xB7 Tela\xAE`)])
40715
+ content: new StyledText10([fg11(PALETTE.dimText)(`v${remyCliVersion} \xB7 Tela\xAE`)])
40328
40716
  });
40329
40717
  const field = await loadPixelFieldUntilAbort({ signal });
40330
40718
  if (!field || signal?.aborted)
@@ -40363,7 +40751,7 @@ async function showRemySplash({
40363
40751
  if (signal)
40364
40752
  await waitForSplashAbort(signal);
40365
40753
  else
40366
- await new Promise((resolve3) => setTimeout(resolve3, durationMs));
40754
+ await new Promise((resolve4) => setTimeout(resolve4, durationMs));
40367
40755
  } finally {
40368
40756
  if (shimmerTimer)
40369
40757
  clearInterval(shimmerTimer);
@@ -40422,7 +40810,7 @@ function createSplashController({
40422
40810
  };
40423
40811
  }
40424
40812
  function renderBootstrapActions({ state, frame }) {
40425
- return new StyledText9([
40813
+ return new StyledText10([
40426
40814
  actionText({ label: state.authenticatedEmail ? `Authenticated as ${state.authenticatedEmail}` : "Checking authentication...", status: state.authentication, frame }),
40427
40815
  fg11(PALETTE.dimText)(`
40428
40816
  `),
@@ -40440,11 +40828,11 @@ function actionText({ label, status, frame }) {
40440
40828
  }
40441
40829
  function renderAuthenticationBand({ state, frame }) {
40442
40830
  if (!state.waitingForAuthentication)
40443
- return new StyledText9([]);
40831
+ return new StyledText10([]);
40444
40832
  const spinner = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
40445
40833
  const url3 = state.verificationUrl ? [fg11(PALETTE.dimText)(`
40446
40834
  Verification URL: `), bold6(fg11(PALETTE.humanAccent)(state.verificationUrl))] : [];
40447
- return new StyledText9([
40835
+ return new StyledText10([
40448
40836
  fg11(PALETTE.bodyText)("Continue authentication in your browser."),
40449
40837
  ...url3,
40450
40838
  fg11(PALETTE.dimText)(`
@@ -40454,7 +40842,7 @@ ${spinner} Waiting for approval \xB7 q / esc cancel`)
40454
40842
  async function waitForSplashAbort(signal) {
40455
40843
  if (signal.aborted)
40456
40844
  return;
40457
- await new Promise((resolve3) => signal.addEventListener("abort", () => resolve3(), { once: true }));
40845
+ await new Promise((resolve4) => signal.addEventListener("abort", () => resolve4(), { once: true }));
40458
40846
  }
40459
40847
  async function loadPixelFieldUntilAbort({ signal }) {
40460
40848
  const field = loadPixelField();
@@ -40469,8 +40857,8 @@ async function loadPixelFieldUntilAbort({ signal }) {
40469
40857
  let resolveAbort = () => {
40470
40858
  return;
40471
40859
  };
40472
- const aborted3 = new Promise((resolve3) => {
40473
- resolveAbort = resolve3;
40860
+ const aborted3 = new Promise((resolve4) => {
40861
+ resolveAbort = resolve4;
40474
40862
  });
40475
40863
  const abort = () => {
40476
40864
  field.catch(() => {
@@ -40542,7 +40930,7 @@ function renderMark(field, frame, rows = markRows) {
40542
40930
  chunks.push(fg11("#000000")(`
40543
40931
  `));
40544
40932
  }
40545
- return new StyledText9(chunks);
40933
+ return new StyledText10(chunks);
40546
40934
  }
40547
40935
  function shimmerBrightness(field, column, sampleRow, columns, sampleRows, center) {
40548
40936
  const x0 = Math.floor(column / columns * field.width);
@@ -40562,7 +40950,7 @@ function grey(value) {
40562
40950
  return `#${hex5}${hex5}${hex5}`;
40563
40951
  }
40564
40952
  function renderSplashTitle() {
40565
- return new StyledText9([
40953
+ return new StyledText10([
40566
40954
  bold6(fg11(PALETTE.bodyText)("Remy CLI"))
40567
40955
  ]);
40568
40956
  }
@@ -40781,7 +41169,7 @@ ${approvalUrl}
40781
41169
  tokenPath: paths.tokenPath,
40782
41170
  candidatePath: candidateTokenPath,
40783
41171
  backupPath: previousTokenPath,
40784
- quarantinePath: join6(dirname6(paths.tokenPath), `.tokens.quarantine.${process.pid}.${randomUUID5()}.json`)
41172
+ quarantinePath: join7(dirname6(paths.tokenPath), `.tokens.quarantine.${process.pid}.${randomUUID5()}.json`)
40785
41173
  };
40786
41174
  await writeCredentialPromotionJournal({ stateDirectory: dirname6(paths.tokenPath), journal, ...dependencies.credentialStateFilesystem });
40787
41175
  journalPrepared = true;
@@ -41263,8 +41651,8 @@ function createSplashPresentation({
41263
41651
  let rejectReady = () => {
41264
41652
  return;
41265
41653
  };
41266
- ready = new Promise((resolve3, reject) => {
41267
- resolveReady = resolve3;
41654
+ ready = new Promise((resolve4, reject) => {
41655
+ resolveReady = resolve4;
41268
41656
  rejectReady = reject;
41269
41657
  });
41270
41658
  splash = (dependencies.showRemySplash ?? showRemySplash)({
@@ -41372,7 +41760,7 @@ async function openBareDashboard({
41372
41760
  return await dashboard({ dependencies, bootstrap });
41373
41761
  }
41374
41762
  async function delayMilliseconds(milliseconds) {
41375
- await new Promise((resolve3) => setTimeout(resolve3, milliseconds));
41763
+ await new Promise((resolve4) => setTimeout(resolve4, milliseconds));
41376
41764
  }
41377
41765
  async function completeDashboardSplash({
41378
41766
  dependencies,
@@ -41668,6 +42056,15 @@ async function runAttachedSession({
41668
42056
  const action = await renderRemyView({
41669
42057
  open: async () => await openSessionTui({
41670
42058
  controller,
42059
+ openBrowser: dependencies.openBrowser,
42060
+ downloadFile: async ({ file: file3, signal }) => await downloadSessionFile({
42061
+ client: operations.client,
42062
+ sessionId,
42063
+ file: file3,
42064
+ directory: join7(dependencies.environment.HOME ?? homedir3(), "Downloads"),
42065
+ signal,
42066
+ fetchClient: globalThis.fetch
42067
+ }),
41671
42068
  initialComposer: composerSnapshot,
41672
42069
  repositoryLabel: repositories.map((repository) => repository.fullName).join(", ") || undefined,
41673
42070
  submitMessage: async (submission) => {
@@ -41889,10 +42286,10 @@ async function collectAllUsers({
41889
42286
  }
41890
42287
  }
41891
42288
  function resolveSessionCachePathForCommand({ dependencies, sessionId }) {
41892
- const stateBase = dependencies.environment.XDG_STATE_HOME ?? (dependencies.environment.HOME ? join6(dependencies.environment.HOME, ".local/state") : undefined);
42289
+ const stateBase = dependencies.environment.XDG_STATE_HOME ?? (dependencies.environment.HOME ? join7(dependencies.environment.HOME, ".local/state") : undefined);
41893
42290
  if (!stateBase)
41894
42291
  throw new Error("HOME is required when XDG_STATE_HOME is not set; refusing to read Remy session cache.");
41895
- return join6(stateBase, "remy", "sessions", `${sessionId}.json`);
42292
+ return join7(stateBase, "remy", "sessions", `${sessionId}.json`);
41896
42293
  }
41897
42294
  function isInteractiveTerminal(dependencies) {
41898
42295
  return dependencies.isTerminal?.() ?? (process.stdin.isTTY === true && process.stdout.isTTY === true);
@@ -42165,8 +42562,8 @@ async function readOptionalCliConfiguration(path) {
42165
42562
  }
42166
42563
  async function createCandidateTokenPath(activeTokenPath) {
42167
42564
  const directory = dirname6(activeTokenPath);
42168
- await mkdir5(directory, { recursive: true, mode: 448 });
42169
- return join6(directory, `.tokens.${process.pid}.${randomUUID5()}.tmp`);
42565
+ await mkdir6(directory, { recursive: true, mode: 448 });
42566
+ return join7(directory, `.tokens.${process.pid}.${randomUUID5()}.tmp`);
42170
42567
  }
42171
42568
  async function removeCandidateToken({
42172
42569
  path,
@@ -42195,7 +42592,7 @@ async function removeCommittedTokenRecoveryArtifacts({
42195
42592
  for (const entry of entries) {
42196
42593
  if (!isCommittedTokenRecoveryArtifact(entry))
42197
42594
  continue;
42198
- await removeCandidateToken({ path: join6(directory, entry), removeTokenFile });
42595
+ await removeCandidateToken({ path: join7(directory, entry), removeTokenFile });
42199
42596
  }
42200
42597
  }
42201
42598
  function isCommittedTokenRecoveryArtifact(entry) {
@@ -42297,10 +42694,10 @@ async function runBrowserLauncher(command) {
42297
42694
  const [executable, ...arguments_] = command;
42298
42695
  if (!executable)
42299
42696
  throw new Error("Browser launcher command is empty.");
42300
- return await new Promise((resolve3, reject) => {
42697
+ return await new Promise((resolve4, reject) => {
42301
42698
  const child = spawn(executable, arguments_, { stdio: "ignore" });
42302
42699
  child.once("error", reject);
42303
- child.once("exit", (code) => resolve3(code ?? 1));
42700
+ child.once("exit", (code) => resolve4(code ?? 1));
42304
42701
  });
42305
42702
  }
42306
42703
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.19.0-rc.0",
3
+ "version": "1.19.0-rc.2",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {