@adhdev/daemon-core 0.9.82-rc.23 → 0.9.82-rc.231
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/daemon-lifecycle.d.ts +1 -0
- 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 +23 -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/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +3 -3
- package/dist/cli-adapters/terminal-backends/types.d.ts +1 -2
- package/dist/cli-adapters/terminal-screen.d.ts +2 -11
- package/dist/commands/cli-manager.d.ts +2 -1
- package/dist/commands/handler.d.ts +110 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router.d.ts +32 -1
- package/dist/config/chat-history.d.ts +9 -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 +9 -1
- package/dist/git/git-diff.d.ts +6 -0
- package/dist/git/git-types.d.ts +2 -0
- package/dist/index.d.ts +47 -11
- package/dist/index.js +30490 -11310
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +30346 -11258
- 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/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-prompt.d.ts +36 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +90 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
- package/dist/mesh/mesh-events-coordinator.d.ts +151 -0
- package/dist/mesh/mesh-events-pending.d.ts +33 -0
- package/dist/mesh/mesh-events-stale.d.ts +40 -0
- package/dist/mesh/mesh-events-utils.d.ts +14 -0
- package/dist/mesh/mesh-events.d.ts +5 -40
- 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-reconciliation.d.ts +23 -3
- package/dist/mesh/mesh-ledger.d.ts +77 -1
- package/dist/mesh/mesh-missions.d.ts +58 -0
- package/dist/mesh/mesh-refine-status.d.ts +26 -0
- package/dist/mesh/mesh-review-inbox.d.ts +90 -0
- package/dist/mesh/mesh-runtime-store.d.ts +324 -0
- package/dist/mesh/mesh-task-stats.d.ts +49 -0
- package/dist/mesh/mesh-work-queue.d.ts +137 -6
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +215 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +135 -0
- package/dist/providers/acp-provider-instance.d.ts +2 -0
- package/dist/providers/approval-utils.d.ts +13 -0
- package/dist/providers/cli-provider-instance.d.ts +40 -3
- package/dist/providers/contracts.d.ts +130 -6
- 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 +27 -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 +13 -0
- package/dist/providers/provider-instance.d.ts +13 -1
- 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 +203 -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 +52 -0
- package/dist/providers/spec/cli-adapter.d.ts +102 -0
- package/dist/providers/spec/evaluator.d.ts +23 -0
- package/dist/providers/spec/fsm-driver.d.ts +231 -0
- package/dist/providers/spec/fsm-evaluator.d.ts +72 -0
- package/dist/providers/spec/fsm-loader.d.ts +14 -0
- package/dist/providers/spec/fsm-types.d.ts +91 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/types.d.ts +129 -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 +126 -1
- package/dist/sessions/registry.d.ts +6 -0
- package/dist/shared-types-extra.d.ts +2 -4
- package/dist/shared-types.d.ts +42 -1
- package/dist/status/normalize.d.ts +1 -1
- package/dist/status/normalize.js +1 -0
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +1 -0
- package/dist/status/normalize.mjs.map +1 -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 +6 -3
- package/src/agent-stream/poller.ts +2 -3
- package/src/boot/daemon-lifecycle.ts +22 -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 +20 -1
- package/src/cli-adapter-types.d.ts +1 -0
- package/src/cli-adapter-types.ts +22 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1162 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +673 -1139
- 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 +35 -29
- package/src/cli-adapters/terminal-backends/types.ts +1 -3
- package/src/cli-adapters/terminal-screen.ts +11 -81
- package/src/commands/chat-commands.ts +1806 -60
- package/src/commands/cli-manager.ts +286 -14
- package/src/commands/handler.ts +809 -2
- package/src/commands/mesh-coordinator.ts +332 -122
- package/src/commands/router.ts +4624 -538
- package/src/config/chat-history.ts +95 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +303 -3
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-auto-implement.ts +3 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +42 -12
- package/src/git/git-diff.ts +53 -0
- package/src/git/git-status.ts +35 -6
- package/src/git/git-types.ts +2 -0
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +127 -10
- 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/contracts.ts +329 -0
- package/src/mesh/coordinator-prompt.ts +219 -31
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +437 -0
- package/src/mesh/mesh-delivery-policy.ts +315 -0
- package/src/mesh/mesh-events-coordinator.ts +1484 -0
- package/src/mesh/mesh-events-pending.ts +371 -0
- package/src/mesh/mesh-events-stale.ts +283 -0
- package/src/mesh/mesh-events-utils.ts +175 -0
- package/src/mesh/mesh-events.ts +24 -908
- package/src/mesh/mesh-fast-forward.ts +438 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger-reconciliation.ts +12 -5
- package/src/mesh/mesh-ledger.ts +587 -102
- package/src/mesh/mesh-missions.ts +151 -0
- package/src/mesh/mesh-refine-status.ts +144 -0
- package/src/mesh/mesh-review-inbox.ts +307 -0
- package/src/mesh/mesh-runtime-store.ts +1305 -0
- package/src/mesh/mesh-task-stats.ts +154 -0
- package/src/mesh/mesh-work-queue.ts +490 -166
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +403 -0
- package/src/mesh/worktree-bootstrap-config.ts +326 -0
- package/src/providers/acp-provider-instance.ts +15 -1
- 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 +850 -71
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +141 -6
- 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 +479 -0
- package/src/providers/native-history/dispatcher.ts +340 -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 +10 -1
- package/src/providers/provider-loader.ts +584 -50
- package/src/providers/provider-schema.ts +87 -14
- 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 +504 -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 +229 -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 +138 -0
- package/src/providers/spec/cli-adapter.ts +646 -0
- package/src/providers/spec/evaluator.ts +321 -0
- package/src/providers/spec/fsm-driver.ts +749 -0
- package/src/providers/spec/fsm-evaluator.ts +255 -0
- package/src/providers/spec/fsm-loader.ts +102 -0
- package/src/providers/spec/fsm-types.ts +184 -0
- package/src/providers/spec/native-history-executor.ts +943 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/types.ts +173 -0
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +425 -0
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +138 -1
- package/src/sessions/registry.ts +6 -0
- package/src/shared-types-extra.ts +2 -4
- package/src/shared-types.ts +45 -1
- package/src/status/builders.ts +26 -6
- package/src/status/normalize.ts +2 -0
- 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/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -1345,7 +1345,16 @@ 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
|
+
workspace?: string;
|
|
1354
|
+
nativeHistoryCoverage?: string;
|
|
1355
|
+
partialReason?: string;
|
|
1356
|
+
unavailableReason?: string;
|
|
1357
|
+
};
|
|
1349
1358
|
|
|
1350
1359
|
function getNativeHistoryScriptName(canonicalHistory: ProviderCanonicalHistoryConfig | undefined, key: 'readSession' | 'listSessions'): string {
|
|
1351
1360
|
const configured = canonicalHistory?.scripts?.[key];
|
|
@@ -1367,19 +1376,42 @@ function normalizeProviderNativeHistoryRecords(agentType: string, historySession
|
|
|
1367
1376
|
if (!Array.isArray(records)) return [];
|
|
1368
1377
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1369
1378
|
return records
|
|
1370
|
-
.map((record: any) =>
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1379
|
+
.map((record: any) => {
|
|
1380
|
+
const base: HistoryMessage = {
|
|
1381
|
+
ts: typeof record?.ts === 'string' ? record.ts : new Date(Number(record?.receivedAt) || Date.now()).toISOString(),
|
|
1382
|
+
receivedAt: Number(record?.receivedAt) || Date.parse(record?.ts || '') || Date.now(),
|
|
1383
|
+
role: record?.role,
|
|
1384
|
+
content: String(record?.content || ''),
|
|
1385
|
+
kind: record?.kind || (record?.role === 'system' ? 'session_start' : 'standard'),
|
|
1386
|
+
senderName: record?.senderName,
|
|
1387
|
+
agent: agentType,
|
|
1388
|
+
instanceId: record?.instanceId,
|
|
1389
|
+
historySessionId: normalizeSavedHistorySessionId(record?.historySessionId || normalizedSessionId),
|
|
1390
|
+
sessionTitle: record?.sessionTitle,
|
|
1391
|
+
workspace: record?.workspace,
|
|
1392
|
+
} as HistoryMessage;
|
|
1393
|
+
// (A2.3 v2 identity passthrough) — if the producer (native_history.js)
|
|
1394
|
+
// emitted v2 stable identity, keep it across the sanitize layer so
|
|
1395
|
+
// downstream (chat-commands.ts normalizeNativeHistoryMessages) sees
|
|
1396
|
+
// the producer's contract output instead of recomputing from index
|
|
1397
|
+
// and content hash. v1 producers without these fields are unaffected.
|
|
1398
|
+
if (typeof record?.providerUnitKey === 'string' && record.providerUnitKey) {
|
|
1399
|
+
(base as any).providerUnitKey = record.providerUnitKey;
|
|
1400
|
+
}
|
|
1401
|
+
if (typeof record?.bubbleId === 'string' && record.bubbleId) {
|
|
1402
|
+
(base as any).bubbleId = record.bubbleId;
|
|
1403
|
+
}
|
|
1404
|
+
if (typeof record?.sequence === 'number' && Number.isFinite(record.sequence)) {
|
|
1405
|
+
(base as any).sequence = record.sequence;
|
|
1406
|
+
}
|
|
1407
|
+
if (typeof record?._turnKey === 'string' && record._turnKey) {
|
|
1408
|
+
(base as any)._turnKey = record._turnKey;
|
|
1409
|
+
}
|
|
1410
|
+
if (typeof record?.bubbleState === 'string' && record.bubbleState) {
|
|
1411
|
+
(base as any).bubbleState = record.bubbleState;
|
|
1412
|
+
}
|
|
1413
|
+
return sanitizeHistoryMessage(agentType, base);
|
|
1414
|
+
})
|
|
1383
1415
|
.filter(Boolean) as HistoryMessage[];
|
|
1384
1416
|
}
|
|
1385
1417
|
|
|
@@ -1387,27 +1419,41 @@ function callProviderNativeHistoryRead(
|
|
|
1387
1419
|
agentType: string,
|
|
1388
1420
|
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1389
1421
|
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1390
|
-
historySessionId: string,
|
|
1422
|
+
historySessionId: string | undefined,
|
|
1391
1423
|
workspace?: string,
|
|
1424
|
+
excludeInProgressTurn?: boolean,
|
|
1425
|
+
sessionStartedAtMs?: number,
|
|
1426
|
+
envOverrides?: Record<string, string>,
|
|
1427
|
+
forceRefresh?: boolean,
|
|
1392
1428
|
): ProviderNativeHistoryReadResult | null {
|
|
1393
1429
|
const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, 'readSession');
|
|
1394
1430
|
if (!fn) return null;
|
|
1431
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
|
|
1395
1432
|
const result = fn({
|
|
1396
1433
|
agentType,
|
|
1397
|
-
sessionId:
|
|
1398
|
-
historySessionId,
|
|
1434
|
+
sessionId: normalizedSessionId,
|
|
1435
|
+
historySessionId: normalizedSessionId,
|
|
1399
1436
|
workspace,
|
|
1400
1437
|
format: canonicalHistory?.format,
|
|
1401
1438
|
watchPath: canonicalHistory?.watchPath,
|
|
1402
|
-
|
|
1439
|
+
excludeInProgressTurn: excludeInProgressTurn === true,
|
|
1440
|
+
sessionStartedAtMs,
|
|
1441
|
+
envOverrides,
|
|
1442
|
+
forceRefresh: forceRefresh === true,
|
|
1443
|
+
args: { sessionId: normalizedSessionId, historySessionId: normalizedSessionId, workspace, excludeInProgressTurn: excludeInProgressTurn === true, sessionStartedAtMs, envOverrides, forceRefresh: forceRefresh === true },
|
|
1403
1444
|
});
|
|
1404
1445
|
if (!result || typeof result !== 'object') return null;
|
|
1405
|
-
const records = normalizeProviderNativeHistoryRecords(agentType,
|
|
1446
|
+
const records = normalizeProviderNativeHistoryRecords(agentType, normalizedSessionId, (result as any).messages || (result as any).records);
|
|
1406
1447
|
if (records.length === 0) return null;
|
|
1407
1448
|
return {
|
|
1408
1449
|
records,
|
|
1409
1450
|
sourcePath: typeof (result as any).sourcePath === 'string' ? (result as any).sourcePath : '',
|
|
1410
1451
|
sourceMtimeMs: Number((result as any).sourceMtimeMs) || 0,
|
|
1452
|
+
providerSessionId: typeof (result as any).providerSessionId === 'string' ? (result as any).providerSessionId.trim() : undefined,
|
|
1453
|
+
workspace: typeof (result as any).workspace === 'string' ? (result as any).workspace.trim() : undefined,
|
|
1454
|
+
nativeHistoryCoverage: typeof (result as any).nativeHistoryCoverage === 'string' ? (result as any).nativeHistoryCoverage.trim() : undefined,
|
|
1455
|
+
partialReason: typeof (result as any).partialReason === 'string' ? (result as any).partialReason.trim() : undefined,
|
|
1456
|
+
unavailableReason: typeof (result as any).unavailableReason === 'string' ? (result as any).unavailableReason.trim() : undefined,
|
|
1411
1457
|
};
|
|
1412
1458
|
}
|
|
1413
1459
|
|
|
@@ -1417,10 +1463,15 @@ function buildNativeHistoryReadResult(
|
|
|
1417
1463
|
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1418
1464
|
historySessionId: string | undefined,
|
|
1419
1465
|
workspace?: string,
|
|
1466
|
+
excludeInProgressTurn?: boolean,
|
|
1467
|
+
sessionStartedAtMs?: number,
|
|
1468
|
+
envOverrides?: Record<string, string>,
|
|
1469
|
+
forceRefresh?: boolean,
|
|
1420
1470
|
): ProviderNativeHistoryReadResult | null {
|
|
1421
1471
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
|
|
1422
|
-
|
|
1423
|
-
|
|
1472
|
+
const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
|
|
1473
|
+
if (!canonicalHistory || (!normalizedSessionId && !normalizedWorkspace) || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
|
|
1474
|
+
return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace, excludeInProgressTurn, sessionStartedAtMs, envOverrides, forceRefresh);
|
|
1424
1475
|
}
|
|
1425
1476
|
|
|
1426
1477
|
function materializeNativeHistoryToMirror(
|
|
@@ -1476,16 +1527,36 @@ export function readProviderChatHistory(
|
|
|
1476
1527
|
excludeRecentCount?: number;
|
|
1477
1528
|
historyBehavior?: ProviderHistoryBehavior;
|
|
1478
1529
|
scripts?: ProviderNativeHistoryScripts;
|
|
1530
|
+
excludeInProgressTurn?: boolean;
|
|
1531
|
+
sessionStartedAtMs?: number;
|
|
1532
|
+
envOverrides?: Record<string, string>;
|
|
1533
|
+
forceRefresh?: boolean;
|
|
1479
1534
|
} = {},
|
|
1480
|
-
): {
|
|
1481
|
-
|
|
1482
|
-
|
|
1535
|
+
): {
|
|
1536
|
+
messages: HistoryMessage[];
|
|
1537
|
+
hasMore: boolean;
|
|
1538
|
+
source: 'provider-native' | 'adhdev-mirror' | 'native-unavailable';
|
|
1539
|
+
sourcePath?: string;
|
|
1540
|
+
sourceMtimeMs?: number;
|
|
1541
|
+
providerSessionId?: string;
|
|
1542
|
+
workspace?: string;
|
|
1543
|
+
nativeHistoryCoverage?: string;
|
|
1544
|
+
partialReason?: string;
|
|
1545
|
+
unavailableReason?: string;
|
|
1546
|
+
} {
|
|
1547
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory) && (options.historySessionId || options.workspace)) {
|
|
1548
|
+
const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace, options.excludeInProgressTurn, options.sessionStartedAtMs, options.envOverrides, options.forceRefresh);
|
|
1483
1549
|
if (!nativeResult) return { messages: [], hasMore: false, source: 'native-unavailable' };
|
|
1484
1550
|
return {
|
|
1485
1551
|
...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
1486
1552
|
source: 'provider-native',
|
|
1487
1553
|
sourcePath: nativeResult.sourcePath,
|
|
1488
1554
|
sourceMtimeMs: nativeResult.sourceMtimeMs,
|
|
1555
|
+
providerSessionId: nativeResult.providerSessionId,
|
|
1556
|
+
workspace: nativeResult.workspace,
|
|
1557
|
+
nativeHistoryCoverage: nativeResult.nativeHistoryCoverage,
|
|
1558
|
+
partialReason: nativeResult.partialReason,
|
|
1559
|
+
unavailableReason: nativeResult.unavailableReason,
|
|
1489
1560
|
};
|
|
1490
1561
|
}
|
|
1491
1562
|
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 });
|
|
@@ -78,12 +95,23 @@ const SESSION_CLEANUP_MODES = new Set(['preserve', 'stop', 'delete_stopped', 'st
|
|
|
78
95
|
const SPAWNED_SESSION_VISIBILITY_MODES = new Set(['visible', 'hidden']);
|
|
79
96
|
|
|
80
97
|
function mergeMeshPolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMeshPolicy> | undefined): RepoMeshPolicy {
|
|
81
|
-
const
|
|
98
|
+
const autoFastForward = normalizeAutoFastForwardPolicy({
|
|
99
|
+
...DEFAULT_MESH_POLICY.autoFastForward,
|
|
100
|
+
...((base?.autoFastForward && typeof base.autoFastForward === 'object') ? base.autoFastForward : {}),
|
|
101
|
+
...((patch?.autoFastForward && typeof patch.autoFastForward === 'object') ? patch.autoFastForward : {}),
|
|
102
|
+
});
|
|
103
|
+
const policy: RepoMeshPolicy = {
|
|
104
|
+
...DEFAULT_MESH_POLICY,
|
|
105
|
+
...(base || {}),
|
|
106
|
+
...(patch || {}),
|
|
107
|
+
autoFastForward,
|
|
108
|
+
};
|
|
82
109
|
if (!['block', 'warn', 'checkpoint_then_continue'].includes(policy.dirtyWorkspaceBehavior)) {
|
|
83
110
|
policy.dirtyWorkspaceBehavior = 'warn';
|
|
84
111
|
}
|
|
85
112
|
const maxParallelTasks = Number(policy.maxParallelTasks);
|
|
86
113
|
policy.maxParallelTasks = Number.isFinite(maxParallelTasks) ? Math.max(1, Math.min(8, Math.floor(maxParallelTasks))) : 2;
|
|
114
|
+
policy.allowAutoPublishSubmoduleMainCommits = policy.allowAutoPublishSubmoduleMainCommits === true;
|
|
87
115
|
if (!SESSION_CLEANUP_MODES.has(String(policy.sessionCleanupOnNodeRemove))) {
|
|
88
116
|
policy.sessionCleanupOnNodeRemove = 'preserve';
|
|
89
117
|
}
|
|
@@ -93,6 +121,18 @@ function mergeMeshPolicy(base: RepoMeshPolicy | undefined, patch: Partial<RepoMe
|
|
|
93
121
|
return policy;
|
|
94
122
|
}
|
|
95
123
|
|
|
124
|
+
function normalizeAutoFastForwardPolicy(value: unknown): NonNullable<RepoMeshPolicy['autoFastForward']> {
|
|
125
|
+
const record = value && typeof value === 'object' && !Array.isArray(value)
|
|
126
|
+
? value as Record<string, unknown>
|
|
127
|
+
: {};
|
|
128
|
+
const maxBehind = Number(record.maxBehind);
|
|
129
|
+
return {
|
|
130
|
+
enabled: record.enabled !== false,
|
|
131
|
+
...(Number.isFinite(maxBehind) && maxBehind >= 0 ? { maxBehind: Math.floor(maxBehind) } : {}),
|
|
132
|
+
requireCleanSubmodules: record.requireCleanSubmodules !== false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
96
136
|
export function listMeshes(): LocalMeshEntry[] {
|
|
97
137
|
return loadMeshConfig().meshes;
|
|
98
138
|
}
|
|
@@ -112,6 +152,7 @@ export interface CreateMeshOptions {
|
|
|
112
152
|
defaultBranch?: string;
|
|
113
153
|
policy?: Partial<RepoMeshPolicy>;
|
|
114
154
|
coordinator?: RepoMeshCoordinatorConfig;
|
|
155
|
+
meshHost?: RepoMeshHostMetadata;
|
|
115
156
|
}
|
|
116
157
|
|
|
117
158
|
export function createMesh(opts: CreateMeshOptions): LocalMeshEntry {
|
|
@@ -133,6 +174,7 @@ export function createMesh(opts: CreateMeshOptions): LocalMeshEntry {
|
|
|
133
174
|
defaultBranch: opts.defaultBranch,
|
|
134
175
|
policy: mergeMeshPolicy(undefined, opts.policy),
|
|
135
176
|
coordinator: opts.coordinator || {},
|
|
177
|
+
meshHost: opts.meshHost || createDefaultMeshHostMetadata(),
|
|
136
178
|
nodes: [],
|
|
137
179
|
createdAt: now,
|
|
138
180
|
updatedAt: now,
|
|
@@ -148,6 +190,7 @@ export interface UpdateMeshOptions {
|
|
|
148
190
|
defaultBranch?: string;
|
|
149
191
|
policy?: Partial<RepoMeshPolicy>;
|
|
150
192
|
coordinator?: RepoMeshCoordinatorConfig;
|
|
193
|
+
meshHost?: RepoMeshHostMetadata;
|
|
151
194
|
}
|
|
152
195
|
|
|
153
196
|
export function updateMesh(meshId: string, opts: UpdateMeshOptions): LocalMeshEntry | undefined {
|
|
@@ -159,6 +202,7 @@ export function updateMesh(meshId: string, opts: UpdateMeshOptions): LocalMeshEn
|
|
|
159
202
|
if (opts.defaultBranch !== undefined) mesh.defaultBranch = opts.defaultBranch;
|
|
160
203
|
if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
|
|
161
204
|
if (opts.coordinator) mesh.coordinator = opts.coordinator;
|
|
205
|
+
if (opts.meshHost) mesh.meshHost = opts.meshHost;
|
|
162
206
|
mesh.updatedAt = new Date().toISOString();
|
|
163
207
|
|
|
164
208
|
saveMeshConfig(config);
|
|
@@ -174,6 +218,240 @@ export function deleteMesh(meshId: string): boolean {
|
|
|
174
218
|
return true;
|
|
175
219
|
}
|
|
176
220
|
|
|
221
|
+
function normalizeManualHostAddress(hostAddress: string): string {
|
|
222
|
+
const normalized = hostAddress.trim().replace(/\/+$/, '');
|
|
223
|
+
if (!normalized) throw new Error('hostAddress required');
|
|
224
|
+
let parsed: URL;
|
|
225
|
+
try {
|
|
226
|
+
parsed = new URL(normalized);
|
|
227
|
+
} catch {
|
|
228
|
+
throw new Error('hostAddress must be a valid http(s) or ws(s) URL');
|
|
229
|
+
}
|
|
230
|
+
if (!['http:', 'https:', 'ws:', 'wss:'].includes(parsed.protocol)) {
|
|
231
|
+
throw new Error('hostAddress must use http, https, ws, or wss');
|
|
232
|
+
}
|
|
233
|
+
return normalized;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function tokenIdForManualPairing(token: string): string {
|
|
237
|
+
return `tok_${createHash('sha256').update(token).digest('hex').slice(0, 16)}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function normalizeTokenExpiry(value: unknown): string | undefined {
|
|
241
|
+
if (typeof value !== 'string' || !value.trim()) return undefined;
|
|
242
|
+
const date = new Date(value);
|
|
243
|
+
if (Number.isNaN(date.getTime())) throw new Error('expiresAt must be a valid ISO date');
|
|
244
|
+
return date.toISOString();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function assertPairingTokenValid(pairing: RepoMeshHostMetadata['pairing'], rawToken: string, nowIso: string): { ok: true; tokenId: string } | { ok: false; reason: string; expectedTokenId?: string; presentedTokenId?: string } {
|
|
248
|
+
const token = rawToken.trim();
|
|
249
|
+
if (!token) return { ok: false, reason: 'token required' };
|
|
250
|
+
const presentedTokenId = tokenIdForManualPairing(token);
|
|
251
|
+
const expectedTokenId = pairing?.tokenId;
|
|
252
|
+
if (!expectedTokenId || pairing?.status === 'not_configured' || pairing?.status === 'revoked') {
|
|
253
|
+
return { ok: false, reason: 'host pairing token is not configured', presentedTokenId };
|
|
254
|
+
}
|
|
255
|
+
if (pairing.expiresAt && new Date(pairing.expiresAt).getTime() <= new Date(nowIso).getTime()) {
|
|
256
|
+
return { ok: false, reason: 'host pairing token expired', expectedTokenId, presentedTokenId };
|
|
257
|
+
}
|
|
258
|
+
if (presentedTokenId !== expectedTokenId) {
|
|
259
|
+
return { ok: false, reason: 'invalid pairing token', expectedTokenId, presentedTokenId };
|
|
260
|
+
}
|
|
261
|
+
return { ok: true, tokenId: presentedTokenId };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface ConfigureMeshHostPairingOptions {
|
|
265
|
+
hostAddress: string;
|
|
266
|
+
token: string;
|
|
267
|
+
now?: string;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function configureMeshHostPairing(
|
|
271
|
+
meshId: string,
|
|
272
|
+
opts: ConfigureMeshHostPairingOptions,
|
|
273
|
+
): { mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata; hostAddress: string } | undefined {
|
|
274
|
+
const hostAddress = normalizeManualHostAddress(opts.hostAddress);
|
|
275
|
+
const token = opts.token.trim();
|
|
276
|
+
if (!token) throw new Error('token required');
|
|
277
|
+
|
|
278
|
+
const config = loadMeshConfig();
|
|
279
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
280
|
+
if (!mesh) return undefined;
|
|
281
|
+
|
|
282
|
+
const now = opts.now || new Date().toISOString();
|
|
283
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
284
|
+
const meshHost: RepoMeshHostMetadata = {
|
|
285
|
+
...previous,
|
|
286
|
+
role: 'member',
|
|
287
|
+
hostAddress,
|
|
288
|
+
pairing: {
|
|
289
|
+
status: 'pairing',
|
|
290
|
+
tokenId: tokenIdForManualPairing(token),
|
|
291
|
+
lastPairedAt: now,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
mesh.meshHost = meshHost;
|
|
296
|
+
mesh.updatedAt = now;
|
|
297
|
+
saveMeshConfig(config);
|
|
298
|
+
return { mesh, meshHost, hostAddress };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export interface CreateMeshHostPairingTokenOptions {
|
|
302
|
+
token?: string;
|
|
303
|
+
expiresAt?: string;
|
|
304
|
+
now?: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function createMeshHostPairingToken(
|
|
308
|
+
meshId: string,
|
|
309
|
+
opts: CreateMeshHostPairingTokenOptions = {},
|
|
310
|
+
): { mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata; token: string; tokenId: string; expiresAt?: string } | undefined {
|
|
311
|
+
const config = loadMeshConfig();
|
|
312
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
313
|
+
if (!mesh) return undefined;
|
|
314
|
+
const now = opts.now || new Date().toISOString();
|
|
315
|
+
const token = (opts.token || `mhj_${randomBytes(24).toString('base64url')}`).trim();
|
|
316
|
+
if (!token) throw new Error('token required');
|
|
317
|
+
const tokenId = tokenIdForManualPairing(token);
|
|
318
|
+
const expiresAt = normalizeTokenExpiry(opts.expiresAt);
|
|
319
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
320
|
+
if (previous.role === 'member') {
|
|
321
|
+
throw new Error('Mesh Host daemon required to create host pairing tokens; member daemons cannot mint host join tokens.');
|
|
322
|
+
}
|
|
323
|
+
const meshHost: RepoMeshHostMetadata = {
|
|
324
|
+
...previous,
|
|
325
|
+
role: 'host',
|
|
326
|
+
pairing: {
|
|
327
|
+
status: 'pairing',
|
|
328
|
+
tokenId,
|
|
329
|
+
lastPairedAt: now,
|
|
330
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
mesh.meshHost = meshHost;
|
|
334
|
+
mesh.updatedAt = now;
|
|
335
|
+
saveMeshConfig(config);
|
|
336
|
+
return { mesh, meshHost, token, tokenId, ...(expiresAt ? { expiresAt } : {}) };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface MeshHostJoinMemberNodeInput {
|
|
340
|
+
id?: string;
|
|
341
|
+
workspace: string;
|
|
342
|
+
repoRoot?: string;
|
|
343
|
+
daemonId?: string;
|
|
344
|
+
machineId?: string;
|
|
345
|
+
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
346
|
+
policy?: RepoMeshNodePolicy;
|
|
347
|
+
role?: RepoMeshDaemonRole;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export interface ApplyMeshHostJoinOptions {
|
|
351
|
+
token: string;
|
|
352
|
+
memberNode: MeshHostJoinMemberNodeInput;
|
|
353
|
+
memberMeshId?: string;
|
|
354
|
+
now?: string;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function applyMeshHostJoinRequest(
|
|
358
|
+
meshId: string,
|
|
359
|
+
opts: ApplyMeshHostJoinOptions,
|
|
360
|
+
): { accepted: true; mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata; node: LocalMeshNodeEntry; tokenId: string } | { accepted: false; mesh?: LocalMeshEntry; meshHost?: RepoMeshHostMetadata; tokenId?: string; reason: string } | undefined {
|
|
361
|
+
const config = loadMeshConfig();
|
|
362
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
363
|
+
if (!mesh) return undefined;
|
|
364
|
+
const now = opts.now || new Date().toISOString();
|
|
365
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
366
|
+
if (previous.role === 'member') {
|
|
367
|
+
return { accepted: false, mesh, meshHost: previous, reason: 'Mesh Host daemon required to accept join requests' };
|
|
368
|
+
}
|
|
369
|
+
const meshHost: RepoMeshHostMetadata = { ...previous, role: 'host' };
|
|
370
|
+
const validation = assertPairingTokenValid(meshHost.pairing, opts.token, now);
|
|
371
|
+
if (!validation.ok) {
|
|
372
|
+
mesh.meshHost = {
|
|
373
|
+
...meshHost,
|
|
374
|
+
pairing: {
|
|
375
|
+
...(meshHost.pairing || { status: 'not_configured' as const }),
|
|
376
|
+
status: 'rejected',
|
|
377
|
+
lastRejectedAt: now,
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
mesh.updatedAt = now;
|
|
381
|
+
saveMeshConfig(config);
|
|
382
|
+
return { accepted: false, mesh, meshHost: mesh.meshHost, tokenId: validation.presentedTokenId, reason: validation.reason };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const workspace = opts.memberNode.workspace.trim();
|
|
386
|
+
if (!workspace) throw new Error('memberNode.workspace required');
|
|
387
|
+
const memberId = opts.memberNode.id?.trim();
|
|
388
|
+
let node = mesh.nodes.find(n => (memberId && n.id === memberId) || n.workspace === workspace);
|
|
389
|
+
if (node) {
|
|
390
|
+
node.workspace = workspace;
|
|
391
|
+
node.repoRoot = opts.memberNode.repoRoot;
|
|
392
|
+
node.daemonId = opts.memberNode.daemonId;
|
|
393
|
+
node.machineId = opts.memberNode.machineId;
|
|
394
|
+
node.userOverrides = opts.memberNode.userOverrides || node.userOverrides || {};
|
|
395
|
+
node.policy = { ...(node.policy || {}), ...(opts.memberNode.policy || {}) };
|
|
396
|
+
node.role = 'member';
|
|
397
|
+
} else {
|
|
398
|
+
if (mesh.nodes.length >= 10) throw new Error('Maximum 10 nodes per mesh');
|
|
399
|
+
node = {
|
|
400
|
+
id: memberId || `node_${randomUUID().replace(/-/g, '')}`,
|
|
401
|
+
workspace,
|
|
402
|
+
repoRoot: opts.memberNode.repoRoot,
|
|
403
|
+
daemonId: opts.memberNode.daemonId,
|
|
404
|
+
machineId: opts.memberNode.machineId,
|
|
405
|
+
userOverrides: opts.memberNode.userOverrides || {},
|
|
406
|
+
policy: opts.memberNode.policy || {},
|
|
407
|
+
role: 'member',
|
|
408
|
+
};
|
|
409
|
+
mesh.nodes.push(node);
|
|
410
|
+
}
|
|
411
|
+
mesh.meshHost = {
|
|
412
|
+
...meshHost,
|
|
413
|
+
pairing: {
|
|
414
|
+
...(meshHost.pairing || {}),
|
|
415
|
+
status: 'paired',
|
|
416
|
+
tokenId: validation.tokenId,
|
|
417
|
+
joinedAt: now,
|
|
418
|
+
lastPairedAt: meshHost.pairing?.lastPairedAt || now,
|
|
419
|
+
...(meshHost.pairing?.expiresAt ? { expiresAt: meshHost.pairing.expiresAt } : {}),
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
mesh.updatedAt = now;
|
|
423
|
+
saveMeshConfig(config);
|
|
424
|
+
return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function markMeshHostPairingJoined(
|
|
428
|
+
meshId: string,
|
|
429
|
+
opts: { hostDaemonId?: string; hostNodeId?: string; joinedAt?: string; token?: string; tokenId?: string },
|
|
430
|
+
): { mesh: LocalMeshEntry; meshHost: RepoMeshHostMetadata } | undefined {
|
|
431
|
+
const config = loadMeshConfig();
|
|
432
|
+
const mesh = config.meshes.find(m => m.id === meshId);
|
|
433
|
+
if (!mesh) return undefined;
|
|
434
|
+
const now = opts.joinedAt || new Date().toISOString();
|
|
435
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
436
|
+
const tokenId = opts.tokenId || (opts.token ? tokenIdForManualPairing(opts.token) : previous.pairing?.tokenId);
|
|
437
|
+
mesh.meshHost = {
|
|
438
|
+
...previous,
|
|
439
|
+
role: 'member',
|
|
440
|
+
...(opts.hostDaemonId ? { hostDaemonId: opts.hostDaemonId } : {}),
|
|
441
|
+
...(opts.hostNodeId ? { hostNodeId: opts.hostNodeId } : {}),
|
|
442
|
+
pairing: {
|
|
443
|
+
...(previous.pairing || {}),
|
|
444
|
+
status: 'paired',
|
|
445
|
+
...(tokenId ? { tokenId } : {}),
|
|
446
|
+
joinedAt: now,
|
|
447
|
+
lastPairedAt: previous.pairing?.lastPairedAt || now,
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
mesh.updatedAt = now;
|
|
451
|
+
saveMeshConfig(config);
|
|
452
|
+
return { mesh, meshHost: mesh.meshHost };
|
|
453
|
+
}
|
|
454
|
+
|
|
177
455
|
// ─── Node Operations ────────────────────────────
|
|
178
456
|
|
|
179
457
|
export interface AddNodeOptions {
|
|
@@ -181,11 +459,14 @@ export interface AddNodeOptions {
|
|
|
181
459
|
repoRoot?: string;
|
|
182
460
|
daemonId?: string;
|
|
183
461
|
machineId?: string;
|
|
462
|
+
capabilities?: string[];
|
|
184
463
|
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
185
464
|
policy?: RepoMeshNodePolicy;
|
|
186
465
|
isLocalWorktree?: boolean;
|
|
187
466
|
worktreeBranch?: string;
|
|
188
467
|
clonedFromNodeId?: string;
|
|
468
|
+
worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
|
|
469
|
+
role?: RepoMeshDaemonRole;
|
|
189
470
|
}
|
|
190
471
|
|
|
191
472
|
export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntry | undefined {
|
|
@@ -208,11 +489,14 @@ export function addNode(meshId: string, opts: AddNodeOptions): LocalMeshNodeEntr
|
|
|
208
489
|
repoRoot: opts.repoRoot,
|
|
209
490
|
daemonId: opts.daemonId,
|
|
210
491
|
machineId: opts.machineId,
|
|
492
|
+
capabilities: normalizeCapabilityTags(opts.capabilities),
|
|
211
493
|
userOverrides: opts.userOverrides || {},
|
|
212
494
|
policy: opts.policy || {},
|
|
213
495
|
isLocalWorktree: opts.isLocalWorktree,
|
|
214
496
|
worktreeBranch: opts.worktreeBranch,
|
|
215
497
|
clonedFromNodeId: opts.clonedFromNodeId,
|
|
498
|
+
worktreeBootstrap: opts.worktreeBootstrap,
|
|
499
|
+
role: opts.role,
|
|
216
500
|
};
|
|
217
501
|
|
|
218
502
|
mesh.nodes.push(node);
|
|
@@ -238,7 +522,14 @@ export function removeNode(meshId: string, nodeId: string): boolean {
|
|
|
238
522
|
export function updateNode(
|
|
239
523
|
meshId: string,
|
|
240
524
|
nodeId: string,
|
|
241
|
-
opts: {
|
|
525
|
+
opts: {
|
|
526
|
+
userOverrides?: Partial<RepoMeshNodeCapabilities>;
|
|
527
|
+
policy?: RepoMeshNodePolicy;
|
|
528
|
+
worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
|
|
529
|
+
/** Per-node instruction surfaced in the coordinator prompt. Pass an
|
|
530
|
+
* empty string or undefined to clear it. */
|
|
531
|
+
systemPrompt?: string;
|
|
532
|
+
},
|
|
242
533
|
): LocalMeshNodeEntry | undefined {
|
|
243
534
|
const config = loadMeshConfig();
|
|
244
535
|
const mesh = config.meshes.find(m => m.id === meshId);
|
|
@@ -249,6 +540,15 @@ export function updateNode(
|
|
|
249
540
|
|
|
250
541
|
if (opts.userOverrides) node.userOverrides = { ...node.userOverrides, ...opts.userOverrides };
|
|
251
542
|
if (opts.policy) node.policy = { ...node.policy, ...opts.policy };
|
|
543
|
+
if (opts.worktreeBootstrap) node.worktreeBootstrap = opts.worktreeBootstrap;
|
|
544
|
+
if (Object.prototype.hasOwnProperty.call(opts, 'systemPrompt')) {
|
|
545
|
+
// Honor explicit clears: { systemPrompt: undefined } drops the field.
|
|
546
|
+
if (opts.systemPrompt && opts.systemPrompt.trim()) {
|
|
547
|
+
node.systemPrompt = opts.systemPrompt;
|
|
548
|
+
} else {
|
|
549
|
+
delete node.systemPrompt;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
252
552
|
mesh.updatedAt = new Date().toISOString();
|
|
253
553
|
saveMeshConfig(config);
|
|
254
554
|
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 {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as os from 'os';
|
|
12
|
+
import { DEFAULT_SESSION_HOST_COLS, DEFAULT_SESSION_HOST_ROWS } from '@adhdev/session-host-core';
|
|
12
13
|
import type * as http from 'http';
|
|
13
14
|
import type { ChildProcess } from 'child_process';
|
|
14
15
|
import type { DevServerContext, ProviderCategory } from './dev-server-types.js';
|
|
@@ -482,8 +483,8 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
482
483
|
const isWin = os.platform() === 'win32';
|
|
483
484
|
child = pty.spawn(isWin ? 'cmd.exe' : (process.env.SHELL || '/bin/zsh'), [isWin ? '/c' : '-c', shellCmd], {
|
|
484
485
|
name: 'xterm-256color',
|
|
485
|
-
cols:
|
|
486
|
-
rows:
|
|
486
|
+
cols: DEFAULT_SESSION_HOST_COLS,
|
|
487
|
+
rows: DEFAULT_SESSION_HOST_ROWS,
|
|
487
488
|
cwd: providerDir,
|
|
488
489
|
env: { ...process.env, ...(spawn.env || {}) },
|
|
489
490
|
});
|
|
@@ -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}` });
|