@adhdev/daemon-standalone 1.0.43 → 1.0.44-rc.2

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
@@ -31423,9 +31423,9 @@ var require_streamx = __commonJS({
31423
31423
  }
31424
31424
  });
31425
31425
 
31426
- // ../daemon-core/node_modules/b4a/index.js
31426
+ // ../../node_modules/tar-stream/node_modules/b4a/index.js
31427
31427
  var require_b4a2 = __commonJS({
31428
- "../daemon-core/node_modules/b4a/index.js"(exports2, module2) {
31428
+ "../../node_modules/tar-stream/node_modules/b4a/index.js"(exports2, module2) {
31429
31429
  "use strict";
31430
31430
  function isBuffer(value) {
31431
31431
  return Buffer.isBuffer(value) || value instanceof Uint8Array;
@@ -31581,9 +31581,9 @@ var require_b4a2 = __commonJS({
31581
31581
  }
31582
31582
  });
31583
31583
 
31584
- // ../daemon-core/node_modules/tar-stream/headers.js
31584
+ // ../../node_modules/tar-stream/headers.js
31585
31585
  var require_headers = __commonJS({
31586
- "../daemon-core/node_modules/tar-stream/headers.js"(exports2) {
31586
+ "../../node_modules/tar-stream/headers.js"(exports2) {
31587
31587
  "use strict";
31588
31588
  var b4a = require_b4a2();
31589
31589
  var ZEROS = "0000000000000000000";
@@ -31844,9 +31844,9 @@ var require_headers = __commonJS({
31844
31844
  }
31845
31845
  });
31846
31846
 
31847
- // ../daemon-core/node_modules/tar-stream/extract.js
31847
+ // ../../node_modules/tar-stream/extract.js
31848
31848
  var require_extract = __commonJS({
31849
- "../daemon-core/node_modules/tar-stream/extract.js"(exports2, module2) {
31849
+ "../../node_modules/tar-stream/extract.js"(exports2, module2) {
31850
31850
  "use strict";
31851
31851
  var { Writable, Readable, getStreamError } = require_streamx();
31852
31852
  var FIFO = require_fast_fifo();
@@ -32189,9 +32189,9 @@ var require_extract = __commonJS({
32189
32189
  }
32190
32190
  });
32191
32191
 
32192
- // ../daemon-core/node_modules/tar-stream/constants.js
32192
+ // ../../node_modules/tar-stream/constants.js
32193
32193
  var require_constants = __commonJS({
32194
- "../daemon-core/node_modules/tar-stream/constants.js"(exports2, module2) {
32194
+ "../../node_modules/tar-stream/constants.js"(exports2, module2) {
32195
32195
  "use strict";
32196
32196
  var constants = {
32197
32197
  // just for envs without fs
@@ -32210,9 +32210,9 @@ var require_constants = __commonJS({
32210
32210
  }
32211
32211
  });
32212
32212
 
32213
- // ../daemon-core/node_modules/tar-stream/pack.js
32213
+ // ../../node_modules/tar-stream/pack.js
32214
32214
  var require_pack = __commonJS({
32215
- "../daemon-core/node_modules/tar-stream/pack.js"(exports2, module2) {
32215
+ "../../node_modules/tar-stream/pack.js"(exports2, module2) {
32216
32216
  "use strict";
32217
32217
  var { Readable, Writable, getStreamError } = require_streamx();
32218
32218
  var b4a = require_b4a2();
@@ -32445,9 +32445,9 @@ var require_pack = __commonJS({
32445
32445
  }
32446
32446
  });
32447
32447
 
32448
- // ../daemon-core/node_modules/tar-stream/index.js
32448
+ // ../../node_modules/tar-stream/index.js
32449
32449
  var require_tar_stream = __commonJS({
32450
- "../daemon-core/node_modules/tar-stream/index.js"(exports2) {
32450
+ "../../node_modules/tar-stream/index.js"(exports2) {
32451
32451
  "use strict";
32452
32452
  exports2.extract = require_extract();
32453
32453
  exports2.pack = require_pack();
@@ -32690,9 +32690,9 @@ var require_pump = __commonJS({
32690
32690
  }
32691
32691
  });
32692
32692
 
32693
- // ../daemon-core/node_modules/tar-fs/index.js
32693
+ // ../../node_modules/tar-fs/index.js
32694
32694
  var require_tar_fs = __commonJS({
32695
- "../daemon-core/node_modules/tar-fs/index.js"(exports2) {
32695
+ "../../node_modules/tar-fs/index.js"(exports2) {
32696
32696
  "use strict";
32697
32697
  var tar = require_tar_stream();
32698
32698
  var pump = require_pump();
@@ -36556,10 +36556,10 @@ var require_dist3 = __commonJS({
36556
36556
  }
36557
36557
  function getDaemonBuildInfo() {
36558
36558
  if (cached2) return cached2;
36559
- const commit = readInjected(true ? "f1fa888e818dbbc75fb7136f3d46d1a44c65b540" : void 0) ?? "unknown";
36560
- const commitShort = readInjected(true ? "f1fa888" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36561
- const version2 = readInjected(true ? "1.0.43" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36562
- const builtAt = readInjected(true ? "2026-08-11T02:54:10.096Z" : void 0);
36559
+ const commit = readInjected(true ? "648305378fc7e0a1970cfe964a80ce7a68172964" : void 0) ?? "unknown";
36560
+ const commitShort = readInjected(true ? "64830537" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36561
+ const version2 = readInjected(true ? "1.0.44-rc.2" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36562
+ const builtAt = readInjected(true ? "2026-08-11T08:23:49.936Z" : void 0);
36563
36563
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36564
36564
  return cached2;
36565
36565
  }
@@ -37817,7 +37817,7 @@ var require_dist3 = __commonJS({
37817
37817
  }
37818
37818
  });
37819
37819
  function resolveBuildTrack(env2 = process.env) {
37820
- const injected = true ? "" : void 0;
37820
+ const injected = true ? "preview" : void 0;
37821
37821
  const stamped = typeof injected === "string" ? injected.trim() : "";
37822
37822
  const raw = (stamped || (env2[BUILD_CHANNEL_ENV_VAR] ?? "").trim()).toLowerCase();
37823
37823
  return raw === "preview" || raw === "next" ? "preview" : "stable";
@@ -38723,7 +38723,7 @@ child.on('exit', () => process.exit(0));
38723
38723
  return typeof value === "boolean" ? value : fallback;
38724
38724
  }
38725
38725
  function resolveQuotaShowAccountEmail(parsed) {
38726
- const fallback = DEFAULT_CONFIG.quotaShowAccountEmail ?? true;
38726
+ const fallback = DEFAULT_CONFIG.quotaShowAccountEmail ?? false;
38727
38727
  if (parsed.quotaShowAccountEmailSetByUser === true) {
38728
38728
  return asBoolean(parsed.quotaShowAccountEmail, fallback);
38729
38729
  }
@@ -38948,7 +38948,7 @@ child.on('exit', () => process.exit(0));
38948
38948
  // preview API instead of silently pinning stable.
38949
38949
  serverUrl: IDENTITY.serverUrl,
38950
38950
  allowServerApiProxy: false,
38951
- quotaShowAccountEmail: true,
38951
+ quotaShowAccountEmail: false,
38952
38952
  selectedIde: null,
38953
38953
  configuredIdes: [],
38954
38954
  installedExtensions: [],
@@ -40271,7 +40271,7 @@ child.on('exit', () => process.exit(0));
40271
40271
  return Array.from(new Set(merged));
40272
40272
  }
40273
40273
  function npmGlobalPrefix() {
40274
- if (cachedNpmPrefix !== void 0) return cachedNpmPrefix ?? void 0;
40274
+ if (cachedNpmPrefix !== void 0) return cachedNpmPrefix;
40275
40275
  try {
40276
40276
  const prefix = (0, import_child_process.execSync)("npm config get prefix", {
40277
40277
  encoding: "utf-8",
@@ -40279,11 +40279,10 @@ child.on('exit', () => process.exit(0));
40279
40279
  windowsHide: true,
40280
40280
  stdio: ["ignore", "pipe", "ignore"]
40281
40281
  }).trim();
40282
- cachedNpmPrefix = prefix && prefix !== "undefined" ? prefix : null;
40282
+ if (prefix && prefix !== "undefined") cachedNpmPrefix = prefix;
40283
40283
  } catch {
40284
- cachedNpmPrefix = null;
40285
40284
  }
40286
- return cachedNpmPrefix ?? void 0;
40285
+ return cachedNpmPrefix;
40287
40286
  }
40288
40287
  function windowsExtraBinDirs() {
40289
40288
  const dirs = [];
@@ -40658,7 +40657,6 @@ child.on('exit', () => process.exit(0));
40658
40657
  MESH_SEND_KEYS_MAX_ITEMS = 64;
40659
40658
  MESH_SEND_KEYS_MAX_TEXT_BYTES = 4096;
40660
40659
  buildCliSpawnEnv = import_session_host_core3.sanitizeSpawnEnv;
40661
- cachedNpmPrefix = void 0;
40662
40660
  }
40663
40661
  });
40664
40662
  function parseVersion(raw) {
@@ -41692,6 +41690,7 @@ ${error48.message || ""}`;
41692
41690
  DEFAULT_DIFFICULTY_BRAINS: () => DEFAULT_DIFFICULTY_BRAINS,
41693
41691
  MAGI_RAW_ANSWER_CAP: () => MAGI_RAW_ANSWER_CAP,
41694
41692
  MESH_TASK_DIFFICULTIES: () => MESH_TASK_DIFFICULTIES,
41693
+ QUOTA_SUPPORTED_PROVIDERS: () => QUOTA_SUPPORTED_PROVIDERS,
41695
41694
  STATUS_PROBE_ARG_KEY: () => STATUS_PROBE_ARG_KEY,
41696
41695
  UNKNOWN_CLI_SLOT_RECIPE: () => UNKNOWN_CLI_SLOT_RECIPE,
41697
41696
  argsCarryStatusProbeMarker: () => argsCarryStatusProbeMarker,
@@ -41733,6 +41732,7 @@ ${error48.message || ""}`;
41733
41732
  sessionIdsEquivalent: () => sessionIdsEquivalent,
41734
41733
  stripStatusProbeMarker: () => stripStatusProbeMarker,
41735
41734
  summarizeGitShape: () => summarizeGitShape,
41735
+ supportsQuota: () => supportsQuota,
41736
41736
  withStatusProbeMarker: () => withStatusProbeMarker
41737
41737
  });
41738
41738
  function readRecord(value) {
@@ -41980,6 +41980,9 @@ ${error48.message || ""}`;
41980
41980
  if (!Number.isFinite(reportedAt) || reportedAt <= 0) return void 0;
41981
41981
  return { ...record2, schemaVersion, reportedAt };
41982
41982
  }
41983
+ function supportsQuota(providerType) {
41984
+ return !!providerType && QUOTA_SUPPORTED_PROVIDERS.includes(providerType);
41985
+ }
41983
41986
  function formatQuotaAccount(quota) {
41984
41987
  const meta3 = quota?.metadata;
41985
41988
  const email3 = typeof meta3?.accountEmail === "string" ? meta3.accountEmail.trim() : "";
@@ -42270,6 +42273,7 @@ ${error48.message || ""}`;
42270
42273
  const { [STATUS_PROBE_ARG_KEY]: _drop, ...rest } = args;
42271
42274
  return rest;
42272
42275
  }
42276
+ var QUOTA_SUPPORTED_PROVIDERS;
42273
42277
  var DAEMON_ID_PREFIXES;
42274
42278
  var MAGI_RAW_ANSWER_CAP;
42275
42279
  var MESH_TASK_DIFFICULTIES;
@@ -42282,6 +42286,12 @@ ${error48.message || ""}`;
42282
42286
  var init_dist = __esm2({
42283
42287
  "../mesh-shared/dist/index.mjs"() {
42284
42288
  "use strict";
42289
+ QUOTA_SUPPORTED_PROVIDERS = [
42290
+ "claude-cli",
42291
+ "codex-cli",
42292
+ "kimi",
42293
+ "opencode"
42294
+ ];
42285
42295
  DAEMON_ID_PREFIXES = ["daemon_", "standalone_"];
42286
42296
  MAGI_RAW_ANSWER_CAP = 4e3;
42287
42297
  MESH_TASK_DIFFICULTIES = ["easy", "medium", "difficult", "freeform"];
@@ -45707,14 +45717,60 @@ Next step: ${nextStep}`;
45707
45717
  const reconciled = terminalJobIds.size === 0 ? events : events.filter((event) => !(event.event === "refine:accepted" && terminalJobIds.has(readRefineJobId2(event))));
45708
45718
  return backfilled.length === 0 ? reconciled : [...reconciled, ...backfilled];
45709
45719
  }
45720
+ function getPendingRetentionCounters() {
45721
+ return { ...pendingRetentionCounters };
45722
+ }
45723
+ function ledgerRecordExpiredUndrainedEvent(row) {
45724
+ try {
45725
+ const restored = row.payload && typeof row.payload === "object" ? row.payload : void 0;
45726
+ const finalSummary = restored?.metadataEvent ? readMeshCompletionSummary(restored.metadataEvent) : void 0;
45727
+ appendLedgerEntry(row.meshId, {
45728
+ kind: "event_held",
45729
+ ...restored?.nodeId ? { nodeId: restored.nodeId } : {},
45730
+ payload: {
45731
+ event: row.event,
45732
+ reason: PENDING_RETENTION_EXPIRED_HOLD_REASON,
45733
+ recoverable: true,
45734
+ nodeLabel: restored?.nodeLabel ?? "",
45735
+ ...restored?.workspace ? { workspace: restored.workspace } : {},
45736
+ targetCoordinatorDaemonId: restored?.targetCoordinatorDaemonId ?? null,
45737
+ ...readNonEmptyString(restored?.eventId) ? { eventId: restored.eventId } : {},
45738
+ queuedAt: restored?.queuedAt ?? null,
45739
+ ...finalSummary ? { finalSummary } : {},
45740
+ // Full original event so mesh_requeue_held_events can restore it
45741
+ // losslessly (event_held→pending), same as every other event_held feeder.
45742
+ ...restored ? { heldEvent: restored } : {}
45743
+ }
45744
+ });
45745
+ } catch (e) {
45746
+ pendingRetentionCounters.undrainedExpiredMirrorFailed++;
45747
+ LOG2.warn("MeshEvents", `Failed to ledger-record retention-expired pending event ${row.event} (row ${row.id}, mesh ${row.meshId}) \u2014 it is being deleted UNRECOVERABLY: ${e?.message || e}`);
45748
+ }
45749
+ }
45710
45750
  function prunePendingMeshCoordinatorEventsRetention() {
45711
45751
  try {
45712
- const removed = MeshRuntimeStore.getInstance().prunePendingEvents({
45752
+ const { drainedExpired, undrainedExpired, undrainedRows } = MeshRuntimeStore.getInstance().prunePendingEvents({
45713
45753
  drainedOlderThanMs: PENDING_EVENTS_DRAINED_RETENTION_MS,
45714
45754
  undrainedOlderThanMs: PENDING_EVENTS_UNDRAINED_RETENTION_MS
45715
45755
  });
45756
+ pendingRetentionCounters.drainedExpired += drainedExpired;
45757
+ pendingRetentionCounters.undrainedExpired += undrainedExpired;
45758
+ if (undrainedRows.length > 0) {
45759
+ for (const row of undrainedRows) {
45760
+ ledgerRecordExpiredUndrainedEvent(row);
45761
+ }
45762
+ const meshIds = [...new Set(undrainedRows.map((r) => r.meshId))];
45763
+ const droppedAt = (/* @__PURE__ */ new Date()).toISOString();
45764
+ LOG2.warn(
45765
+ "MeshEvents",
45766
+ `Pending-event retention DROPPED ${undrainedRows.length} never-delivered event(s) at ${droppedAt} (queued >30d, still undrained) across mesh(es) ${meshIds.join(", ")} \u2014 mirrored to the ledger as event_held (reason: ${PENDING_RETENTION_EXPIRED_HOLD_REASON}); recover with mesh_requeue_held_events.`
45767
+ );
45768
+ }
45769
+ const removed = drainedExpired + undrainedExpired;
45716
45770
  if (removed > 0) {
45717
45771
  LOG2.info("MeshEvents", `Pruned ${removed} stale pending-event row(s) (drained >7d / undrained >30d)`);
45772
+ } else {
45773
+ pendingRetentionCounters.sweepsNoop++;
45718
45774
  }
45719
45775
  return removed;
45720
45776
  } catch (e) {
@@ -46043,6 +46099,8 @@ Next step: ${nextStep}`;
46043
46099
  var TERMINAL_COMPLETION_EVENTS;
46044
46100
  var PENDING_EVENTS_DRAINED_RETENTION_MS;
46045
46101
  var PENDING_EVENTS_UNDRAINED_RETENTION_MS;
46102
+ var PENDING_RETENTION_EXPIRED_HOLD_REASON;
46103
+ var pendingRetentionCounters;
46046
46104
  var init_mesh_events_pending = __esm2({
46047
46105
  "src/mesh/mesh-events-pending.ts"() {
46048
46106
  "use strict";
@@ -46091,6 +46149,22 @@ Next step: ${nextStep}`;
46091
46149
  TERMINAL_COMPLETION_EVENTS = /* @__PURE__ */ new Set(["agent:generating_completed", "agent:stopped"]);
46092
46150
  PENDING_EVENTS_DRAINED_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
46093
46151
  PENDING_EVENTS_UNDRAINED_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
46152
+ PENDING_RETENTION_EXPIRED_HOLD_REASON = "pending_retention_expired";
46153
+ pendingRetentionCounters = {
46154
+ /** Already-drained rows deleted past the 7-day dedup-useful window. Not a drop. */
46155
+ drainedExpired: 0,
46156
+ /** Never-delivered rows deleted past the 30-day undrained window. A genuine
46157
+ * silent-drop risk — mirrored to event_held before deletion (see below). */
46158
+ undrainedExpired: 0,
46159
+ /** undrainedExpired rows that failed to mirror to the ledger (ledger write threw).
46160
+ * Non-zero here means those specific rows are NOT recoverable via
46161
+ * mesh_requeue_held_events — the delete still proceeds (retention must not wedge
46162
+ * on a ledger fault), but this count is the operator's signal of true loss. */
46163
+ undrainedExpiredMirrorFailed: 0,
46164
+ /** How many times the sweep has run and found nothing to prune (0 in both
46165
+ * windows). Purely diagnostic — confirms the sweep is actually firing. */
46166
+ sweepsNoop: 0
46167
+ };
46094
46168
  }
46095
46169
  });
46096
46170
  var mesh_missions_exports = {};
@@ -49828,21 +49902,43 @@ Next step: ${nextStep}`;
49828
49902
  * receives its backlog; only genuinely unrecoverable orphans are swept.
49829
49903
  *
49830
49904
  * Both windows key off `queued_at` (always present) — `drained_at` can be NULL on
49831
- * legacy rows. Returns the number of rows deleted. Best-effort / idempotent:
49832
- * running it repeatedly with nothing to prune is a cheap no-op.
49905
+ * legacy rows. Returns the number of rows deleted, split by which window matched:
49906
+ * `drainedExpired` (already-delivered rows past the dedup-useful window not a
49907
+ * drop, the coordinator already got these) and `undrainedExpired` (rows that were
49908
+ * NEVER delivered — a genuine silent drop, same shape as the retired JSONL trim's
49909
+ * `pending_trim_dropped`). `undrainedRows` carries the id/meshId/event/payload of
49910
+ * every undrained-expired row BEFORE deletion so the caller can mirror it to the
49911
+ * mesh ledger as `event_held` (recoverable via mesh_requeue_held_events) instead of
49912
+ * losing it silently — this is the observability gap the retired trim used to cover
49913
+ * and the SQLite-only cutover left open. Best-effort / idempotent: running it
49914
+ * repeatedly with nothing to prune is a cheap no-op.
49833
49915
  */
49834
49916
  prunePendingEvents(opts) {
49835
49917
  const now = Date.now();
49836
49918
  const drainedCutoff = now - Math.max(0, opts.drainedOlderThanMs);
49837
49919
  const undrainedCutoff = now - Math.max(0, opts.undrainedOlderThanMs);
49838
- let removed = 0;
49839
- removed += this.db.prepare(
49920
+ const undrainedSelectRows = this.db.prepare(
49921
+ "SELECT id, mesh_id, event, payload FROM mesh_pending_events WHERE drained = 0 AND queued_at < ?"
49922
+ ).all(undrainedCutoff);
49923
+ const undrainedRows = undrainedSelectRows.map((r) => ({
49924
+ id: r.id,
49925
+ meshId: r.mesh_id,
49926
+ event: r.event,
49927
+ payload: (() => {
49928
+ try {
49929
+ return JSON.parse(r.payload);
49930
+ } catch {
49931
+ return {};
49932
+ }
49933
+ })()
49934
+ }));
49935
+ const drainedExpired = this.db.prepare(
49840
49936
  "DELETE FROM mesh_pending_events WHERE drained = 1 AND queued_at < ?"
49841
49937
  ).run(drainedCutoff).changes;
49842
- removed += this.db.prepare(
49938
+ const undrainedExpired = this.db.prepare(
49843
49939
  "DELETE FROM mesh_pending_events WHERE drained = 0 AND queued_at < ?"
49844
49940
  ).run(undrainedCutoff).changes;
49845
- return removed;
49941
+ return { drainedExpired, undrainedExpired, undrainedRows };
49846
49942
  }
49847
49943
  // ── TURN-LEDGER (Stage 5): authoritative turn attempts ───────────────────
49848
49944
  /**
@@ -59943,6 +60039,332 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
59943
60039
  PROVIDER_QUOTA_WEEKLY_LOW_SKIP_REASON = "provider_quota_weekly_low";
59944
60040
  }
59945
60041
  });
60042
+ function isAnthropicProvider(providerType) {
60043
+ const p = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
60044
+ return p.length > 0 && ANTHROPIC_PROVIDER_TYPES.has(p);
60045
+ }
60046
+ function isAnthropicModel(model) {
60047
+ const m = typeof model === "string" ? model.trim().toLowerCase() : "";
60048
+ if (!m) return false;
60049
+ if (m.startsWith("claude") || m.startsWith("anthropic")) return true;
60050
+ return /^(opus|sonnet|haiku)(\b|[-_])/.test(m);
60051
+ }
60052
+ function isModelCompatibleWithProvider(model, providerType) {
60053
+ if (!isAnthropicModel(model)) return true;
60054
+ if (isAnthropicProvider(providerType)) return true;
60055
+ return false;
60056
+ }
60057
+ var ANTHROPIC_PROVIDER_TYPES;
60058
+ var init_model_provider_compat = __esm2({
60059
+ "src/mesh/model-provider-compat.ts"() {
60060
+ "use strict";
60061
+ ANTHROPIC_PROVIDER_TYPES = /* @__PURE__ */ new Set([
60062
+ "claude-cli"
60063
+ ]);
60064
+ }
60065
+ });
60066
+ function encodeDuplicateMeshDispatchCode(holderSessionId) {
60067
+ const holder = typeof holderSessionId === "string" ? holderSessionId.trim() : "";
60068
+ return holder ? `${DUPLICATE_MESH_DISPATCH_CODE}:${holder}` : DUPLICATE_MESH_DISPATCH_CODE;
60069
+ }
60070
+ function classifyDuplicateMeshDispatch(err) {
60071
+ if (!err || typeof err !== "object") return null;
60072
+ const e = err;
60073
+ if (e.code === DUPLICATE_MESH_DISPATCH_CODE) {
60074
+ const holder = typeof e.holderSessionId === "string" ? e.holderSessionId.trim() : "";
60075
+ return holder ? { holderSessionId: holder } : {};
60076
+ }
60077
+ for (const raw of [e.meshCode, e.code]) {
60078
+ if (typeof raw !== "string") continue;
60079
+ if (raw !== DUPLICATE_MESH_DISPATCH_CODE && !raw.startsWith(`${DUPLICATE_MESH_DISPATCH_CODE}:`)) continue;
60080
+ const holder = raw.slice(DUPLICATE_MESH_DISPATCH_CODE.length + 1).trim();
60081
+ return holder ? { holderSessionId: holder } : {};
60082
+ }
60083
+ return null;
60084
+ }
60085
+ var DUPLICATE_MESH_DISPATCH_CODE;
60086
+ var DuplicateMeshDispatchError;
60087
+ var init_mesh_duplicate_dispatch = __esm2({
60088
+ "src/mesh/mesh-duplicate-dispatch.ts"() {
60089
+ "use strict";
60090
+ DUPLICATE_MESH_DISPATCH_CODE = "DUPLICATE_MESH_DISPATCH";
60091
+ DuplicateMeshDispatchError = class extends Error {
60092
+ code = DUPLICATE_MESH_DISPATCH_CODE;
60093
+ holderSessionId;
60094
+ constructor(message, info = {}) {
60095
+ super(message);
60096
+ this.name = "DuplicateMeshDispatchError";
60097
+ this.holderSessionId = info.holderSessionId;
60098
+ }
60099
+ };
60100
+ }
60101
+ });
60102
+ function acquireAutoFastForwardLease(workspace) {
60103
+ const key2 = normalizeMeshWorkspaceForCompare(workspace);
60104
+ if (!key2) return false;
60105
+ if (autoFastForwardWorkspaceLease.has(key2)) return false;
60106
+ autoFastForwardWorkspaceLease.add(key2);
60107
+ return true;
60108
+ }
60109
+ function releaseAutoFastForwardLease(workspace) {
60110
+ const key2 = normalizeMeshWorkspaceForCompare(workspace);
60111
+ if (key2) autoFastForwardWorkspaceLease.delete(key2);
60112
+ }
60113
+ function isWorkspaceAutoFastForwardInFlight(workspace) {
60114
+ const key2 = normalizeMeshWorkspaceForCompare(workspace || "");
60115
+ return !!key2 && autoFastForwardWorkspaceLease.has(key2);
60116
+ }
60117
+ function __resetIdleAutoFastForwardForTests() {
60118
+ idleAutoFastForwardLastAttempt.clear();
60119
+ continuousAutoFastForwardLastScan.clear();
60120
+ autoFastForwardWorkspaceLease.clear();
60121
+ }
60122
+ function isDirtyNode(node) {
60123
+ return node?.health === "dirty" || node?.git?.dirty === true;
60124
+ }
60125
+ function resolveAutoFastForwardPolicy(mesh) {
60126
+ const record2 = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
60127
+ const maxBehind = Number(record2.maxBehind);
60128
+ return {
60129
+ enabled: record2.enabled !== false,
60130
+ ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
60131
+ requireCleanSubmodules: record2.requireCleanSubmodules !== false,
60132
+ // Strict opt-in: absent/false → self-only (historical behavior). Only an
60133
+ // explicit `true` extends auto ff to remote owning-daemon nodes.
60134
+ remoteNodes: record2.remoteNodes === true,
60135
+ // Absent/anything-but-continuous → 'idle' (historical idle-edge-only detection).
60136
+ mode: record2.mode === "continuous" ? "continuous" : "idle"
60137
+ };
60138
+ }
60139
+ function dryRunSatisfiesAutoFastForwardPolicy(dryRun, policy) {
60140
+ if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return false;
60141
+ const behind = Number(dryRun.current?.behind);
60142
+ if (!Number.isFinite(behind) || behind <= 0) return false;
60143
+ if (policy.maxBehind !== void 0 && behind > policy.maxBehind) return false;
60144
+ if (policy.requireCleanSubmodules) {
60145
+ const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
60146
+ if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return false;
60147
+ }
60148
+ return true;
60149
+ }
60150
+ function readNodeSubmoduleIgnorePaths(node) {
60151
+ return Array.isArray(node?.policy?.submoduleIgnorePaths) ? node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
60152
+ }
60153
+ function nodeIsAutoFastForwardEligible(components, meshId, nodeId, node, currentSessionId) {
60154
+ if (!node) return false;
60155
+ if (node.status === "disabled" || node.status === "removed") return false;
60156
+ if (node.readOnly === true || node.policy?.readOnly === true) return false;
60157
+ if (isWorktreeBootstrapStaleRunning(node)) return false;
60158
+ if (node.worktreeBootstrap?.status === "running") return false;
60159
+ if (nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId)) return false;
60160
+ return true;
60161
+ }
60162
+ function remoteNodeIsConnected(components, node) {
60163
+ const daemonId = readMeshNodeDaemonId(node ?? {});
60164
+ if (!daemonId) return false;
60165
+ const getPeerStatus = components.getMeshPeerConnectionStatus;
60166
+ if (getPeerStatus) {
60167
+ const snapshot = getPeerStatus(daemonId);
60168
+ return !!snapshot && String(snapshot.state) === "connected";
60169
+ }
60170
+ return readNonEmptyString(node?.connection?.state).toLowerCase() === "connected";
60171
+ }
60172
+ async function delegateRemoteAutoFastForward(components, args) {
60173
+ const dispatchMeshCommand = components.dispatchMeshCommand;
60174
+ if (!dispatchMeshCommand) return;
60175
+ if (!acquireAutoFastForwardLease(args.workspace)) return;
60176
+ const submoduleIgnorePaths = readNodeSubmoduleIgnorePaths(args.node);
60177
+ const mesh = getMeshWithCache(components, args.meshId);
60178
+ const baseArgs = {
60179
+ meshId: args.meshId,
60180
+ nodeId: args.nodeId,
60181
+ workspace: args.workspace,
60182
+ inlineMesh: mesh,
60183
+ ...submoduleIgnorePaths ? { submoduleIgnorePaths } : {},
60184
+ trigger: args.trigger,
60185
+ // Preserve the pre-fix self-only semantics for submodules: the local idle path
60186
+ // never updated submodules (updateSubmodules:false), so the remote path matches.
60187
+ updateSubmodules: false
60188
+ };
60189
+ try {
60190
+ const remoteDry = await dispatchMeshCommand(args.daemonId, "fast_forward_mesh_node", {
60191
+ ...baseArgs,
60192
+ execute: false,
60193
+ dryRun: true
60194
+ });
60195
+ if (!dryRunSatisfiesAutoFastForwardPolicy(remoteDry, args.policy)) return;
60196
+ if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId)) return;
60197
+ const executed = await dispatchMeshCommand(args.daemonId, "fast_forward_mesh_node", {
60198
+ ...baseArgs,
60199
+ execute: true,
60200
+ dryRun: false
60201
+ });
60202
+ if (executed?.executed === true) {
60203
+ LOG2.info("MeshFastForward", `Remote auto fast-forward executed for node ${args.nodeId} (daemon ${String(args.daemonId).slice(0, 12)}, trigger ${args.trigger})`);
60204
+ }
60205
+ } catch (e) {
60206
+ LOG2.warn("MeshFastForward", `Remote auto fast-forward delegation failed for ${args.nodeId}: ${e?.message || e}`);
60207
+ } finally {
60208
+ releaseAutoFastForwardLease(args.workspace);
60209
+ }
60210
+ }
60211
+ async function executeLocalAutoFastForward(args) {
60212
+ if (!acquireAutoFastForwardLease(args.workspace)) return;
60213
+ const submoduleIgnorePaths = readNodeSubmoduleIgnorePaths(args.node);
60214
+ try {
60215
+ const dryRun = await fastForwardMeshNode({
60216
+ meshId: args.meshId,
60217
+ nodeId: args.nodeId,
60218
+ workspace: args.workspace,
60219
+ execute: false,
60220
+ dryRun: true,
60221
+ updateSubmodules: false,
60222
+ submoduleIgnorePaths,
60223
+ trigger: args.trigger
60224
+ });
60225
+ if (!dryRunSatisfiesAutoFastForwardPolicy(dryRun, args.policy)) return;
60226
+ await fastForwardMeshNode({
60227
+ meshId: args.meshId,
60228
+ nodeId: args.nodeId,
60229
+ workspace: args.workspace,
60230
+ execute: true,
60231
+ dryRun: false,
60232
+ updateSubmodules: false,
60233
+ submoduleIgnorePaths,
60234
+ trigger: args.trigger
60235
+ });
60236
+ } catch (e) {
60237
+ LOG2.warn("MeshFastForward", `Idle auto fast-forward check failed for ${args.nodeId}: ${e?.message || e}`);
60238
+ } finally {
60239
+ releaseAutoFastForwardLease(args.workspace);
60240
+ }
60241
+ }
60242
+ async function maybeAutoFastForwardIdleNode(components, args) {
60243
+ const mesh = getMeshWithCache(components, args.meshId);
60244
+ const node = mesh?.nodes?.find((candidate) => meshNodeIdMatches(candidate, args.nodeId));
60245
+ const workspace = readNonEmptyString(node?.workspace);
60246
+ if (!workspace) return;
60247
+ const policy = resolveAutoFastForwardPolicy(mesh);
60248
+ if (!policy.enabled) return;
60249
+ if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
60250
+ const throttleKey = `${args.meshId}:${args.nodeId}`;
60251
+ const now = Date.now();
60252
+ const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
60253
+ if (now - lastAttempt < IDLE_AUTO_FAST_FORWARD_THROTTLE_MS) return;
60254
+ idleAutoFastForwardLastAttempt.set(throttleKey, now);
60255
+ if (isLocalAutoLaunchNode(node)) {
60256
+ if (!(0, import_fs14.existsSync)(workspace)) return;
60257
+ await executeLocalAutoFastForward({ meshId: args.meshId, nodeId: args.nodeId, node, workspace, policy, trigger: "idle_auto" });
60258
+ return;
60259
+ }
60260
+ if (!policy.remoteNodes) return;
60261
+ const daemonId = readMeshNodeDaemonId(node ?? {});
60262
+ if (!daemonId || !components.dispatchMeshCommand) return;
60263
+ if (!remoteNodeIsConnected(components, node)) return;
60264
+ await delegateRemoteAutoFastForward(components, { meshId: args.meshId, nodeId: args.nodeId, node, daemonId, workspace, policy, trigger: "idle_auto" });
60265
+ }
60266
+ async function runContinuousAutoFastForwardScan(components, mesh) {
60267
+ if (!components.dispatchMeshCommand) return;
60268
+ const policy = resolveAutoFastForwardPolicy(mesh);
60269
+ if (!policy.enabled || !policy.remoteNodes || policy.mode !== "continuous") return;
60270
+ const meshId = readNonEmptyString(mesh?.id);
60271
+ if (!meshId) return;
60272
+ const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
60273
+ const now = Date.now();
60274
+ for (const node of nodes) {
60275
+ const nodeId = normalizeMeshNodeId(node);
60276
+ if (!nodeId) continue;
60277
+ if (node?.isLocalWorktree === true) continue;
60278
+ if (isLocalAutoLaunchNode(node)) continue;
60279
+ const workspace = readNonEmptyString(node?.workspace);
60280
+ if (!workspace) continue;
60281
+ const daemonId = readMeshNodeDaemonId(node ?? {});
60282
+ if (!daemonId) continue;
60283
+ if (!nodeIsAutoFastForwardEligible(components, meshId, nodeId, node)) continue;
60284
+ if (!remoteNodeIsConnected(components, node)) continue;
60285
+ const cooldownKey = `${meshId}:${nodeId}`;
60286
+ const lastScan = continuousAutoFastForwardLastScan.get(cooldownKey) || 0;
60287
+ if (now - lastScan < CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS) continue;
60288
+ continuousAutoFastForwardLastScan.set(cooldownKey, now);
60289
+ await delegateRemoteAutoFastForward(components, { meshId, nodeId, node, daemonId, workspace, policy, trigger: "reconcile_auto" });
60290
+ }
60291
+ }
60292
+ async function runPendingCoordinatorCatchupScan(components, mesh) {
60293
+ const meshId = readNonEmptyString(mesh?.id);
60294
+ if (!meshId) return;
60295
+ const localIds = expandDaemonIdForms([
60296
+ readNonEmptyString(components.statusInstanceId),
60297
+ readNonEmptyString(loadConfig2().machineId)
60298
+ ]);
60299
+ let markers = [];
60300
+ try {
60301
+ markers = drainPendingMeshCoordinatorEvents(
60302
+ meshId,
60303
+ localIds.length > 0 ? localIds : void 0,
60304
+ { onlyEvents: /* @__PURE__ */ new Set(["coordinator_catchup"]) }
60305
+ );
60306
+ } catch (e) {
60307
+ LOG2.warn("MeshReconcile", `Coordinator-catchup drain failed for mesh ${meshId}: ${e?.message || e}`);
60308
+ return;
60309
+ }
60310
+ if (markers.length === 0) return;
60311
+ const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
60312
+ for (const marker of markers) {
60313
+ const meta3 = marker.metadataEvent || {};
60314
+ const nodeId = readNonEmptyString(marker.nodeId) || readNonEmptyString(meta3.nodeId);
60315
+ const workspace = readNonEmptyString(marker.workspace) || readNonEmptyString(meta3.workspace);
60316
+ const baseBranch = readNonEmptyString(meta3.baseBranch);
60317
+ if (!workspace) continue;
60318
+ if (nodeId && nodeHasActiveMeshWork(components, meshId, nodeId)) {
60319
+ try {
60320
+ queuePendingMeshCoordinatorEvent(marker);
60321
+ } catch {
60322
+ }
60323
+ continue;
60324
+ }
60325
+ try {
60326
+ const ff = await fastForwardMeshNode({
60327
+ meshId,
60328
+ ...nodeId ? { nodeId } : {},
60329
+ workspace,
60330
+ ...baseBranch ? { branch: baseBranch } : {},
60331
+ mode: "merge",
60332
+ execute: true,
60333
+ trigger: "refine_post_push_catchup",
60334
+ allowAutoPublishSubmoduleMainCommits: mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true
60335
+ });
60336
+ LOG2.info("MeshReconcile", `Coordinator catch-up ff for ${meshId}/${nodeId || workspace}: ${ff.code} (executed=${ff.executed})`);
60337
+ } catch (e) {
60338
+ LOG2.warn("MeshReconcile", `Coordinator catch-up ff failed for ${meshId}/${nodeId || workspace}: ${e?.message || e}`);
60339
+ }
60340
+ }
60341
+ }
60342
+ var import_fs14;
60343
+ var IDLE_AUTO_FAST_FORWARD_THROTTLE_MS;
60344
+ var idleAutoFastForwardLastAttempt;
60345
+ var CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS;
60346
+ var continuousAutoFastForwardLastScan;
60347
+ var autoFastForwardWorkspaceLease;
60348
+ var init_mesh_auto_fast_forward = __esm2({
60349
+ "src/mesh/mesh-auto-fast-forward.ts"() {
60350
+ "use strict";
60351
+ import_fs14 = require("fs");
60352
+ init_logger();
60353
+ init_config();
60354
+ init_mesh_fast_forward();
60355
+ init_dist();
60356
+ init_mesh_events_utils();
60357
+ init_mesh_node_identity();
60358
+ init_mesh_events_pending();
60359
+ init_worktree_bootstrap_config();
60360
+ init_mesh_queue_assignment();
60361
+ IDLE_AUTO_FAST_FORWARD_THROTTLE_MS = 30 * 60 * 1e3;
60362
+ idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
60363
+ CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS = 45 * 1e3;
60364
+ continuousAutoFastForwardLastScan = /* @__PURE__ */ new Map();
60365
+ autoFastForwardWorkspaceLease = /* @__PURE__ */ new Set();
60366
+ }
60367
+ });
59946
60368
  function normalizeNodeIdKey(nodeId) {
59947
60369
  return normalizeMeshNodeId({ id: nodeId ?? void 0 }) ?? "";
59948
60370
  }
@@ -59978,88 +60400,407 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
59978
60400
  MAX_TRACKED_CLONED_NODES = 512;
59979
60401
  }
59980
60402
  });
59981
- function isAnthropicProvider(providerType) {
59982
- const p = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
59983
- return p.length > 0 && ANTHROPIC_PROVIDER_TYPES.has(p);
60403
+ function isActionableSkipReason(reason) {
60404
+ if (!reason) return false;
60405
+ return ACTIONABLE_SKIP_REASON_PREFIXES.some((prefix) => reason === prefix || reason.startsWith(prefix));
59984
60406
  }
59985
- function isAnthropicModel(model) {
59986
- const m = typeof model === "string" ? model.trim().toLowerCase() : "";
59987
- if (!m) return false;
59988
- if (m.startsWith("claude") || m.startsWith("anthropic")) return true;
59989
- return /^(opus|sonnet|haiku)(\b|[-_])/.test(m);
60407
+ function isTargetNodeTransientlyUnresolved(mesh, task) {
60408
+ const targetNodeId = readNonEmptyString(task.targetNodeId);
60409
+ if (!targetNodeId) return false;
60410
+ const node = Array.isArray(mesh?.nodes) ? mesh.nodes.find((n) => meshNodeIdMatches(n, targetNodeId)) : void 0;
60411
+ if (node && node.worktreeBootstrap?.status === "running" && !isWorktreeBootstrapStaleRunning(node)) {
60412
+ return true;
60413
+ }
60414
+ return isWithinCloneBootstrapGrace(targetNodeId);
59990
60415
  }
59991
- function isModelCompatibleWithProvider(model, providerType) {
59992
- if (!isAnthropicModel(model)) return true;
59993
- if (isAnthropicProvider(providerType)) return true;
59994
- return false;
60416
+ function targetPinAgeMs(task, nowMs = Date.now()) {
60417
+ const anchorMs = Date.parse(task.requeuedAt || task.createdAt || "");
60418
+ return Number.isFinite(anchorMs) ? nowMs - anchorMs : null;
59995
60419
  }
59996
- var ANTHROPIC_PROVIDER_TYPES;
59997
- var init_model_provider_compat = __esm2({
59998
- "src/mesh/model-provider-compat.ts"() {
60420
+ function resolveDeadTargetVerdict(components, meshId, mesh, task) {
60421
+ const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
60422
+ const targetSessionId = readNonEmptyString(task.targetSessionId);
60423
+ const targetNodeId = readNonEmptyString(task.targetNodeId);
60424
+ if (!targetSessionId && !targetNodeId) return NOT_DEAD;
60425
+ const lastUpdateMs = Date.parse(task.updatedAt || task.createdAt || "");
60426
+ if (Number.isFinite(lastUpdateMs) && Date.now() - lastUpdateMs < DEAD_TARGET_GRACE_MS) return NOT_DEAD;
60427
+ const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
60428
+ if (targetNodeId) {
60429
+ const nodePresent = nodes.some((n) => meshNodeIdMatches(n, targetNodeId));
60430
+ if (!nodePresent) {
60431
+ if (isTargetNodeTransientlyUnresolved(mesh, task)) return NOT_DEAD;
60432
+ return { dead: true, nodeDead: true, reason: "dead_target_node_absent" };
60433
+ }
60434
+ }
60435
+ if (targetSessionId) {
60436
+ const node = targetNodeId ? nodes.find((n) => meshNodeIdMatches(n, targetNodeId)) : void 0;
60437
+ const nodeIsLocal = node ? isLocalAutoLaunchNode(node) : true;
60438
+ if (nodeIsLocal) {
60439
+ const verdict = resolveSessionBusyVerdict(components, targetSessionId);
60440
+ if (verdict === "UNKNOWN") {
60441
+ return { dead: true, nodeDead: false, reason: "dead_target_session_absent" };
60442
+ }
60443
+ }
60444
+ }
60445
+ return NOT_DEAD;
60446
+ }
60447
+ function retractActionableSkipIfPreviouslyNotified(meshId, taskId) {
60448
+ const dedupKey = `${meshId}:${taskId}`;
60449
+ if (!lastActionableSkipNotified.delete(dedupKey)) return;
60450
+ try {
60451
+ const coordinatorDaemonId = readNonEmptyString(loadConfig2().machineId) || void 0;
60452
+ const removed = retractPendingDispatchBlockedEvent(meshId, taskId, coordinatorDaemonId);
60453
+ if (removed > 0) {
60454
+ LOG2.info("MeshQueue", `Retracted ${removed} stale dispatch-blocked event(s) for task ${taskId} (mesh ${meshId}) \u2014 its blocker resolved`);
60455
+ }
60456
+ } catch (e) {
60457
+ LOG2.warn("MeshQueue", `Failed to retract stale dispatch-blocked event for task ${taskId} (mesh ${meshId}): ${e?.message || e}`);
60458
+ }
60459
+ }
60460
+ function resolveTaskDeliveryEvidence(meshId, taskId) {
60461
+ try {
60462
+ const store = MeshRuntimeStore.getInstance();
60463
+ if (store.taskDeliveryConsumed(meshId, taskId)) return "consumed";
60464
+ if (store.taskHasConfirmedDelivery(meshId, taskId)) return "delivered";
60465
+ } catch {
60466
+ return "delivered";
60467
+ }
60468
+ return "never_dispatched";
60469
+ }
60470
+ function actionableSkipGuidance(reason, evidence) {
60471
+ if (reason === "target_node_id_unmatched") return {
60472
+ summary: "it is pinned to a target node id that matches no node in the mesh (the node may have been removed, or its id form does not resolve)",
60473
+ nextAction: "Verify the target node still exists with mesh_status, then re-enqueue without the node pin or with a valid node id (or re-clone the node)."
60474
+ };
60475
+ if (reason === "no_node_satisfies_required_tags") return {
60476
+ summary: "no node in the mesh can satisfy the task's required capability tags",
60477
+ nextAction: "Relax the task's requiredTags, or add/launch a node whose provider produces the required capabilities."
60478
+ };
60479
+ if (reason === "mesh_convergence_target_is_worktree") return {
60480
+ summary: "it is a convergence task (base-only: merge \u2192 push \u2192 cleanup) but every candidate node is a worktree clone",
60481
+ nextAction: "Dispatch the convergence task to the base node, or run the deterministic fast-forward path (mesh_fast_forward_node / mesh_refine_node) instead."
60482
+ };
60483
+ if (reason.startsWith("remote_auto_launch")) return {
60484
+ summary: "the target node is on a remote daemon this coordinator cannot auto-launch a session on (no dispatch transport, or no coordinator daemon id to stamp)",
60485
+ nextAction: "Launch a session on that node yourself with mesh_launch_session, or ensure the remote daemon is connected over P2P."
60486
+ };
60487
+ if (reason.startsWith("provider") || reason === "missing_provider_priority") return {
60488
+ summary: "the node has no usable provider for this task (provider priority missing/unusable, or the provider loader is unavailable)",
60489
+ nextAction: "Check the node's providerPriority policy and that the required CLI/ACP provider is installed and enabled on that machine."
60490
+ };
60491
+ if (reason === "dirty_workspace") return {
60492
+ summary: "the node's workspace is dirty, so auto-launch is blocked to avoid clobbering uncommitted changes",
60493
+ nextAction: "Clean or commit the node's working tree (or fast-forward it); the task will then auto-assign."
60494
+ };
60495
+ if (reason === "target_session_pin_expired") {
60496
+ if (evidence === "consumed") return {
60497
+ summary: "it was pinned to a specific session and the pin TTL expired before the queue row was claimed \u2014 but the delivery record shows this session DID receive and start acting on the message (a turn was started for it), so the queue row lagging is a bookkeeping gap, not a lost delta",
60498
+ nextAction: "Do NOT re-send it \u2014 the session already has this message and re-sending would run the same instruction twice. Check its current output (mesh_read_chat / mesh_read_terminal) to confirm the work is under way."
60499
+ };
60500
+ if (evidence === "delivered") return {
60501
+ summary: "it was pinned to a specific session and the pin TTL expired before the queue row was claimed; the message WAS handed to that session's transport, but the session never echoed a turn start, so whether it acted on it is unconfirmed",
60502
+ nextAction: "Verify before re-sending: check the session with mesh_read_chat / mesh_read_terminal. Re-send only if its output shows no sign of this message \u2014 it may already be acting on it, and re-sending would duplicate the instruction."
60503
+ };
60504
+ return {
60505
+ summary: "it was pinned to a specific session (a follow-up/delta for work already in flight) that never claimed it within the pin TTL, so the pin was cleared; no delivery to that session was ever recorded, so the message did not reach it",
60506
+ nextAction: "The addressed session never received this delta and is still acting on its previous instructions. Re-send it to that session once it is idle (or re-target it), and re-check the work it produced in the meantime."
60507
+ };
60508
+ }
60509
+ if (reason === SLOT_MODEL_ABSENT_SKIP_REASON) return {
60510
+ summary: "no capability slot on the node declares the model this task resolved to (its difficulty\u2192brain preset picked a model the node was never configured to run)",
60511
+ nextAction: "Re-enqueue with a difficulty/model the node's slots declare, target a node that declares this model, or add a slot for it. The task is NOT run on a substitute model \u2014 an undeclared model is never launched."
60512
+ };
60513
+ return {
60514
+ summary: `it cannot be dispatched (${reason})`,
60515
+ nextAction: "Inspect the node/mesh state with mesh_status and resolve the blocker, or re-enqueue the task."
60516
+ };
60517
+ }
60518
+ function notifyCoordinatorOfActionableSkip(meshId, taskId, reason, nodeId) {
60519
+ if (!isActionableSkipReason(reason)) return;
60520
+ if (reason === "target_node_id_unmatched" && isWithinCloneBootstrapGrace(readNonEmptyString(nodeId))) return;
60521
+ const dedupKey = `${meshId}:${taskId}`;
60522
+ if (lastActionableSkipNotified.get(dedupKey) === reason) return;
60523
+ lastActionableSkipNotified.set(dedupKey, reason);
60524
+ if (lastActionableSkipNotified.size > AUTO_LAUNCH_LEDGER_DEDUP_MAX) {
60525
+ const oldest = lastActionableSkipNotified.keys().next().value;
60526
+ if (oldest !== void 0) lastActionableSkipNotified.delete(oldest);
60527
+ }
60528
+ let task;
60529
+ try {
60530
+ task = getQueue(meshId).find((t) => t.id === taskId);
60531
+ } catch {
60532
+ }
60533
+ const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
60534
+ const targetCoordinatorSessionId = readNonEmptyString(task?.sourceCoordinatorSessionId);
60535
+ const nodeLabel = readNonEmptyString(nodeId) || readNonEmptyString(task?.targetNodeId);
60536
+ const evidence = reason === "target_session_pin_expired" ? resolveTaskDeliveryEvidence(meshId, taskId) : void 0;
60537
+ const { summary, nextAction } = actionableSkipGuidance(reason, evidence);
60538
+ const closing = reason === "target_session_pin_expired" ? "The stale pin has already been cleared, so the task is now claimable by any compatible session \u2014 the action above is about the session it was originally addressed to." : "This is an actionable blocker \u2014 it will NOT clear on its own; the task stays pending until you resolve it.";
60539
+ const coordinatorMessage = `[System] A queued mesh task${nodeLabel ? ` for node ${nodeLabel}` : ""} is not being dispatched because ${summary}. ${nextAction} ${closing}`;
60540
+ try {
60541
+ queuePendingMeshCoordinatorEvent({
60542
+ event: "mesh:dispatch_blocked",
60543
+ meshId,
60544
+ nodeLabel: nodeLabel || meshId,
60545
+ ...nodeLabel ? { nodeId: nodeLabel } : {},
60546
+ metadataEvent: {
60547
+ source: "mesh_queue_dispatch_skip",
60548
+ taskId,
60549
+ reason,
60550
+ ...nodeLabel ? { nodeId: nodeLabel } : {},
60551
+ coordinatorMessage
60552
+ },
60553
+ coordinatorMessage,
60554
+ queuedAt: Date.now(),
60555
+ ...targetCoordinatorDaemonId ? { targetCoordinatorDaemonId } : {},
60556
+ ...targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}
60557
+ });
60558
+ } catch (e) {
60559
+ LOG2.warn("MeshQueue", `Failed to surface actionable dispatch-skip (${reason}) for task ${taskId}: ${e?.message || e}`);
60560
+ }
60561
+ }
60562
+ var ACTIONABLE_SKIP_REASON_PREFIXES;
60563
+ var TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON;
60564
+ var lastActionableSkipNotified;
60565
+ var DEAD_TARGET_GRACE_MS;
60566
+ var TARGET_SESSION_PIN_TTL_MS;
60567
+ var init_mesh_skip_notify = __esm2({
60568
+ "src/mesh/mesh-skip-notify.ts"() {
59999
60569
  "use strict";
60000
- ANTHROPIC_PROVIDER_TYPES = /* @__PURE__ */ new Set([
60001
- "claude-cli"
60002
- ]);
60570
+ init_logger();
60571
+ init_mesh_work_queue();
60572
+ init_mesh_runtime_store();
60573
+ init_dist();
60574
+ init_mesh_events_utils();
60575
+ init_mesh_events_pending();
60576
+ init_worktree_bootstrap_config();
60577
+ init_mesh_clone_grace();
60578
+ init_config();
60579
+ init_slot_model_enforcement();
60580
+ init_mesh_queue_assignment();
60581
+ ACTIONABLE_SKIP_REASON_PREFIXES = [
60582
+ "target_node_id_unmatched",
60583
+ "no_node_satisfies_required_tags",
60584
+ "mesh_convergence_target_is_worktree",
60585
+ "remote_auto_launch_unsupported",
60586
+ "remote_auto_launch_no_coordinator_daemon_id",
60587
+ "missing_provider_priority",
60588
+ "provider_loader_unavailable",
60589
+ "provider_priority_unusable",
60590
+ "provider_unusable",
60591
+ "dirty_workspace",
60592
+ // TARGET-PIN-TTL EXPIRY: the task was pinned to a specific session (a delta —
60593
+ // a correction addressed to work already in flight) and that session never
60594
+ // claimed it within the TTL, so the pin was cleared. Unlike the transient
60595
+ // skips, this does NOT self-resolve into the intended outcome: the delta is
60596
+ // now claimable by ANY compatible session, i.e. it will not reach the session
60597
+ // it was written for. Previously this only bumped a metrics counter and wrote
60598
+ // a log, so the coordinator kept waiting for a delivery that could no longer
60599
+ // happen — measured live as 74min of silence while a worker continued on a
60600
+ // premise the delta was meant to correct. The coordinator must know its
60601
+ // addressed message lost its address.
60602
+ "target_session_pin_expired",
60603
+ // SLOT MODEL GUARD (absent): no slot on the node declares the task's model.
60604
+ // Permanent — no amount of waiting produces a slot, so the coordinator must
60605
+ // re-drive (adjust difficulty, target another node, ask the owner). Its
60606
+ // busy counterpart SLOT_MODEL_BUSY_SKIP_REASON is deliberately NOT listed:
60607
+ // that one clears on its own when the slot goes idle.
60608
+ SLOT_MODEL_ABSENT_SKIP_REASON
60609
+ // QUOTA GATE: 'provider_quota_session_low' / 'provider_quota_weekly_low' are
60610
+ // deliberately NOT listed either — an exhausted quota window RESETS, so the
60611
+ // block self-resolves exactly like the slot-busy case; the task waits in the
60612
+ // queue and the coordinator is not paged (mesh-quota-routing.ts).
60613
+ ];
60614
+ TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON = "target_node_bootstrap_pending";
60615
+ lastActionableSkipNotified = /* @__PURE__ */ new Map();
60616
+ DEAD_TARGET_GRACE_MS = 6e4;
60617
+ TARGET_SESSION_PIN_TTL_MS = 15 * 6e4;
60003
60618
  }
60004
60619
  });
60005
- function encodeDuplicateMeshDispatchCode(holderSessionId) {
60006
- const holder = typeof holderSessionId === "string" ? holderSessionId.trim() : "";
60007
- return holder ? `${DUPLICATE_MESH_DISPATCH_CODE}:${holder}` : DUPLICATE_MESH_DISPATCH_CODE;
60620
+ function activeWriteAssignedCount(meshId) {
60621
+ return getQueue(meshId, { status: ["assigned"] }).filter((task) => !isTaskReadonly(task)).length;
60008
60622
  }
60009
- function classifyDuplicateMeshDispatch(err) {
60010
- if (!err || typeof err !== "object") return null;
60011
- const e = err;
60012
- if (e.code === DUPLICATE_MESH_DISPATCH_CODE) {
60013
- const holder = typeof e.holderSessionId === "string" ? e.holderSessionId.trim() : "";
60014
- return holder ? { holderSessionId: holder } : {};
60623
+ function activeReadonlyAssignedCount(meshId) {
60624
+ return getQueue(meshId, { status: ["assigned"] }).filter(isTaskReadonly).length;
60625
+ }
60626
+ function nodeHasActiveAssignment(meshId, nodeId) {
60627
+ return getQueue(meshId, { status: ["assigned"] }).some((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId));
60628
+ }
60629
+ function nodeActiveLoad(meshId, nodeId) {
60630
+ return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
60631
+ }
60632
+ function meshHasExplicitSlots(mesh) {
60633
+ const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
60634
+ return nodes.some((n) => normalizeNodeCapabilitySlots(n?.policy?.slots).length > 0);
60635
+ }
60636
+ function resolveSchedulingStrategy(mesh) {
60637
+ const raw = mesh?.policy?.schedulingStrategy;
60638
+ if (typeof raw === "string" && raw.trim()) {
60639
+ return normalizeMeshSchedulingStrategy(raw);
60015
60640
  }
60016
- for (const raw of [e.meshCode, e.code]) {
60017
- if (typeof raw !== "string") continue;
60018
- if (raw !== DUPLICATE_MESH_DISPATCH_CODE && !raw.startsWith(`${DUPLICATE_MESH_DISPATCH_CODE}:`)) continue;
60019
- const holder = raw.slice(DUPLICATE_MESH_DISPATCH_CODE.length + 1).trim();
60020
- return holder ? { holderSessionId: holder } : {};
60641
+ return meshHasExplicitSlots(mesh) ? "fitness" : normalizeMeshSchedulingStrategy(void 0);
60642
+ }
60643
+ function buildSchedulingPool(localCandidates, remoteCandidates) {
60644
+ const pool = [...localCandidates, ...remoteCandidates].map((c) => ({
60645
+ ...c,
60646
+ nodeId: normalizeMeshNodeId(c.node) ?? c.nodeId
60647
+ }));
60648
+ const uniqueNodes = [...new Set(pool.map((c) => c.nodeId))].map((nodeId, index) => ({
60649
+ nodeId,
60650
+ node: pool.find((c) => meshNodeIdMatches({ id: c.nodeId }, nodeId))?.node,
60651
+ index
60652
+ }));
60653
+ return { pool, uniqueNodes };
60654
+ }
60655
+ function scoreSlotForTask(slot, task, quotaBonus = 0) {
60656
+ let score = 1;
60657
+ const diff = isMeshTaskDifficulty(task.difficulty) ? task.difficulty : void 0;
60658
+ if (diff) {
60659
+ if (slot.difficulty?.length) {
60660
+ score += slot.difficulty.includes(diff) ? 100 : 0;
60661
+ } else {
60662
+ score += 20;
60663
+ }
60021
60664
  }
60022
- return null;
60665
+ const req = task.requiredTags?.filter((t) => !!t) ?? [];
60666
+ if (req.length) {
60667
+ const cap = new Set(slot.capability ?? []);
60668
+ const covered = req.every((t) => cap.has(t));
60669
+ score += covered ? 30 : 0;
60670
+ }
60671
+ score += quotaBonus;
60672
+ return score;
60023
60673
  }
60024
- var DUPLICATE_MESH_DISPATCH_CODE;
60025
- var DuplicateMeshDispatchError;
60026
- var init_mesh_duplicate_dispatch = __esm2({
60027
- "src/mesh/mesh-duplicate-dispatch.ts"() {
60028
- "use strict";
60029
- DUPLICATE_MESH_DISPATCH_CODE = "DUPLICATE_MESH_DISPATCH";
60030
- DuplicateMeshDispatchError = class extends Error {
60031
- code = DUPLICATE_MESH_DISPATCH_CODE;
60032
- holderSessionId;
60033
- constructor(message, info = {}) {
60034
- super(message);
60035
- this.name = "DuplicateMeshDispatchError";
60036
- this.holderSessionId = info.holderSessionId;
60674
+ function orderSlotsForProviderSelection(slots, meshId, nodeId, node, task, quotaBonusByProvider) {
60675
+ return [...slots].sort((a, b) => {
60676
+ const capDelta = Number(slotHasCapacity(meshId, nodeId, node, b)) - Number(slotHasCapacity(meshId, nodeId, node, a));
60677
+ if (capDelta !== 0) return capDelta;
60678
+ return scoreSlotForTask(b, task, quotaBonusByProvider?.[b.provider] ?? 0) - scoreSlotForTask(a, task, quotaBonusByProvider?.[a.provider] ?? 0);
60679
+ });
60680
+ }
60681
+ function bestSlotForTask(node, task, meshId, quotaBonusByProvider) {
60682
+ const slots = resolveNodeCapabilitySlots(node, meshId);
60683
+ if (!slots.length) return null;
60684
+ let best = null;
60685
+ for (const slot of slots) {
60686
+ const score = scoreSlotForTask(slot, task, quotaBonusByProvider?.[slot.provider] ?? 0);
60687
+ if (!best || score > best.score) best = { slot, score };
60688
+ }
60689
+ return best;
60690
+ }
60691
+ function nodeFitnessForTask(node, task, meshId, quotaBonusByProvider) {
60692
+ return bestSlotForTask(node, task, meshId, quotaBonusByProvider)?.score ?? 0;
60693
+ }
60694
+ function slotCoversTaskDifficulty(slot, difficulty) {
60695
+ if (!slot) return false;
60696
+ if (!isMeshTaskDifficulty(difficulty)) return false;
60697
+ if (!slot.difficulty?.length) return true;
60698
+ return slot.difficulty.includes(difficulty);
60699
+ }
60700
+ function resolveLaunchAxis(taskValue, taskSource, slotValue, slotCoversDifficulty) {
60701
+ const stamped = typeof taskValue === "string" && taskValue.trim() ? taskValue.trim() : void 0;
60702
+ if (!stamped) return slotValue;
60703
+ if (taskSource !== "preset") return stamped;
60704
+ return slotCoversDifficulty && slotValue ? slotValue : stamped;
60705
+ }
60706
+ function orderEligibleNodes(meshId, strategy, nodes, opts) {
60707
+ if (strategy === "first_eligible" || nodes.length <= 1) {
60708
+ return nodes;
60709
+ }
60710
+ if (strategy === "fitness" && opts?.task) {
60711
+ const task = opts.task;
60712
+ const bonusCache = /* @__PURE__ */ new Map();
60713
+ const bonusFor = (c) => {
60714
+ let bonus = bonusCache.get(c.nodeId);
60715
+ if (!bonus) {
60716
+ bonus = quotaSpreadBonusByProvider(c.node, opts.quotaRouting);
60717
+ bonusCache.set(c.nodeId, bonus);
60037
60718
  }
60719
+ return bonus;
60038
60720
  };
60721
+ return [...nodes].sort((a, b) => {
60722
+ const fitDelta = nodeFitnessForTask(b.node, task, meshId, bonusFor(b)) - nodeFitnessForTask(a.node, task, meshId, bonusFor(a));
60723
+ if (fitDelta !== 0) return fitDelta;
60724
+ const prioDelta = resolveNodeSchedulingPriority(b.node?.policy) - resolveNodeSchedulingPriority(a.node?.policy);
60725
+ if (prioDelta !== 0) return prioDelta;
60726
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
60727
+ if (loadDelta !== 0) return loadDelta;
60728
+ return a.index - b.index;
60729
+ });
60039
60730
  }
60040
- });
60041
- function localCoordinatorDaemonId() {
60042
- return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
60731
+ const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
60732
+ let rotation = 0;
60733
+ if (strategy === "least_loaded" || strategy === "round_robin") {
60734
+ const cursor = opts?.bumpCursor ? MeshRuntimeStore.getInstance().bumpSchedulerCursor(meshId) : MeshRuntimeStore.getInstance().getSchedulerCursor(meshId);
60735
+ rotation = (cursor % nodes.length + nodes.length) % nodes.length;
60736
+ }
60737
+ const rotationRank = (index) => (index - rotation + nodes.length) % nodes.length;
60738
+ return [...nodes].sort((a, b) => {
60739
+ const prioDelta = priorityOf(b) - priorityOf(a);
60740
+ if (prioDelta !== 0) return prioDelta;
60741
+ if (strategy === "least_loaded" || strategy === "round_robin") {
60742
+ const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
60743
+ if (loadDelta !== 0) return loadDelta;
60744
+ }
60745
+ return rotationRank(a.index) - rotationRank(b.index);
60746
+ });
60043
60747
  }
60044
- function acquireAutoFastForwardLease(workspace) {
60045
- const key2 = normalizeMeshWorkspaceForCompare(workspace);
60046
- if (!key2) return false;
60047
- if (autoFastForwardWorkspaceLease.has(key2)) return false;
60048
- autoFastForwardWorkspaceLease.add(key2);
60049
- return true;
60748
+ function activeProviderAssignedCount(meshId, nodeId, providerType) {
60749
+ return getQueue(meshId, { status: ["assigned"] }).filter((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId) && task.assignedProviderType === providerType).length;
60050
60750
  }
60051
- function releaseAutoFastForwardLease(workspace) {
60052
- const key2 = normalizeMeshWorkspaceForCompare(workspace);
60053
- if (key2) autoFastForwardWorkspaceLease.delete(key2);
60751
+ function activeSlotAssignedCount(meshId, nodeId, providerType, slot) {
60752
+ return getQueue(meshId, { status: ["assigned"] }).filter((task) => {
60753
+ if (!daemonIdsEquivalent(task.assignedNodeId, nodeId)) return false;
60754
+ if (task.assignedProviderType !== providerType) return false;
60755
+ const assignedModel = task.assignedModel;
60756
+ if (typeof assignedModel !== "string" || !assignedModel.trim()) return true;
60757
+ return isModelAllowedBySlot(assignedModel, slot);
60758
+ }).length;
60054
60759
  }
60055
- function isWorkspaceAutoFastForwardInFlight(workspace) {
60056
- const key2 = normalizeMeshWorkspaceForCompare(workspace || "");
60057
- return !!key2 && autoFastForwardWorkspaceLease.has(key2);
60760
+ function slotCapacityRemaining(meshId, nodeId, node, slot) {
60761
+ const providerType = typeof slot.provider === "string" ? slot.provider.trim() : "";
60762
+ if (!providerType) return { available: false };
60763
+ const slots = resolveNodeCapabilitySlots(node, meshId);
60764
+ const slotCap = resolveSlotMaxParallel(slots, providerType, slot.model, isModelAllowedBySlot);
60765
+ if (slotCap !== void 0 && activeSlotAssignedCount(meshId, nodeId, providerType, slot) >= slotCap) {
60766
+ return { available: false, slotCap };
60767
+ }
60768
+ const providerCap = resolveProviderMaxParallel(slots, providerType);
60769
+ if (providerCap !== void 0 && activeProviderAssignedCount(meshId, nodeId, providerType) >= providerCap) {
60770
+ return { available: false, slotCap, providerCap };
60771
+ }
60772
+ return { available: true, slotCap, providerCap };
60058
60773
  }
60059
- function __resetIdleAutoFastForwardForTests() {
60060
- idleAutoFastForwardLastAttempt.clear();
60061
- continuousAutoFastForwardLastScan.clear();
60062
- autoFastForwardWorkspaceLease.clear();
60774
+ function slotHasCapacity(meshId, nodeId, node, slot) {
60775
+ return slotCapacityRemaining(meshId, nodeId, node, slot).available;
60776
+ }
60777
+ function sessionHasActiveAssignment(meshId, sessionId) {
60778
+ if (getQueue(meshId, { status: ["assigned"] }).some((task) => sessionIdsEquivalent(task.assignedSessionId, sessionId))) {
60779
+ return true;
60780
+ }
60781
+ try {
60782
+ if (getActiveDirectDispatches(meshId).some((d) => sessionIdsEquivalent(d.sessionId, sessionId))) return true;
60783
+ if (hasUnterminalDirectDispatchLedgerEntry(meshId, sessionId)) return true;
60784
+ } catch {
60785
+ }
60786
+ return false;
60787
+ }
60788
+ var init_mesh_scheduling_fitness = __esm2({
60789
+ "src/mesh/mesh-scheduling-fitness.ts"() {
60790
+ "use strict";
60791
+ init_mesh_work_queue();
60792
+ init_mesh_runtime_store();
60793
+ init_mesh_work_queue();
60794
+ init_repo_mesh_types();
60795
+ init_dist();
60796
+ init_mesh_node_slots();
60797
+ init_mesh_quota_routing();
60798
+ init_mesh_events_stale();
60799
+ init_slot_model_enforcement();
60800
+ }
60801
+ });
60802
+ function localCoordinatorDaemonId() {
60803
+ return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
60063
60804
  }
60064
60805
  function loadRepoConfigForNode(node) {
60065
60806
  const workspace = typeof node?.workspace === "string" && node.workspace.trim() ? node.workspace.trim() : "";
@@ -60247,14 +60988,15 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
60247
60988
  if (timer) clearTimeout(timer);
60248
60989
  const isQueued = res && typeof res === "object" && res.status === "queued";
60249
60990
  updateSessionDeliveryStatus(delivery.id, isQueued ? "queued" : "delivered");
60250
- if (!isQueued && ctx.task.attemptId) {
60991
+ if (ctx.task.attemptId) {
60251
60992
  try {
60252
60993
  recordTurnAck({
60253
60994
  meshId: ctx.meshId,
60254
60995
  taskId: ctx.task.id,
60255
- kind: "delivered",
60996
+ kind: isQueued ? "accepted" : "delivered",
60256
60997
  attemptId: ctx.task.attemptId,
60257
- sessionId: ctx.sessionId
60998
+ sessionId: ctx.sessionId,
60999
+ ...isQueued ? { evidence: { source: "transport_queued_in_adapter" } } : {}
60258
61000
  });
60259
61001
  } catch {
60260
61002
  }
@@ -60295,22 +61037,52 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
60295
61037
  LOG2.error("MeshQueue", `Failed to dispatch task via ${ctx.transport} to node ${ctx.nodeId}: ${e?.message}`);
60296
61038
  updateSessionDeliveryStatus(delivery.id, "failed", { lastError: e?.message, incrementAttempt: true });
60297
61039
  endTaskDispatchInFlight(ctx.meshId, ctx.task.id);
60298
- updateTaskStatus(ctx.meshId, ctx.task.id, "pending");
60299
61040
  try {
60300
61041
  closeAttemptForReassignment({ meshId: ctx.meshId, taskId: ctx.task.id, reason: "dispatch_failed" });
60301
61042
  } catch {
60302
61043
  }
61044
+ const retryable = isRetryableDispatchFailure(e);
61045
+ if (!retryable) {
61046
+ failTaskAsUndeliverable(ctx, `dispatch_unrecoverable: ${e?.message || "transport reported the failure as non-recoverable"}`);
61047
+ } else {
61048
+ const requeued = requeueTask(ctx.meshId, ctx.task.id, {
61049
+ reason: "dispatch_failed",
61050
+ clearTargetSession: false
61051
+ });
61052
+ if (requeued?.status === "failed") {
61053
+ LOG2.error("MeshQueue", `Task ${ctx.task.id} (mesh ${ctx.meshId}) failed after repeated undeliverable dispatches to node ${ctx.nodeId}: ${requeued.cancelReason || "max_retries_exceeded"}. Dependents were unblocked.`);
61054
+ }
61055
+ }
60303
61056
  try {
60304
61057
  appendLedgerEntry(ctx.meshId, {
60305
61058
  kind: "dispatch_failed",
60306
61059
  nodeId: ctx.nodeId,
60307
61060
  sessionId: ctx.sessionId,
60308
- payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable: isRetryableDispatchFailure(e), transport: ctx.transport }
61061
+ payload: { taskId: ctx.task.id, deliveryId: delivery.id, error: e?.message, retryable, transport: ctx.transport }
60309
61062
  });
60310
61063
  } catch {
60311
61064
  }
60312
61065
  });
60313
61066
  }
61067
+ function failTaskAsUndeliverable(ctx, reason) {
61068
+ try {
61069
+ const failed = requeueTask(ctx.meshId, ctx.task.id, { maxRetries: 0, reason, clearTargetSession: false });
61070
+ if (!failed) return;
61071
+ } catch (err) {
61072
+ LOG2.warn("MeshQueue", `Failed to mark undeliverable task ${ctx.task.id} (mesh ${ctx.meshId}) terminal: ${err?.message || err}`);
61073
+ return;
61074
+ }
61075
+ LOG2.error("MeshQueue", `Task ${ctx.task.id} (mesh ${ctx.meshId}) is undeliverable to node ${ctx.nodeId} (session ${ctx.sessionId ?? "?"}) and will NOT be retried: ${reason}`);
61076
+ try {
61077
+ appendLedgerEntry(ctx.meshId, {
61078
+ kind: "task_failed",
61079
+ nodeId: ctx.nodeId,
61080
+ sessionId: ctx.sessionId,
61081
+ payload: { taskId: ctx.task.id, reason, undeliverable: true }
61082
+ });
61083
+ } catch {
61084
+ }
61085
+ }
60314
61086
  function isRetryableDispatchFailure(e) {
60315
61087
  if (e && typeof e === "object") {
60316
61088
  if (e.retryRecommended === false) return false;
@@ -60604,143 +61376,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
60604
61376
  }
60605
61377
  return out;
60606
61378
  }
60607
- function isActionableSkipReason(reason) {
60608
- if (!reason) return false;
60609
- return ACTIONABLE_SKIP_REASON_PREFIXES.some((prefix) => reason === prefix || reason.startsWith(prefix));
60610
- }
60611
- function isTargetNodeTransientlyUnresolved(mesh, task) {
60612
- const targetNodeId = readNonEmptyString(task.targetNodeId);
60613
- if (!targetNodeId) return false;
60614
- const node = Array.isArray(mesh?.nodes) ? mesh.nodes.find((n) => meshNodeIdMatches(n, targetNodeId)) : void 0;
60615
- if (node && node.worktreeBootstrap?.status === "running" && !isWorktreeBootstrapStaleRunning(node)) {
60616
- return true;
60617
- }
60618
- return isWithinCloneBootstrapGrace(targetNodeId);
60619
- }
60620
- function targetPinAgeMs(task, nowMs = Date.now()) {
60621
- const anchorMs = Date.parse(task.requeuedAt || task.createdAt || "");
60622
- return Number.isFinite(anchorMs) ? nowMs - anchorMs : null;
60623
- }
60624
- function resolveDeadTargetVerdict(components, meshId, mesh, task) {
60625
- const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
60626
- const targetSessionId = readNonEmptyString(task.targetSessionId);
60627
- const targetNodeId = readNonEmptyString(task.targetNodeId);
60628
- if (!targetSessionId && !targetNodeId) return NOT_DEAD;
60629
- const lastUpdateMs = Date.parse(task.updatedAt || task.createdAt || "");
60630
- if (Number.isFinite(lastUpdateMs) && Date.now() - lastUpdateMs < DEAD_TARGET_GRACE_MS) return NOT_DEAD;
60631
- const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
60632
- if (targetNodeId) {
60633
- const nodePresent = nodes.some((n) => meshNodeIdMatches(n, targetNodeId));
60634
- if (!nodePresent) {
60635
- if (isTargetNodeTransientlyUnresolved(mesh, task)) return NOT_DEAD;
60636
- return { dead: true, nodeDead: true, reason: "dead_target_node_absent" };
60637
- }
60638
- }
60639
- if (targetSessionId) {
60640
- const node = targetNodeId ? nodes.find((n) => meshNodeIdMatches(n, targetNodeId)) : void 0;
60641
- const nodeIsLocal = node ? isLocalAutoLaunchNode(node) : true;
60642
- if (nodeIsLocal) {
60643
- const verdict = resolveSessionBusyVerdict(components, targetSessionId);
60644
- if (verdict === "UNKNOWN") {
60645
- return { dead: true, nodeDead: false, reason: "dead_target_session_absent" };
60646
- }
60647
- }
60648
- }
60649
- return NOT_DEAD;
60650
- }
60651
- function retractActionableSkipIfPreviouslyNotified(meshId, taskId) {
60652
- const dedupKey = `${meshId}:${taskId}`;
60653
- if (!lastActionableSkipNotified.delete(dedupKey)) return;
60654
- try {
60655
- const coordinatorDaemonId = readNonEmptyString(loadConfig2().machineId) || void 0;
60656
- const removed = retractPendingDispatchBlockedEvent(meshId, taskId, coordinatorDaemonId);
60657
- if (removed > 0) {
60658
- LOG2.info("MeshQueue", `Retracted ${removed} stale dispatch-blocked event(s) for task ${taskId} (mesh ${meshId}) \u2014 its blocker resolved`);
60659
- }
60660
- } catch (e) {
60661
- LOG2.warn("MeshQueue", `Failed to retract stale dispatch-blocked event for task ${taskId} (mesh ${meshId}): ${e?.message || e}`);
60662
- }
60663
- }
60664
- function actionableSkipGuidance(reason) {
60665
- if (reason === "target_node_id_unmatched") return {
60666
- summary: "it is pinned to a target node id that matches no node in the mesh (the node may have been removed, or its id form does not resolve)",
60667
- nextAction: "Verify the target node still exists with mesh_status, then re-enqueue without the node pin or with a valid node id (or re-clone the node)."
60668
- };
60669
- if (reason === "no_node_satisfies_required_tags") return {
60670
- summary: "no node in the mesh can satisfy the task's required capability tags",
60671
- nextAction: "Relax the task's requiredTags, or add/launch a node whose provider produces the required capabilities."
60672
- };
60673
- if (reason === "mesh_convergence_target_is_worktree") return {
60674
- summary: "it is a convergence task (base-only: merge \u2192 push \u2192 cleanup) but every candidate node is a worktree clone",
60675
- nextAction: "Dispatch the convergence task to the base node, or run the deterministic fast-forward path (mesh_fast_forward_node / mesh_refine_node) instead."
60676
- };
60677
- if (reason.startsWith("remote_auto_launch")) return {
60678
- summary: "the target node is on a remote daemon this coordinator cannot auto-launch a session on (no dispatch transport, or no coordinator daemon id to stamp)",
60679
- nextAction: "Launch a session on that node yourself with mesh_launch_session, or ensure the remote daemon is connected over P2P."
60680
- };
60681
- if (reason.startsWith("provider") || reason === "missing_provider_priority") return {
60682
- summary: "the node has no usable provider for this task (provider priority missing/unusable, or the provider loader is unavailable)",
60683
- nextAction: "Check the node's providerPriority policy and that the required CLI/ACP provider is installed and enabled on that machine."
60684
- };
60685
- if (reason === "dirty_workspace") return {
60686
- summary: "the node's workspace is dirty, so auto-launch is blocked to avoid clobbering uncommitted changes",
60687
- nextAction: "Clean or commit the node's working tree (or fast-forward it); the task will then auto-assign."
60688
- };
60689
- if (reason === "target_session_pin_expired") return {
60690
- summary: "it was pinned to a specific session (a follow-up/delta for work already in flight) that never claimed it within the pin TTL, so the pin was cleared and the message will NOT reach the session it was addressed to",
60691
- nextAction: "Assume the addressed session never received this delta and is still acting on its previous instructions. Re-send it to that session once it is idle (or re-target it), and re-check the work it produced in the meantime."
60692
- };
60693
- if (reason === SLOT_MODEL_ABSENT_SKIP_REASON) return {
60694
- summary: "no capability slot on the node declares the model this task resolved to (its difficulty\u2192brain preset picked a model the node was never configured to run)",
60695
- nextAction: "Re-enqueue with a difficulty/model the node's slots declare, target a node that declares this model, or add a slot for it. The task is NOT run on a substitute model \u2014 an undeclared model is never launched."
60696
- };
60697
- return {
60698
- summary: `it cannot be dispatched (${reason})`,
60699
- nextAction: "Inspect the node/mesh state with mesh_status and resolve the blocker, or re-enqueue the task."
60700
- };
60701
- }
60702
- function notifyCoordinatorOfActionableSkip(meshId, taskId, reason, nodeId) {
60703
- if (!isActionableSkipReason(reason)) return;
60704
- if (reason === "target_node_id_unmatched" && isWithinCloneBootstrapGrace(readNonEmptyString(nodeId))) return;
60705
- const dedupKey = `${meshId}:${taskId}`;
60706
- if (lastActionableSkipNotified.get(dedupKey) === reason) return;
60707
- lastActionableSkipNotified.set(dedupKey, reason);
60708
- if (lastActionableSkipNotified.size > AUTO_LAUNCH_LEDGER_DEDUP_MAX) {
60709
- const oldest = lastActionableSkipNotified.keys().next().value;
60710
- if (oldest !== void 0) lastActionableSkipNotified.delete(oldest);
60711
- }
60712
- let task;
60713
- try {
60714
- task = getQueue(meshId).find((t) => t.id === taskId);
60715
- } catch {
60716
- }
60717
- const targetCoordinatorDaemonId = readNonEmptyString(loadConfig2().machineId);
60718
- const targetCoordinatorSessionId = readNonEmptyString(task?.sourceCoordinatorSessionId);
60719
- const nodeLabel = readNonEmptyString(nodeId) || readNonEmptyString(task?.targetNodeId);
60720
- const { summary, nextAction } = actionableSkipGuidance(reason);
60721
- const coordinatorMessage = `[System] A queued mesh task${nodeLabel ? ` for node ${nodeLabel}` : ""} is not being dispatched because ${summary}. ${nextAction} This is an actionable blocker \u2014 it will NOT clear on its own; the task stays pending until you resolve it.`;
60722
- try {
60723
- queuePendingMeshCoordinatorEvent({
60724
- event: "mesh:dispatch_blocked",
60725
- meshId,
60726
- nodeLabel: nodeLabel || meshId,
60727
- ...nodeLabel ? { nodeId: nodeLabel } : {},
60728
- metadataEvent: {
60729
- source: "mesh_queue_dispatch_skip",
60730
- taskId,
60731
- reason,
60732
- ...nodeLabel ? { nodeId: nodeLabel } : {},
60733
- coordinatorMessage
60734
- },
60735
- coordinatorMessage,
60736
- queuedAt: Date.now(),
60737
- ...targetCoordinatorDaemonId ? { targetCoordinatorDaemonId } : {},
60738
- ...targetCoordinatorSessionId ? { targetCoordinatorSessionId } : {}
60739
- });
60740
- } catch (e) {
60741
- LOG2.warn("MeshQueue", `Failed to surface actionable dispatch-skip (${reason}) for task ${taskId}: ${e?.message || e}`);
60742
- }
60743
- }
60744
61379
  function sweepExpiredCooldowns() {
60745
61380
  const now = Date.now();
60746
61381
  for (const [key2, until] of autoLaunchCooldownUntil) {
@@ -60760,28 +61395,11 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
60760
61395
  function isTerminalSessionStatus(status) {
60761
61396
  return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
60762
61397
  }
60763
- function isIdleSessionState(state) {
61398
+ function isIdleSessionState2(state) {
60764
61399
  const status = readNonEmptyString(state?.status).toLowerCase();
60765
61400
  if (isTerminalSessionStatus(status)) return false;
60766
61401
  return status === "idle" || state?.activeChat?.status === "waiting_input";
60767
61402
  }
60768
- function isDirtyNode(node) {
60769
- return node?.health === "dirty" || node?.git?.dirty === true;
60770
- }
60771
- function resolveAutoFastForwardPolicy(mesh) {
60772
- const record2 = mesh?.policy?.autoFastForward && typeof mesh.policy.autoFastForward === "object" && !Array.isArray(mesh.policy.autoFastForward) ? mesh.policy.autoFastForward : {};
60773
- const maxBehind = Number(record2.maxBehind);
60774
- return {
60775
- enabled: record2.enabled !== false,
60776
- ...Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {},
60777
- requireCleanSubmodules: record2.requireCleanSubmodules !== false,
60778
- // Strict opt-in: absent/false → self-only (historical behavior). Only an
60779
- // explicit `true` extends auto ff to remote owning-daemon nodes.
60780
- remoteNodes: record2.remoteNodes === true,
60781
- // Absent/anything-but-continuous → 'idle' (historical idle-edge-only detection).
60782
- mode: record2.mode === "continuous" ? "continuous" : "idle"
60783
- };
60784
- }
60785
61403
  function sessionStateLooksActive(state) {
60786
61404
  const status = readNonEmptyString(state?.status).toLowerCase();
60787
61405
  const chatStatus = readNonEmptyString(state?.activeChat?.status).toLowerCase();
@@ -60797,7 +61415,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
60797
61415
  const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
60798
61416
  if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
60799
61417
  const sessionId = readNonEmptyString(state.instanceId);
60800
- if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState(state)) return false;
61418
+ if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState2(state)) return false;
60801
61419
  return sessionStateLooksActive(state);
60802
61420
  });
60803
61421
  }
@@ -60832,174 +61450,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
60832
61450
  if (!coordinatorDaemonId) return { mode: "skip", reason: "remote_auto_launch_no_coordinator_daemon_id" };
60833
61451
  return { mode: "remote", daemonId, coordinatorDaemonId };
60834
61452
  }
60835
- function activeWriteAssignedCount(meshId) {
60836
- return getQueue(meshId, { status: ["assigned"] }).filter((task) => !isTaskReadonly(task)).length;
60837
- }
60838
- function activeReadonlyAssignedCount(meshId) {
60839
- return getQueue(meshId, { status: ["assigned"] }).filter(isTaskReadonly).length;
60840
- }
60841
- function nodeHasActiveAssignment(meshId, nodeId) {
60842
- return getQueue(meshId, { status: ["assigned"] }).some((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId));
60843
- }
60844
- function nodeActiveLoad(meshId, nodeId) {
60845
- return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
60846
- }
60847
- function meshHasExplicitSlots(mesh) {
60848
- const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
60849
- return nodes.some((n) => normalizeNodeCapabilitySlots(n?.policy?.slots).length > 0);
60850
- }
60851
- function resolveSchedulingStrategy(mesh) {
60852
- const raw = mesh?.policy?.schedulingStrategy;
60853
- if (typeof raw === "string" && raw.trim()) {
60854
- return normalizeMeshSchedulingStrategy(raw);
60855
- }
60856
- return meshHasExplicitSlots(mesh) ? "fitness" : normalizeMeshSchedulingStrategy(void 0);
60857
- }
60858
- function buildSchedulingPool(localCandidates, remoteCandidates) {
60859
- const pool = [...localCandidates, ...remoteCandidates].map((c) => ({
60860
- ...c,
60861
- nodeId: normalizeMeshNodeId(c.node) ?? c.nodeId
60862
- }));
60863
- const uniqueNodes = [...new Set(pool.map((c) => c.nodeId))].map((nodeId, index) => ({
60864
- nodeId,
60865
- node: pool.find((c) => meshNodeIdMatches({ id: c.nodeId }, nodeId))?.node,
60866
- index
60867
- }));
60868
- return { pool, uniqueNodes };
60869
- }
60870
- function scoreSlotForTask(slot, task, quotaBonus = 0) {
60871
- let score = 1;
60872
- const diff = isMeshTaskDifficulty(task.difficulty) ? task.difficulty : void 0;
60873
- if (diff) {
60874
- if (slot.difficulty?.length) {
60875
- score += slot.difficulty.includes(diff) ? 100 : 0;
60876
- } else {
60877
- score += 20;
60878
- }
60879
- }
60880
- const req = task.requiredTags?.filter((t) => !!t) ?? [];
60881
- if (req.length) {
60882
- const cap = new Set(slot.capability ?? []);
60883
- const covered = req.every((t) => cap.has(t));
60884
- score += covered ? 30 : 0;
60885
- }
60886
- score += quotaBonus;
60887
- return score;
60888
- }
60889
- function orderSlotsForProviderSelection(slots, meshId, nodeId, node, task, quotaBonusByProvider) {
60890
- return [...slots].sort((a, b) => {
60891
- const capDelta = Number(slotHasCapacity(meshId, nodeId, node, b)) - Number(slotHasCapacity(meshId, nodeId, node, a));
60892
- if (capDelta !== 0) return capDelta;
60893
- return scoreSlotForTask(b, task, quotaBonusByProvider?.[b.provider] ?? 0) - scoreSlotForTask(a, task, quotaBonusByProvider?.[a.provider] ?? 0);
60894
- });
60895
- }
60896
- function bestSlotForTask(node, task, meshId, quotaBonusByProvider) {
60897
- const slots = resolveNodeCapabilitySlots(node, meshId);
60898
- if (!slots.length) return null;
60899
- let best = null;
60900
- for (const slot of slots) {
60901
- const score = scoreSlotForTask(slot, task, quotaBonusByProvider?.[slot.provider] ?? 0);
60902
- if (!best || score > best.score) best = { slot, score };
60903
- }
60904
- return best;
60905
- }
60906
- function nodeFitnessForTask(node, task, meshId, quotaBonusByProvider) {
60907
- return bestSlotForTask(node, task, meshId, quotaBonusByProvider)?.score ?? 0;
60908
- }
60909
- function slotCoversTaskDifficulty(slot, difficulty) {
60910
- if (!slot) return false;
60911
- if (!isMeshTaskDifficulty(difficulty)) return false;
60912
- if (!slot.difficulty?.length) return true;
60913
- return slot.difficulty.includes(difficulty);
60914
- }
60915
- function resolveLaunchAxis(taskValue, taskSource, slotValue, slotCoversDifficulty) {
60916
- const stamped = typeof taskValue === "string" && taskValue.trim() ? taskValue.trim() : void 0;
60917
- if (!stamped) return slotValue;
60918
- if (taskSource !== "preset") return stamped;
60919
- return slotCoversDifficulty && slotValue ? slotValue : stamped;
60920
- }
60921
- function orderEligibleNodes(meshId, strategy, nodes, opts) {
60922
- if (strategy === "first_eligible" || nodes.length <= 1) {
60923
- return nodes;
60924
- }
60925
- if (strategy === "fitness" && opts?.task) {
60926
- const task = opts.task;
60927
- const bonusCache = /* @__PURE__ */ new Map();
60928
- const bonusFor = (c) => {
60929
- let bonus = bonusCache.get(c.nodeId);
60930
- if (!bonus) {
60931
- bonus = quotaSpreadBonusByProvider(c.node, opts.quotaRouting);
60932
- bonusCache.set(c.nodeId, bonus);
60933
- }
60934
- return bonus;
60935
- };
60936
- return [...nodes].sort((a, b) => {
60937
- const fitDelta = nodeFitnessForTask(b.node, task, meshId, bonusFor(b)) - nodeFitnessForTask(a.node, task, meshId, bonusFor(a));
60938
- if (fitDelta !== 0) return fitDelta;
60939
- const prioDelta = resolveNodeSchedulingPriority(b.node?.policy) - resolveNodeSchedulingPriority(a.node?.policy);
60940
- if (prioDelta !== 0) return prioDelta;
60941
- const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
60942
- if (loadDelta !== 0) return loadDelta;
60943
- return a.index - b.index;
60944
- });
60945
- }
60946
- const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
60947
- let rotation = 0;
60948
- if (strategy === "least_loaded" || strategy === "round_robin") {
60949
- const cursor = opts?.bumpCursor ? MeshRuntimeStore.getInstance().bumpSchedulerCursor(meshId) : MeshRuntimeStore.getInstance().getSchedulerCursor(meshId);
60950
- rotation = (cursor % nodes.length + nodes.length) % nodes.length;
60951
- }
60952
- const rotationRank = (index) => (index - rotation + nodes.length) % nodes.length;
60953
- return [...nodes].sort((a, b) => {
60954
- const prioDelta = priorityOf(b) - priorityOf(a);
60955
- if (prioDelta !== 0) return prioDelta;
60956
- if (strategy === "least_loaded" || strategy === "round_robin") {
60957
- const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
60958
- if (loadDelta !== 0) return loadDelta;
60959
- }
60960
- return rotationRank(a.index) - rotationRank(b.index);
60961
- });
60962
- }
60963
- function activeProviderAssignedCount(meshId, nodeId, providerType) {
60964
- return getQueue(meshId, { status: ["assigned"] }).filter((task) => daemonIdsEquivalent(task.assignedNodeId, nodeId) && task.assignedProviderType === providerType).length;
60965
- }
60966
- function activeSlotAssignedCount(meshId, nodeId, providerType, slot) {
60967
- return getQueue(meshId, { status: ["assigned"] }).filter((task) => {
60968
- if (!daemonIdsEquivalent(task.assignedNodeId, nodeId)) return false;
60969
- if (task.assignedProviderType !== providerType) return false;
60970
- const assignedModel = task.assignedModel;
60971
- if (typeof assignedModel !== "string" || !assignedModel.trim()) return true;
60972
- return isModelAllowedBySlot(assignedModel, slot);
60973
- }).length;
60974
- }
60975
- function slotCapacityRemaining(meshId, nodeId, node, slot) {
60976
- const providerType = typeof slot.provider === "string" ? slot.provider.trim() : "";
60977
- if (!providerType) return { available: false };
60978
- const slots = resolveNodeCapabilitySlots(node, meshId);
60979
- const slotCap = resolveSlotMaxParallel(slots, providerType, slot.model, isModelAllowedBySlot);
60980
- if (slotCap !== void 0 && activeSlotAssignedCount(meshId, nodeId, providerType, slot) >= slotCap) {
60981
- return { available: false, slotCap };
60982
- }
60983
- const providerCap = resolveProviderMaxParallel(slots, providerType);
60984
- if (providerCap !== void 0 && activeProviderAssignedCount(meshId, nodeId, providerType) >= providerCap) {
60985
- return { available: false, slotCap, providerCap };
60986
- }
60987
- return { available: true, slotCap, providerCap };
60988
- }
60989
- function slotHasCapacity(meshId, nodeId, node, slot) {
60990
- return slotCapacityRemaining(meshId, nodeId, node, slot).available;
60991
- }
60992
- function sessionHasActiveAssignment(meshId, sessionId) {
60993
- if (getQueue(meshId, { status: ["assigned"] }).some((task) => sessionIdsEquivalent(task.assignedSessionId, sessionId))) {
60994
- return true;
60995
- }
60996
- try {
60997
- if (getActiveDirectDispatches(meshId).some((d) => sessionIdsEquivalent(d.sessionId, sessionId))) return true;
60998
- if (hasUnterminalDirectDispatchLedgerEntry(meshId, sessionId)) return true;
60999
- } catch {
61000
- }
61001
- return false;
61002
- }
61003
61453
  function isSessionActivelyGenerating(components, sessionId) {
61004
61454
  if (!sessionId) return false;
61005
61455
  const state = components.instanceManager?.getInstance?.(sessionId)?.getState?.();
@@ -61594,7 +62044,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61594
62044
  if (instMeshId !== meshId) continue;
61595
62045
  const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
61596
62046
  if (!nodeId) continue;
61597
- if (!isIdleSessionState(state)) {
62047
+ if (!isIdleSessionState2(state)) {
61598
62048
  const status = readNonEmptyString(state.status).toLowerCase();
61599
62049
  skippedSessions.push({
61600
62050
  nodeId,
@@ -61701,209 +62151,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61701
62151
  ...pendingAfter > 0 && newlyAssignedTasks.length === 0 && localIdleSessionsChecked === 0 && remoteIdleSessionsChecked === 0 && !autoLaunchPending ? { noIdleMeshSessionAvailable: true } : {}
61702
62152
  };
61703
62153
  }
61704
- function dryRunSatisfiesAutoFastForwardPolicy(dryRun, policy) {
61705
- if (!dryRun || dryRun.code !== "fast_forward_available" || dryRun.allowed !== true) return false;
61706
- const behind = Number(dryRun.current?.behind);
61707
- if (!Number.isFinite(behind) || behind <= 0) return false;
61708
- if (policy.maxBehind !== void 0 && behind > policy.maxBehind) return false;
61709
- if (policy.requireCleanSubmodules) {
61710
- const submodules = Array.isArray(dryRun.current?.submodules) ? dryRun.current.submodules : [];
61711
- if (submodules.some((submodule) => submodule?.dirty || submodule?.outOfSync || submodule?.error)) return false;
61712
- }
61713
- return true;
61714
- }
61715
- function readNodeSubmoduleIgnorePaths(node) {
61716
- return Array.isArray(node?.policy?.submoduleIgnorePaths) ? node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
61717
- }
61718
- function nodeIsAutoFastForwardEligible(components, meshId, nodeId, node, currentSessionId) {
61719
- if (!node) return false;
61720
- if (node.status === "disabled" || node.status === "removed") return false;
61721
- if (node.readOnly === true || node.policy?.readOnly === true) return false;
61722
- if (isWorktreeBootstrapStaleRunning(node)) return false;
61723
- if (node.worktreeBootstrap?.status === "running") return false;
61724
- if (nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId)) return false;
61725
- return true;
61726
- }
61727
- function remoteNodeIsConnected(components, node) {
61728
- const daemonId = readMeshNodeDaemonId(node ?? {});
61729
- if (!daemonId) return false;
61730
- const getPeerStatus = components.getMeshPeerConnectionStatus;
61731
- if (getPeerStatus) {
61732
- const snapshot = getPeerStatus(daemonId);
61733
- return !!snapshot && String(snapshot.state) === "connected";
61734
- }
61735
- return readNonEmptyString(node?.connection?.state).toLowerCase() === "connected";
61736
- }
61737
- async function delegateRemoteAutoFastForward(components, args) {
61738
- const dispatchMeshCommand = components.dispatchMeshCommand;
61739
- if (!dispatchMeshCommand) return;
61740
- if (!acquireAutoFastForwardLease(args.workspace)) return;
61741
- const submoduleIgnorePaths = readNodeSubmoduleIgnorePaths(args.node);
61742
- const mesh = getMeshWithCache(components, args.meshId);
61743
- const baseArgs = {
61744
- meshId: args.meshId,
61745
- nodeId: args.nodeId,
61746
- workspace: args.workspace,
61747
- inlineMesh: mesh,
61748
- ...submoduleIgnorePaths ? { submoduleIgnorePaths } : {},
61749
- trigger: args.trigger,
61750
- // Preserve the pre-fix self-only semantics for submodules: the local idle path
61751
- // never updated submodules (updateSubmodules:false), so the remote path matches.
61752
- updateSubmodules: false
61753
- };
61754
- try {
61755
- const remoteDry = await dispatchMeshCommand(args.daemonId, "fast_forward_mesh_node", {
61756
- ...baseArgs,
61757
- execute: false,
61758
- dryRun: true
61759
- });
61760
- if (!dryRunSatisfiesAutoFastForwardPolicy(remoteDry, args.policy)) return;
61761
- if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId)) return;
61762
- const executed = await dispatchMeshCommand(args.daemonId, "fast_forward_mesh_node", {
61763
- ...baseArgs,
61764
- execute: true,
61765
- dryRun: false
61766
- });
61767
- if (executed?.executed === true) {
61768
- LOG2.info("MeshFastForward", `Remote auto fast-forward executed for node ${args.nodeId} (daemon ${String(args.daemonId).slice(0, 12)}, trigger ${args.trigger})`);
61769
- }
61770
- } catch (e) {
61771
- LOG2.warn("MeshFastForward", `Remote auto fast-forward delegation failed for ${args.nodeId}: ${e?.message || e}`);
61772
- } finally {
61773
- releaseAutoFastForwardLease(args.workspace);
61774
- }
61775
- }
61776
- async function executeLocalAutoFastForward(args) {
61777
- if (!acquireAutoFastForwardLease(args.workspace)) return;
61778
- const submoduleIgnorePaths = readNodeSubmoduleIgnorePaths(args.node);
61779
- try {
61780
- const dryRun = await fastForwardMeshNode({
61781
- meshId: args.meshId,
61782
- nodeId: args.nodeId,
61783
- workspace: args.workspace,
61784
- execute: false,
61785
- dryRun: true,
61786
- updateSubmodules: false,
61787
- submoduleIgnorePaths,
61788
- trigger: args.trigger
61789
- });
61790
- if (!dryRunSatisfiesAutoFastForwardPolicy(dryRun, args.policy)) return;
61791
- await fastForwardMeshNode({
61792
- meshId: args.meshId,
61793
- nodeId: args.nodeId,
61794
- workspace: args.workspace,
61795
- execute: true,
61796
- dryRun: false,
61797
- updateSubmodules: false,
61798
- submoduleIgnorePaths,
61799
- trigger: args.trigger
61800
- });
61801
- } catch (e) {
61802
- LOG2.warn("MeshFastForward", `Idle auto fast-forward check failed for ${args.nodeId}: ${e?.message || e}`);
61803
- } finally {
61804
- releaseAutoFastForwardLease(args.workspace);
61805
- }
61806
- }
61807
- async function maybeAutoFastForwardIdleNode(components, args) {
61808
- const mesh = getMeshWithCache(components, args.meshId);
61809
- const node = mesh?.nodes?.find((candidate) => meshNodeIdMatches(candidate, args.nodeId));
61810
- const workspace = readNonEmptyString(node?.workspace);
61811
- if (!workspace) return;
61812
- const policy = resolveAutoFastForwardPolicy(mesh);
61813
- if (!policy.enabled) return;
61814
- if (nodeHasActiveMeshWork(components, args.meshId, args.nodeId, args.sessionId)) return;
61815
- const throttleKey = `${args.meshId}:${args.nodeId}`;
61816
- const now = Date.now();
61817
- const lastAttempt = idleAutoFastForwardLastAttempt.get(throttleKey) || 0;
61818
- if (now - lastAttempt < IDLE_AUTO_FAST_FORWARD_THROTTLE_MS) return;
61819
- idleAutoFastForwardLastAttempt.set(throttleKey, now);
61820
- if (isLocalAutoLaunchNode(node)) {
61821
- if (!(0, import_fs14.existsSync)(workspace)) return;
61822
- await executeLocalAutoFastForward({ meshId: args.meshId, nodeId: args.nodeId, node, workspace, policy, trigger: "idle_auto" });
61823
- return;
61824
- }
61825
- if (!policy.remoteNodes) return;
61826
- const daemonId = readMeshNodeDaemonId(node ?? {});
61827
- if (!daemonId || !components.dispatchMeshCommand) return;
61828
- if (!remoteNodeIsConnected(components, node)) return;
61829
- await delegateRemoteAutoFastForward(components, { meshId: args.meshId, nodeId: args.nodeId, node, daemonId, workspace, policy, trigger: "idle_auto" });
61830
- }
61831
- async function runContinuousAutoFastForwardScan(components, mesh) {
61832
- if (!components.dispatchMeshCommand) return;
61833
- const policy = resolveAutoFastForwardPolicy(mesh);
61834
- if (!policy.enabled || !policy.remoteNodes || policy.mode !== "continuous") return;
61835
- const meshId = readNonEmptyString(mesh?.id);
61836
- if (!meshId) return;
61837
- const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
61838
- const now = Date.now();
61839
- for (const node of nodes) {
61840
- const nodeId = normalizeMeshNodeId(node);
61841
- if (!nodeId) continue;
61842
- if (node?.isLocalWorktree === true) continue;
61843
- if (isLocalAutoLaunchNode(node)) continue;
61844
- const workspace = readNonEmptyString(node?.workspace);
61845
- if (!workspace) continue;
61846
- const daemonId = readMeshNodeDaemonId(node ?? {});
61847
- if (!daemonId) continue;
61848
- if (!nodeIsAutoFastForwardEligible(components, meshId, nodeId, node)) continue;
61849
- if (!remoteNodeIsConnected(components, node)) continue;
61850
- const cooldownKey = `${meshId}:${nodeId}`;
61851
- const lastScan = continuousAutoFastForwardLastScan.get(cooldownKey) || 0;
61852
- if (now - lastScan < CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS) continue;
61853
- continuousAutoFastForwardLastScan.set(cooldownKey, now);
61854
- await delegateRemoteAutoFastForward(components, { meshId, nodeId, node, daemonId, workspace, policy, trigger: "reconcile_auto" });
61855
- }
61856
- }
61857
- async function runPendingCoordinatorCatchupScan(components, mesh) {
61858
- const meshId = readNonEmptyString(mesh?.id);
61859
- if (!meshId) return;
61860
- const localIds = expandDaemonIdForms([
61861
- readNonEmptyString(components.statusInstanceId),
61862
- readNonEmptyString(loadConfig2().machineId)
61863
- ]);
61864
- let markers = [];
61865
- try {
61866
- markers = drainPendingMeshCoordinatorEvents(
61867
- meshId,
61868
- localIds.length > 0 ? localIds : void 0,
61869
- { onlyEvents: /* @__PURE__ */ new Set(["coordinator_catchup"]) }
61870
- );
61871
- } catch (e) {
61872
- LOG2.warn("MeshReconcile", `Coordinator-catchup drain failed for mesh ${meshId}: ${e?.message || e}`);
61873
- return;
61874
- }
61875
- if (markers.length === 0) return;
61876
- const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
61877
- for (const marker of markers) {
61878
- const meta3 = marker.metadataEvent || {};
61879
- const nodeId = readNonEmptyString(marker.nodeId) || readNonEmptyString(meta3.nodeId);
61880
- const workspace = readNonEmptyString(marker.workspace) || readNonEmptyString(meta3.workspace);
61881
- const baseBranch = readNonEmptyString(meta3.baseBranch);
61882
- if (!workspace) continue;
61883
- if (nodeId && nodeHasActiveMeshWork(components, meshId, nodeId)) {
61884
- try {
61885
- queuePendingMeshCoordinatorEvent(marker);
61886
- } catch {
61887
- }
61888
- continue;
61889
- }
61890
- try {
61891
- const ff = await fastForwardMeshNode({
61892
- meshId,
61893
- ...nodeId ? { nodeId } : {},
61894
- workspace,
61895
- ...baseBranch ? { branch: baseBranch } : {},
61896
- mode: "merge",
61897
- execute: true,
61898
- trigger: "refine_post_push_catchup",
61899
- allowAutoPublishSubmoduleMainCommits: mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true
61900
- });
61901
- LOG2.info("MeshReconcile", `Coordinator catch-up ff for ${meshId}/${nodeId || workspace}: ${ff.code} (executed=${ff.executed})`);
61902
- } catch (e) {
61903
- LOG2.warn("MeshReconcile", `Coordinator catch-up ff failed for ${meshId}/${nodeId || workspace}: ${e?.message || e}`);
61904
- }
61905
- }
61906
- }
61907
62154
  function runIdleMaintenanceThenAssignQueue(components, args) {
61908
62155
  setImmediate(() => {
61909
62156
  maybeAutoFastForwardIdleNode(components, args).finally(() => {
@@ -61915,12 +62162,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61915
62162
  });
61916
62163
  });
61917
62164
  }
61918
- var import_fs14;
61919
- var IDLE_AUTO_FAST_FORWARD_THROTTLE_MS;
61920
- var idleAutoFastForwardLastAttempt;
61921
- var CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS;
61922
- var continuousAutoFastForwardLastScan;
61923
- var autoFastForwardWorkspaceLease;
61924
62165
  var BOOTSTRAP_TERMINAL_STATUSES;
61925
62166
  var DISPATCH_CONFIRM_TIMEOUT_MS;
61926
62167
  var DISPATCH_CONNECT_TIMEOUT_MS;
@@ -61936,15 +62177,9 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61936
62177
  var autoLaunchAwaitClaimBackoff;
61937
62178
  var lastAutoLaunchLedgerKey;
61938
62179
  var AUTO_LAUNCH_LEDGER_DEDUP_MAX;
61939
- var ACTIONABLE_SKIP_REASON_PREFIXES;
61940
- var TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON;
61941
- var lastActionableSkipNotified;
61942
- var DEAD_TARGET_GRACE_MS;
61943
- var TARGET_SESSION_PIN_TTL_MS;
61944
62180
  var init_mesh_queue_assignment = __esm2({
61945
62181
  "src/mesh/mesh-queue-assignment.ts"() {
61946
62182
  "use strict";
61947
- import_fs14 = require("fs");
61948
62183
  init_runtime_defaults();
61949
62184
  init_config();
61950
62185
  init_mesh_config();
@@ -61953,7 +62188,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61953
62188
  init_mesh_ledger();
61954
62189
  init_mesh_work_queue();
61955
62190
  init_transcript_evidence();
61956
- init_mesh_fast_forward();
61957
62191
  init_mesh_delivery_policy();
61958
62192
  init_mesh_runtime_store();
61959
62193
  init_mesh_event_trace();
@@ -61966,19 +62200,18 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61966
62200
  init_mesh_events_stale();
61967
62201
  init_mesh_events_utils();
61968
62202
  init_mesh_node_identity();
61969
- init_mesh_events_pending();
61970
62203
  init_worktree_bootstrap_config();
61971
- init_mesh_clone_grace();
61972
62204
  init_mesh_task_inflight();
61973
62205
  init_model_provider_compat();
61974
62206
  init_slot_model_enforcement();
61975
62207
  init_mesh_turn_ledger();
61976
62208
  init_mesh_duplicate_dispatch();
61977
- IDLE_AUTO_FAST_FORWARD_THROTTLE_MS = 30 * 60 * 1e3;
61978
- idleAutoFastForwardLastAttempt = /* @__PURE__ */ new Map();
61979
- CONTINUOUS_AUTO_FAST_FORWARD_SCAN_COOLDOWN_MS = 45 * 1e3;
61980
- continuousAutoFastForwardLastScan = /* @__PURE__ */ new Map();
61981
- autoFastForwardWorkspaceLease = /* @__PURE__ */ new Set();
62209
+ init_mesh_auto_fast_forward();
62210
+ init_mesh_skip_notify();
62211
+ init_mesh_scheduling_fitness();
62212
+ init_mesh_auto_fast_forward();
62213
+ init_mesh_skip_notify();
62214
+ init_mesh_scheduling_fitness();
61982
62215
  BOOTSTRAP_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["complete", "failed"]);
61983
62216
  DISPATCH_CONFIRM_TIMEOUT_MS = 12e4;
61984
62217
  DISPATCH_CONNECT_TIMEOUT_MS = MESH_CONNECT_TIMEOUT_MS2;
@@ -61994,43 +62227,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61994
62227
  autoLaunchAwaitClaimBackoff = /* @__PURE__ */ new Map();
61995
62228
  lastAutoLaunchLedgerKey = /* @__PURE__ */ new Map();
61996
62229
  AUTO_LAUNCH_LEDGER_DEDUP_MAX = 2e3;
61997
- ACTIONABLE_SKIP_REASON_PREFIXES = [
61998
- "target_node_id_unmatched",
61999
- "no_node_satisfies_required_tags",
62000
- "mesh_convergence_target_is_worktree",
62001
- "remote_auto_launch_unsupported",
62002
- "remote_auto_launch_no_coordinator_daemon_id",
62003
- "missing_provider_priority",
62004
- "provider_loader_unavailable",
62005
- "provider_priority_unusable",
62006
- "provider_unusable",
62007
- "dirty_workspace",
62008
- // TARGET-PIN-TTL EXPIRY: the task was pinned to a specific session (a delta —
62009
- // a correction addressed to work already in flight) and that session never
62010
- // claimed it within the TTL, so the pin was cleared. Unlike the transient
62011
- // skips, this does NOT self-resolve into the intended outcome: the delta is
62012
- // now claimable by ANY compatible session, i.e. it will not reach the session
62013
- // it was written for. Previously this only bumped a metrics counter and wrote
62014
- // a log, so the coordinator kept waiting for a delivery that could no longer
62015
- // happen — measured live as 74min of silence while a worker continued on a
62016
- // premise the delta was meant to correct. The coordinator must know its
62017
- // addressed message lost its address.
62018
- "target_session_pin_expired",
62019
- // SLOT MODEL GUARD (absent): no slot on the node declares the task's model.
62020
- // Permanent — no amount of waiting produces a slot, so the coordinator must
62021
- // re-drive (adjust difficulty, target another node, ask the owner). Its
62022
- // busy counterpart SLOT_MODEL_BUSY_SKIP_REASON is deliberately NOT listed:
62023
- // that one clears on its own when the slot goes idle.
62024
- SLOT_MODEL_ABSENT_SKIP_REASON
62025
- // QUOTA GATE: 'provider_quota_session_low' / 'provider_quota_weekly_low' are
62026
- // deliberately NOT listed either — an exhausted quota window RESETS, so the
62027
- // block self-resolves exactly like the slot-busy case; the task waits in the
62028
- // queue and the coordinator is not paged (mesh-quota-routing.ts).
62029
- ];
62030
- TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON = "target_node_bootstrap_pending";
62031
- lastActionableSkipNotified = /* @__PURE__ */ new Map();
62032
- DEAD_TARGET_GRACE_MS = 6e4;
62033
- TARGET_SESSION_PIN_TTL_MS = 15 * 6e4;
62034
62230
  }
62035
62231
  });
62036
62232
  function readSettings(state) {
@@ -62504,6 +62700,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
62504
62700
  return cachedBinding;
62505
62701
  }
62506
62702
  const errors = [];
62703
+ let allModuleNotFound = true;
62507
62704
  for (const ref of getBindingCandidates()) {
62508
62705
  try {
62509
62706
  const mod = require(ref);
@@ -62515,15 +62712,19 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
62515
62712
  errors.push(`${ref}: module not found`);
62516
62713
  continue;
62517
62714
  }
62715
+ allModuleNotFound = false;
62518
62716
  const message = error48 instanceof Error ? error48.message : String(error48);
62519
62717
  errors.push(`${ref}: ${message}`);
62520
62718
  }
62521
62719
  }
62522
- cachedBinding = null;
62523
- cachedBindingError = new Error(
62720
+ const buildError = () => new Error(
62524
62721
  `ghostty-vt binding unavailable for runtime ${runtimeTriplet()} (${errors.join("; ") || "no candidates tried"})`
62525
62722
  );
62526
- throw cachedBindingError;
62723
+ if (allModuleNotFound) {
62724
+ cachedBinding = null;
62725
+ cachedBindingError = buildError();
62726
+ }
62727
+ throw buildError();
62527
62728
  }
62528
62729
  var DEFAULT_BINDING_CANDIDATES;
62529
62730
  var cachedBinding;
@@ -68949,6 +69150,7 @@ ${cleanBody}`;
68949
69150
  });
68950
69151
  var mesh_events_exports = {};
68951
69152
  __export2(mesh_events_exports, {
69153
+ PENDING_RETENTION_EXPIRED_HOLD_REASON: () => PENDING_RETENTION_EXPIRED_HOLD_REASON,
68952
69154
  __resetIdleAutoFastForwardForTests: () => __resetIdleAutoFastForwardForTests,
68953
69155
  __resetMeshWorkspaceCacheForTests: () => __resetMeshWorkspaceCacheForTests,
68954
69156
  clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
@@ -68956,6 +69158,7 @@ ${cleanBody}`;
68956
69158
  getMeshV2BackstopCounters: () => getMeshV2BackstopCounters,
68957
69159
  getMeshV2DrainCounters: () => getMeshV2DrainCounters,
68958
69160
  getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
69161
+ getPendingRetentionCounters: () => getPendingRetentionCounters,
68959
69162
  handleMeshForwardEvent: () => handleMeshForwardEvent,
68960
69163
  isMeshCoordinatorEvent: () => isMeshCoordinatorEvent,
68961
69164
  isMeshProtocolV2EnforceEnabled: () => isMeshProtocolV2EnforceEnabled,
@@ -69936,20 +70139,31 @@ ${cleanBody}`;
69936
70139
  });
69937
70140
  var pty_transport_exports = {};
69938
70141
  __export2(pty_transport_exports, {
69939
- NodePtyTransportFactory: () => NodePtyTransportFactory
70142
+ NodePtyTransportFactory: () => NodePtyTransportFactory,
70143
+ __setNodePtyLoaderForTests: () => __setNodePtyLoaderForTests
69940
70144
  });
70145
+ function isModuleNotFoundError2(error48) {
70146
+ return error48 instanceof Error && "code" in error48 && error48.code === "MODULE_NOT_FOUND";
70147
+ }
69941
70148
  function loadNodePty() {
69942
70149
  if (cachedPty !== void 0) return cachedPty;
69943
70150
  try {
69944
- cachedPty = require("node-pty");
70151
+ cachedPty = requireNodePty();
69945
70152
  (0, import_session_host_core3.ensureNodePtySpawnHelperPermissions)();
69946
- } catch {
69947
- cachedPty = null;
70153
+ } catch (error48) {
70154
+ if (isModuleNotFoundError2(error48)) {
70155
+ cachedPty = null;
70156
+ }
69948
70157
  }
69949
- return cachedPty;
70158
+ return cachedPty ?? null;
70159
+ }
70160
+ function __setNodePtyLoaderForTests(loader2) {
70161
+ cachedPty = void 0;
70162
+ requireNodePty = loader2 ?? (() => require("node-pty"));
69950
70163
  }
69951
70164
  var os12;
69952
70165
  var cachedPty;
70166
+ var requireNodePty;
69953
70167
  var NodePtyRuntimeTransport;
69954
70168
  var NodePtyTransportFactory;
69955
70169
  var init_pty_transport = __esm2({
@@ -69958,6 +70172,7 @@ ${cleanBody}`;
69958
70172
  os12 = __toESM2(require("os"));
69959
70173
  init_spawn_env();
69960
70174
  init_resolve_executable();
70175
+ requireNodePty = () => require("node-pty");
69961
70176
  NodePtyRuntimeTransport = class {
69962
70177
  constructor(handle) {
69963
70178
  this.handle = handle;
@@ -70012,6 +70227,100 @@ ${cleanBody}`;
70012
70227
  };
70013
70228
  }
70014
70229
  });
70230
+ function resolveTerminationSignal(termination) {
70231
+ if (typeof termination.signal === "number" && termination.signal > 0) return termination.signal;
70232
+ const code = termination.exitCode;
70233
+ if (typeof code === "number" && code > 128 && code <= 159) return code - 128;
70234
+ return null;
70235
+ }
70236
+ function terminationSignalName(signal) {
70237
+ return signal !== null ? SIGNAL_NAMES[signal] : void 0;
70238
+ }
70239
+ function classifyMeshTerminationStop(termination) {
70240
+ const signal = resolveTerminationSignal(termination);
70241
+ const signalName = terminationSignalName(signal);
70242
+ if (termination.requestedStop) {
70243
+ return { reason: "host_requested_stop", intentional: true, signal, signalName };
70244
+ }
70245
+ if (signal !== null) return { reason: "external_signal", intentional: false, signal, signalName };
70246
+ if (termination.exitCode === null) return { reason: "unknown_termination", intentional: false, signal, signalName };
70247
+ if (termination.exitCode === 0) return { reason: "self_exit", intentional: false, signal, signalName };
70248
+ return { reason: "unexpected_exit", intentional: false, signal, signalName };
70249
+ }
70250
+ function buildMeshTerminationStopPayload(input) {
70251
+ const { termination } = input;
70252
+ const classified = classifyMeshTerminationStop(termination);
70253
+ const terminatedAtIso = Number.isFinite(termination.terminatedAt) ? new Date(termination.terminatedAt).toISOString() : void 0;
70254
+ const lastOutputAtIso = typeof termination.lastOutputAt === "number" && Number.isFinite(termination.lastOutputAt) ? new Date(termination.lastOutputAt).toISOString() : void 0;
70255
+ const silentForMs = typeof termination.lastOutputAt === "number" && Number.isFinite(termination.lastOutputAt) && Number.isFinite(termination.terminatedAt) && termination.terminatedAt >= termination.lastOutputAt ? termination.terminatedAt - termination.lastOutputAt : void 0;
70256
+ return {
70257
+ intentional: classified.intentional,
70258
+ reason: classified.reason,
70259
+ // `source` marks who wrote the row, mirroring the operator-cleanup path's
70260
+ // convention so a reader can tell the two writers apart.
70261
+ source: "session_host_tombstone",
70262
+ observedVia: "session_exit",
70263
+ exitCode: termination.exitCode,
70264
+ signal: classified.signal,
70265
+ ...classified.signalName ? { signalName: classified.signalName } : {},
70266
+ // session-host's own classification, kept verbatim alongside ours so the
70267
+ // ledger never silently disagrees with the tombstone on disk.
70268
+ terminationReason: termination.reason,
70269
+ lifecycle: termination.lifecycle,
70270
+ ...termination.previousLifecycle ? { previousLifecycle: termination.previousLifecycle } : {},
70271
+ ...termination.requestedStop ? { requestedStop: termination.requestedStop } : {},
70272
+ ...typeof termination.osPid === "number" ? { osPid: termination.osPid } : {},
70273
+ ...terminatedAtIso ? { terminatedAt: terminatedAtIso } : {},
70274
+ ...lastOutputAtIso ? { lastOutputAt: lastOutputAtIso } : {},
70275
+ ...silentForMs !== void 0 ? { silentForMs } : {},
70276
+ ...input.isCoordinator ? { coordinatorSession: true } : {},
70277
+ ...input.workspace ? { workspace: input.workspace } : {}
70278
+ };
70279
+ }
70280
+ function resolveMeshTerminationBinding(settings) {
70281
+ if (!settings || typeof settings !== "object") return null;
70282
+ const read = (value) => typeof value === "string" ? value.trim() : "";
70283
+ const workerMeshId = read(settings.meshNodeFor);
70284
+ if (workerMeshId) {
70285
+ const nodeId = read(settings.meshNodeId);
70286
+ return { meshId: workerMeshId, ...nodeId ? { nodeId } : {}, isCoordinator: false };
70287
+ }
70288
+ const coordinatorMeshId = read(settings.meshCoordinatorFor);
70289
+ if (coordinatorMeshId) return { meshId: coordinatorMeshId, isCoordinator: true };
70290
+ return null;
70291
+ }
70292
+ async function recordMeshSessionTerminationStop(input) {
70293
+ if (!input.meshId || !input.sessionId) return;
70294
+ try {
70295
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
70296
+ appendLedgerEntry2(input.meshId, {
70297
+ kind: "session_stopped",
70298
+ ...input.nodeId ? { nodeId: input.nodeId } : {},
70299
+ sessionId: input.sessionId,
70300
+ ...input.providerType ? { providerType: input.providerType } : {},
70301
+ payload: buildMeshTerminationStopPayload(input)
70302
+ });
70303
+ } catch (e) {
70304
+ LOG2.warn("MeshTermination", `Failed to record termination stop for ${input.sessionId}: ${e?.message || e}`);
70305
+ }
70306
+ }
70307
+ var SIGNAL_NAMES;
70308
+ var init_mesh_termination_bridge = __esm2({
70309
+ "src/mesh/mesh-termination-bridge.ts"() {
70310
+ "use strict";
70311
+ init_logger();
70312
+ SIGNAL_NAMES = {
70313
+ 1: "SIGHUP",
70314
+ 2: "SIGINT",
70315
+ 3: "SIGQUIT",
70316
+ 6: "SIGABRT",
70317
+ 9: "SIGKILL",
70318
+ 11: "SIGSEGV",
70319
+ 13: "SIGPIPE",
70320
+ 15: "SIGTERM"
70321
+ };
70322
+ }
70323
+ });
70015
70324
  function chunkPreservingSurrogates(text, size) {
70016
70325
  const chunks = [];
70017
70326
  let offset = 0;
@@ -74444,6 +74753,7 @@ ${cont}` : cont;
74444
74753
  init_terminal_screen();
74445
74754
  import_session_host_core8 = require_dist();
74446
74755
  init_pty_transport();
74756
+ init_mesh_termination_bridge();
74447
74757
  init_pty_write_chunking();
74448
74758
  init_provider_cli_shared();
74449
74759
  init_cli_script_runner();
@@ -74457,9 +74767,10 @@ ${cont}` : cont;
74457
74767
  missingBackgroundSourceWarned = /* @__PURE__ */ new Set();
74458
74768
  FORCE_SUBMIT_SETTLE_MS = 150;
74459
74769
  ProviderCliAdapter = class _ProviderCliAdapter {
74460
- constructor(provider, workingDir, extraArgs = [], extraEnv = {}, transportFactory = new NodePtyTransportFactory()) {
74770
+ constructor(provider, workingDir, extraArgs = [], extraEnv = {}, transportFactory = new NodePtyTransportFactory(), owningSessionId) {
74461
74771
  this.extraArgs = extraArgs;
74462
74772
  this.extraEnv = extraEnv;
74773
+ this.owningSessionId = owningSessionId;
74463
74774
  this.runner = new CliScriptRunner(provider.type);
74464
74775
  this.provider = provider;
74465
74776
  this.transportFactory = transportFactory;
@@ -74909,8 +75220,9 @@ ${lastSnapshot}`;
74909
75220
  }
74910
75221
  }
74911
75222
  });
74912
- this.ptyProcess.onExit(({ exitCode, signal }) => {
75223
+ this.ptyProcess.onExit(({ exitCode, signal, termination }) => {
74913
75224
  LOG2.info("CLI", `[${this.cliType}] Exit code ${exitCode === null || exitCode === void 0 ? "unknown" : exitCode}${signal ? ` (signal ${signal})` : ""}`);
75225
+ this.recordMeshTerminationIfBound(termination);
74914
75226
  this.flushPendingOutputParse();
74915
75227
  this.ptyProcess = null;
74916
75228
  this.engine.onPtyExit();
@@ -74938,6 +75250,38 @@ ${lastSnapshot}`;
74938
75250
  this.scheduleStartupSettleCheck();
74939
75251
  this.onStatusChange?.();
74940
75252
  }
75253
+ /**
75254
+ * TOMBSTONE-LEDGER-BRIDGE: write a mesh `session_stopped` ledger entry for a
75255
+ * terminated mesh session.
75256
+ *
75257
+ * This is the daemon's existing observation point for a dying CLI child — the
75258
+ * session host already emits `session_exit` carrying its termination
75259
+ * classification, so no watcher or polling loop is added. Previously the
75260
+ * classification was discarded here, which is why an externally-killed mesh
75261
+ * session (e.g. a SIGTERM from outside the mesh) left the ledger with no entry
75262
+ * at all and its death read as an unexplained gap in the record.
75263
+ *
75264
+ * Silent no-op for a non-mesh session or a transport that reports no
75265
+ * termination (raw node-pty), and never throws into the exit path.
75266
+ */
75267
+ recordMeshTerminationIfBound(termination) {
75268
+ if (!termination || !this.owningSessionId) return;
75269
+ try {
75270
+ const binding = resolveMeshTerminationBinding(this.runtimeSettings);
75271
+ if (!binding) return;
75272
+ void recordMeshSessionTerminationStop({
75273
+ meshId: binding.meshId,
75274
+ sessionId: this.owningSessionId,
75275
+ nodeId: binding.nodeId,
75276
+ providerType: this.cliType,
75277
+ workspace: this.workingDir,
75278
+ isCoordinator: binding.isCoordinator,
75279
+ termination
75280
+ });
75281
+ } catch (e) {
75282
+ LOG2.warn("CLI", `[${this.cliType}] mesh termination ledger bridge failed: ${e?.message || e}`);
75283
+ }
75284
+ }
74941
75285
  // ─── Output Handling ────────────────────────────
74942
75286
  handleOutput(rawData) {
74943
75287
  this.terminalScreen.write(rawData);
@@ -84198,10 +84542,50 @@ ${effect.notification.body || ""}`.trim();
84198
84542
  init_contracts2();
84199
84543
  init_chat_history();
84200
84544
  init_state_store();
84201
- init_coordinator_registry();
84202
- init_mesh_turn_presentation();
84203
84545
  init_logger();
84204
84546
  init_debug_trace();
84547
+ init_chat_message_normalization();
84548
+ init_contracts2();
84549
+ init_coordinator_registry();
84550
+ init_logger();
84551
+ init_chat_message_normalization();
84552
+ function normalizeReadChatTailLimit(args) {
84553
+ const value = Number(args?.tailLimit || 0);
84554
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
84555
+ }
84556
+ function normalizeReadChatMessages(payload) {
84557
+ const messages = Array.isArray(payload.messages) ? payload.messages : [];
84558
+ return normalizeChatMessages(messages);
84559
+ }
84560
+ function maybeHideCoordinatorPromptMessage(h, providerType, sessionId, messages) {
84561
+ if (!Array.isArray(messages) || messages.length === 0) return messages;
84562
+ if (!sessionId) return messages;
84563
+ const loader2 = h.ctx?.providerLoader;
84564
+ if (!loader2) return messages;
84565
+ let showSetting = void 0;
84566
+ try {
84567
+ showSetting = loader2.getSettingValue?.(providerType, "showCoordinatorSystemPrompt");
84568
+ } catch {
84569
+ }
84570
+ if (showSetting === true) return messages;
84571
+ const coord = getCoordinatorForSession(sessionId);
84572
+ if (!coord) return messages;
84573
+ const promptBody = typeof coord.systemPrompt === "string" ? coord.systemPrompt : "";
84574
+ const MARKER = "adhdev-mesh-coordinator-prompt";
84575
+ const filtered = messages.filter((m) => {
84576
+ const role = String(m?.role || "").toLowerCase();
84577
+ if (role !== "user" && role !== "system") return true;
84578
+ const content = flattenContent(m?.content);
84579
+ if (!content) return true;
84580
+ if (content.includes(MARKER)) return false;
84581
+ if (promptBody && content.includes(promptBody.slice(0, Math.min(400, promptBody.length)))) return false;
84582
+ return true;
84583
+ });
84584
+ if (filtered.length !== messages.length) {
84585
+ LOG2.debug("ChatFilter", `[${providerType}] hid ${messages.length - filtered.length} coordinator-prompt message(s) from ${sessionId}`);
84586
+ }
84587
+ return filtered;
84588
+ }
84205
84589
  var INITIAL_CHAT_SOURCE_STATE = Object.freeze({
84206
84590
  name: "Booting",
84207
84591
  nativeSequencePeak: void 0,
@@ -84571,9 +84955,394 @@ ${effect.notification.body || ""}`.trim();
84571
84955
  return `v1:${providerType}:${sessionId}:${ts2 ?? ""}:${positionalSeq}:${role}`;
84572
84956
  }
84573
84957
  var CHAT_SOURCE_REGISTRY = new ChatSourceRegistry();
84958
+ init_chat_history();
84959
+ var CLI_NATIVE_TRANSCRIPT_PROVIDERS = /* @__PURE__ */ new Set(["codex-cli", "claude-cli", "hermes-cli", "antigravity-cli"]);
84960
+ var warnedLegacyNativeAllowlistHits = /* @__PURE__ */ new Set();
84961
+ function warnLegacyNativeAllowlistHit(providerType) {
84962
+ if (warnedLegacyNativeAllowlistHits.has(providerType)) return;
84963
+ warnedLegacyNativeAllowlistHits.add(providerType);
84964
+ console.warn(
84965
+ `[chat-commands] supportsCliNativeTranscript fell back to the hardcoded CLI_NATIVE_TRANSCRIPT_PROVIDERS set for "${providerType}". The provider module was unavailable or did not declare canonicalHistory. Set canonicalHistory.contractVersion in the provider.json to remove this dependency.`
84966
+ );
84967
+ }
84968
+ function supportsCliNativeTranscript(providerType, provider) {
84969
+ if (provider?.category === "cli" && isNativeSourceCanonicalHistory(provider?.nativeHistory)) {
84970
+ return true;
84971
+ }
84972
+ if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) {
84973
+ warnLegacyNativeAllowlistHit(providerType);
84974
+ return true;
84975
+ }
84976
+ return false;
84977
+ }
84978
+ function getMessageNewestReceivedAt(messages) {
84979
+ let newest = 0;
84980
+ for (const message of messages) {
84981
+ const receivedAt = Number(message?.receivedAt ?? message?.timestamp ?? 0);
84982
+ if (Number.isFinite(receivedAt) && receivedAt > newest) newest = receivedAt;
84983
+ }
84984
+ return newest;
84985
+ }
84986
+ function buildCliMessageSourceProvenance(args) {
84987
+ const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
84988
+ const sourceMtimeAgeMs = sourceMtimeMs > 0 ? Math.max(0, Date.now() - sourceMtimeMs) : void 0;
84989
+ const nativeMessages = args.nativeMessages || [];
84990
+ const ptyMessages = args.ptyMessages || [];
84991
+ const returnedMessages = args.returnedMessages || [];
84992
+ const identityStatus = args.selected === "native-history" ? "safe" : args.fallbackReason === "native_history_not_safely_mapped" ? "ambiguous_session_identity" : args.fallbackReason?.startsWith("native_history_unavailable") ? "transcript_unmapped" : void 0;
84993
+ return {
84994
+ selected: args.selected,
84995
+ provider: args.provider,
84996
+ providerType: args.provider,
84997
+ ...identityStatus ? { identityStatus } : {},
84998
+ ...args.nativeHandle ? { nativeHandle: args.nativeHandle } : {},
84999
+ ...args.nativeHandle ? { nativeSessionId: args.nativeHandle } : {},
85000
+ ...args.sessionWorkspace ? { sessionWorkspace: args.sessionWorkspace } : {},
85001
+ ...args.intendedWorkspace ? { intendedWorkspace: args.intendedWorkspace } : {},
85002
+ ...args.transcriptWorkspace ? { transcriptWorkspace: args.transcriptWorkspace } : {},
85003
+ ...args.fallbackReason ? { fallbackReason: args.fallbackReason } : {},
85004
+ ...args.nativeSource ? { nativeSource: args.nativeSource } : {},
85005
+ ...args.sourcePath ? { sourcePath: args.sourcePath } : {},
85006
+ ...args.nativeHistoryCoverage ? { nativeHistoryCoverage: args.nativeHistoryCoverage } : {},
85007
+ ...args.partialReason ? { partialReason: args.partialReason } : {},
85008
+ ...args.unavailableReason ? { unavailableReason: args.unavailableReason } : {},
85009
+ ptyStatusApprovalOnly: args.ptyStatusApprovalOnly === true,
85010
+ staleness: {
85011
+ sourceMtimeMs: sourceMtimeMs || void 0,
85012
+ sourceMtimeAgeMs,
85013
+ nativeNewestMessageAt: getMessageNewestReceivedAt(nativeMessages),
85014
+ ptyNewestMessageAt: getMessageNewestReceivedAt(ptyMessages),
85015
+ freshEnough: args.freshEnough === true
85016
+ },
85017
+ coverage: {
85018
+ nativeMessageCount: nativeMessages.length,
85019
+ ptyMessageCount: ptyMessages.length,
85020
+ returnedMessageCount: returnedMessages.length,
85021
+ safeMapping: args.safeMapping === true,
85022
+ // true when PTY message bodies are suppressed and must not be treated as
85023
+ // chat content. PTY may still contribute status/approval/screen evidence.
85024
+ ptyMessagesSuppressed: args.selected === "native-history" || args.ptyStatusApprovalOnly === true
85025
+ }
85026
+ };
85027
+ }
85028
+ function causeToLegacyFallbackReason(cause, selected, extraDetail) {
85029
+ if (selected === "native-history") return void 0;
85030
+ switch (cause) {
85031
+ case "initial":
85032
+ return "native_history_not_checked";
85033
+ case "native_progressed":
85034
+ return "native_history_not_selected";
85035
+ case "native_regressed_shrunk":
85036
+ return "native_history_empty";
85037
+ case "native_regressed_unsafe_mapping":
85038
+ return "native_history_not_safely_mapped";
85039
+ case "native_regressed_coverage_partial":
85040
+ return "native_history_partial";
85041
+ case "native_regressed_coverage_unavailable":
85042
+ return "native_history_unavailable";
85043
+ case "native_unavailable_read_error":
85044
+ return extraDetail?.unavailableReason ? `native_history_unavailable:${extraDetail.unavailableReason}` : "native_history_unavailable";
85045
+ case "native_unavailable_provider_unsupported":
85046
+ return "provider_native_transcript_not_supported";
85047
+ case "native_unavailable_empty":
85048
+ return "native_history_empty";
85049
+ case "native_unavailable_not_native_source":
85050
+ return extraDetail?.nativeSource ? `native_history_source_${extraDetail.nativeSource}` : "native_history_unavailable";
85051
+ }
85052
+ }
85053
+ function decideCliReadChatSource(args) {
85054
+ const supportsNative = supportsCliNativeTranscript(args.providerType, args.provider);
85055
+ const observation = buildObservationForCli(args, supportsNative);
85056
+ const sessionKey = chatSourceSessionKey(args.providerType, args.sessionId);
85057
+ const priorSnapshot = CHAT_SOURCE_REGISTRY.snapshotRecord(sessionKey);
85058
+ const priorState = priorSnapshot?.state ?? CHAT_SOURCE_REGISTRY.getState(sessionKey);
85059
+ const eligibleForStickyHold = args.trustedExactNativeIdentity === true && (priorState.name === "NativeLocked" || priorState.name === "Recovering");
85060
+ let decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
85061
+ const heldNativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
85062
+ const mayHoldNativeSlice = heldNativeMessages.length > 0 && args.safeMapping === true;
85063
+ if (eligibleForStickyHold && decision.selected === "pty-parser" && mayHoldNativeSlice) {
85064
+ CHAT_SOURCE_REGISTRY.restoreRecord(sessionKey, priorSnapshot);
85065
+ const messageSource2 = buildCliMessageSourceProvenance({
85066
+ selected: "native-history",
85067
+ provider: args.providerType,
85068
+ nativeHandle: typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0,
85069
+ sessionWorkspace: args.sessionWorkspace,
85070
+ intendedWorkspace: args.intendedWorkspace,
85071
+ transcriptWorkspace: void 0,
85072
+ fallbackReason: "native_history_transient_gap_held",
85073
+ nativeSource: "provider-native",
85074
+ sourcePath: typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0,
85075
+ sourceMtimeMs: typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0,
85076
+ nativeHistoryCoverage: void 0,
85077
+ partialReason: void 0,
85078
+ unavailableReason: observation.kind === "native_unavailable" ? observation.reason : void 0,
85079
+ nativeMessages: heldNativeMessages,
85080
+ ptyMessages: args.ptyMessages,
85081
+ returnedMessages: heldNativeMessages,
85082
+ safeMapping: args.safeMapping,
85083
+ freshEnough: true,
85084
+ ptyStatusApprovalOnly: true
85085
+ });
85086
+ return {
85087
+ decision: {
85088
+ selected: "native-history",
85089
+ nextState: priorState,
85090
+ transition: {
85091
+ fromState: priorState.name,
85092
+ toState: priorState.name,
85093
+ event: "NoOp",
85094
+ cause: decision.transition.cause,
85095
+ at: Date.now()
85096
+ },
85097
+ lockState: { locked: priorState.name === "NativeLocked" }
85098
+ },
85099
+ messageSource: messageSource2,
85100
+ nativeMessages: heldNativeMessages,
85101
+ nativeSelected: true
85102
+ };
85103
+ }
85104
+ if (decision.selected === "pty-parser" && args.trustedExactNativeIdentity === true && args.safeMapping && args.ptyMessages.length === 0 && observation.kind === "native_present" && observation.coverage !== "partial" && observation.messages.length > 0) {
85105
+ CHAT_SOURCE_REGISTRY.clear(sessionKey);
85106
+ decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
85107
+ }
85108
+ const nativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
85109
+ const nativeSource = typeof args.nativeHistoryResult?.source === "string" ? args.nativeHistoryResult.source : void 0;
85110
+ const sourcePath = typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0;
85111
+ const sourceMtimeMs = typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0;
85112
+ const coverageHint = typeof args.nativeHistoryResult?.nativeHistoryCoverage === "string" ? args.nativeHistoryResult.nativeHistoryCoverage : void 0;
85113
+ const partialReason = typeof args.nativeHistoryResult?.partialReason === "string" ? args.nativeHistoryResult.partialReason : void 0;
85114
+ const unavailableReason = typeof args.nativeHistoryResult?.unavailableReason === "string" ? args.nativeHistoryResult.unavailableReason : args.nativeHistoryError ? `error:${args.nativeHistoryError?.message || String(args.nativeHistoryError)}` : void 0;
85115
+ const nativeHandle = typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0;
85116
+ const transcriptWorkspace = typeof args.nativeHistoryResult?.workspace === "string" ? args.nativeHistoryResult.workspace : nativeMessages.map((m) => typeof m?.workspace === "string" ? m.workspace.trim() : "").find(Boolean);
85117
+ const fallbackReason = causeToLegacyFallbackReason(decision.transition.cause, decision.selected, {
85118
+ unavailableReason,
85119
+ nativeSource: nativeSource && nativeSource !== "provider-native" ? nativeSource : void 0
85120
+ });
85121
+ const ptyStatusApprovalOnly = decision.selected === "native-history" ? true : args.ptyStatusApprovalOnly;
85122
+ const messageSource = buildCliMessageSourceProvenance({
85123
+ selected: decision.selected,
85124
+ provider: args.providerType,
85125
+ nativeHandle,
85126
+ sessionWorkspace: args.sessionWorkspace,
85127
+ intendedWorkspace: args.intendedWorkspace,
85128
+ transcriptWorkspace,
85129
+ fallbackReason,
85130
+ nativeSource,
85131
+ sourcePath,
85132
+ sourceMtimeMs,
85133
+ nativeHistoryCoverage: coverageHint,
85134
+ partialReason,
85135
+ unavailableReason,
85136
+ nativeMessages,
85137
+ ptyMessages: args.ptyMessages,
85138
+ returnedMessages: decision.selected === "native-history" ? nativeMessages : args.ptyMessages,
85139
+ safeMapping: args.safeMapping,
85140
+ // freshEnough is a v1 concept the machine does not model directly.
85141
+ // We surface lockState.locked here so v1 consumers reading
85142
+ // staleness.freshEnough still get a meaningful boolean.
85143
+ freshEnough: decision.lockState.locked,
85144
+ ptyStatusApprovalOnly
85145
+ });
85146
+ return {
85147
+ decision,
85148
+ messageSource,
85149
+ nativeMessages,
85150
+ nativeSelected: decision.selected === "native-history"
85151
+ };
85152
+ }
85153
+ function buildObservationForCli(args, supportsNative) {
85154
+ if (!supportsNative) {
85155
+ return { kind: "native_unavailable", reason: "provider_not_supported" };
85156
+ }
85157
+ if (args.nativeHistoryError) {
85158
+ return { kind: "native_unavailable", reason: "read_error" };
85159
+ }
85160
+ const result = args.nativeHistoryResult;
85161
+ if (!result || typeof result !== "object") {
85162
+ return { kind: "native_unavailable", reason: "read_error" };
85163
+ }
85164
+ const source = typeof result.source === "string" ? result.source : "";
85165
+ if (source && source !== "provider-native") {
85166
+ return { kind: "native_unavailable", reason: source === "native-unavailable" ? "empty" : "not_native_source" };
85167
+ }
85168
+ const messages = Array.isArray(result.messages) ? result.messages : [];
85169
+ if (messages.length === 0) {
85170
+ return { kind: "native_unavailable", reason: "empty" };
85171
+ }
85172
+ const coverage = typeof result.nativeHistoryCoverage === "string" ? result.nativeHistoryCoverage : "tail";
85173
+ if (coverage === "unavailable") {
85174
+ return { kind: "native_unavailable", reason: "coverage_unavailable" };
85175
+ }
85176
+ return buildV1NativePresentObservation({
85177
+ providerType: args.providerType,
85178
+ sessionId: args.sessionId,
85179
+ messages,
85180
+ coverage: coverage === "full" || coverage === "tail" || coverage === "current-turn" || coverage === "partial" ? coverage : "tail",
85181
+ safeMapping: args.safeMapping
85182
+ });
85183
+ }
85184
+ function extractNativeMessagesFromResult(providerType, result) {
85185
+ if (!result || !Array.isArray(result.messages)) return [];
85186
+ return normalizeNativeHistoryMessages(
85187
+ providerType,
85188
+ result.messages,
85189
+ typeof result.providerSessionId === "string" ? result.providerSessionId : void 0
85190
+ );
85191
+ }
85192
+ init_mesh_turn_presentation();
84574
85193
  init_chat_message_normalization();
85194
+ function shouldPreserveReadChatPayloadField(key2) {
85195
+ return key2 === "messageSource" || key2 === "transcriptProvenance";
85196
+ }
85197
+ function updateMessageSourceReturnedCount(value, returnedMessageCount) {
85198
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
85199
+ const record2 = value;
85200
+ const coverage = record2.coverage && typeof record2.coverage === "object" && !Array.isArray(record2.coverage) ? record2.coverage : void 0;
85201
+ if (!coverage) return value;
85202
+ return {
85203
+ ...record2,
85204
+ coverage: {
85205
+ ...coverage,
85206
+ returnedMessageCount
85207
+ }
85208
+ };
85209
+ }
85210
+ function buildFullTail(messages, tailLimit) {
85211
+ const totalMessages = messages.length;
85212
+ const tailMessages = tailLimit > 0 ? messages.slice(-tailLimit) : messages;
85213
+ return {
85214
+ messages: tailMessages,
85215
+ totalMessages
85216
+ };
85217
+ }
85218
+ function hasNonEmptyModalButtons(activeModal) {
85219
+ if (!activeModal || typeof activeModal !== "object") return false;
85220
+ const buttons = activeModal.buttons;
85221
+ return Array.isArray(buttons) && buttons.some((button) => typeof button === "string" && button.trim().length > 0);
85222
+ }
85223
+ function normalizeReadChatCommandStatus(status, activeModal) {
85224
+ const raw = typeof status === "string" ? status.trim() : "";
85225
+ if (!raw) {
85226
+ return hasNonEmptyModalButtons(activeModal) ? "waiting_approval" : "idle";
85227
+ }
85228
+ switch (raw) {
85229
+ case "starting":
85230
+ return hasNonEmptyModalButtons(activeModal) ? "waiting_approval" : "starting";
85231
+ case "stopped":
85232
+ case "disconnected":
85233
+ case "not_monitored":
85234
+ return "error";
85235
+ case "waiting_approval":
85236
+ return hasNonEmptyModalButtons(activeModal) ? "waiting_approval" : "generating";
85237
+ default:
85238
+ return raw;
85239
+ }
85240
+ }
85241
+ function finalizeStreamingMessagesWhenIdle(messages, status) {
85242
+ if (status !== "idle") return messages;
85243
+ return messages.map((message) => {
85244
+ const meta3 = message.meta && typeof message.meta === "object" ? message.meta : void 0;
85245
+ const hasStreamingMeta = meta3?.streaming === true;
85246
+ if (message.bubbleState !== "streaming" && !hasStreamingMeta) return message;
85247
+ return {
85248
+ ...message,
85249
+ ...message.bubbleState === "streaming" ? { bubbleState: "final" } : {},
85250
+ ...hasStreamingMeta ? { meta: { ...meta3, streaming: false } } : {}
85251
+ };
85252
+ });
85253
+ }
85254
+ function collapseAdjacentDuplicateChatMessages(messages) {
85255
+ if (!Array.isArray(messages) || messages.length <= 1) return messages;
85256
+ const result = [];
85257
+ let prevRoleKind = "";
85258
+ let prevStripped = "";
85259
+ for (const message of messages) {
85260
+ const role = typeof message.role === "string" ? message.role : "";
85261
+ const kind = typeof message.kind === "string" ? message.kind : "standard";
85262
+ const content = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.map((p) => typeof p?.text === "string" ? p.text : "").join("") : "";
85263
+ const strippedContent = content.replace(/\s+/g, "");
85264
+ if (!strippedContent || role === "system") {
85265
+ result.push(message);
85266
+ prevRoleKind = "";
85267
+ prevStripped = "";
85268
+ continue;
85269
+ }
85270
+ const roleKind = `${role}:${kind}`;
85271
+ const sameStripped = strippedContent === prevStripped && roleKind === prevRoleKind;
85272
+ if (result.length > 0 && sameStripped) {
85273
+ result[result.length - 1] = message;
85274
+ prevRoleKind = roleKind;
85275
+ prevStripped = strippedContent;
85276
+ continue;
85277
+ }
85278
+ result.push(message);
85279
+ prevRoleKind = roleKind;
85280
+ prevStripped = strippedContent;
85281
+ }
85282
+ return result;
85283
+ }
85284
+ function buildReadChatCommandResult(payload, args, h) {
85285
+ let validatedPayload;
85286
+ const debugReadChat = payload?.debugReadChat && typeof payload.debugReadChat === "object" ? payload.debugReadChat : void 0;
85287
+ const presentationSessionIdHint = typeof args?.targetSessionId === "string" && args.targetSessionId.trim() ? args.targetSessionId.trim() : typeof args?.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : typeof h?.currentSession?.sessionId === "string" ? String(h.currentSession.sessionId) : "";
85288
+ const providerHint = typeof args?.cliType === "string" ? args.cliType : typeof args?.providerType === "string" ? args.providerType : typeof args?.agentType === "string" ? args.agentType : "";
85289
+ const legacyStatus = normalizeReadChatCommandStatus(payload?.status, payload?.activeModal);
85290
+ const turnPresentation = resolveSessionTurnPresentation({
85291
+ sessionId: presentationSessionIdHint || void 0,
85292
+ legacyStatus,
85293
+ providerType: providerHint || void 0,
85294
+ surface: "read_chat"
85295
+ });
85296
+ let effectiveStatus = legacyStatus;
85297
+ if (turnPresentation.authority === "turn_reducer") {
85298
+ effectiveStatus = turnPresentation.status;
85299
+ if (effectiveStatus === "waiting_approval" && !hasNonEmptyModalButtons(payload?.activeModal)) {
85300
+ effectiveStatus = "generating";
85301
+ }
85302
+ }
85303
+ try {
85304
+ validatedPayload = validateReadChatResultPayload({
85305
+ ...payload,
85306
+ status: effectiveStatus
85307
+ }, "read_chat command result");
85308
+ } catch (error48) {
85309
+ return { success: false, error: error48?.message || String(error48) };
85310
+ }
85311
+ const messages = normalizeReadChatMessages(validatedPayload);
85312
+ const sessionIdHint = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : "";
85313
+ const filteredMessages = h ? maybeHideCoordinatorPromptMessage(h, providerHint, sessionIdHint, messages) : messages;
85314
+ const includeActivity = args?.includeActivity === true || args?.includeActivity === "true";
85315
+ const visibleMessages = includeActivity ? filteredMessages.filter((m) => isUserFacingChatMessage(m) || isActivityChatMessage(m)) : filterUserFacingChatMessages(filteredMessages);
85316
+ const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
85317
+ const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
85318
+ const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key2]) => shouldPreserveReadChatPayloadField(key2)));
85319
+ if (preservedPayloadFields.messageSource) {
85320
+ preservedPayloadFields.messageSource = updateMessageSourceReturnedCount(preservedPayloadFields.messageSource, sync.messages.length);
85321
+ }
85322
+ if (preservedPayloadFields.transcriptProvenance) {
85323
+ preservedPayloadFields.transcriptProvenance = updateMessageSourceReturnedCount(preservedPayloadFields.transcriptProvenance, sync.messages.length);
85324
+ }
85325
+ const returnedDebugReadChat = debugReadChat ? {
85326
+ ...debugReadChat,
85327
+ fullMsgCount: typeof debugReadChat.fullMsgCount === "number" ? debugReadChat.fullMsgCount : messages.length,
85328
+ visibleMsgCount: visibleMessages.length,
85329
+ hiddenMsgCount,
85330
+ returnedMsgCount: sync.messages.length
85331
+ } : void 0;
85332
+ return {
85333
+ success: true,
85334
+ ...validatedPayload,
85335
+ ...preservedPayloadFields,
85336
+ messages: sync.messages,
85337
+ totalMessages: sync.totalMessages,
85338
+ // Stage 6: the authoritative turn presentation rides the read_chat
85339
+ // payload for mesh-owned sessions (identity + stage + evidence
85340
+ // timestamps; the `status` field above already reflects it).
85341
+ ...turnPresentation.authority === "turn_reducer" ? { turn: turnPresentation } : {},
85342
+ ...returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}
85343
+ };
85344
+ }
84575
85345
  var HOT_TAIL_MIN_LIMIT = 60;
84576
- var CLI_NATIVE_TRANSCRIPT_PROVIDERS = /* @__PURE__ */ new Set(["codex-cli", "claude-cli", "hermes-cli", "antigravity-cli"]);
84577
85346
  var lastBoundProviderSessionIdByMeshSession = /* @__PURE__ */ new Map();
84578
85347
  var persistedProviderSessionPinsHydrated = false;
84579
85348
  function hydratePersistedProviderSessionPinsOnce() {
@@ -84609,14 +85378,6 @@ ${effect.notification.body || ""}`.trim();
84609
85378
  const pinned = lastBoundProviderSessionIdByMeshSession.get(key2);
84610
85379
  return pinned && pinned.trim() ? pinned.trim() : void 0;
84611
85380
  }
84612
- var warnedLegacyNativeAllowlistHits = /* @__PURE__ */ new Set();
84613
- function warnLegacyNativeAllowlistHit(providerType) {
84614
- if (warnedLegacyNativeAllowlistHits.has(providerType)) return;
84615
- warnedLegacyNativeAllowlistHits.add(providerType);
84616
- console.warn(
84617
- `[chat-commands] supportsCliNativeTranscript fell back to the hardcoded CLI_NATIVE_TRANSCRIPT_PROVIDERS set for "${providerType}". The provider module was unavailable or did not declare canonicalHistory. Set canonicalHistory.contractVersion in the provider.json to remove this dependency.`
84618
- );
84619
- }
84620
85381
  function getExplicitHistorySessionId(args) {
84621
85382
  const explicit = typeof args?.historySessionId === "string" ? args.historySessionId.trim() : "";
84622
85383
  if (explicit) return explicit;
@@ -84695,22 +85456,6 @@ ${effect.notification.body || ""}`.trim();
84695
85456
  payload: options.payload
84696
85457
  });
84697
85458
  }
84698
- function normalizeReadChatTailLimit(args) {
84699
- const value = Number(args?.tailLimit || 0);
84700
- return Number.isFinite(value) ? Math.max(0, value) : 0;
84701
- }
84702
- function normalizeReadChatMessages(payload) {
84703
- const messages = Array.isArray(payload.messages) ? payload.messages : [];
84704
- return normalizeChatMessages(messages);
84705
- }
84706
- function getMessageNewestReceivedAt(messages) {
84707
- let newest = 0;
84708
- for (const message of messages) {
84709
- const receivedAt = Number(message?.receivedAt ?? message?.timestamp ?? 0);
84710
- if (Number.isFinite(receivedAt) && receivedAt > newest) newest = receivedAt;
84711
- }
84712
- return newest;
84713
- }
84714
85459
  function readHistorySessionIdFromMessages(messages) {
84715
85460
  for (const message of messages) {
84716
85461
  const historySessionId = typeof message?.historySessionId === "string" ? message.historySessionId.trim() : "";
@@ -84731,35 +85476,6 @@ ${effect.notification.body || ""}`.trim();
84731
85476
  }
84732
85477
  return true;
84733
85478
  }
84734
- function maybeHideCoordinatorPromptMessage(h, providerType, sessionId, messages) {
84735
- if (!Array.isArray(messages) || messages.length === 0) return messages;
84736
- if (!sessionId) return messages;
84737
- const loader2 = h.ctx?.providerLoader;
84738
- if (!loader2) return messages;
84739
- let showSetting = void 0;
84740
- try {
84741
- showSetting = loader2.getSettingValue?.(providerType, "showCoordinatorSystemPrompt");
84742
- } catch {
84743
- }
84744
- if (showSetting === true) return messages;
84745
- const coord = getCoordinatorForSession(sessionId);
84746
- if (!coord) return messages;
84747
- const promptBody = typeof coord.systemPrompt === "string" ? coord.systemPrompt : "";
84748
- const MARKER = "adhdev-mesh-coordinator-prompt";
84749
- const filtered = messages.filter((m) => {
84750
- const role = String(m?.role || "").toLowerCase();
84751
- if (role !== "user" && role !== "system") return true;
84752
- const content = flattenContent(m?.content);
84753
- if (!content) return true;
84754
- if (content.includes(MARKER)) return false;
84755
- if (promptBody && content.includes(promptBody.slice(0, Math.min(400, promptBody.length)))) return false;
84756
- return true;
84757
- });
84758
- if (filtered.length !== messages.length) {
84759
- LOG2.debug("ChatFilter", `[${providerType}] hid ${messages.length - filtered.length} coordinator-prompt message(s) from ${sessionId}`);
84760
- }
84761
- return filtered;
84762
- }
84763
85479
  function normalizeAndFilterNativeHistory(h, providerType, args, messages, nativeSessionId) {
84764
85480
  const normalized = normalizeNativeHistoryMessages(providerType, messages, nativeSessionId);
84765
85481
  const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : void 0;
@@ -84833,212 +85549,6 @@ ${effect.notification.body || ""}`.trim();
84833
85549
  };
84834
85550
  });
84835
85551
  }
84836
- function buildCliMessageSourceProvenance(args) {
84837
- const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
84838
- const sourceMtimeAgeMs = sourceMtimeMs > 0 ? Math.max(0, Date.now() - sourceMtimeMs) : void 0;
84839
- const nativeMessages = args.nativeMessages || [];
84840
- const ptyMessages = args.ptyMessages || [];
84841
- const returnedMessages = args.returnedMessages || [];
84842
- const identityStatus = args.selected === "native-history" ? "safe" : args.fallbackReason === "native_history_not_safely_mapped" ? "ambiguous_session_identity" : args.fallbackReason?.startsWith("native_history_unavailable") ? "transcript_unmapped" : void 0;
84843
- return {
84844
- selected: args.selected,
84845
- provider: args.provider,
84846
- providerType: args.provider,
84847
- ...identityStatus ? { identityStatus } : {},
84848
- ...args.nativeHandle ? { nativeHandle: args.nativeHandle } : {},
84849
- ...args.nativeHandle ? { nativeSessionId: args.nativeHandle } : {},
84850
- ...args.sessionWorkspace ? { sessionWorkspace: args.sessionWorkspace } : {},
84851
- ...args.intendedWorkspace ? { intendedWorkspace: args.intendedWorkspace } : {},
84852
- ...args.transcriptWorkspace ? { transcriptWorkspace: args.transcriptWorkspace } : {},
84853
- ...args.fallbackReason ? { fallbackReason: args.fallbackReason } : {},
84854
- ...args.nativeSource ? { nativeSource: args.nativeSource } : {},
84855
- ...args.sourcePath ? { sourcePath: args.sourcePath } : {},
84856
- ...args.nativeHistoryCoverage ? { nativeHistoryCoverage: args.nativeHistoryCoverage } : {},
84857
- ...args.partialReason ? { partialReason: args.partialReason } : {},
84858
- ...args.unavailableReason ? { unavailableReason: args.unavailableReason } : {},
84859
- ptyStatusApprovalOnly: args.ptyStatusApprovalOnly === true,
84860
- staleness: {
84861
- sourceMtimeMs: sourceMtimeMs || void 0,
84862
- sourceMtimeAgeMs,
84863
- nativeNewestMessageAt: getMessageNewestReceivedAt(nativeMessages),
84864
- ptyNewestMessageAt: getMessageNewestReceivedAt(ptyMessages),
84865
- freshEnough: args.freshEnough === true
84866
- },
84867
- coverage: {
84868
- nativeMessageCount: nativeMessages.length,
84869
- ptyMessageCount: ptyMessages.length,
84870
- returnedMessageCount: returnedMessages.length,
84871
- safeMapping: args.safeMapping === true,
84872
- // true when PTY message bodies are suppressed and must not be treated as
84873
- // chat content. PTY may still contribute status/approval/screen evidence.
84874
- ptyMessagesSuppressed: args.selected === "native-history" || args.ptyStatusApprovalOnly === true
84875
- }
84876
- };
84877
- }
84878
- function causeToLegacyFallbackReason(cause, selected, extraDetail) {
84879
- if (selected === "native-history") return void 0;
84880
- switch (cause) {
84881
- case "initial":
84882
- return "native_history_not_checked";
84883
- case "native_progressed":
84884
- return "native_history_not_selected";
84885
- case "native_regressed_shrunk":
84886
- return "native_history_empty";
84887
- case "native_regressed_unsafe_mapping":
84888
- return "native_history_not_safely_mapped";
84889
- case "native_regressed_coverage_partial":
84890
- return "native_history_partial";
84891
- case "native_regressed_coverage_unavailable":
84892
- return "native_history_unavailable";
84893
- case "native_unavailable_read_error":
84894
- return extraDetail?.unavailableReason ? `native_history_unavailable:${extraDetail.unavailableReason}` : "native_history_unavailable";
84895
- case "native_unavailable_provider_unsupported":
84896
- return "provider_native_transcript_not_supported";
84897
- case "native_unavailable_empty":
84898
- return "native_history_empty";
84899
- case "native_unavailable_not_native_source":
84900
- return extraDetail?.nativeSource ? `native_history_source_${extraDetail.nativeSource}` : "native_history_unavailable";
84901
- }
84902
- }
84903
- function decideCliReadChatSource(args) {
84904
- const supportsNative = supportsCliNativeTranscript(args.providerType, args.provider);
84905
- const observation = buildObservationForCli(args, supportsNative);
84906
- const sessionKey = chatSourceSessionKey(args.providerType, args.sessionId);
84907
- const priorSnapshot = CHAT_SOURCE_REGISTRY.snapshotRecord(sessionKey);
84908
- const priorState = priorSnapshot?.state ?? CHAT_SOURCE_REGISTRY.getState(sessionKey);
84909
- const eligibleForStickyHold = args.trustedExactNativeIdentity === true && (priorState.name === "NativeLocked" || priorState.name === "Recovering");
84910
- let decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
84911
- const heldNativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
84912
- const mayHoldNativeSlice = heldNativeMessages.length > 0 && args.safeMapping === true;
84913
- if (eligibleForStickyHold && decision.selected === "pty-parser" && mayHoldNativeSlice) {
84914
- CHAT_SOURCE_REGISTRY.restoreRecord(sessionKey, priorSnapshot);
84915
- const messageSource2 = buildCliMessageSourceProvenance({
84916
- selected: "native-history",
84917
- provider: args.providerType,
84918
- nativeHandle: typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0,
84919
- sessionWorkspace: args.sessionWorkspace,
84920
- intendedWorkspace: args.intendedWorkspace,
84921
- transcriptWorkspace: void 0,
84922
- fallbackReason: "native_history_transient_gap_held",
84923
- nativeSource: "provider-native",
84924
- sourcePath: typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0,
84925
- sourceMtimeMs: typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0,
84926
- nativeHistoryCoverage: void 0,
84927
- partialReason: void 0,
84928
- unavailableReason: observation.kind === "native_unavailable" ? observation.reason : void 0,
84929
- nativeMessages: heldNativeMessages,
84930
- ptyMessages: args.ptyMessages,
84931
- returnedMessages: heldNativeMessages,
84932
- safeMapping: args.safeMapping,
84933
- freshEnough: true,
84934
- ptyStatusApprovalOnly: true
84935
- });
84936
- return {
84937
- decision: {
84938
- selected: "native-history",
84939
- nextState: priorState,
84940
- transition: {
84941
- fromState: priorState.name,
84942
- toState: priorState.name,
84943
- event: "NoOp",
84944
- cause: decision.transition.cause,
84945
- at: Date.now()
84946
- },
84947
- lockState: { locked: priorState.name === "NativeLocked" }
84948
- },
84949
- messageSource: messageSource2,
84950
- nativeMessages: heldNativeMessages,
84951
- nativeSelected: true
84952
- };
84953
- }
84954
- if (decision.selected === "pty-parser" && args.trustedExactNativeIdentity === true && args.safeMapping && args.ptyMessages.length === 0 && observation.kind === "native_present" && observation.coverage !== "partial" && observation.messages.length > 0) {
84955
- CHAT_SOURCE_REGISTRY.clear(sessionKey);
84956
- decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
84957
- }
84958
- const nativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
84959
- const nativeSource = typeof args.nativeHistoryResult?.source === "string" ? args.nativeHistoryResult.source : void 0;
84960
- const sourcePath = typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0;
84961
- const sourceMtimeMs = typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0;
84962
- const coverageHint = typeof args.nativeHistoryResult?.nativeHistoryCoverage === "string" ? args.nativeHistoryResult.nativeHistoryCoverage : void 0;
84963
- const partialReason = typeof args.nativeHistoryResult?.partialReason === "string" ? args.nativeHistoryResult.partialReason : void 0;
84964
- const unavailableReason = typeof args.nativeHistoryResult?.unavailableReason === "string" ? args.nativeHistoryResult.unavailableReason : args.nativeHistoryError ? `error:${args.nativeHistoryError?.message || String(args.nativeHistoryError)}` : void 0;
84965
- const nativeHandle = typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0;
84966
- const transcriptWorkspace = typeof args.nativeHistoryResult?.workspace === "string" ? args.nativeHistoryResult.workspace : nativeMessages.map((m) => typeof m?.workspace === "string" ? m.workspace.trim() : "").find(Boolean);
84967
- const fallbackReason = causeToLegacyFallbackReason(decision.transition.cause, decision.selected, {
84968
- unavailableReason,
84969
- nativeSource: nativeSource && nativeSource !== "provider-native" ? nativeSource : void 0
84970
- });
84971
- const ptyStatusApprovalOnly = decision.selected === "native-history" ? true : args.ptyStatusApprovalOnly;
84972
- const messageSource = buildCliMessageSourceProvenance({
84973
- selected: decision.selected,
84974
- provider: args.providerType,
84975
- nativeHandle,
84976
- sessionWorkspace: args.sessionWorkspace,
84977
- intendedWorkspace: args.intendedWorkspace,
84978
- transcriptWorkspace,
84979
- fallbackReason,
84980
- nativeSource,
84981
- sourcePath,
84982
- sourceMtimeMs,
84983
- nativeHistoryCoverage: coverageHint,
84984
- partialReason,
84985
- unavailableReason,
84986
- nativeMessages,
84987
- ptyMessages: args.ptyMessages,
84988
- returnedMessages: decision.selected === "native-history" ? nativeMessages : args.ptyMessages,
84989
- safeMapping: args.safeMapping,
84990
- // freshEnough is a v1 concept the machine does not model directly.
84991
- // We surface lockState.locked here so v1 consumers reading
84992
- // staleness.freshEnough still get a meaningful boolean.
84993
- freshEnough: decision.lockState.locked,
84994
- ptyStatusApprovalOnly
84995
- });
84996
- return {
84997
- decision,
84998
- messageSource,
84999
- nativeMessages,
85000
- nativeSelected: decision.selected === "native-history"
85001
- };
85002
- }
85003
- function buildObservationForCli(args, supportsNative) {
85004
- if (!supportsNative) {
85005
- return { kind: "native_unavailable", reason: "provider_not_supported" };
85006
- }
85007
- if (args.nativeHistoryError) {
85008
- return { kind: "native_unavailable", reason: "read_error" };
85009
- }
85010
- const result = args.nativeHistoryResult;
85011
- if (!result || typeof result !== "object") {
85012
- return { kind: "native_unavailable", reason: "read_error" };
85013
- }
85014
- const source = typeof result.source === "string" ? result.source : "";
85015
- if (source && source !== "provider-native") {
85016
- return { kind: "native_unavailable", reason: source === "native-unavailable" ? "empty" : "not_native_source" };
85017
- }
85018
- const messages = Array.isArray(result.messages) ? result.messages : [];
85019
- if (messages.length === 0) {
85020
- return { kind: "native_unavailable", reason: "empty" };
85021
- }
85022
- const coverage = typeof result.nativeHistoryCoverage === "string" ? result.nativeHistoryCoverage : "tail";
85023
- if (coverage === "unavailable") {
85024
- return { kind: "native_unavailable", reason: "coverage_unavailable" };
85025
- }
85026
- return buildV1NativePresentObservation({
85027
- providerType: args.providerType,
85028
- sessionId: args.sessionId,
85029
- messages,
85030
- coverage: coverage === "full" || coverage === "tail" || coverage === "current-turn" || coverage === "partial" ? coverage : "tail",
85031
- safeMapping: args.safeMapping
85032
- });
85033
- }
85034
- function extractNativeMessagesFromResult(providerType, result) {
85035
- if (!result || !Array.isArray(result.messages)) return [];
85036
- return normalizeNativeHistoryMessages(
85037
- providerType,
85038
- result.messages,
85039
- typeof result.providerSessionId === "string" ? result.providerSessionId : void 0
85040
- );
85041
- }
85042
85552
  function applyUnsafeNativeDaemonFallback(args) {
85043
85553
  if (args.adapter.cliType !== "codex-cli") {
85044
85554
  return;
@@ -85210,16 +85720,6 @@ ${effect.notification.body || ""}`.trim();
85210
85720
  const start = Math.max(0, end - args.limit);
85211
85721
  return { messages: ptyMessages.slice(start, end), hasMore: start > 0 };
85212
85722
  }
85213
- function supportsCliNativeTranscript(providerType, provider) {
85214
- if (provider?.category === "cli" && isNativeSourceCanonicalHistory(provider?.nativeHistory)) {
85215
- return true;
85216
- }
85217
- if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) {
85218
- warnLegacyNativeAllowlistHit(providerType);
85219
- return true;
85220
- }
85221
- return false;
85222
- }
85223
85723
  function getComparableVisibleText(message) {
85224
85724
  if (!message) return "";
85225
85725
  const role = String(message.role || "").trim().toLowerCase();
@@ -85352,22 +85852,6 @@ ${effect.notification.body || ""}`.trim();
85352
85852
  });
85353
85853
  return { ...history, lookup: "workspace" };
85354
85854
  }
85355
- function shouldPreserveReadChatPayloadField(key2) {
85356
- return key2 === "messageSource" || key2 === "transcriptProvenance";
85357
- }
85358
- function updateMessageSourceReturnedCount(value, returnedMessageCount) {
85359
- if (!value || typeof value !== "object" || Array.isArray(value)) return value;
85360
- const record2 = value;
85361
- const coverage = record2.coverage && typeof record2.coverage === "object" && !Array.isArray(record2.coverage) ? record2.coverage : void 0;
85362
- if (!coverage) return value;
85363
- return {
85364
- ...record2,
85365
- coverage: {
85366
- ...coverage,
85367
- returnedMessageCount
85368
- }
85369
- };
85370
- }
85371
85855
  function deriveHistoryDedupKey(message) {
85372
85856
  const unitKey = typeof message._unitKey === "string" ? message._unitKey.trim() : "";
85373
85857
  if (unitKey) return `read_chat:${unitKey}`;
@@ -85391,37 +85875,6 @@ ${effect.notification.body || ""}`.trim();
85391
85875
  historyDedupKey: deriveHistoryDedupKey(message)
85392
85876
  }));
85393
85877
  }
85394
- function buildFullTail(messages, tailLimit) {
85395
- const totalMessages = messages.length;
85396
- const tailMessages = tailLimit > 0 ? messages.slice(-tailLimit) : messages;
85397
- return {
85398
- messages: tailMessages,
85399
- totalMessages
85400
- };
85401
- }
85402
- function hasNonEmptyModalButtons(activeModal) {
85403
- if (!activeModal || typeof activeModal !== "object") return false;
85404
- const buttons = activeModal.buttons;
85405
- return Array.isArray(buttons) && buttons.some((button) => typeof button === "string" && button.trim().length > 0);
85406
- }
85407
- function normalizeReadChatCommandStatus(status, activeModal) {
85408
- const raw = typeof status === "string" ? status.trim() : "";
85409
- if (!raw) {
85410
- return hasNonEmptyModalButtons(activeModal) ? "waiting_approval" : "idle";
85411
- }
85412
- switch (raw) {
85413
- case "starting":
85414
- return hasNonEmptyModalButtons(activeModal) ? "waiting_approval" : "starting";
85415
- case "stopped":
85416
- case "disconnected":
85417
- case "not_monitored":
85418
- return "error";
85419
- case "waiting_approval":
85420
- return hasNonEmptyModalButtons(activeModal) ? "waiting_approval" : "generating";
85421
- default:
85422
- return raw;
85423
- }
85424
- }
85425
85878
  function isGeneratingLikeStatus(status) {
85426
85879
  return status === "generating" || status === "streaming" || status === "no_progress" || status === "long_generating" || status === "starting";
85427
85880
  }
@@ -85461,110 +85914,6 @@ ${effect.notification.body || ""}`.trim();
85461
85914
  if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return "idle";
85462
85915
  return typeof parsedStatus === "string" && parsedStatus.trim() ? parsedStatus : "idle";
85463
85916
  }
85464
- function finalizeStreamingMessagesWhenIdle(messages, status) {
85465
- if (status !== "idle") return messages;
85466
- return messages.map((message) => {
85467
- const meta3 = message.meta && typeof message.meta === "object" ? message.meta : void 0;
85468
- const hasStreamingMeta = meta3?.streaming === true;
85469
- if (message.bubbleState !== "streaming" && !hasStreamingMeta) return message;
85470
- return {
85471
- ...message,
85472
- ...message.bubbleState === "streaming" ? { bubbleState: "final" } : {},
85473
- ...hasStreamingMeta ? { meta: { ...meta3, streaming: false } } : {}
85474
- };
85475
- });
85476
- }
85477
- function collapseAdjacentDuplicateChatMessages(messages) {
85478
- if (!Array.isArray(messages) || messages.length <= 1) return messages;
85479
- const result = [];
85480
- let prevRoleKind = "";
85481
- let prevStripped = "";
85482
- for (const message of messages) {
85483
- const role = typeof message.role === "string" ? message.role : "";
85484
- const kind = typeof message.kind === "string" ? message.kind : "standard";
85485
- const content = typeof message.content === "string" ? message.content : Array.isArray(message.content) ? message.content.map((p) => typeof p?.text === "string" ? p.text : "").join("") : "";
85486
- const strippedContent = content.replace(/\s+/g, "");
85487
- if (!strippedContent || role === "system") {
85488
- result.push(message);
85489
- prevRoleKind = "";
85490
- prevStripped = "";
85491
- continue;
85492
- }
85493
- const roleKind = `${role}:${kind}`;
85494
- const sameStripped = strippedContent === prevStripped && roleKind === prevRoleKind;
85495
- if (result.length > 0 && sameStripped) {
85496
- result[result.length - 1] = message;
85497
- prevRoleKind = roleKind;
85498
- prevStripped = strippedContent;
85499
- continue;
85500
- }
85501
- result.push(message);
85502
- prevRoleKind = roleKind;
85503
- prevStripped = strippedContent;
85504
- }
85505
- return result;
85506
- }
85507
- function buildReadChatCommandResult(payload, args, h) {
85508
- let validatedPayload;
85509
- const debugReadChat = payload?.debugReadChat && typeof payload.debugReadChat === "object" ? payload.debugReadChat : void 0;
85510
- const presentationSessionIdHint = typeof args?.targetSessionId === "string" && args.targetSessionId.trim() ? args.targetSessionId.trim() : typeof args?.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : typeof h?.currentSession?.sessionId === "string" ? String(h.currentSession.sessionId) : "";
85511
- const providerHint = typeof args?.cliType === "string" ? args.cliType : typeof args?.providerType === "string" ? args.providerType : typeof args?.agentType === "string" ? args.agentType : "";
85512
- const legacyStatus = normalizeReadChatCommandStatus(payload?.status, payload?.activeModal);
85513
- const turnPresentation = resolveSessionTurnPresentation({
85514
- sessionId: presentationSessionIdHint || void 0,
85515
- legacyStatus,
85516
- providerType: providerHint || void 0,
85517
- surface: "read_chat"
85518
- });
85519
- let effectiveStatus = legacyStatus;
85520
- if (turnPresentation.authority === "turn_reducer") {
85521
- effectiveStatus = turnPresentation.status;
85522
- if (effectiveStatus === "waiting_approval" && !hasNonEmptyModalButtons(payload?.activeModal)) {
85523
- effectiveStatus = "generating";
85524
- }
85525
- }
85526
- try {
85527
- validatedPayload = validateReadChatResultPayload({
85528
- ...payload,
85529
- status: effectiveStatus
85530
- }, "read_chat command result");
85531
- } catch (error48) {
85532
- return { success: false, error: error48?.message || String(error48) };
85533
- }
85534
- const messages = normalizeReadChatMessages(validatedPayload);
85535
- const sessionIdHint = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : "";
85536
- const filteredMessages = h ? maybeHideCoordinatorPromptMessage(h, providerHint, sessionIdHint, messages) : messages;
85537
- const includeActivity = args?.includeActivity === true || args?.includeActivity === "true";
85538
- const visibleMessages = includeActivity ? filteredMessages.filter((m) => isUserFacingChatMessage(m) || isActivityChatMessage(m)) : filterUserFacingChatMessages(filteredMessages);
85539
- const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
85540
- const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
85541
- const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key2]) => shouldPreserveReadChatPayloadField(key2)));
85542
- if (preservedPayloadFields.messageSource) {
85543
- preservedPayloadFields.messageSource = updateMessageSourceReturnedCount(preservedPayloadFields.messageSource, sync.messages.length);
85544
- }
85545
- if (preservedPayloadFields.transcriptProvenance) {
85546
- preservedPayloadFields.transcriptProvenance = updateMessageSourceReturnedCount(preservedPayloadFields.transcriptProvenance, sync.messages.length);
85547
- }
85548
- const returnedDebugReadChat = debugReadChat ? {
85549
- ...debugReadChat,
85550
- fullMsgCount: typeof debugReadChat.fullMsgCount === "number" ? debugReadChat.fullMsgCount : messages.length,
85551
- visibleMsgCount: visibleMessages.length,
85552
- hiddenMsgCount,
85553
- returnedMsgCount: sync.messages.length
85554
- } : void 0;
85555
- return {
85556
- success: true,
85557
- ...validatedPayload,
85558
- ...preservedPayloadFields,
85559
- messages: sync.messages,
85560
- totalMessages: sync.totalMessages,
85561
- // Stage 6: the authoritative turn presentation rides the read_chat
85562
- // payload for mesh-owned sessions (identity + stage + evidence
85563
- // timestamps; the `status` field above already reflects it).
85564
- ...turnPresentation.authority === "turn_reducer" ? { turn: turnPresentation } : {},
85565
- ...returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}
85566
- };
85567
- }
85568
85917
  function toNonNegativeNumber(value) {
85569
85918
  const numeric = Number(value ?? 0);
85570
85919
  return Number.isFinite(numeric) ? Math.max(0, numeric) : 0;
@@ -95473,7 +95822,7 @@ ${marker}`,
95473
95822
  LOG2.warn("spec-route", `[${provider.type}] spec invalid, falling back to ProviderCliAdapter: ${err.message}`);
95474
95823
  }
95475
95824
  }
95476
- return new ProviderCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFactory);
95825
+ return new ProviderCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFactory, sessionId);
95477
95826
  }
95478
95827
  init_chat_history();
95479
95828
  init_logger();
@@ -102801,7 +103150,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
102801
103150
  const { shouldDeferDispatchForBootstrap: shouldDeferDispatchForBootstrap2 } = await Promise.resolve().then(() => (init_worktree_bootstrap_config(), worktree_bootstrap_config_exports));
102802
103151
  if (shouldDeferDispatchForBootstrap2(nodeObj)) {
102803
103152
  const dispatchSessionState = dispatchSessionId ? ctx.deps.instanceManager?.getInstance?.(dispatchSessionId)?.getState?.() : void 0;
102804
- const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState(dispatchSessionState);
103153
+ const sessionConfirmedReady = !!dispatchSessionState && isIdleSessionState2(dispatchSessionState);
102805
103154
  if (!sessionConfirmedReady) {
102806
103155
  return {
102807
103156
  success: false,
@@ -110641,7 +110990,8 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
110641
110990
  drain: { ...getMeshV2DrainCounters() },
110642
110991
  backstop: { ...getMeshV2BackstopCounters() }
110643
110992
  };
110644
- return { success: true, events, hasLiveCliCoordinator, meshProtocolV2Counters, ...selfCoordinatorInboxRead ? { surfacedForSelfCoordinator: true } : {} };
110993
+ const pendingRetentionCounters2 = { ...getPendingRetentionCounters() };
110994
+ return { success: true, events, hasLiveCliCoordinator, meshProtocolV2Counters, pendingRetentionCounters: pendingRetentionCounters2, ...selfCoordinatorInboxRead ? { surfacedForSelfCoordinator: true } : {} };
110645
110995
  },
110646
110996
  interactive_prompt_response: async (ctx, args) => {
110647
110997
  const sessionId = typeof args?.targetSessionId === "string" && args.targetSessionId.trim() ? args.targetSessionId.trim() : typeof args?.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : "";
@@ -111798,6 +112148,7 @@ ${ptyResult.output.slice(-2e3)}`);
111798
112148
  drain: { ...getMeshV2DrainCounters() },
111799
112149
  backstop: { ...getMeshV2BackstopCounters() }
111800
112150
  };
112151
+ const pendingRetentionCounters2 = { ...getPendingRetentionCounters() };
111801
112152
  const turnPresentationCounters = getTurnPresentationMetrics();
111802
112153
  const previewFreshness = (() => {
111803
112154
  const localRepoRoot = nodeStatuses.map((node) => readStringValue(node?.git?.repoRoot, node?.repoRoot, node?.workspace)).find((candidate) => !!candidate && fs43.existsSync(candidate));
@@ -111889,6 +112240,7 @@ ${ptyResult.output.slice(-2e3)}`);
111889
112240
  ...pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {},
111890
112241
  ...unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {},
111891
112242
  meshProtocolV2Counters,
112243
+ pendingRetentionCounters: pendingRetentionCounters2,
111892
112244
  turnPresentationCounters,
111893
112245
  activeRefineJobs: Array.from(ctx.runningRefineJobs.values()).filter((job) => job.meshId === meshId).map((job) => ({
111894
112246
  jobId: job.jobId,
@@ -111899,13 +112251,14 @@ ${ptyResult.output.slice(-2e3)}`);
111899
112251
  targetCoordinatorDaemonId: job.targetCoordinatorDaemonId
111900
112252
  }))
111901
112253
  };
111902
- const { pendingCoordinatorEvents: _pendingCoordinatorEvents, unroutableDeliveries: _unroutableDeliveries, meshProtocolV2Counters: _meshProtocolV2Counters, turnPresentationCounters: _turnPresentationCounters, ...cacheableStatusResult } = statusResult;
112254
+ const { pendingCoordinatorEvents: _pendingCoordinatorEvents, unroutableDeliveries: _unroutableDeliveries, meshProtocolV2Counters: _meshProtocolV2Counters, pendingRetentionCounters: _pendingRetentionCounters, turnPresentationCounters: _turnPresentationCounters, ...cacheableStatusResult } = statusResult;
111903
112255
  const rememberedStatus = verboseMissions ? cacheableStatusResult : ctx.rememberAggregateMeshStatus(meshId, cacheableStatusResult, refreshReason);
111904
112256
  const returnedStatus = {
111905
112257
  ...rememberedStatus,
111906
112258
  ...pendingCoordinatorEvents.length > 0 ? { pendingCoordinatorEvents } : {},
111907
112259
  ...unroutableDeliveries.length > 0 ? { unroutableDeliveries } : {},
111908
112260
  meshProtocolV2Counters,
112261
+ pendingRetentionCounters: pendingRetentionCounters2,
111909
112262
  turnPresentationCounters
111910
112263
  };
111911
112264
  logRepoMeshStatusDebug("return_live", {
@@ -115770,17 +116123,27 @@ ${e?.stderr || ""}`;
115770
116123
  }
115771
116124
  async function startMeshRefineJob(self, meshId, nodeId, args) {
115772
116125
  const key2 = buildRefineJobKey(self, meshId, nodeId);
115773
- const running = self.runningRefineJobs.get(key2);
115774
- if (running) return { ...running, duplicate: true };
115775
116126
  const terminal = self.terminalRefineJobs.get(key2);
116127
+ const alreadyRunning = self.runningRefineJobs.get(key2);
116128
+ if (alreadyRunning) return { ...alreadyRunning, duplicate: true };
116129
+ const jobId = `refine_${createInteractionId()}`;
116130
+ const interactionId = createInteractionId();
116131
+ const placeholder = buildRefineJobHandle(self, { meshId, nodeId, jobId, interactionId, retryOfJobId: terminal?.jobId });
116132
+ self.runningRefineJobs.set(key2, placeholder);
115776
116133
  const meshRecord = await self.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
115777
116134
  const mesh = meshRecord?.mesh;
115778
116135
  const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
115779
- if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh` };
115780
- if (!node.isLocalWorktree || !node.workspace) return { success: false, error: `Refinery requires a local worktree node` };
116136
+ if (!node) {
116137
+ self.runningRefineJobs.delete(key2);
116138
+ return { success: false, error: `Node '${nodeId}' not found in mesh` };
116139
+ }
116140
+ if (!node.isLocalWorktree || !node.workspace) {
116141
+ self.runningRefineJobs.delete(key2);
116142
+ return { success: false, error: `Refinery requires a local worktree node` };
116143
+ }
115781
116144
  const coordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : self.deps.statusInstanceId || void 0;
115782
116145
  const coordinatorSessionId = typeof args?.coordinatorSessionId === "string" && args.coordinatorSessionId.trim() ? args.coordinatorSessionId.trim() : void 0;
115783
- const handle = buildRefineJobHandle(self, { meshId, nodeId, node, retryOfJobId: terminal?.jobId, coordinatorDaemonId, coordinatorSessionId });
116146
+ const handle = buildRefineJobHandle(self, { meshId, nodeId, node, jobId, interactionId, retryOfJobId: terminal?.jobId, coordinatorDaemonId, coordinatorSessionId });
115784
116147
  self.runningRefineJobs.set(key2, handle);
115785
116148
  await appendRefineJobLedger(self, "task_dispatched", handle);
115786
116149
  queueRefineJobEvent(self, "refine:accepted", handle);
@@ -124411,8 +124774,9 @@ data: ${JSON.stringify(msg.data)}
124411
124774
  if (event.type === "session_exit") {
124412
124775
  const exitCode = typeof event.exitCode === "number" ? event.exitCode : null;
124413
124776
  const signal = typeof event.signal === "number" ? event.signal : null;
124777
+ const termination = event.termination;
124414
124778
  for (const callback of this.exitCallbacks) {
124415
- callback({ exitCode, signal });
124779
+ callback({ exitCode, signal, termination });
124416
124780
  }
124417
124781
  void this.closeClient(false);
124418
124782
  }