lody 0.89.4 → 0.90.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.
@@ -63,7 +63,7 @@ const DEEPSEEK_HARNESS_AGENT_PRESETS = [
63
63
  }
64
64
  ];
65
65
  const ACP_EXTENSION_DSH_VERSION = "0.1.0";
66
- const DEEPSEEK_HARNESS_VERSION = "0.1.1-rc.1";
66
+ const DEEPSEEK_HARNESS_VERSION = "0.1.1-rc.2";
67
67
  const ACP_EXTENSION_DSH_PROFILE_REVISION = "v5";
68
68
  const ACP_EXTENSION_DSH_SESSION_ROOT_ENV = "ACP_EXTENSION_DSH_SESSION_ROOT";
69
69
  const ACP_EXTENSION_DSH_QUERY_PATH_ENV = "ACP_EXTENSION_DSH_QUERY_PATH";
@@ -32,7 +32,7 @@ import "perf_hooks";
32
32
  import "net";
33
33
  import "tls";
34
34
  import "url";
35
- import "./profile-Cxxl5hCq.js";
35
+ import "./profile-CPDWaQXE.js";
36
36
  import "assert";
37
37
  import "fs/promises";
38
38
  import "better-sqlite3";
@@ -80,7 +80,7 @@ let __tla = Promise.all([
80
80
  }
81
81
  })()
82
82
  ]).then(async () => {
83
- const reviewViewerVersion = "0.89.4";
83
+ const reviewViewerVersion = "0.90.0";
84
84
  const reviewViewerSha256 = "dc9c7fc1dde49bb39c5fe86392d69219b469c0cab4fd492d8b562045e7f676e4";
85
85
  const reviewViewerFileName = "standalone.html";
86
86
  const DEFAULT_CDN_BASES = [
@@ -2,7 +2,7 @@ import { randomUUID, createHash } from "node:crypto";
2
2
  import { isAbsolute } from "node:path";
3
3
  import { Readable, Writable } from "node:stream";
4
4
  import { n as ndJsonStream, A as AgentSideConnection, R as RequestError, P as PROTOCOL_VERSION } from "./chunks/acp-DBqkNziA.js";
5
- import { D as DEEPSEEK_HARNESS_PERMISSION_MODES, c as DEEPSEEK_HARNESS_REASONING_OPTIONS, e as ACP_EXTENSION_DSH_VERSION, a as DEEPSEEK_HARNESS_AGENT_PRESETS } from "./chunks/profile-Cxxl5hCq.js";
5
+ import { D as DEEPSEEK_HARNESS_PERMISSION_MODES, c as DEEPSEEK_HARNESS_REASONING_OPTIONS, e as ACP_EXTENSION_DSH_VERSION, a as DEEPSEEK_HARNESS_AGENT_PRESETS } from "./chunks/profile-CPDWaQXE.js";
6
6
  import "./chunks/schemas-Du3qLZPS.js";
7
7
  const name = "acp-extension-dsh";
8
8
  const inject = [
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import * as os$1 from "node:os";
14
14
  import os__default, { userInfo, homedir, cpus } from "node:os";
15
15
  import * as path from "node:path";
16
16
  import path__default, { posix as posix$1, basename, win32, join as join$2, resolve as resolve$1, dirname as dirname$1, relative as relative$1, isAbsolute as isAbsolute$2, sep as sep$1, delimiter, normalize } from "node:path";
17
- import { _ as _coercedNumber, Z as ZodNumber$1, o as object$1, s as string$1, l as literal$1, n as number$4, b as boolean, a as _enum$1, c as array$2, d as discriminatedUnion, e as _instanceof, r as record$1, u as unknown, f as union$7, p as preprocess, g as ZodError$1, h as _null, i as normalizeParams, $ as $constructor, j as $ZodObject, k as defineLazy, m as $ZodType, q as parse$5, t as safeParse$1, v as parseAsync, w as safeParseAsync$1, x as clone$2, y as looseObject, z as intersection, A as optional$1, B as datetime, C as custom$1, D as toJSONSchema, E as ZodOptional$1, F as never$1, G as xor } from "./chunks/schemas-Du3qLZPS.js";
17
+ import { _ as _coercedNumber, Z as ZodNumber$1, o as object$1, s as string$1, l as literal$1, n as number$4, b as boolean, a as _enum$1, c as array$2, r as record$1, d as discriminatedUnion, e as _instanceof, u as unknown, f as union$7, p as preprocess, g as ZodError$1, h as _null, i as normalizeParams, $ as $constructor, j as $ZodObject, k as defineLazy, m as $ZodType, q as parse$5, t as safeParse$1, v as parseAsync, w as safeParseAsync$1, x as clone$2, y as looseObject, z as intersection, A as optional$1, B as datetime, C as custom$1, D as toJSONSchema, E as ZodOptional$1, F as never$1, G as xor } from "./chunks/schemas-Du3qLZPS.js";
18
18
  import require$$0$4, { once as once$1, EventEmitter as EventEmitter$1 } from "node:events";
19
19
  import Tw__default, { execFile, spawn as spawn$1, execFileSync } from "node:child_process";
20
20
  import process$2 from "node:process";
@@ -42,7 +42,7 @@ import { performance as performance$1 } from "perf_hooks";
42
42
  import require$$0$9 from "net";
43
43
  import require$$4$2 from "tls";
44
44
  import require$$7$2, { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1 } from "url";
45
- import { D as DEEPSEEK_HARNESS_PERMISSION_MODES, a as DEEPSEEK_HARNESS_AGENT_PRESETS, b as DEEPSEEK_HARNESS_MODELS, c as DEEPSEEK_HARNESS_REASONING_OPTIONS, A as ACP_EXTENSION_DSH_CAPABILITY_SOURCE_VERSION, d as createDeepSeekHarnessCordisConfig, e as ACP_EXTENSION_DSH_VERSION, f as ACP_EXTENSION_DSH_PROFILE_REVISION, g as ACP_EXTENSION_DSH_QUERY_PATH_ENV, h as ACP_EXTENSION_DSH_SESSION_ROOT_ENV, i as DEEPSEEK_HARNESS_NPX_PACKAGES, j as DEEPSEEK_HARNESS_VERSION, k as DEEPSEEK_HARNESS_DEFAULT_SESSION_COMPRESSION } from "./chunks/profile-Cxxl5hCq.js";
45
+ import { D as DEEPSEEK_HARNESS_PERMISSION_MODES, a as DEEPSEEK_HARNESS_AGENT_PRESETS, b as DEEPSEEK_HARNESS_MODELS, c as DEEPSEEK_HARNESS_REASONING_OPTIONS, A as ACP_EXTENSION_DSH_CAPABILITY_SOURCE_VERSION, d as createDeepSeekHarnessCordisConfig, e as ACP_EXTENSION_DSH_VERSION, f as ACP_EXTENSION_DSH_PROFILE_REVISION, g as ACP_EXTENSION_DSH_QUERY_PATH_ENV, h as ACP_EXTENSION_DSH_SESSION_ROOT_ENV, i as DEEPSEEK_HARNESS_NPX_PACKAGES, j as DEEPSEEK_HARNESS_VERSION, k as DEEPSEEK_HARNESS_DEFAULT_SESSION_COMPRESSION } from "./chunks/profile-CPDWaQXE.js";
46
46
  import assert from "assert";
47
47
  import * as fs$2 from "fs/promises";
48
48
  import fs__default$2 from "fs/promises";
@@ -4325,7 +4325,7 @@ Upgrade Node, then re-run: npx lody@latest`;
4325
4325
  }
4326
4326
  const name$2 = "lody";
4327
4327
  const name$1 = "@lody/cli-cloud";
4328
- const version$9 = "0.89.4";
4328
+ const version$9 = "0.90.0";
4329
4329
  const type$2 = "module";
4330
4330
  const scripts = {
4331
4331
  "dev": "node dev.mjs",
@@ -11506,6 +11506,10 @@ Requirements:
11506
11506
  connection: CliBackendConnectionSchema
11507
11507
  }).strict().optional(),
11508
11508
  connectedWorkspaces: array$2(CliRuntimeWorkspaceSchema).optional(),
11509
+ connectionAges: object$1({
11510
+ backendNotConnectedSinceMs: number$4().int().nonnegative().optional(),
11511
+ workspaceNotConnectedSinceMs: record$1(string$1().min(1), number$4().int().nonnegative()).optional()
11512
+ }).strict().optional(),
11509
11513
  machineId: string$1().min(1).optional(),
11510
11514
  pid: number$4().int(),
11511
11515
  updatedAtMs: number$4(),
@@ -23342,28 +23346,35 @@ ${context2.authorReply.trim()}
23342
23346
  ...right2
23343
23347
  ];
23344
23348
  };
23345
- const classifyIpv4 = ([first2, second, third, fourth]) => {
23349
+ const isBenchmarkingIpv4 = ([first2, second]) => first2 === 198 && (second === 18 || second === 19);
23350
+ const classifyIpv4 = (octets) => {
23351
+ const [first2, second, third, fourth] = octets;
23346
23352
  if (first2 === 127) return "loopback";
23347
23353
  if (first2 === 10 || first2 === 172 && second >= 16 && second <= 31) return "private-lan";
23348
23354
  if (first2 === 192 && second === 168) return "private-lan";
23349
- const prohibited = first2 === 0 || first2 >= 224 || first2 === 100 && second >= 64 && second <= 127 || first2 === 169 && second === 254 || first2 === 192 && second === 0 && third === 0 || first2 === 192 && second === 0 && third === 2 || first2 === 198 && (second === 18 || second === 19) || first2 === 198 && second === 51 && third === 100 || first2 === 203 && second === 0 && third === 113 || first2 === 255 && second === 255 && third === 255 && fourth === 255;
23355
+ const prohibited = first2 === 0 || first2 >= 224 || first2 === 100 && second >= 64 && second <= 127 || first2 === 169 && second === 254 || first2 === 192 && second === 0 && third === 0 || first2 === 192 && second === 0 && third === 2 || isBenchmarkingIpv4(octets) || first2 === 198 && second === 51 && third === 100 || first2 === 203 && second === 0 && third === 113 || first2 === 255 && second === 255 && third === 255 && fourth === 255;
23350
23356
  return prohibited ? "prohibited" : "public";
23351
23357
  };
23358
+ const mappedIpv4 = (segments) => {
23359
+ if (!segments.slice(0, 5).every((segment) => segment === 0) || segments[5] !== 65535) {
23360
+ return null;
23361
+ }
23362
+ const high = segments[6] ?? 0;
23363
+ const low = segments[7] ?? 0;
23364
+ return [
23365
+ high >> 8,
23366
+ high & 255,
23367
+ low >> 8,
23368
+ low & 255
23369
+ ];
23370
+ };
23352
23371
  const classifyIpv6 = (segments) => {
23353
23372
  if (segments.every((segment) => segment === 0)) return "prohibited";
23354
23373
  if (segments.slice(0, 7).every((segment) => segment === 0) && segments[7] === 1) {
23355
23374
  return "loopback";
23356
23375
  }
23357
- if (segments.slice(0, 5).every((segment) => segment === 0) && segments[5] === 65535) {
23358
- const high = segments[6] ?? 0;
23359
- const low = segments[7] ?? 0;
23360
- return classifyIpv4([
23361
- high >> 8,
23362
- high & 255,
23363
- low >> 8,
23364
- low & 255
23365
- ]);
23366
- }
23376
+ const mapped = mappedIpv4(segments);
23377
+ if (mapped) return classifyIpv4(mapped);
23367
23378
  const first2 = segments[0] ?? 0;
23368
23379
  if ((first2 & 65024) === 64512) return "private-lan";
23369
23380
  if ((first2 & 65472) === 65152 || (first2 & 65280) === 65280) return "prohibited";
@@ -127598,7 +127609,6 @@ ${error2.message}` : execaMessage;
127598
127609
  return {
127599
127610
  command: "npx",
127600
127611
  args: [
127601
- "--force",
127602
127612
  "--prefer-offline",
127603
127613
  "-y",
127604
127614
  ...DEEPSEEK_HARNESS_NPX_PACKAGES.flatMap((packageName) => [
@@ -129075,7 +129085,7 @@ ${fallbackStderrTail}` : errorMessage2;
129075
129085
  sessionResponse
129076
129086
  };
129077
129087
  };
129078
- function waitForChildProcessExit$1(child, timeoutMs) {
129088
+ function waitForChildProcessExit$2(child, timeoutMs) {
129079
129089
  if (child.exitCode !== null) {
129080
129090
  return Promise.resolve(true);
129081
129091
  }
@@ -129112,7 +129122,7 @@ ${fallbackStderrTail}` : errorMessage2;
129112
129122
  } catch {
129113
129123
  return;
129114
129124
  }
129115
- if (await waitForChildProcessExit$1(child, exitTimeoutMs)) {
129125
+ if (await waitForChildProcessExit$2(child, exitTimeoutMs)) {
129116
129126
  return;
129117
129127
  }
129118
129128
  logger2.debug(`[${sessionLabel}] ACP agent process did not exit within ${exitTimeoutMs}ms of SIGTERM; escalating to SIGKILL`);
@@ -129121,7 +129131,7 @@ ${fallbackStderrTail}` : errorMessage2;
129121
129131
  } catch {
129122
129132
  return;
129123
129133
  }
129124
- await waitForChildProcessExit$1(child, exitTimeoutMs);
129134
+ await waitForChildProcessExit$2(child, exitTimeoutMs);
129125
129135
  }
129126
129136
  const spawnAcpProcess = (options) => {
129127
129137
  let command2 = options.command;
@@ -166472,7 +166482,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166472
166482
  const ACP_OPERATION_TIMEOUT_MS = 12e4;
166473
166483
  const ACP_PROCESS_EXIT_TIMEOUT_MS = 3e3;
166474
166484
  const MAX_LOCAL_PROJECT_HISTORY_CATALOG_SESSIONS = 100;
166475
- function waitForChildProcessExit(child, timeoutMs) {
166485
+ function waitForChildProcessExit$1(child, timeoutMs) {
166476
166486
  if (child.exitCode !== null) {
166477
166487
  return Promise.resolve(true);
166478
166488
  }
@@ -166504,7 +166514,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166504
166514
  } catch {
166505
166515
  return;
166506
166516
  }
166507
- if (await waitForChildProcessExit(child, ACP_PROCESS_EXIT_TIMEOUT_MS)) {
166517
+ if (await waitForChildProcessExit$1(child, ACP_PROCESS_EXIT_TIMEOUT_MS)) {
166508
166518
  return;
166509
166519
  }
166510
166520
  try {
@@ -166512,7 +166522,7 @@ ${escapeHtmlScriptContent(VISUAL_ANNOTATION_INSPECTOR_BROWSER_SCRIPT)}
166512
166522
  } catch {
166513
166523
  return;
166514
166524
  }
166515
- await waitForChildProcessExit(child, ACP_PROCESS_EXIT_TIMEOUT_MS);
166525
+ await waitForChildProcessExit$1(child, ACP_PROCESS_EXIT_TIMEOUT_MS);
166516
166526
  }
166517
166527
  async function withTimeout(promise2, label2) {
166518
166528
  let timeout2 = null;
@@ -193456,20 +193466,29 @@ ${trimmed2}${closing}`;
193456
193466
  phase = "starting";
193457
193467
  startupStage = "bootstrap";
193458
193468
  connectivity;
193459
- backend = {
193460
- authorization: "pending",
193461
- connection: "connecting"
193462
- };
193469
+ backend;
193470
+ backendNotConnectedSinceMs;
193463
193471
  connectedWorkspaces = [];
193472
+ workspaceNotConnectedSinceMs = /* @__PURE__ */ new Map();
193464
193473
  machineId;
193465
193474
  activeSessionCount = 0;
193466
193475
  connectedRoomCount = 0;
193467
- updatedAtMs = Date.now();
193476
+ updatedAtMs;
193468
193477
  issuesByCode = /* @__PURE__ */ new Map();
193478
+ trackBackendConnectionAge;
193479
+ now;
193469
193480
  constructor(options = {}) {
193470
193481
  this.pid = options.pid ?? process.pid;
193471
193482
  this.machineId = options.machineId;
193472
193483
  this.supervisor = options.supervisor;
193484
+ this.trackBackendConnectionAge = options.trackBackendConnectionAge ?? true;
193485
+ this.now = options.now ?? Date.now;
193486
+ this.updatedAtMs = this.now();
193487
+ this.backend = {
193488
+ authorization: "pending",
193489
+ connection: "connecting"
193490
+ };
193491
+ this.backendNotConnectedSinceMs = this.trackBackendConnectionAge ? this.updatedAtMs : void 0;
193473
193492
  }
193474
193493
  setMachineId(machineId) {
193475
193494
  if (this.machineId === machineId) {
@@ -193517,24 +193536,52 @@ ${trimmed2}${closing}`;
193517
193536
  this.touch();
193518
193537
  }
193519
193538
  setBackendConnection(connection2) {
193520
- if (this.backend.connection === connection2) {
193539
+ const nowMs2 = this.now();
193540
+ const notConnectedSinceMs = !this.trackBackendConnectionAge || connection2 === "connected" ? void 0 : this.backend.connection === "connected" ? nowMs2 : this.backendNotConnectedSinceMs ?? nowMs2;
193541
+ if (this.backend.connection === connection2 && this.backendNotConnectedSinceMs === notConnectedSinceMs) {
193521
193542
  return;
193522
193543
  }
193523
193544
  this.backend = {
193524
193545
  ...this.backend,
193525
193546
  connection: connection2
193526
193547
  };
193548
+ this.backendNotConnectedSinceMs = notConnectedSinceMs;
193527
193549
  this.touch();
193528
193550
  }
193529
193551
  setConnectedWorkspaces(workspaces) {
193530
- if (JSON.stringify(this.connectedWorkspaces) === JSON.stringify(workspaces)) {
193552
+ const nowMs2 = this.now();
193553
+ const previousById = new Map(this.connectedWorkspaces.map((workspace) => [
193554
+ workspace.id,
193555
+ workspace
193556
+ ]));
193557
+ const nextWorkspaceNotConnectedSinceMs = /* @__PURE__ */ new Map();
193558
+ const nextWorkspaces = workspaces.map((workspace) => {
193559
+ const previous = previousById.get(workspace.id);
193560
+ const notConnectedSinceMs = !this.trackBackendConnectionAge || workspace.backendConnection === "connected" ? void 0 : previous?.backendConnection === "connected" ? nowMs2 : this.workspaceNotConnectedSinceMs.get(workspace.id) ?? nowMs2;
193561
+ if (notConnectedSinceMs !== void 0) {
193562
+ nextWorkspaceNotConnectedSinceMs.set(workspace.id, notConnectedSinceMs);
193563
+ }
193564
+ return {
193565
+ id: workspace.id,
193566
+ name: workspace.name,
193567
+ slug: workspace.slug,
193568
+ role: workspace.role,
193569
+ backendConnection: workspace.backendConnection
193570
+ };
193571
+ });
193572
+ if (JSON.stringify(this.connectedWorkspaces) === JSON.stringify(nextWorkspaces) && JSON.stringify([
193573
+ ...this.workspaceNotConnectedSinceMs
193574
+ ]) === JSON.stringify([
193575
+ ...nextWorkspaceNotConnectedSinceMs
193576
+ ])) {
193531
193577
  return;
193532
193578
  }
193533
- this.connectedWorkspaces = workspaces;
193579
+ this.connectedWorkspaces = nextWorkspaces;
193580
+ this.workspaceNotConnectedSinceMs = nextWorkspaceNotConnectedSinceMs;
193534
193581
  this.touch();
193535
193582
  }
193536
193583
  upsertIssue(input2) {
193537
- const nowMs2 = Date.now();
193584
+ const nowMs2 = this.now();
193538
193585
  const existing = this.issuesByCode.get(input2.code);
193539
193586
  if (existing) {
193540
193587
  const next2 = {
@@ -193599,6 +193646,14 @@ ${trimmed2}${closing}`;
193599
193646
  connectivity: this.connectivity,
193600
193647
  backend: this.backend,
193601
193648
  connectedWorkspaces: this.connectedWorkspaces,
193649
+ connectionAges: this.trackBackendConnectionAge ? {
193650
+ ...this.backendNotConnectedSinceMs === void 0 ? {} : {
193651
+ backendNotConnectedSinceMs: this.backendNotConnectedSinceMs
193652
+ },
193653
+ ...this.workspaceNotConnectedSinceMs.size === 0 ? {} : {
193654
+ workspaceNotConnectedSinceMs: Object.fromEntries(this.workspaceNotConnectedSinceMs)
193655
+ }
193656
+ } : void 0,
193602
193657
  machineId: this.machineId,
193603
193658
  pid: this.pid,
193604
193659
  updatedAtMs: this.updatedAtMs,
@@ -193625,7 +193680,7 @@ ${trimmed2}${closing}`;
193625
193680
  }
193626
193681
  }
193627
193682
  touch() {
193628
- this.updatedAtMs = Date.now();
193683
+ this.updatedAtMs = this.now();
193629
193684
  this.phase = this.computePhase();
193630
193685
  }
193631
193686
  computePhase() {
@@ -194171,7 +194226,7 @@ Shutting down gracefully${reason}...`);
194171
194226
  }
194172
194227
  if (!hostRecord) {
194173
194228
  hostRecord = parseRecord(value);
194174
- if (!hostRecord || hostRecord.instanceId !== options.instanceId || options.expectedPid !== void 0 && hostRecord.pid !== options.expectedPid || hostRecord.mode !== options.expectedMode) {
194229
+ if (!hostRecord || hostRecord.instanceId !== options.instanceId || options.expectedPid !== void 0 && hostRecord.pid !== options.expectedPid || options.expectedMode !== void 0 && hostRecord.mode !== options.expectedMode) {
194175
194230
  finish({
194176
194231
  ok: false,
194177
194232
  error: "owner_mismatch"
@@ -194337,7 +194392,8 @@ Shutting down gracefully${reason}...`);
194337
194392
  };
194338
194393
  const authFailureExitCode = supervisorIdentity ? EXIT_CODE_AUTH_FAILURE : 1;
194339
194394
  const runtimeStateReporter = new CliRuntimeStateReporter({
194340
- supervisor: toRuntimeSupervisorIdentity(supervisorIdentity)
194395
+ supervisor: toRuntimeSupervisorIdentity(supervisorIdentity),
194396
+ trackBackendConnectionAge: platformKind === "cloud"
194341
194397
  });
194342
194398
  runtimeStateReporter.setStartupStage("bootstrap");
194343
194399
  const electronManaged = supervisorIdentity?.launchMode === "electron";
@@ -222170,7 +222226,7 @@ ${page}${helpTipBottom}${choiceDescription}${ansiEscapes.cursorHide}`;
222170
222226
  data = createReviewBundleSnapshot(bundle);
222171
222227
  }
222172
222228
  const { injectReviewSnapshot } = await import("./chunks/index-VoI6Ds2-.js");
222173
- const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-HMN3RPEa.js").then(async (m) => {
222229
+ const { resolveReviewViewerTemplate } = await import("./chunks/review-viewer-CGdWKpLa.js").then(async (m) => {
222174
222230
  await m.__tla;
222175
222231
  return m;
222176
222232
  });
@@ -222340,7 +222396,7 @@ ${result.stderr}`;
222340
222396
  const DEFAULT_HEALTHY_RUN_MS = 6e4;
222341
222397
  const DEFAULT_TERMINATION_GRACE_MS = 3e4;
222342
222398
  const DEFAULT_FORCE_KILL_WAIT_MS = 5e3;
222343
- function isChildProcessRunning(child) {
222399
+ function isChildProcessRunning$1(child) {
222344
222400
  return child !== null && child.exitCode === null && child.signalCode === null;
222345
222401
  }
222346
222402
  function runtimeMatchesHost(runtime, host) {
@@ -222760,7 +222816,7 @@ ${result.stderr}`;
222760
222816
  startHealthyRunTimer(run2) {
222761
222817
  this.clearHealthyRunTimer();
222762
222818
  const timer2 = setTimeout(() => {
222763
- if (this.healthyRunTimer !== timer2 || this.activeRun !== run2 || !this.isCurrent(run2.generation) || !isChildProcessRunning(run2.handle.child)) {
222819
+ if (this.healthyRunTimer !== timer2 || this.activeRun !== run2 || !this.isCurrent(run2.generation) || !isChildProcessRunning$1(run2.handle.child)) {
222764
222820
  return;
222765
222821
  }
222766
222822
  this.healthyRunTimer = null;
@@ -222810,7 +222866,7 @@ ${result.stderr}`;
222810
222866
  this.signalChild(run2.handle.child, "SIGTERM");
222811
222867
  }
222812
222868
  signalChild(child, signal) {
222813
- if (!isChildProcessRunning(child)) return;
222869
+ if (!isChildProcessRunning$1(child)) return;
222814
222870
  try {
222815
222871
  child.kill(signal);
222816
222872
  } catch {
@@ -222831,7 +222887,7 @@ ${result.stderr}`;
222831
222887
  }
222832
222888
  }
222833
222889
  async waitForChildExit(child) {
222834
- if (!isChildProcessRunning(child)) return;
222890
+ if (!isChildProcessRunning$1(child)) return;
222835
222891
  await new Promise((resolve2) => {
222836
222892
  let settled = false;
222837
222893
  const finish = () => {
@@ -222843,7 +222899,7 @@ ${result.stderr}`;
222843
222899
  };
222844
222900
  child.once("exit", finish);
222845
222901
  child.once("close", finish);
222846
- if (!isChildProcessRunning(child)) finish();
222902
+ if (!isChildProcessRunning$1(child)) finish();
222847
222903
  });
222848
222904
  }
222849
222905
  async abortable(operation, signal, message) {
@@ -223033,7 +223089,7 @@ ${result.stderr}`;
223033
223089
  }
223034
223090
  buildState() {
223035
223091
  const runtime = this.latestRuntimeState;
223036
- const childRunning = isChildProcessRunning(this.activeRun?.handle.child ?? null);
223092
+ const childRunning = isChildProcessRunning$1(this.activeRun?.handle.child ?? null);
223037
223093
  let phase;
223038
223094
  if (this.desiredState === "stopped") {
223039
223095
  phase = childRunning ? "stopping" : runtime ? phaseFromRuntime(runtime) : "stopped";
@@ -223197,6 +223253,8 @@ ${result.stdout ?? ""}`;
223197
223253
  const DAEMON_RUNNER_READY_FD = 3;
223198
223254
  const DAEMON_RUNNER_READY_TIMEOUT_MS = 3e4;
223199
223255
  const DAEMON_RUNNER_READY_MAX_BUFFER = 8 * 1024;
223256
+ const DAEMON_RUNNER_SHUTDOWN_GRACE_MS = 4e4;
223257
+ const DAEMON_RUNNER_FORCE_KILL_WAIT_MS = 5e3;
223200
223258
  const DaemonRunnerLaunchOutcomeSchema = discriminatedUnion("status", [
223201
223259
  object$1({
223202
223260
  status: literal$1("ready"),
@@ -223229,6 +223287,94 @@ ${result.stdout ?? ""}`;
223229
223287
  } catch {
223230
223288
  }
223231
223289
  }
223290
+ function interpretDaemonRunnerLaunchOutcome(outcome, runnerPid) {
223291
+ switch (outcome.status) {
223292
+ case "ready":
223293
+ return {
223294
+ outcome: {
223295
+ status: "ready",
223296
+ pid: runnerPid,
223297
+ instanceId: outcome.instanceId
223298
+ },
223299
+ cancelRunner: false
223300
+ };
223301
+ case "occupied":
223302
+ return {
223303
+ outcome: {
223304
+ status: "occupied",
223305
+ runnerPid,
223306
+ ownerMode: outcome.ownerMode,
223307
+ ownerPid: outcome.ownerPid
223308
+ },
223309
+ cancelRunner: false
223310
+ };
223311
+ case "error":
223312
+ return {
223313
+ outcome: {
223314
+ status: "error",
223315
+ runnerPid,
223316
+ message: outcome.message
223317
+ },
223318
+ cancelRunner: true
223319
+ };
223320
+ default: {
223321
+ const unreachable = outcome;
223322
+ return unreachable;
223323
+ }
223324
+ }
223325
+ }
223326
+ function isChildProcessRunning(child) {
223327
+ return child.exitCode === null && child.signalCode === null;
223328
+ }
223329
+ async function waitForChildProcessExit(child, timeoutMs) {
223330
+ if (!isChildProcessRunning(child)) return true;
223331
+ return await new Promise((resolve2) => {
223332
+ let settled = false;
223333
+ let timeout2;
223334
+ let onExit2;
223335
+ const finish = (exited) => {
223336
+ if (settled) return;
223337
+ settled = true;
223338
+ if (timeout2 !== void 0) clearTimeout(timeout2);
223339
+ child.off("exit", onExit2);
223340
+ child.off("close", onExit2);
223341
+ resolve2(exited);
223342
+ };
223343
+ onExit2 = () => finish(true);
223344
+ timeout2 = setTimeout(() => finish(!isChildProcessRunning(child)), timeoutMs);
223345
+ child.once("exit", onExit2);
223346
+ child.once("close", onExit2);
223347
+ if (!isChildProcessRunning(child)) finish(true);
223348
+ });
223349
+ }
223350
+ async function terminateSpawnedDaemonRunner(child, runnerPid, options = {}) {
223351
+ if (!isChildProcessRunning(child)) return true;
223352
+ const pidRecord = readPidFileRecord();
223353
+ let gracefulShutdownRequested = false;
223354
+ if (pidRecord?.pid === runnerPid) {
223355
+ const requested = await requestLocalCliHostShutdown({
223356
+ instanceId: pidRecord.instanceId,
223357
+ token: pidRecord.controlToken,
223358
+ expectedPid: runnerPid,
223359
+ expectedMode: "daemon"
223360
+ });
223361
+ gracefulShutdownRequested = requested.ok;
223362
+ }
223363
+ if (!gracefulShutdownRequested) {
223364
+ try {
223365
+ child.kill("SIGTERM");
223366
+ } catch {
223367
+ }
223368
+ }
223369
+ if (await waitForChildProcessExit(child, options.shutdownGraceMs ?? DAEMON_RUNNER_SHUTDOWN_GRACE_MS)) {
223370
+ return true;
223371
+ }
223372
+ try {
223373
+ child.kill("SIGKILL");
223374
+ } catch {
223375
+ }
223376
+ return await waitForChildProcessExit(child, options.forceKillWaitMs ?? DAEMON_RUNNER_FORCE_KILL_WAIT_MS);
223377
+ }
223232
223378
  async function spawnDaemonRunnerAndAwaitReady(passthroughArgs, options = {}) {
223233
223379
  const bin = resolveLodyBin();
223234
223380
  const env2 = {
@@ -223266,26 +223412,30 @@ ${result.stdout ?? ""}`;
223266
223412
  };
223267
223413
  }
223268
223414
  const readyPipe = child.stdio[DAEMON_RUNNER_READY_FD] ?? null;
223269
- const result = await new Promise((resolve2) => {
223415
+ const waitResult = await new Promise((resolve2) => {
223270
223416
  let settled = false;
223271
223417
  let buffer2 = "";
223272
- const finish = (outcome) => {
223418
+ let timeout2;
223419
+ const finish = (outcome, cancelRunner = false) => {
223273
223420
  if (settled) return;
223274
223421
  settled = true;
223275
- clearTimeout(timeout2);
223276
- resolve2(outcome);
223422
+ if (timeout2 !== void 0) clearTimeout(timeout2);
223423
+ resolve2({
223424
+ outcome,
223425
+ cancelRunner
223426
+ });
223277
223427
  };
223278
- const timeout2 = setTimeout(() => finish({
223428
+ timeout2 = setTimeout(() => finish({
223279
223429
  status: "timeout",
223280
223430
  runnerPid
223281
- }), options.timeoutMs ?? DAEMON_RUNNER_READY_TIMEOUT_MS);
223431
+ }, true), options.timeoutMs ?? DAEMON_RUNNER_READY_TIMEOUT_MS);
223282
223432
  timeout2.unref?.();
223283
223433
  if (!readyPipe) {
223284
223434
  finish({
223285
223435
  status: "error",
223286
223436
  runnerPid,
223287
223437
  message: "readiness channel unavailable"
223288
- });
223438
+ }, true);
223289
223439
  return;
223290
223440
  }
223291
223441
  readyPipe.on("data", (chunk) => {
@@ -223297,7 +223447,7 @@ ${result.stdout ?? ""}`;
223297
223447
  status: "error",
223298
223448
  runnerPid,
223299
223449
  message: "invalid readiness report"
223300
- });
223450
+ }, true);
223301
223451
  }
223302
223452
  return;
223303
223453
  }
@@ -223309,7 +223459,7 @@ ${result.stdout ?? ""}`;
223309
223459
  status: "error",
223310
223460
  runnerPid,
223311
223461
  message: "invalid readiness report"
223312
- });
223462
+ }, true);
223313
223463
  return;
223314
223464
  }
223315
223465
  const outcome = DaemonRunnerLaunchOutcomeSchema.safeParse(parsed);
@@ -223318,41 +223468,31 @@ ${result.stdout ?? ""}`;
223318
223468
  status: "error",
223319
223469
  runnerPid,
223320
223470
  message: "invalid readiness report"
223321
- });
223471
+ }, true);
223322
223472
  return;
223323
223473
  }
223324
- if (outcome.data.status === "ready") {
223325
- finish({
223326
- status: "ready",
223327
- pid: runnerPid,
223328
- instanceId: outcome.data.instanceId
223329
- });
223330
- } else if (outcome.data.status === "occupied") {
223331
- finish({
223332
- status: "occupied",
223333
- runnerPid,
223334
- ownerMode: outcome.data.ownerMode,
223335
- ownerPid: outcome.data.ownerPid
223336
- });
223337
- } else {
223338
- finish({
223339
- status: "error",
223340
- runnerPid,
223341
- message: outcome.data.message
223342
- });
223343
- }
223474
+ const interpreted = interpretDaemonRunnerLaunchOutcome(outcome.data, runnerPid);
223475
+ finish(interpreted.outcome, interpreted.cancelRunner);
223344
223476
  });
223345
223477
  readyPipe.once("end", () => finish({
223346
223478
  status: "runner_exited",
223347
223479
  runnerPid
223348
- }));
223480
+ }, true));
223349
223481
  readyPipe.once("error", () => finish({
223350
223482
  status: "runner_exited",
223351
223483
  runnerPid
223352
- }));
223484
+ }, true));
223353
223485
  });
223354
223486
  readyPipe?.removeAllListeners();
223355
223487
  readyPipe?.destroy();
223488
+ let result = waitResult.outcome;
223489
+ if (waitResult.cancelRunner && !await terminateSpawnedDaemonRunner(child, runnerPid)) {
223490
+ result = {
223491
+ status: "error",
223492
+ runnerPid,
223493
+ message: `Daemon launch failed (${result.status}) and runner ${runnerPid} did not exit`
223494
+ };
223495
+ }
223356
223496
  child.unref();
223357
223497
  return result;
223358
223498
  }
@@ -223457,21 +223597,115 @@ ${result.stdout ?? ""}`;
223457
223597
  ...sanitized
223458
223598
  ];
223459
223599
  }
223460
- function formatDaemonBackendStatus(runtimeState) {
223600
+ const DAEMON_CONNECTION_ERROR_AFTER_MS = 6e4;
223601
+ const defaultPalette = {
223602
+ success: chalk.green,
223603
+ warning: chalk.yellow,
223604
+ error: chalk.red,
223605
+ muted: chalk.gray
223606
+ };
223607
+ function formatElapsed(elapsedMs) {
223608
+ const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
223609
+ if (totalSeconds < 60) return `${totalSeconds}s`;
223610
+ const totalMinutes = Math.floor(totalSeconds / 60);
223611
+ const seconds2 = totalSeconds % 60;
223612
+ if (totalMinutes < 60) {
223613
+ return seconds2 === 0 ? `${totalMinutes}m` : `${totalMinutes}m ${seconds2}s`;
223614
+ }
223615
+ const totalHours = Math.floor(totalMinutes / 60);
223616
+ const minutes2 = totalMinutes % 60;
223617
+ if (totalHours < 24) {
223618
+ return minutes2 === 0 ? `${totalHours}h` : `${totalHours}h ${minutes2}m`;
223619
+ }
223620
+ const days2 = Math.floor(totalHours / 24);
223621
+ const hours2 = totalHours % 24;
223622
+ return hours2 === 0 ? `${days2}d` : `${days2}d ${hours2}h`;
223623
+ }
223624
+ function formatAuthorization(authorization, palette) {
223625
+ switch (authorization) {
223626
+ case "authorized":
223627
+ return palette.success(authorization);
223628
+ case "pending":
223629
+ return palette.warning(authorization);
223630
+ case "rejected":
223631
+ return palette.error(authorization);
223632
+ case "unknown":
223633
+ return palette.muted(authorization);
223634
+ default: {
223635
+ const unreachable = authorization;
223636
+ return unreachable;
223637
+ }
223638
+ }
223639
+ }
223640
+ function formatConnection(connection2, notConnectedSinceMs, options) {
223641
+ if (connection2 === "connected") {
223642
+ return {
223643
+ formatted: options.palette.success(connection2),
223644
+ unavailableFor: null,
223645
+ stale: false
223646
+ };
223647
+ }
223648
+ if (connection2 === "unknown") {
223649
+ return {
223650
+ formatted: options.palette.muted(connection2),
223651
+ unavailableFor: null,
223652
+ stale: false
223653
+ };
223654
+ }
223655
+ const elapsedMs = notConnectedSinceMs === void 0 ? null : Math.max(0, options.nowMs - notConnectedSinceMs);
223656
+ const unavailableFor = elapsedMs === null ? null : formatElapsed(elapsedMs);
223657
+ const label2 = unavailableFor === null ? connection2 : `${connection2} (${unavailableFor})`;
223658
+ const stale = elapsedMs !== null && elapsedMs >= options.connectionErrorAfterMs;
223659
+ const tone = stale || connection2 === "disconnected" ? options.palette.error : options.palette.warning;
223660
+ return {
223661
+ formatted: tone(label2),
223662
+ unavailableFor,
223663
+ stale
223664
+ };
223665
+ }
223666
+ function formatDaemonConnectivityStatus(connectivity, palette = defaultPalette) {
223667
+ switch (connectivity) {
223668
+ case "online":
223669
+ return palette.success(connectivity);
223670
+ case "reconnecting":
223671
+ return palette.warning(connectivity);
223672
+ case "offline":
223673
+ return palette.error(connectivity);
223674
+ default: {
223675
+ const unreachable = connectivity;
223676
+ return unreachable;
223677
+ }
223678
+ }
223679
+ }
223680
+ function formatDaemonBackendStatus(runtimeState, options = {}) {
223681
+ const resolvedOptions = {
223682
+ nowMs: options.nowMs ?? Date.now(),
223683
+ connectionErrorAfterMs: options.connectionErrorAfterMs ?? DAEMON_CONNECTION_ERROR_AFTER_MS,
223684
+ palette: options.palette ?? defaultPalette
223685
+ };
223686
+ const authorization = runtimeState.backend?.authorization ?? "unknown";
223687
+ const backendConnection = formatConnection(runtimeState.backend?.connection ?? "unknown", runtimeState.connectionAges?.backendNotConnectedSinceMs, resolvedOptions);
223461
223688
  const lines2 = [
223462
- ` Backend Auth: ${runtimeState.backend?.authorization ?? "unknown"}`,
223463
- ` Backend Link: ${runtimeState.backend?.connection ?? "unknown"}`
223689
+ ` Backend Auth: ${formatAuthorization(authorization, resolvedOptions.palette)}`,
223690
+ ` Backend Link: ${backendConnection.formatted}`
223464
223691
  ];
223692
+ if (backendConnection.stale && backendConnection.unavailableFor) {
223693
+ lines2.push(resolvedOptions.palette.error(` Backend Error: not connected for ${backendConnection.unavailableFor}`));
223694
+ }
223465
223695
  if (!runtimeState.connectedWorkspaces) {
223466
- lines2.push(" Workspaces: unavailable");
223696
+ lines2.push(` Workspaces: ${resolvedOptions.palette.muted("unavailable")}`);
223467
223697
  return lines2;
223468
223698
  }
223469
223699
  lines2.push(` Workspaces: ${runtimeState.connectedWorkspaces.length}`);
223470
223700
  for (const workspace of runtimeState.connectedWorkspaces) {
223471
223701
  const label2 = workspace.slug ? `${workspace.name} (${workspace.slug})` : workspace.name;
223702
+ const connection2 = formatConnection(workspace.backendConnection, runtimeState.connectionAges?.workspaceNotConnectedSinceMs?.[workspace.id], resolvedOptions);
223472
223703
  lines2.push(` - ${label2} [${workspace.role}]`);
223473
223704
  lines2.push(` ID: ${workspace.id}`);
223474
- lines2.push(` Backend: ${workspace.backendConnection}`);
223705
+ lines2.push(` Backend: ${connection2.formatted}`);
223706
+ if (connection2.stale && connection2.unavailableFor) {
223707
+ lines2.push(resolvedOptions.palette.error(` Error: not connected for ${connection2.unavailableFor}`));
223708
+ }
223475
223709
  }
223476
223710
  return lines2;
223477
223711
  }
@@ -223567,7 +223801,7 @@ ${result.stdout ?? ""}`;
223567
223801
  };
223568
223802
  case "error":
223569
223803
  return {
223570
- status: "runner_error",
223804
+ status: "startup_error",
223571
223805
  pid: result.runnerPid,
223572
223806
  message: result.message
223573
223807
  };
@@ -223582,7 +223816,7 @@ ${result.stdout ?? ""}`;
223582
223816
  };
223583
223817
  case "timeout":
223584
223818
  return {
223585
- status: "claim_timeout",
223819
+ status: "startup_timeout",
223586
223820
  pid: result.runnerPid
223587
223821
  };
223588
223822
  default: {
@@ -223693,11 +223927,11 @@ ${result.stdout ?? ""}`;
223693
223927
  await exitDaemonCommand(1);
223694
223928
  return;
223695
223929
  }
223696
- if (result.status === "runner_error") {
223930
+ if (result.status === "startup_error") {
223697
223931
  captureDaemonEvent("daemon_start_failed", {
223698
- reason_code: "runner_error"
223932
+ reason_code: "startup_error"
223699
223933
  });
223700
- console.error(`Daemon runner (PID ${result.pid}) failed to start: ${result.message}`);
223934
+ console.error(`Daemon startup (runner PID ${result.pid}) failed: ${result.message}`);
223701
223935
  await exitDaemonCommand(1);
223702
223936
  return;
223703
223937
  }
@@ -223705,15 +223939,15 @@ ${result.stdout ?? ""}`;
223705
223939
  captureDaemonEvent("daemon_start_failed", {
223706
223940
  reason_code: "runner_exited"
223707
223941
  });
223708
- console.error(`Daemon runner (PID ${result.pid}) exited before claiming ownership.`);
223942
+ console.error(`Daemon runner (PID ${result.pid}) exited before its worker became ready.`);
223709
223943
  await exitDaemonCommand(1);
223710
223944
  return;
223711
223945
  }
223712
- if (result.status === "claim_timeout") {
223946
+ if (result.status === "startup_timeout") {
223713
223947
  captureDaemonEvent("daemon_start_failed", {
223714
- reason_code: "claim_timeout"
223948
+ reason_code: "startup_timeout"
223715
223949
  });
223716
- console.error(`Daemon runner (PID ${result.pid}) did not claim ownership in time.`);
223950
+ console.error(`Daemon worker (PID ${result.pid}) did not become ready in time.`);
223717
223951
  await exitDaemonCommand(1);
223718
223952
  return;
223719
223953
  }
@@ -223775,9 +224009,9 @@ ${result.stdout ?? ""}`;
223775
224009
  console.log(` Stage: ${runtimeState.startupStage}`);
223776
224010
  }
223777
224011
  if (runtimeBelongsToDaemon && runtimeState.connectivity) {
223778
- console.log(` Connectivity: ${runtimeState.connectivity}`);
224012
+ console.log(` Connectivity: ${formatDaemonConnectivityStatus(runtimeState.connectivity)}`);
223779
224013
  }
223780
- if (runtimeBelongsToDaemon) {
224014
+ if (runtimeBelongsToDaemon && getCliPlatformKind() === "cloud") {
223781
224015
  for (const line3 of formatDaemonBackendStatus(runtimeState)) {
223782
224016
  console.log(line3);
223783
224017
  }
@@ -223910,18 +224144,18 @@ ${result.stdout ?? ""}`;
223910
224144
  await exitDaemonCommand(1);
223911
224145
  return;
223912
224146
  }
223913
- if (startResult.status === "runner_error") {
223914
- console.error(`Daemon runner (PID ${startResult.pid}) failed to start: ${startResult.message}`);
224147
+ if (startResult.status === "startup_error") {
224148
+ console.error(`Daemon startup (runner PID ${startResult.pid}) failed: ${startResult.message}`);
223915
224149
  await exitDaemonCommand(1);
223916
224150
  return;
223917
224151
  }
223918
224152
  if (startResult.status === "runner_exited") {
223919
- console.error(`Daemon runner (PID ${startResult.pid}) exited before claiming ownership.`);
224153
+ console.error(`Daemon runner (PID ${startResult.pid}) exited before its worker became ready.`);
223920
224154
  await exitDaemonCommand(1);
223921
224155
  return;
223922
224156
  }
223923
- if (startResult.status === "claim_timeout") {
223924
- console.error(`Daemon runner (PID ${startResult.pid}) did not claim ownership in time.`);
224157
+ if (startResult.status === "startup_timeout") {
224158
+ console.error(`Daemon worker (PID ${startResult.pid}) did not become ready in time.`);
223925
224159
  await exitDaemonCommand(1);
223926
224160
  return;
223927
224161
  }
@@ -223933,6 +224167,23 @@ ${result.stdout ?? ""}`;
223933
224167
  await exitDaemonCommand(0);
223934
224168
  return;
223935
224169
  }));
224170
+ const MAX_STARTUP_ERROR_CHARS = 2e3;
224171
+ function isDaemonWorkerReady(state2) {
224172
+ return state2.runtime?.startupStage === "ready" && state2.runtime.phase !== "fatal";
224173
+ }
224174
+ function isRetryableDaemonWorkerStartupExit(result) {
224175
+ return result.code === EXIT_CODE_RETRYABLE_STARTUP;
224176
+ }
224177
+ function describeDaemonWorkerStartupFailure(result) {
224178
+ const output = (result.stderr.trim() || result.stdout.trim()).split("\0").join("");
224179
+ if (output) {
224180
+ return output.length <= MAX_STARTUP_ERROR_CHARS ? output : `\u2026${output.slice(-MAX_STARTUP_ERROR_CHARS)}`;
224181
+ }
224182
+ if (result.signal) {
224183
+ return `CLI worker exited from signal ${result.signal} before becoming ready`;
224184
+ }
224185
+ return `CLI worker exited with code ${result.code ?? "unknown"} before becoming ready`;
224186
+ }
223936
224187
  function launchLodyStart(passthroughArgs, identity2) {
223937
224188
  const bin = resolveLodyBin();
223938
224189
  const args2 = [
@@ -224014,6 +224265,12 @@ ${result.stdout ?? ""}`;
224014
224265
  instanceId: randomUUID(),
224015
224266
  token: `${randomUUID()}${randomUUID()}`
224016
224267
  };
224268
+ let launchOutcomePending = process.env[DAEMON_RUNNER_READY_FD_ENV] !== void 0;
224269
+ const reportLaunchOutcome = (outcome) => {
224270
+ if (!launchOutcomePending) return;
224271
+ launchOutcomePending = false;
224272
+ reportDaemonRunnerLaunchOutcome(outcome);
224273
+ };
224017
224274
  let hostShutdownRequested = false;
224018
224275
  let hostShutdownHandler = null;
224019
224276
  const requestHostShutdown = () => {
@@ -224032,7 +224289,7 @@ ${result.stdout ?? ""}`;
224032
224289
  if (initialLease.status === "occupied") {
224033
224290
  const owner = initialLease.record ? `${initialLease.record.mode} process ${initialLease.record.pid}` : "another local CLI host";
224034
224291
  logger2.error(`Cannot start daemon: ${owner} already owns the local agent runtime.`);
224035
- reportDaemonRunnerLaunchOutcome({
224292
+ reportLaunchOutcome({
224036
224293
  status: "occupied",
224037
224294
  ...initialLease.record ? {
224038
224295
  ownerMode: initialLease.record.mode,
@@ -224051,18 +224308,13 @@ ${result.stdout ?? ""}`;
224051
224308
  hostLease = null;
224052
224309
  const message = error2 instanceof Error ? error2.message : String(error2);
224053
224310
  logger2.error(`Failed to claim daemon ownership: ${message}`);
224054
- reportDaemonRunnerLaunchOutcome({
224311
+ reportLaunchOutcome({
224055
224312
  status: "error",
224056
224313
  message
224057
224314
  });
224058
224315
  await flushTelemetry();
224059
224316
  process.exit(1);
224060
224317
  }
224061
- reportDaemonRunnerLaunchOutcome({
224062
- status: "ready",
224063
- pid: process.pid,
224064
- instanceId: supervisorIdentity.instanceId
224065
- });
224066
224318
  logger2.info(`Daemon watchdog started (PID ${process.pid})`);
224067
224319
  await normalizeCurrentProcessResourceProfile(logger2);
224068
224320
  for (const diagnostic of getRuntimeDiagnostics(version$9)) {
@@ -224189,6 +224441,17 @@ ${result.stdout ?? ""}`;
224189
224441
  }
224190
224442
  },
224191
224443
  decideExit: async (result, signal) => {
224444
+ if (launchOutcomePending && !isRetryableDaemonWorkerStartupExit(result)) {
224445
+ const message = describeDaemonWorkerStartupFailure(result);
224446
+ reportLaunchOutcome({
224447
+ status: "error",
224448
+ message
224449
+ });
224450
+ return {
224451
+ action: "fatal",
224452
+ message: `Initial daemon worker startup failed: ${message}`
224453
+ };
224454
+ }
224192
224455
  if (result.code === 0) {
224193
224456
  return {
224194
224457
  action: "stop",
@@ -224256,6 +224519,13 @@ ${result.stdout ?? ""}`;
224256
224519
  },
224257
224520
  onStateChange: (state2) => {
224258
224521
  reportSupervisorAnalytics(state2);
224522
+ if (launchOutcomePending && isDaemonWorkerReady(state2)) {
224523
+ reportLaunchOutcome({
224524
+ status: "ready",
224525
+ pid: process.pid,
224526
+ instanceId: supervisorIdentity.instanceId
224527
+ });
224528
+ }
224259
224529
  if (state2.phase === "fatal") {
224260
224530
  logger2.error(`Fatal: ${state2.message ?? "unknown"}`);
224261
224531
  } else if (state2.message) {
@@ -240485,6 +240755,8 @@ ${result.stdout ?? ""}`;
240485
240755
  const SESSION_CREATE_MANY_TOOL_NAME = "lody_session_create_many";
240486
240756
  const SESSION_CHAT_MANY_TOOL_NAME = "lody_session_chat_many";
240487
240757
  const SESSION_ARCHIVE_TOOL_NAME = "lody_session_archive";
240758
+ const SESSION_RENAME_TOOL_NAME = "lody_session_rename";
240759
+ const SESSION_RENAME_MANY_TOOL_NAME = "lody_session_rename_many";
240488
240760
  const OPERATION_GET_TOOL_NAME = "lody_operation_get";
240489
240761
  const OPERATION_CANCEL_TOOL_NAME = "lody_operation_cancel";
240490
240762
  const TASK_LIST_TOOL_NAME = "lody_task_list";
@@ -240509,6 +240781,7 @@ ${result.stdout ?? ""}`;
240509
240781
  const DEFAULT_MCP_SESSION_HISTORY_LIMIT = 10;
240510
240782
  const MAX_MCP_SESSION_HISTORY_LIMIT = 50;
240511
240783
  const MAX_MCP_SESSION_HISTORY_BYTES = 128 * 1024;
240784
+ const MAX_MCP_SESSION_TITLE_CHARS = 200;
240512
240785
  const MAX_MCP_TASK_BODY_BYTES = 64 * 1024;
240513
240786
  const MAX_MCP_TASK_LINKS = 50;
240514
240787
  const MAX_MCP_TASK_EDIT_CHARS = 64e3;
@@ -240639,7 +240912,7 @@ ${result.stdout ?? ""}`;
240639
240912
  resume: boolean().optional().describe("Set to true to resume an accepted Operation without resending its input."),
240640
240913
  wait: literal$1(true).optional().describe("Wait for the assistant reply using the temporary legacy adapter."),
240641
240914
  timeoutSeconds: number$4().int().positive().max(MAX_MCP_SESSION_WAIT_TIMEOUT_SECONDS).optional().describe("Legacy wait timeout in seconds, up to one hour."),
240642
- useCurrentSessionAsParent: boolean().optional().describe("Create a child of the current Session; cannot be combined with workContext."),
240915
+ useCurrentSessionAsParent: boolean().optional().describe("Create a child of the current Session that reuses the exact same workspace directory; cannot be combined with workContext."),
240643
240916
  workContext: SessionWorkContextInputSchema.optional().describe("Execution context for an independent Session; cannot be combined with useCurrentSessionAsParent=true.")
240644
240917
  }).strict().superRefine((value, ctx) => {
240645
240918
  const result = SessionCreateRuntimeInputSchema.safeParse(value);
@@ -240761,7 +241034,7 @@ ${result.stdout ?? ""}`;
240761
241034
  ]);
240762
241035
  const SessionCreateBatchPublishedItemSchema = object$1({
240763
241036
  ...SessionCreateBatchItemShape,
240764
- useCurrentSessionAsParent: boolean().optional().describe("Create a child of the current Session; cannot be combined with workContext."),
241037
+ useCurrentSessionAsParent: boolean().optional().describe("Create a child of the current Session that reuses the exact same workspace directory; cannot be combined with workContext."),
240765
241038
  workContext: SessionWorkContextInputSchema.optional().describe("Execution context for an independent Session; cannot be combined with useCurrentSessionAsParent=true.")
240766
241039
  }).strict();
240767
241040
  const SessionCreateManyToolInputSchema = object$1({
@@ -240793,6 +241066,18 @@ ${result.stdout ?? ""}`;
240793
241066
  const SessionArchiveToolInputSchema = object$1({
240794
241067
  sessionId: string$1().trim().min(1)
240795
241068
  }).strict();
241069
+ const SessionTitleToolInputSchema = string$1().trim().min(1).max(MAX_MCP_SESSION_TITLE_CHARS).describe(`New session title, up to ${MAX_MCP_SESSION_TITLE_CHARS} characters.`);
241070
+ const SessionRenameToolInputSchema = object$1({
241071
+ sessionId: string$1().trim().min(1).optional().describe("Target session id, or current. Defaults to current."),
241072
+ title: SessionTitleToolInputSchema
241073
+ }).strict();
241074
+ const SessionRenameManyItemSchema = object$1({
241075
+ sessionId: string$1().trim().min(1).describe("Target session id, or current."),
241076
+ title: SessionTitleToolInputSchema
241077
+ }).strict();
241078
+ const SessionRenameManyToolInputSchema = object$1({
241079
+ items: array$2(SessionRenameManyItemSchema).min(1).max(MAX_MCP_COMMAND_BATCH_SIZE).refine((items2) => new Set(items2.map((item) => item.sessionId)).size === items2.length, "sessionId values must be unique")
241080
+ }).strict();
240796
241081
  const SessionStatusManyToolInputSchema = object$1({
240797
241082
  sessionIds: array$2(string$1().trim().min(1)).max(1e3).refine((items2) => new Set(items2).size === items2.length, "sessionIds must be unique")
240798
241083
  }).strict();
@@ -240907,30 +241192,22 @@ ${result.stdout ?? ""}`;
240907
241192
  } : {}
240908
241193
  });
240909
241194
  const jsonTextResult = (value, isError2 = false) => textResult(JSON.stringify(value, null, 2), isError2);
240910
- const mcpErrorResult = (error2) => {
241195
+ const normalizeMcpError = (error2) => {
240911
241196
  if (error2 instanceof LodyOperationStoreError) {
240912
- return jsonTextResult({
240913
- ok: false,
240914
- error: error2.toLodyError()
240915
- }, true);
241197
+ return error2.toLodyError();
240916
241198
  }
240917
241199
  if (error2 instanceof LocalDaemonAvailabilityError) {
240918
- return jsonTextResult({
240919
- ok: false,
240920
- error: error2.toLodyError()
240921
- }, true);
241200
+ return error2.toLodyError();
240922
241201
  }
240923
241202
  if (error2 instanceof WorkspaceSyncUnavailableError) {
240924
- return jsonTextResult({
240925
- ok: false,
240926
- error: error2.toLodyError()
240927
- }, true);
241203
+ return error2.toLodyError();
240928
241204
  }
240929
- return jsonTextResult({
240930
- ok: false,
240931
- error: makeLodyError("INTERNAL_ERROR", formatMcpErrorMessage(error2), false)
240932
- }, true);
241205
+ return makeLodyError("INTERNAL_ERROR", formatMcpErrorMessage(error2), false);
240933
241206
  };
241207
+ const mcpErrorResult = (error2) => jsonTextResult({
241208
+ ok: false,
241209
+ error: normalizeMcpError(error2)
241210
+ }, true);
240934
241211
  const postSessionControl = async (request2, localControlSocketPath, timeoutMs = SESSION_CONTROL_TIMEOUT_MS) => {
240935
241212
  const responses = await runPromise(makeLocalControlClientAuto({
240936
241213
  socketPath: localControlSocketPath
@@ -242264,6 +242541,60 @@ ${prompt2}` : prompt2;
242264
242541
  }, worker));
242265
242542
  return output;
242266
242543
  };
242544
+ const resolveSessionRenameItems = (items2, ctx) => {
242545
+ assertBatchSize(items2.length, MAX_MCP_COMMAND_BATCH_SIZE);
242546
+ const resolved = items2.map((item) => ({
242547
+ sessionId: resolveMcpSessionId(item.sessionId, ctx),
242548
+ title: item.title
242549
+ }));
242550
+ if (new Set(resolved.map((item) => item.sessionId)).size !== resolved.length) {
242551
+ throw new LodyOperationStoreError("DUPLICATE_SESSION", "A session may appear only once in a rename batch.", false);
242552
+ }
242553
+ return resolved;
242554
+ };
242555
+ const applySessionRenameItems = async (items2, rename2) => await mapWithConcurrency(items2, 5, async (item) => {
242556
+ try {
242557
+ await rename2(item);
242558
+ return {
242559
+ sessionId: item.sessionId,
242560
+ ok: true,
242561
+ title: item.title
242562
+ };
242563
+ } catch (error2) {
242564
+ return {
242565
+ sessionId: item.sessionId,
242566
+ ok: false,
242567
+ error: normalizeMcpError(error2)
242568
+ };
242569
+ }
242570
+ });
242571
+ const persistSessionRenameItems = async (manager, items2, syncReason) => {
242572
+ const results = await applySessionRenameItems(items2, async (item) => {
242573
+ const session2 = await readCurrentSessionMeta(manager, item.sessionId);
242574
+ if (!session2) {
242575
+ throw new LodyOperationStoreError("SESSION_NOT_FOUND", `Session not found: ${item.sessionId}`, false);
242576
+ }
242577
+ await manager.repo.upsertDocMeta(getSessionRoomId(item.sessionId), {
242578
+ title: item.title,
242579
+ titleSource: "user"
242580
+ });
242581
+ });
242582
+ if (results.some((item) => item.ok)) {
242583
+ await ensureWorkspaceMetaSynced(manager, syncReason);
242584
+ }
242585
+ return results;
242586
+ };
242587
+ const renameSessionItems = async (input2) => {
242588
+ const ctx = getSessionContext();
242589
+ const resolved = resolveSessionRenameItems(input2.items, ctx);
242590
+ const auth = getAuthContextOrThrow$1("mcp");
242591
+ const workspace = await resolveWorkspaceOrThrow$1(auth, getMcpWorkspaceId(ctx));
242592
+ return await withWorkspaceManager$1(auth, workspace, "mcp", async (manager) => {
242593
+ const reason = `mcp.session_rename_many:${ctx.sessionId}`;
242594
+ await syncWorkspaceMetaForRead(manager, reason);
242595
+ return await persistSessionRenameItems(manager, resolved, reason);
242596
+ });
242597
+ };
242267
242598
  const buildOperationTargetCancelArgs = (workspaceId, item) => [
242268
242599
  "session",
242269
242600
  "cancel",
@@ -243244,6 +243575,41 @@ ${lines2.join("\n")}` : ""}${suffix}`);
243244
243575
  return mcpErrorResult(error2);
243245
243576
  }
243246
243577
  });
243578
+ server.registerTool(SESSION_RENAME_TOOL_NAME, {
243579
+ title: "Rename a Lody session",
243580
+ description: "Set the title of one Session. Omit sessionId (or pass current) to rename the current Session. The durable rename is treated as a user-directed title and is not replaced by later automatic title generation.",
243581
+ inputSchema: SessionRenameToolInputSchema
243582
+ }, async (args2) => {
243583
+ try {
243584
+ const [item] = await renameSessionItems({
243585
+ items: [
243586
+ {
243587
+ sessionId: args2.sessionId ?? "current",
243588
+ title: args2.title
243589
+ }
243590
+ ]
243591
+ });
243592
+ if (!item) {
243593
+ throw new Error("Session rename returned no result.");
243594
+ }
243595
+ return jsonTextResult(item, item.ok === false);
243596
+ } catch (error2) {
243597
+ return mcpErrorResult(error2);
243598
+ }
243599
+ });
243600
+ server.registerTool(SESSION_RENAME_MANY_TOOL_NAME, {
243601
+ title: "Rename multiple Lody sessions",
243602
+ description: "Set titles for 1-20 Sessions. Session ids must be unique. Results preserve input order and failures are isolated per item; successful renames remain applied when another item fails.",
243603
+ inputSchema: SessionRenameManyToolInputSchema
243604
+ }, async (args2) => {
243605
+ try {
243606
+ return jsonTextResult({
243607
+ items: await renameSessionItems(args2)
243608
+ });
243609
+ } catch (error2) {
243610
+ return mcpErrorResult(error2);
243611
+ }
243612
+ });
243247
243613
  server.registerTool(SESSION_ARCHIVE_TOOL_NAME, {
243248
243614
  title: "Archive a Lody session",
243249
243615
  description: "Idempotently archive a Session. Archiving a parent cascades to child Sessions; pending Operation Deliveries remain pending until a human restores the Session.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lody",
3
- "version": "0.89.4",
3
+ "version": "0.90.0",
4
4
  "description": "Lody Agent CLI tool for managing remote command execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",