@adhdev/daemon-standalone 0.9.82-rc.537 → 0.9.82-rc.539

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
@@ -30208,10 +30208,10 @@ var require_dist3 = __commonJS({
30208
30208
  }
30209
30209
  function getDaemonBuildInfo() {
30210
30210
  if (cached2) return cached2;
30211
- const commit = readInjected(true ? "b47ad45cf2b2ad404d911162a9cadd7db2e274a6" : void 0) ?? "unknown";
30212
- const commitShort = readInjected(true ? "b47ad45c" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30213
- const version2 = readInjected(true ? "0.9.82-rc.537" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30214
- const builtAt = readInjected(true ? "2026-07-15T13:13:29.115Z" : void 0);
30211
+ const commit = readInjected(true ? "35ac849eca76162066561b27633e11827fa19a73" : void 0) ?? "unknown";
30212
+ const commitShort = readInjected(true ? "35ac849e" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30213
+ const version2 = readInjected(true ? "0.9.82-rc.539" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30214
+ const builtAt = readInjected(true ? "2026-07-15T15:55:46.990Z" : void 0);
30215
30215
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30216
30216
  return cached2;
30217
30217
  }
@@ -35565,6 +35565,8 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
35565
35565
  MESH_TASK_MODES: () => MESH_TASK_MODES,
35566
35566
  MESH_TASK_PRIORITIES: () => MESH_TASK_PRIORITIES,
35567
35567
  NOT_BEFORE_RELATIVE_THRESHOLD_MS: () => NOT_BEFORE_RELATIVE_THRESHOLD_MS,
35568
+ REDRIVE_RECLAIM_REASONS: () => REDRIVE_RECLAIM_REASONS,
35569
+ REDRIVE_SUPERSEDE_WINDOW_MS: () => REDRIVE_SUPERSEDE_WINDOW_MS,
35568
35570
  __clearDirectDispatchesForTests: () => __clearDirectDispatchesForTests,
35569
35571
  __clearMeshQueueForTests: () => __clearMeshQueueForTests,
35570
35572
  __replaceMeshQueueForTests: () => __replaceMeshQueueForTests,
@@ -35860,10 +35862,21 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
35860
35862
  return true;
35861
35863
  });
35862
35864
  }
35863
- function firstProviderPriority(policy) {
35864
- const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
35865
- if (!Array.isArray(raw)) return void 0;
35866
- return raw.find((type) => typeof type === "string" && type.trim())?.trim();
35865
+ function readNodeProviderTypes(policy) {
35866
+ const record2 = policy && typeof policy === "object" && !Array.isArray(policy) ? policy : {};
35867
+ const seen = /* @__PURE__ */ new Set();
35868
+ const out = [];
35869
+ const push = (type) => {
35870
+ const trimmed = typeof type === "string" ? type.trim() : "";
35871
+ if (!trimmed || seen.has(trimmed)) return;
35872
+ seen.add(trimmed);
35873
+ out.push(trimmed);
35874
+ };
35875
+ for (const slot of normalizeNodeCapabilitySlots(record2.slots)) push(slot.provider);
35876
+ if (Array.isArray(record2.providerPriority)) {
35877
+ for (const type of record2.providerPriority) push(type);
35878
+ }
35879
+ return out;
35867
35880
  }
35868
35881
  function readNodeOverride(node, key2) {
35869
35882
  const overrides = node?.userOverrides;
@@ -35876,7 +35889,8 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
35876
35889
  return typeof value === "string" && value.trim() ? value.trim() : null;
35877
35890
  }
35878
35891
  function buildMeshNodeCapabilityTags(node, providerType) {
35879
- const provider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : firstProviderPriority(node?.policy);
35892
+ const pinnedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : void 0;
35893
+ const providerTags = pinnedProvider ? [pinnedProvider] : readNodeProviderTypes(node?.policy);
35880
35894
  const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
35881
35895
  const os322 = readNodeOverride(node, "platform") ?? readNodeReporter(node, "platform") ?? process.platform;
35882
35896
  const arch2 = readNodeOverride(node, "arch") ?? readNodeReporter(node, "arch") ?? process.arch;
@@ -35884,7 +35898,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
35884
35898
  ...Array.isArray(node?.capabilities) ? node.capabilities : [],
35885
35899
  `os=${os322}`,
35886
35900
  `arch=${arch2}`,
35887
- ...provider ? [`provider=${provider}`] : [],
35901
+ ...providerTags.map((p) => `provider=${p}`),
35888
35902
  // Worktree nodes automatically expose a "worktree=<branch>" tag so that
35889
35903
  // mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
35890
35904
  // only to the matching worktree node.
@@ -36400,6 +36414,8 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
36400
36414
  var GIT_STASH_READONLY_SUBCOMMANDS;
36401
36415
  var DEPENDENCY_FAILURE_TERMINALS;
36402
36416
  var MAX_STRANDED_RECLAIMS;
36417
+ var REDRIVE_RECLAIM_REASONS;
36418
+ var REDRIVE_SUPERSEDE_WINDOW_MS;
36403
36419
  var init_mesh_work_queue = __esm2({
36404
36420
  "src/mesh/mesh-work-queue.ts"() {
36405
36421
  "use strict";
@@ -36468,6 +36484,12 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
36468
36484
  GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
36469
36485
  DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
36470
36486
  MAX_STRANDED_RECLAIMS = 3;
36487
+ REDRIVE_RECLAIM_REASONS = /* @__PURE__ */ new Set([
36488
+ "delivered_no_turn_deadline",
36489
+ "reclaim_after_unknown_grace",
36490
+ "delivered_not_consumed_redrive"
36491
+ ]);
36492
+ REDRIVE_SUPERSEDE_WINDOW_MS = 5 * 6e4;
36471
36493
  }
36472
36494
  });
36473
36495
  function loadDatabaseCtor() {
@@ -46753,7 +46775,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
46753
46775
  if (task.targetNodeId && !meshNodeIdMatches(node, task.targetNodeId)) return false;
46754
46776
  if (task.taskMode === "convergence" && node?.isLocalWorktree === true) return false;
46755
46777
  if (task.requiredTags?.length) {
46756
- const priorities = normalizeProviderPriority2(node?.policy);
46778
+ const slotProviders = resolveNodeCapabilitySlots(node).map((s2) => s2.provider).filter(Boolean);
46779
+ const priorities = slotProviders.length ? slotProviders : normalizeProviderPriority2(node?.policy);
46757
46780
  const providerCandidates = priorities.length ? priorities : [void 0];
46758
46781
  return providerCandidates.some(
46759
46782
  (p) => nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, p))
@@ -50373,6 +50396,52 @@ ${cleanBody}`;
50373
50396
  LOG2.warn("MeshQueue", `stopStaleMeshWorker error for ${sessionId}: ${e?.message || e}`);
50374
50397
  }
50375
50398
  }
50399
+ function supersedeRedriveReclaimForLateCompletion(components, meshId, row, completingSessionId, outcome, args) {
50400
+ if (!row.requeueReason || !REDRIVE_RECLAIM_REASONS.has(row.requeueReason)) return false;
50401
+ if (row.status === "completed" || row.status === "failed" || row.status === "cancelled") return false;
50402
+ const requeuedAtMs = Date.parse(row.requeuedAt ?? "");
50403
+ if (!Number.isFinite(requeuedAtMs)) return false;
50404
+ if (Date.now() - requeuedAtMs > REDRIVE_SUPERSEDE_WINDOW_MS) return false;
50405
+ const reDispatchedSessionId = row.assignedSessionId;
50406
+ if (row.status === "assigned" && reDispatchedSessionId && !sessionIdsEquivalent(reDispatchedSessionId, completingSessionId)) {
50407
+ stopStaleMeshWorker(components, {
50408
+ meshId,
50409
+ sessionId: reDispatchedSessionId,
50410
+ nodeId: row.assignedNodeId,
50411
+ providerType: row.assignedProviderType
50412
+ });
50413
+ }
50414
+ endTaskDispatchInFlight(meshId, row.id);
50415
+ updateTaskStatus(meshId, row.id, outcome === "completed" ? "completed" : "failed");
50416
+ if (!findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
50417
+ try {
50418
+ appendLedgerEntry(meshId, {
50419
+ kind: outcome === "completed" ? "task_completed" : "task_failed",
50420
+ sessionId: completingSessionId,
50421
+ nodeId: readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId) || void 0,
50422
+ providerType: readNonEmptyString2(args.metadataEvent.providerType) || void 0,
50423
+ payload: {
50424
+ taskId: row.id,
50425
+ event: args.event,
50426
+ source: "redrive_late_completion_supersede",
50427
+ reclaimReason: row.requeueReason,
50428
+ reclaimAgeMs: Date.now() - requeuedAtMs,
50429
+ finalSummary: readNonEmptyString2(args.metadataEvent.finalSummary) || void 0
50430
+ }
50431
+ });
50432
+ } catch {
50433
+ }
50434
+ }
50435
+ LOG2.warn("MeshQueue", `Late completion superseded re-drive for task ${row.id} on mesh ${meshId} (reclaimed '${row.requeueReason}' ${Math.round((Date.now() - requeuedAtMs) / 1e3)}s ago; completing session ${completingSessionId}) \u2192 flipped ${outcome}${row.status === "assigned" && reDispatchedSessionId && !sessionIdsEquivalent(reDispatchedSessionId, completingSessionId) ? `, stopped duplicate re-dispatch on ${reDispatchedSessionId}` : ""}`);
50436
+ traceMeshEventDrop("redrive_late_completion_supersede", {
50437
+ taskId: row.id,
50438
+ sessionId: completingSessionId,
50439
+ nodeId: row.assignedNodeId ?? args.nodeId,
50440
+ meshId,
50441
+ event: args.event
50442
+ }, `${row.requeueReason} ${Math.round((Date.now() - requeuedAtMs) / 1e3)}s \u2192 ${outcome}`);
50443
+ return true;
50444
+ }
50376
50445
  function injectMeshSystemMessage(components, args) {
50377
50446
  const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
50378
50447
  const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
@@ -50484,6 +50553,7 @@ ${cleanBody}`;
50484
50553
  }
50485
50554
  });
50486
50555
  }
50556
+ } else if (strandedRow && supersedeRedriveReclaimForLateCompletion(components, args.meshId, strandedRow, sessionId, outcome, args)) {
50487
50557
  }
50488
50558
  } catch {
50489
50559
  }
@@ -51807,6 +51877,50 @@ ${cleanBody}`;
51807
51877
  LOG2.info("MeshReconcile", `Auto-pruned ${result.prunedCount} orphaned direct dispatch record(s) for mesh ${mesh.id}`);
51808
51878
  }
51809
51879
  }
51880
+ async function pollAssignedTaskTerminalEvidence(components, mesh, row) {
51881
+ const sessionId = readNonEmptyString2(row.assignedSessionId);
51882
+ const nodeId = readNonEmptyString2(row.assignedNodeId);
51883
+ if (!sessionId || !nodeId) return null;
51884
+ const node = (mesh.nodes ?? []).find((n) => n.id === nodeId);
51885
+ const nodeDaemonId = readNonEmptyString2(node?.daemonId);
51886
+ const localDaemonId = readNonEmptyString2(components.statusInstanceId);
51887
+ const isLocalNode = !nodeDaemonId || daemonIdsEquivalent(nodeDaemonId, localDaemonId) || !!components.instanceManager.getInstance(sessionId);
51888
+ const providerType = readNonEmptyString2(row.assignedProviderType);
51889
+ const readArgs = {
51890
+ sessionId,
51891
+ targetSessionId: sessionId,
51892
+ tailLimit: 10,
51893
+ ...node?.workspace ? { workspace: node.workspace } : {},
51894
+ ...providerType ? { agentType: providerType, providerType } : {}
51895
+ };
51896
+ let payload = null;
51897
+ try {
51898
+ if (isLocalNode) {
51899
+ const result = await components.commandHandler?.handle("read_chat", readArgs);
51900
+ if (result && result.success === false) return null;
51901
+ payload = unwrapReadChatPayload(result);
51902
+ } else if (components.dispatchMeshCommand) {
51903
+ const result = await components.dispatchMeshCommand(nodeDaemonId, "read_chat", readArgs);
51904
+ payload = unwrapReadChatPayload(result);
51905
+ if (payload && payload.success === false) return null;
51906
+ } else {
51907
+ return null;
51908
+ }
51909
+ } catch {
51910
+ return null;
51911
+ }
51912
+ if (!payload) return null;
51913
+ if (readChatPayloadStatus(payload) !== "idle") return null;
51914
+ const messages = Array.isArray(payload.messages) ? payload.messages : [];
51915
+ const evidence = extractFinalAssistantSummaryEvidence(messages);
51916
+ if (!evidence.finalSummary) return null;
51917
+ const dispatchedAtMs = Date.parse(readNonEmptyString2(row.dispatchTimestamp));
51918
+ const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
51919
+ if (!(Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs >= dispatchedAtMs)) {
51920
+ return null;
51921
+ }
51922
+ return "completed";
51923
+ }
51810
51924
  var init_mesh_completion_synthesis = __esm2({
51811
51925
  "src/mesh/mesh-completion-synthesis.ts"() {
51812
51926
  "use strict";
@@ -52081,7 +52195,8 @@ ${cleanBody}`;
52081
52195
  }
52082
52196
  return delivered;
52083
52197
  }
52084
- function recoverStrandedAssignedDispatches(components, meshId, store) {
52198
+ async function recoverStrandedAssignedDispatches(components, mesh, store) {
52199
+ const meshId = mesh.id;
52085
52200
  const assigned = getQueue(meshId, { status: ["assigned"] });
52086
52201
  if (!assigned.length) return;
52087
52202
  const nowMs = Date.now();
@@ -52188,6 +52303,36 @@ ${cleanBody}`;
52188
52303
  }
52189
52304
  reclaimReason = "reclaim_after_unknown_grace";
52190
52305
  }
52306
+ const terminalEvidence = await pollAssignedTaskTerminalEvidence(components, mesh, row);
52307
+ if (terminalEvidence) {
52308
+ deliveredNoTurnUnknownStreak.delete(streakKey);
52309
+ updateTaskStatus(meshId, row.id, terminalEvidence);
52310
+ if (!findTerminalLedgerEvidenceForTask({ meshId, taskId: row.id })) {
52311
+ try {
52312
+ appendLedgerEntry(meshId, {
52313
+ kind: terminalEvidence === "completed" ? "task_completed" : "task_failed",
52314
+ nodeId: row.assignedNodeId,
52315
+ sessionId: row.assignedSessionId,
52316
+ providerType: row.assignedProviderType,
52317
+ payload: {
52318
+ taskId: row.id,
52319
+ event: "agent:generating_completed",
52320
+ source: "redrive_deadline_transcript_evidence"
52321
+ }
52322
+ });
52323
+ } catch {
52324
+ }
52325
+ }
52326
+ LOG2.warn("MeshReconcile", `Skipped delivered-no-turn re-drive for task ${row.id} on mesh ${meshId} (node=${row.assignedNodeId ?? "?"} session=${row.assignedSessionId ?? "?"}): worker transcript is idle with a final assistant message after dispatch \u2014 the completion event was lost/late, task is ${terminalEvidence}, NOT re-driving`);
52327
+ traceMeshEventDrop("redrive_deadline_transcript_completed", {
52328
+ taskId: row.id,
52329
+ sessionId: row.assignedSessionId,
52330
+ nodeId: row.assignedNodeId,
52331
+ meshId,
52332
+ event: "agent:generating_completed"
52333
+ }, `${reclaimReason} \u2192 transcript ${terminalEvidence}`);
52334
+ continue;
52335
+ }
52191
52336
  const reclaimedLost = reclaimStrandedAssignedTask(meshId, row.id, {
52192
52337
  reason: reclaimReason,
52193
52338
  ageMs: nowMs - dispatchedAtMs
@@ -52314,7 +52459,7 @@ ${cleanBody}`;
52314
52459
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
52315
52460
  if (!daemonHostsMesh(mesh, selfIds)) continue;
52316
52461
  try {
52317
- recoverStrandedAssignedDispatches(components, mesh.id, store);
52462
+ await recoverStrandedAssignedDispatches(components, mesh, store);
52318
52463
  } catch (e) {
52319
52464
  LOG2.warn("MeshReconcile", `Assigned-stranded watchdog failed for mesh ${mesh.id}: ${e?.message || e}`);
52320
52465
  }
@@ -54619,6 +54764,21 @@ ${cleanBody}`;
54619
54764
  }
54620
54765
  function findQuestionLineIndex(spec, lines) {
54621
54766
  const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
54767
+ const buttonFlags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
54768
+ const buttonRe = compile3(spec.buttonPattern, buttonFlags);
54769
+ const isButtonLine = (line) => {
54770
+ buttonRe.lastIndex = 0;
54771
+ return buttonRe.test(line);
54772
+ };
54773
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
54774
+ if (primary.test(lines[i]) && !isButtonLine(lines[i])) return { index: i, matchedSource: "primary" };
54775
+ }
54776
+ for (const variant of spec.questionVariants ?? []) {
54777
+ const re = compile3(variant.regex, variant.flags ?? "i");
54778
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
54779
+ if (re.test(lines[i]) && !isButtonLine(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
54780
+ }
54781
+ }
54622
54782
  for (let i = lines.length - 1; i >= 0; i -= 1) {
54623
54783
  if (primary.test(lines[i])) return { index: i, matchedSource: "primary" };
54624
54784
  }
@@ -55352,6 +55512,7 @@ ${cont}` : cont;
55352
55512
  * paint and made the engine type "1" repeatedly into the prompt.
55353
55513
  */
55354
55514
  modalLostAt = 0;
55515
+ modalLostRecheckTimer = null;
55355
55516
  approvalExitTimeout = null;
55356
55517
  // ── Response tracking ────────────────────────────
55357
55518
  responseEpoch = 0;
@@ -55403,6 +55564,10 @@ ${cont}` : cont;
55403
55564
  setStatus(status, trigger) {
55404
55565
  const prev = this.currentStatus;
55405
55566
  if (prev === status) return;
55567
+ if (prev === "waiting_approval" && this.modalLostRecheckTimer) {
55568
+ clearTimeout(this.modalLostRecheckTimer);
55569
+ this.modalLostRecheckTimer = null;
55570
+ }
55406
55571
  this.currentStatus = status;
55407
55572
  this.statusHistory.push({ status, at: Date.now(), trigger });
55408
55573
  if (this.statusHistory.length > 50) this.statusHistory.shift();
@@ -55563,6 +55728,10 @@ ${cont}` : cont;
55563
55728
  clearTimeout(this.approvalExitTimeout);
55564
55729
  this.approvalExitTimeout = null;
55565
55730
  }
55731
+ if (this.modalLostRecheckTimer) {
55732
+ clearTimeout(this.modalLostRecheckTimer);
55733
+ this.modalLostRecheckTimer = null;
55734
+ }
55566
55735
  if (this.finishRetryTimer) {
55567
55736
  clearTimeout(this.finishRetryTimer);
55568
55737
  this.finishRetryTimer = null;
@@ -55874,7 +56043,7 @@ ${cont}` : cont;
55874
56043
  if (!inCooldown || modal) {
55875
56044
  if (!modal) {
55876
56045
  LOG2.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
55877
- if (this.currentStatus === "waiting_approval" && this.activeModal) {
56046
+ if (this.currentStatus === "waiting_approval") {
55878
56047
  const lostAt = this.modalLostAt || Date.now();
55879
56048
  if (!this.modalLostAt) this.modalLostAt = lostAt;
55880
56049
  if (Date.now() - lostAt >= this.timeouts.approvalCooldown) {
@@ -55882,6 +56051,8 @@ ${cont}` : cont;
55882
56051
  this.modalLostAt = 0;
55883
56052
  this.setStatus("generating", "approval_lost_modal");
55884
56053
  this.callbacks.onStatusChange();
56054
+ } else {
56055
+ this.armModalLostRecheck();
55885
56056
  }
55886
56057
  }
55887
56058
  return;
@@ -56143,6 +56314,25 @@ ${cont}` : cont;
56143
56314
  this.recordTrace("idle_finish_cancelled", { trigger: reason });
56144
56315
  }
56145
56316
  // ─── Helpers ────────────────────────────────────────────────────────────
56317
+ /**
56318
+ * Schedule one more settled evaluation while pinned to `waiting_approval`
56319
+ * with no actionable modal. The settled FSM normally only re-runs on new
56320
+ * PTY output; a provider whose modal cue lingers in a form detectStatus
56321
+ * still matches (e.g. kimi's questionPattern hitting the user echo) but
56322
+ * whose PTY has gone quiet would never get another evaluation, latching
56323
+ * `waiting_approval` forever. This timer guarantees the modal-lost recovery
56324
+ * in `applyWaitingApproval` is reached even against a silent PTY. It is a
56325
+ * no-op once the FSM leaves `waiting_approval` (the re-evaluation itself
56326
+ * takes the recovery branch and clears the state).
56327
+ */
56328
+ armModalLostRecheck() {
56329
+ if (this.modalLostRecheckTimer) return;
56330
+ this.modalLostRecheckTimer = setTimeout(() => {
56331
+ this.modalLostRecheckTimer = null;
56332
+ if (this.currentStatus !== "waiting_approval") return;
56333
+ this.evaluateSettled(this.transport.getSnapshot());
56334
+ }, this.timeouts.approvalCooldown);
56335
+ }
56146
56336
  armApprovalExitTimeout() {
56147
56337
  if (this.approvalExitTimeout) clearTimeout(this.approvalExitTimeout);
56148
56338
  this.approvalExitTimeout = setTimeout(() => {
@@ -65891,6 +66081,33 @@ ${effect.notification.body || ""}`.trim();
65891
66081
  getState(key2) {
65892
66082
  return this.records.get(key2)?.state ?? INITIAL_CHAT_SOURCE_STATE;
65893
66083
  }
66084
+ /**
66085
+ * Opaque snapshot of a session's FULL record (state + lock + transitions),
66086
+ * for callers that need to speculatively `observe()` and then roll back if
66087
+ * the resulting decision is undesirable (STICKY-NATIVE hold: a trusted
66088
+ * exact-identity session must not flip to PTY on a transient native gap).
66089
+ * Returns undefined when the session has no record yet. The snapshot is a
66090
+ * shallow copy — transitions array is copied so a later append does not
66091
+ * mutate it.
66092
+ */
66093
+ snapshotRecord(key2) {
66094
+ const rec = this.records.get(key2);
66095
+ if (!rec) return void 0;
66096
+ return { state: rec.state, lockedSince: rec.lockedSince, transitions: [...rec.transitions] };
66097
+ }
66098
+ /** Restore a previously snapshotted record, undoing a speculative observe.
66099
+ * Passing undefined clears the key (it had no record when snapshotted). */
66100
+ restoreRecord(key2, snapshot) {
66101
+ if (!snapshot) {
66102
+ this.records.delete(key2);
66103
+ return;
66104
+ }
66105
+ this.records.set(key2, {
66106
+ state: snapshot.state,
66107
+ lockedSince: snapshot.lockedSince,
66108
+ transitions: [...snapshot.transitions]
66109
+ });
66110
+ }
65894
66111
  /** Recent transitions, newest last. Empty array when nothing has happened. */
65895
66112
  getTransitions(key2) {
65896
66113
  return this.records.get(key2)?.transitions ?? [];
@@ -66312,7 +66529,52 @@ ${effect.notification.body || ""}`.trim();
66312
66529
  const supportsNative = supportsCliNativeTranscript(args.providerType, args.provider);
66313
66530
  const observation = buildObservationForCli(args, supportsNative);
66314
66531
  const sessionKey = chatSourceSessionKey(args.providerType, args.sessionId);
66532
+ const priorSnapshot = CHAT_SOURCE_REGISTRY.snapshotRecord(sessionKey);
66533
+ const priorState = priorSnapshot?.state ?? CHAT_SOURCE_REGISTRY.getState(sessionKey);
66534
+ const eligibleForStickyHold = args.trustedExactNativeIdentity === true && (priorState.name === "NativeLocked" || priorState.name === "Recovering");
66315
66535
  let decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
66536
+ if (eligibleForStickyHold && decision.selected === "pty-parser") {
66537
+ CHAT_SOURCE_REGISTRY.restoreRecord(sessionKey, priorSnapshot);
66538
+ const heldNativeMessages = observation.kind === "native_present" ? extractNativeMessagesFromResult(args.providerType, args.nativeHistoryResult) : [];
66539
+ const messageSource2 = buildCliMessageSourceProvenance({
66540
+ selected: "native-history",
66541
+ provider: args.providerType,
66542
+ nativeHandle: typeof args.nativeHistoryResult?.providerSessionId === "string" ? args.nativeHistoryResult.providerSessionId : void 0,
66543
+ sessionWorkspace: args.sessionWorkspace,
66544
+ intendedWorkspace: args.intendedWorkspace,
66545
+ transcriptWorkspace: void 0,
66546
+ fallbackReason: "native_history_transient_gap_held",
66547
+ nativeSource: "provider-native",
66548
+ sourcePath: typeof args.nativeHistoryResult?.sourcePath === "string" ? args.nativeHistoryResult.sourcePath : void 0,
66549
+ sourceMtimeMs: typeof args.nativeHistoryResult?.sourceMtimeMs === "number" ? args.nativeHistoryResult.sourceMtimeMs : void 0,
66550
+ nativeHistoryCoverage: void 0,
66551
+ partialReason: void 0,
66552
+ unavailableReason: observation.kind === "native_unavailable" ? observation.reason : void 0,
66553
+ nativeMessages: heldNativeMessages,
66554
+ ptyMessages: args.ptyMessages,
66555
+ returnedMessages: heldNativeMessages,
66556
+ safeMapping: args.safeMapping,
66557
+ freshEnough: true,
66558
+ ptyStatusApprovalOnly: true
66559
+ });
66560
+ return {
66561
+ decision: {
66562
+ selected: "native-history",
66563
+ nextState: priorState,
66564
+ transition: {
66565
+ fromState: priorState.name,
66566
+ toState: priorState.name,
66567
+ event: "NoOp",
66568
+ cause: decision.transition.cause,
66569
+ at: Date.now()
66570
+ },
66571
+ lockState: { locked: priorState.name === "NativeLocked" }
66572
+ },
66573
+ messageSource: messageSource2,
66574
+ nativeMessages: heldNativeMessages,
66575
+ nativeSelected: true
66576
+ };
66577
+ }
66316
66578
  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) {
66317
66579
  CHAT_SOURCE_REGISTRY.clear(sessionKey);
66318
66580
  decision = CHAT_SOURCE_REGISTRY.observe(sessionKey, observation);
@@ -66632,7 +66894,7 @@ ${effect.notification.body || ""}`.trim();
66632
66894
  function readCliProviderNativeHistory(agentStr, args) {
66633
66895
  const canBindFromLiveSession = !args.historySessionId && typeof args.sessionStartedAtMs === "number" && args.sessionStartedAtMs > 0 && typeof args.workspace === "string" && args.workspace.trim().length > 0;
66634
66896
  const pinnedProviderSessionId = typeof args.pinnedProviderSessionId === "string" ? args.pinnedProviderSessionId.trim() : "";
66635
- const effectiveHistorySessionId = args.historySessionId || (!canBindFromLiveSession ? pinnedProviderSessionId : "");
66897
+ const effectiveHistorySessionId = args.historySessionId || pinnedProviderSessionId || "";
66636
66898
  const workspaceLatestFallback = !effectiveHistorySessionId && !canBindFromLiveSession && !pinnedProviderSessionId && args.allowWorkspaceLatestFallback === true && typeof args.workspace === "string" && args.workspace.trim().length > 0;
66637
66899
  if (!effectiveHistorySessionId && !canBindFromLiveSession && !workspaceLatestFallback) {
66638
66900
  return {