@adhdev/daemon-core 0.9.82-rc.440 → 0.9.82-rc.441

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.440",
3
+ "version": "0.9.82-rc.441",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "author": "vilmire",
47
47
  "license": "AGPL-3.0-or-later",
48
48
  "dependencies": {
49
- "@adhdev/mesh-shared": "0.9.82-rc.440",
49
+ "@adhdev/mesh-shared": "0.9.82-rc.441",
50
50
  "@adhdev/session-host-core": "*",
51
51
  "@agentclientprotocol/sdk": "^0.16.1",
52
52
  "ajv": "^8.20.0",
@@ -50,6 +50,34 @@ const HOT_TAIL_MIN_LIMIT = 60;
50
50
  // and routes solely through canonicalHistory.contractVersion +
51
51
  // isNativeSourceCanonicalHistory().
52
52
  const CLI_NATIVE_TRANSCRIPT_PROVIDERS = new Set(['codex-cli', 'claude-cli', 'hermes-cli', 'antigravity-cli']);
53
+
54
+ // Last successfully-bound provider-native session id, keyed by the mesh session
55
+ // id (targetSessionId) the read was scoped to. The live pin lives on the
56
+ // CliProviderInstance and is torn down when the turn ends; a *post-turn* read
57
+ // then finds historySessionId empty AND canBindFromLiveSession=false (no live
58
+ // spawnedAtMs), so readCliProviderNativeHistory would fail closed with
59
+ // native_history_workspace_only_lookup_unsafe and surface providerSessionId=null
60
+ // + zero rows even though the transcript is physically present in state.db.
61
+ // Persisting the last resolved id here lets that later read reuse the known pin
62
+ // and run the native query normally instead of fail-closing. Refreshed on every
63
+ // successful bind; never lets an empty id clear a known pin. Keyed by mesh
64
+ // session id so pins never alias across distinct sessions sharing a workspace.
65
+ const lastBoundProviderSessionIdByMeshSession = new Map<string, string>();
66
+
67
+ function recordBoundProviderSessionId(meshSessionId: string | undefined, providerSessionId: string | undefined): void {
68
+ const key = typeof meshSessionId === 'string' ? meshSessionId.trim() : '';
69
+ const value = typeof providerSessionId === 'string' ? providerSessionId.trim() : '';
70
+ if (!key || !value) return;
71
+ lastBoundProviderSessionIdByMeshSession.set(key, value);
72
+ }
73
+
74
+ function getBoundProviderSessionIdPin(meshSessionId: string | undefined): string | undefined {
75
+ const key = typeof meshSessionId === 'string' ? meshSessionId.trim() : '';
76
+ if (!key) return undefined;
77
+ const pinned = lastBoundProviderSessionIdByMeshSession.get(key);
78
+ return pinned && pinned.trim() ? pinned.trim() : undefined;
79
+ }
80
+
53
81
  const warnedLegacyNativeAllowlistHits = new Set<string>();
54
82
  function warnLegacyNativeAllowlistHit(providerType: string): void {
55
83
  if (warnedLegacyNativeAllowlistHits.has(providerType)) return;
@@ -1055,13 +1083,49 @@ function readCliProviderNativeHistory(agentStr: string, args: {
1055
1083
  excludeInProgressTurn?: boolean;
1056
1084
  sessionStartedAtMs?: number;
1057
1085
  envOverrides?: Record<string, string>;
1086
+ // Last provider-native session id previously bound for this mesh session
1087
+ // (see lastBoundProviderSessionIdByMeshSession). When historySessionId is
1088
+ // empty and no live session can be bound (post-turn read), reuse this pin so
1089
+ // the native query runs against the known session instead of fail-closing.
1090
+ pinnedProviderSessionId?: string;
1091
+ // Opt-in last-resort: when there is no caller session id, no live binding,
1092
+ // and NO pin was ever recorded, allow a workspace-scoped read (newest
1093
+ // session in state.db with rows for this workspace). Strictly behind the pin
1094
+ // — it never fires when pinnedProviderSessionId is set — and still subject to
1095
+ // the downstream hasSafeNativeHistoryMapping workspace-overlap gate. Only the
1096
+ // read_chat path opts in, and only with a concrete workspace.
1097
+ allowWorkspaceLatestFallback?: boolean;
1058
1098
  }): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
1059
1099
  const canBindFromLiveSession = !args.historySessionId
1060
1100
  && typeof args.sessionStartedAtMs === 'number'
1061
1101
  && args.sessionStartedAtMs > 0
1062
1102
  && typeof args.workspace === 'string'
1063
1103
  && args.workspace.trim().length > 0;
1064
- if (!args.historySessionId && !canBindFromLiveSession) {
1104
+ const pinnedProviderSessionId = typeof args.pinnedProviderSessionId === 'string'
1105
+ ? args.pinnedProviderSessionId.trim()
1106
+ : '';
1107
+ // Pin reuse (PRIMARY): a later read whose live binding is gone
1108
+ // (historySessionId empty, no live spawnedAtMs) can still resolve to the
1109
+ // session it was last bound to. Read THAT session directly by threading the
1110
+ // pin through as historySessionId — same code path an explicit session read
1111
+ // takes — instead of fail-closing. Never overrides a caller-supplied
1112
+ // historySessionId; only kicks in when there is none.
1113
+ const effectiveHistorySessionId = args.historySessionId || (!canBindFromLiveSession ? pinnedProviderSessionId : '');
1114
+ // Last-resort workspace-latest (b): only when nothing above resolved a
1115
+ // session id AND no pin exists AND the caller opted in with a workspace.
1116
+ // Strictly behind pin reuse — pinnedProviderSessionId being set disables it.
1117
+ const workspaceLatestFallback = !effectiveHistorySessionId
1118
+ && !canBindFromLiveSession
1119
+ && !pinnedProviderSessionId
1120
+ && args.allowWorkspaceLatestFallback === true
1121
+ && typeof args.workspace === 'string'
1122
+ && args.workspace.trim().length > 0;
1123
+ if (!effectiveHistorySessionId && !canBindFromLiveSession && !workspaceLatestFallback) {
1124
+ // No caller session id, no live binding, no known pin, no opted-in
1125
+ // workspace-latest. This is the genuinely-unresolvable case — a
1126
+ // workspace-only lookup here could alias a concurrent session sharing the
1127
+ // cwd, so fail closed as before. The pin/live/workspace-latest paths are
1128
+ // all checked AHEAD of this so a resolvable session is never dropped here.
1065
1129
  return {
1066
1130
  messages: [],
1067
1131
  hasMore: false,
@@ -1072,7 +1136,7 @@ function readCliProviderNativeHistory(agentStr: string, args: {
1072
1136
  }
1073
1137
  const sessionHistory = readProviderChatHistory(agentStr, {
1074
1138
  canonicalHistory: args.canonicalHistory,
1075
- historySessionId: args.historySessionId,
1139
+ historySessionId: effectiveHistorySessionId || undefined,
1076
1140
  workspace: args.workspace,
1077
1141
  offset: args.offset,
1078
1142
  limit: args.limit,
@@ -1087,10 +1151,11 @@ function readCliProviderNativeHistory(agentStr: string, args: {
1087
1151
  ? (sessionHistory as any).providerSessionId.trim()
1088
1152
  : '';
1089
1153
  // A fresh live session can be bound without a provider id when the native
1090
- // reader matched both cwd and session_meta.timestamp to spawnedAtMs.
1154
+ // reader matched both cwd and session_meta.timestamp to spawnedAtMs. A
1155
+ // pin-bound read is always session-scoped (we passed an explicit id).
1091
1156
  return {
1092
1157
  ...(sessionHistory as any),
1093
- lookup: args.historySessionId || (canBindFromLiveSession && boundProviderSessionId)
1158
+ lookup: effectiveHistorySessionId || (canBindFromLiveSession && boundProviderSessionId)
1094
1159
  ? 'session'
1095
1160
  : 'workspace',
1096
1161
  };
@@ -1476,6 +1541,7 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
1476
1541
  scripts: provider?.scripts as any,
1477
1542
  sessionStartedAtMs: sessionStartedAtMsFromRegistry(h, args?.targetSessionId),
1478
1543
  envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
1544
+ pinnedProviderSessionId: getBoundProviderSessionIdPin(args?.targetSessionId),
1479
1545
  })
1480
1546
  : readProviderChatHistory(agentStr, {
1481
1547
  canonicalHistory: provider?.nativeHistory,
@@ -1495,6 +1561,9 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
1495
1561
  const historyProviderSessionId = typeof (result as any)?.providerSessionId === 'string'
1496
1562
  ? (result as any).providerSessionId
1497
1563
  : readHistorySessionIdFromMessages(messages) || historySessionId;
1564
+ if (typeof (result as any)?.providerSessionId === 'string' && (result as any).providerSessionId.trim()) {
1565
+ recordBoundProviderSessionId(args?.targetSessionId, (result as any).providerSessionId.trim());
1566
+ }
1498
1567
  const safeMapping = hasSafeNativeHistoryMapping({
1499
1568
  historySessionId: lookup === 'workspace' ? undefined : historySessionId,
1500
1569
  providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
@@ -1702,8 +1771,24 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1702
1771
  scripts: provider?.scripts as any,
1703
1772
  excludeInProgressTurn: returnedStatus === 'waiting_approval',
1704
1773
  sessionStartedAtMs: sessionStartedAtMsFromRegistry(h, args?.targetSessionId),
1705
- envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
1774
+ envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
1775
+ pinnedProviderSessionId: getBoundProviderSessionIdPin(targetSessionId),
1776
+ // Last-resort only when no pin was ever recorded for this
1777
+ // session; the downstream workspace-overlap safety gate
1778
+ // still filters an aliased session out.
1779
+ allowWorkspaceLatestFallback: !getBoundProviderSessionIdPin(targetSessionId),
1706
1780
  });
1781
+ // Refresh the per-mesh-session pin whenever a native read
1782
+ // resolves a concrete provider-native session id. A later
1783
+ // post-turn read (live binding gone) can then reuse it
1784
+ // instead of fail-closing. Only a non-empty resolved id
1785
+ // updates the pin; an empty result never clears a known one.
1786
+ const resolvedProviderSessionId = typeof nativeHistory?.providerSessionId === 'string'
1787
+ ? nativeHistory.providerSessionId.trim()
1788
+ : '';
1789
+ if (resolvedProviderSessionId) {
1790
+ recordBoundProviderSessionId(targetSessionId, resolvedProviderSessionId);
1791
+ }
1707
1792
  } catch (error: any) {
1708
1793
  nativeHistoryError = error;
1709
1794
  nativeHistory = null;
@@ -2026,10 +2111,32 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
2026
2111
  const intendedWorkspace = argsWorkspace;
2027
2112
  const supportsNative = supportsCliNativeTranscript(agentStr, provider)
2028
2113
  && isNativeSourceCanonicalHistory(provider?.nativeHistory);
2114
+ // Post-turn read (no live adapter): getHistorySessionId falls back to
2115
+ // the daemon runtime session id when no provider-native id was ever
2116
+ // registered. That runtime id is NOT a real provider session, so a
2117
+ // native read keyed on it resolves nothing (providerSessionId=null,
2118
+ // zero rows) even though the transcript is present in state.db. When
2119
+ // this is that runtime fallback (historySessionId === targetSid and no
2120
+ // explicit id was passed) and we hold a pin from an earlier bound
2121
+ // read, prefer the pin so the query hits the real session.
2122
+ const pinnedProviderSessionIdForHistory = getBoundProviderSessionIdPin(targetSid);
2123
+ const historySessionIdIsRuntimeFallback = Boolean(
2124
+ targetSid
2125
+ && historySessionId === targetSid
2126
+ && !getExplicitHistorySessionId(args),
2127
+ );
2128
+ // When this is the runtime fallback (not a real provider id): prefer
2129
+ // the pin if we have one, else drop the runtime id entirely so the
2130
+ // pin-reuse / workspace-latest logic inside readCliProviderNativeHistory
2131
+ // can engage (passing the runtime id as historySessionId would pin the
2132
+ // query to a non-existent session and never reach those paths).
2133
+ const effectiveHistorySessionIdForRead = historySessionIdIsRuntimeFallback
2134
+ ? (pinnedProviderSessionIdForHistory || undefined)
2135
+ : historySessionId;
2029
2136
  const history = supportsNative
2030
2137
  ? readCliProviderNativeHistory(agentStr, {
2031
2138
  canonicalHistory: provider?.nativeHistory,
2032
- historySessionId,
2139
+ historySessionId: effectiveHistorySessionIdForRead,
2033
2140
  workspace,
2034
2141
  offset: 0,
2035
2142
  limit: historyLimit,
@@ -2037,7 +2144,11 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
2037
2144
  historyBehavior: provider?.historyBehavior,
2038
2145
  scripts: provider?.scripts as any,
2039
2146
  sessionStartedAtMs: sessionStartedAtMsFromRegistry(h, args?.targetSessionId),
2040
- envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
2147
+ envOverrides: sessionSpawnEnvFromAdapter(h, args?.targetSessionId),
2148
+ pinnedProviderSessionId: pinnedProviderSessionIdForHistory,
2149
+ // Last-resort only when no pin was ever recorded AND the
2150
+ // runtime fallback did not resolve a real provider session.
2151
+ allowWorkspaceLatestFallback: !pinnedProviderSessionIdForHistory && historySessionIdIsRuntimeFallback,
2041
2152
  })
2042
2153
  : readProviderChatHistory(agentStr, {
2043
2154
  canonicalHistory: provider?.nativeHistory,
@@ -2055,19 +2166,28 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
2055
2166
  : [];
2056
2167
  const historyProviderSessionId = typeof (history as any)?.providerSessionId === 'string'
2057
2168
  ? (history as any).providerSessionId
2058
- : readHistorySessionIdFromMessages(historyMessages) || historySessionId;
2169
+ : readHistorySessionIdFromMessages(historyMessages) || effectiveHistorySessionIdForRead;
2170
+ // Refresh the pin whenever this path resolves a real provider id.
2171
+ if (typeof (history as any)?.providerSessionId === 'string' && (history as any).providerSessionId.trim()) {
2172
+ recordBoundProviderSessionId(targetSid, (history as any).providerSessionId.trim());
2173
+ }
2174
+ // Use the id we actually read with (pin / real provider id), NOT the
2175
+ // raw runtime-fallback historySessionId — otherwise the mapping guard
2176
+ // compares the stamped messages' real id against the runtime id and
2177
+ // fails closed, undoing the pin reuse.
2178
+ const mappingSessionId = effectiveHistorySessionIdForRead;
2059
2179
  const safeMapping = supportsNative
2060
2180
  ? hasSafeNativeHistoryMapping({
2061
- historySessionId: lookup === 'workspace' ? undefined : historySessionId,
2181
+ historySessionId: lookup === 'workspace' ? undefined : mappingSessionId,
2062
2182
  providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
2063
2183
  workspace,
2064
2184
  nativeMessages: historyMessages,
2065
2185
  })
2066
2186
  : false;
2067
2187
  const trustedExactNativeIdentity = lookup !== 'workspace'
2068
- && Boolean(historySessionId)
2188
+ && Boolean(mappingSessionId)
2069
2189
  && Boolean(historyProviderSessionId)
2070
- && historySessionId === historyProviderSessionId;
2190
+ && mappingSessionId === historyProviderSessionId;
2071
2191
 
2072
2192
  const machineSessionKey = String(
2073
2193
  args?.targetSessionId