@adhdev/daemon-core 0.9.82-rc.117 → 0.9.82-rc.119
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 +41 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/commands/chat-commands.ts +40 -30
- package/src/commands/router.ts +29 -6
package/package.json
CHANGED
|
@@ -26,6 +26,11 @@ const RECENT_SEND_WINDOW_MS = 1200;
|
|
|
26
26
|
export const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25_000;
|
|
27
27
|
const HERMES_CLI_STARTING_SEND_SETTLE_MS = 2_000;
|
|
28
28
|
const CLI_NATIVE_HISTORY_FRESH_MS = 5 * 60_000;
|
|
29
|
+
// Fallback list for supportsCliNativeTranscript() when the ProviderModule is
|
|
30
|
+
// unavailable (e.g. provider config not yet loaded). The authoritative check is
|
|
31
|
+
// provider.canonicalHistory via isNativeSourceCanonicalHistory(). New providers
|
|
32
|
+
// with native transcripts should set canonicalHistory in their provider.json
|
|
33
|
+
// rather than adding entries here.
|
|
29
34
|
const CLI_NATIVE_TRANSCRIPT_PROVIDERS = new Set(['codex-cli', 'claude-cli', 'hermes-cli', 'antigravity-cli']);
|
|
30
35
|
const recentSendByTarget = new Map<string, number>();
|
|
31
36
|
|
|
@@ -402,11 +407,13 @@ function buildNativeHistoryFallbackReason(args: {
|
|
|
402
407
|
provider?: ProviderModule;
|
|
403
408
|
nativeSource?: string;
|
|
404
409
|
nativeHistoryCoverage?: string;
|
|
410
|
+
unavailableReason?: string;
|
|
405
411
|
nativeMessageCount: number;
|
|
406
412
|
safeMapping: boolean;
|
|
407
413
|
freshEnough: boolean;
|
|
408
414
|
}): string {
|
|
409
415
|
if (!supportsCliNativeTranscript(args.providerType, args.provider)) return 'provider_native_transcript_not_supported';
|
|
416
|
+
if (args.unavailableReason) return `native_history_unavailable:${args.unavailableReason}`;
|
|
410
417
|
if (args.nativeSource === 'native-unavailable') return 'native_history_unavailable';
|
|
411
418
|
if (args.nativeHistoryCoverage === 'partial') return 'native_history_partial';
|
|
412
419
|
if (args.nativeHistoryCoverage === 'unavailable') return 'native_history_unavailable';
|
|
@@ -469,15 +476,20 @@ function hasSafeNativeHistoryMapping(args: {
|
|
|
469
476
|
return messageSessionIds.some((id) => id === explicitSessionId);
|
|
470
477
|
}
|
|
471
478
|
|
|
479
|
+
// Messages carry no historySessionId — cannot confirm they belong to the requested session.
|
|
480
|
+
// Only allow a coordinator transcript that is also confirmed by the PTY side; otherwise
|
|
481
|
+
// fail closed so a same-workspace session's history is never silently accepted.
|
|
472
482
|
if (isCoordinatorTranscript && args.ptyMessages && args.ptyMessages.length > 0) {
|
|
473
483
|
const ptyHasCoordinator = args.ptyMessages.some((m: any) => {
|
|
474
484
|
const text = typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content || '');
|
|
475
485
|
return text.includes('mesh_send_task') || text.includes('mesh_status') || text.includes('mesh_read_chat');
|
|
476
486
|
});
|
|
477
|
-
|
|
478
|
-
return false;
|
|
479
|
-
}
|
|
487
|
+
return ptyHasCoordinator;
|
|
480
488
|
}
|
|
489
|
+
|
|
490
|
+
// No historySessionId in messages and no coordinator cross-check: fail closed.
|
|
491
|
+
// Workspace-only matching must not override an explicit session identity.
|
|
492
|
+
return false;
|
|
481
493
|
}
|
|
482
494
|
const workspace = String(args.workspace || '').trim();
|
|
483
495
|
if (!workspace) return false;
|
|
@@ -498,6 +510,11 @@ function hasSafeNativeHistoryMapping(args: {
|
|
|
498
510
|
return hasOverlappingVisibleConversationText(args.nativeMessages, args.ptyMessages || []);
|
|
499
511
|
}
|
|
500
512
|
|
|
513
|
+
// Provenance boundary: workspace-only native history lookup is never safe
|
|
514
|
+
// because multiple concurrent sessions sharing the same cwd would alias each
|
|
515
|
+
// other. historySessionId (the provider-native session key) is required to
|
|
516
|
+
// establish ownership. hasSafeNativeHistoryMapping() enforces the same
|
|
517
|
+
// invariant after the read; both guards must hold for native history to be used.
|
|
501
518
|
function readCliProviderNativeHistory(agentStr: string, args: {
|
|
502
519
|
canonicalHistory?: ProviderModule['canonicalHistory'];
|
|
503
520
|
historySessionId?: string;
|
|
@@ -507,8 +524,16 @@ function readCliProviderNativeHistory(agentStr: string, args: {
|
|
|
507
524
|
excludeRecentCount: number;
|
|
508
525
|
historyBehavior?: ProviderModule['historyBehavior'];
|
|
509
526
|
scripts?: ProviderScripts;
|
|
510
|
-
exactSessionScoped?: boolean;
|
|
511
527
|
}): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
|
|
528
|
+
if (!args.historySessionId) {
|
|
529
|
+
return {
|
|
530
|
+
messages: [],
|
|
531
|
+
hasMore: false,
|
|
532
|
+
source: 'native-unavailable',
|
|
533
|
+
unavailableReason: 'native_history_workspace_only_lookup_unsafe',
|
|
534
|
+
lookup: 'session',
|
|
535
|
+
} as ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' };
|
|
536
|
+
}
|
|
512
537
|
const sessionHistory = readProviderChatHistory(agentStr, {
|
|
513
538
|
canonicalHistory: args.canonicalHistory,
|
|
514
539
|
historySessionId: args.historySessionId,
|
|
@@ -519,24 +544,10 @@ function readCliProviderNativeHistory(agentStr: string, args: {
|
|
|
519
544
|
historyBehavior: args.historyBehavior,
|
|
520
545
|
scripts: args.scripts as any,
|
|
521
546
|
});
|
|
522
|
-
//
|
|
523
|
-
// workspace
|
|
524
|
-
//
|
|
525
|
-
|
|
526
|
-
if ((sessionHistory as any).source !== 'native-unavailable' || args.exactSessionScoped || !args.historySessionId || !args.workspace) {
|
|
527
|
-
return { ...(sessionHistory as any), lookup: args.historySessionId ? 'session' : 'workspace' };
|
|
528
|
-
}
|
|
529
|
-
const workspaceHistory = readProviderChatHistory(agentStr, {
|
|
530
|
-
canonicalHistory: args.canonicalHistory,
|
|
531
|
-
historySessionId: undefined,
|
|
532
|
-
workspace: args.workspace,
|
|
533
|
-
offset: args.offset,
|
|
534
|
-
limit: args.limit,
|
|
535
|
-
excludeRecentCount: args.excludeRecentCount,
|
|
536
|
-
historyBehavior: args.historyBehavior,
|
|
537
|
-
scripts: args.scripts as any,
|
|
538
|
-
});
|
|
539
|
-
return { ...(workspaceHistory as any), lookup: 'workspace' };
|
|
547
|
+
// Native transcripts are keyed by provider/runtime session identity. Falling
|
|
548
|
+
// back to workspace makes concurrent local Codex/Hermes sessions alias each
|
|
549
|
+
// other when they share the same cwd.
|
|
550
|
+
return { ...(sessionHistory as any), lookup: 'session' };
|
|
540
551
|
}
|
|
541
552
|
|
|
542
553
|
function isNativeHistoryFreshEnough(args: {
|
|
@@ -1223,7 +1234,6 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
|
|
|
1223
1234
|
excludeRecentCount,
|
|
1224
1235
|
historyBehavior: provider?.historyBehavior,
|
|
1225
1236
|
scripts: provider?.scripts as any,
|
|
1226
|
-
exactSessionScoped: exactNativeHistoryScope,
|
|
1227
1237
|
})
|
|
1228
1238
|
: readProviderChatHistory(agentStr, {
|
|
1229
1239
|
canonicalHistory: provider?.canonicalHistory,
|
|
@@ -1363,7 +1373,6 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1363
1373
|
excludeRecentCount: 0,
|
|
1364
1374
|
historyBehavior: provider?.historyBehavior,
|
|
1365
1375
|
scripts: provider?.scripts as any,
|
|
1366
|
-
exactSessionScoped: exactNativeHistoryScope,
|
|
1367
1376
|
});
|
|
1368
1377
|
} catch (error: any) {
|
|
1369
1378
|
const fallbackReason = `native_history_error:${error?.message || String(error)}`;
|
|
@@ -1446,11 +1455,12 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1446
1455
|
const fallbackReason = buildNativeHistoryFallbackReason({
|
|
1447
1456
|
providerType,
|
|
1448
1457
|
provider,
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1458
|
+
nativeSource: (nativeHistory as any).source,
|
|
1459
|
+
nativeHistoryCoverage,
|
|
1460
|
+
unavailableReason,
|
|
1461
|
+
nativeMessageCount: nativeMessages.length,
|
|
1462
|
+
safeMapping,
|
|
1463
|
+
freshEnough,
|
|
1454
1464
|
});
|
|
1455
1465
|
messageSource = buildCliMessageSourceProvenance({
|
|
1456
1466
|
selected: 'pty-parser',
|
|
@@ -1528,7 +1538,6 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1528
1538
|
excludeRecentCount: 0,
|
|
1529
1539
|
historyBehavior: provider?.historyBehavior,
|
|
1530
1540
|
scripts: provider?.scripts as any,
|
|
1531
|
-
exactSessionScoped: exactNativeHistoryScope,
|
|
1532
1541
|
})
|
|
1533
1542
|
: readProviderChatHistory(agentStr, {
|
|
1534
1543
|
canonicalHistory: provider?.canonicalHistory,
|
|
@@ -1581,6 +1590,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1581
1590
|
provider,
|
|
1582
1591
|
nativeSource: (history as any).source,
|
|
1583
1592
|
nativeHistoryCoverage,
|
|
1593
|
+
unavailableReason,
|
|
1584
1594
|
nativeMessageCount: historyMessages.length,
|
|
1585
1595
|
safeMapping,
|
|
1586
1596
|
freshEnough: true,
|
package/src/commands/router.ts
CHANGED
|
@@ -1079,9 +1079,12 @@ function summarizeMeshSessionRecord(record: any): Record<string, unknown> {
|
|
|
1079
1079
|
};
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
|
-
function liveSessionRecordMatchesMeshNode(record: any, meshId: string, nodeId: string): boolean {
|
|
1082
|
+
function liveSessionRecordMatchesMeshNode(record: any, meshId: string, nodeId: string, nodeWorkspace = '', nodeIsMissingLocalWorktree = false): boolean {
|
|
1083
1083
|
const recordNodeId = readStringValue(record?.meta?.meshNodeId);
|
|
1084
1084
|
if (!recordNodeId || recordNodeId !== nodeId) return false;
|
|
1085
|
+
if (nodeIsMissingLocalWorktree) return false;
|
|
1086
|
+
const recordWorkspace = readStringValue(record?.workspace);
|
|
1087
|
+
if (nodeWorkspace && recordWorkspace && recordWorkspace !== nodeWorkspace) return false;
|
|
1085
1088
|
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
1086
1089
|
return !recordMeshId || recordMeshId === meshId;
|
|
1087
1090
|
}
|
|
@@ -1130,9 +1133,15 @@ function collectLiveMeshSessionRecords(args: {
|
|
|
1130
1133
|
liveSessionRecords: any[];
|
|
1131
1134
|
allowCoordinatorSession?: boolean;
|
|
1132
1135
|
}): any[] {
|
|
1136
|
+
const nodeWorkspace = readStringValue(args.node?.workspace);
|
|
1137
|
+
const nodeIsMissingLocalWorktree = args.node?.isLocalWorktree === true
|
|
1138
|
+
&& !!nodeWorkspace
|
|
1139
|
+
&& !fs.existsSync(nodeWorkspace);
|
|
1133
1140
|
const matches = args.liveSessionRecords.filter((record) => {
|
|
1134
|
-
const
|
|
1135
|
-
if (
|
|
1141
|
+
const recordNodeId = readStringValue(record?.meta?.meshNodeId);
|
|
1142
|
+
if (recordNodeId && recordNodeId !== args.nodeId) return false;
|
|
1143
|
+
if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId, nodeWorkspace || '', nodeIsMissingLocalWorktree)) return true;
|
|
1144
|
+
if (nodeIsMissingLocalWorktree) return false;
|
|
1136
1145
|
return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
|
|
1137
1146
|
});
|
|
1138
1147
|
|
|
@@ -1155,11 +1164,15 @@ function buildHistoricalMeshSessions(args: {
|
|
|
1155
1164
|
}): { count: number; sessions: Record<string, unknown>[]; instruction: string } | undefined {
|
|
1156
1165
|
const liveNodeIds = new Set<string>();
|
|
1157
1166
|
const liveWorkspaces = new Set<string>();
|
|
1167
|
+
const missingLocalWorktreeNodeIds = new Set<string>();
|
|
1158
1168
|
for (const node of args.nodes || []) {
|
|
1159
1169
|
const nodeId = readStringValue(node?.id, node?.nodeId);
|
|
1160
1170
|
const workspace = readStringValue(node?.workspace);
|
|
1161
1171
|
if (nodeId) liveNodeIds.add(nodeId);
|
|
1162
1172
|
if (workspace) liveWorkspaces.add(workspace);
|
|
1173
|
+
if (nodeId && node?.isLocalWorktree === true && workspace && !fs.existsSync(workspace)) {
|
|
1174
|
+
missingLocalWorktreeNodeIds.add(nodeId);
|
|
1175
|
+
}
|
|
1163
1176
|
}
|
|
1164
1177
|
|
|
1165
1178
|
const sessions: Record<string, unknown>[] = [];
|
|
@@ -1169,7 +1182,7 @@ function buildHistoricalMeshSessions(args: {
|
|
|
1169
1182
|
if (recordMeshId !== args.meshId) continue;
|
|
1170
1183
|
const recordNodeId = readStringValue(meta.meshNodeId);
|
|
1171
1184
|
const workspace = readStringValue(record?.workspace);
|
|
1172
|
-
const removedNode = !!recordNodeId && !liveNodeIds.has(recordNodeId);
|
|
1185
|
+
const removedNode = !!recordNodeId && (!liveNodeIds.has(recordNodeId) || missingLocalWorktreeNodeIds.has(recordNodeId));
|
|
1173
1186
|
const orphanedWorkspace = !!workspace && !liveWorkspaces.has(workspace) && meta.meshCoordinatorFor !== args.meshId;
|
|
1174
1187
|
if (!removedNode && !orphanedWorkspace) continue;
|
|
1175
1188
|
sessions.push({
|
|
@@ -5636,12 +5649,22 @@ export class DaemonCommandRouter {
|
|
|
5636
5649
|
for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
|
|
5637
5650
|
const nodeId = String(node.id || node.nodeId || '');
|
|
5638
5651
|
const daemonId = readStringValue(node.daemonId);
|
|
5652
|
+
const nodeMachineId = readMeshNodeMachineId(node as Record<string, unknown>);
|
|
5653
|
+
const nodeHostname = readMeshNodeHostname(node as Record<string, unknown>);
|
|
5639
5654
|
const providerPriority = readProviderPriorityFromPolicy(node.policy);
|
|
5655
|
+
const configuredCoordinatorNode = Boolean(
|
|
5656
|
+
nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId,
|
|
5657
|
+
);
|
|
5658
|
+
const sparseConfiguredCoordinatorNode = configuredCoordinatorNode
|
|
5659
|
+
&& !daemonId
|
|
5660
|
+
&& !nodeMachineId
|
|
5661
|
+
&& !nodeHostname;
|
|
5640
5662
|
const isSelfNode = Boolean(
|
|
5641
5663
|
nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId,
|
|
5642
5664
|
) || Boolean(
|
|
5643
5665
|
daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId),
|
|
5644
|
-
) || Boolean(meshRecord?.inline && nodeIndex === 0)
|
|
5666
|
+
) || Boolean(meshRecord?.inline && nodeIndex === 0)
|
|
5667
|
+
|| sparseConfiguredCoordinatorNode;
|
|
5645
5668
|
const machineIdentity = buildMeshNodeMachineIdentity(node as Record<string, unknown>, {
|
|
5646
5669
|
localMachineId,
|
|
5647
5670
|
localDaemonId: this.deps.statusInstanceId,
|
|
@@ -5660,7 +5683,7 @@ export class DaemonCommandRouter {
|
|
|
5660
5683
|
worktreeBranch: node.worktreeBranch,
|
|
5661
5684
|
role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? 'host' : undefined),
|
|
5662
5685
|
daemonId,
|
|
5663
|
-
machineId:
|
|
5686
|
+
machineId: nodeMachineId || node.machineId,
|
|
5664
5687
|
machine: machineIdentity,
|
|
5665
5688
|
machineStatus: node.machineStatus,
|
|
5666
5689
|
health: 'unknown',
|