@adhdev/daemon-core 0.9.82-rc.448 → 0.9.82-rc.449

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.
@@ -38,6 +38,16 @@ export interface CliTransportFactoryParams {
38
38
  cliArgs?: string[];
39
39
  providerSessionId?: string;
40
40
  attachExisting?: boolean;
41
+ /**
42
+ * Launch-time record meta (meshNodeId / meshNodeFor / launchedByCoordinator /
43
+ * autoLaunchedForQueueTaskId). Defense-in-depth for SESSION-ACCUMULATION-LEAK:
44
+ * a session-host factory impl SHOULD seed the create_session record with this
45
+ * so the node binding is present the instant the record exists, rather than
46
+ * relying solely on the post-spawn updateRuntimeMeta round-trip. Optional and
47
+ * additive — factory impls that ignore it keep the prior behavior; the
48
+ * post-spawn WTDISPATCH stamp in register() still runs as the primary path.
49
+ */
50
+ initialMeta?: Record<string, unknown>;
41
51
  }
42
52
  export interface HostedCliRuntimeDescriptor {
43
53
  runtimeId: string;
@@ -94,6 +94,16 @@ export interface MedFamilyContext {
94
94
  dryRun?: boolean;
95
95
  source?: 'mesh_cleanup_sessions' | 'mesh_remove_node' | 'magi_session_cleanup';
96
96
  requireAutoLaunchedForTaskIds?: Record<string, string>;
97
+ /**
98
+ * Opt-in orphan reclaim (default false). When true, a workspace-only live
99
+ * session (no node binding) OR a session bound to a node that no longer
100
+ * exists in `liveMeshNodeIds` is stopped instead of skipped — reclaims the
101
+ * SESSION-ACCUMULATION-LEAK orphans. `liveMeshNodeIds` is the current mesh's
102
+ * node id set; a session whose meshNodeId is still present is treated as an
103
+ * active sibling and left alone.
104
+ */
105
+ reclaimOrphans?: boolean;
106
+ liveMeshNodeIds?: string[];
97
107
  }) => Promise<{
98
108
  success: boolean;
99
109
  [key: string]: unknown;
package/dist/index.js CHANGED
@@ -409,10 +409,10 @@ function readInjected(value) {
409
409
  }
410
410
  function getDaemonBuildInfo() {
411
411
  if (cached) return cached;
412
- const commit = readInjected(true ? "7636b9d4fc6c456ebfe178b9b0a7b81664b02257" : void 0) ?? "unknown";
413
- const commitShort = readInjected(true ? "7636b9d4" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
- const version = readInjected(true ? "0.9.82-rc.448" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
- const builtAt = readInjected(true ? "2026-07-02T00:30:54.215Z" : void 0);
412
+ const commit = readInjected(true ? "fe0e4d2aa03aea2793c2379e5ffc89abef5143d6" : void 0) ?? "unknown";
413
+ const commitShort = readInjected(true ? "fe0e4d2a" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
+ const version = readInjected(true ? "0.9.82-rc.449" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
+ const builtAt = readInjected(true ? "2026-07-03T01:58:07.610Z" : void 0);
416
416
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
417
417
  return cached;
418
418
  }
@@ -38006,6 +38006,16 @@ var TerminalAdapter = class {
38006
38006
  this.recordEvent("input", capPreview(escapeControl(text)), text.length);
38007
38007
  this.pty?.write(text);
38008
38008
  }
38009
+ /** Forward runtime metadata (meshNodeId, workspaceLabel, lifecycle, …) to
38010
+ * the underlying transport so it reaches the session registry. The spec
38011
+ * path previously dropped everything but providerSessionId here, which
38012
+ * left autoLaunch's meshNodeId stamp unbound on the record (see
38013
+ * SESSION-ACCUMULATION-LEAK). No-op when the transport does not support
38014
+ * metadata updates (e.g. plain node-pty). */
38015
+ updateMeta(meta, replace = false) {
38016
+ if (!this.pty || typeof this.pty.updateMeta !== "function") return;
38017
+ this.pty.updateMeta(meta, replace);
38018
+ }
38009
38019
  /** Debug-only: most-recent PTY input/output/resize/cursor events, oldest
38010
38020
  * first. Pure observation — never consulted by the FSM. */
38011
38021
  getEventTimeline(limit = MAX_PTY_EVENTS) {
@@ -38305,6 +38315,13 @@ var FsmDriver = class {
38305
38315
  return;
38306
38316
  }
38307
38317
  }
38318
+ /** Forward runtime metadata to the terminal transport so mesh binding
38319
+ * fields (meshNodeId / meshNodeFor / workspaceLabel / lifecycle) reach
38320
+ * the session registry. Not a DashboardCommand — this is a control-plane
38321
+ * update, not user input. */
38322
+ updateMeta(meta, replace = false) {
38323
+ this.adapter.updateMeta(meta, replace);
38324
+ }
38308
38325
  snapshot() {
38309
38326
  return this.adapter.snapshot();
38310
38327
  }
@@ -40509,9 +40526,14 @@ var SpecCliAdapter = class _SpecCliAdapter {
40509
40526
  };
40510
40527
  }
40511
40528
  updateRuntimeMeta(meta) {
40512
- if (meta && typeof meta.providerSessionId === "string") {
40529
+ if (!meta) return;
40530
+ if (typeof meta.providerSessionId === "string") {
40513
40531
  this.providerSessionId = meta.providerSessionId;
40514
40532
  }
40533
+ try {
40534
+ this.driver.updateMeta(meta);
40535
+ } catch {
40536
+ }
40515
40537
  }
40516
40538
  refreshProviderDefinition() {
40517
40539
  }
@@ -44947,14 +44969,15 @@ var DaemonCliManager = class {
44947
44969
  console.error(colorize("red", ` \u2717 Failed to save recent activity: ${e}`));
44948
44970
  }
44949
44971
  }
44950
- getTransportFactory(runtimeId, providerType, workspace, cliArgs, providerSessionId, attachExisting = false) {
44972
+ getTransportFactory(runtimeId, providerType, workspace, cliArgs, providerSessionId, attachExisting = false, initialMeta) {
44951
44973
  return this.deps.createPtyTransportFactory?.({
44952
44974
  runtimeId,
44953
44975
  providerType,
44954
44976
  workspace,
44955
44977
  cliArgs,
44956
44978
  providerSessionId,
44957
- attachExisting
44979
+ attachExisting,
44980
+ ...initialMeta && Object.keys(initialMeta).length ? { initialMeta } : {}
44958
44981
  }) || void 0;
44959
44982
  }
44960
44983
  createAdapter(cliType, workingDir, cliArgs, runtimeId, providerSessionId, attachExisting = false, extraEnv) {
@@ -45010,13 +45033,25 @@ var DaemonCliManager = class {
45010
45033
  const instanceManager = this.deps.getInstanceManager();
45011
45034
  const sessionRegistry = this.deps.getSessionRegistry?.() || null;
45012
45035
  if (!instanceManager) throw new Error("InstanceManager not available");
45036
+ const launchMeshNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
45037
+ const launchMeshNodeFor = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
45038
+ const launchAutoLaunchedForQueueTaskId = typeof settings?.autoLaunchedForQueueTaskId === "string" ? settings.autoLaunchedForQueueTaskId.trim() : "";
45039
+ const launchRecordMeta = {
45040
+ ...launchMeshNodeId ? { meshNodeId: launchMeshNodeId } : {},
45041
+ ...launchMeshNodeFor ? { meshNodeFor: launchMeshNodeFor } : {},
45042
+ ...settings?.launchedByCoordinator === true ? { launchedByCoordinator: true } : {},
45043
+ ...launchAutoLaunchedForQueueTaskId ? { autoLaunchedForQueueTaskId: launchAutoLaunchedForQueueTaskId } : {}
45044
+ };
45013
45045
  const transportFactory = this.getTransportFactory(
45014
45046
  key2,
45015
45047
  normalizedType,
45016
45048
  resolvedDir,
45017
45049
  cliArgs,
45018
45050
  options?.providerSessionId,
45019
- attachExisting
45051
+ attachExisting,
45052
+ // Only seed at create time for fresh launches — an attach restores an
45053
+ // existing record whose meta is already stamped; re-seeding could clobber.
45054
+ attachExisting ? void 0 : launchRecordMeta
45020
45055
  );
45021
45056
  const cliInstance = new CliProviderInstance(provider, resolvedDir, cliArgs, key2, transportFactory, options);
45022
45057
  try {
@@ -45053,17 +45088,9 @@ var DaemonCliManager = class {
45053
45088
  throw new Error(`Failed to start ${provider.displayName || provider.name || cliType}: ${spawnErr?.message}`);
45054
45089
  }
45055
45090
  this.adapters.set(key2, cliInstance.getAdapter());
45056
- const launchMeshNodeId = typeof settings?.meshNodeId === "string" ? settings.meshNodeId.trim() : "";
45057
- const launchMeshNodeFor = typeof settings?.meshNodeFor === "string" ? settings.meshNodeFor.trim() : "";
45058
- const launchAutoLaunchedForQueueTaskId = typeof settings?.autoLaunchedForQueueTaskId === "string" ? settings.autoLaunchedForQueueTaskId.trim() : "";
45059
- if (launchMeshNodeId || launchMeshNodeFor || launchAutoLaunchedForQueueTaskId) {
45091
+ if (Object.keys(launchRecordMeta).length) {
45060
45092
  try {
45061
- cliInstance.getAdapter().updateRuntimeMeta?.({
45062
- ...launchMeshNodeId ? { meshNodeId: launchMeshNodeId } : {},
45063
- ...launchMeshNodeFor ? { meshNodeFor: launchMeshNodeFor } : {},
45064
- ...settings?.launchedByCoordinator === true ? { launchedByCoordinator: true } : {},
45065
- ...launchAutoLaunchedForQueueTaskId ? { autoLaunchedForQueueTaskId: launchAutoLaunchedForQueueTaskId } : {}
45066
- });
45093
+ cliInstance.getAdapter().updateRuntimeMeta?.({ ...launchRecordMeta });
45067
45094
  } catch {
45068
45095
  }
45069
45096
  }
@@ -50533,6 +50560,8 @@ var meshCrudHandlers = {
50533
50560
  const sessionIds = Array.isArray(args?.sessionIds) ? args.sessionIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean) : void 0;
50534
50561
  const source = args?.source === "magi_session_cleanup" ? "magi_session_cleanup" : "mesh_cleanup_sessions";
50535
50562
  const requireAutoLaunchedForTaskIds = args?.requireAutoLaunchedForTaskIds && typeof args.requireAutoLaunchedForTaskIds === "object" && !Array.isArray(args.requireAutoLaunchedForTaskIds) ? args.requireAutoLaunchedForTaskIds : void 0;
50563
+ const reclaimOrphans = args?.reclaimOrphans === true;
50564
+ const liveMeshNodeIds = Array.isArray(mesh?.nodes) ? mesh.nodes.map((n) => normalizeMeshNodeId(n)).filter(Boolean) : [];
50536
50565
  const result = await ctx.cleanupMeshSessions({
50537
50566
  meshId,
50538
50567
  nodeId,
@@ -50541,7 +50570,9 @@ var meshCrudHandlers = {
50541
50570
  sessionIds,
50542
50571
  dryRun: args?.dryRun === true,
50543
50572
  source,
50544
- requireAutoLaunchedForTaskIds
50573
+ requireAutoLaunchedForTaskIds,
50574
+ reclaimOrphans,
50575
+ liveMeshNodeIds
50545
50576
  });
50546
50577
  return result;
50547
50578
  } catch (e) {
@@ -56626,6 +56657,13 @@ var DaemonCommandRouter = class {
56626
56657
  }
56627
56658
  if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
56628
56659
  const requestedSessionIds = Array.isArray(args.sessionIds) ? new Set(args.sessionIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean)) : void 0;
56660
+ const reclaimOrphans = args.reclaimOrphans === true;
56661
+ const liveMeshNodeIds = Array.isArray(args.liveMeshNodeIds) ? args.liveMeshNodeIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean) : [];
56662
+ const isNodeStillLive = (candidateNodeId) => {
56663
+ if (!candidateNodeId) return false;
56664
+ return liveMeshNodeIds.some((liveId) => liveId === candidateNodeId || meshNodeIdMatches({ id: liveId }, candidateNodeId) || daemonIdsEquivalent(liveId, candidateNodeId));
56665
+ };
56666
+ const reclaimedOrphanSessionIds = [];
56629
56667
  const sessions = await this.deps.sessionHostControl.listSessions();
56630
56668
  const matched = sessions.filter((record) => this.sessionMatchesMeshNode(record, args.node, args.nodeId, requestedSessionIds));
56631
56669
  const hasExplicitSessionIds = !!requestedSessionIds?.size;
@@ -56694,7 +56732,8 @@ var DaemonCommandRouter = class {
56694
56732
  const matchedByWorkspaceOnly = !recordNodeId;
56695
56733
  const isWorktreeNodeRemoval = cleanupSource === "mesh_remove_node" && args.node?.isLocalWorktree === true;
56696
56734
  const cleanWorkspaceOnlyForWorktree = isWorktreeNodeRemoval && matchedByWorkspaceOnly;
56697
- if (!hasExplicitSessionIds && liveRuntime && !delegateBoundToThisNode && !cleanWorkspaceOnlyForWorktree) {
56735
+ const reclaimableOrphan = reclaimOrphans && liveRuntime && !delegateBoundToThisNode && (matchedByWorkspaceOnly || !isNodeStillLive(recordNodeId));
56736
+ if (!hasExplicitSessionIds && liveRuntime && !delegateBoundToThisNode && !cleanWorkspaceOnlyForWorktree && !reclaimableOrphan) {
56698
56737
  skippedSessionIds.push(sessionId);
56699
56738
  skippedLiveSessionIds.push(sessionId);
56700
56739
  const reason = recordNodeId && recordNodeId !== args.nodeId ? `live_delegate_bound_to_other_node:${recordNodeId}` : matchedByWorkspaceOnly ? "live_session_matched_by_workspace_only_no_node_binding" : "live_session_not_bound_to_this_node";
@@ -56704,6 +56743,10 @@ var DaemonCommandRouter = class {
56704
56743
  if (cleanWorkspaceOnlyForWorktree && !delegateBoundToThisNode) {
56705
56744
  actedLiveDelegateSessionIds.push(sessionId);
56706
56745
  }
56746
+ if (reclaimableOrphan && !cleanWorkspaceOnlyForWorktree && !delegateBoundToThisNode) {
56747
+ reclaimedOrphanSessionIds.push(sessionId);
56748
+ actedLiveDelegateSessionIds.push(sessionId);
56749
+ }
56707
56750
  if (!hasExplicitSessionIds && liveRuntime && delegateBoundToThisNode && args.mode === "delete_stopped") {
56708
56751
  skippedSessionIds.push(sessionId);
56709
56752
  skippedLiveSessionIds.push(sessionId);
@@ -56776,6 +56819,7 @@ var DaemonCommandRouter = class {
56776
56819
  skippedCoordinatorSessionIds,
56777
56820
  ...skippedMarkerMismatchSessionIds.length ? { skippedMarkerMismatchSessionIds } : {},
56778
56821
  ...actedLiveDelegateSessionIds.length ? { actedLiveDelegateSessionIds } : {},
56822
+ ...reclaimedOrphanSessionIds.length ? { reclaimedOrphanSessionIds } : {},
56779
56823
  ...skippedLiveSessionReasons.length ? { skippedLiveSessionReasons } : {},
56780
56824
  ...deleteUnsupported ? {
56781
56825
  deleteUnsupported: true,