@adhdev/daemon-core 0.9.76-rc.36 → 0.9.76-rc.38

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.mjs CHANGED
@@ -36,7 +36,8 @@ var init_repo_mesh_types = __esm({
36
36
  requireApprovalForPush: true,
37
37
  requireApprovalForDestructiveGit: true,
38
38
  dirtyWorkspaceBehavior: "warn",
39
- maxParallelTasks: 2
39
+ maxParallelTasks: 2,
40
+ sessionCleanupOnNodeRemove: "preserve"
40
41
  };
41
42
  }
42
43
  });
@@ -458,6 +459,18 @@ function normalizeRepoIdentity(remoteUrl) {
458
459
  if (sshMatch) return `${sshMatch[1]}/${sshMatch[2]}`;
459
460
  return identity;
460
461
  }
462
+ function mergeMeshPolicy(base, patch) {
463
+ const policy = { ...DEFAULT_MESH_POLICY, ...base || {}, ...patch || {} };
464
+ if (!["block", "warn", "checkpoint_then_continue"].includes(policy.dirtyWorkspaceBehavior)) {
465
+ policy.dirtyWorkspaceBehavior = "warn";
466
+ }
467
+ const maxParallelTasks = Number(policy.maxParallelTasks);
468
+ policy.maxParallelTasks = Number.isFinite(maxParallelTasks) ? Math.max(1, Math.min(8, Math.floor(maxParallelTasks))) : 2;
469
+ if (!SESSION_CLEANUP_MODES.has(String(policy.sessionCleanupOnNodeRemove))) {
470
+ policy.sessionCleanupOnNodeRemove = "preserve";
471
+ }
472
+ return policy;
473
+ }
461
474
  function listMeshes() {
462
475
  return loadMeshConfig().meshes;
463
476
  }
@@ -481,7 +494,7 @@ function createMesh(opts) {
481
494
  repoIdentity,
482
495
  repoRemoteUrl: opts.repoRemoteUrl,
483
496
  defaultBranch: opts.defaultBranch,
484
- policy: { ...DEFAULT_MESH_POLICY, ...opts.policy },
497
+ policy: mergeMeshPolicy(void 0, opts.policy),
485
498
  coordinator: opts.coordinator || {},
486
499
  nodes: [],
487
500
  createdAt: now,
@@ -497,7 +510,7 @@ function updateMesh(meshId, opts) {
497
510
  if (!mesh) return void 0;
498
511
  if (opts.name !== void 0) mesh.name = opts.name.trim().slice(0, 100);
499
512
  if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
500
- if (opts.policy) mesh.policy = { ...mesh.policy, ...opts.policy };
513
+ if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
501
514
  if (opts.coordinator) mesh.coordinator = opts.coordinator;
502
515
  mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
503
516
  saveMeshConfig(config);
@@ -560,11 +573,13 @@ function updateNode(meshId, nodeId, opts) {
560
573
  saveMeshConfig(config);
561
574
  return node;
562
575
  }
576
+ var SESSION_CLEANUP_MODES;
563
577
  var init_mesh_config = __esm({
564
578
  "src/config/mesh-config.ts"() {
565
579
  "use strict";
566
580
  init_config();
567
581
  init_repo_mesh_types();
582
+ SESSION_CLEANUP_MODES = /* @__PURE__ */ new Set(["preserve", "stop", "delete_stopped", "stop_and_delete"]);
568
583
  }
569
584
  });
570
585
 
@@ -21299,6 +21314,75 @@ var DaemonCommandRouter = class {
21299
21314
  this.inlineMeshCache.set(meshId, mesh);
21300
21315
  return true;
21301
21316
  }
21317
+ normalizeMeshSessionCleanupMode(value) {
21318
+ return value === "stop" || value === "delete_stopped" || value === "stop_and_delete" || value === "preserve" ? value : "preserve";
21319
+ }
21320
+ sessionMatchesMeshNode(record, node, nodeId, sessionIds) {
21321
+ const sessionId = typeof record?.sessionId === "string" ? record.sessionId : "";
21322
+ if (!sessionId) return false;
21323
+ if (sessionIds?.size) return sessionIds.has(sessionId);
21324
+ const workspace = typeof node?.workspace === "string" ? node.workspace : "";
21325
+ if (workspace && record?.workspace === workspace) return true;
21326
+ if (record?.meta?.meshNodeId === nodeId) return true;
21327
+ return false;
21328
+ }
21329
+ isCompletedHostedSession(record) {
21330
+ return record?.lifecycle === "stopped" || record?.lifecycle === "failed" || record?.lifecycle === "interrupted";
21331
+ }
21332
+ async cleanupMeshSessions(args) {
21333
+ if (args.mode === "preserve") {
21334
+ return { success: true, mode: "preserve", matchedCount: 0, stoppedSessionIds: [], deletedSessionIds: [], skippedSessionIds: [] };
21335
+ }
21336
+ if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
21337
+ const requestedSessionIds = Array.isArray(args.sessionIds) ? new Set(args.sessionIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean)) : void 0;
21338
+ const sessions = await this.deps.sessionHostControl.listSessions();
21339
+ const matched = sessions.filter((record) => this.sessionMatchesMeshNode(record, args.node, args.nodeId, requestedSessionIds));
21340
+ const stoppedSessionIds = [];
21341
+ const deletedSessionIds = [];
21342
+ const skippedSessionIds = [];
21343
+ const errors = [];
21344
+ for (const record of matched) {
21345
+ const sessionId = String(record.sessionId);
21346
+ const completed = this.isCompletedHostedSession(record);
21347
+ try {
21348
+ if (args.mode === "stop") {
21349
+ if (!completed) {
21350
+ if (!args.dryRun) await this.deps.sessionHostControl.stopSession(sessionId);
21351
+ stoppedSessionIds.push(sessionId);
21352
+ } else {
21353
+ skippedSessionIds.push(sessionId);
21354
+ }
21355
+ continue;
21356
+ }
21357
+ if (args.mode === "delete_stopped") {
21358
+ if (completed) {
21359
+ if (!args.dryRun) await this.deps.sessionHostControl.deleteSession(sessionId, { force: false });
21360
+ deletedSessionIds.push(sessionId);
21361
+ } else {
21362
+ skippedSessionIds.push(sessionId);
21363
+ }
21364
+ continue;
21365
+ }
21366
+ if (args.mode === "stop_and_delete") {
21367
+ if (!args.dryRun) await this.deps.sessionHostControl.deleteSession(sessionId, { force: true });
21368
+ deletedSessionIds.push(sessionId);
21369
+ continue;
21370
+ }
21371
+ } catch (e) {
21372
+ errors.push({ sessionId, error: e?.message || String(e) });
21373
+ }
21374
+ }
21375
+ return {
21376
+ success: errors.length === 0,
21377
+ mode: args.mode,
21378
+ dryRun: args.dryRun === true,
21379
+ matchedCount: matched.length,
21380
+ stoppedSessionIds,
21381
+ deletedSessionIds,
21382
+ skippedSessionIds,
21383
+ ...errors.length ? { errors } : {}
21384
+ };
21385
+ }
21302
21386
  async traceSessionHostAction(action, args, run, summarizeResult) {
21303
21387
  const interactionId = typeof args?._interactionId === "string" ? args._interactionId : void 0;
21304
21388
  const sessionId = typeof args?.sessionId === "string" ? args.sessionId : void 0;
@@ -21965,7 +22049,26 @@ var DaemonCommandRouter = class {
21965
22049
  if (!name) return { success: false, error: "name required" };
21966
22050
  try {
21967
22051
  const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
21968
- const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch });
22052
+ const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy });
22053
+ return { success: true, mesh };
22054
+ } catch (e) {
22055
+ return { success: false, error: e.message };
22056
+ }
22057
+ }
22058
+ case "update_mesh": {
22059
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
22060
+ if (!meshId) return { success: false, error: "meshId required" };
22061
+ try {
22062
+ const { updateMesh: updateMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
22063
+ const patch = {};
22064
+ if (typeof args?.name === "string") patch.name = args.name;
22065
+ if (typeof args?.defaultBranch === "string") patch.defaultBranch = args.defaultBranch;
22066
+ if (args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy)) patch.policy = args.policy;
22067
+ if (args?.coordinator && typeof args.coordinator === "object" && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
22068
+ if (!Object.keys(patch).length) return { success: false, error: "No updates provided" };
22069
+ const mesh = updateMesh2(meshId, patch);
22070
+ if (!mesh) return { success: false, error: "Mesh not found" };
22071
+ this.inlineMeshCache.set(meshId, mesh);
21969
22072
  return { success: true, mesh };
21970
22073
  } catch (e) {
21971
22074
  return { success: false, error: e.message };
@@ -22002,6 +22105,54 @@ var DaemonCommandRouter = class {
22002
22105
  return { success: false, error: e.message };
22003
22106
  }
22004
22107
  }
22108
+ case "update_mesh_node": {
22109
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
22110
+ const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
22111
+ if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
22112
+ try {
22113
+ const { updateNode: updateNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
22114
+ const policy = args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy) ? { ...args.policy } : {};
22115
+ if (Array.isArray(args?.providerPriority)) {
22116
+ const providerPriority = args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean);
22117
+ delete policy.provider_priority;
22118
+ if (providerPriority.length) {
22119
+ policy.providerPriority = providerPriority;
22120
+ } else {
22121
+ delete policy.providerPriority;
22122
+ }
22123
+ }
22124
+ const node = updateNode2(meshId, nodeId, { policy });
22125
+ if (!node) return { success: false, error: "Mesh node not found" };
22126
+ return { success: true, node };
22127
+ } catch (e) {
22128
+ return { success: false, error: e.message };
22129
+ }
22130
+ }
22131
+ case "cleanup_mesh_sessions": {
22132
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
22133
+ const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
22134
+ if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
22135
+ try {
22136
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
22137
+ const mesh = meshRecord?.mesh;
22138
+ if (!mesh) return { success: false, error: "Mesh not found" };
22139
+ const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
22140
+ if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
22141
+ const mode = this.normalizeMeshSessionCleanupMode(args?.mode ?? mesh?.policy?.sessionCleanupOnNodeRemove);
22142
+ const sessionIds = Array.isArray(args?.sessionIds) ? args.sessionIds.map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean) : void 0;
22143
+ const result = await this.cleanupMeshSessions({
22144
+ meshId,
22145
+ nodeId,
22146
+ node,
22147
+ mode,
22148
+ sessionIds,
22149
+ dryRun: args?.dryRun === true
22150
+ });
22151
+ return result;
22152
+ } catch (e) {
22153
+ return { success: false, error: e.message };
22154
+ }
22155
+ }
22005
22156
  case "remove_mesh_node": {
22006
22157
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
22007
22158
  const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
@@ -22010,6 +22161,14 @@ var DaemonCommandRouter = class {
22010
22161
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
22011
22162
  const mesh = meshRecord?.mesh;
22012
22163
  const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
22164
+ const sessionCleanupMode = this.normalizeMeshSessionCleanupMode(
22165
+ args?.sessionCleanupMode ?? args?.session_cleanup_mode ?? mesh?.policy?.sessionCleanupOnNodeRemove
22166
+ );
22167
+ let sessionCleanup;
22168
+ if (node && sessionCleanupMode !== "preserve") {
22169
+ sessionCleanup = await this.cleanupMeshSessions({ meshId, nodeId, node, mode: sessionCleanupMode });
22170
+ if (sessionCleanup.success === false) return { success: false, removed: false, sessionCleanup };
22171
+ }
22013
22172
  if (node?.isLocalWorktree && node.workspace) {
22014
22173
  try {
22015
22174
  const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
@@ -22029,7 +22188,7 @@ var DaemonCommandRouter = class {
22029
22188
  const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
22030
22189
  removed = removeNode3(meshId, nodeId);
22031
22190
  }
22032
- return { success: true, removed };
22191
+ return { success: true, removed, ...sessionCleanup ? { sessionCleanup } : {} };
22033
22192
  } catch (e) {
22034
22193
  return { success: false, error: e.message };
22035
22194
  }