@adhdev/daemon-core 0.9.82-rc.35 → 0.9.82-rc.351
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 +8 -0
- package/dist/boot/process-hardening.d.ts +50 -0
- package/dist/build-info.d.ts +37 -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 +46 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +198 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +116 -64
- package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +53 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/cli-adapters/resolve-executable.d.ts +14 -0
- package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
- package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
- package/dist/cli-adapters/terminal-screen.d.ts +4 -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 +324 -1
- package/dist/commands/upgrade-helper.d.ts +12 -0
- package/dist/config/chat-history.d.ts +9 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +77 -1
- package/dist/detection/ide-detector.d.ts +13 -0
- package/dist/detection/win32-ide-version.d.ts +37 -0
- package/dist/git/change-impact-config.d.ts +159 -0
- package/dist/git/git-commands.d.ts +11 -1
- package/dist/git/git-diff.d.ts +6 -0
- package/dist/git/git-executor.d.ts +11 -0
- package/dist/git/git-status.d.ts +23 -0
- package/dist/git/git-types.d.ts +2 -50
- package/dist/git/index.d.ts +2 -0
- package/dist/index.d.ts +53 -13
- package/dist/index.js +39817 -14303
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41436 -16047
- 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/logging/log-redactor.d.ts +24 -0
- package/dist/logging/log-tail-reader.d.ts +46 -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 +174 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
- package/dist/mesh/mesh-events-coordinator.d.ts +169 -0
- package/dist/mesh/mesh-events-pending.d.ts +55 -0
- package/dist/mesh/mesh-events-stale.d.ts +40 -0
- package/dist/mesh/mesh-events-utils.d.ts +62 -0
- package/dist/mesh/mesh-events.d.ts +6 -49
- package/dist/mesh/mesh-fast-forward.d.ts +81 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-init.d.ts +86 -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 +164 -0
- package/dist/mesh/mesh-reconcile-loop.d.ts +7 -0
- package/dist/mesh/mesh-refine-batch.d.ts +68 -0
- package/dist/mesh/mesh-refine-status.d.ts +62 -0
- package/dist/mesh/mesh-review-inbox.d.ts +90 -0
- package/dist/mesh/mesh-routing.d.ts +70 -0
- package/dist/mesh/mesh-runtime-store.d.ts +420 -0
- package/dist/mesh/mesh-task-stats.d.ts +49 -0
- package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
- package/dist/mesh/mesh-work-queue.d.ts +218 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +216 -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/chat-message-normalization.d.ts +12 -0
- package/dist/providers/cli-provider-instance.d.ts +77 -3
- package/dist/providers/contracts.d.ts +139 -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 +14 -0
- package/dist/providers/provider-instance.d.ts +15 -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 +69 -0
- package/dist/providers/spec/cli-adapter.d.ts +217 -0
- package/dist/providers/spec/evaluator.d.ts +23 -0
- package/dist/providers/spec/fsm-driver.d.ts +322 -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 +144 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/types.d.ts +180 -0
- package/dist/providers/status-monitor.d.ts +7 -7
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +84 -0
- package/dist/providers/working-dir.d.ts +17 -0
- package/dist/repo-mesh-types.d.ts +295 -13
- package/dist/sessions/registry.d.ts +6 -0
- package/dist/shared-types-extra.d.ts +2 -4
- package/dist/shared-types.d.ts +59 -2
- 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 +26 -0
- package/dist/system/load-better-sqlite3.d.ts +21 -0
- package/dist/types.d.ts +5 -0
- package/package.json +7 -3
- package/src/agent-stream/poller.ts +2 -3
- package/src/agent-stream/provider-adapter.ts +1 -1
- package/src/boot/daemon-lifecycle.ts +55 -12
- package/src/boot/process-hardening.ts +89 -0
- package/src/build-info.ts +73 -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 +47 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1215 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +774 -1141
- 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 +56 -7
- package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/src/cli-adapters/provider-cli-shared.ts +100 -11
- package/src/cli-adapters/pty-transport.ts +2 -1
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/resolve-executable.ts +90 -0
- package/src/cli-adapters/session-host-transport.ts +2 -1
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +53 -29
- package/src/cli-adapters/terminal-backends/types.ts +10 -3
- package/src/cli-adapters/terminal-screen.ts +16 -81
- package/src/commands/chat-commands.ts +1823 -62
- package/src/commands/cli-manager.ts +304 -14
- package/src/commands/handler.ts +809 -2
- package/src/commands/mesh-coordinator.ts +332 -122
- package/src/commands/router.ts +7505 -1311
- package/src/commands/stream-commands.ts +8 -0
- package/src/commands/upgrade-helper.ts +81 -30
- package/src/config/chat-history.ts +483 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +388 -6
- 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/cli-detector.ts +28 -9
- package/src/detection/ide-detector.ts +55 -16
- package/src/detection/win32-ide-version.ts +106 -0
- package/src/git/change-impact-config.ts +354 -0
- package/src/git/git-commands.ts +55 -14
- package/src/git/git-diff.ts +53 -0
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +574 -43
- package/src/git/git-types.ts +14 -62
- package/src/git/git-worktree.ts +8 -1
- package/src/git/index.ts +16 -0
- package/src/index.ts +151 -12
- 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/log-redactor.ts +100 -0
- package/src/logging/log-tail-reader.ts +220 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/contracts.ts +329 -0
- package/src/mesh/coordinator-prompt.ts +234 -31
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +667 -0
- package/src/mesh/mesh-delivery-policy.ts +315 -0
- package/src/mesh/mesh-events-coordinator.ts +2303 -0
- package/src/mesh/mesh-events-pending.ts +567 -0
- package/src/mesh/mesh-events-stale.ts +307 -0
- package/src/mesh/mesh-events-utils.ts +381 -0
- package/src/mesh/mesh-events.ts +30 -1035
- package/src/mesh/mesh-fast-forward.ts +839 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-init.ts +260 -0
- package/src/mesh/mesh-ledger-reconciliation.ts +12 -5
- package/src/mesh/mesh-ledger.ts +590 -102
- package/src/mesh/mesh-missions.ts +322 -0
- package/src/mesh/mesh-reconcile-loop.ts +989 -0
- package/src/mesh/mesh-refine-batch.ts +197 -0
- package/src/mesh/mesh-refine-status.ts +231 -0
- package/src/mesh/mesh-review-inbox.ts +307 -0
- package/src/mesh/mesh-routing.ts +291 -0
- package/src/mesh/mesh-runtime-store.ts +1664 -0
- package/src/mesh/mesh-task-stats.ts +161 -0
- package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
- package/src/mesh/mesh-work-queue.ts +901 -141
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +423 -0
- package/src/mesh/worktree-bootstrap-config.ts +331 -0
- package/src/providers/acp-provider-instance.ts +25 -9
- package/src/providers/approval-utils.d.ts +4 -0
- package/src/providers/approval-utils.ts +47 -5
- package/src/providers/chat-message-normalization.ts +45 -5
- package/src/providers/cli-provider-instance.ts +1041 -99
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +150 -6
- package/src/providers/extension-provider-instance.ts +12 -7
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +35 -12
- 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 +670 -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 +168 -0
- package/src/providers/spec/cli-adapter.ts +986 -0
- package/src/providers/spec/evaluator.ts +345 -0
- package/src/providers/spec/fsm-driver.ts +1065 -0
- package/src/providers/spec/fsm-evaluator.ts +255 -0
- package/src/providers/spec/fsm-loader.ts +120 -0
- package/src/providers/spec/fsm-types.ts +238 -0
- package/src/providers/spec/native-history-executor.ts +1136 -0
- package/src/providers/spec/pre-launch-trust.ts +104 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/types.ts +226 -0
- package/src/providers/status-monitor.d.ts +7 -7
- package/src/providers/status-monitor.ts +37 -22
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +536 -0
- package/src/providers/version-archive.ts +64 -24
- package/src/providers/working-dir.ts +23 -0
- package/src/repo-mesh-types.ts +378 -14
- package/src/sessions/registry.ts +6 -0
- package/src/shared-types-extra.ts +2 -4
- package/src/shared-types.d.ts +8 -0
- package/src/shared-types.ts +64 -1
- package/src/status/builders.ts +26 -6
- package/src/status/normalize.ts +2 -0
- package/src/status/reporter.ts +19 -1
- package/src/status/snapshot.ts +95 -26
- package/src/system/host-memory.ts +29 -12
- package/src/system/load-better-sqlite3.ts +68 -0
- 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
|
@@ -91,6 +91,7 @@ function normalizeManagedStatus(status, opts) {
|
|
|
91
91
|
if (hasApprovalButtons(opts?.activeModal)) return "waiting_approval";
|
|
92
92
|
const normalized = String(status || "idle").trim().toLowerCase();
|
|
93
93
|
if (normalized === "waiting_approval") return "waiting_approval";
|
|
94
|
+
if (normalized === "waiting_choice") return "waiting_choice";
|
|
94
95
|
if (WORKING_STATUSES.has(normalized)) return "generating";
|
|
95
96
|
if (normalized === "error") return "error";
|
|
96
97
|
if (normalized === "stopped") return "stopped";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\nimport type { SessionActiveChatData } from '../shared-types.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\nexport interface NormalizeActiveChatOptions {\n includeMessages?: boolean;\n includeInputContent?: boolean;\n includeActiveModal?: boolean;\n messageLimit?: number;\n totalBytesLimit?: number;\n stringLimit?: number;\n fallbackStringLimit?: number;\n}\n\n// Full snapshots are still capped, but can carry recent chat context for API/inspection use.\nconst FULL_STATUS_ACTIVE_CHAT_OPTIONS: Required<NormalizeActiveChatOptions> = {\n includeMessages: true,\n includeInputContent: true,\n includeActiveModal: true,\n messageLimit: 60,\n totalBytesLimit: 96 * 1024,\n stringLimit: 4 * 1024,\n fallbackStringLimit: 1024,\n};\n\n// Live/metadata snapshots only need routing + UI summary. Current chat text is loaded\n// on demand via `read_chat`, and older history via `chat_history`.\nexport const LIVE_STATUS_ACTIVE_CHAT_OPTIONS: Required<NormalizeActiveChatOptions> = {\n includeMessages: false,\n includeInputContent: false,\n includeActiveModal: false,\n messageLimit: 0,\n totalBytesLimit: 0,\n stringLimit: 512,\n fallbackStringLimit: 256,\n};\n\nconst STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;\nconst STATUS_MODAL_BUTTON_LIMIT = 120;\n\nfunction truncateString(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));\n return `${value.slice(0, maxChars - 12)}...[truncated]`;\n}\n\nfunction truncateStringTail(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(value.length - Math.max(0, maxChars));\n return `...[truncated]${value.slice(value.length - (maxChars - 12))}`;\n}\n\nfunction trimStructuredStrings(value: unknown, maxChars: number): unknown {\n if (typeof value === 'string') return truncateString(value, maxChars);\n if (Array.isArray(value)) return value.map((item) => trimStructuredStrings(item, maxChars));\n if (!value || typeof value !== 'object') return value;\n return Object.fromEntries(\n Object.entries(value).map(([key, nested]) => [key, trimStructuredStrings(nested, maxChars)]),\n );\n}\n\nfunction estimateBytes(value: unknown): number {\n try {\n return JSON.stringify(value).length;\n } catch {\n return String(value ?? '').length;\n }\n}\n\nfunction trimMessageForStatus(message: unknown, stringLimit: number): unknown {\n if (!message || typeof message !== 'object') return message;\n return trimStructuredStrings(message, stringLimit);\n}\n\n/**\n * Collapse timestamp / createdAt into receivedAt so downstream consumers\n * only ever need to read a single canonical time field.\n */\nfunction normalizeMessageTime(message: unknown): unknown {\n if (!message || typeof message !== 'object') return message;\n const msg = message as Record<string, unknown>;\n if (msg.receivedAt == null) {\n const fallback = msg.timestamp ?? msg.createdAt;\n if (fallback != null) {\n const ts = typeof fallback === 'string' ? Date.parse(fallback as string) : Number(fallback);\n if (Number.isFinite(ts) && ts > 0) msg.receivedAt = ts;\n }\n }\n return msg;\n}\n\nfunction trimMessagesForStatus(\n messages: unknown[] | null | undefined,\n options: Required<NormalizeActiveChatOptions>,\n): unknown[] {\n if (!options.includeMessages || options.messageLimit <= 0 || options.totalBytesLimit <= 0) return [];\n if (!Array.isArray(messages) || messages.length === 0) return [];\n\n const recent = messages.slice(-options.messageLimit);\n const kept: unknown[] = [];\n let totalBytes = 0;\n\n for (let i = recent.length - 1; i >= 0; i -= 1) {\n let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.stringLimit));\n let size = estimateBytes(normalized);\n\n if (size > options.totalBytesLimit) {\n normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.fallbackStringLimit));\n size = estimateBytes(normalized);\n }\n\n if (kept.length > 0 && (totalBytes + size) > options.totalBytesLimit) {\n continue;\n }\n\n kept.push(normalized);\n totalBytes += size;\n }\n\n return kept.reverse();\n}\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n options: NormalizeActiveChatOptions = FULL_STATUS_ACTIVE_CHAT_OPTIONS,\n): T extends null | undefined ? T : SessionActiveChatData {\n if (!activeChat) return activeChat as T extends null | undefined ? T : SessionActiveChatData;\n const resolvedOptions: Required<NormalizeActiveChatOptions> = {\n ...FULL_STATUS_ACTIVE_CHAT_OPTIONS,\n ...options,\n };\n const {\n messages: _messages,\n ...rest\n } = activeChat;\n const normalized: SessionActiveChatData = {\n ...rest,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n activeModal: resolvedOptions.includeActiveModal && activeChat.activeModal ? {\n message: truncateString(activeChat.activeModal.message || '', STATUS_MODAL_MESSAGE_LIMIT),\n buttons: (activeChat.activeModal.buttons || []).map((button) =>\n truncateString(String(button || ''), STATUS_MODAL_BUTTON_LIMIT)\n ),\n } : null,\n inputContent: resolvedOptions.includeInputContent && activeChat.inputContent\n ? truncateString(activeChat.inputContent, 2 * 1024)\n : undefined,\n };\n if (resolvedOptions.includeMessages) {\n normalized.messages = trimMessagesForStatus(activeChat.messages, resolvedOptions) as SessionActiveChatData['messages'];\n }\n return normalized as T extends null | undefined ? T : SessionActiveChatData;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\nimport type { SessionActiveChatData } from '../shared-types.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'waiting_choice'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\nexport interface NormalizeActiveChatOptions {\n includeMessages?: boolean;\n includeInputContent?: boolean;\n includeActiveModal?: boolean;\n messageLimit?: number;\n totalBytesLimit?: number;\n stringLimit?: number;\n fallbackStringLimit?: number;\n}\n\n// Full snapshots are still capped, but can carry recent chat context for API/inspection use.\nconst FULL_STATUS_ACTIVE_CHAT_OPTIONS: Required<NormalizeActiveChatOptions> = {\n includeMessages: true,\n includeInputContent: true,\n includeActiveModal: true,\n messageLimit: 60,\n totalBytesLimit: 96 * 1024,\n stringLimit: 4 * 1024,\n fallbackStringLimit: 1024,\n};\n\n// Live/metadata snapshots only need routing + UI summary. Current chat text is loaded\n// on demand via `read_chat`, and older history via `chat_history`.\nexport const LIVE_STATUS_ACTIVE_CHAT_OPTIONS: Required<NormalizeActiveChatOptions> = {\n includeMessages: false,\n includeInputContent: false,\n includeActiveModal: false,\n messageLimit: 0,\n totalBytesLimit: 0,\n stringLimit: 512,\n fallbackStringLimit: 256,\n};\n\nconst STATUS_MODAL_MESSAGE_LIMIT = 2 * 1024;\nconst STATUS_MODAL_BUTTON_LIMIT = 120;\n\nfunction truncateString(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(0, Math.max(0, maxChars));\n return `${value.slice(0, maxChars - 12)}...[truncated]`;\n}\n\nfunction truncateStringTail(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value;\n if (maxChars <= 12) return value.slice(value.length - Math.max(0, maxChars));\n return `...[truncated]${value.slice(value.length - (maxChars - 12))}`;\n}\n\nfunction trimStructuredStrings(value: unknown, maxChars: number): unknown {\n if (typeof value === 'string') return truncateString(value, maxChars);\n if (Array.isArray(value)) return value.map((item) => trimStructuredStrings(item, maxChars));\n if (!value || typeof value !== 'object') return value;\n return Object.fromEntries(\n Object.entries(value).map(([key, nested]) => [key, trimStructuredStrings(nested, maxChars)]),\n );\n}\n\nfunction estimateBytes(value: unknown): number {\n try {\n return JSON.stringify(value).length;\n } catch {\n return String(value ?? '').length;\n }\n}\n\nfunction trimMessageForStatus(message: unknown, stringLimit: number): unknown {\n if (!message || typeof message !== 'object') return message;\n return trimStructuredStrings(message, stringLimit);\n}\n\n/**\n * Collapse timestamp / createdAt into receivedAt so downstream consumers\n * only ever need to read a single canonical time field.\n */\nfunction normalizeMessageTime(message: unknown): unknown {\n if (!message || typeof message !== 'object') return message;\n const msg = message as Record<string, unknown>;\n if (msg.receivedAt == null) {\n const fallback = msg.timestamp ?? msg.createdAt;\n if (fallback != null) {\n const ts = typeof fallback === 'string' ? Date.parse(fallback as string) : Number(fallback);\n if (Number.isFinite(ts) && ts > 0) msg.receivedAt = ts;\n }\n }\n return msg;\n}\n\nfunction trimMessagesForStatus(\n messages: unknown[] | null | undefined,\n options: Required<NormalizeActiveChatOptions>,\n): unknown[] {\n if (!options.includeMessages || options.messageLimit <= 0 || options.totalBytesLimit <= 0) return [];\n if (!Array.isArray(messages) || messages.length === 0) return [];\n\n const recent = messages.slice(-options.messageLimit);\n const kept: unknown[] = [];\n let totalBytes = 0;\n\n for (let i = recent.length - 1; i >= 0; i -= 1) {\n let normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.stringLimit));\n let size = estimateBytes(normalized);\n\n if (size > options.totalBytesLimit) {\n normalized = normalizeMessageTime(trimMessageForStatus(recent[i], options.fallbackStringLimit));\n size = estimateBytes(normalized);\n }\n\n if (kept.length > 0 && (totalBytes + size) > options.totalBytesLimit) {\n continue;\n }\n\n kept.push(normalized);\n totalBytes += size;\n }\n\n return kept.reverse();\n}\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (normalized === 'waiting_choice') return 'waiting_choice';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n options: NormalizeActiveChatOptions = FULL_STATUS_ACTIVE_CHAT_OPTIONS,\n): T extends null | undefined ? T : SessionActiveChatData {\n if (!activeChat) return activeChat as T extends null | undefined ? T : SessionActiveChatData;\n const resolvedOptions: Required<NormalizeActiveChatOptions> = {\n ...FULL_STATUS_ACTIVE_CHAT_OPTIONS,\n ...options,\n };\n const {\n messages: _messages,\n ...rest\n } = activeChat;\n const normalized: SessionActiveChatData = {\n ...rest,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n activeModal: resolvedOptions.includeActiveModal && activeChat.activeModal ? {\n message: truncateString(activeChat.activeModal.message || '', STATUS_MODAL_MESSAGE_LIMIT),\n buttons: (activeChat.activeModal.buttons || []).map((button) =>\n truncateString(String(button || ''), STATUS_MODAL_BUTTON_LIMIT)\n ),\n } : null,\n inputContent: resolvedOptions.includeInputContent && activeChat.inputContent\n ? truncateString(activeChat.inputContent, 2 * 1024)\n : undefined,\n };\n if (resolvedOptions.includeMessages) {\n normalized.messages = trimMessagesForStatus(activeChat.messages, resolvedOptions) as SessionActiveChatData['messages'];\n }\n return normalized as T extends null | undefined ? T : SessionActiveChatData;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -46,6 +46,8 @@ export declare class DaemonStatusReporter {
|
|
|
46
46
|
private lastStatusSentAt;
|
|
47
47
|
private statusPendingThrottle;
|
|
48
48
|
private lastP2PStatusHash;
|
|
49
|
+
private lastP2PStatusSentAt;
|
|
50
|
+
private p2pDebounceTimer;
|
|
49
51
|
private lastServerStatusHash;
|
|
50
52
|
private lastStatusSummary;
|
|
51
53
|
private statusTimer;
|
|
@@ -65,6 +65,31 @@ export interface RecentReadDebugSnapshot {
|
|
|
65
65
|
}
|
|
66
66
|
export declare function shouldEmitRecentReadDebugLog(cache: Map<string, string>, snapshot: RecentReadDebugSnapshot): boolean;
|
|
67
67
|
export declare function buildMachineInfo(profile?: 'full' | 'live' | 'metadata'): MachineInfo;
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the last user-visible (non-system) message for a session as a preview.
|
|
70
|
+
*
|
|
71
|
+
* Exported so the cloud coordinator can derive a preview for a LOCAL mesh-owned
|
|
72
|
+
* worktree session directly from the real provider instance it hosts. A REMOTE
|
|
73
|
+
* worker session has no local instance (its preview rides the completion event's
|
|
74
|
+
* finalSummary — see resolveMeshSurfacedSessionPreview), but a LOCAL coordinator
|
|
75
|
+
* IS the worker, so its hosted instance's transcript holds the assistant reply and
|
|
76
|
+
* this is the source of truth for that case.
|
|
77
|
+
*/
|
|
78
|
+
export declare function getLastDisplayMessage(session: {
|
|
79
|
+
activeChat?: {
|
|
80
|
+
messages?: Array<{
|
|
81
|
+
role?: string;
|
|
82
|
+
content?: unknown;
|
|
83
|
+
receivedAt?: number | string;
|
|
84
|
+
timestamp?: number | string;
|
|
85
|
+
}> | null;
|
|
86
|
+
} | null;
|
|
87
|
+
}): {
|
|
88
|
+
role: string;
|
|
89
|
+
preview: string;
|
|
90
|
+
receivedAt: number;
|
|
91
|
+
hash: string;
|
|
92
|
+
} | null;
|
|
68
93
|
export { getSessionCurrentNotificationId, applySessionNotificationOverlay } from '../config/recent-activity.js';
|
|
69
94
|
export declare function getSessionCompletionMarker(session: {
|
|
70
95
|
activeChat?: {
|
|
@@ -75,6 +100,7 @@ export declare function getSessionCompletionMarker(session: {
|
|
|
75
100
|
receivedAt?: number | string;
|
|
76
101
|
timestamp?: number | string;
|
|
77
102
|
_turnKey?: string;
|
|
103
|
+
kind?: string;
|
|
78
104
|
}> | null;
|
|
79
105
|
} | null;
|
|
80
106
|
}): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type BetterSqlite3 from 'better-sqlite3';
|
|
2
|
+
/**
|
|
3
|
+
* Load the `better-sqlite3` constructor in a way that survives every runtime the
|
|
4
|
+
* daemon ships in.
|
|
5
|
+
*
|
|
6
|
+
* The naive `typeof require === 'function' ? require : createRequire(import.meta.url)`
|
|
7
|
+
* is unsafe inside the daemon-cloud bundle: esbuild emits CJS output but, for any
|
|
8
|
+
* chunk that touches `import.meta.url`, it shims the local `require` with a stub
|
|
9
|
+
* that THROWS `Dynamic require of "..." is not supported`. That stub is still
|
|
10
|
+
* `typeof === 'function'`, so the ternary picks it and the call throws — it never
|
|
11
|
+
* reaches the `createRequire` fallback. The failure surfaced as `mesh_send_task`
|
|
12
|
+
* crashing the whole tool call instead of gracefully degrading.
|
|
13
|
+
*
|
|
14
|
+
* The robust approach is to ATTEMPT the real `require` and CATCH the esbuild stub's
|
|
15
|
+
* throw, then fall back to `createRequire`. We try multiple resolution bases so the
|
|
16
|
+
* load works whether the code runs as:
|
|
17
|
+
* - a genuine CJS module (bare `require` works),
|
|
18
|
+
* - an esbuild CJS bundle with the throwing `require` shim (createRequire(import.meta.url)),
|
|
19
|
+
* - a pure ESM module where `require` is undefined (createRequire(import.meta.url)).
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadBetterSqlite3(): typeof BetterSqlite3;
|
package/dist/types.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ export interface ChatMessage {
|
|
|
36
36
|
index?: number;
|
|
37
37
|
timestamp?: number;
|
|
38
38
|
receivedAt?: number;
|
|
39
|
+
/** (A2.3) Monotonic sequence number per (session, source). Producer-asserted
|
|
40
|
+
* when emitted by v2-aware providers (transcript v2 schema); otherwise
|
|
41
|
+
* derived by the daemon from receivedAt/timestamp/index. ChatSourceMachine
|
|
42
|
+
* uses this as the native peak watermark for regression detection. */
|
|
43
|
+
sequence?: number;
|
|
39
44
|
_turnKey?: string;
|
|
40
45
|
/** Tool calls associated with this message */
|
|
41
46
|
toolCalls?: ToolCallInfo[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.351",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsup && npm run build:types",
|
|
29
29
|
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly --declaration --declarationMap false",
|
|
30
|
-
"dev": "tsup --watch",
|
|
30
|
+
"dev": "tsup --watch --no-clean",
|
|
31
31
|
"test": "vitest run",
|
|
32
32
|
"test:watch": "vitest",
|
|
33
33
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
@@ -46,9 +46,12 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.351",
|
|
49
50
|
"@adhdev/session-host-core": "*",
|
|
50
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
51
|
-
"
|
|
52
|
+
"ajv": "^8.20.0",
|
|
53
|
+
"ajv-formats": "^3.0.1",
|
|
54
|
+
"better-sqlite3": "^12.10.0",
|
|
52
55
|
"chalk": "^5.3.0",
|
|
53
56
|
"chokidar": "^4.0.3",
|
|
54
57
|
"conf": "^13.0.0",
|
|
@@ -60,6 +63,7 @@
|
|
|
60
63
|
"@adhdev/ghostty-vt-node": "*"
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
66
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
63
67
|
"@types/js-yaml": "^4.0.9",
|
|
64
68
|
"@types/node": "^22.0.0",
|
|
65
69
|
"@types/ws": "^8.18.1",
|
|
@@ -19,7 +19,7 @@ import type { SessionRegistry } from '../sessions/registry.js';
|
|
|
19
19
|
import { reconcileIdeRuntimeSessions } from '../sessions/reconcile.js';
|
|
20
20
|
import { LOG } from '../logging/logger.js';
|
|
21
21
|
import type { AgentStreamState } from './types.js';
|
|
22
|
-
import { formatAutoApprovalMessage,
|
|
22
|
+
import { formatAutoApprovalMessage, pickAutoApprovalButton } from '../providers/approval-utils.js';
|
|
23
23
|
import type { ProviderModule } from '../providers/contracts.js';
|
|
24
24
|
import { buildRuntimeSystemChatMessage } from '../providers/chat-message-normalization.js';
|
|
25
25
|
|
|
@@ -210,8 +210,7 @@ export class AgentStreamPoller {
|
|
|
210
210
|
if (stream?.status === 'waiting_approval') {
|
|
211
211
|
const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
|
|
212
212
|
if (autoApprove && resolvedActiveSessionId) {
|
|
213
|
-
const
|
|
214
|
-
const { label: buttonLabel } = pickApprovalButton(stream.activeModal?.buttons, provider);
|
|
213
|
+
const { label: buttonLabel } = pickAutoApprovalButton(stream.activeModal?.buttons);
|
|
215
214
|
const approved = await agentStreamManager.resolveSessionAction(cdp, resolvedActiveSessionId, 'approve', buttonLabel);
|
|
216
215
|
if (approved) {
|
|
217
216
|
const effectId = [
|
|
@@ -256,7 +256,7 @@ export class ProviderStreamAdapter implements IAgentStreamAdapter {
|
|
|
256
256
|
}
|
|
257
257
|
const validated = validateReadChatResultPayload(data, `${this.agentType} readChat`);
|
|
258
258
|
const validatedStatus = (validated as any).status as string;
|
|
259
|
-
const streamStatus = validatedStatus === 'generating' || validatedStatus === 'long_generating'
|
|
259
|
+
const streamStatus = validatedStatus === 'generating' || validatedStatus === 'no_progress' || validatedStatus === 'long_generating'
|
|
260
260
|
? 'streaming'
|
|
261
261
|
: validatedStatus;
|
|
262
262
|
const state: AgentStreamState = {
|
|
@@ -37,6 +37,10 @@ import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
|
37
37
|
import type { IdeProviderInstance } from '../providers/ide-provider-instance.js';
|
|
38
38
|
import { createDefaultGitCommandServices } from '../git/git-commands.js';
|
|
39
39
|
import { setupMeshEventForwarding } from '../mesh/mesh-events.js';
|
|
40
|
+
import { setupMeshReconcileLoop } from '../mesh/mesh-reconcile-loop.js';
|
|
41
|
+
import { loadMeshCoordinatorRegistry } from '../mesh/coordinator-registry.js';
|
|
42
|
+
import { applyProcessHardening } from './process-hardening.js';
|
|
43
|
+
import { installProviderProcessShim } from '../providers/sdk/v1/sandbox/require-whitelist.js';
|
|
40
44
|
|
|
41
45
|
// ─── Init Config ───
|
|
42
46
|
|
|
@@ -60,6 +64,7 @@ export interface DaemonInitConfig {
|
|
|
60
64
|
|
|
61
65
|
/** Router transport-specific callbacks */
|
|
62
66
|
onStatusChange?: () => void;
|
|
67
|
+
onMeshStateChange?: (meshId: string) => void;
|
|
63
68
|
onPostChatCommand?: () => void;
|
|
64
69
|
sessionHostControl?: SessionHostControlPlane | null;
|
|
65
70
|
getCdpLogFn?: (ideType: string) => (msg: string) => void;
|
|
@@ -88,6 +93,8 @@ export interface DaemonInitConfig {
|
|
|
88
93
|
dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
|
|
89
94
|
/** Returns selected-coordinator mesh peer telemetry for a target daemon when available. */
|
|
90
95
|
getMeshPeerConnectionStatus?: (daemonId: string) => Record<string, unknown> | null;
|
|
96
|
+
/** Cloud-only: P2P dashboard metadata sync after the core forwarder handles a mesh event. */
|
|
97
|
+
onMeshCoordinatorEventForwarded?: (payload: Record<string, unknown>) => void;
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
// ─── Result ───
|
|
@@ -106,6 +113,24 @@ export interface DaemonComponents {
|
|
|
106
113
|
detectedIdes: { value: IDEInfo[] };
|
|
107
114
|
refreshProviderAvailability: (providerType?: string) => Promise<void>;
|
|
108
115
|
dispatchMeshCommand?: (daemonId: string, command: string, args: Record<string, unknown>) => Promise<any>;
|
|
116
|
+
// Cloud-only hook: after the single core forwarder handles a mesh coordinator event, cloud
|
|
117
|
+
// uses this to keep its P2P dashboard view in sync (mesh-owned session metadata + flush
|
|
118
|
+
// subscriptions). Injected by daemon-cloud; absent/no-op on standalone. Replaces cloud's
|
|
119
|
+
// former separate instanceManager.onEvent listener so the event path stays single-listener.
|
|
120
|
+
onMeshCoordinatorEventForwarded?: (payload: Record<string, unknown>) => void;
|
|
121
|
+
// Periodic queue → live-coordinator reconcile loop handle. Set during
|
|
122
|
+
// initDaemonComponents, stopped during shutdownDaemonComponents. Drives the
|
|
123
|
+
// single-model (queue + polling) delivery: drains the pending-events queue
|
|
124
|
+
// on a fixed interval and injects into live CLI coordinators when idle.
|
|
125
|
+
meshReconcileLoop?: { stop(): void };
|
|
126
|
+
// Canonical status/daemon identity (e.g. `standalone_<machineId>` /
|
|
127
|
+
// `daemon_<machineId>`). This is the SAME id the MCP layer stamps as a
|
|
128
|
+
// worker's meshCoordinatorDaemonId (ctx.localDaemonId, sourced from
|
|
129
|
+
// getStatus().status.instanceId), so the reconcile loop MUST drain with it —
|
|
130
|
+
// draining with bare loadConfig().machineId never matches a unicast event
|
|
131
|
+
// stamped with the prefixed status id. Absent → reconcile falls back to
|
|
132
|
+
// machineId only.
|
|
133
|
+
statusInstanceId?: string;
|
|
109
134
|
}
|
|
110
135
|
|
|
111
136
|
export interface DaemonDevSupportOptions {
|
|
@@ -129,8 +154,16 @@ export interface DaemonDevSupportOptions {
|
|
|
129
154
|
* 8. Start instance ticking
|
|
130
155
|
*/
|
|
131
156
|
export async function initDaemonComponents(config: DaemonInitConfig): Promise<DaemonComponents> {
|
|
157
|
+
// 0. Process-level hardening (must run before any provider script is loaded).
|
|
158
|
+
// Freezes built-in prototypes (Object/Array/Function/String/Number/Boolean/Promise)
|
|
159
|
+
// and shadows process.exit/kill/abort/binding/dlopen so provider-script callers
|
|
160
|
+
// throw instead of killing the daemon. See ./process-hardening.ts for details.
|
|
161
|
+
applyProcessHardening();
|
|
162
|
+
installProviderProcessShim();
|
|
163
|
+
|
|
132
164
|
// 1. Global log interceptor
|
|
133
165
|
installGlobalInterceptor();
|
|
166
|
+
loadMeshCoordinatorRegistry();
|
|
134
167
|
|
|
135
168
|
// 2. ProviderLoader (provider source mode from config.json)
|
|
136
169
|
const appConfig = loadConfig();
|
|
@@ -142,16 +175,11 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
142
175
|
userDir: appConfig.providerDir,
|
|
143
176
|
});
|
|
144
177
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
} catch (e: any) {
|
|
151
|
-
LOG.warn('Provider', `⚠ Failed to fetch providers: ${e?.message}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
178
|
+
// Boot-time auto-sync is intentionally disabled. The user picks which
|
|
179
|
+
// providers to install via the dashboard onboarding / Providers tab; the
|
|
180
|
+
// daemon ships empty and only contains what the user explicitly installs.
|
|
181
|
+
// Manual sync is still available via the install / check_provider_updates
|
|
182
|
+
// commands (and the REST endpoint at /api/v1/providers/updates).
|
|
155
183
|
providerLoader.loadAll();
|
|
156
184
|
providerLoader.registerToDetector();
|
|
157
185
|
|
|
@@ -304,11 +332,13 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
304
332
|
},
|
|
305
333
|
onIdeConnected: () => poller?.start(),
|
|
306
334
|
onStatusChange: config.onStatusChange,
|
|
335
|
+
onMeshStateChange: config.onMeshStateChange,
|
|
307
336
|
onPostChatCommand: config.onPostChatCommand,
|
|
308
337
|
sessionHostControl: config.sessionHostControl,
|
|
309
338
|
statusInstanceId: config.statusInstanceId,
|
|
310
339
|
statusVersion: config.statusVersion,
|
|
311
340
|
getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
|
|
341
|
+
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
312
342
|
getCdpLogFn: config.getCdpLogFn || ((ideType: string) => LOG.forComponent(`CDP:${ideType}`).asLogFn()),
|
|
313
343
|
});
|
|
314
344
|
|
|
@@ -325,7 +355,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
325
355
|
// 10. Start instance ticking
|
|
326
356
|
instanceManager.startTicking(config.tickIntervalMs ?? 5_000);
|
|
327
357
|
|
|
328
|
-
const components = {
|
|
358
|
+
const components: DaemonComponents = {
|
|
329
359
|
providerLoader,
|
|
330
360
|
instanceManager,
|
|
331
361
|
cliManager,
|
|
@@ -339,10 +369,21 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
339
369
|
detectedIdes: detectedIdesRef,
|
|
340
370
|
refreshProviderAvailability,
|
|
341
371
|
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
372
|
+
onMeshCoordinatorEventForwarded: config.onMeshCoordinatorEventForwarded,
|
|
373
|
+
statusInstanceId: config.statusInstanceId,
|
|
342
374
|
};
|
|
343
375
|
|
|
344
|
-
// 11. Setup Mesh Event Forwarding
|
|
376
|
+
// 11. Setup Mesh Event Forwarding (queue persistence) + periodic reconcile loop.
|
|
377
|
+
// injectMeshSystemMessage now ONLY persists events to the pending-events queue;
|
|
378
|
+
// the reconcile loop drains that queue on a fixed interval and injects into live
|
|
379
|
+
// CLI coordinators when idle (and, in cloud mode, pulls remote worker daemons'
|
|
380
|
+
// queues over P2P). This is the single-model (queue + polling) replacement for the
|
|
381
|
+
// old spontaneous-forward push paths.
|
|
345
382
|
setupMeshEventForwarding(components);
|
|
383
|
+
components.meshReconcileLoop = setupMeshReconcileLoop(components);
|
|
384
|
+
|
|
385
|
+
// 12. Resume any refine jobs that were interrupted by a previous daemon restart.
|
|
386
|
+
setImmediate(() => void router.resumePendingRefineJobsOnStartup());
|
|
346
387
|
|
|
347
388
|
return components;
|
|
348
389
|
}
|
|
@@ -384,11 +425,13 @@ export async function shutdownDaemonComponents(components: DaemonComponents): Pr
|
|
|
384
425
|
const {
|
|
385
426
|
poller, cdpInitializer, agentStreamManager,
|
|
386
427
|
cliManager, instanceManager, cdpManagers,
|
|
428
|
+
meshReconcileLoop,
|
|
387
429
|
} = components;
|
|
388
430
|
|
|
389
431
|
// 1. Stop timers
|
|
390
432
|
poller.stop();
|
|
391
433
|
cdpInitializer.stop();
|
|
434
|
+
try { meshReconcileLoop?.stop(); } catch { /* noop */ }
|
|
392
435
|
|
|
393
436
|
// 2. Dispose agent stream
|
|
394
437
|
try {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level hardening — boot-time mitigations against provider scripts.
|
|
3
|
+
*
|
|
4
|
+
* Provider scripts run in-process (see providers/sdk/v1/sandbox/README-design.ts).
|
|
5
|
+
* The require() whitelist (sandbox/require-whitelist.ts) blocks the obvious
|
|
6
|
+
* attack surfaces (no `child_process.exec`, no `fs.writeFile`, no `net`, etc.)
|
|
7
|
+
* but a malicious or buggy script can still:
|
|
8
|
+
*
|
|
9
|
+
* 1. Pollute Object.prototype to influence the daemon's downstream consumers
|
|
10
|
+
* (e.g. `Object.prototype.toString = () => '...'` makes every untagged
|
|
11
|
+
* object stringify to that value across the whole process).
|
|
12
|
+
* 2. Mutate Array/Function/String prototypes for similar effect.
|
|
13
|
+
* 3. Call `process.exit()` / `process.kill()` to kill the daemon.
|
|
14
|
+
*
|
|
15
|
+
* Sandbox isolation (isolated-vm) is the proper fix but has a real perf cost
|
|
16
|
+
* and a real marshalling-complexity cost. As a cheaper layer we just freeze
|
|
17
|
+
* the built-in prototypes at boot, which is irreversible and catches every
|
|
18
|
+
* prototype-pollution variant for the lifetime of the process.
|
|
19
|
+
*
|
|
20
|
+
* `applyProcessHardening()` is idempotent — calling it twice is a no-op the
|
|
21
|
+
* second time. The function intentionally swallows any "cannot redefine
|
|
22
|
+
* property" errors that would arise if some upstream code already froze
|
|
23
|
+
* the same prototype: the post-condition (prototype is frozen) is satisfied
|
|
24
|
+
* either way.
|
|
25
|
+
*
|
|
26
|
+
* Trade-offs:
|
|
27
|
+
* - Read-only access to prototype methods (`Object.prototype.hasOwnProperty.call`,
|
|
28
|
+
* `Array.prototype.slice.call`, etc.) is unaffected — only writes throw.
|
|
29
|
+
* - Some older npm libraries patch Array.prototype or Function.prototype at
|
|
30
|
+
* import time. If a daemon-core dep does this, the import will throw. The
|
|
31
|
+
* daemon-core vitest suite is the canonical regression check for this.
|
|
32
|
+
* - We deliberately do NOT freeze RegExp.prototype, Error.prototype, Map/Set
|
|
33
|
+
* prototypes, or Date.prototype because at least one of those tends to be
|
|
34
|
+
* mutated by mainstream test/runtime libs and the value of freezing them
|
|
35
|
+
* is low (no useful prototype-pollution attack surface).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
let _hardened = false;
|
|
39
|
+
|
|
40
|
+
/** Prototypes that get frozen at boot. */
|
|
41
|
+
const HARDENED_PROTOS: Array<{ name: string; proto: object }> = [
|
|
42
|
+
{ name: 'Object', proto: Object.prototype },
|
|
43
|
+
{ name: 'Array', proto: Array.prototype },
|
|
44
|
+
{ name: 'Function', proto: Function.prototype },
|
|
45
|
+
{ name: 'String', proto: String.prototype },
|
|
46
|
+
{ name: 'Number', proto: Number.prototype },
|
|
47
|
+
{ name: 'Boolean', proto: Boolean.prototype },
|
|
48
|
+
{ name: 'Promise', proto: Promise.prototype },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Freeze the canonical built-in prototypes so provider scripts (and anything
|
|
53
|
+
* else running in this process) cannot mutate them. Idempotent.
|
|
54
|
+
*
|
|
55
|
+
* Returns the list of prototype names that were newly frozen — primarily for
|
|
56
|
+
* tests / observability. Already-frozen prototypes are not re-reported.
|
|
57
|
+
*/
|
|
58
|
+
export function applyProcessHardening(): string[] {
|
|
59
|
+
if (_hardened) return [];
|
|
60
|
+
_hardened = true;
|
|
61
|
+
|
|
62
|
+
const newlyFrozen: string[] = [];
|
|
63
|
+
for (const entry of HARDENED_PROTOS) {
|
|
64
|
+
if (Object.isFrozen(entry.proto)) continue;
|
|
65
|
+
try {
|
|
66
|
+
Object.freeze(entry.proto);
|
|
67
|
+
newlyFrozen.push(entry.name);
|
|
68
|
+
} catch {
|
|
69
|
+
// Some host/runtime combinations refuse to freeze a built-in
|
|
70
|
+
// prototype (e.g. it has a non-configurable accessor). We treat
|
|
71
|
+
// this as best-effort — log nothing here (logger not guaranteed
|
|
72
|
+
// to be initialized this early) and move on. Subsequent reads
|
|
73
|
+
// of `Object.isFrozen(proto)` will reveal the gap.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return newlyFrozen;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** For tests only — clears the internal "already hardened" flag. The
|
|
80
|
+
* prototypes themselves cannot be un-frozen, so this is mostly useful for
|
|
81
|
+
* testing the idempotency guard. */
|
|
82
|
+
export function _resetProcessHardeningForTest(): void {
|
|
83
|
+
_hardened = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** For tests only — peek at the hardened state. */
|
|
87
|
+
export function _isProcessHardened(): boolean {
|
|
88
|
+
return _hardened;
|
|
89
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon build stamp — single runtime source for "which commit is this running
|
|
3
|
+
* daemon built from?".
|
|
4
|
+
*
|
|
5
|
+
* The values are injected at BUILD time via tsup `define` (esbuild global
|
|
6
|
+
* replacement). Every build config that bundles daemon-core into a shippable
|
|
7
|
+
* daemon (daemon-core's own dist, daemon-standalone, daemon-cloud) replaces
|
|
8
|
+
* these `__DAEMON_BUILD_*` identifiers with the literal git commit/version that
|
|
9
|
+
* was current when the bundle was produced. See:
|
|
10
|
+
* - oss/packages/daemon-core/tsup.config.ts (standalone consumes this dist)
|
|
11
|
+
* - packages/daemon-cloud/tsup.config.ts (re-bundles daemon-core from source)
|
|
12
|
+
*
|
|
13
|
+
* If a bundle is produced WITHOUT the define (e.g. running src directly through
|
|
14
|
+
* tsx in dev, or a build env without git), the identifiers stay undefined and
|
|
15
|
+
* we fall back to `"unknown"` — never a ReferenceError, never a build failure.
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT (live-reflection caveat): a fresh local `daemon-core dist` rebuild +
|
|
18
|
+
* daemon restart is NOT enough to make a *cloud* daemon report a new commit —
|
|
19
|
+
* daemon-cloud ships its own re-bundle of daemon-core, so the build stamp only
|
|
20
|
+
* advances after the cloud daemon is rebuilt/redeployed and restarted. This is
|
|
21
|
+
* precisely the gap `mesh_status`'s `staleDaemonBuild` warning exists to surface.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// These are replaced at build time by tsup `define`. `typeof` guards keep this
|
|
25
|
+
// safe when the define is absent (the identifiers are simply not declared).
|
|
26
|
+
declare const __DAEMON_BUILD_COMMIT__: string | undefined;
|
|
27
|
+
declare const __DAEMON_BUILD_COMMIT_SHORT__: string | undefined;
|
|
28
|
+
declare const __DAEMON_BUILD_VERSION__: string | undefined;
|
|
29
|
+
declare const __DAEMON_BUILD_AT__: string | undefined;
|
|
30
|
+
|
|
31
|
+
export interface DaemonBuildInfo {
|
|
32
|
+
/** Full 40-char git commit the daemon bundle was built from, or 'unknown'. */
|
|
33
|
+
commit: string;
|
|
34
|
+
/** Short (7-char) form of the same commit, or 'unknown'. */
|
|
35
|
+
commitShort: string;
|
|
36
|
+
/** package.json version baked in at build time, or 'unknown'. */
|
|
37
|
+
version: string;
|
|
38
|
+
/** ISO build timestamp if the build config injected one. */
|
|
39
|
+
builtAt?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readInjected(value: string | undefined): string | undefined {
|
|
43
|
+
if (typeof value !== 'string') return undefined;
|
|
44
|
+
const trimmed = value.trim();
|
|
45
|
+
if (!trimmed || trimmed === 'unknown') return undefined;
|
|
46
|
+
return trimmed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let cached: DaemonBuildInfo | undefined;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the build stamp baked into the running daemon bundle. Pure read of
|
|
53
|
+
* build-time constants — no I/O, safe to call from any runtime path. Cached.
|
|
54
|
+
*/
|
|
55
|
+
export function getDaemonBuildInfo(): DaemonBuildInfo {
|
|
56
|
+
if (cached) return cached;
|
|
57
|
+
|
|
58
|
+
const commit =
|
|
59
|
+
readInjected(typeof __DAEMON_BUILD_COMMIT__ !== 'undefined' ? __DAEMON_BUILD_COMMIT__ : undefined)
|
|
60
|
+
?? 'unknown';
|
|
61
|
+
const commitShort =
|
|
62
|
+
readInjected(typeof __DAEMON_BUILD_COMMIT_SHORT__ !== 'undefined' ? __DAEMON_BUILD_COMMIT_SHORT__ : undefined)
|
|
63
|
+
?? (commit !== 'unknown' ? commit.slice(0, 7) : 'unknown');
|
|
64
|
+
const version =
|
|
65
|
+
readInjected(typeof __DAEMON_BUILD_VERSION__ !== 'undefined' ? __DAEMON_BUILD_VERSION__ : undefined)
|
|
66
|
+
// Fall back to the runtime-injected package version env used elsewhere.
|
|
67
|
+
?? readInjected(typeof process !== 'undefined' ? process.env?.ADHDEV_PKG_VERSION : undefined)
|
|
68
|
+
?? 'unknown';
|
|
69
|
+
const builtAt = readInjected(typeof __DAEMON_BUILD_AT__ !== 'undefined' ? __DAEMON_BUILD_AT__ : undefined);
|
|
70
|
+
|
|
71
|
+
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
72
|
+
return cached;
|
|
73
|
+
}
|