@adhdev/daemon-core 0.9.82-rc.475 → 0.9.82-rc.477

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.
@@ -3,5 +3,13 @@
3
3
  * native-history / source-resolution / normalization helpers they use.
4
4
  */
5
5
  import type { CommandResult, CommandHelpers } from './handler.js';
6
+ /**
7
+ * Test-only: clear the in-memory read-pin map and re-arm cold-start hydration so
8
+ * each test starts from a clean pin state. The on-disk mirror is isolated per
9
+ * test process via ADHDEV_CONFIG_DIR (test/helpers/setup-env.ts); this resets the
10
+ * module-level cache that would otherwise leak a pin across tests sharing the
11
+ * worker. Not part of the runtime contract.
12
+ */
13
+ export declare function __resetProviderSessionPinsForTest(): void;
6
14
  export declare function handleChatHistory(h: CommandHelpers, args: any): Promise<CommandResult>;
7
15
  export declare function handleReadChat(h: CommandHelpers, args: any): Promise<CommandResult>;
@@ -10,5 +10,5 @@
10
10
  export { READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, buildSendInputSignature } from './chat-commands-shared.js';
11
11
  export { evaluateReadChatNodeWorkspaceScope } from './chat-commands-scope.js';
12
12
  export { sanitizeDebugBundleValue, handleGetChatDebugBundle } from './chat-commands-debug-bundle.js';
13
- export { handleChatHistory, handleReadChat } from './chat-commands-read.js';
13
+ export { handleChatHistory, handleReadChat, __resetProviderSessionPinsForTest } from './chat-commands-read.js';
14
14
  export { handleSendChat, handleListChats, handleNewChat, handleSwitchChat, handleSetMode, handleChangeModel, handleSetThoughtLevel, handleResolveAction, } from './chat-commands-write.js';
@@ -21,6 +21,17 @@ export interface DaemonState {
21
21
  sessionNotificationDismissals: Record<string, string>;
22
22
  /** Current notification unread override ids keyed by stable session target */
23
23
  sessionNotificationUnreadOverrides: Record<string, string>;
24
+ /**
25
+ * Resolved provider-native conversation id for a live session, keyed by the
26
+ * ADHDev/mesh session id. Persisted so it survives a daemon restart: a
27
+ * provider whose on-disk store is keyed by an internally-generated id it
28
+ * never exposes on the CLI (antigravity — no --session-id) can then exact-bind
29
+ * its conversation .db after restart instead of re-running the mtime/recency
30
+ * heuristic, which drops the store once idle and collapses read_chat to the
31
+ * PTY parse (user echo only, assistant tail lost). Mirrors the in-memory read
32
+ * pin (chat-commands-read lastBoundProviderSessionIdByMeshSession).
33
+ */
34
+ sessionProviderSessionPins: Record<string, string>;
24
35
  }
25
36
  /**
26
37
  * Load runtime state from disk
@@ -34,3 +45,22 @@ export declare function saveState(state: DaemonState): void;
34
45
  * Reset runtime state
35
46
  */
36
47
  export declare function resetState(): void;
48
+ /**
49
+ * Load the full persisted session→provider-conversation pin map (sessionId →
50
+ * provider-native conversation id). Survives daemon restart. Empty object when
51
+ * none recorded or the state file is unreadable.
52
+ */
53
+ export declare function loadPersistedProviderSessionPins(): Record<string, string>;
54
+ /**
55
+ * Persist one session→provider-conversation pin. Load-mutate-save against the
56
+ * on-disk state so it survives a daemon restart; a no-op when the value already
57
+ * matches (avoids rewriting state.json on every read). Never clears a pin with an
58
+ * empty value.
59
+ */
60
+ export declare function recordPersistedProviderSessionPin(sessionId: string, providerSessionId: string): void;
61
+ /**
62
+ * Test-only: drop all persisted session→provider-conversation pins from disk.
63
+ * Used by tests that assert a clean "no pin" state so a sibling test's write to
64
+ * the shared per-process ADHDEV_CONFIG_DIR does not leak in.
65
+ */
66
+ export declare function clearPersistedProviderSessionPins(): void;
package/dist/index.js CHANGED
@@ -409,10 +409,10 @@ function readInjected(value) {
409
409
  }
410
410
  function getDaemonBuildInfo() {
411
411
  if (cached) return cached;
412
- const commit = readInjected(true ? "c7b88fafbc63d3fc0de4b93c74facdb5145b8be6" : void 0) ?? "unknown";
413
- const commitShort = readInjected(true ? "c7b88faf" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
- const version = readInjected(true ? "0.9.82-rc.475" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
- const builtAt = readInjected(true ? "2026-07-06T06:24:24.877Z" : void 0);
412
+ const commit = readInjected(true ? "e565a4d93874e580f3923a562a7a58229af17997" : void 0) ?? "unknown";
413
+ const commitShort = readInjected(true ? "e565a4d9" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
+ const version = readInjected(true ? "0.9.82-rc.477" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
+ const builtAt = readInjected(true ? "2026-07-06T18:09:09.172Z" : void 0);
416
416
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
417
417
  return cached;
418
418
  }
@@ -16694,13 +16694,17 @@ function normalizeState(raw) {
16694
16694
  const sessionNotificationUnreadOverrides = Object.fromEntries(
16695
16695
  Object.entries(isPlainObject2(parsed.sessionNotificationUnreadOverrides) ? parsed.sessionNotificationUnreadOverrides : {}).filter(([, value]) => typeof value === "string" && value.length > 0)
16696
16696
  );
16697
+ const sessionProviderSessionPins = Object.fromEntries(
16698
+ Object.entries(isPlainObject2(parsed.sessionProviderSessionPins) ? parsed.sessionProviderSessionPins : {}).filter(([key2, value]) => typeof key2 === "string" && key2.length > 0 && typeof value === "string" && value.length > 0)
16699
+ );
16697
16700
  return {
16698
16701
  recentActivity,
16699
16702
  savedProviderSessions,
16700
16703
  sessionReads,
16701
16704
  sessionReadMarkers,
16702
16705
  sessionNotificationDismissals,
16703
- sessionNotificationUnreadOverrides
16706
+ sessionNotificationUnreadOverrides,
16707
+ sessionProviderSessionPins
16704
16708
  };
16705
16709
  }
16706
16710
  function loadState() {
@@ -16723,6 +16727,20 @@ function saveState(state) {
16723
16727
  function resetState() {
16724
16728
  saveState({ ...DEFAULT_STATE });
16725
16729
  }
16730
+ function loadPersistedProviderSessionPins() {
16731
+ return { ...loadState().sessionProviderSessionPins };
16732
+ }
16733
+ function recordPersistedProviderSessionPin(sessionId, providerSessionId) {
16734
+ const key2 = typeof sessionId === "string" ? sessionId.trim() : "";
16735
+ const value = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
16736
+ if (!key2 || !value) return;
16737
+ const state = loadState();
16738
+ if (state.sessionProviderSessionPins[key2] === value) return;
16739
+ saveState({
16740
+ ...state,
16741
+ sessionProviderSessionPins: { ...state.sessionProviderSessionPins, [key2]: value }
16742
+ });
16743
+ }
16726
16744
  var import_fs14, import_path11, DEFAULT_STATE;
16727
16745
  var init_state_store = __esm({
16728
16746
  "src/config/state-store.ts"() {
@@ -16736,7 +16754,8 @@ var init_state_store = __esm({
16736
16754
  sessionReads: {},
16737
16755
  sessionReadMarkers: {},
16738
16756
  sessionNotificationDismissals: {},
16739
- sessionNotificationUnreadOverrides: {}
16757
+ sessionNotificationUnreadOverrides: {},
16758
+ sessionProviderSessionPins: {}
16740
16759
  };
16741
16760
  }
16742
16761
  });
@@ -19292,6 +19311,16 @@ function evaluateMeshEventSuppression(args, ctx) {
19292
19311
  }
19293
19312
  return null;
19294
19313
  }
19314
+ function sourceWorkerAutoApproves(components, sessionId) {
19315
+ if (!sessionId) return false;
19316
+ try {
19317
+ const state = components.instanceManager?.getInstance?.(sessionId)?.getState?.();
19318
+ const settings = state?.settings || {};
19319
+ return settings.autoApprove === true;
19320
+ } catch {
19321
+ return false;
19322
+ }
19323
+ }
19295
19324
  function injectMeshSystemMessage(components, args) {
19296
19325
  const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
19297
19326
  const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
@@ -19350,6 +19379,11 @@ function injectMeshSystemMessage(components, args) {
19350
19379
  }
19351
19380
  }
19352
19381
  const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
19382
+ if (args.event === "agent:waiting_approval" && sourceWorkerAutoApproves(components, eventSessionId)) {
19383
+ LOG.info("MeshEvents", `Suppressed agent:waiting_approval for auto-approving worker session ${eventSessionId || "(unknown)"} (mesh ${args.meshId}) \u2014 modal is resolved locally, coordinator not notified`);
19384
+ traceMeshEventDrop("waiting_approval_auto_approving_worker", traceCtx);
19385
+ return { success: true, forwarded: 0, suppressed: true, autoApprovingWorkerApproval: true };
19386
+ }
19353
19387
  const suppression = evaluateMeshEventSuppression(args, {
19354
19388
  traceCtx,
19355
19389
  eventSessionId,
@@ -34086,6 +34120,7 @@ init_debug_trace();
34086
34120
  // src/commands/chat-commands-read.ts
34087
34121
  var path16 = __toESM(require("path"));
34088
34122
  init_contracts2();
34123
+ init_state_store();
34089
34124
  init_coordinator_registry();
34090
34125
  init_logger();
34091
34126
  init_debug_trace();
@@ -34441,15 +34476,37 @@ init_chat_message_normalization();
34441
34476
  var HOT_TAIL_MIN_LIMIT = 60;
34442
34477
  var CLI_NATIVE_TRANSCRIPT_PROVIDERS = /* @__PURE__ */ new Set(["codex-cli", "claude-cli", "hermes-cli", "antigravity-cli"]);
34443
34478
  var lastBoundProviderSessionIdByMeshSession = /* @__PURE__ */ new Map();
34444
- function recordBoundProviderSessionId(meshSessionId, providerSessionId) {
34479
+ var persistedProviderSessionPinsHydrated = false;
34480
+ function hydratePersistedProviderSessionPinsOnce() {
34481
+ if (persistedProviderSessionPinsHydrated) return;
34482
+ persistedProviderSessionPinsHydrated = true;
34483
+ try {
34484
+ for (const [key2, value] of Object.entries(loadPersistedProviderSessionPins())) {
34485
+ if (!lastBoundProviderSessionIdByMeshSession.has(key2)) {
34486
+ lastBoundProviderSessionIdByMeshSession.set(key2, value);
34487
+ }
34488
+ }
34489
+ } catch {
34490
+ }
34491
+ }
34492
+ function recordBoundProviderSessionId(h, meshSessionId, providerSessionId) {
34445
34493
  const key2 = typeof meshSessionId === "string" ? meshSessionId.trim() : "";
34446
34494
  const value = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
34447
34495
  if (!key2 || !value) return;
34496
+ try {
34497
+ h.ctx?.sessionRegistry?.setProviderSessionId?.(key2, value);
34498
+ } catch {
34499
+ }
34448
34500
  lastBoundProviderSessionIdByMeshSession.set(key2, value);
34501
+ try {
34502
+ recordPersistedProviderSessionPin(key2, value);
34503
+ } catch {
34504
+ }
34449
34505
  }
34450
34506
  function getBoundProviderSessionIdPin(meshSessionId) {
34451
34507
  const key2 = typeof meshSessionId === "string" ? meshSessionId.trim() : "";
34452
34508
  if (!key2) return void 0;
34509
+ hydratePersistedProviderSessionPinsOnce();
34453
34510
  const pinned = lastBoundProviderSessionIdByMeshSession.get(key2);
34454
34511
  return pinned && pinned.trim() ? pinned.trim() : void 0;
34455
34512
  }
@@ -35012,8 +35069,14 @@ function hasSafeNativeHistoryMapping(args) {
35012
35069
  if (!args.requireWorkspaceContentOverlap) return true;
35013
35070
  return hasOverlappingVisibleConversationText(args.nativeMessages, args.ptyMessages || []);
35014
35071
  }
35072
+ function effectiveReadSessionId(h, targetSessionId) {
35073
+ const explicit = typeof targetSessionId === "string" ? targetSessionId.trim() : "";
35074
+ if (explicit) return explicit;
35075
+ const current = h.currentSession?.sessionId;
35076
+ return typeof current === "string" ? current.trim() : "";
35077
+ }
35015
35078
  function sessionStartedAtMsFromRegistry(h, targetSessionId) {
35016
- const sid = typeof targetSessionId === "string" ? targetSessionId.trim() : "";
35079
+ const sid = effectiveReadSessionId(h, targetSessionId);
35017
35080
  if (!sid) return void 0;
35018
35081
  const target = h.ctx?.sessionRegistry?.get?.(sid);
35019
35082
  return typeof target?.spawnedAtMs === "number" ? target.spawnedAtMs : void 0;
@@ -35310,7 +35373,7 @@ async function handleChatHistory(h, args) {
35310
35373
  scripts: provider?.scripts,
35311
35374
  sessionStartedAtMs: sessionStartedAtMsFromRegistry(h, args?.targetSessionId),
35312
35375
  envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
35313
- instanceId: typeof args?.targetSessionId === "string" ? args.targetSessionId : void 0,
35376
+ instanceId: effectiveReadSessionId(h, args?.targetSessionId) || void 0,
35314
35377
  pinnedProviderSessionId: getBoundProviderSessionIdPin(args?.targetSessionId)
35315
35378
  }) : readProviderChatHistory(agentStr, {
35316
35379
  canonicalHistory: provider?.nativeHistory,
@@ -35327,7 +35390,7 @@ async function handleChatHistory(h, args) {
35327
35390
  const messages = Array.isArray(result.messages) ? normalizeAndFilterNativeHistory(h, agentStr, args, result.messages, result?.providerSessionId) : [];
35328
35391
  const historyProviderSessionId = typeof result?.providerSessionId === "string" ? result.providerSessionId : readHistorySessionIdFromMessages(messages) || historySessionId;
35329
35392
  if (typeof result?.providerSessionId === "string" && result.providerSessionId.trim()) {
35330
- recordBoundProviderSessionId(args?.targetSessionId, result.providerSessionId.trim());
35393
+ recordBoundProviderSessionId(h, effectiveReadSessionId(h, args?.targetSessionId), result.providerSessionId.trim());
35331
35394
  }
35332
35395
  const safeMapping = hasSafeNativeHistoryMapping({
35333
35396
  historySessionId: lookup === "workspace" ? void 0 : historySessionId,
@@ -35451,10 +35514,15 @@ async function handleReadChat(h, args) {
35451
35514
  let nativeHistory = null;
35452
35515
  let nativeHistoryError;
35453
35516
  if (supportsNative) {
35517
+ const pinnedProviderSessionIdForRead = getBoundProviderSessionIdPin(targetSessionId);
35518
+ const nativeReadSessionIdIsRuntimeFallback = Boolean(
35519
+ targetSessionId && nativeHistoryReadSessionId === targetSessionId && !getExplicitHistorySessionId(args)
35520
+ );
35521
+ const effectiveNativeReadSessionId = nativeReadSessionIdIsRuntimeFallback ? pinnedProviderSessionIdForRead || void 0 : nativeHistoryReadSessionId;
35454
35522
  try {
35455
35523
  nativeHistory = readCliProviderNativeHistory(agentStr, {
35456
35524
  canonicalHistory: provider?.nativeHistory,
35457
- historySessionId: nativeHistoryReadSessionId,
35525
+ historySessionId: effectiveNativeReadSessionId,
35458
35526
  workspace,
35459
35527
  offset: 0,
35460
35528
  limit: nativeHistoryLimit,
@@ -35466,16 +35534,16 @@ async function handleReadChat(h, args) {
35466
35534
  envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
35467
35535
  // Stable per-session identity for antigravity's conversation-claim
35468
35536
  // owner token (== session registry sessionId == instance instanceId).
35469
- instanceId: typeof args?.targetSessionId === "string" ? args.targetSessionId : void 0,
35470
- pinnedProviderSessionId: getBoundProviderSessionIdPin(targetSessionId),
35537
+ instanceId: effectiveReadSessionId(h, args?.targetSessionId) || void 0,
35538
+ pinnedProviderSessionId: pinnedProviderSessionIdForRead,
35471
35539
  // Last-resort only when no pin was ever recorded for this
35472
35540
  // session; the downstream workspace-overlap safety gate
35473
35541
  // still filters an aliased session out.
35474
- allowWorkspaceLatestFallback: !getBoundProviderSessionIdPin(targetSessionId)
35542
+ allowWorkspaceLatestFallback: !pinnedProviderSessionIdForRead
35475
35543
  });
35476
35544
  const resolvedProviderSessionId = typeof nativeHistory?.providerSessionId === "string" ? nativeHistory.providerSessionId.trim() : "";
35477
35545
  if (resolvedProviderSessionId) {
35478
- recordBoundProviderSessionId(targetSessionId, resolvedProviderSessionId);
35546
+ recordBoundProviderSessionId(h, effectiveReadSessionId(h, targetSessionId), resolvedProviderSessionId);
35479
35547
  }
35480
35548
  } catch (error) {
35481
35549
  nativeHistoryError = error;
@@ -35517,7 +35585,7 @@ async function handleReadChat(h, args) {
35517
35585
  excludeInProgressTurn: returnedStatus === "waiting_approval",
35518
35586
  sessionStartedAtMs,
35519
35587
  envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
35520
- instanceId: typeof args?.targetSessionId === "string" ? args.targetSessionId : void 0
35588
+ instanceId: effectiveReadSessionId(h, args?.targetSessionId) || void 0
35521
35589
  });
35522
35590
  nativeHistoryError = void 0;
35523
35591
  nativeMessages = nativeHistory && Array.isArray(nativeHistory.messages) ? normalizeAndFilterNativeHistory(h, agentStr, args, nativeHistory.messages, nativeHistory.providerSessionId) : [];
@@ -35739,7 +35807,7 @@ async function handleReadChat(h, args) {
35739
35807
  scripts: provider?.scripts,
35740
35808
  sessionStartedAtMs: sessionStartedAtMsFromRegistry(h, args?.targetSessionId),
35741
35809
  envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
35742
- instanceId: typeof args?.targetSessionId === "string" ? args.targetSessionId : void 0,
35810
+ instanceId: effectiveReadSessionId(h, args?.targetSessionId) || void 0,
35743
35811
  pinnedProviderSessionId: pinnedProviderSessionIdForHistory,
35744
35812
  // Last-resort only when no pin was ever recorded AND the
35745
35813
  // runtime fallback did not resolve a real provider session.
@@ -35758,7 +35826,7 @@ async function handleReadChat(h, args) {
35758
35826
  const historyMessages = Array.isArray(history?.messages) ? normalizeAndFilterNativeHistory(h, agentStr, args, history.messages, history?.providerSessionId) : [];
35759
35827
  const historyProviderSessionId = typeof history?.providerSessionId === "string" ? history.providerSessionId : readHistorySessionIdFromMessages(historyMessages) || effectiveHistorySessionIdForRead;
35760
35828
  if (typeof history?.providerSessionId === "string" && history.providerSessionId.trim()) {
35761
- recordBoundProviderSessionId(targetSid, history.providerSessionId.trim());
35829
+ recordBoundProviderSessionId(h, effectiveReadSessionId(h, targetSid), history.providerSessionId.trim());
35762
35830
  }
35763
35831
  const mappingSessionId = effectiveHistorySessionIdForRead;
35764
35832
  const safeMapping = supportsNative ? hasSafeNativeHistoryMapping({
@@ -44225,6 +44293,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
44225
44293
  }
44226
44294
 
44227
44295
  // src/providers/cli-provider-instance.ts
44296
+ init_state_store();
44228
44297
  init_logger();
44229
44298
  init_debug_trace();
44230
44299
  init_debug_config();
@@ -44257,13 +44326,10 @@ function normalizeUuid(uuid) {
44257
44326
  return String(uuid || "").trim().toLowerCase();
44258
44327
  }
44259
44328
  function antigravityOwnerToken(workspace, sessionStartedAtMs, instanceId) {
44329
+ void workspace;
44330
+ void sessionStartedAtMs;
44260
44331
  const iid = typeof instanceId === "string" ? instanceId.trim() : "";
44261
- if (iid) return `iid:${iid}`;
44262
- if (typeof sessionStartedAtMs === "number" && sessionStartedAtMs > 0) {
44263
- const ws = String(workspace || "").trim().toLowerCase();
44264
- return `spawn:${ws}:${sessionStartedAtMs}`;
44265
- }
44266
- return "";
44332
+ return iid ? `iid:${iid}` : "";
44267
44333
  }
44268
44334
  function claimAntigravityConversation(uuid, owner, now = Date.now()) {
44269
44335
  const key2 = normalizeUuid(uuid);
@@ -45023,6 +45089,27 @@ var CliProviderInstance = class _CliProviderInstance {
45023
45089
  senderName: message.senderName,
45024
45090
  receivedAt: message.receivedAt
45025
45091
  })) : mergedMessages;
45092
+ const adapterOwnsMessagesElsewhereForTail = this.adapter?.chatMessagesOwnedExternally === true;
45093
+ if (adapterOwnsMessagesElsewhereForTail && this.lastCompletionSummary) {
45094
+ const summary = this.lastCompletionSummary;
45095
+ let hasTrailingAssistant = false;
45096
+ for (let i = statusMessages.length - 1; i >= 0; i -= 1) {
45097
+ const m = statusMessages[i];
45098
+ const role = typeof m?.role === "string" ? m.role : "";
45099
+ if (role === "system") continue;
45100
+ if (typeof m?.kind === "string" && m.kind === "tool") continue;
45101
+ hasTrailingAssistant = role === "assistant" && typeof m?.receivedAt === "number" && m.receivedAt >= summary.receivedAt - 1e3;
45102
+ break;
45103
+ }
45104
+ if (!hasTrailingAssistant) {
45105
+ statusMessages.push({
45106
+ role: "assistant",
45107
+ content: summary.content,
45108
+ kind: "standard",
45109
+ receivedAt: summary.receivedAt
45110
+ });
45111
+ }
45112
+ }
45026
45113
  const dirName = workingDirBasename(this.workingDir);
45027
45114
  const parsedChatStatus = typeof parsedStatus?.status === "string" && parsedStatus.status.trim() ? parsedStatus.status.trim() : void 0;
45028
45115
  const suppressStaleParsedBusyStatus = this.shouldSuppressStaleParsedBusyStatus(parsedStatus, adapterStatus);
@@ -45455,6 +45542,18 @@ var CliProviderInstance = class _CliProviderInstance {
45455
45542
  completedDebounceTimer = null;
45456
45543
  completedDebouncePending = null;
45457
45544
  lastExternalCompletionProbe = null;
45545
+ /**
45546
+ * The final assistant summary of the last completed turn, cached at
45547
+ * completion-emit time. For a native-source provider (antigravity) whose
45548
+ * assistant answer lives only in native-history — never in the PTY parse that
45549
+ * feeds activeChat.messages — the dashboard's preview / lastMessageRole /
45550
+ * completionMarker would otherwise never see the answer and show the session
45551
+ * stuck on the user prompt. getState() appends this cached assistant bubble to
45552
+ * the status messages when the PTY tail has none, so those fields reflect the
45553
+ * real last answer with ZERO per-tick native reads (the native read already ran
45554
+ * once at completion). Reset on the next turn's start.
45555
+ */
45556
+ lastCompletionSummary = null;
45458
45557
  async enforceFreshSessionLaunchIfNeeded() {
45459
45558
  const scriptName = getForcedNewSessionScriptName(this.provider, this.launchMode);
45460
45559
  if (!scriptName) return;
@@ -45518,8 +45617,15 @@ var CliProviderInstance = class _CliProviderInstance {
45518
45617
  readExternalCompletionMessages() {
45519
45618
  const adapterOwnsMessagesElsewhere = this.adapter?.chatMessagesOwnedExternally === true;
45520
45619
  if (!adapterOwnsMessagesElsewhere) return null;
45521
- if (!this.providerSessionId) return null;
45522
45620
  if (!isNativeSourceCanonicalHistory(this.provider.nativeHistory)) return null;
45621
+ let resolvedHandle = this.providerSessionId || "";
45622
+ if (!resolvedHandle) {
45623
+ try {
45624
+ const pinned = loadPersistedProviderSessionPins()[this.instanceId];
45625
+ if (typeof pinned === "string" && pinned.trim()) resolvedHandle = pinned.trim();
45626
+ } catch {
45627
+ }
45628
+ }
45523
45629
  if (this.lastExternalCompletionProbe?.sourcePath) {
45524
45630
  try {
45525
45631
  fs21.statSync(this.lastExternalCompletionProbe.sourcePath);
@@ -45528,13 +45634,18 @@ var CliProviderInstance = class _CliProviderInstance {
45528
45634
  }
45529
45635
  const restoredHistory = readProviderChatHistory(this.type, {
45530
45636
  canonicalHistory: this.provider.nativeHistory,
45531
- historySessionId: this.providerSessionId,
45637
+ historySessionId: resolvedHandle || void 0,
45532
45638
  workspace: this.workingDir,
45533
45639
  offset: 0,
45534
45640
  limit: Number.MAX_SAFE_INTEGER,
45535
45641
  historyBehavior: this.provider.historyBehavior,
45536
45642
  scripts: this.provider.scripts,
45537
45643
  sessionStartedAtMs: this.startedAt,
45644
+ // The claim owner token must match read_chat's so the exact-bind on our
45645
+ // own conversation stays idempotent rather than looking foreign, and so
45646
+ // the floor-based resolution above claims THIS session's db under its
45647
+ // own owner (never a sibling's).
45648
+ instanceId: this.instanceId,
45538
45649
  envOverrides: this.spawnedEnvOverrides(),
45539
45650
  forceRefresh: true
45540
45651
  });
@@ -45549,6 +45660,26 @@ var CliProviderInstance = class _CliProviderInstance {
45549
45660
  );
45550
45661
  return restoredHistory.messages;
45551
45662
  }
45663
+ /**
45664
+ * The content of the LAST visible assistant bubble in a message list, or ''
45665
+ * when the tail is not an assistant reply. Skips trailing system/tool/activity
45666
+ * bubbles; stops (returns '') at the first user/human message. Used only for
45667
+ * the dashboard tail-repair cache — a display value, not a completion decision.
45668
+ */
45669
+ lastVisibleAssistantSummary(messages) {
45670
+ if (!Array.isArray(messages)) return "";
45671
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
45672
+ const m = messages[i];
45673
+ const role = typeof m?.role === "string" ? m.role : "";
45674
+ const kind = typeof m?.kind === "string" ? m.kind : "";
45675
+ if (role === "system") continue;
45676
+ if (kind === "tool" || kind === "activity") continue;
45677
+ if (role === "user" || role === "human") return "";
45678
+ if (role === "assistant") return flattenContent(m.content).trim();
45679
+ return "";
45680
+ }
45681
+ return "";
45682
+ }
45552
45683
  completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
45553
45684
  const turnClosed = !this.hasAdapterPendingResponse();
45554
45685
  if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
@@ -45560,8 +45691,13 @@ var CliProviderInstance = class _CliProviderInstance {
45560
45691
  }
45561
45692
  const externalMessages = this.readExternalCompletionMessages();
45562
45693
  if (externalMessages) {
45694
+ const present = turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt);
45695
+ const lastVisibleAssistant = this.lastVisibleAssistantSummary(externalMessages);
45696
+ if (lastVisibleAssistant) {
45697
+ this.lastCompletionSummary = { content: lastVisibleAssistant, receivedAt: Date.now() };
45698
+ }
45563
45699
  return {
45564
- present: turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
45700
+ present,
45565
45701
  messages: externalMessages,
45566
45702
  source: "external-native"
45567
45703
  };
@@ -45581,7 +45717,10 @@ var CliProviderInstance = class _CliProviderInstance {
45581
45717
  if (adapterOwnsMessagesElsewhere) {
45582
45718
  const externalMessages = this.readExternalCompletionMessages();
45583
45719
  const externalSummary = externalMessages ? extractFinalSummaryFromMessagesAfter(externalMessages, turnStartedAt) : "";
45584
- if (externalSummary) return externalSummary;
45720
+ if (externalSummary) {
45721
+ this.lastCompletionSummary = { content: externalSummary, receivedAt: Date.now() };
45722
+ return externalSummary;
45723
+ }
45585
45724
  return parsedSummary || void 0;
45586
45725
  }
45587
45726
  return parsedSummary || void 0;
@@ -46049,6 +46188,10 @@ var CliProviderInstance = class _CliProviderInstance {
46049
46188
  * the emitted event, exactly as each inline builder produced before.
46050
46189
  */
46051
46190
  emitGeneratingCompleted(opts) {
46191
+ const summary = typeof opts.finalSummary === "string" ? opts.finalSummary.trim() : "";
46192
+ if (summary) {
46193
+ this.lastCompletionSummary = { content: summary, receivedAt: opts.timestamp };
46194
+ }
46052
46195
  this.pushEvent({
46053
46196
  event: "agent:generating_completed",
46054
46197
  chatTitle: opts.chatTitle,
@@ -46333,6 +46476,7 @@ var CliProviderInstance = class _CliProviderInstance {
46333
46476
  this.completedDebouncePending = null;
46334
46477
  }
46335
46478
  if (!this.generatingStartedAt) this.generatingStartedAt = now;
46479
+ this.lastCompletionSummary = null;
46336
46480
  this.busyEpoch++;
46337
46481
  if (this.generatingDebounceTimer) clearTimeout(this.generatingDebounceTimer);
46338
46482
  this.generatingDebouncePending = { chatTitle, timestamp: now };
@@ -51300,6 +51444,13 @@ function createNativeHistoryDispatcher(reader) {
51300
51444
  if (requestedProviderSid && session.providerSessionId && session.providerSessionId !== requestedProviderSid) {
51301
51445
  return null;
51302
51446
  }
51447
+ let resolvedProviderSessionId = session.providerSessionId;
51448
+ if (reader === "antigravity-cli") {
51449
+ const onDiskUuid = extractAntigravityConversationUuid(session.sourcePath || sourcePath);
51450
+ if (onDiskUuid && (!resolvedProviderSessionId || resolvedProviderSessionId === sessionId)) {
51451
+ resolvedProviderSessionId = onDiskUuid;
51452
+ }
51453
+ }
51303
51454
  return {
51304
51455
  messages: session.messages.map((m) => ({
51305
51456
  role: normalizeRole2(m.role),
@@ -51308,7 +51459,7 @@ function createNativeHistoryDispatcher(reader) {
51308
51459
  kind: typeof m.kind === "string" ? m.kind : "standard",
51309
51460
  workspace: typeof m.workspace === "string" ? m.workspace : workspace || void 0
51310
51461
  })),
51311
- providerSessionId: session.providerSessionId,
51462
+ providerSessionId: resolvedProviderSessionId,
51312
51463
  sourcePath: session.sourcePath,
51313
51464
  sourceMtimeMs: session.sourceMtimeMs,
51314
51465
  nativeHistoryCoverage: session.nativeHistoryCoverage || "full"
@@ -51437,6 +51588,17 @@ function resolveRealPath(value) {
51437
51588
  return value;
51438
51589
  }
51439
51590
  }
51591
+ function extractAntigravityConversationUuid(sourcePath) {
51592
+ if (!sourcePath) return "";
51593
+ const segments = sourcePath.split(/[\\/]/);
51594
+ const base = segments[segments.length - 1] || "";
51595
+ const baseMatch = /^([0-9a-f-]+)\.(?:db|pb)$/i.exec(base);
51596
+ if (baseMatch && isUuidLikeSessionId2(baseMatch[1])) return baseMatch[1];
51597
+ for (const seg of segments) {
51598
+ if (isUuidLikeSessionId2(seg)) return seg;
51599
+ }
51600
+ return "";
51601
+ }
51440
51602
  var AGY_SPAWN_CLAIM_GRACE_MS = 2e3;
51441
51603
  function resolveAntigravityPath(workspace, sessionId, sessionStartedAtMs, instanceId) {
51442
51604
  const agyRoot = path34.join(os25.homedir(), ".gemini", "antigravity-cli");
@@ -51451,10 +51613,24 @@ function resolveAntigravityPath(workspace, sessionId, sessionStartedAtMs, instan
51451
51613
  const brainRoot2 = path34.join(agyRoot, "brain");
51452
51614
  if (fs26.existsSync(brainRoot2)) {
51453
51615
  const cutoff = spawnAwareCutoff(sessionStartedAtMs);
51454
- const entries = fs26.readdirSync(brainRoot2, { withFileTypes: true }).filter((e) => e.isDirectory() && isUuidLikeSessionId2(e.name)).filter((e) => !isAntigravityConversationClaimedByOther(e.name, owner)).map((e) => ({ uuid: e.name, p: path34.join(brainRoot2, e.name), mtime: safeMtime(path34.join(brainRoot2, e.name)) })).filter((e) => e.mtime >= cutoff).sort((a, b) => b.mtime - a.mtime);
51455
- for (const e of entries) {
51456
- const t = path34.join(e.p, ".system_generated", "logs", "transcript.jsonl");
51457
- if (fs26.existsSync(t) && safeSize(t) > 0) {
51616
+ const nonEmptyBrain = (uuid, p) => {
51617
+ const t = path34.join(p, ".system_generated", "logs", "transcript.jsonl");
51618
+ return fs26.existsSync(t) && safeSize(t) > 0 ? t : null;
51619
+ };
51620
+ const all = fs26.readdirSync(brainRoot2, { withFileTypes: true }).filter((e) => e.isDirectory() && isUuidLikeSessionId2(e.name)).filter((e) => !isAntigravityConversationClaimedByOther(e.name, owner)).map((e) => {
51621
+ const p = path34.join(brainRoot2, e.name);
51622
+ return { uuid: e.name, p, mtime: safeMtime(p), birth: safeBirthtime(p) };
51623
+ }).filter((e) => e.mtime >= cutoff);
51624
+ let ordered = [];
51625
+ if (sessionStartedAtMs > 0) {
51626
+ const floor = sessionStartedAtMs - AGY_SPAWN_CLAIM_GRACE_MS;
51627
+ ordered = all.filter((e) => (e.birth > 0 ? e.birth : e.mtime) >= floor).sort((a, b) => (a.birth || a.mtime) - (b.birth || b.mtime));
51628
+ } else {
51629
+ ordered = [...all].sort((a, b) => b.mtime - a.mtime);
51630
+ }
51631
+ for (const e of ordered) {
51632
+ const t = nonEmptyBrain(e.uuid, e.p);
51633
+ if (t) {
51458
51634
  if (owner) claimAntigravityConversation(e.uuid, owner);
51459
51635
  return t;
51460
51636
  }
@@ -51475,6 +51651,7 @@ function pickUnboundConversationDb(convRoot, sessionFloorMs, owner) {
51475
51651
  } catch {
51476
51652
  return null;
51477
51653
  }
51654
+ const applyRecencyCutoff = !(sessionFloorMs > 0);
51478
51655
  const recencyCutoff = Date.now() - RECENT_WINDOW_MS;
51479
51656
  const candidates = [];
51480
51657
  for (const entry of entries) {
@@ -51485,7 +51662,7 @@ function pickUnboundConversationDb(convRoot, sessionFloorMs, owner) {
51485
51662
  if (isAntigravityConversationClaimedByOther(uuid, owner)) continue;
51486
51663
  const p = path34.join(convRoot, entry.name);
51487
51664
  const mtime = safeMtime(p);
51488
- if (mtime < recencyCutoff) continue;
51665
+ if (applyRecencyCutoff && mtime < recencyCutoff) continue;
51489
51666
  candidates.push({ path: p, uuid, mtime, birth: safeBirthtime(p) });
51490
51667
  }
51491
51668
  if (candidates.length === 0) return null;
@@ -68726,7 +68903,11 @@ var SessionRegistry = class {
68726
68903
  byInstanceKey = /* @__PURE__ */ new Map();
68727
68904
  byParentSessionId = /* @__PURE__ */ new Map();
68728
68905
  register(target) {
68906
+ const priorProviderSessionId = this.bySessionId.get(target.sessionId)?.providerSessionId;
68729
68907
  this.unregister(target.sessionId);
68908
+ if (priorProviderSessionId && !target.providerSessionId) {
68909
+ target = { ...target, providerSessionId: priorProviderSessionId };
68910
+ }
68730
68911
  this.bySessionId.set(target.sessionId, target);
68731
68912
  if (target.cdpManagerKey) this.addIndex(this.byManagerKey, target.cdpManagerKey, target.sessionId);
68732
68913
  if (target.instanceKey) this.addIndex(this.byInstanceKey, target.instanceKey, target.sessionId);
@@ -68736,6 +68917,22 @@ var SessionRegistry = class {
68736
68917
  if (!sessionId) return void 0;
68737
68918
  return this.bySessionId.get(sessionId);
68738
68919
  }
68920
+ /**
68921
+ * Record the authoritative provider-native conversation id for a session
68922
+ * (SSOT). Idempotent; a no-op when the session is unknown or the value is
68923
+ * empty or unchanged. Never overwrites a known binding with an empty one.
68924
+ * Returns whether the stored value changed.
68925
+ */
68926
+ setProviderSessionId(sessionId, providerSessionId) {
68927
+ const sid = typeof sessionId === "string" ? sessionId.trim() : "";
68928
+ const value = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
68929
+ if (!sid || !value) return false;
68930
+ const target = this.bySessionId.get(sid);
68931
+ if (!target) return false;
68932
+ if (target.providerSessionId === value) return false;
68933
+ target.providerSessionId = value;
68934
+ return true;
68935
+ }
68739
68936
  unregister(sessionId) {
68740
68937
  if (!sessionId) return;
68741
68938
  const target = this.bySessionId.get(sessionId);