chatroom-cli 1.80.1 → 1.82.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/dist/index.js CHANGED
@@ -77473,6 +77473,7 @@ var init_unresolved_decisions = () => {};
77473
77473
  // ../../services/backend/prompts/utils/handoff-report-template-body.ts
77474
77474
  function getHandoffReportTemplateBody(roleGuidanceContext) {
77475
77475
  return `<handoff-overview>
77476
+ <!-- For informational tasks (summaries, feedback, Q&amp;A with no code changes): put the complete primary answer in Summary and What changed — the user only sees this handoff. -->
77476
77477
  ## Summary
77477
77478
  <what was accomplished, in plain terms — no references to prior messages>
77478
77479
 
@@ -77834,7 +77835,7 @@ function getNativeHandoffTurnEndGuidance(nextRole) {
77834
77835
  if (nextRole.toLowerCase() === "user") {
77835
77836
  lines.push("The system delivers the next chatroom task when the user sends one.");
77836
77837
  } else {
77837
- lines.push(`The system delivers \`${nextRole}\`'s handback when they finish — do not poll \`messages list\` or sleep waiting.`);
77838
+ lines.push(`The system delivers \`${nextRole}\`'s handback when they finish — do not poll \`messages download\` while waiting. For history reconstruction tasks, \`messages download\` is the correct tool.`);
77838
77839
  }
77839
77840
  return lines.join(`
77840
77841
  `);
@@ -80525,29 +80526,19 @@ var init_skill = __esm(() => {
80525
80526
 
80526
80527
  // src/commands/messages/messages-fs-service.ts
80527
80528
  import * as nodeFs2 from "node:fs/promises";
80528
- function buildMessageMarkdown(msg) {
80529
+ function messageFilename(msg) {
80530
+ const sortPrefix = String(SORT_KEY_MAX - msg._creationTime).padStart(13, "0");
80531
+ const receiver = msg.targetRole ?? "all";
80532
+ return `${sortPrefix}_${msg.senderRole}-to-${receiver}_${msg._id}.md`;
80533
+ }
80534
+ function buildLinearMessageContent(msg) {
80529
80535
  const ts = new Date(msg._creationTime).toISOString();
80530
- const parts2 = [];
80531
- parts2.push("---");
80532
- parts2.push(`id: ${msg._id}`);
80533
- parts2.push(`createdAt: ${ts}`);
80534
- parts2.push(`senderRole: ${msg.senderRole}`);
80535
- parts2.push(`type: ${msg.type}`);
80536
- if (msg.targetRole)
80537
- parts2.push(`targetRole: ${msg.targetRole}`);
80538
- if (msg.classification)
80539
- parts2.push(`classification: ${msg.classification}`);
80540
- if (msg.taskStatus)
80541
- parts2.push(`taskStatus: ${msg.taskStatus}`);
80542
- if (msg.featureTitle)
80543
- parts2.push(`featureTitle: ${msg.featureTitle}`);
80544
- parts2.push("---");
80545
- parts2.push("");
80546
- parts2.push(msg.content);
80547
- return parts2.join(`
80548
- `);
80536
+ const receiver = msg.targetRole ? ` → ${msg.targetRole}` : "";
80537
+ return `${ts} | ${msg.senderRole}${receiver}
80538
+
80539
+ ${msg.content}`;
80549
80540
  }
80550
- var MessagesFsService, MessagesFsServiceLive;
80541
+ var MessagesFsService, MessagesFsServiceLive, SORT_KEY_MAX = 9999999999999;
80551
80542
  var init_messages_fs_service = __esm(() => {
80552
80543
  init_esm();
80553
80544
  MessagesFsService = class MessagesFsService extends exports_Context.Tag("MessagesFsService")() {
@@ -80560,10 +80551,186 @@ var init_messages_fs_service = __esm(() => {
80560
80551
  mkdir: (path3, opts) => exports_Effect.tryPromise({
80561
80552
  try: () => nodeFs2.mkdir(path3, opts),
80562
80553
  catch: (e) => e instanceof Error ? e : new Error(String(e))
80554
+ }),
80555
+ rm: (path3, opts) => exports_Effect.tryPromise({
80556
+ try: () => nodeFs2.rm(path3, opts),
80557
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
80563
80558
  })
80564
80559
  });
80565
80560
  });
80566
80561
 
80562
+ // src/commands/messages/download.ts
80563
+ var exports_download = {};
80564
+ __export(exports_download, {
80565
+ resolveDownloadOutputDir: () => resolveDownloadOutputDir,
80566
+ downloadMessagesEffect: () => downloadMessagesEffect,
80567
+ downloadMessages: () => downloadMessages
80568
+ });
80569
+ import * as nodePath2 from "node:path";
80570
+ function parseLimit(raw) {
80571
+ const cap = raw ?? DEFAULT_LIMIT;
80572
+ if (!Number.isFinite(cap) || cap < 1)
80573
+ return DEFAULT_LIMIT;
80574
+ return Math.min(Math.floor(cap), ABSOLUTE_MAX);
80575
+ }
80576
+ function resolveDownloadOutputDir(format4, cwd = process.cwd()) {
80577
+ const downloadId = new Date().toISOString().replace(/[:.]/g, "-");
80578
+ return nodePath2.resolve(cwd, ".chatroom", "downloads", "messages", format4, downloadId);
80579
+ }
80580
+ async function downloadMessages(chatroomId, options, deps) {
80581
+ const { getSessionId: getSessionId2, getOtherSessionUrls: getOtherSessionUrls2 } = await Promise.resolve().then(() => (init_storage(), exports_storage));
80582
+ const { getConvexClient: getConvexClient2, getConvexUrl: getConvexUrl2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
80583
+ const client4 = await getConvexClient2();
80584
+ const actualDeps = deps ?? {
80585
+ backend: {
80586
+ mutation: (ep, args2) => client4.mutation(ep, args2),
80587
+ query: (ep, args2) => client4.query(ep, args2)
80588
+ },
80589
+ session: { getSessionId: getSessionId2, getConvexUrl: getConvexUrl2, getOtherSessionUrls: getOtherSessionUrls2 }
80590
+ };
80591
+ const layer = commandServicesLayerFromDeps(actualDeps);
80592
+ const handler = (err) => {
80593
+ return exports_Effect.sync(() => {
80594
+ if (err._tag === "NotAuthenticated") {
80595
+ console.error(`❌ Not authenticated`);
80596
+ process.exit(1);
80597
+ } else if (err._tag === "InvalidChatroomId") {
80598
+ console.error(`❌ Invalid chatroom ID`);
80599
+ process.exit(1);
80600
+ } else if (err._tag === "QueryFailed") {
80601
+ console.error(`
80602
+ ❌ Error fetching messages: ${err.cause.message}`);
80603
+ process.exit(1);
80604
+ } else if (err._tag === "OutputDirError") {
80605
+ console.error(`
80606
+ ❌ Failed to create output directory: ${err.cause.message}`);
80607
+ process.exit(1);
80608
+ } else if (err._tag === "WriteFailed") {
80609
+ console.error(`
80610
+ ❌ Failed to write ${err.path}: ${err.cause.message}`);
80611
+ process.exit(1);
80612
+ } else {
80613
+ console.error(`
80614
+ ❌ Download failed: ${err.message}`);
80615
+ process.exit(1);
80616
+ }
80617
+ });
80618
+ };
80619
+ await exports_Effect.runPromise(downloadMessagesEffect(chatroomId, options).pipe(exports_Effect.catchAll(handler), exports_Effect.provide(exports_Layer.mergeAll(layer, MessagesFsServiceLive))));
80620
+ }
80621
+ var PAGE_SIZE = 50, ABSOLUTE_MAX = 5000, DEFAULT_LIMIT = 10, downloadMessagesEffect = (chatroomId, options) => exports_Effect.gen(function* () {
80622
+ const backend2 = yield* BackendService;
80623
+ const fs11 = yield* MessagesFsService;
80624
+ const sessionId = yield* requireSessionIdEffect((a) => ({
80625
+ _tag: "NotAuthenticated",
80626
+ convexUrl: a.convexUrl,
80627
+ otherUrls: a.otherUrls
80628
+ }));
80629
+ yield* validateChatroomIdEffect(chatroomId, (id3) => ({
80630
+ _tag: "InvalidChatroomId",
80631
+ id: id3
80632
+ }));
80633
+ const format4 = options.format ?? "linear";
80634
+ const maxDownload = parseLimit(options.limit);
80635
+ const outputDir = options.outputDir ?? resolveDownloadOutputDir(format4);
80636
+ const absoluteOutputDir = nodePath2.resolve(outputDir);
80637
+ const messages = [];
80638
+ let truncated = false;
80639
+ let hasMore = true;
80640
+ const firstPageSize = Math.min(PAGE_SIZE, maxDownload);
80641
+ const latest = yield* backend2.query(api.messageList.getLatestMessages, { sessionId, chatroomId, limit: firstPageSize });
80642
+ messages.push(...latest.messages);
80643
+ hasMore = latest.hasMore;
80644
+ while (hasMore && messages.length < maxDownload) {
80645
+ const oldest = messages[0];
80646
+ if (!oldest) {
80647
+ hasMore = false;
80648
+ break;
80649
+ }
80650
+ const remaining = maxDownload - messages.length;
80651
+ const pageSize = Math.min(PAGE_SIZE, remaining);
80652
+ const batch = yield* backend2.query(api.messageList.listMessagesBefore, {
80653
+ sessionId,
80654
+ chatroomId,
80655
+ before: oldest._creationTime,
80656
+ limit: pageSize
80657
+ });
80658
+ if (batch.length === 0) {
80659
+ hasMore = false;
80660
+ break;
80661
+ }
80662
+ const space = maxDownload - messages.length;
80663
+ const toPrepend = batch.slice(Math.max(0, batch.length - space));
80664
+ for (let i2 = toPrepend.length - 1;i2 >= 0; i2--)
80665
+ messages.unshift(toPrepend[i2]);
80666
+ hasMore = batch.length >= pageSize && messages.length < maxDownload;
80667
+ if (messages.length >= maxDownload && (batch.length >= pageSize || latest.hasMore))
80668
+ truncated = true;
80669
+ if (toPrepend.length < batch.length)
80670
+ truncated = true;
80671
+ }
80672
+ const complete3 = !truncated && !hasMore;
80673
+ yield* fs11.rm(outputDir, { recursive: true, force: true }).pipe(exports_Effect.catchAll(() => exports_Effect.void));
80674
+ yield* fs11.mkdir(outputDir, { recursive: true }).pipe(exports_Effect.mapError((cause3) => ({ _tag: "OutputDirError", cause: cause3 })));
80675
+ const manifestEntries = [];
80676
+ for (const msg of messages) {
80677
+ const file = messageFilename(msg);
80678
+ const content = buildLinearMessageContent(msg);
80679
+ const filePath = nodePath2.join(outputDir, file);
80680
+ yield* fs11.writeFile(filePath, content).pipe(exports_Effect.mapError((cause3) => ({
80681
+ _tag: "WriteFailed",
80682
+ path: filePath,
80683
+ cause: cause3
80684
+ })));
80685
+ manifestEntries.push({
80686
+ id: msg._id,
80687
+ file,
80688
+ createdAt: new Date(msg._creationTime).toISOString(),
80689
+ senderRole: msg.senderRole,
80690
+ targetRole: msg.targetRole
80691
+ });
80692
+ }
80693
+ const manifest = {
80694
+ chatroomId,
80695
+ downloadedAt: new Date().toISOString(),
80696
+ count: messages.length,
80697
+ complete: complete3,
80698
+ truncated,
80699
+ oldestDownloadedAt: messages[0] ? new Date(messages[0]._creationTime).toISOString() : null,
80700
+ newestDownloadedAt: messages[messages.length - 1] ? new Date(messages[messages.length - 1]._creationTime).toISOString() : null,
80701
+ messages: manifestEntries
80702
+ };
80703
+ const manifestPath = nodePath2.join(outputDir, "manifest.json");
80704
+ yield* fs11.writeFile(manifestPath, JSON.stringify(manifest, null, 2)).pipe(exports_Effect.mapError((cause3) => ({
80705
+ _tag: "WriteFailed",
80706
+ path: manifestPath,
80707
+ cause: cause3
80708
+ })));
80709
+ yield* exports_Effect.sync(() => {
80710
+ console.log(`
80711
+ ✅ Downloaded ${messages.length} messages to:`);
80712
+ console.log(` ${absoluteOutputDir}`);
80713
+ console.log(` complete=${complete3} truncated=${truncated}`);
80714
+ console.log(`
80715
+ \uD83D\uDCA1 Read recent history:`);
80716
+ console.log(` ls "${absoluteOutputDir}/"`);
80717
+ console.log(` cat "${absoluteOutputDir}/manifest.json"`);
80718
+ console.log(` rg "pattern" "${absoluteOutputDir}/"`);
80719
+ if (truncated) {
80720
+ const nextLimit = Math.min(messages.length * 2, ABSOLUTE_MAX);
80721
+ console.log(`
80722
+ \uD83D\uDCA1 Truncated — fetch more history by increasing --limit:`);
80723
+ console.log(` chatroom messages download --chatroom-id=${chatroomId} --role=${options.role} --format=linear --limit=${nextLimit}`);
80724
+ }
80725
+ });
80726
+ });
80727
+ var init_download = __esm(() => {
80728
+ init_esm();
80729
+ init_messages_fs_service();
80730
+ init_api3();
80731
+ init_services();
80732
+ });
80733
+
80567
80734
  // src/commands/messages/index.ts
80568
80735
  var exports_messages = {};
80569
80736
  __export(exports_messages, {
@@ -80571,10 +80738,8 @@ __export(exports_messages, {
80571
80738
  listSinceMessage: () => listSinceMessage,
80572
80739
  listBySenderRoleEffect: () => listBySenderRoleEffect,
80573
80740
  listBySenderRole: () => listBySenderRole,
80574
- exportMessagesEffect: () => exportMessagesEffect,
80575
- exportMessages: () => exportMessages
80741
+ downloadMessages: () => downloadMessages
80576
80742
  });
80577
- import * as nodePath2 from "node:path";
80578
80743
  async function createDefaultDeps14() {
80579
80744
  const client4 = await getConvexClient();
80580
80745
  return {
@@ -80645,20 +80810,6 @@ async function listSinceMessage(chatroomId, options, deps) {
80645
80810
  const layer = commandServicesLayerFromDeps(d);
80646
80811
  await exports_Effect.runPromise(listSinceMessageEffect(chatroomId, options).pipe(exports_Effect.catchAll((err) => handleMessagesError(err)), exports_Effect.provide(layer)));
80647
80812
  }
80648
- async function exportMessages(chatroomId, options, deps) {
80649
- const d = deps ?? await createDefaultDeps14();
80650
- const layer = commandServicesLayerFromDeps(d);
80651
- await exports_Effect.runPromise(exportMessagesEffect(chatroomId, options).pipe(exports_Effect.catchAll((err) => {
80652
- if ("_tag" in err) {
80653
- return handleMessagesError(err);
80654
- }
80655
- return exports_Effect.sync(() => {
80656
- console.error(`
80657
- ❌ Export failed: ${err.message}`);
80658
- process.exit(1);
80659
- });
80660
- }), exports_Effect.provide(exports_Layer.mergeAll(layer, MessagesFsServiceLive))));
80661
- }
80662
80813
  var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(function* () {
80663
80814
  const backend2 = yield* BackendService;
80664
80815
  const sessionId = yield* requireSessionIdEffect((a) => ({
@@ -80749,63 +80900,14 @@ ${roleIndicator} ${message.senderRole}${message.targetRole ? ` → ${message.tar
80749
80900
  ` + "─".repeat(60));
80750
80901
  console.log(`\uD83D\uDCA1 Use --full to see complete message content`);
80751
80902
  });
80752
- }), exportMessagesEffect = (chatroomId, options) => exports_Effect.gen(function* () {
80753
- const backend2 = yield* BackendService;
80754
- const fs11 = yield* MessagesFsService;
80755
- const sessionId = yield* requireSessionIdEffect((a) => ({
80756
- _tag: "NotAuthenticated",
80757
- convexUrl: a.convexUrl,
80758
- otherUrls: a.otherUrls
80759
- }));
80760
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
80761
- _tag: "InvalidChatroomId",
80762
- id: id3
80763
- }));
80764
- const messagesResult = yield* exports_Effect.either(backend2.query(api.messages.listBySenderRole, {
80765
- sessionId,
80766
- chatroomId,
80767
- senderRole: options.senderRole || "planner",
80768
- limit: options.limit
80769
- }));
80770
- if (messagesResult._tag === "Left") {
80771
- return yield* exports_Effect.fail({
80772
- _tag: "QueryFailed",
80773
- cause: messagesResult.left
80774
- });
80775
- }
80776
- const messages = messagesResult.right;
80777
- if (messages.length === 0) {
80778
- yield* exports_Effect.sync(() => console.log("No messages to export."));
80779
- return;
80780
- }
80781
- const dir = options.exportPath || `.chatroom/exports/messages/${chatroomId}`;
80782
- yield* fs11.mkdir(dir, { recursive: true });
80783
- const manifest = [];
80784
- for (const msg of messages) {
80785
- const filename = `${msg._creationTime}-${msg.senderRole}-${msg._id.slice(0, 8)}.md`;
80786
- const markdown = buildMessageMarkdown(msg);
80787
- yield* fs11.writeFile(nodePath2.join(dir, filename), markdown);
80788
- manifest.push({
80789
- id: msg._id,
80790
- senderRole: msg.senderRole,
80791
- createdAt: msg._creationTime,
80792
- filename
80793
- });
80794
- }
80795
- yield* fs11.writeFile(nodePath2.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2));
80796
- yield* exports_Effect.sync(() => {
80797
- console.log(`
80798
- \uD83D\uDCE8 Exported ${messages.length} messages to ${dir}`);
80799
- console.log(` Manifest: ${dir}/manifest.json`);
80800
- });
80801
80903
  });
80802
80904
  var init_messages = __esm(() => {
80803
80905
  init_esm();
80804
- init_messages_fs_service();
80805
80906
  init_api3();
80806
80907
  init_storage();
80807
80908
  init_client2();
80808
80909
  init_services();
80910
+ init_download();
80809
80911
  });
80810
80912
 
80811
80913
  // src/commands/context/format-new-context-error.ts
@@ -97229,7 +97331,7 @@ async function emitPhase(deps, event, phase, detail) {
97229
97331
  });
97230
97332
  }
97231
97333
  function sleep5(ms) {
97232
- return new Promise((resolve4) => setTimeout(resolve4, ms));
97334
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
97233
97335
  }
97234
97336
  async function waitForHarnessSessionId(deps, event, pid) {
97235
97337
  const initial = deps.agentMgr.getSlot(event.chatroomId, event.role);
@@ -97741,19 +97843,19 @@ class ProcessManager {
97741
97843
  this.pendingStops.clear();
97742
97844
  }
97743
97845
  waitForExit(runId, ms) {
97744
- return new Promise((resolve4) => {
97846
+ return new Promise((resolve5) => {
97745
97847
  const interval = 100;
97746
97848
  let elapsed3 = 0;
97747
97849
  const timer = setInterval(() => {
97748
97850
  if (!this.runningProcesses.has(runId)) {
97749
97851
  clearInterval(timer);
97750
- resolve4(true);
97852
+ resolve5(true);
97751
97853
  return;
97752
97854
  }
97753
97855
  elapsed3 += interval;
97754
97856
  if (elapsed3 >= ms) {
97755
97857
  clearInterval(timer);
97756
- resolve4(false);
97858
+ resolve5(false);
97757
97859
  }
97758
97860
  }, interval);
97759
97861
  });
@@ -97884,7 +97986,7 @@ var init_log_observer_sync = __esm(() => {
97884
97986
  });
97885
97987
 
97886
97988
  // src/commands/machine/daemon-start/handlers/process/output-store.ts
97887
- import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile6, rm } from "node:fs/promises";
97989
+ import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile6, rm as rm2 } from "node:fs/promises";
97888
97990
  import { tmpdir } from "node:os";
97889
97991
  import { join as join17 } from "node:path";
97890
97992
 
@@ -97941,7 +98043,7 @@ class TempFileOutputStore {
97941
98043
  }
97942
98044
  async destroy() {
97943
98045
  try {
97944
- await rm(this.state.filePath, { force: true });
98046
+ await rm2(this.state.filePath, { force: true });
97945
98047
  } catch {}
97946
98048
  }
97947
98049
  }
@@ -97957,7 +98059,7 @@ async function ensureTempDir() {
97957
98059
  }
97958
98060
  async function cleanOrphanTempFiles() {
97959
98061
  try {
97960
- await rm(TEMP_DIR, { recursive: true, force: true });
98062
+ await rm2(TEMP_DIR, { recursive: true, force: true });
97961
98063
  } catch {}
97962
98064
  }
97963
98065
  var TAIL_WINDOW_BYTES, TEMP_DIR, RUN_ID_RE, MAX_TAIL_LINES_V2 = 50;
@@ -98323,8 +98425,8 @@ var init_command_runner = __esm(() => {
98323
98425
  }).catch((err) => {
98324
98426
  console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark run as killed on shutdown: ${getErrorMessage(err)}`);
98325
98427
  })));
98326
- yield* exports_Effect.promise(() => new Promise((resolve4) => {
98327
- const t = setTimeout(resolve4, 3000);
98428
+ yield* exports_Effect.promise(() => new Promise((resolve5) => {
98429
+ const t = setTimeout(resolve5, 3000);
98328
98430
  t.unref?.();
98329
98431
  }));
98330
98432
  for (const [, tracked] of trackedEntries) {
@@ -98336,8 +98438,8 @@ var init_command_runner = __esm(() => {
98336
98438
  clearTrackedPids();
98337
98439
  yield* exports_Effect.promise(() => Promise.race([
98338
98440
  statusUpdates,
98339
- new Promise((resolve4) => {
98340
- const t = setTimeout(resolve4, 2000);
98441
+ new Promise((resolve5) => {
98442
+ const t = setTimeout(resolve5, 2000);
98341
98443
  t.unref?.();
98342
98444
  })
98343
98445
  ]));
@@ -98474,10 +98576,10 @@ function resolveWhichCommand(name) {
98474
98576
  return process.platform === "win32" ? `where ${name}` : `which ${name}`;
98475
98577
  }
98476
98578
  function isCliAvailable(cliName) {
98477
- return new Promise((resolve4) => {
98579
+ return new Promise((resolve5) => {
98478
98580
  const child = spawn5(resolveWhichCommand(cliName), [], DETACHED_SHELL_SPAWN_OPTIONS);
98479
- child.on("error", () => resolve4(false));
98480
- child.on("close", (code2) => resolve4(code2 === 0));
98581
+ child.on("error", () => resolve5(false));
98582
+ child.on("close", (code2) => resolve5(code2 === 0));
98481
98583
  child.unref();
98482
98584
  });
98483
98585
  }
@@ -98762,7 +98864,7 @@ async function waitForLockOrTimeout(deadline, intervalMs, sleep6) {
98762
98864
  async function acquireLockWithRetry(options) {
98763
98865
  const intervalMs = options?.intervalMs ?? LOCK_RETRY_INTERVAL_MS;
98764
98866
  const maxWaitMs = options?.maxWaitMs ?? LOCK_RETRY_MAX_WAIT_MS;
98765
- const sleep6 = options?.sleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
98867
+ const sleep6 = options?.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
98766
98868
  const deadline = Date.now() + maxWaitMs;
98767
98869
  if (await waitForLockOrTimeout(deadline, intervalMs, sleep6)) {
98768
98870
  return true;
@@ -99468,14 +99570,14 @@ var init_sse_event_buffer = __esm(() => {
99468
99570
  }
99469
99571
  _wake() {
99470
99572
  if (this._waiter) {
99471
- const resolve4 = this._waiter;
99573
+ const resolve5 = this._waiter;
99472
99574
  this._waiter = null;
99473
- resolve4();
99575
+ resolve5();
99474
99576
  }
99475
99577
  }
99476
99578
  _waitForData() {
99477
- return new Promise((resolve4) => {
99478
- this._waiter = resolve4;
99579
+ return new Promise((resolve5) => {
99580
+ this._waiter = resolve5;
99479
99581
  });
99480
99582
  }
99481
99583
  [Symbol.asyncIterator]() {
@@ -99530,8 +99632,8 @@ class OpencodeSdkSession {
99530
99632
  async prompt(input) {
99531
99633
  if (this.closed)
99532
99634
  throw new Error("Session is closed");
99533
- const idlePromise = new Promise((resolve4) => {
99534
- this._idleResolve = resolve4;
99635
+ const idlePromise = new Promise((resolve5) => {
99636
+ this._idleResolve = resolve5;
99535
99637
  });
99536
99638
  const IDLE_TIMEOUT_MS = 300000;
99537
99639
  const timeoutPromise = new Promise((_, reject) => {
@@ -99862,14 +99964,14 @@ class OpencodeSdkHarness {
99862
99964
  }
99863
99965
  this.sessionListeners.clear();
99864
99966
  this.childProcess.kill("SIGTERM");
99865
- await new Promise((resolve4) => {
99967
+ await new Promise((resolve5) => {
99866
99968
  const timeout3 = setTimeout(() => {
99867
99969
  this.childProcess.kill("SIGKILL");
99868
- resolve4();
99970
+ resolve5();
99869
99971
  }, 5000);
99870
99972
  this.childProcess.once("exit", () => {
99871
99973
  clearTimeout(timeout3);
99872
- resolve4();
99974
+ resolve5();
99873
99975
  });
99874
99976
  });
99875
99977
  }
@@ -101303,10 +101405,10 @@ class BufferedJournalFactory {
101303
101405
  const waitForInProgress = () => {
101304
101406
  if (!flushInProgress)
101305
101407
  return Promise.resolve();
101306
- return new Promise((resolve4) => {
101408
+ return new Promise((resolve5) => {
101307
101409
  const check4 = () => {
101308
101410
  if (!flushInProgress)
101309
- resolve4();
101411
+ resolve5();
101310
101412
  else
101311
101413
  setTimeout(check4, 10);
101312
101414
  };
@@ -101866,11 +101968,11 @@ async function waitForEnhancerJobResolution(params) {
101866
101968
  outcome = "failed";
101867
101969
  onFailure("Agent process exited without completing enhancer job", false);
101868
101970
  });
101869
- await new Promise((resolve4) => {
101971
+ await new Promise((resolve5) => {
101870
101972
  const check4 = setInterval(() => {
101871
101973
  if (outcome) {
101872
101974
  clearInterval(check4);
101873
- resolve4();
101975
+ resolve5();
101874
101976
  }
101875
101977
  }, 100);
101876
101978
  });
@@ -102148,7 +102250,7 @@ var init_assert_registered_working_dir = __esm(() => {
102148
102250
 
102149
102251
  // src/infrastructure/services/workspace/workspace-path-security.ts
102150
102252
  import { realpath as realpath2 } from "node:fs/promises";
102151
- import { basename, dirname as dirname8, isAbsolute, relative, resolve as resolve4, sep } from "node:path";
102253
+ import { basename, dirname as dirname8, isAbsolute, relative, resolve as resolve5, sep } from "node:path";
102152
102254
  import { gunzipSync as gunzipSync2 } from "node:zlib";
102153
102255
  function validateRelativePathSegments(filePath) {
102154
102256
  if (filePath.includes("\x00"))
@@ -102175,11 +102277,11 @@ async function resolvePathWithinWorkspace(workingDir, filePath) {
102175
102277
  return basic;
102176
102278
  let workspaceRoot;
102177
102279
  try {
102178
- workspaceRoot = await realpath2(resolve4(workingDir));
102280
+ workspaceRoot = await realpath2(resolve5(workingDir));
102179
102281
  } catch {
102180
102282
  return { ok: false, error: "Working directory not found" };
102181
102283
  }
102182
- const candidate = resolve4(workspaceRoot, filePath);
102284
+ const candidate = resolve5(workspaceRoot, filePath);
102183
102285
  if (!isPathInsideRoot(workspaceRoot, candidate)) {
102184
102286
  return { ok: false, error: "Path escapes workspace" };
102185
102287
  }
@@ -102201,7 +102303,7 @@ async function resolvePathWithinWorkspace(workingDir, filePath) {
102201
102303
  if (!isPathInsideRoot(workspaceRoot, parentReal)) {
102202
102304
  return { ok: false, error: "Path escapes workspace" };
102203
102305
  }
102204
- const resolved = resolve4(parentReal, basename(candidate));
102306
+ const resolved = resolve5(parentReal, basename(candidate));
102205
102307
  if (!isPathInsideRoot(workspaceRoot, resolved)) {
102206
102308
  return { ok: false, error: "Path escapes workspace" };
102207
102309
  }
@@ -104125,7 +104227,7 @@ class NodeFsHandler {
104125
104227
  this._addToNodeFs(path6, initialAdd, wh, depth + 1);
104126
104228
  }
104127
104229
  }).on(EV.ERROR, this._boundHandleError);
104128
- return new Promise((resolve6, reject) => {
104230
+ return new Promise((resolve7, reject) => {
104129
104231
  if (!stream4)
104130
104232
  return reject();
104131
104233
  stream4.once(STR_END, () => {
@@ -104134,7 +104236,7 @@ class NodeFsHandler {
104134
104236
  return;
104135
104237
  }
104136
104238
  const wasThrottled = throttler ? throttler.clear() : false;
104137
- resolve6(undefined);
104239
+ resolve7(undefined);
104138
104240
  previous.getChildren().filter((item) => {
104139
104241
  return item !== directory && !current.has(item);
104140
104242
  }).forEach((item) => {
@@ -105352,8 +105454,8 @@ function createWorkspaceFsWatcher(options) {
105352
105454
  awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 25 }
105353
105455
  });
105354
105456
  watcher.on("add", (filePath) => enqueue("add", filePath)).on("addDir", (filePath) => enqueue("addDir", filePath)).on("change", (filePath) => enqueue("change", filePath)).on("unlink", (filePath) => enqueue("unlink", filePath)).on("unlinkDir", (filePath) => enqueue("unlinkDir", filePath)).on("error", (error51) => options.onError?.(error51));
105355
- const ready = new Promise((resolve7) => {
105356
- watcher.once("ready", resolve7);
105457
+ const ready = new Promise((resolve8) => {
105458
+ watcher.once("ready", resolve8);
105357
105459
  });
105358
105460
  return {
105359
105461
  ready,
@@ -105978,7 +106080,7 @@ function unsupportedFileWriteOperationMessage(operation) {
105978
106080
  }
105979
106081
 
105980
106082
  // src/commands/machine/daemon-start/file-write-fulfillment.ts
105981
- import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
106083
+ import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm4, writeFile as writeFile9 } from "node:fs/promises";
105982
106084
  import { dirname as dirname11 } from "node:path";
105983
106085
  function isTerminalFileWriteError(errorMessage) {
105984
106086
  const terminalMessages = new Set([
@@ -106132,7 +106234,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
106132
106234
  });
106133
106235
  return;
106134
106236
  }
106135
- await rm3(resolved.absolutePath, { recursive: true, force: false });
106237
+ await rm4(resolved.absolutePath, { recursive: true, force: false });
106136
106238
  await completeWriteRequest(session2, request2._id, { status: "done" });
106137
106239
  const elapsed4 = Date.now() - startTime;
106138
106240
  console.log(`[${formatTimestamp()}] ✏️ File delete fulfilled: ${filePath} (${elapsed4}ms)`);
@@ -109544,7 +109646,7 @@ function createDefaultDeps19() {
109544
109646
  },
109545
109647
  clock: {
109546
109648
  now: () => Date.now(),
109547
- delay: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))
109649
+ delay: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))
109548
109650
  },
109549
109651
  spawning: new HarnessSpawningService({ rateLimiter: new SpawnRateLimiter }),
109550
109652
  agentProcessManager: null
@@ -110909,7 +111011,7 @@ var init_jsonc = __esm(() => {
110909
111011
 
110910
111012
  // src/infrastructure/services/workspace/workspace-resolver.ts
110911
111013
  import { readFile as readFile10, readdir as readdir3, stat as stat6 } from "node:fs/promises";
110912
- import { join as join22, basename as basename4, resolve as resolve7 } from "node:path";
111014
+ import { join as join22, basename as basename4, resolve as resolve8 } from "node:path";
110913
111015
  async function resolveGlobPatternStar(rootDir, cleaned) {
110914
111016
  const parentDir = join22(rootDir, cleaned.slice(0, -2));
110915
111017
  try {
@@ -110919,7 +111021,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
110919
111021
  if (!entry.isDirectory())
110920
111022
  continue;
110921
111023
  const dirPath = join22(parentDir, entry.name);
110922
- if (resolve7(dirPath).startsWith(resolve7(rootDir))) {
111024
+ if (resolve8(dirPath).startsWith(resolve8(rootDir))) {
110923
111025
  dirs.push(dirPath);
110924
111026
  }
110925
111027
  }
@@ -110931,7 +111033,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
110931
111033
  async function resolveLiteralPath(rootDir, cleaned) {
110932
111034
  const dir = join22(rootDir, cleaned);
110933
111035
  try {
110934
- if (!resolve7(dir).startsWith(resolve7(rootDir)))
111036
+ if (!resolve8(dir).startsWith(resolve8(rootDir)))
110935
111037
  return [];
110936
111038
  const s = await stat6(dir);
110937
111039
  if (s.isDirectory())
@@ -111808,7 +111910,7 @@ var init_layers = __esm(() => {
111808
111910
  IntervalClock = class IntervalClock extends exports_Context.Tag("IntervalClock")() {
111809
111911
  };
111810
111912
  IntervalClockLive = exports_Layer.succeed(IntervalClock, {
111811
- sleep: (ms) => exports_Effect.promise(() => new Promise((resolve8) => setTimeout(resolve8, ms)))
111913
+ sleep: (ms) => exports_Effect.promise(() => new Promise((resolve9) => setTimeout(resolve9, ms)))
111812
111914
  });
111813
111915
  });
111814
111916
 
@@ -112910,8 +113012,8 @@ var init_command_loop = __esm(() => {
112910
113012
  const withTimeout2 = async (p, ms) => {
112911
113013
  await Promise.race([
112912
113014
  Promise.resolve(p).catch(() => {}),
112913
- new Promise((resolve8) => {
112914
- const t = setTimeout(resolve8, ms);
113015
+ new Promise((resolve9) => {
113016
+ const t = setTimeout(resolve9, ms);
112915
113017
  t.unref?.();
112916
113018
  })
112917
113019
  ]);
@@ -113076,7 +113178,7 @@ async function daemonStop() {
113076
113178
  console.log(`Stopping daemon (PID: ${pid})...`);
113077
113179
  try {
113078
113180
  process.kill(pid, "SIGTERM");
113079
- await new Promise((resolve8) => setTimeout(resolve8, 8000));
113181
+ await new Promise((resolve9) => setTimeout(resolve9, 8000));
113080
113182
  try {
113081
113183
  process.kill(pid, 0);
113082
113184
  console.log(`Process did not exit gracefully, forcing...`);
@@ -113645,7 +113747,7 @@ async function withRetry(fn2, opts) {
113645
113747
  return;
113646
113748
  }
113647
113749
  const delay3 = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
113648
- await new Promise((resolve8) => setTimeout(resolve8, delay3));
113750
+ await new Promise((resolve9) => setTimeout(resolve9, delay3));
113649
113751
  }
113650
113752
  }
113651
113753
  return;
@@ -114032,6 +114134,28 @@ messagesCommand.command("list").description("List messages by sender role or sin
114032
114134
  });
114033
114135
  }
114034
114136
  });
114137
+ messagesCommand.command("download").description("Download chatroom message history to local files for reading/grep").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").option("--format <format>", "Download format (default: linear)", "linear").option("--output-dir <path>", "Output directory (default: .chatroom/downloads/messages/linear/<download-id>)").option("--limit <n>", "Max messages to download (default: 10, max: 5000)").action(async (options) => {
114138
+ await maybeRequireAuth();
114139
+ if (options.format && options.format !== "linear") {
114140
+ console.error("❌ Unsupported format. Only --format=linear is supported.");
114141
+ process.exit(1);
114142
+ }
114143
+ let parsedLimit;
114144
+ if (options.limit) {
114145
+ parsedLimit = parseInt(options.limit, 10);
114146
+ if (isNaN(parsedLimit) || parsedLimit < 1) {
114147
+ console.error("❌ --limit must be a positive integer (1-5000)");
114148
+ process.exit(1);
114149
+ }
114150
+ }
114151
+ const { downloadMessages: downloadMessages2 } = await Promise.resolve().then(() => (init_download(), exports_download));
114152
+ await downloadMessages2(options.chatroomId, {
114153
+ role: options.role,
114154
+ format: "linear",
114155
+ outputDir: options.outputDir,
114156
+ limit: parsedLimit
114157
+ });
114158
+ });
114035
114159
  var contextCommand = program2.command("context").description("Manage chatroom context and state (explicit context management)");
114036
114160
  contextCommand.command("read").description("Read context for your role (conversation history, tasks, status)").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Your role").action(async (options) => {
114037
114161
  await maybeRequireAuth();
@@ -114177,4 +114301,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
114177
114301
  });
114178
114302
  program2.parse();
114179
114303
 
114180
- //# debugId=DFC337C81483539964756E2164756E21
114304
+ //# debugId=7AA8EA8DC834B0AF64756E2164756E21