chatroom-cli 1.77.1 → 1.77.4

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
@@ -77251,7 +77251,7 @@ function getDelegationBriefDisclosureBlock() {
77251
77251
  return `${DELEGATION_BRIEF_DISCLOSURE_CHECKBOX}
77252
77252
  ${getDelegationBriefDisclosureComment()}`;
77253
77253
  }
77254
- var DELEGATION_BRIEF_DISCLOSURE_CHECKBOX = "- [ ] I confirm that the goal and acceptance criteria from the planner’s delegation brief have been met";
77254
+ var DELEGATION_BRIEF_DISCLOSURE_CHECKBOX = "- [ ] I confirm the delegation brief is fully met: all (Required) files done, verified end-to-end, acceptance criteria pass";
77255
77255
 
77256
77256
  // ../../services/backend/prompts/utils/file-reference-guidance.ts
77257
77257
  function getFileReferenceGuidanceComment() {
@@ -77423,7 +77423,7 @@ ${getDelegationBriefIntro()}
77423
77423
  <choices that greatly simplify the solution while preserving long-term maintainability — reuse existing abstractions, avoid unnecessary layers, leverage platform conventions>
77424
77424
 
77425
77425
  ## Files to implement (exhaustive, file-level)
77426
- List **every** file in this slice. For each file, state the exact change and paste the code the builder should match (no guessing).
77426
+ List **every** file in this slice. Mark each file **(Required)** or **(Optional)** — all Required files must land before PR. For each file, state the exact change and paste the code the builder should match (no guessing).
77427
77427
  ${getFileReferenceGuidanceComment()}
77428
77428
 
77429
77429
  ### \`apps/webapp/src/path/to/file.ts\`
@@ -77458,6 +77458,7 @@ Cross-file types, interfaces, or patterns that apply beyond a single file. Omit
77458
77458
 
77459
77459
  ## Requirements (acceptance criteria)
77460
77460
  - <verifiable outcome the builder can self-check>
77461
+ - Include at least one check that the feature is **verified end-to-end**. Unit tests alone are insufficient for new features.
77461
77462
 
77462
77463
  ## What to avoid
77463
77464
  - <anti-patterns, recurring mistakes, or scope creep for this slice — be explicit>
@@ -77582,7 +77583,7 @@ ${getFileReferenceProofOfCompletionExample()}
77582
77583
  <Omit if no backlog items were in scope.>
77583
77584
 
77584
77585
  ## Backlog Pending User Review Confirmation
77585
- - [ ] I confirm that every backlog item implemented in this work has been moved to \`pending_user_review\` via \`chatroom backlog mark-for-review\` because a PR has been raised for user review
77586
+ - [ ] I confirm that every backlog item implemented in this work has been moved to \`pending_user_review\` via \`chatroom backlog mark-for-review\` after the feature was verified end-to-end and a PR was raised for user review
77586
77587
  - PR URL(s): <link to PR(s)>
77587
77588
  <Omit this section if no backlog items apply.>
77588
77589
 
@@ -77610,9 +77611,6 @@ ${getUnresolvedDecisionsSectionBlock()}
77610
77611
 
77611
77612
  ## Notes
77612
77613
  <anything the user should know — context, caveats, or observations not covered above. Omit if none.>
77613
-
77614
- ## Next steps
77615
- <follow-up actions for the user or team. Omit if none.>
77616
77614
  \`\`\``;
77617
77615
  }
77618
77616
  var init_planner_to_user = __esm(() => {
@@ -77687,7 +77685,7 @@ ${getFileReferenceProofOfCompletionExample()}
77687
77685
  <Omit if no backlog items were in scope.>
77688
77686
 
77689
77687
  ## Backlog Pending User Review Confirmation
77690
- - [ ] I confirm that every backlog item implemented in this work has been moved to \`pending_user_review\` via \`chatroom backlog mark-for-review\` because a PR has been raised for user review
77688
+ - [ ] I confirm that every backlog item implemented in this work has been moved to \`pending_user_review\` via \`chatroom backlog mark-for-review\` after the feature was verified end-to-end and a PR was raised for user review
77691
77689
  - PR URL(s): <link to PR(s)>
77692
77690
  <Omit this section if no backlog items apply.>
77693
77691
 
@@ -77715,9 +77713,6 @@ ${getUnresolvedDecisionsSectionBlock()}
77715
77713
 
77716
77714
  ## Notes
77717
77715
  <anything the user should know — context, caveats, or observations not covered above. Omit if none.>
77718
-
77719
- ## Next steps
77720
- <follow-up actions for the user or team. Omit if none.>
77721
77716
  \`\`\``;
77722
77717
  }
77723
77718
  var init_solo_to_user = __esm(() => {
@@ -78814,6 +78809,10 @@ ${getBuilderFlowMermaid(nativeIntegration, codeChangesTarget, questionTarget)}
78814
78809
  - Follow established patterns and best practices from the codebase
78815
78810
  - Handle edge cases and error scenarios
78816
78811
  - Commit work with descriptive, atomic commit messages
78812
+
78813
+ **Completion gates (before PR or handoff):**
78814
+ - All **(Required)** files done; **verified end-to-end** (user-facing entry point works: CLI command runnable, API reachable, or UI action functional)
78815
+ - If blocked → ## Blockers / questions to planner. No PR or \`mark-for-review\` until gates pass — unless the user explicitly requested a draft or incremental PR
78817
78816
  `;
78818
78817
  }
78819
78818
  var init_builder = () => {};
@@ -78885,18 +78884,21 @@ flowchart TD
78885
78884
  - **Spell out what to avoid** — anti-patterns and recurring mistakes you have seen from builders on similar work (scope creep, wrong abstractions, forbidden refactors).
78886
78885
  - **One slice ≈ one focused review surface.** If you can't imagine reviewing it in one sitting, split it.
78887
78886
  - **Order by dependency**, not by team convention. A slice should be runnable/testable when its dependencies are done.
78887
+ - **A slice is shippable only when verified end-to-end** — infra/helper files alone are not a complete slice.
78888
78888
  - **Skip phases that don't apply** (e.g., no frontend for a backend-only change, no schema for a pure refactor).
78889
78889
 
78890
78890
  **Code review:** For code-producing work, review before delivering. Activate the review framework with: ${cmd("skill activate code-review")}.
78891
78891
 
78892
78892
  **Backlog items:** When the task originates from a backlog item, activate the backlog skill: ${cmd("skill activate backlog")}.
78893
78893
 
78894
- **If stuck:** After 2 failed rework attempts → step back, replan the slice, or deliver partial results with a clear explanation.
78894
+ **If stuck:** After 2 failed rework attempts → replan or hand back to planner as blocked. No partial PR, \`mark-for-review\`, or user delivery.
78895
78895
 
78896
78896
  **Review loop:**
78897
78897
  - Review completed work before moving to the next slice.
78898
78898
  - Send back with specific feedback if requirements aren't met.
78899
- - ${feedingNote}.`;
78899
+ - ${feedingNote}.
78900
+
78901
+ **When enhancement is enabled:** See \`<handoff-enhancer>\` in task delivery — one check-in per delegation before builder.`;
78900
78902
  }
78901
78903
  function getDelegationGuidelinesSection(config3, options) {
78902
78904
  const feedingNote = config3.hasBuilder ? "Feed slices to the builder incrementally — one at a time, not all at once" : "When implementing yourself, tackle one layer at a time — avoid large monolithic changes";
@@ -78914,9 +78916,10 @@ function getDelegationGuidelinesSection(config3, options) {
78914
78916
  function buildHandoffRuleLines(config3) {
78915
78917
  return [
78916
78918
  config3.hasBuilder ? "- **To delegate implementation** → Hand off to `builder` with clear requirements" : "- **To implement** → Work on the chatroom task directly (you are acting as implementer)",
78919
+ config3.hasBuilder ? "- **When enhancement is enabled** → See `<handoff-enhancer>` in task delivery before each builder delegation" : null,
78917
78920
  "- **To deliver to user** → Hand off to `user` with a complete, standalone summary\n ⚠️ The user can ONLY see the handoff-to-user message — progress reports and all other messages are invisible to them. Write the handoff as a self-contained document: include all relevant context, results, and next steps without assuming the user read any prior conversation.",
78918
78921
  config3.hasBuilder ? "- **For rework** → Hand off back to `builder` with specific feedback on what needs to change" : "- **For rework** → Revise your implementation directly and re-validate"
78919
- ].join(`
78922
+ ].filter((line) => line !== null).join(`
78920
78923
  `);
78921
78924
  }
78922
78925
  function getHandoffRulesSection(config3, nativeIntegration) {
@@ -78932,7 +78935,7 @@ var init_handoff_rules = () => {};
78932
78935
 
78933
78936
  // ../../services/backend/prompts/cli/sections/when-work-comes-back.ts
78934
78937
  function getWhenWorkComesBackSection(config3) {
78935
- const reworkLine = config3.hasBuilder ? "3. If requirements are NOT met → hand back to `builder` for rework" : "3. If requirements are NOT met revise your own implementation and re-validate";
78938
+ const reworkLine = config3.hasBuilder ? "3. If requirements are NOT met (including partial work) → hand back to `builder` for rework" : "3. If requirements are NOT met (including partial work) revise and re-validate";
78936
78939
  return `**When you receive work back from team members:**
78937
78940
  1. Review the completed work against the original user request
78938
78941
  2. If requirements are met → deliver to \`user\`
@@ -82333,7 +82336,7 @@ function renderBacklogAttachments(items, ctx) {
82333
82336
  for (const item of items) {
82334
82337
  lines.push(` <attachment type="backlog" backlog-item-id="${escapeXmlAttribute(item._id)}">`);
82335
82338
  lines.push(...xmlTextElement("content", item.content, " "));
82336
- lines.push(` <hint>Work on this item. When done: chatroom backlog mark-for-review --chatroom-id="${escapeXmlAttribute(ctx.chatroomId)}" --role="${escapeXmlAttribute(ctx.role)}" --backlog-item-id=${item._id}</hint>`);
82339
+ lines.push(` <hint>Work on this item. Verified end-to-end + PR ready: chatroom backlog mark-for-review --chatroom-id="${escapeXmlAttribute(ctx.chatroomId)}" --role="${escapeXmlAttribute(ctx.role)}" --backlog-item-id=${item._id}</hint>`);
82337
82340
  lines.push(` </attachment>`);
82338
82341
  }
82339
82342
  return lines;
@@ -82439,7 +82442,7 @@ function renderContextSection(input, chatroomId, role) {
82439
82442
  "</context>"
82440
82443
  ];
82441
82444
  if (ctx.triggerMessageContent) {
82442
- lines.push(`(For the message that triggered this context, run: chatroom context read --chatroom-id="${chatroomId}" --role="${role}")`);
82445
+ lines.push(`(Trigger: chatroom context read --chatroom-id="${chatroomId}" --role="${role}")`);
82443
82446
  }
82444
82447
  lines.push(...renderStalenessNotice(ctx.elapsedHours));
82445
82448
  return lines;
@@ -82451,7 +82454,7 @@ function renderTaskPrompt(input) {
82451
82454
  lines.push(` Task ID: ${taskId}`);
82452
82455
  lines.push(` Status: ${status3}`);
82453
82456
  if (input.context) {
82454
- lines.push("On conflict, the message wins over background context.");
82457
+ lines.push("On conflict, message wins over background context.");
82455
82458
  }
82456
82459
  lines.push(...renderDivergenceWarnings(input));
82457
82460
  lines.push(`
@@ -82748,14 +82751,58 @@ var init_skill = __esm(() => {
82748
82751
  init_convex_error();
82749
82752
  });
82750
82753
 
82754
+ // src/commands/messages/messages-fs-service.ts
82755
+ import * as nodeFs2 from "node:fs/promises";
82756
+ function buildMessageMarkdown(msg) {
82757
+ const ts = new Date(msg._creationTime).toISOString();
82758
+ const parts2 = [];
82759
+ parts2.push("---");
82760
+ parts2.push(`id: ${msg._id}`);
82761
+ parts2.push(`createdAt: ${ts}`);
82762
+ parts2.push(`senderRole: ${msg.senderRole}`);
82763
+ parts2.push(`type: ${msg.type}`);
82764
+ if (msg.targetRole)
82765
+ parts2.push(`targetRole: ${msg.targetRole}`);
82766
+ if (msg.classification)
82767
+ parts2.push(`classification: ${msg.classification}`);
82768
+ if (msg.taskStatus)
82769
+ parts2.push(`taskStatus: ${msg.taskStatus}`);
82770
+ if (msg.featureTitle)
82771
+ parts2.push(`featureTitle: ${msg.featureTitle}`);
82772
+ parts2.push("---");
82773
+ parts2.push("");
82774
+ parts2.push(msg.content);
82775
+ return parts2.join(`
82776
+ `);
82777
+ }
82778
+ var MessagesFsService, MessagesFsServiceLive;
82779
+ var init_messages_fs_service = __esm(() => {
82780
+ init_esm();
82781
+ MessagesFsService = class MessagesFsService extends exports_Context.Tag("MessagesFsService")() {
82782
+ };
82783
+ MessagesFsServiceLive = exports_Layer.succeed(MessagesFsService, {
82784
+ writeFile: (path3, data) => exports_Effect.tryPromise({
82785
+ try: () => nodeFs2.writeFile(path3, data, "utf-8"),
82786
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
82787
+ }),
82788
+ mkdir: (path3, opts) => exports_Effect.tryPromise({
82789
+ try: () => nodeFs2.mkdir(path3, opts),
82790
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
82791
+ })
82792
+ });
82793
+ });
82794
+
82751
82795
  // src/commands/messages/index.ts
82752
82796
  var exports_messages = {};
82753
82797
  __export(exports_messages, {
82754
82798
  listSinceMessageEffect: () => listSinceMessageEffect,
82755
82799
  listSinceMessage: () => listSinceMessage,
82756
82800
  listBySenderRoleEffect: () => listBySenderRoleEffect,
82757
- listBySenderRole: () => listBySenderRole
82801
+ listBySenderRole: () => listBySenderRole,
82802
+ exportMessagesEffect: () => exportMessagesEffect,
82803
+ exportMessages: () => exportMessages
82758
82804
  });
82805
+ import * as nodePath2 from "node:path";
82759
82806
  async function createDefaultDeps14() {
82760
82807
  const client4 = await getConvexClient();
82761
82808
  return {
@@ -82826,6 +82873,20 @@ async function listSinceMessage(chatroomId, options, deps) {
82826
82873
  const layer = commandServicesLayerFromDeps(d);
82827
82874
  await exports_Effect.runPromise(listSinceMessageEffect(chatroomId, options).pipe(exports_Effect.catchAll((err) => handleMessagesError(err)), exports_Effect.provide(layer)));
82828
82875
  }
82876
+ async function exportMessages(chatroomId, options, deps) {
82877
+ const d = deps ?? await createDefaultDeps14();
82878
+ const layer = commandServicesLayerFromDeps(d);
82879
+ await exports_Effect.runPromise(exportMessagesEffect(chatroomId, options).pipe(exports_Effect.catchAll((err) => {
82880
+ if ("_tag" in err) {
82881
+ return handleMessagesError(err);
82882
+ }
82883
+ return exports_Effect.sync(() => {
82884
+ console.error(`
82885
+ ❌ Export failed: ${err.message}`);
82886
+ process.exit(1);
82887
+ });
82888
+ }), exports_Effect.provide(exports_Layer.mergeAll(layer, MessagesFsServiceLive))));
82889
+ }
82829
82890
  var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(function* () {
82830
82891
  const backend2 = yield* BackendService;
82831
82892
  const sessionId = yield* requireSessionIdEffect((a) => ({
@@ -82916,9 +82977,59 @@ ${roleIndicator} ${message.senderRole}${message.targetRole ? ` → ${message.tar
82916
82977
  ` + "─".repeat(60));
82917
82978
  console.log(`\uD83D\uDCA1 Use --full to see complete message content`);
82918
82979
  });
82980
+ }), exportMessagesEffect = (chatroomId, options) => exports_Effect.gen(function* () {
82981
+ const backend2 = yield* BackendService;
82982
+ const fs11 = yield* MessagesFsService;
82983
+ const sessionId = yield* requireSessionIdEffect((a) => ({
82984
+ _tag: "NotAuthenticated",
82985
+ convexUrl: a.convexUrl,
82986
+ otherUrls: a.otherUrls
82987
+ }));
82988
+ yield* validateChatroomIdEffect(chatroomId, (id3) => ({
82989
+ _tag: "InvalidChatroomId",
82990
+ id: id3
82991
+ }));
82992
+ const messagesResult = yield* exports_Effect.either(backend2.query(api.messages.listBySenderRole, {
82993
+ sessionId,
82994
+ chatroomId,
82995
+ senderRole: options.senderRole || "planner",
82996
+ limit: options.limit
82997
+ }));
82998
+ if (messagesResult._tag === "Left") {
82999
+ return yield* exports_Effect.fail({
83000
+ _tag: "QueryFailed",
83001
+ cause: messagesResult.left
83002
+ });
83003
+ }
83004
+ const messages = messagesResult.right;
83005
+ if (messages.length === 0) {
83006
+ yield* exports_Effect.sync(() => console.log("No messages to export."));
83007
+ return;
83008
+ }
83009
+ const dir = options.exportPath || `.chatroom/exports/messages/${chatroomId}`;
83010
+ yield* fs11.mkdir(dir, { recursive: true });
83011
+ const manifest = [];
83012
+ for (const msg of messages) {
83013
+ const filename = `${msg._creationTime}-${msg.senderRole}-${msg._id.slice(0, 8)}.md`;
83014
+ const markdown = buildMessageMarkdown(msg);
83015
+ yield* fs11.writeFile(nodePath2.join(dir, filename), markdown);
83016
+ manifest.push({
83017
+ id: msg._id,
83018
+ senderRole: msg.senderRole,
83019
+ createdAt: msg._creationTime,
83020
+ filename
83021
+ });
83022
+ }
83023
+ yield* fs11.writeFile(nodePath2.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2));
83024
+ yield* exports_Effect.sync(() => {
83025
+ console.log(`
83026
+ \uD83D\uDCE8 Exported ${messages.length} messages to ${dir}`);
83027
+ console.log(` Manifest: ${dir}/manifest.json`);
83028
+ });
82919
83029
  });
82920
83030
  var init_messages = __esm(() => {
82921
83031
  init_esm();
83032
+ init_messages_fs_service();
82922
83033
  init_api3();
82923
83034
  init_storage();
82924
83035
  init_client2();
@@ -99681,17 +99792,17 @@ import {
99681
99792
  writeFileSync as writeFileSync3
99682
99793
  } from "node:fs";
99683
99794
  import { homedir as homedir5 } from "node:os";
99684
- import { join as join15 } from "node:path";
99795
+ import { join as join16 } from "node:path";
99685
99796
  function getUrlHash() {
99686
99797
  const url3 = getConvexUrl();
99687
99798
  return createHash2("sha256").update(url3).digest("hex").substring(0, 8);
99688
99799
  }
99689
99800
  function getChildPidsFilePath() {
99690
- const dir = join15(homedir5(), ".chatroom");
99691
- return join15(dir, `daemon-children-${getUrlHash()}.pids`);
99801
+ const dir = join16(homedir5(), ".chatroom");
99802
+ return join16(dir, `daemon-children-${getUrlHash()}.pids`);
99692
99803
  }
99693
99804
  function ensureChatroomDir2() {
99694
- const dir = join15(homedir5(), ".chatroom");
99805
+ const dir = join16(homedir5(), ".chatroom");
99695
99806
  if (!existsSync5(dir)) {
99696
99807
  mkdirSync4(dir, { recursive: true, mode: 448 });
99697
99808
  }
@@ -100021,9 +100132,9 @@ var init_log_observer_sync = __esm(() => {
100021
100132
  });
100022
100133
 
100023
100134
  // src/commands/machine/daemon-start/handlers/process/output-store.ts
100024
- import { appendFile as appendFile2, mkdir as mkdir7, readFile as readFile6, rm } from "node:fs/promises";
100135
+ import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile6, rm } from "node:fs/promises";
100025
100136
  import { tmpdir } from "node:os";
100026
- import { join as join16 } from "node:path";
100137
+ import { join as join17 } from "node:path";
100027
100138
 
100028
100139
  class TempFileOutputStore {
100029
100140
  state;
@@ -100086,11 +100197,11 @@ function createOutputStore(runId) {
100086
100197
  if (!RUN_ID_RE.test(runId)) {
100087
100198
  throw new Error(`Invalid runId: ${runId}`);
100088
100199
  }
100089
- const filePath = join16(TEMP_DIR, `${runId}.log`);
100200
+ const filePath = join17(TEMP_DIR, `${runId}.log`);
100090
100201
  return new TempFileOutputStore(filePath);
100091
100202
  }
100092
100203
  async function ensureTempDir() {
100093
- await mkdir7(TEMP_DIR, { recursive: true });
100204
+ await mkdir8(TEMP_DIR, { recursive: true });
100094
100205
  }
100095
100206
  async function cleanOrphanTempFiles() {
100096
100207
  try {
@@ -100100,7 +100211,7 @@ async function cleanOrphanTempFiles() {
100100
100211
  var TAIL_WINDOW_BYTES, TEMP_DIR, RUN_ID_RE, MAX_TAIL_LINES_V2 = 50;
100101
100212
  var init_output_store = __esm(() => {
100102
100213
  TAIL_WINDOW_BYTES = 32 * 1024;
100103
- TEMP_DIR = join16(tmpdir(), "chatroom-cli", "runs");
100214
+ TEMP_DIR = join17(tmpdir(), "chatroom-cli", "runs");
100104
100215
  RUN_ID_RE = /^[a-z0-9]+$/i;
100105
100216
  });
100106
100217
 
@@ -100791,7 +100902,7 @@ var init_pick_folder = () => {};
100791
100902
  import { createHash as createHash3 } from "node:crypto";
100792
100903
  import { existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync5 } from "node:fs";
100793
100904
  import { homedir as homedir6 } from "node:os";
100794
- import { join as join17 } from "node:path";
100905
+ import { join as join18 } from "node:path";
100795
100906
  function getUrlHash2() {
100796
100907
  const url3 = getConvexUrl();
100797
100908
  return createHash3("sha256").update(url3).digest("hex").substring(0, 8);
@@ -100805,7 +100916,7 @@ function ensureChatroomDir3() {
100805
100916
  }
100806
100917
  }
100807
100918
  function getPidFilePath() {
100808
- return join17(CHATROOM_DIR4, getPidFileName());
100919
+ return join18(CHATROOM_DIR4, getPidFileName());
100809
100920
  }
100810
100921
  function isProcessRunning(pid) {
100811
100922
  try {
@@ -100902,7 +101013,7 @@ function releaseLock() {
100902
101013
  var CHATROOM_DIR4, LOCK_RETRY_INTERVAL_MS = 500, LOCK_RETRY_MAX_WAIT_MS = 15000;
100903
101014
  var init_pid = __esm(() => {
100904
101015
  init_client2();
100905
- CHATROOM_DIR4 = join17(homedir6(), ".chatroom");
101016
+ CHATROOM_DIR4 = join18(homedir6(), ".chatroom");
100906
101017
  });
100907
101018
 
100908
101019
  // src/infrastructure/harnesses/claude-sdk/claude-session.ts
@@ -103884,7 +103995,7 @@ var init_start_subscriptions2 = __esm(() => {
103884
103995
  });
103885
103996
 
103886
103997
  // src/commands/machine/daemon-start/enhancer/constants.ts
103887
- var ENHANCER_AGENT_ROLE = "enhancer";
103998
+ var ENHANCER_AGENT_ROLE = "enhancer", ENHANCER_AGENT_END_GRACE_MS = 3000, ENHANCER_JOB_POLL_INTERVAL_MS = 500, ENHANCER_SILENCE_TIMEOUT_MS = 120000;
103888
103999
 
103889
104000
  // src/commands/machine/daemon-start/enhancer/enhancer-log.ts
103890
104001
  function formatEnhancerLogLine(message) {
@@ -103899,6 +104010,114 @@ function writeEnhancerLog(message) {
103899
104010
  }
103900
104011
  var ENHANCER_LOG_PREFIX = "[enhancer]";
103901
104012
 
104013
+ // src/commands/machine/daemon-start/enhancer/wait-for-enhancer-job.ts
104014
+ async function waitForEnhancerJobResolution(params) {
104015
+ const { sessionId, chatroomId, jobId, backend: backend2, onFailure, onSalvageComplete } = params;
104016
+ let outcome = null;
104017
+ let lastActivityAt = Date.now();
104018
+ let salvagedText = "";
104019
+ const pollInterval = setInterval(async () => {
104020
+ if (outcome)
104021
+ return;
104022
+ try {
104023
+ const status3 = await backend2.query(api.web.enhancer.index.getJob, {
104024
+ sessionId,
104025
+ chatroomId,
104026
+ jobId
104027
+ });
104028
+ if (status3?.status === "complete") {
104029
+ outcome = "complete";
104030
+ writeEnhancerLog(`completed job=${jobId}`);
104031
+ }
104032
+ } catch {}
104033
+ }, ENHANCER_JOB_POLL_INTERVAL_MS);
104034
+ const silenceInterval = setInterval(() => {
104035
+ if (outcome)
104036
+ return;
104037
+ if (Date.now() - lastActivityAt >= ENHANCER_SILENCE_TIMEOUT_MS) {
104038
+ outcome = "failed";
104039
+ writeEnhancerLog(`silence timeout — no activity for ${ENHANCER_SILENCE_TIMEOUT_MS}ms`);
104040
+ onFailure("Enhancer silence timeout — no output received", false);
104041
+ }
104042
+ }, ENHANCER_JOB_POLL_INTERVAL_MS);
104043
+ params.onAssistantText?.((text) => {
104044
+ lastActivityAt = Date.now();
104045
+ salvagedText += text;
104046
+ });
104047
+ params.onAgentEnd?.(() => {
104048
+ if (outcome)
104049
+ return;
104050
+ const check4 = () => {
104051
+ if (outcome)
104052
+ return;
104053
+ backend2.query(api.web.enhancer.index.getJob, {
104054
+ sessionId,
104055
+ chatroomId,
104056
+ jobId
104057
+ }).then((status3) => {
104058
+ if (outcome)
104059
+ return;
104060
+ if (status3?.status === "complete") {
104061
+ outcome = "complete";
104062
+ return;
104063
+ }
104064
+ if (status3?.status === "running") {
104065
+ const trimmed = salvagedText.trim();
104066
+ if (trimmed && onSalvageComplete) {
104067
+ onSalvageComplete(trimmed).then(() => {
104068
+ if (outcome)
104069
+ return;
104070
+ backend2.query(api.web.enhancer.index.getJob, {
104071
+ sessionId,
104072
+ chatroomId,
104073
+ jobId
104074
+ }).then((afterSalvage) => {
104075
+ if (afterSalvage?.status === "complete") {
104076
+ outcome = "complete";
104077
+ writeEnhancerLog("agent_end: salvaged assistant text via complete");
104078
+ return;
104079
+ }
104080
+ outcome = "failed";
104081
+ writeEnhancerLog("agent_end: turn ended without complete — failing terminal");
104082
+ onFailure("Agent exited without completing enhancer job", true);
104083
+ });
104084
+ }).catch(() => {
104085
+ outcome = "failed";
104086
+ writeEnhancerLog("agent_end: turn ended without complete — failing terminal");
104087
+ onFailure("Agent exited without completing enhancer job", true);
104088
+ });
104089
+ } else {
104090
+ outcome = "failed";
104091
+ writeEnhancerLog("agent_end: turn ended without complete — failing terminal");
104092
+ onFailure("Agent exited without completing enhancer job", true);
104093
+ }
104094
+ }
104095
+ });
104096
+ };
104097
+ setTimeout(check4, ENHANCER_AGENT_END_GRACE_MS);
104098
+ });
104099
+ params.onExit(() => {
104100
+ if (outcome)
104101
+ return;
104102
+ outcome = "failed";
104103
+ onFailure("Agent process exited without completing enhancer job", false);
104104
+ });
104105
+ await new Promise((resolve4) => {
104106
+ const check4 = setInterval(() => {
104107
+ if (outcome) {
104108
+ clearInterval(check4);
104109
+ resolve4();
104110
+ }
104111
+ }, 100);
104112
+ });
104113
+ clearInterval(pollInterval);
104114
+ clearInterval(silenceInterval);
104115
+ return outcome ?? "failed";
104116
+ }
104117
+ var init_wait_for_enhancer_job = __esm(() => {
104118
+ init_api3();
104119
+ });
104120
+
103902
104121
  // src/commands/machine/daemon-start/enhancer/job-subscriber.ts
103903
104122
  function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, wsClient2, agentServices) {
103904
104123
  const inFlight = new Set;
@@ -103911,6 +104130,8 @@ function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, w
103911
104130
  let claimed = false;
103912
104131
  let chatroomId = job.chatroomId;
103913
104132
  let jobId = job.jobId;
104133
+ let spawnResult = null;
104134
+ let service3 = null;
103914
104135
  try {
103915
104136
  const claim = await backend2.mutation(api.daemon.enhancer.index.claimForSpawn, {
103916
104137
  sessionId,
@@ -103928,7 +104149,7 @@ function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, w
103928
104149
  chatroomId = payload.chatroomId;
103929
104150
  jobId = payload.jobId;
103930
104151
  writeEnhancerLog(`spawning harness=${payload.agentHarness} model=${payload.model} job=${payload.jobId}`);
103931
- const service3 = agentServices.get(payload.agentHarness);
104152
+ service3 = agentServices.get(payload.agentHarness) ?? null;
103932
104153
  if (!service3) {
103933
104154
  await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
103934
104155
  sessionId,
@@ -103938,7 +104159,7 @@ function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, w
103938
104159
  });
103939
104160
  return;
103940
104161
  }
103941
- const spawnResult = await service3.spawn({
104162
+ spawnResult = await service3.spawn({
103942
104163
  workingDir: payload.workingDir,
103943
104164
  prompt: createSpawnPrompt(payload.taskEnvelope),
103944
104165
  systemPrompt: payload.systemPrompt,
@@ -103953,29 +104174,34 @@ function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, w
103953
104174
  spawnResult.onLogLine?.((line) => {
103954
104175
  writeEnhancerLog(line);
103955
104176
  });
103956
- spawnResult.onAssistantText?.((text) => {
103957
- if (text)
103958
- writeEnhancerLog(`text] ${text}`);
103959
- });
103960
- await new Promise((resolve4) => {
103961
- spawnResult.onExit(() => resolve4());
103962
- });
103963
- const status3 = await backend2.query(api.web.enhancer.index.getJob, {
104177
+ const sr = spawnResult;
104178
+ const outcome = await waitForEnhancerJobResolution({
103964
104179
  sessionId,
103965
104180
  chatroomId: payload.chatroomId,
103966
- jobId: payload.jobId
104181
+ jobId: payload.jobId,
104182
+ backend: backend2,
104183
+ onAssistantText: sr.onAssistantText ? (cb) => sr.onAssistantText(cb) : undefined,
104184
+ onAgentEnd: sr.onAgentEnd ? (cb) => sr.onAgentEnd(cb) : undefined,
104185
+ onExit: (cb) => sr.onExit(() => cb()),
104186
+ onSalvageComplete: async (content) => {
104187
+ await backend2.mutation(api.web.enhancer.index.complete, {
104188
+ sessionId,
104189
+ chatroomId: payload.chatroomId,
104190
+ jobId: payload.jobId,
104191
+ enhancedContent: content
104192
+ });
104193
+ },
104194
+ onFailure: async (error51, forceTerminal) => {
104195
+ await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
104196
+ sessionId,
104197
+ chatroomId: payload.chatroomId,
104198
+ jobId: payload.jobId,
104199
+ error: error51,
104200
+ ...forceTerminal ? { forceTerminal: true } : {}
104201
+ });
104202
+ }
103967
104203
  });
103968
- if (status3?.status === "complete") {
103969
- writeEnhancerLog(`completed job=${payload.jobId}`);
103970
- }
103971
- if (status3?.status === "running") {
103972
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
103973
- sessionId,
103974
- chatroomId: payload.chatroomId,
103975
- jobId: payload.jobId,
103976
- error: "Agent exited without completing enhancer job"
103977
- });
103978
- }
104204
+ writeEnhancerLog(`completed job=${jobId}`);
103979
104205
  } catch (err) {
103980
104206
  writeEnhancerLog(`error: ${err instanceof Error ? err.message : String(err)}`);
103981
104207
  if (claimed) {
@@ -103988,6 +104214,11 @@ function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, w
103988
104214
  }
103989
104215
  } finally {
103990
104216
  inFlight.delete(job.jobId);
104217
+ if (spawnResult && service3) {
104218
+ try {
104219
+ await service3.stop(spawnResult.pid);
104220
+ } catch {}
104221
+ }
103991
104222
  }
103992
104223
  })();
103993
104224
  }
@@ -103995,6 +104226,7 @@ function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, backend2, w
103995
104226
  return { stop: unsub };
103996
104227
  }
103997
104228
  var init_job_subscriber = __esm(() => {
104229
+ init_wait_for_enhancer_job();
103998
104230
  init_api3();
103999
104231
  });
104000
104232
 
@@ -104112,11 +104344,11 @@ async function getWorkspacesForMachine(deps) {
104112
104344
  return store.workspaces;
104113
104345
  }
104114
104346
  try {
104115
- const workspaces = await deps.backend.query(api.workspaces.listRecentlyObservedWorkspacesForMachine, {
104347
+ const workingDirs = await deps.backend.query(api.workspaces.listRecentlyObservedWorkspacesForMachine, {
104116
104348
  sessionId: deps.sessionId,
104117
104349
  machineId: deps.machineId
104118
104350
  });
104119
- const mapped = workspaces.map((ws) => ({ workingDir: ws.workingDir }));
104351
+ const mapped = (workingDirs ?? []).map((workingDir) => ({ workingDir }));
104120
104352
  if (store) {
104121
104353
  store.workspaces = mapped;
104122
104354
  store.updatedAt = Date.now();
@@ -107430,7 +107662,7 @@ function diffPathIndexes(previous, next4) {
107430
107662
  import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
107431
107663
  import * as fs11 from "node:fs/promises";
107432
107664
  import { homedir as homedir7 } from "node:os";
107433
- import { join as join20 } from "node:path";
107665
+ import { join as join21 } from "node:path";
107434
107666
  function workspaceKeyFor(workingDir) {
107435
107667
  const normalized = normalizeWorkingDirForLookup(workingDir);
107436
107668
  return createHash6("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -107444,10 +107676,10 @@ function buildPathIndex(entries2) {
107444
107676
  }
107445
107677
  function manifestPath(machineId, workingDir) {
107446
107678
  const key = workspaceKeyFor(workingDir);
107447
- return join20(SYNC_STATE_DIR, machineId, key, "manifest.json");
107679
+ return join21(SYNC_STATE_DIR, machineId, key, "manifest.json");
107448
107680
  }
107449
107681
  async function ensureDir(filePath) {
107450
- await fs11.mkdir(join20(filePath, ".."), { recursive: true, mode: 448 });
107682
+ await fs11.mkdir(join21(filePath, ".."), { recursive: true, mode: 448 });
107451
107683
  }
107452
107684
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
107453
107685
  try {
@@ -107507,7 +107739,7 @@ function entriesFromPathIndex(paths) {
107507
107739
  }
107508
107740
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
107509
107741
  var init_workspace_sync_state = __esm(() => {
107510
- SYNC_STATE_DIR = join20(homedir7(), ".chatroom", "sync-state");
107742
+ SYNC_STATE_DIR = join21(homedir7(), ".chatroom", "sync-state");
107511
107743
  });
107512
107744
 
107513
107745
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -107837,18 +108069,18 @@ async function syncScannedFileTree(session2, normalizedWorkingDir, tree, dataHas
107837
108069
  function toDeltaOperations(delta) {
107838
108070
  return [
107839
108071
  ...delta.added.map((entry) => ({
107840
- operation: "add",
107841
- path: entry.path,
107842
- entryType: entry.type
108072
+ o: "a",
108073
+ p: entry.path,
108074
+ e: entry.type === "directory" ? "d" : "f"
107843
108075
  })),
107844
108076
  ...delta.removed.map((entryPath) => ({
107845
- operation: "remove",
107846
- path: entryPath
108077
+ o: "r",
108078
+ p: entryPath
107847
108079
  })),
107848
108080
  ...delta.typeChanged.map((entry) => ({
107849
- operation: "type-change",
107850
- path: entry.path,
107851
- entryType: entry.type
108081
+ o: "t",
108082
+ p: entry.path,
108083
+ e: entry.type === "directory" ? "d" : "f"
107852
108084
  }))
107853
108085
  ];
107854
108086
  }
@@ -107982,7 +108214,7 @@ function unsupportedFileWriteOperationMessage(operation) {
107982
108214
  }
107983
108215
 
107984
108216
  // src/commands/machine/daemon-start/file-write-fulfillment.ts
107985
- import { access as access4, mkdir as mkdir9, rename as rename4, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
108217
+ import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm3, writeFile as writeFile9 } from "node:fs/promises";
107986
108218
  import { dirname as dirname11 } from "node:path";
107987
108219
  function isTerminalFileWriteError(errorMessage) {
107988
108220
  const terminalMessages = new Set([
@@ -108035,9 +108267,9 @@ async function validateWriteOperation(operation, absolutePath) {
108035
108267
  }
108036
108268
  async function writePayloadToDisk(absolutePath, operation, content) {
108037
108269
  if (operation === "create") {
108038
- await mkdir9(dirname11(absolutePath), { recursive: true });
108270
+ await mkdir10(dirname11(absolutePath), { recursive: true });
108039
108271
  }
108040
- await writeFile8(absolutePath, content);
108272
+ await writeFile9(absolutePath, content);
108041
108273
  }
108042
108274
  async function fulfillOneFileWriteRequest(session2, request2) {
108043
108275
  const startTime = Date.now();
@@ -108098,7 +108330,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
108098
108330
  });
108099
108331
  return;
108100
108332
  }
108101
- await mkdir9(dirname11(targetResolved.absolutePath), { recursive: true });
108333
+ await mkdir10(dirname11(targetResolved.absolutePath), { recursive: true });
108102
108334
  await rename4(resolved.absolutePath, targetResolved.absolutePath);
108103
108335
  await completeWriteRequest(session2, request2._id, { status: "done" });
108104
108336
  const elapsed4 = Date.now() - startTime;
@@ -108114,7 +108346,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
108114
108346
  });
108115
108347
  return;
108116
108348
  }
108117
- await mkdir9(resolved.absolutePath, { recursive: true });
108349
+ await mkdir10(resolved.absolutePath, { recursive: true });
108118
108350
  await completeWriteRequest(session2, request2._id, { status: "done" });
108119
108351
  const elapsed4 = Date.now() - startTime;
108120
108352
  console.log(`[${formatTimestamp()}] ✏️ Directory mkdir fulfilled: ${filePath} (${elapsed4}ms)`);
@@ -112838,16 +113070,16 @@ var init_jsonc = __esm(() => {
112838
113070
 
112839
113071
  // src/infrastructure/services/workspace/workspace-resolver.ts
112840
113072
  import { readFile as readFile10, readdir as readdir3, stat as stat6 } from "node:fs/promises";
112841
- import { join as join21, basename as basename4, resolve as resolve7 } from "node:path";
113073
+ import { join as join22, basename as basename4, resolve as resolve7 } from "node:path";
112842
113074
  async function resolveGlobPatternStar(rootDir, cleaned) {
112843
- const parentDir = join21(rootDir, cleaned.slice(0, -2));
113075
+ const parentDir = join22(rootDir, cleaned.slice(0, -2));
112844
113076
  try {
112845
113077
  const entries2 = await readdir3(parentDir, { withFileTypes: true });
112846
113078
  const dirs = [];
112847
113079
  for (const entry of entries2) {
112848
113080
  if (!entry.isDirectory())
112849
113081
  continue;
112850
- const dirPath = join21(parentDir, entry.name);
113082
+ const dirPath = join22(parentDir, entry.name);
112851
113083
  if (resolve7(dirPath).startsWith(resolve7(rootDir))) {
112852
113084
  dirs.push(dirPath);
112853
113085
  }
@@ -112858,7 +113090,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
112858
113090
  }
112859
113091
  }
112860
113092
  async function resolveLiteralPath(rootDir, cleaned) {
112861
- const dir = join21(rootDir, cleaned);
113093
+ const dir = join22(rootDir, cleaned);
112862
113094
  try {
112863
113095
  if (!resolve7(dir).startsWith(resolve7(rootDir)))
112864
113096
  return [];
@@ -112878,7 +113110,7 @@ async function resolveGlobPattern(rootDir, pattern) {
112878
113110
  }
112879
113111
  async function readPackageJson(dir) {
112880
113112
  try {
112881
- const content = await readFile10(join21(dir, "package.json"), "utf-8");
113113
+ const content = await readFile10(join22(dir, "package.json"), "utf-8");
112882
113114
  const pkg = JSON.parse(content);
112883
113115
  return {
112884
113116
  name: pkg.name || basename4(dir),
@@ -112890,7 +113122,7 @@ async function readPackageJson(dir) {
112890
113122
  }
112891
113123
  async function readPnpmWorkspacePatterns(rootDir) {
112892
113124
  try {
112893
- const content = await readFile10(join21(rootDir, "pnpm-workspace.yaml"), "utf-8");
113125
+ const content = await readFile10(join22(rootDir, "pnpm-workspace.yaml"), "utf-8");
112894
113126
  const patterns = [];
112895
113127
  let inPackages = false;
112896
113128
  for (const line of content.split(`
@@ -112917,7 +113149,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
112917
113149
  }
112918
113150
  async function readPackageJsonWorkspacePatterns(rootDir) {
112919
113151
  try {
112920
- const content = await readFile10(join21(rootDir, "package.json"), "utf-8");
113152
+ const content = await readFile10(join22(rootDir, "package.json"), "utf-8");
112921
113153
  const pkg = JSON.parse(content);
112922
113154
  if (!pkg.workspaces)
112923
113155
  return [];
@@ -112998,11 +113230,11 @@ var MAX_NAME_LENGTH = 256;
112998
113230
 
112999
113231
  // src/infrastructure/services/workspace/command-discovery.ts
113000
113232
  import { access as access5, readFile as readFile11 } from "node:fs/promises";
113001
- import { join as join22, relative as relative4, basename as basename5 } from "node:path";
113233
+ import { join as join23, relative as relative4, basename as basename5 } from "node:path";
113002
113234
  async function detectPackageManager(workingDir) {
113003
113235
  for (const { file: file2, manager } of LOCKFILE_MAP) {
113004
113236
  try {
113005
- await access5(join22(workingDir, file2));
113237
+ await access5(join23(workingDir, file2));
113006
113238
  return manager;
113007
113239
  } catch {}
113008
113240
  }
@@ -113071,7 +113303,7 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
113071
113303
  }
113072
113304
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
113073
113305
  let rootPackageName = basename5(workingDir);
113074
- const pkg = await readJsonFile(join22(workingDir, "package.json"), "root package.json");
113306
+ const pkg = await readJsonFile(join23(workingDir, "package.json"), "root package.json");
113075
113307
  if (!pkg)
113076
113308
  return { commands: [], rootPackageName };
113077
113309
  if (pkg.name)
@@ -113087,7 +113319,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
113087
113319
  }
113088
113320
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
113089
113321
  const turboTaskNames = [];
113090
- const turbo = await readJsonFile(join22(workingDir, "turbo.json"), "turbo.json");
113322
+ const turbo = await readJsonFile(join23(workingDir, "turbo.json"), "turbo.json");
113091
113323
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
113092
113324
  return turboTaskNames;
113093
113325
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -113100,7 +113332,7 @@ async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
113100
113332
  async function readDenoJson(workingDir) {
113101
113333
  const commands = [];
113102
113334
  for (const fileName of ["deno.json", "deno.jsonc"]) {
113103
- const deno = await readJsonFile(join22(workingDir, fileName), fileName);
113335
+ const deno = await readJsonFile(join23(workingDir, fileName), fileName);
113104
113336
  if (!deno?.tasks || typeof deno.tasks !== "object")
113105
113337
  continue;
113106
113338
  const pkgName = deno.name ?? basename5(workingDir);
@@ -113123,7 +113355,7 @@ async function readMakefile(workingDir) {
113123
113355
  const commands = [];
113124
113356
  for (const fileName of ["Makefile", "makefile"]) {
113125
113357
  try {
113126
- const content = await readFile11(join22(workingDir, fileName), "utf-8");
113358
+ const content = await readFile11(join23(workingDir, fileName), "utf-8");
113127
113359
  const targets = parseMakefileTargets(content);
113128
113360
  if (targets.length === 0)
113129
113361
  continue;
@@ -113276,9 +113508,9 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
113276
113508
  sessionId: session2.sessionId,
113277
113509
  machineId: session2.machineId
113278
113510
  }, (observed) => {
113279
- if (stopped)
113511
+ if (stopped || observed == null)
113280
113512
  return;
113281
- handleObservedChange(observed ?? []);
113513
+ handleObservedChange(observed);
113282
113514
  }, (err) => {
113283
113515
  console.warn(`[${formatTimestamp()}] ⚠️ Observed-sync subscription error: ${getErrorMessage(err)}`);
113284
113516
  });
@@ -113291,8 +113523,8 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
113291
113523
  sessionId: session2.sessionId,
113292
113524
  machineId: session2.machineId
113293
113525
  }).then((observed) => {
113294
- if (!stopped)
113295
- handleObservedChange(observed ?? []);
113526
+ if (!stopped && observed != null)
113527
+ handleObservedChange(observed);
113296
113528
  }).catch((err) => {
113297
113529
  console.warn(`[${formatTimestamp()}] ⚠️ Observed-sync reconcile query failed: ${getErrorMessage(err)}`);
113298
113530
  }).finally(() => {
@@ -114501,8 +114733,8 @@ var init_task_monitor = __esm(() => {
114501
114733
  });
114502
114734
 
114503
114735
  // src/commands/machine/daemon-start/workspace-list-subscription.ts
114504
- function toSyncWorkspaces(workspaces) {
114505
- return workspaces.map((ws) => ({ workingDir: ws.workingDir }));
114736
+ function toSyncWorkspaces(workingDirs) {
114737
+ return workingDirs.map((workingDir) => ({ workingDir }));
114506
114738
  }
114507
114739
  var startWorkspaceListSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
114508
114740
  const session2 = yield* DaemonSessionService;
@@ -114514,16 +114746,16 @@ var startWorkspaceListSubscriptionEffect = (wsClient2) => exports_Effect.gen(fun
114514
114746
  };
114515
114747
  let stopped = false;
114516
114748
  let reconcileInFlight = false;
114517
- const applyList = (workspaces) => {
114749
+ const applyList = (workingDirs) => {
114518
114750
  if (!session2.workspaceListStore)
114519
114751
  return;
114520
- session2.workspaceListStore.workspaces = toSyncWorkspaces(workspaces);
114752
+ session2.workspaceListStore.workspaces = toSyncWorkspaces(workingDirs);
114521
114753
  session2.workspaceListStore.updatedAt = Date.now();
114522
114754
  };
114523
114755
  const unsubscribe = wsClient2.onUpdate(api.workspaces.listRecentlyObservedWorkspacesForMachine, queryArgs, (workspaces) => {
114524
- if (stopped)
114756
+ if (stopped || workspaces == null)
114525
114757
  return;
114526
- applyList(workspaces ?? []);
114758
+ applyList(workspaces);
114527
114759
  }, (err) => {
114528
114760
  console.warn(`[${formatTimestamp()}] ⚠️ Workspace-list subscription error: ${getErrorMessage(err)}`);
114529
114761
  });
@@ -114532,8 +114764,8 @@ var startWorkspaceListSubscriptionEffect = (wsClient2) => exports_Effect.gen(fun
114532
114764
  return;
114533
114765
  reconcileInFlight = true;
114534
114766
  session2.backend.query(api.workspaces.listRecentlyObservedWorkspacesForMachine, queryArgs).then((workspaces) => {
114535
- if (!stopped)
114536
- applyList(workspaces ?? []);
114767
+ if (!stopped && workspaces != null)
114768
+ applyList(workspaces);
114537
114769
  }).catch((err) => {
114538
114770
  console.warn(`[${formatTimestamp()}] ⚠️ Workspace-list reconcile failed: ${getErrorMessage(err)}`);
114539
114771
  }).finally(() => {
@@ -116147,4 +116379,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
116147
116379
  });
116148
116380
  program2.parse();
116149
116381
 
116150
- //# debugId=94A8E14D18276BBD64756E2164756E21
116382
+ //# debugId=550B12123AE171BD64756E2164756E21