@adhdev/daemon-core 0.9.82-rc.18 → 0.9.82-rc.181
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/boot/process-hardening.d.ts +50 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +22 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +106 -63
- package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +29 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/commands/handler.d.ts +110 -0
- package/dist/commands/mesh-coordinator.d.ts +72 -1
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/chat-history.d.ts +8 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +72 -1
- package/dist/git/git-commands.d.ts +5 -1
- package/dist/index.d.ts +35 -8
- package/dist/index.js +20754 -5387
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20567 -5270
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +72 -0
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +30 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +90 -0
- package/dist/mesh/mesh-events.d.ts +97 -5
- package/dist/mesh/mesh-fast-forward.d.ts +41 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +58 -1
- package/dist/mesh/mesh-refine-status.d.ts +26 -0
- package/dist/mesh/mesh-work-queue.d.ts +54 -6
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
- package/dist/providers/approval-utils.d.ts +13 -0
- package/dist/providers/cli-provider-instance.d.ts +35 -3
- package/dist/providers/contracts.d.ts +98 -5
- package/dist/providers/external-sources.d.ts +71 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/dispatcher.d.ts +25 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-instance-manager.d.ts +12 -0
- package/dist/providers/provider-instance.d.ts +12 -0
- package/dist/providers/provider-loader.d.ts +26 -4
- package/dist/providers/provider-trust.d.ts +31 -0
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +169 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +56 -0
- package/dist/providers/spec/cli-adapter.d.ts +97 -0
- package/dist/providers/spec/driver.d.ts +159 -0
- package/dist/providers/spec/evaluator.d.ts +47 -0
- package/dist/providers/spec/loader.d.ts +14 -0
- package/dist/providers/spec/native-history-executor.d.ts +39 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/schema.gen.d.ts +511 -0
- package/dist/providers/spec/types.d.ts +212 -0
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +48 -0
- package/dist/repo-mesh-types.d.ts +102 -1
- package/dist/sessions/registry.d.ts +3 -0
- package/dist/shared-types.d.ts +42 -1
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -2
- package/src/agent-stream/poller.ts +2 -3
- package/src/boot/daemon-lifecycle.ts +17 -10
- package/src/boot/process-hardening.ts +89 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +14 -1
- package/src/cli-adapter-types.d.ts +1 -0
- package/src/cli-adapter-types.ts +21 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1086 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +670 -1137
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +13 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +59 -11
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
- package/src/commands/chat-commands.ts +1597 -60
- package/src/commands/cli-manager.ts +230 -3
- package/src/commands/handler.ts +809 -2
- package/src/commands/mesh-coordinator.ts +231 -127
- package/src/commands/router.ts +3495 -502
- package/src/config/chat-history.ts +91 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +280 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +17 -5
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +113 -7
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +486 -0
- package/src/mesh/contracts.ts +329 -0
- package/src/mesh/coordinator-prompt.ts +204 -30
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +437 -0
- package/src/mesh/mesh-events.ts +963 -67
- package/src/mesh/mesh-fast-forward.ts +438 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +457 -104
- package/src/mesh/mesh-refine-status.ts +144 -0
- package/src/mesh/mesh-work-queue.ts +263 -159
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +247 -0
- package/src/providers/approval-utils.d.ts +4 -0
- package/src/providers/approval-utils.ts +47 -5
- package/src/providers/chat-message-normalization.ts +4 -11
- package/src/providers/cli-provider-instance.ts +651 -57
- package/src/providers/contracts.ts +105 -5
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +19 -5
- package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +419 -0
- package/src/providers/native-history/dispatcher.ts +231 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-instance-manager.ts +30 -0
- package/src/providers/provider-instance.ts +9 -0
- package/src/providers/provider-loader.ts +585 -50
- package/src/providers/provider-schema.ts +31 -13
- package/src/providers/provider-trust.ts +114 -0
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +458 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +210 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +168 -0
- package/src/providers/spec/cli-adapter.ts +507 -0
- package/src/providers/spec/driver.ts +540 -0
- package/src/providers/spec/evaluator.ts +298 -0
- package/src/providers/spec/loader.ts +130 -0
- package/src/providers/spec/native-history-executor.ts +706 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/schema.gen.ts +511 -0
- package/src/providers/spec/schema.json +211 -0
- package/src/providers/spec/types.ts +231 -0
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +396 -0
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +112 -1
- package/src/sessions/registry.ts +3 -0
- package/src/shared-types.ts +45 -1
- package/src/status/builders.ts +24 -6
- package/src/status/reporter.ts +15 -0
- package/src/status/snapshot.ts +84 -25
- package/src/system/host-memory.ts +29 -12
- package/src/types.ts +5 -0
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -1345,7 +1345,15 @@ function rewriteCanonicalSavedHistory(agentType: string, historySessionId: strin
|
|
|
1345
1345
|
|
|
1346
1346
|
export type ProviderNativeHistoryScripts = Record<string, ((input: any) => any) | undefined>;
|
|
1347
1347
|
|
|
1348
|
-
type ProviderNativeHistoryReadResult = {
|
|
1348
|
+
type ProviderNativeHistoryReadResult = {
|
|
1349
|
+
records: HistoryMessage[];
|
|
1350
|
+
sourcePath: string;
|
|
1351
|
+
sourceMtimeMs: number;
|
|
1352
|
+
providerSessionId?: string;
|
|
1353
|
+
nativeHistoryCoverage?: string;
|
|
1354
|
+
partialReason?: string;
|
|
1355
|
+
unavailableReason?: string;
|
|
1356
|
+
};
|
|
1349
1357
|
|
|
1350
1358
|
function getNativeHistoryScriptName(canonicalHistory: ProviderCanonicalHistoryConfig | undefined, key: 'readSession' | 'listSessions'): string {
|
|
1351
1359
|
const configured = canonicalHistory?.scripts?.[key];
|
|
@@ -1367,19 +1375,42 @@ function normalizeProviderNativeHistoryRecords(agentType: string, historySession
|
|
|
1367
1375
|
if (!Array.isArray(records)) return [];
|
|
1368
1376
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1369
1377
|
return records
|
|
1370
|
-
.map((record: any) =>
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1378
|
+
.map((record: any) => {
|
|
1379
|
+
const base: HistoryMessage = {
|
|
1380
|
+
ts: typeof record?.ts === 'string' ? record.ts : new Date(Number(record?.receivedAt) || Date.now()).toISOString(),
|
|
1381
|
+
receivedAt: Number(record?.receivedAt) || Date.parse(record?.ts || '') || Date.now(),
|
|
1382
|
+
role: record?.role,
|
|
1383
|
+
content: String(record?.content || ''),
|
|
1384
|
+
kind: record?.kind || (record?.role === 'system' ? 'session_start' : 'standard'),
|
|
1385
|
+
senderName: record?.senderName,
|
|
1386
|
+
agent: agentType,
|
|
1387
|
+
instanceId: record?.instanceId,
|
|
1388
|
+
historySessionId: normalizeSavedHistorySessionId(record?.historySessionId || normalizedSessionId),
|
|
1389
|
+
sessionTitle: record?.sessionTitle,
|
|
1390
|
+
workspace: record?.workspace,
|
|
1391
|
+
} as HistoryMessage;
|
|
1392
|
+
// (A2.3 v2 identity passthrough) — if the producer (native_history.js)
|
|
1393
|
+
// emitted v2 stable identity, keep it across the sanitize layer so
|
|
1394
|
+
// downstream (chat-commands.ts normalizeNativeHistoryMessages) sees
|
|
1395
|
+
// the producer's contract output instead of recomputing from index
|
|
1396
|
+
// and content hash. v1 producers without these fields are unaffected.
|
|
1397
|
+
if (typeof record?.providerUnitKey === 'string' && record.providerUnitKey) {
|
|
1398
|
+
(base as any).providerUnitKey = record.providerUnitKey;
|
|
1399
|
+
}
|
|
1400
|
+
if (typeof record?.bubbleId === 'string' && record.bubbleId) {
|
|
1401
|
+
(base as any).bubbleId = record.bubbleId;
|
|
1402
|
+
}
|
|
1403
|
+
if (typeof record?.sequence === 'number' && Number.isFinite(record.sequence)) {
|
|
1404
|
+
(base as any).sequence = record.sequence;
|
|
1405
|
+
}
|
|
1406
|
+
if (typeof record?._turnKey === 'string' && record._turnKey) {
|
|
1407
|
+
(base as any)._turnKey = record._turnKey;
|
|
1408
|
+
}
|
|
1409
|
+
if (typeof record?.bubbleState === 'string' && record.bubbleState) {
|
|
1410
|
+
(base as any).bubbleState = record.bubbleState;
|
|
1411
|
+
}
|
|
1412
|
+
return sanitizeHistoryMessage(agentType, base);
|
|
1413
|
+
})
|
|
1383
1414
|
.filter(Boolean) as HistoryMessage[];
|
|
1384
1415
|
}
|
|
1385
1416
|
|
|
@@ -1387,27 +1418,40 @@ function callProviderNativeHistoryRead(
|
|
|
1387
1418
|
agentType: string,
|
|
1388
1419
|
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1389
1420
|
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1390
|
-
historySessionId: string,
|
|
1421
|
+
historySessionId: string | undefined,
|
|
1391
1422
|
workspace?: string,
|
|
1423
|
+
excludeInProgressTurn?: boolean,
|
|
1424
|
+
sessionStartedAtMs?: number,
|
|
1425
|
+
envOverrides?: Record<string, string>,
|
|
1426
|
+
forceRefresh?: boolean,
|
|
1392
1427
|
): ProviderNativeHistoryReadResult | null {
|
|
1393
1428
|
const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, 'readSession');
|
|
1394
1429
|
if (!fn) return null;
|
|
1430
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
|
|
1395
1431
|
const result = fn({
|
|
1396
1432
|
agentType,
|
|
1397
|
-
sessionId:
|
|
1398
|
-
historySessionId,
|
|
1433
|
+
sessionId: normalizedSessionId,
|
|
1434
|
+
historySessionId: normalizedSessionId,
|
|
1399
1435
|
workspace,
|
|
1400
1436
|
format: canonicalHistory?.format,
|
|
1401
1437
|
watchPath: canonicalHistory?.watchPath,
|
|
1402
|
-
|
|
1438
|
+
excludeInProgressTurn: excludeInProgressTurn === true,
|
|
1439
|
+
sessionStartedAtMs,
|
|
1440
|
+
envOverrides,
|
|
1441
|
+
forceRefresh: forceRefresh === true,
|
|
1442
|
+
args: { sessionId: normalizedSessionId, historySessionId: normalizedSessionId, workspace, excludeInProgressTurn: excludeInProgressTurn === true, sessionStartedAtMs, envOverrides, forceRefresh: forceRefresh === true },
|
|
1403
1443
|
});
|
|
1404
1444
|
if (!result || typeof result !== 'object') return null;
|
|
1405
|
-
const records = normalizeProviderNativeHistoryRecords(agentType,
|
|
1445
|
+
const records = normalizeProviderNativeHistoryRecords(agentType, normalizedSessionId, (result as any).messages || (result as any).records);
|
|
1406
1446
|
if (records.length === 0) return null;
|
|
1407
1447
|
return {
|
|
1408
1448
|
records,
|
|
1409
1449
|
sourcePath: typeof (result as any).sourcePath === 'string' ? (result as any).sourcePath : '',
|
|
1410
1450
|
sourceMtimeMs: Number((result as any).sourceMtimeMs) || 0,
|
|
1451
|
+
providerSessionId: typeof (result as any).providerSessionId === 'string' ? (result as any).providerSessionId.trim() : undefined,
|
|
1452
|
+
nativeHistoryCoverage: typeof (result as any).nativeHistoryCoverage === 'string' ? (result as any).nativeHistoryCoverage.trim() : undefined,
|
|
1453
|
+
partialReason: typeof (result as any).partialReason === 'string' ? (result as any).partialReason.trim() : undefined,
|
|
1454
|
+
unavailableReason: typeof (result as any).unavailableReason === 'string' ? (result as any).unavailableReason.trim() : undefined,
|
|
1411
1455
|
};
|
|
1412
1456
|
}
|
|
1413
1457
|
|
|
@@ -1417,10 +1461,15 @@ function buildNativeHistoryReadResult(
|
|
|
1417
1461
|
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1418
1462
|
historySessionId: string | undefined,
|
|
1419
1463
|
workspace?: string,
|
|
1464
|
+
excludeInProgressTurn?: boolean,
|
|
1465
|
+
sessionStartedAtMs?: number,
|
|
1466
|
+
envOverrides?: Record<string, string>,
|
|
1467
|
+
forceRefresh?: boolean,
|
|
1420
1468
|
): ProviderNativeHistoryReadResult | null {
|
|
1421
1469
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
|
|
1422
|
-
|
|
1423
|
-
|
|
1470
|
+
const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
|
|
1471
|
+
if (!canonicalHistory || (!normalizedSessionId && !normalizedWorkspace) || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
|
|
1472
|
+
return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace, excludeInProgressTurn, sessionStartedAtMs, envOverrides, forceRefresh);
|
|
1424
1473
|
}
|
|
1425
1474
|
|
|
1426
1475
|
function materializeNativeHistoryToMirror(
|
|
@@ -1476,16 +1525,34 @@ export function readProviderChatHistory(
|
|
|
1476
1525
|
excludeRecentCount?: number;
|
|
1477
1526
|
historyBehavior?: ProviderHistoryBehavior;
|
|
1478
1527
|
scripts?: ProviderNativeHistoryScripts;
|
|
1528
|
+
excludeInProgressTurn?: boolean;
|
|
1529
|
+
sessionStartedAtMs?: number;
|
|
1530
|
+
envOverrides?: Record<string, string>;
|
|
1531
|
+
forceRefresh?: boolean;
|
|
1479
1532
|
} = {},
|
|
1480
|
-
): {
|
|
1481
|
-
|
|
1482
|
-
|
|
1533
|
+
): {
|
|
1534
|
+
messages: HistoryMessage[];
|
|
1535
|
+
hasMore: boolean;
|
|
1536
|
+
source: 'provider-native' | 'adhdev-mirror' | 'native-unavailable';
|
|
1537
|
+
sourcePath?: string;
|
|
1538
|
+
sourceMtimeMs?: number;
|
|
1539
|
+
providerSessionId?: string;
|
|
1540
|
+
nativeHistoryCoverage?: string;
|
|
1541
|
+
partialReason?: string;
|
|
1542
|
+
unavailableReason?: string;
|
|
1543
|
+
} {
|
|
1544
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory) && (options.historySessionId || options.workspace)) {
|
|
1545
|
+
const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace, options.excludeInProgressTurn, options.sessionStartedAtMs, options.envOverrides, options.forceRefresh);
|
|
1483
1546
|
if (!nativeResult) return { messages: [], hasMore: false, source: 'native-unavailable' };
|
|
1484
1547
|
return {
|
|
1485
1548
|
...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
1486
1549
|
source: 'provider-native',
|
|
1487
1550
|
sourcePath: nativeResult.sourcePath,
|
|
1488
1551
|
sourceMtimeMs: nativeResult.sourceMtimeMs,
|
|
1552
|
+
providerSessionId: nativeResult.providerSessionId,
|
|
1553
|
+
nativeHistoryCoverage: nativeResult.nativeHistoryCoverage,
|
|
1554
|
+
partialReason: nativeResult.partialReason,
|
|
1555
|
+
unavailableReason: nativeResult.unavailableReason,
|
|
1489
1556
|
};
|
|
1490
1557
|
}
|
|
1491
1558
|
return {
|
package/src/config/config.ts
CHANGED
|
@@ -272,6 +272,18 @@ export function getConfigDir(): string {
|
|
|
272
272
|
return dir;
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Get the daemon runtime data directory (~/.adhdev/daemon/).
|
|
277
|
+
* Distinct from the user-config dir so runtime state can be cleared independently.
|
|
278
|
+
*/
|
|
279
|
+
export function getDaemonDataDir(): string {
|
|
280
|
+
const dir = join(getConfigDir(), 'daemon');
|
|
281
|
+
if (!existsSync(dir)) {
|
|
282
|
+
mkdirSync(dir, { recursive: true });
|
|
283
|
+
}
|
|
284
|
+
return dir;
|
|
285
|
+
}
|
|
286
|
+
|
|
275
287
|
/**
|
|
276
288
|
* Get the config file path
|
|
277
289
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
10
10
|
import { join } from 'path';
|
|
11
|
-
import { randomUUID } from 'crypto';
|
|
11
|
+
import { createHash, randomBytes, randomUUID } from 'crypto';
|
|
12
12
|
import { getConfigDir } from './config.js';
|
|
13
13
|
import type {
|
|
14
14
|
LocalMeshConfig,
|
|
@@ -18,8 +18,11 @@ import type {
|
|
|
18
18
|
RepoMeshNodePolicy,
|
|
19
19
|
RepoMeshNodeCapabilities,
|
|
20
20
|
RepoMeshCoordinatorConfig,
|
|
21
|
+
RepoMeshHostMetadata,
|
|
22
|
+
RepoMeshDaemonRole,
|
|
21
23
|
} from '../repo-mesh-types.js';
|
|
22
24
|
import { DEFAULT_MESH_POLICY } from '../repo-mesh-types.js';
|
|
25
|
+
import { createDefaultMeshHostMetadata } from '../mesh/mesh-host-ownership.js';
|
|
23
26
|
|
|
24
27
|
// ─── Persistence ────────────────────────────────
|
|
25
28
|
|
|
@@ -39,6 +42,20 @@ function loadMeshConfig(): LocalMeshConfig {
|
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
function normalizeCapabilityTags(value: unknown): string[] | undefined {
|
|
46
|
+
if (!Array.isArray(value)) return undefined;
|
|
47
|
+
const seen = new Set<string>();
|
|
48
|
+
const tags = value
|
|
49
|
+
.map(tag => typeof tag === 'string' ? tag.trim() : '')
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.filter(tag => {
|
|
52
|
+
if (seen.has(tag)) return false;
|
|
53
|
+
seen.add(tag);
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
return tags.length ? tags : undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
function saveMeshConfig(config: LocalMeshConfig): void {
|
|
43
60
|
const path = getMeshConfigPath();
|
|
44
61
|
writeFileSync(path, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
@@ -84,6 +101,7 @@ function mergeMeshPolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMe
|
|
|
84
101
|
}
|
|
85
102
|
const maxParallelTasks = Number(policy.maxParallelTasks);
|
|
86
103
|
policy.maxParallelTasks = Number.isFinite(maxParallelTasks) ? Math.max(1, Math.min(8, Math.floor(maxParallelTasks))) : 2;
|
|
104
|
+
policy.allowAutoPublishSubmoduleMainCommits = policy.allowAutoPublishSubmoduleMainCommits === true;
|
|
87
105
|
if (!SESSION_CLEANUP_MODES.has(String(policy.sessionCleanupOnNodeRemove))) {
|
|
88
106
|
policy.sessionCleanupOnNodeRemove = 'preserve';
|
|
89
107
|
}
|
|
@@ -112,6 +130,7 @@ export interface CreateMeshOptions {
|
|
|
112
130
|
defaultBranch?: string;
|
|
113
131
|
policy?: Partial<RepoMeshPolicy>;
|
|
114
132
|
coordinator?: RepoMeshCoordinatorConfig;
|
|
133
|
+
meshHost?: RepoMeshHostMetadata;
|
|
115
134
|
}
|
|
116
135
|
|
|
117
136
|
export function createMesh(opts: CreateMeshOptions): LocalMeshEntry {
|
|
@@ -133,6 +152,7 @@ export function createMesh(opts: CreateMeshOptions): LocalMeshEntry {
|
|
|
133
152
|
defaultBranch: opts.defaultBranch,
|
|
134
153
|
policy: mergeMeshPolicy(undefined, opts.policy),
|
|
135
154
|
coordinator: opts.coordinator || {},
|
|
155
|
+
meshHost: opts.meshHost || createDefaultMeshHostMetadata(),
|
|
136
156
|
nodes: [],
|
|
137
157
|
createdAt: now,
|
|
138
158
|
updatedAt: now,
|
|
@@ -148,6 +168,7 @@ export interface UpdateMeshOptions {
|
|
|
148
168
|
defaultBranch?: string;
|
|
149
169
|
policy?: Partial<RepoMeshPolicy>;
|
|
150
170
|
coordinator?: RepoMeshCoordinatorConfig;
|
|
171
|
+
meshHost?: RepoMeshHostMetadata;
|
|
151
172
|
}
|
|
152
173
|
|
|
153
174
|
export function updateMesh(meshId: string, opts: UpdateMeshOptions): LocalMeshEntry | undefined {
|
|
@@ -159,6 +180,7 @@ export function updateMesh(meshId: string, opts: UpdateMeshOptions): LocalMeshEn
|
|
|
159
180
|
if (opts.defaultBranch !== undefined) mesh.defaultBranch = opts.defaultBranch;
|
|
160
181
|
if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
|
|
161
182
|
if (opts.coordinator) mesh.coordinator = opts.coordinator;
|
|
183
|
+
if (opts.meshHost) mesh.meshHost = opts.meshHost;
|
|
162
184
|
mesh.updatedAt = new Date().toISOString();
|
|
163
185
|
|
|
164
186
|
saveMeshConfig(config);
|
|
@@ -174,6 +196,240 @@ export function deleteMesh(meshId: string): boolean {
|
|
|
174
196
|
return true;
|
|
175
197
|
}
|
|
176
198
|
|
|
199
|
+
function normalizeManualHostAddress(hostAddress: string): string {
|
|
200
|
+
const normalized = hostAddress.trim().replace(/\/+$/, '');
|
|
201
|
+
if (!normalized) throw new Error('hostAddress required');
|
|
202
|
+
let parsed: URL;
|
|
203
|
+
try {
|
|
204
|
+
parsed = new URL(normalized);
|
|
205
|
+
} catch {
|
|
206
|
+
throw new Error('hostAddress must be a valid http(s) or ws(s) URL');
|
|
207
|
+
}
|
|
208
|
+
if (!['http:', 'https:', 'ws:', 'wss:'].includes(parsed.protocol)) {
|
|
209
|
+
throw new Error('hostAddress must use http, https, ws, or wss');
|
|
210
|
+
}
|
|
211
|
+
return normalized;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function tokenIdForManualPairing(token: string): string {
|
|
215
|
+
return `tok_${createHash('sha256').update(token).digest('hex').slice(0, 16)}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function normalizeTokenExpiry(value: unknown): string | undefined {
|
|
219
|
+
if (typeof value !== 'string' || !value.trim()) return undefined;
|
|
220
|
+
const date = new Date(value);
|
|
221
|
+
if (Number.isNaN(date.getTime())) throw new Error('expiresAt must be a valid ISO date');
|
|
222
|
+
return date.toISOString();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function assertPairingTokenValid(pairing: RepoMeshHostMetadata['pairing'], rawToken: string, nowIso: string): { ok: true; tokenId: string } | { ok: false; reason: string; expectedTokenId?: string; presentedTokenId?: string } {
|
|
226
|
+
const token = rawToken.trim();
|
|
227
|
+
if (!token) return { ok: false, reason: 'token required' };
|
|
228
|
+
const presentedTokenId = tokenIdForManualPairing(token);
|
|
229
|
+
const expectedTokenId = pairing?.tokenId;
|
|
230
|
+
if (!expectedTokenId || pairing?.status === 'not_configured' || pairing?.status === 'revoked') {
|
|
231
|
+
return { ok: false, reason: 'host pairing token is not configured', presentedTokenId };
|
|
232
|
+
}
|
|
233
|
+
if (pairing.expiresAt && new Date(pairing.expiresAt).getTime() <= new Date(nowIso).getTime()) {
|
|
234
|
+
return { ok: false, reason: 'host pairing token expired', expectedTokenId, presentedTokenId };
|
|
235
|
+
}
|
|
236
|
+
if (presentedTokenId !== expectedTokenId) {
|
|
237
|
+
return { ok: false, reason: 'invalid pairing token', expectedTokenId, presentedTokenId };
|
|
238
|
+
}
|
|
239
|
+
return { ok: true, tokenId: presentedTokenId };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface ConfigureMeshHostPairingOptions {
|
|
243
|
+
hostAddress: string;
|
|
244
|
+
token: string;
|
|
245
|
+
now?: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function configureMeshHostPairing(
|
|
249
|
+
meshId: string,
|
|
250
|
+
opts: ConfigureMeshHostPairingOptions,
|
|
251
|
+
): { mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata; hostAddress: string } | undefined {
|
|
252
|
+
const hostAddress = normalizeManualHostAddress(opts.hostAddress);
|
|
253
|
+
const token = opts.token.trim();
|
|
254
|
+
if (!token) throw new Error('token required');
|
|
255
|
+
|
|
256
|
+
const config = loadMeshConfig();
|
|
257
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
258
|
+
if (!mesh) return undefined;
|
|
259
|
+
|
|
260
|
+
const now = opts.now || new Date().toISOString();
|
|
261
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
262
|
+
const meshHost: RepoMeshHostMetadata = {
|
|
263
|
+
...previous,
|
|
264
|
+
role: 'member',
|
|
265
|
+
hostAddress,
|
|
266
|
+
pairing: {
|
|
267
|
+
status: 'pairing',
|
|
268
|
+
tokenId: tokenIdForManualPairing(token),
|
|
269
|
+
lastPairedAt: now,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
mesh.meshHost = meshHost;
|
|
274
|
+
mesh.updatedAt = now;
|
|
275
|
+
saveMeshConfig(config);
|
|
276
|
+
return { mesh, meshHost, hostAddress };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface CreateMeshHostPairingTokenOptions {
|
|
280
|
+
token?: string;
|
|
281
|
+
expiresAt?: string;
|
|
282
|
+
now?: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function createMeshHostPairingToken(
|
|
286
|
+
meshId: string,
|
|
287
|
+
opts: CreateMeshHostPairingTokenOptions = {},
|
|
288
|
+
): { mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata; token: string; tokenId: string; expiresAt?: string } | undefined {
|
|
289
|
+
const config = loadMeshConfig();
|
|
290
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
291
|
+
if (!mesh) return undefined;
|
|
292
|
+
const now = opts.now || new Date().toISOString();
|
|
293
|
+
const token = (opts.token || `mhj_${randomBytes(24).toString('base64url')}`).trim();
|
|
294
|
+
if (!token) throw new Error('token required');
|
|
295
|
+
const tokenId = tokenIdForManualPairing(token);
|
|
296
|
+
const expiresAt = normalizeTokenExpiry(opts.expiresAt);
|
|
297
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
298
|
+
if (previous.role === 'member') {
|
|
299
|
+
throw new Error('Mesh Host daemon required to create host pairing tokens; member daemons cannot mint host join tokens.');
|
|
300
|
+
}
|
|
301
|
+
const meshHost: RepoMeshHostMetadata = {
|
|
302
|
+
...previous,
|
|
303
|
+
role: 'host',
|
|
304
|
+
pairing: {
|
|
305
|
+
status: 'pairing',
|
|
306
|
+
tokenId,
|
|
307
|
+
lastPairedAt: now,
|
|
308
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
mesh.meshHost = meshHost;
|
|
312
|
+
mesh.updatedAt = now;
|
|
313
|
+
saveMeshConfig(config);
|
|
314
|
+
return { mesh, meshHost, token, tokenId, ...(expiresAt ? { expiresAt } : {}) };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export interface MeshHostJoinMemberNodeInput {
|
|
318
|
+
id?: string;
|
|
319
|
+
workspace: string;
|
|
320
|
+
repoRoot?: string;
|
|
321
|
+
daemonId?: string;
|
|
322
|
+
machineId?: string;
|
|
323
|
+
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
324
|
+
policy?: RepoMeshNodePolicy;
|
|
325
|
+
role?: RepoMeshDaemonRole;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface ApplyMeshHostJoinOptions {
|
|
329
|
+
token: string;
|
|
330
|
+
memberNode: MeshHostJoinMemberNodeInput;
|
|
331
|
+
memberMeshId?: string;
|
|
332
|
+
now?: string;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function applyMeshHostJoinRequest(
|
|
336
|
+
meshId: string,
|
|
337
|
+
opts: ApplyMeshHostJoinOptions,
|
|
338
|
+
): { accepted: true; mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata; node: LocalMeshNodeEntry; tokenId: string } | { accepted: false; mesh?: LocalMeshEntry; meshHost?: RepoMeshHostMetadata; tokenId?: string; reason: string } | undefined {
|
|
339
|
+
const config = loadMeshConfig();
|
|
340
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
341
|
+
if (!mesh) return undefined;
|
|
342
|
+
const now = opts.now || new Date().toISOString();
|
|
343
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
344
|
+
if (previous.role === 'member') {
|
|
345
|
+
return { accepted: false, mesh, meshHost: previous, reason: 'Mesh Host daemon required to accept join requests' };
|
|
346
|
+
}
|
|
347
|
+
const meshHost: RepoMeshHostMetadata = { ...previous, role: 'host' };
|
|
348
|
+
const validation = assertPairingTokenValid(meshHost.pairing, opts.token, now);
|
|
349
|
+
if (!validation.ok) {
|
|
350
|
+
mesh.meshHost = {
|
|
351
|
+
...meshHost,
|
|
352
|
+
pairing: {
|
|
353
|
+
...(meshHost.pairing || { status: 'not_configured' as const }),
|
|
354
|
+
status: 'rejected',
|
|
355
|
+
lastRejectedAt: now,
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
mesh.updatedAt = now;
|
|
359
|
+
saveMeshConfig(config);
|
|
360
|
+
return { accepted: false, mesh, meshHost: mesh.meshHost, tokenId: validation.presentedTokenId, reason: validation.reason };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const workspace = opts.memberNode.workspace.trim();
|
|
364
|
+
if (!workspace) throw new Error('memberNode.workspace required');
|
|
365
|
+
const memberId = opts.memberNode.id?.trim();
|
|
366
|
+
let node = mesh.nodes.find(n => (memberId && n.id === memberId) || n.workspace === workspace);
|
|
367
|
+
if (node) {
|
|
368
|
+
node.workspace = workspace;
|
|
369
|
+
node.repoRoot = opts.memberNode.repoRoot;
|
|
370
|
+
node.daemonId = opts.memberNode.daemonId;
|
|
371
|
+
node.machineId = opts.memberNode.machineId;
|
|
372
|
+
node.userOverrides = opts.memberNode.userOverrides || node.userOverrides || {};
|
|
373
|
+
node.policy = { ...(node.policy || {}), ...(opts.memberNode.policy || {}) };
|
|
374
|
+
node.role = 'member';
|
|
375
|
+
} else {
|
|
376
|
+
if (mesh.nodes.length >= 10) throw new Error('Maximum 10 nodes per mesh');
|
|
377
|
+
node = {
|
|
378
|
+
id: memberId || `node_${randomUUID().replace(/-/g, '')}`,
|
|
379
|
+
workspace,
|
|
380
|
+
repoRoot: opts.memberNode.repoRoot,
|
|
381
|
+
daemonId: opts.memberNode.daemonId,
|
|
382
|
+
machineId: opts.memberNode.machineId,
|
|
383
|
+
userOverrides: opts.memberNode.userOverrides || {},
|
|
384
|
+
policy: opts.memberNode.policy || {},
|
|
385
|
+
role: 'member',
|
|
386
|
+
};
|
|
387
|
+
mesh.nodes.push(node);
|
|
388
|
+
}
|
|
389
|
+
mesh.meshHost = {
|
|
390
|
+
...meshHost,
|
|
391
|
+
pairing: {
|
|
392
|
+
...(meshHost.pairing || {}),
|
|
393
|
+
status: 'paired',
|
|
394
|
+
tokenId: validation.tokenId,
|
|
395
|
+
joinedAt: now,
|
|
396
|
+
lastPairedAt: meshHost.pairing?.lastPairedAt || now,
|
|
397
|
+
...(meshHost.pairing?.expiresAt ? { expiresAt: meshHost.pairing.expiresAt } : {}),
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
mesh.updatedAt = now;
|
|
401
|
+
saveMeshConfig(config);
|
|
402
|
+
return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function markMeshHostPairingJoined(
|
|
406
|
+
meshId: string,
|
|
407
|
+
opts: { hostDaemonId?: string; hostNodeId?: string; joinedAt?: string; token?: string; tokenId?: string },
|
|
408
|
+
): { mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata } | undefined {
|
|
409
|
+
const config = loadMeshConfig();
|
|
410
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
411
|
+
if (!mesh) return undefined;
|
|
412
|
+
const now = opts.joinedAt || new Date().toISOString();
|
|
413
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
414
|
+
const tokenId = opts.tokenId || (opts.token ? tokenIdForManualPairing(opts.token) : previous.pairing?.tokenId);
|
|
415
|
+
mesh.meshHost = {
|
|
416
|
+
...previous,
|
|
417
|
+
role: 'member',
|
|
418
|
+
...(opts.hostDaemonId ? { hostDaemonId: opts.hostDaemonId } : {}),
|
|
419
|
+
...(opts.hostNodeId ? { hostNodeId: opts.hostNodeId } : {}),
|
|
420
|
+
pairing: {
|
|
421
|
+
...(previous.pairing || {}),
|
|
422
|
+
status: 'paired',
|
|
423
|
+
...(tokenId ? { tokenId } : {}),
|
|
424
|
+
joinedAt: now,
|
|
425
|
+
lastPairedAt: previous.pairing?.lastPairedAt || now,
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
mesh.updatedAt = now;
|
|
429
|
+
saveMeshConfig(config);
|
|
430
|
+
return { mesh, meshHost: mesh.meshHost };
|
|
431
|
+
}
|
|
432
|
+
|
|
177
433
|
// ─── Node Operations ────────────────────────────
|
|
178
434
|
|
|
179
435
|
export interface AddNodeOptions {
|
|
@@ -181,11 +437,14 @@ export interface AddNodeOptions {
|
|
|
181
437
|
repoRoot?: string;
|
|
182
438
|
daemonId?: string;
|
|
183
439
|
machineId?: string;
|
|
440
|
+
capabilities?: string[];
|
|
184
441
|
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
185
442
|
policy?: RepoMeshNodePolicy;
|
|
186
443
|
isLocalWorktree?: boolean;
|
|
187
444
|
worktreeBranch?: string;
|
|
188
445
|
clonedFromNodeId?: string;
|
|
446
|
+
worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
|
|
447
|
+
role?: RepoMeshDaemonRole;
|
|
189
448
|
}
|
|
190
449
|
|
|
191
450
|
export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntry | undefined {
|
|
@@ -208,11 +467,14 @@ export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntr
|
|
|
208
467
|
repoRoot: opts.repoRoot,
|
|
209
468
|
daemonId: opts.daemonId,
|
|
210
469
|
machineId: opts.machineId,
|
|
470
|
+
capabilities: normalizeCapabilityTags(opts.capabilities),
|
|
211
471
|
userOverrides: opts.userOverrides || {},
|
|
212
472
|
policy: opts.policy || {},
|
|
213
473
|
isLocalWorktree: opts.isLocalWorktree,
|
|
214
474
|
worktreeBranch: opts.worktreeBranch,
|
|
215
475
|
clonedFromNodeId: opts.clonedFromNodeId,
|
|
476
|
+
worktreeBootstrap: opts.worktreeBootstrap,
|
|
477
|
+
role: opts.role,
|
|
216
478
|
};
|
|
217
479
|
|
|
218
480
|
mesh.nodes.push(node);
|
|
@@ -238,7 +500,14 @@ export function removeNode(meshId: string, nodeId: string): boolean {
|
|
|
238
500
|
export function updateNode(
|
|
239
501
|
meshId: string,
|
|
240
502
|
nodeId: string,
|
|
241
|
-
opts: {
|
|
503
|
+
opts: {
|
|
504
|
+
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
505
|
+
policy?: RepoMeshNodePolicy;
|
|
506
|
+
worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
|
|
507
|
+
/** Per-node instruction surfaced in the coordinator prompt. Pass an
|
|
508
|
+
* empty string or undefined to clear it. */
|
|
509
|
+
systemPrompt?: string;
|
|
510
|
+
},
|
|
242
511
|
): LocalMeshNodeEntry | undefined {
|
|
243
512
|
const config = loadMeshConfig();
|
|
244
513
|
const mesh = config.meshes.find(m => m.id === meshId);
|
|
@@ -249,6 +518,15 @@ export function updateNode(
|
|
|
249
518
|
|
|
250
519
|
if (opts.userOverrides) node.userOverrides = { ...node.userOverrides, ...opts.userOverrides };
|
|
251
520
|
if (opts.policy) node.policy = { ...node.policy, ...opts.policy };
|
|
521
|
+
if (opts.worktreeBootstrap) node.worktreeBootstrap = opts.worktreeBootstrap;
|
|
522
|
+
if (Object.prototype.hasOwnProperty.call(opts, 'systemPrompt')) {
|
|
523
|
+
// Honor explicit clears: { systemPrompt: undefined } drops the field.
|
|
524
|
+
if (opts.systemPrompt && opts.systemPrompt.trim()) {
|
|
525
|
+
node.systemPrompt = opts.systemPrompt;
|
|
526
|
+
} else {
|
|
527
|
+
delete node.systemPrompt;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
252
530
|
mesh.updatedAt = new Date().toISOString();
|
|
253
531
|
saveMeshConfig(config);
|
|
254
532
|
return node;
|
|
@@ -92,12 +92,18 @@ export function buildSessionReadStateKey(sessionId: string, providerSessionId?:
|
|
|
92
92
|
|
|
93
93
|
export function getSessionSeenAt(state: DaemonState, sessionId: string, providerSessionId?: string | null): number {
|
|
94
94
|
const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
|
|
95
|
-
return state.sessionReads?.[providerKey] || state.sessionReads?.[sessionId] || 0;
|
|
95
|
+
return Math.max(state.sessionReads?.[providerKey] || 0, state.sessionReads?.[sessionId] || 0);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function getSessionSeenMarker(state: DaemonState, sessionId: string, providerSessionId?: string | null): string {
|
|
99
99
|
const providerKey = buildSessionReadStateKey(sessionId, providerSessionId);
|
|
100
|
-
|
|
100
|
+
const providerSeenAt = state.sessionReads?.[providerKey] || 0;
|
|
101
|
+
const sessionSeenAt = state.sessionReads?.[sessionId] || 0;
|
|
102
|
+
const providerMarker = state.sessionReadMarkers?.[providerKey] || '';
|
|
103
|
+
const sessionMarker = state.sessionReadMarkers?.[sessionId] || '';
|
|
104
|
+
if (sessionSeenAt > providerSeenAt && sessionMarker) return sessionMarker;
|
|
105
|
+
if (providerSeenAt > sessionSeenAt && providerMarker) return providerMarker;
|
|
106
|
+
return providerMarker || sessionMarker;
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
export function getSessionNotificationDismissal(state: DaemonState, sessionId: string, providerSessionId?: string | null): string {
|
|
@@ -802,7 +802,16 @@ export async function handleCliSend(ctx: DevServerContext, req: http.IncomingMes
|
|
|
802
802
|
}
|
|
803
803
|
|
|
804
804
|
try {
|
|
805
|
-
|
|
805
|
+
if (target.category === 'cli') {
|
|
806
|
+
const bundle = getCliTargetBundle(ctx, type, instanceId);
|
|
807
|
+
if (!bundle) {
|
|
808
|
+
ctx.json(res, 404, { error: `No running CLI adapter found for: ${type || instanceId}` });
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
await bundle.adapter.sendMessage(text);
|
|
812
|
+
} else {
|
|
813
|
+
ctx.instanceManager!.sendEvent(target.instanceId, 'send_message', { text });
|
|
814
|
+
}
|
|
806
815
|
ctx.json(res, 200, { sent: true, type: target.type, instanceId: target.instanceId });
|
|
807
816
|
} catch (e: any) {
|
|
808
817
|
ctx.json(res, 500, { error: `Send failed: ${e.message}` });
|