@adhdev/mesh-shared 1.0.58-rc.7 → 1.0.58-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/node-facts.d.ts +20 -1
- package/package.json +1 -1
- package/src/node-facts.ts +21 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/json.ts","../src/git-normalize.ts","../src/session-normalize.ts","../src/node-normalize.ts","../src/node-facts.ts","../src/workspace-normalize.ts","../src/daemon-normalize.ts","../src/git-summarize.ts","../src/magi.ts","../src/brain-routing.ts","../src/slot-proposal.ts","../src/interpolation.ts","../src/mesh-tool-names.ts","../src/mesh-status-probe.ts","../src/rpc-chunking.ts","../src/semver-compare.ts","../src/ws-protocol.ts"],"sourcesContent":["/**\n * @adhdev/mesh-shared — pure, dependency-free mesh/git status normalizers shared\n * by daemon-core (standalone / local IPC) and web-core (cloud / P2P transit).\n *\n * This package exists to kill a recurring bug class: the cloud and standalone\n * transports each used to carry their own hand-synced copy of these normalizers,\n * and they drifted (cloud would strip/reshape fields, the web filter would drop\n * entries the standalone path kept). Both cores now import this single source of\n * truth. It MUST stay a pure leaf — types + pure functions on plain JS objects,\n * no Node/DOM APIs, no git exec, no transport, and an empty dependency set.\n */\n\nexport * from './json'\nexport * from './types'\nexport * from './git-normalize'\nexport * from './session-normalize'\nexport * from './node-normalize'\nexport * from './node-facts'\nexport * from './workspace-normalize'\nexport * from './daemon-normalize'\nexport * from './git-summarize'\nexport * from './magi'\nexport * from './brain-routing'\nexport * from './slot-proposal'\nexport * from './interpolation'\nexport * from './mesh-tool-names'\nexport * from './mesh-status-probe'\nexport * from './rpc-chunking'\nexport * from './semver-compare'\nexport * from './ws-protocol'\n","/**\n * Pure JSON-record reading primitives shared by the cloud (web-core / P2P transit)\n * and standalone (daemon-core / local IPC) mesh normalizers.\n *\n * These operate only on plain JS values — no Node/DOM APIs, no transport, no git\n * exec — so both cores can import them without violating the core↔core dependency\n * ban. They are the single source of truth for the field-coercion rules that the\n * two transports previously hand-synced (and drifted on).\n */\n\nexport type JsonRecord = Record<string, unknown>\n\nexport function readRecord(value: unknown): JsonRecord {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : {}\n}\n\nexport function readString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value !== 'string') continue\n const trimmed = value.trim()\n if (trimmed) return trimmed\n }\n return undefined\n}\n\nexport function readNumber(...values: unknown[]): number | undefined {\n for (const value of values) {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\nexport function readBoolean(...values: unknown[]): boolean | undefined {\n for (const value of values) {\n if (typeof value === 'boolean') return value\n }\n return undefined\n}\n\nexport function readStringArray(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)\n : []\n}\n\n/**\n * Coerce a value that is either an already-parsed plain object or a JSON string\n * into a plain object record. Arrays, primitives, parse failures and empty\n * strings all collapse to {} — callers treat the result as a best-effort record.\n *\n * This is the single source of truth for the `parseJsonRecord`/`parseJsonObject`\n * coercion that cloud mesh normalizers previously hand-redefined per file.\n */\nexport function parseJsonRecord(value: unknown): JsonRecord {\n if (!value) return {}\n if (typeof value === 'object') return readRecord(value)\n if (typeof value !== 'string') return {}\n const trimmed = value.trim()\n if (!trimmed) return {}\n try {\n return readRecord(JSON.parse(trimmed))\n } catch {\n return {}\n }\n}\n\n/**\n * Join a (possibly absent) repo root with a relative submodule path. Returns the\n * path unchanged when it is already absolute, and undefined when nothing usable\n * can be derived — callers must treat the result as optional.\n */\nexport function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {\n const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\\\/]+$/, '') : ''\n const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : ''\n if (!normalizedPath) return undefined\n if (/^(?:[A-Za-z]:[\\\\/]|\\/)/.test(normalizedPath)) return normalizedPath\n if (!normalizedRoot) return undefined\n return `${normalizedRoot}/${normalizedPath.replace(/^[\\\\/]+/, '')}`\n}\n","/**\n * Canonical git-status normalizers shared by the cloud (web-core) and standalone\n * (daemon-core router) mesh paths. Previously each transport hand-maintained its\n * own copy and they drifted (e.g. submodule drop rules, evidence checks); this is\n * the one implementation both import.\n */\n\nimport { joinRepoPath, readBoolean, readNumber, readRecord, readString, type JsonRecord } from './json'\nimport type { DaemonBuildBehind, GitRepoStatus, GitSubmoduleStatus, GitUpstreamFreshness } from './types'\n\nexport function scoreGitUpstreamFreshness(status: GitUpstreamFreshness | undefined): number {\n switch (status) {\n case 'fresh':\n return 30\n case 'no_upstream':\n return 4\n case 'unchecked':\n case undefined:\n return 0\n case 'stale':\n return -10\n case 'unavailable':\n return -15\n default:\n return 0\n }\n}\n\nexport function readGitSubmodules(value: unknown, parentRepoRoot?: string): GitSubmoduleStatus[] | undefined {\n if (!Array.isArray(value)) return undefined\n const submodules = value\n .map(entry => {\n const submodule = readRecord(entry)\n const path = readString(submodule.path)\n const commit = readString(submodule.commit)\n const repoPath = readString(submodule.repoPath, submodule.repo_root)\n ?? joinRepoPath(parentRepoRoot, path)\n // repoPath is only used for the submodule node's display workspace, which is\n // allowed to be empty. The cloud P2P transit path can deliver submodule entries\n // without repoPath (and a per-node git object without a derivable repoRoot), so\n // dropping on missing repoPath would silently strip every submodule graph node.\n // Keep any submodule that carries both path and commit.\n if (!path || !commit) return null\n const result: GitSubmoduleStatus = {\n path,\n commit,\n dirty: readBoolean(submodule.dirty) ?? false,\n outOfSync: readBoolean(submodule.outOfSync, submodule.out_of_sync) ?? false,\n lastCheckedAt: readNumber(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),\n }\n if (repoPath) result.repoPath = repoPath\n const error = readString(submodule.error)\n if (error) result.error = error\n return result\n })\n .filter((entry): entry is GitSubmoduleStatus => entry !== null)\n return submodules.length > 0 ? submodules : undefined\n}\n\nexport function hasGitStatusEvidence(status: JsonRecord): boolean {\n // BUG FIX: a transit-reshaped git status that carries only a repoRoot/workspace\n // (e.g. cloud P2P stripped the branch/upstream/counters but kept the path) must\n // NOT be dropped — otherwise the node loses its git object and any submodules\n // hanging off it. Treat a present repoRoot/repo_root/workspace as evidence too.\n return readBoolean(status.isGitRepo) !== undefined\n || Boolean(readString(status.branch, status.upstream, status.upstreamStatus, status.upstream_status, status.headCommit))\n || Boolean(readString(status.repoRoot, status.repo_root, status.workspace))\n || readNumber(\n status.ahead,\n status.behind,\n status.staged,\n status.modified,\n status.untracked,\n status.deleted,\n status.renamed,\n status.lastCheckedAt,\n status.last_checked_at,\n ) !== undefined\n || (Array.isArray(status.submodules) && status.submodules.length > 0)\n}\n\nexport function normalizeGitStatus(\n status: JsonRecord,\n node: JsonRecord,\n options?: { lastCheckedAt?: number },\n): GitRepoStatus | undefined {\n const explicitIsGitRepo = readBoolean(status.isGitRepo)\n if (!Object.keys(status).length || !hasGitStatusEvidence(status)) return undefined\n const isGitRepo = explicitIsGitRepo ?? true\n const conflictFiles = Array.isArray(status.conflictFiles)\n ? status.conflictFiles.filter((entry): entry is string => typeof entry === 'string')\n : []\n const conflictCount = readNumber(status.conflicts) ?? conflictFiles.length\n const hasConflicts = readBoolean(status.hasConflicts) ?? conflictCount > 0\n // node.workspace is in the fallback chain so a transit node carrying its path only\n // on the node (not the inner git object) still yields a parentRepoRoot for submodules.\n const repoRoot = readString(status.repoRoot, status.repo_root, node.repoRoot, node.repo_root, status.workspace, node.workspace) || undefined\n const submodules = readGitSubmodules(status.submodules, repoRoot)\n const upstreamStatus = readString(status.upstreamStatus, status.upstream_status)\n const upstreamFetchedAt = readNumber(status.upstreamFetchedAt, status.upstream_fetched_at)\n const upstreamFetchError = readString(status.upstreamFetchError, status.upstream_fetch_error)\n const error = readString(status.error)\n const staged = readNumber(status.staged) ?? 0\n const modified = readNumber(status.modified) ?? 0\n const untracked = readNumber(status.untracked) ?? 0\n const deleted = readNumber(status.deleted) ?? 0\n const renamed = readNumber(status.renamed) ?? 0\n return {\n workspace: readString(status.workspace, node.workspace) || '',\n repoRoot: repoRoot ?? null,\n isGitRepo,\n branch: readString(status.branch) ?? null,\n headCommit: readString(status.headCommit) ?? null,\n headMessage: readString(status.headMessage) ?? null,\n upstream: readString(status.upstream) ?? null,\n upstreamStatus: (upstreamStatus as GitUpstreamFreshness) ?? 'unchecked',\n ...(upstreamFetchedAt !== undefined ? { upstreamFetchedAt } : {}),\n ...(upstreamFetchError ? { upstreamFetchError } : {}),\n ahead: readNumber(status.ahead) ?? 0,\n behind: readNumber(status.behind) ?? 0,\n staged,\n modified,\n untracked,\n deleted,\n renamed,\n dirty: readBoolean(status.dirty, status.isDirty, status.is_dirty) ?? (staged + modified + untracked + deleted + renamed > 0 || hasConflicts),\n hasConflicts,\n conflictFiles,\n stashCount: readNumber(status.stashCount, status.stash_count) ?? 0,\n lastCheckedAt: options?.lastCheckedAt ?? readNumber(status.lastCheckedAt, status.last_checked_at) ?? Date.now(),\n ...(submodules ? { submodules } : {}),\n // Deploy-lag visibility: daemonBuildBehind is computed by the reporting\n // daemon's git probe (build commit vs workspace/submodule HEAD). It must\n // survive relay reassembly or downstream staleDaemonBuild surfaces\n // (dashboard Status tab badge, MCP staleDaemonBuilds warning) never fire\n // for remote nodes.\n ...(status.daemonBuildBehind && typeof status.daemonBuildBehind === 'object'\n ? { daemonBuildBehind: status.daemonBuildBehind as unknown as DaemonBuildBehind }\n : {}),\n ...(error ? { error } : {}),\n }\n}\n\nexport function scoreGitStatusCandidate(git: GitRepoStatus | undefined): number {\n if (!git) return Number.NEGATIVE_INFINITY\n let score = 0\n if (git.isGitRepo === true) score += 50\n if (git.isGitRepo === false) score -= 10\n if (git.branch) score += 20\n if (git.headCommit) score += 20\n if (git.upstream) score += 10\n score += scoreGitUpstreamFreshness(git.upstreamStatus)\n if (typeof git.ahead === 'number') score += 2\n if (typeof git.behind === 'number') score += 2\n if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length\n if (git.error) score -= 20\n return score\n}\n\n/**\n * Pick the best git status out of the four transit envelope slots a mesh node can\n * carry: lastGit.status, lastGit.result.status, lastProbe.git.status,\n * lastProbe.git.result.status. Returns undefined when none carry git evidence.\n */\nexport function pickBestTransitGitStatus(node: JsonRecord, options?: { lastCheckedAt?: number }): GitRepoStatus | undefined {\n const rawGit = readRecord(node.lastGit ?? node.last_git)\n const gitResult = readRecord(rawGit.result)\n const directStatus = readRecord(rawGit.status)\n const nestedStatus = readRecord(gitResult.status)\n const rawProbe = readRecord(node.lastProbe ?? node.last_probe)\n const probeGit = readRecord(rawProbe.git)\n const probeGitResult = readRecord(probeGit.result)\n const probeDirectStatus = readRecord(probeGit.status)\n const probeNestedStatus = readRecord(probeGitResult.status)\n const lastCheckedAt = options?.lastCheckedAt\n let best: { git: GitRepoStatus; score: number } | null = null\n for (const status of [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus]) {\n const normalized = normalizeGitStatus(status, node, { lastCheckedAt: lastCheckedAt ?? Date.now() })\n if (!normalized) continue\n const score = scoreGitStatusCandidate(normalized)\n if (!best || score > best.score) best = { git: normalized, score }\n }\n return best?.git\n}\n","/**\n * Canonical mesh-session record normalizer shared by the cloud (web-core) mesh\n * paths. Parses an already-transit-shaped session record into a typed\n * RepoMeshSessionStatus.\n */\n\nimport { readBoolean, readRecord, readString } from './json'\nimport type { RepoMeshSessionStatus } from './types'\n\n/**\n * Build a deterministic synthetic session id from a record that has no explicit\n * id. Two transit refreshes of the same logical session produce the same id so\n * downstream dedupe stays stable across refreshes (a random id would create a new\n * node every poll). Derived from the stable identifying fields available on the\n * record; prefixed \"synthetic:\" so callers can tell it apart from a real id.\n */\nfunction deriveSyntheticSessionId(record: ReturnType<typeof readRecord>): string | undefined {\n const parts = [\n readString(record.workspace),\n readString(record.providerType, record.provider),\n readString(record.role),\n readString(record.state, record.status),\n readString(record.title),\n readString(record.createdAt, record.created_at),\n readString(record.startedAt, record.started_at),\n ].filter((part): part is string => Boolean(part))\n if (parts.length === 0) return undefined\n return `synthetic:${parts.join('|')}`\n}\n\n/**\n * Session-id equivalence — the sessionId counterpart of daemonIdsEquivalent /\n * meshNodeIdMatches. A session id is SINGLE-FORM: one canonical UUID minted once\n * via crypto.randomUUID() in the provider instance and carried verbatim across\n * daemons, with no node/daemon-id style serialization variants. Equivalence is\n * therefore an exact match after trimming, never matching an absent/empty id\n * against another absent/empty id. Routing every mesh comparison site through\n * this one predicate keeps that single-form policy in one place (and gives any\n * future session-id aliasing a single seam) instead of scattering raw `===`.\n */\nexport function sessionIdsEquivalent(a: string | null | undefined, b: string | null | undefined): boolean {\n const idA = readString(a)\n const idB = readString(b)\n if (!idA || !idB) return false\n return idA === idB\n}\n\nexport function normalizeMeshSessionRecord(entry: unknown): RepoMeshSessionStatus | null {\n const record = readRecord(entry)\n // BUG FIX: cloud transit can reshape/strip the explicit id field. Fall back\n // through sessionId → session_id → id, then to a DETERMINISTIC synthetic id\n // derived from record content so the session survives the round trip instead\n // of being dropped (and so dedupe stays stable across refreshes). Return null\n // ONLY when the record carries no identifying fields at all.\n const sessionId = readString(record.sessionId, record.session_id, record.id)\n ?? deriveSyntheticSessionId(record)\n if (!sessionId) return null\n return {\n sessionId,\n ...(readString(record.providerType, record.provider) ? { providerType: readString(record.providerType, record.provider) } : {}),\n ...(readString(record.state, record.status) ? { state: readString(record.state, record.status) } : {}),\n ...(readString(record.chatStatus, record.chat_status) ? { chatStatus: readString(record.chatStatus, record.chat_status) } : {}),\n ...(readString(record.lifecycle) ? { lifecycle: readString(record.lifecycle) as RepoMeshSessionStatus['lifecycle'] } : {}),\n ...(readString(record.surfaceKind, record.surface_kind) ? { surfaceKind: readString(record.surfaceKind, record.surface_kind) as RepoMeshSessionStatus['surfaceKind'] } : {}),\n ...(readString(record.recoveryState, record.recovery_state) ? { recoveryState: readString(record.recoveryState, record.recovery_state) } : {}),\n ...(readString(record.workspace) ? { workspace: readString(record.workspace) } : {}),\n ...(readString(record.title) ? { title: readString(record.title) } : {}),\n ...(readString(record.role) ? { role: readString(record.role) } : {}),\n ...(readBoolean(record.isSelfCoordinator, record.is_self_coordinator) !== undefined ? { isSelfCoordinator: readBoolean(record.isSelfCoordinator, record.is_self_coordinator) } : {}),\n ...(readString(record.statusNote, record.status_note) ? { statusNote: readString(record.statusNote, record.status_note) } : {}),\n ...(readString(record.createdAt, record.created_at) ? { createdAt: readString(record.createdAt, record.created_at) } : {}),\n ...(readString(record.startedAt, record.started_at) ? { startedAt: readString(record.startedAt, record.started_at) } : {}),\n ...(readString(record.lastActivityAt, record.last_activity_at) ? { lastActivityAt: readString(record.lastActivityAt, record.last_activity_at) } : {}),\n ...(readBoolean(record.isCached, record.is_cached) !== undefined ? { isCached: readBoolean(record.isCached, record.is_cached) } : {}),\n }\n}\n","/**\n * Canonical mesh-node identity normalizer shared by daemon-core (standalone /\n * local IPC) and web-core (cloud / P2P transit) — the node-id counterpart of\n * session-normalize.ts.\n *\n * A mesh node record can carry its stable identifier under THREE different field\n * names depending on the serialization path it travelled, all with the same\n * value:\n * - `id` — config canonical form (mesh registry / persisted config)\n * - `nodeId` — runtime/wire camelCase form (inline-cache de-serialization,\n * mesh_status output via readStringValue(node.nodeId, node.id))\n * - `node_id` — SQLite DB column form leaked onto the object\n *\n * Comparing only `node.id` (or a 2-way `id ?? nodeId` that omits `node_id`)\n * against a target id silently drops nodes that arrived in another form — e.g. an\n * inline-cached worktree node, leaving a target-routed task permanently pending\n * with a misleading `no_node_satisfies_required_tags` skip. This module is the\n * single 3-way source of truth so every comparison site absorbs all three forms.\n */\n\nimport { readString } from './json'\nimport type { MeshNodeIdentified } from './types'\n\n/**\n * Read a mesh node's stable identifier, absorbing any of the three\n * serialization forms (`id` / `nodeId` / `node_id`). Returns undefined when the\n * record carries no usable id in any form.\n */\nexport function normalizeMeshNodeId(node: MeshNodeIdentified | null | undefined): string | undefined {\n const record = (node && typeof node === 'object' ? node : {}) as MeshNodeIdentified\n return readString(record.id, record.nodeId, record.node_id)\n}\n\n/**\n * Whether a mesh node record matches the given candidate id, comparing the\n * node's normalized id (any form) against the candidate. False when either side\n * is empty — never matches an absent id against an absent candidate.\n */\nexport function meshNodeIdMatches(\n node: MeshNodeIdentified | null | undefined,\n candidateId: string | null | undefined,\n): boolean {\n if (!candidateId) return false\n const trimmed = candidateId.trim()\n if (!trimmed) return false\n return normalizeMeshNodeId(node) === trimmed\n}\n","/**\n * MeshNodeFacts — the versioned per-machine RUNTIME facts bundle a reporting\n * daemon ships wholesale (design: root repo\n * docs/design/2026-07-25-deploy-lag-visibility.md §(a)).\n *\n * The mirror-defect class (a field reaching the dashboard for self nodes but\n * not remote ones, or vice versa) came from every fact being plumbed\n * field-by-field through six reassembly layers. The bundle rule that kills the\n * class: producers build it with ONE builder (daemon-core buildLocalNodeFacts —\n * used by BOTH the reporter envelope and the self-node stamp), and every relay\n * layer passes the object through OPAQUELY — never rebuild it field-by-field.\n * schemaVersion lets future fields ride through old relays untouched.\n *\n * Slots are deliberately NOT part of the bundle: they are coordinator-owned\n * config (REMOTE-NODE-SLOTS-COORDINATOR-LOCAL), not a reported runtime fact.\n */\n\nexport interface MeshNodeFactsDaemonBuild {\n /** Full build commit baked into the running daemon (40-hex). */\n commit?: string\n commitShort?: string\n version?: string\n builtAt?: string\n}\n\n/**\n * One rolling quota window, structurally identical to daemon-core's\n * `QuotaWindow`. Redeclared here rather than imported because this package is a\n * dependency-free leaf (see the header of index.ts) and must never import\n * daemon-core — the dependency arrow points the other way. daemon-core's\n * richer type stays assignable to this one, so the producer passes its snapshot\n * straight through with no mapping layer to drift.\n */\nexport interface MeshNodeFactsQuotaWindow {\n usedPercent: number\n windowMinutes: number\n resetsAt: number | null\n}\n\n/**\n * A provider's quota snapshot as reported by the node that owns the credentials.\n *\n * `status` is the field that matters to a reader: 'ok' means the windows are\n * usable, anything else means they are not. A node that CANNOT read a quota\n * still reports an entry (status 'unavailable'/'error' + metadata.failureKind)\n * rather than omitting the provider — an absent entry means \"this node never\n * told us\", a present failing entry means \"this node looked and could not\n * tell\", and a reader that cannot distinguish those two cannot diagnose\n * anything. Extra provider-specific fields (buckets, monthly) ride through via\n * the index signature.\n */\nexport interface MeshNodeFactsProviderQuota {\n provider: string\n status: string\n session: MeshNodeFactsQuotaWindow | null\n weekly: MeshNodeFactsQuotaWindow | null\n /** Unix ms of the snapshot itself — older than the bundle's reportedAt. */\n updatedAt: number\n error: string | null\n /**\n * `accountEmail` is PII and rides this bundle because the bundle is\n * P2P/local only — it must never be added to a server-bound payload. See\n * daemon-core `QuotaMetadata.accountEmail` and the regression suite that\n * pins its absence from every server allow-list.\n */\n metadata?: {\n failureKind?: string\n source?: string\n planType?: string | null\n accountEmail?: string | null\n /**\n * True when `session`/`weekly` are NOT this snapshot's own reading but a\n * retained last-good reading carried forward by daemon-core's\n * `carryForwardLastGoodWindows` (quota/refresh.ts) after a TRANSIENT\n * fetch failure (expired token, network blip, rate limit). `status` on\n * this same entry is the fresh failure, not 'ok' — the numbers are real,\n * just not from THIS tick. A reader should label them (e.g. \"· refreshing\")\n * rather than presenting them as a freshly measured value.\n */\n lastGoodWindows?: boolean\n /**\n * Unix ms when the reporting node last ATTEMPTED a refresh of this\n * provider — deliberately distinct from `updatedAt`, which dates the\n * DATA. They differ for file-source providers (claude-cli reports its\n * statusline snapshot's capture time, codex-cli the rollout entry's),\n * whose `updatedAt` does not move while the source file is unchanged\n * however often it is successfully re-read.\n *\n * A reader judging FRESHNESS wants `updatedAt`. This field answers the\n * different question \"is that node still looking?\", which is what makes\n * \"3h old but re-checked a minute ago\" distinguishable from \"3h old and\n * nobody has looked since\". Absent on entries written by daemons\n * predating the field.\n */\n fetchedAt?: number\n [extra: string]: unknown\n }\n [extra: string]: unknown\n}\n\n/**\n * One provider's enablement state on the reporting machine — see\n * `MeshNodeFacts.providerEnablement`.\n *\n * Both fields are REQUIRED booleans on the wire even though the underlying\n * config defaults are asymmetric (`enabled` defaults false, `quotaEnabled`\n * defaults true). The producer resolves those defaults before stamping, so a\n * reader never re-derives them — a second copy of the default rules is exactly\n * the drift this shape avoids. Absence is expressed by omitting the whole\n * provider entry (or the whole bundle field), never by a missing sub-field.\n */\nexport interface MeshNodeFactsProviderEnablement {\n /** \"This machine uses provider X\" — gates launching and mesh claims. */\n enabled: boolean\n /** \"...and its quota is probed here\" — an independent user opt-out. */\n quotaEnabled: boolean\n}\n\nexport interface MeshNodeFacts {\n schemaVersion: number\n reportedAt: number\n daemonBuild?: MeshNodeFactsDaemonBuild\n providerVersions?: Record<string, string>\n /**\n * Verified-channel PIN per provider type: which provider MANIFEST the node\n * actually loads. NOT the same as `providerVersions`, which is the CLI\n * BINARY version — a node can run kimi-code 1.2.3 while pinned to kimi\n * spec 1.0.0. Keep them separate; folding them would repeat the\n * multi-identifier confusion behind the canon-identity defect class.\n *\n * This is what makes a remote node's pin knowable. Provider fixes do not\n * propagate on their own (the pin advances only on an explicit\n * activation, by design), so without this field a node that never adopted\n * a published fix looks exactly like one that did.\n *\n * A missing entry means \"no pin\", never a fabricated value.\n */\n providerSpecPins?: Record<string, string>\n platform?: string\n arch?: string\n machineNickname?: string\n /**\n * Per-provider quota snapshots, keyed by QuotaProvider id ('claude-cli',\n * 'codex-cli', 'kimi'). Consumed by ROUTING as well as observation: the\n * coordinator's quota gate / spread bonus (daemon-core mesh-quota-routing.ts,\n * thresholds in RepoMeshPolicy.quotaRouting) reads exactly this shape, so\n * field renames here are a routing-contract change, not a cosmetic one.\n * Both consumers fail open on missing/stale data.\n *\n * Freshness is `Date.now() - reportedAt` (the bundle stamp) plus each\n * entry's own `updatedAt`; there is deliberately NO ttl/expiry field here,\n * because refresh cadence is owned by the reporting node and the delivery\n * cadence by whoever calls git_status. Neither end is in a position to\n * assert a TTL, so readers judge age themselves (the routing consumer's\n * rule: quotaSnapshotAgeMs in daemon-core mesh-quota-routing.ts).\n */\n quota?: Record<string, MeshNodeFactsProviderQuota>\n /**\n * Per-provider ENABLEMENT state on the reporting machine, keyed the same\n * way as `quota`. Exists because `quota` alone cannot answer \"why is there\n * no snapshot\": a provider that is disabled — on either axis — is pruned\n * from the quota cache entirely (daemon-core quota/refresh.ts drops it and\n * refuses to restore it from disk), so a deliberate opt-out and a\n * never-yet-measured provider both arrive as the SAME absent entry. On the\n * node that owns the config that ambiguity is resolvable by reading the\n * config; for every OTHER node in the mesh it was not resolvable at all,\n * which is what this field fixes.\n *\n * The two axes mirror ProviderLoader exactly and are INDEPENDENT:\n * `enabled` is \"this machine uses provider X\" (gates launching and mesh\n * claims), `quotaEnabled` gates ONLY the quota probe — a machine can use a\n * provider and still opt out of having its usage read.\n *\n * ★An ABSENT bundle field means \"this node did not tell us\" — a daemon too\n * old to send it — and must NEVER be read as \"disabled\". The consumer\n * (daemon-core mesh-quota-routing.ts classifyAbsentQuotaReason) keeps its\n * unclassified fallback for exactly that case; treating absence as\n * disabled would invent a fail-closed verdict out of a missing field.\n *\n * Booleans keyed by provider type only — no free text, no credentials.\n */\n providerEnablement?: Record<string, MeshNodeFactsProviderEnablement>\n /** Future fields ride through opaquely — do not enumerate them in relays. */\n [extra: string]: unknown\n}\n\n/**\n * Validate the minimal envelope shape and pass EVERYTHING else through\n * untouched. Returns undefined for anything that is not a v1+ bundle so\n * callers skip the stamp instead of shipping garbage.\n */\nexport function normalizeMeshNodeFacts(raw: unknown): MeshNodeFacts | undefined {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined\n const record = raw as Record<string, unknown>\n const schemaVersion = Number(record.schemaVersion)\n const reportedAt = Number(record.reportedAt)\n if (!Number.isFinite(schemaVersion) || schemaVersion < 1) return undefined\n if (!Number.isFinite(reportedAt) || reportedAt <= 0) return undefined\n return { ...record, schemaVersion, reportedAt } as MeshNodeFacts\n}\n\n/**\n * Provider types with a SHIPPED quota fetcher — the providers whose quota can\n * actually be read on a node today.\n *\n * This must mirror `REFRESHERS` in daemon-core's `quota/refresh.ts`, which is\n * the runtime authority: a provider absent from REFRESHERS is never probed, so\n * offering it a quota switch anywhere would promise a control that does\n * nothing. It lives here, in the dependency-free leaf, for the same reason\n * `formatQuotaAccount` does — the machine page and the new-install surfaces are\n * in web-core, the fetchers and the `adhdev setup` wizard reach it from\n * daemon-core, and the dependency arrow only runs one way. This list previously\n * existed as a hand-copied literal in web-core's ProvidersTab; that copy is now\n * derived from this one.\n *\n * Deliberately NOT necessarily the same set as the `QuotaProvider` union in\n * daemon-core's quota/types.ts: that union is the set of valid keys a snapshot\n * can be carried under, while membership HERE means \"a fetcher exists\".\n *\n * Known non-members and why, so this is not re-litigated per surface:\n * - cursor-cli — permanently impossible; no personal usage API exists\n * - hermes-cli — no model-axis quota to report\n *\n * ★grok-cli WAS listed here as impossible and is not: that verdict came from\n * probing `api.x.ai` / `management-api.x.ai` (the team-API billing axis, which\n * genuinely rejects a CLI OAuth token) and from reading `grok --help`, where\n * the `/usage` view does not appear because it is a TUI slash command. The\n * subscription quota is served by the CLI's own chat proxy — see the endpoint\n * provenance note in daemon-core `quota/fetchers/grok.ts`.\n *\n * ★antigravity-cli was likewise twice judged impossible, from reading\n * `agy --help` where the usage view does not appear because it is a TUI view.\n * Its quota comes from the SHARED Gemini Code Assist backend\n * (`daily-cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary` —\n * the host `agy` itself uses; the unprefixed `cloudcode-pa` host 429s Google\n * AI Pro Antigravity accounts), and its credential lives in the OS keyring,\n * not the stale on-disk token file — see the provenance note in daemon-core\n * `quota/fetchers/antigravity.ts`. Supported on macOS and Windows; other\n * platforms report `unsupported` rather than guess at a keyring backend\n * nobody has verified.\n *\n * ★Adding a provider here without adding its fetcher to REFRESHERS re-creates\n * exactly the \"switch that does nothing\" this constant prevents.\n */\nexport const QUOTA_SUPPORTED_PROVIDERS: readonly string[] = [\n 'antigravity-cli',\n 'claude-cli',\n 'codex-cli',\n 'grok-cli',\n 'kimi',\n 'opencode',\n]\n\n/** Whether this provider's quota can be probed at all — see QUOTA_SUPPORTED_PROVIDERS. */\nexport function supportsQuota(providerType: string | undefined | null): boolean {\n return !!providerType && QUOTA_SUPPORTED_PROVIDERS.includes(providerType)\n}\n\n/**\n * The account/plan label for a provider quota row: \"you@example.com · Plus\".\n *\n * Lives HERE, in the dependency-free leaf, because both renderers need it and\n * they cannot share code any other way: the dashboards are in web-core and the\n * `adhdev quota` CLI is in daemon-core, and the dependency arrow runs\n * web-core → daemon-core, never back. Duplicating the formatter in the CLI is\n * what produced the drift this function exists to prevent — the CLI showed no\n * account at all while the UI showed one.\n *\n * Both halves are optional and independent: codex reports both, kimi reports\n * neither, and Claude Code exposes no account at all. Returns null when there\n * is nothing to say, so a provider without an account renders no empty slot and\n * no \"unknown\" placeholder — the absence is simply invisible, in every surface.\n *\n * ★The email is PII travelling on a P2P-only path. Rendering it locally is\n * fine; it must never be forwarded to a server payload or a push body. See\n * daemon-core QuotaMetadata.accountEmail and the server-boundary suite.\n */\nexport function formatQuotaAccount(quota: MeshNodeFactsProviderQuota | undefined): string | null {\n const meta = quota?.metadata\n const email = typeof meta?.accountEmail === 'string' ? meta.accountEmail.trim() : ''\n const plan = typeof meta?.planType === 'string' ? meta.planType.trim() : ''\n const parts = [email, plan].filter(Boolean)\n return parts.length > 0 ? parts.join(' · ') : null\n}\n","/**\n * Canonical workspace-path normalizer for mesh node/session scope comparison,\n * shared by daemon-core (standalone / local IPC) and any other core that has to\n * tell a base node apart from a co-located worktree clone whose ONLY structural\n * difference is its distinct workspace root.\n *\n * One physical daemon can host a base node plus several worktree nodes. Session\n * records, queue claims, and read_chat requests are scoped to a node by matching\n * the session's actual workspace against the node's declared workspace. Those two\n * paths can arrive in different but equivalent spellings (back/forward slashes,\n * trailing separators, Windows case-insensitivity), so a raw string compare would\n * either falsely separate equal paths or fail to engage at all.\n *\n * This folds separator style, trailing slashes, and case into a single comparable\n * form. It was previously a module-private copy in daemon-core's\n * mesh-events-coordinator.ts (WTCLAIM fix-B); promoting it here keeps the one\n * comparison rule identical across the enqueue→claim path, the mesh_status\n * per-node session filter, and the read_chat node scope guard. Pure string ops —\n * no Node/DOM APIs — so it stays a valid mesh-shared leaf.\n */\nexport function normalizeMeshWorkspaceForCompare(dir?: string | null): string {\n if (typeof dir !== 'string') return ''\n return dir.trim().replace(/[\\\\/]+/g, '/').replace(/\\/+$/, '').toLowerCase()\n}\n\n/**\n * Whether two workspace paths refer to the same workspace root after\n * normalization. Returns false when either side is empty — an unknown workspace\n * never \"matches\" another, so callers must decide separately whether an unknown\n * workspace should be treated permissively (the WTCLAIM convention: unknown →\n * do not block).\n */\nexport function meshWorkspacesEquivalent(a?: string | null, b?: string | null): boolean {\n const left = normalizeMeshWorkspaceForCompare(a)\n const right = normalizeMeshWorkspaceForCompare(b)\n if (!left || !right) return false\n return left === right\n}\n","/**\n * Canonical coordinator/daemon-id form normalizer shared by daemon-core (the mesh\n * reconcile loop, pending-event queue, and MCP surface) — the daemon-id counterpart\n * of node-normalize.ts.\n *\n * A daemon answers to the SAME machine under three interchangeable id forms, all\n * derived from one `mach_<hex>` machine id:\n * - bare — `mach_<hex>` (loadConfig().machineId; stamped by the local\n * queue-assignment dispatch path)\n * - cloud — `daemon_mach_<hex>` (the coordinator mesh node's config-form\n * daemonId, which the MCP layer's resolveCoordinatorDaemonId\n * prefers and stamps onto a worker's meshCoordinatorDaemonId)\n * - standalone — `standalone_mach_<hex>` (a standalone daemon's status instanceId)\n *\n * A worker's completion event is scoped (`coordinator_daemon_id`) with whichever\n * form the dispatch path happened to stamp, but the coordinator that later drains /\n * surfaces that event resolves its OWN id through a different path and frequently\n * holds a DIFFERENT form. Because the scope filter is an exact-string match\n * (`coordinator_daemon_id IS NULL OR IN (...)` in SQL, `.includes()` in JS), a\n * completion stamped `daemon_mach_X` is silently skipped by a coordinator whose\n * self-id set only contains bare `mach_X` (or standalone form) — the event never\n * surfaces and the coordinator is never auto-notified, while NULL-scoped events\n * (e.g. worktree bootstrap) always pass via the `IS NULL` branch.\n *\n * This module is the single source of truth that collapses the three forms to one\n * machine core and EXPANDS a self-id set to every equivalent form, so a scope match\n * succeeds regardless of which form stamped the event. Expansion stays WITHIN a\n * single machine core (`daemon_mach_X` only ever expands to other `mach_X` forms),\n * so an event scoped to a DIFFERENT coordinator is never falsely claimed.\n */\n\nimport { readString } from './json'\n\nconst DAEMON_ID_PREFIXES = ['daemon_', 'standalone_'] as const\n\n/**\n * A daemon DO addressed by a raw 64-hex DO id (`idFromString`) rather than by a\n * canonical name (`idFromName(\"daemon_mach_<hex>\")`). Decide by FORMAT, not by\n * length — the canonical name is 43+ chars, so a `length > 32` heuristic would\n * misclassify it. Mirrors the server's session-routing `isRawDoId`.\n */\nexport function isRawDaemonDoId(id: string | null | undefined): boolean {\n const trimmed = readString(id)\n return !!trimmed && /^[0-9a-f]{64}$/i.test(trimmed)\n}\n\n/** The machine-evidence fields a real daemon reports about its hardware. */\nexport interface DaemonMachineEvidence {\n machineNickname?: string | null\n nickname?: string | null\n hostname?: string | null\n platform?: string | null\n machineId?: string | null\n machine?: { hostname?: string | null; platform?: string | null } | null\n sessions?: unknown[] | null\n}\n\n/**\n * A phantom daemon entry — a UI surface must not offer it as a real machine.\n *\n * Symptom block for the raw-DO-id ghost (GHOST-MACHINE-REGISTRATIONS). When the\n * `X-ADHDEV-Daemon` instanceId header is missing or unparseable, the /ws handler\n * falls back to a random DO name, so every reconnect mints a FRESH raw 64-hex DO\n * id. Those keys accumulate as hardware-less entries that render as a bare hash\n * where a machine name belongs.\n *\n * BOTH conditions are required, and the second is what keeps this safe:\n * 1. the id is a raw DO id — no `daemon_`/`standalone_` canonical prefix.\n * 2. no machine evidence at all — no nickname, hostname, platform, registered\n * machineId, and no sessions.\n *\n * A legacy / not-yet-reauthed daemon that ACTUALLY reports itself fails (2) and\n * therefore still renders, still attaches, and still routes — dropping on (1)\n * alone would make real daemons disappear from the picker. This is strictly a\n * PRESENTATION filter: routing tables (server `lastStatuses`, P2P relay) keep\n * the entry, because the raw key still resolves via `idFromString`.\n *\n * The server applies the same rule to its dashboard payloads via\n * `_unresolvedIdentity` (UserSession.isPhantomMachineEntry). That marker is\n * server-internal and never reaches the browser, and the client daemon store\n * never evicts an entry once injected — so a client surface reading the merged\n * P2P/WS store must re-derive the verdict from the entry SHAPE. This function is\n * that shared rule.\n */\nexport function isPhantomDaemonEntry(\n entry: (DaemonMachineEvidence & { id?: string | null }) | null | undefined,\n): boolean {\n if (!entry) return false\n if (!isRawDaemonDoId(entry.id)) return false\n const hasMachineEvidence = Boolean(\n readString(entry.machineNickname)\n || readString(entry.nickname)\n || readString(entry.hostname)\n || readString(entry.platform)\n || readString(entry.machine?.hostname)\n || readString(entry.machine?.platform)\n || readString(entry.machineId)\n || (Array.isArray(entry.sessions) && entry.sessions.length > 0),\n )\n return !hasMachineEvidence\n}\n\n/**\n * Reduce any daemon-id form to its machine core: strips a leading `daemon_` /\n * `standalone_` prefix, leaving the bare `mach_<hex>` (or returning a non-prefixed\n * id unchanged). Returns undefined for an empty/absent id.\n */\nexport function machineCoreFromDaemonId(id: string | null | undefined): string | undefined {\n const trimmed = readString(id)\n if (!trimmed) return undefined\n for (const prefix of DAEMON_ID_PREFIXES) {\n if (trimmed.startsWith(prefix)) {\n const core = trimmed.slice(prefix.length).trim()\n return core || undefined\n }\n }\n return trimmed\n}\n\n/**\n * Canonicalize any daemon-id form to the single CANON producer form\n * `daemon_mach_<core>` (the cloud `daemon_` form).\n *\n * CANON-IDENTITY double-dispatch root cause: the coordinator daemon id is stamped\n * onto a worker dispatch by TWO independent producers — the MCP-side\n * resolveCoordinatorDaemonId (which prefers the coordinator mesh node's config-form\n * `daemon_mach_X` daemonId) and the daemon-core queue dispatch (which stamps the\n * bare `loadConfig().machineId` = `mach_X`). When the SAME coordinator dispatches\n * the same task down both paths, the two worker sessions are stamped with two\n * DIFFERENT coordinator-id forms; a raw-string dedup that should recognise \"this\n * task is already dispatched by me\" fails, and the task runs twice.\n *\n * The durable fix is comparator-side (daemonIdsEquivalent / expandDaemonIdForms),\n * but unifying every PRODUCER on one canonical form shrinks the surface so even a\n * raw `===` agrees. The canon is the cloud `daemon_` form because that is what the\n * coordinator mesh node's config-form daemonId already carries and what the cloud\n * P2P signaling layer registers a daemon under — so canonicalizing the bare/standalone\n * fallback forms makes them consistent with the already-working primary path.\n *\n * Only a `mach_<…>` core is rewritten; an arbitrary/non-machine id (e.g. a custom\n * node id) is returned unchanged so it is never ballooned into a spurious `daemon_`\n * form. Idempotent. Returns undefined for an empty/absent id.\n */\nexport function canonicalDaemonId(id: string | null | undefined): string | undefined {\n const core = machineCoreFromDaemonId(id)\n if (!core) return undefined\n if (!core.startsWith('mach_')) return core\n return `daemon_${core}`\n}\n\n/** True when both ids resolve to the same machine core — i.e. they are the same\n * daemon under different id forms. False when either side is empty. */\nexport function daemonIdsEquivalent(a: string | null | undefined, b: string | null | undefined): boolean {\n const coreA = machineCoreFromDaemonId(a)\n const coreB = machineCoreFromDaemonId(b)\n if (!coreA || !coreB) return false\n return coreA === coreB\n}\n\n/**\n * Expand a set of coordinator/daemon ids to every equivalent form, so an\n * exact-string scope filter (SQL `IN (...)` or JS `.includes()`) matches a\n * completion stamped in any form of the same machine. The original ids are kept in\n * their input order and FIRST (callers that treat `[0]` as the primary — e.g.\n * per-daemon JSONL file naming — keep their original primary); the derived\n * `mach_<hex>` / `daemon_mach_<hex>` / `standalone_mach_<hex>` forms are appended.\n *\n * Derived prefixed forms are emitted ONLY for a core that looks like a real machine\n * id (`mach_<…>`), so arbitrary/test ids (e.g. `node-daemon-id`) are passed through\n * untouched and never balloon into spurious forms. Result is de-duplicated.\n */\nexport function expandDaemonIdForms(\n ids: string | null | undefined | ReadonlyArray<string | null | undefined>,\n): string[] {\n const list = Array.isArray(ids) ? ids : ids != null ? [ids] : []\n const out: string[] = []\n const seen = new Set<string>()\n const add = (value: string | undefined): void => {\n if (!value || seen.has(value)) return\n seen.add(value)\n out.push(value)\n }\n // Pass 1: originals first, in input order (preserve caller's primary at [0]).\n for (const raw of list) add(readString(raw))\n // Pass 2: derived machine-core forms for any id that resolves to a `mach_` core.\n for (const raw of list) {\n const core = machineCoreFromDaemonId(readString(raw))\n if (!core || !core.startsWith('mach_')) continue\n add(core)\n for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`)\n }\n return out\n}\n","/**\n * Canonical compact git-shape summarizer used by debug/log surfaces on both the\n * cloud (daemon-cloud mesh command summarizer) and standalone (daemon-core router\n * RepoMeshStatusDebug) paths. Produces a small, log-safe projection of a git\n * status record — commit SHAs are truncated to 12 chars.\n *\n * Transport-specific envelope unwrapping (result / result.status / top-level)\n * stays in the caller; this takes an already-unwrapped status record.\n */\n\nimport { readBoolean, readNumber, readRecord, readString } from './json'\n\nexport function summarizeGitShape(status: unknown): Record<string, unknown> | null {\n const record = readRecord(status)\n if (!Object.keys(record).length) return null\n const submodules = Array.isArray(record.submodules)\n ? record.submodules.map((entry: unknown) => {\n const sub = readRecord(entry)\n return {\n path: readString(sub.path) ?? null,\n commit: readString(sub.commit)?.slice(0, 12) ?? null,\n dirty: readBoolean(sub.dirty) ?? false,\n outOfSync: readBoolean(sub.outOfSync, sub.out_of_sync) ?? false,\n }\n })\n : []\n return {\n isGitRepo: readBoolean(record.isGitRepo),\n workspace: readString(record.workspace) ?? null,\n repoRoot: readString(record.repoRoot, record.repo_root) ?? null,\n branch: readString(record.branch) ?? null,\n upstream: readString(record.upstream) ?? null,\n upstreamStatus: readString(record.upstreamStatus, record.upstream_status) ?? null,\n headCommit: readString(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,\n ahead: readNumber(record.ahead) ?? null,\n behind: readNumber(record.behind) ?? null,\n dirtyCounts: {\n staged: readNumber(record.staged) ?? 0,\n modified: readNumber(record.modified) ?? 0,\n untracked: readNumber(record.untracked) ?? 0,\n deleted: readNumber(record.deleted) ?? 0,\n renamed: readNumber(record.renamed) ?? 0,\n },\n lastCheckedAt: readNumber(record.lastCheckedAt, record.last_checked_at) ?? null,\n submoduleCount: submodules.length,\n submodules,\n }\n}\n","/**\n * MAGI — Multi-Agent Ground-truth Insight.\n *\n * Pure shared types for the mesh cross-verification quorum: the per-task_kind\n * panel binding (machine-local config, stored in ~/.adhdev/meshes.json\n * `magiKindPanels`), the agent-agnostic common output schema every dispatched\n * replica answers with, and the synthesis result shapes. These cross the\n * daemon-core (storage / accessors) ↔ mcp-server (fan-out / synthesis) boundary,\n * so they live in the dependency-free mesh-shared leaf — no runtime, no Node/DOM APIs.\n *\n * Design: docs/design/2026-06-28-mesh-magi-review.md. Core stance: no personas,\n * no named lenses — a panel slot is just one `(node × provider)` target that\n * answers the SAME question. The value is the friction (contested / singleton /\n * source-coupled findings), NOT a majority vote.\n *\n * NOTE: the former named-panel model (MagiPanel / MagiPanelMember / MagiPanelMap\n * and inline `members`) was REMOVED. A MAGI review resolves its fan-out slots\n * EXCLUSIVELY from the `magiKindPanels` binding for the review's `task_kind`; an\n * unconfigured kind is a hard error, never a synthesized or named-panel fallback.\n */\n\n// ─── Kind → panel binding (machine-local config) ─────────\n//\n// MAGI-KIND-PANEL: an explicit, per-task_kind panel binding. `mesh_magi_review`\n// invoked with a bare `task_kind` (no panel name / inline members) resolves the\n// panel from THIS map — the user must have configured ≥1 slot for that kind in mesh\n// settings. There is NO hardcoded preset auto-synthesis fallback: an unconfigured\n// kind is a hard error (magi_kind_not_configured), never a silent synthetic panel.\n\n/**\n * One kind-panel slot: a `(node × provider [× model])` target. `provider` required;\n * `nodeId` pins a concrete mesh node; `model` optionally selects the agent model at\n * launch; `capabilityTags` route by tag when no nodeId is given; `n` is an optional\n * per-slot replica count. This is the SOLE panel-member shape — the fan-out planner\n * (buildMagiFanoutPlan) resolves a `MagiSlot[]` directly.\n *\n * ─── DELIBERATELY REDUCED SCHEMA — not a missing feature ─────────────────────\n *\n * A MagiSlot is a strict subset of a node's `NodeCapabilitySlot`. It accepts\n * `provider` + optional `model` / `nodeId` / `capabilityTags` / `n`, and it\n * deliberately does NOT accept the node-capability routing axes — `thinkingLevel`,\n * `difficulty`, `maxParallel`. That asymmetry is the design, not an oversight:\n *\n * - A node capability slot answers **\"which work goes where\"** — routing FITNESS.\n * Difficulty and thinking level exist there to match a task against the slot best\n * suited to run it.\n * - A MAGI panel slot answers **\"who answers independently\"** — cross-verification\n * DIVERSITY. Its whole value is that the replicas are NOT selected for fitness.\n *\n * Reviving the difficulty/thinking axes here would couple panel membership to routing\n * optimization, and the best-fitting provider would win every slot. The panel would\n * collapse toward one provider — which is precisely the failure MAGI exists to\n * prevent, since agreement among coupled agents carries no information (see the\n * `source_coupled` weighting in synthesis). A reduced schema is what keeps the two\n * axes orthogonal.\n *\n * Consistent with that, `mesh_magi_review` enqueues every replica with a fixed\n * `difficulty: 'freeform'` sentinel rather than a caller-chosen grade — the panel has\n * already pinned the (node, provider) target, so any difficulty would be inert at best\n * and would fight the panel's own slot selection at worst. To change how hard a\n * replica thinks or how much parallelism a node grants, edit that NODE's capability\n * slots (`mesh_node_slots_set`); it is a different axis, on purpose.\n *\n * Unknown keys are dropped rather than rejected, so slots written by another version\n * stay readable. Write paths pair the normalizer with\n * `collectIgnoredMagiSlotFields()` and report the drops as `ignoredFields`, so the\n * reduction is visible instead of silent.\n */\nexport interface MagiSlot {\n /** Optional — pin to a specific mesh node id. Absent → route by capabilityTags + provider. */\n nodeId?: string\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'gemini-cli'. */\n provider: string\n /**\n * Optional model override applied at replica launch (e.g. 'opus' | 'sonnet' for\n * claude-cli). Threaded through enqueueTask → the auto-launched session's\n * `launch_cli` payload as `initialModel`. For ACP providers it drives\n * setConfigOption('model', …); for CLI providers it is expanded via the provider\n * manifest's `modelLaunchArgs` template into launch args (a provider with no\n * template silently ignores it — model is best-effort, never fatal).\n */\n model?: string\n /** Optional routing tags, ANDed with the provider tag when nodeId is absent. */\n capabilityTags?: string[]\n /** Optional per-slot replica count; defaults to the kind-panel defaultN / global n / 1. */\n n?: number\n}\n\n/**\n * Per-task_kind panel binding for ONE mesh, stored machine-local in\n * `~/.adhdev/meshes.json` under that mesh's entry (`meshes[].magiKindPanels`). The\n * scope is per mesh: two meshes on the same machine hold independent bindings for\n * the same task_kind. (It formerly sat at the config root keyed by task_kind alone,\n * which let one mesh's write clobber another's.) A kind absent from the map has NO\n * configured panel → `mesh_magi_review({task_kind})` errors with\n * `magi_kind_not_configured` rather than synthesizing one. `freeform` MAY be bound\n * like any other kind (a direct kind→slots binding).\n */\nexport type MagiKindPanelMap = Partial<Record<MagiTaskKind, MagiSlot[]>>\n\n/**\n * The output-schema selector a MAGI fan-out injects into every replica prompt and\n * the strict parser used at collection. Code-orthogonal to the panel's slot set\n * (the fan-out planner never reads it) — it ONLY shapes the per-replica prompt /\n * parse contract. SSOT lives here (mesh-shared leaf) so both daemon-core (slot\n * normalization) and mcp-server (resolution / prompt assembly) consume one union.\n *\n * - claim_audit (default, backward-compatible), rca, design → require evidence[].\n * - freeform → no schema, no evidence; contributes NO structured claims to\n * synthesis. It MAY still be bound as a kind-panel key (a direct kind→slots\n * binding), unlike the removed named-panel `defaultKind`.\n */\nexport type MagiTaskKind = 'claim_audit' | 'rca' | 'design' | 'freeform'\n\n/**\n * Synthesis emphasis hint. Affects weighting / labels only — NEVER the agent\n * count or the common schema. (Per-mode weighting tuning is a deferred refinement;\n * the field is accepted now so callers and panels are forward-stable.)\n */\nexport type MagiMode = 'rca' | 'investigation' | 'claim_audit' | 'design_review' | 'code_audit'\n\n// ─── Common output schema (agent-agnostic) ──────\n\nexport type MagiClaimStance = 'support' | 'oppose' | 'uncertain'\n\n/**\n * One claim from one agent. `evidence` carries `file:line` or external-source\n * strings; `confidence` is 0..1. Identical regardless of which provider/machine\n * produced it — this is the forced structured-output contract injected into each\n * dispatched task prompt.\n */\nexport interface MagiClaim {\n claim: string\n stance: MagiClaimStance\n evidence: string[]\n confidence: number\n}\n\n/** The agent-agnostic response every dispatched replica returns. */\nexport interface MagiAgentResponse {\n claims: MagiClaim[]\n top_findings: string[]\n open_questions: string[]\n}\n\n// ─── Synthesis result shapes ────────────────────\n\n/**\n * Where one response came from — the `(node × provider)` identity that backs a\n * claim's independence. `ok=false` marks a replica that died / produced no\n * parseable common-schema output (excluded from clusters, counted as missing).\n */\nexport interface MagiResponseSource {\n /** Mesh task id of the dispatched replica. */\n taskId: string\n nodeId?: string\n provider?: string\n /** False when the replica failed or its output could not be parsed. */\n ok: boolean\n /** Reason when ok=false (timeout / failed / unparseable / stale). */\n error?: string\n /**\n * True when the replica was detected STALE during collection — assigned to a\n * node/session no longer present in the live mesh (so it will never reach a\n * terminal state). Distinguishes a dead-assignment replica from one that is\n * merely still generating. Always implies ok=false.\n */\n stale?: boolean\n /**\n * Git ref of the node this replica ran on, captured at collection time from the\n * live mesh node's compact git summary. Lets synthesis (and a dashboard) detect\n * GIT SKEW across the panel — if the answering replicas span different branches\n * or diverge (ahead/behind), their file:line evidence is comparing different code\n * and \"agreement\" is less meaningful. Best-effort; absent when the node carried\n * no git summary.\n */\n git?: MagiReplicaGitRef\n /**\n * The replica's raw end-user answer text as read from its session transcript at\n * collection time, truncated to MAGI_RAW_ANSWER_CAP chars (with `rawAnswerTruncated`\n * set when it was longer). This is the human-readable original a replica produced —\n * useful when the structured claim parse dropped nuance, or for a cluster member that\n * needs the source prose. GATED: stripped from the persisted `magi_synthesis` ledger\n * entry (to bound ledger payload growth) and from the default mesh_magi_collect\n * response; surfaced ONLY in mesh_magi_collect verbose. Best-effort; absent for a\n * replica that produced no readable transcript (failed / stale / unparseable).\n */\n rawAnswer?: string\n /** True when `rawAnswer` was truncated at MAGI_RAW_ANSWER_CAP. */\n rawAnswerTruncated?: boolean\n}\n\n/** Max chars of a replica's raw answer retained on MagiResponseSource.rawAnswer. */\nexport const MAGI_RAW_ANSWER_CAP = 4000\n\n/** Compact git ref of the node a replica ran on (subset of GitRepoStatus). */\nexport interface MagiReplicaGitRef {\n branch?: string | null\n /** HEAD commit sha of the node's workspace — the exact code the replica saw. */\n headCommit?: string | null\n ahead?: number\n behind?: number\n dirty?: boolean\n}\n\n/**\n * Cross-replica git divergence assessment. `skewed` is true when the answering\n * replicas span more than one branch OR any replica diverges from its upstream\n * (ahead/behind > 0) — both mean the panel is not all looking at the same code, so\n * file:line evidence and agreement are git-skewed. Always present on a synthesis\n * (skewed=false / distinctBranches≤1 when there is nothing to flag).\n */\nexport interface MagiGitSkew {\n skewed: boolean\n /** Number of distinct branches across the answering replicas with a known branch. */\n distinctBranches: number\n /** The distinct branch names (sorted), for display. */\n branches: string[]\n /** Replicas whose branch/divergence differs from the panel baseline. */\n divergentReplicas: number\n note?: string\n}\n\n/** A parsed replica response paired with its source identity. */\nexport interface MagiSynthesizedResponse {\n source: MagiResponseSource\n response: MagiAgentResponse\n}\n\n/**\n * Synthesis category for a claim cluster. `needs_verification` is the PRIMARY\n * OUTPUT = contested ∪ singleton ∪ source_coupled ∪ (high-impact claims lacking\n * independent evidence). `agreed` is the only \"safe to trust, low priority\" bucket.\n */\nexport type MagiClusterCategory =\n | 'agreed'\n | 'contested'\n | 'dissent'\n | 'singleton'\n | 'source_coupled'\n\n/** One member observation inside a claim cluster. */\nexport interface MagiClusterMember {\n taskId: string\n nodeId?: string\n provider?: string\n claim: string\n stance: MagiClaimStance\n evidence: string[]\n confidence: number\n}\n\n/**\n * A cluster of semantically-equivalent claims across responses, with its\n * stance tally and diversity-weighted independence assessment.\n */\nexport interface MagiClaimCluster {\n /** Representative (first / longest) claim text for the cluster. */\n claim: string\n category: MagiClusterCategory\n members: MagiClusterMember[]\n stance: { support: number; oppose: number; uncertain: number }\n /** Distinct providers / nodes / evidence sources backing the cluster. */\n distinctProviders: number\n distinctNodes: number\n distinctEvidence: number\n /** Diversity-weighted independence score (NOT the raw agent count). */\n independenceScore: number\n /** True when this cluster is routed to needs_verification. */\n needsVerification: boolean\n /** Why it needs verification (contested / singleton / source_coupled / no_independent_evidence). */\n reasons: string[]\n}\n\n/** The full synthesis result — N-agnostic, diversity-weighted, not a vote. */\nexport interface MagiSynthesis {\n /** How many replicas were expected vs. produced parseable output. */\n replicasExpected: number\n replicasAnswered: number\n replicasMissing: number\n /** Distinct providers / nodes across the answering replicas. */\n distinctProviders: number\n distinctNodes: number\n /**\n * Set when the resolved panel collapsed to a single provider or single\n * machine — agreements are then flagged source-coupled. Null when independence\n * was achieved.\n */\n independenceBanner: string | null\n clusters: MagiClaimCluster[]\n /** PRIMARY OUTPUT — clusters routed to needs_verification, highest priority first. */\n needsVerification: MagiClaimCluster[]\n /** High-independence agreements — safe to trust, lowest review priority. */\n agreed: MagiClaimCluster[]\n /** Union of every response's open_questions (deduped). */\n openQuestions: string[]\n /**\n * Per-replica source identity (taskId / nodeId / provider / ok / stale / git) for\n * every replica in the fan-out. Lets a consumer (the dashboard's extractMagiActivity)\n * read which node × provider answered and the git ref each ran at — the inputs behind\n * the gitSkew assessment.\n */\n replicas: MagiResponseSource[]\n /**\n * Cross-replica git divergence. When skewed, the answering replicas were not all\n * on the same code (different branches / ahead-behind), so evidence and agreement\n * should be read with that caveat. Always present.\n */\n gitSkew: MagiGitSkew\n}\n","/**\n * Brain-routing (task difficulty → provider/model/thinking) types.\n *\n * The coordinator classifies each task it enqueues by execution difficulty, and a\n * per-difficulty \"brain\" preset resolves that into a concrete provider / model /\n * thinking-level for the launched session. The goal is token economy: an `easy`\n * task runs on a cheaper model at low reasoning effort; a `difficult` task gets a\n * stronger model at high effort. This is a separate axis from MAGI's review kinds\n * (rca/design/…) — MAGI fans out review replicas; this picks the single brain that\n * *executes* a task — but it reuses the same slot shape (provider/model) and the\n * same machine-local storage (`~/.adhdev/meshes.json`).\n */\n\n/** The fixed difficulty axis the coordinator classifies a task into. */\nexport type MeshTaskDifficulty = 'easy' | 'medium' | 'difficult' | 'freeform'\n\nexport const MESH_TASK_DIFFICULTIES: MeshTaskDifficulty[] = ['easy', 'medium', 'difficult', 'freeform']\n\n/**\n * A per-difficulty brain preset: which provider / model / thinking level a task of\n * that difficulty should run on. Every field is optional — a preset may set only a\n * model (keep the routed provider) or only a thinking level. Applied at enqueue:\n * an explicit model/thinkingLevel on the task always wins over the preset.\n */\nexport interface BrainSlot {\n /** Optional provider type, e.g. 'claude-cli' | 'codex-cli'. Absent → keep the tag/priority-routed provider. */\n provider?: string\n /** Optional model, e.g. 'opus' | 'sonnet' | 'haiku'. Best-effort at launch (initialModel). */\n model?: string\n /** Optional standard thinking level, 'low' | 'medium' | 'high'. Best-effort (initialThinkingLevel). */\n thinkingLevel?: 'low' | 'medium' | 'high'\n}\n\n/**\n * Per-difficulty brain bindings for ONE mesh, stored machine-local in\n * `~/.adhdev/meshes.json` under that mesh's entry (`meshes[].difficultyBrains`,\n * sibling of `magiKindPanels`). The scope is per mesh: two meshes on the same\n * machine hold independent presets, so one can pin `difficult` to a cheaper model\n * without changing what any other mesh runs. (It formerly sat at the config root\n * keyed by difficulty alone — and since this map picks the MODEL a task runs on,\n * that meant the shipped defaults, and any one mesh's override, applied to every\n * mesh on the machine.) A difficulty absent from the map has no preset — the task\n * runs with no difficulty-derived model/thinking (ordinary routing).\n */\nexport type DifficultyBrainMap = Partial<Record<MeshTaskDifficulty, BrainSlot>>\n\n/** True for a recognized difficulty value. */\nexport function isMeshTaskDifficulty(value: unknown): value is MeshTaskDifficulty {\n return typeof value === 'string' && (MESH_TASK_DIFFICULTIES as string[]).includes(value)\n}\n\n/**\n * Shipped difficulty presets: NONE. Operator-authored capability slots are the\n * authority: easy < medium < difficult is a hard minimum (freeform is explicitly\n * unconstrained), while the preset map only supplies optional launch axes.\n *\n * WHY THIS IS EMPTY (it formerly shipped easy→haiku/low, medium→sonnet/medium,\n * difficult→opus/high):\n *\n * 1. WRONG AXIS. `opus`/`sonnet`/`haiku` are Claude model families, but the\n * preset was stamped on a task at ENQUEUE time — before routing has chosen\n * a node, let alone a provider. A `difficult` task therefore carried\n * `model: 'opus'` even when it landed on kimi / codex / antigravity. The\n * CODEX-400 guard (model-provider-compat.ts) exists ONLY to strip these\n * Claude aliases back off before a non-Anthropic launch — a workaround for\n * a value that should never have been stamped provider-blind.\n *\n * 2. IT FOUGHT THE SLOTS. Operators express model choice per node via\n * capability slots (`mesh_node_slots_set`), which know the provider. A\n * mesh-wide preset that also picks a model duplicated that decision from a\n * position with strictly less information, and needed the modelSource\n * ('explicit' vs 'preset') precedence machinery plus a fail-closed\n * slot-model guard just to keep the slot winning.\n *\n * Removing the shipped defaults does NOT remove the feature: the map is still\n * honored, so an operator who deliberately sets presets (difficulty_brains_set)\n * keeps exactly that behavior. It only stops the mesh from inventing a model\n * nobody configured. A node WITH slots is unaffected (its slots already decided);\n * a node WITHOUT slots now launches on the provider's own default model instead\n * of a Claude alias picked blind.\n */\nexport const DEFAULT_DIFFICULTY_BRAINS: DifficultyBrainMap = {}\n\n/** Normalize a raw thinking level to the standard union, or undefined. */\nexport function normalizeThinkingLevel(value: unknown): 'low' | 'medium' | 'high' | undefined {\n const v = typeof value === 'string' ? value.trim().toLowerCase() : ''\n return v === 'low' || v === 'medium' || v === 'high' ? v : undefined\n}\n\n/** Normalize a raw BrainSlot (trim strings, drop empties, clamp thinkingLevel). */\nexport function normalizeBrainSlot(raw: unknown): BrainSlot {\n const r = (raw && typeof raw === 'object') ? raw as Record<string, unknown> : {}\n const provider = typeof r.provider === 'string' ? r.provider.trim() : ''\n const model = typeof r.model === 'string' ? r.model.trim() : ''\n const thinkingLevel = normalizeThinkingLevel(r.thinkingLevel)\n return {\n ...(provider ? { provider } : {}),\n ...(model ? { model } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n }\n}\n\n/** Normalize a raw DifficultyBrainMap, dropping unknown keys and empty slots. */\nexport function normalizeDifficultyBrainMap(raw: unknown): DifficultyBrainMap {\n const out: DifficultyBrainMap = {}\n if (!raw || typeof raw !== 'object') return out\n for (const key of MESH_TASK_DIFFICULTIES) {\n const slot = normalizeBrainSlot((raw as Record<string, unknown>)[key])\n if (slot.provider || slot.model || slot.thinkingLevel) out[key] = slot\n }\n return out\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Node capability slots (ORCHESTRATION_NODE_SLOTS.md)\n//\n// A node's \"Preferred AI tools\" list is redefined as an ordered array of\n// capability slots. Each slot bundles what used to be scattered across\n// providerPriority (order), a per-provider maxParallel cap, and the\n// the owning mesh's difficultyBrains (difficulty → model/thinking). Slot order =\n// preference. This single profile is the source of truth for task routing, MAGI\n// fan-out, and orchestrator-proposed edits.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * One capability slot on a mesh node. Extends the BrainSlot shape\n * (provider/model/thinkingLevel) with the difficulty range it handles, capability\n * tags, and a per-slot parallelism cap.\n */\nexport interface NodeCapabilitySlot {\n /** Provider type this slot uses, e.g. 'claude-cli' | 'codex-cli'. Required (a slot is defined by its provider). */\n provider: string\n /** Optional model, e.g. 'opus' | 'sonnet' | 'haiku'. Best-effort at launch. */\n model?: string\n /**\n * Optional thinking level. The provider's own vocabulary (e.g. low/medium/high,\n * or codex's low/medium/high/max) passed through verbatim — best-effort at\n * launch. A string, not the standard union, so provider-declared levels like\n * 'max' are not dropped.\n */\n thinkingLevel?: string\n /**\n * Difficulty grades this slot handles. On an explicit-slot node these form a\n * hard minimum: higher grades may run lower tasks, but lower grades never run\n * higher tasks. Empty/absent is ungraded and only unconstrained freeform tasks\n * use it; legacy providerPriority-derived slots remain backward-compatible.\n */\n difficulty?: MeshTaskDifficulty[]\n /** Capability tags this slot satisfies (matched against a task's requiredTags). */\n capability?: string[]\n /** Per-node·per-slot max concurrent tasks. Omit = no per-slot cap. */\n maxParallel?: number\n}\n\n/** Normalize a raw NodeCapabilitySlot; returns null when it has no usable provider. */\nexport function normalizeNodeCapabilitySlot(raw: unknown): NodeCapabilitySlot | null {\n const r = (raw && typeof raw === 'object') ? raw as Record<string, unknown> : {}\n const provider = typeof r.provider === 'string' ? r.provider.trim() : ''\n if (!provider) return null\n const model = typeof r.model === 'string' ? r.model.trim() : ''\n // Pass the provider's own thinking-level vocabulary through verbatim (don't\n // clamp to low/medium/high — a provider may declare 'max' etc.).\n const thinkingLevel = typeof r.thinkingLevel === 'string' ? r.thinkingLevel.trim() : ''\n const difficulty = Array.isArray(r.difficulty)\n ? (r.difficulty.filter(isMeshTaskDifficulty) as MeshTaskDifficulty[])\n : []\n const capability = Array.isArray(r.capability)\n ? r.capability.filter((t): t is string => typeof t === 'string' && !!t.trim()).map(t => t.trim())\n : []\n const maxParallelNum = Number(r.maxParallel)\n const maxParallel = Number.isFinite(maxParallelNum) && maxParallelNum > 0 ? Math.floor(maxParallelNum) : undefined\n return {\n provider,\n ...(model ? { model } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n ...(difficulty.length ? { difficulty } : {}),\n ...(capability.length ? { capability } : {}),\n ...(maxParallel !== undefined ? { maxParallel } : {}),\n }\n}\n\n/** Normalize a raw slot array, dropping provider-less entries. */\nexport function normalizeNodeCapabilitySlots(raw: unknown): NodeCapabilitySlot[] {\n if (!Array.isArray(raw)) return []\n const out: NodeCapabilitySlot[] = []\n for (const entry of raw) {\n const slot = normalizeNodeCapabilitySlot(entry)\n if (slot) out.push(slot)\n }\n return out\n}\n\n/**\n * Back-compat migration: derive capability slots from the legacy fields when a\n * node has no explicit `slots`. Order follows providerPriority; difficulty/model/\n * thinking are folded in from the OWNING MESH's difficultyBrains (each difficulty\n * attaches to the slot whose provider the brain preset names, or — when the brain\n * has no provider — to every slot as a shared model/thinking default for that\n * difficulty).\n *\n * (Legacy nodes' per-provider `maxParallel` cap has been migrated onto\n * `slots[].maxParallel` at config-load time, so it is no longer folded in here.)\n *\n * Returns [] when there's nothing to derive (caller then keeps legacy behavior:\n * first available provider).\n */\nexport function deriveSlotsFromLegacy(input: {\n providerPriority?: string[]\n difficultyBrains?: DifficultyBrainMap\n}): NodeCapabilitySlot[] {\n const priority = Array.isArray(input.providerPriority)\n ? input.providerPriority.filter((p): p is string => typeof p === 'string' && !!p.trim()).map(p => p.trim())\n : []\n if (priority.length === 0) return []\n\n // Brain presets keyed by the provider they name (provider-specific), plus a\n // provider-agnostic list applied to every slot as a shared default.\n const brains = input.difficultyBrains || {}\n const byProvider = new Map<string, Array<{ difficulty: MeshTaskDifficulty; model?: string; thinkingLevel?: 'low' | 'medium' | 'high' }>>()\n const shared: Array<{ difficulty: MeshTaskDifficulty; model?: string; thinkingLevel?: 'low' | 'medium' | 'high' }> = []\n for (const diff of MESH_TASK_DIFFICULTIES) {\n const b = brains[diff]\n if (!b) continue\n const entry = { difficulty: diff, model: b.model, thinkingLevel: b.thinkingLevel }\n if (b.provider) {\n const list = byProvider.get(b.provider) ?? []\n list.push(entry)\n byProvider.set(b.provider, list)\n } else {\n shared.push(entry)\n }\n }\n\n return priority.map((provider): NodeCapabilitySlot => {\n // Provider-specific brain first, else the shared default, else nothing.\n const specific = byProvider.get(provider) || []\n const applied = specific.length ? specific : shared\n const difficulty = applied.map(a => a.difficulty)\n // Fold model/thinking from the applied presets: take the first that sets each.\n const model = applied.find(a => a.model)?.model\n const thinkingLevel = applied.find(a => a.thinkingLevel)?.thinkingLevel\n return {\n provider,\n ...(model ? { model } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n ...(difficulty.length ? { difficulty } : {}),\n }\n })\n}\n\n/**\n * The reverse direction of deriveSlotsFromLegacy: derive the legacy\n * `providerPriority` order from capability slots — each slot's provider, in\n * first-appearance order, de-duplicated. Slot order = preference is the settled\n * slots semantics (ORCHESTRATION_NODE_SLOTS.md), so this is what readers of the\n * legacy `policy.providerPriority` field must fall back to when a node declares\n * slots but no explicit providerPriority (otherwise such a node reads as\n * unlaunchable even though its slots fully determine the preference order).\n *\n * Accepts the raw `policy.slots` value (normalized internally). Returns [] when\n * there's nothing to derive (caller then keeps legacy behavior: no priority).\n */\nexport function deriveProviderPriorityFromSlots(slots: unknown): string[] {\n const seen = new Set<string>()\n const out: string[] = []\n for (const slot of normalizeNodeCapabilitySlots(slots)) {\n if (seen.has(slot.provider)) continue\n seen.add(slot.provider)\n out.push(slot.provider)\n }\n return out\n}\n","/**\n * CLI auto-detect → capability-slot / MAGI-panel PROPOSAL generator.\n *\n * Detection of installed CLI providers already exists per node (the status\n * snapshot's `availableProviders`), and applying a slot profile already exists\n * (`mesh_node_slots_set`, dry-run by default). What was missing is the bit in\n * between: turning \"these CLIs are installed on this node\" into a concrete\n * `NodeCapabilitySlot[]` draft the operator can review. This module is that\n * bridge, and nothing more — it is a PURE proposal generator. It never writes;\n * the caller feeds its output into the existing dry-run/approve tools.\n *\n * ─── Why the mapping is a static table ───────────────────────────────────────\n *\n * Provider manifests carry NO difficulty or capability-grade information. The\n * fields that look like they might (`modelOptions`, `thinkingLevelOptions`)\n * describe what a provider ACCEPTS, not what it is GOOD AT — a provider listing\n * `opus` says nothing about whether opus should get the hard tasks. So there is\n * no honest way to derive difficulty from the manifest today.\n *\n * The table below is therefore seeded from the operator's real, in-use slot\n * configuration rather than from a guess. That makes it a starting point with\n * actual provenance, and it is deliberately isolated in ONE constant so the\n * planned usage-data-driven replacement has a single, obvious swap point.\n *\n * Kept in the dependency-free mesh-shared leaf because both daemon-core (which\n * has the detection data) and mcp-server (which owns the propose/apply tools)\n * need it, and it is pure data + pure functions on plain objects.\n */\nimport {\n normalizeNodeCapabilitySlot,\n type MeshTaskDifficulty,\n type NodeCapabilitySlot,\n} from './brain-routing'\nimport type { MagiSlot } from './magi'\n\n/**\n * One provider's seeded slot recipe. A provider may map to MORE THAN ONE slot\n * (claude-cli does: a wide sonnet slot plus a narrow opus slot for the hard\n * work), which is why the table's values are arrays.\n */\nexport interface CliSlotRecipe {\n /** Optional model to pin on the slot. Best-effort at launch. */\n model?: string\n /** Optional thinking level, in the provider's own vocabulary. */\n thinkingLevel?: string\n /** Difficulty range this slot handles. Empty = general-purpose. */\n difficulty?: MeshTaskDifficulty[]\n /** Per-slot concurrency cap. */\n maxParallel?: number\n /**\n * Set when this recipe is an unvalidated guess rather than a transcription\n * of a slot the operator actually runs. Surfaced on the proposal so the\n * reviewer knows which lines to scrutinize.\n */\n provisional?: boolean\n /** Short human-readable justification, echoed into the proposal. */\n rationale?: string\n}\n\n/**\n * ★ THE MAPPING TABLE — the single swap point.\n *\n * Seeded (2026-08-03) from the operator's live slot configuration, on the\n * reasoning that a transcription of what is actually in production beats an\n * invented heuristic. Replace wholesale once usage data (success rate, cost,\n * turn latency per provider×difficulty) can drive it.\n *\n * Every entry except `hermes-cli` reflects a real configured slot. `hermes-cli`\n * is a conservative GUESS — see its `provisional` flag.\n */\nexport const CLI_SLOT_RECIPES: Readonly<Record<string, readonly CliSlotRecipe[]>> = Object.freeze<Record<string, readonly CliSlotRecipe[]>>({\n 'claude-cli': [\n {\n model: 'sonnet',\n thinkingLevel: 'high',\n difficulty: ['medium', 'easy'],\n maxParallel: 5,\n rationale: 'Primary workhorse — widest parallelism for routine work.',\n },\n {\n model: 'opus',\n thinkingLevel: 'high',\n difficulty: ['difficult'],\n maxParallel: 1,\n rationale: 'Reserved for hard tasks; capped at 1 to bound cost.',\n },\n ],\n 'kimi': [\n {\n model: 'kimi-code/k3',\n difficulty: ['medium', 'difficult'],\n maxParallel: 2,\n rationale: 'Independent second opinion on mid/hard work.',\n },\n ],\n 'codex-cli': [\n {\n difficulty: ['medium', 'difficult', 'freeform'],\n maxParallel: 2,\n rationale: 'Broad range including freeform; no model pin.',\n },\n ],\n 'antigravity-cli': [\n {\n model: 'Gemini 3.1 Pro (High)',\n difficulty: ['easy'],\n maxParallel: 2,\n rationale: 'Cheap capacity for easy tasks.',\n },\n ],\n 'cursor-cli': [\n {\n model: 'auto',\n difficulty: ['easy'],\n maxParallel: 1,\n rationale: 'Easy tasks only; auto model selection.',\n },\n ],\n 'hermes-cli': [\n {\n difficulty: ['medium'],\n maxParallel: 2,\n provisional: true,\n // NOTE: ESTIMATE, NOT OBSERVED. hermes-cli is absent from the live\n // slot configuration this table was seeded from, so `medium` is a\n // conservative placement rather than a transcription. Revisit once\n // it has real usage data.\n rationale: 'ESTIMATE — no live slot to transcribe; conservative mid placement. Adjust after real use.',\n },\n ],\n})\n\n/**\n * Fallback for a detected CLI provider absent from {@link CLI_SLOT_RECIPES}.\n * Deliberately timid: one general-purpose slot at the lowest parallelism, so an\n * unrecognized provider can be used but never silently soaks up the queue.\n */\nexport const UNKNOWN_CLI_SLOT_RECIPE: Readonly<CliSlotRecipe> = Object.freeze<CliSlotRecipe>({\n difficulty: ['medium'],\n maxParallel: 1,\n provisional: true,\n rationale: 'Unrecognized provider — conservative default (medium, maxParallel 1). Review before relying on it.',\n})\n\n/** A detected, installed CLI provider on one node — the generator's input. */\nexport interface DetectedCliProvider {\n /** Provider type id, e.g. 'claude-cli'. */\n type: string\n /** Human-readable name, for display in the proposal. */\n displayName?: string\n /** Detected version, when known. Display only — never affects the mapping. */\n version?: string\n}\n\n/** One proposed slot plus why it was proposed. */\nexport interface ProposedSlotEntry {\n slot: NodeCapabilitySlot\n /** True when the provider had no table entry and took the conservative fallback. */\n unknownProvider: boolean\n /** True when the recipe behind this slot is flagged as an unvalidated estimate. */\n provisional: boolean\n rationale?: string\n}\n\n/** The full slot proposal for one node, including what a write would DESTROY. */\nexport interface SlotProposal {\n /** The draft slot list — a WHOLESALE replacement for the node's policy.slots. */\n proposedSlots: NodeCapabilitySlot[]\n /** Per-slot provenance, index-aligned with `proposedSlots`. */\n entries: ProposedSlotEntry[]\n /** Provider types detected but not present in the mapping table. */\n unknownProviders: string[]\n /** Provider types whose proposal rests on an unvalidated estimate. */\n provisionalProviders: string[]\n /**\n * Slots currently configured on the node that the proposal does NOT\n * reproduce — i.e. what applying this proposal would DELETE. Slot writes are\n * wholesale replacements, so an operator-hand-tuned slot absent from the\n * detection-derived draft is silently destroyed unless it is named here.\n */\n droppedSlots: NodeCapabilitySlot[]\n /** Provider types that appear in `droppedSlots` but in no proposed slot at all. */\n droppedProviders: string[]\n /** True when applying the proposal would remove at least one existing slot. */\n destructive: boolean\n}\n\n/** Stable key identifying a slot's identity for current-vs-proposed diffing. */\nfunction slotKey(slot: NodeCapabilitySlot): string {\n return [\n slot.provider,\n slot.model ?? '',\n slot.thinkingLevel ?? '',\n [...(slot.difficulty ?? [])].sort().join('|'),\n [...(slot.capability ?? [])].sort().join('|'),\n slot.maxParallel ?? '',\n ].join('\u0000')\n}\n\n/** Dedupe detected providers by type, preserving first-seen order. */\nfunction dedupeDetected(detected: readonly DetectedCliProvider[]): DetectedCliProvider[] {\n const seen = new Set<string>()\n const out: DetectedCliProvider[] = []\n for (const d of detected) {\n const type = typeof d?.type === 'string' ? d.type.trim() : ''\n if (!type || seen.has(type)) continue\n seen.add(type)\n out.push({ ...d, type })\n }\n return out\n}\n\n/**\n * Build a capability-slot proposal from a node's detected CLI providers.\n *\n * Pure and total: zero detections yields an empty proposal (never throws), which\n * the caller should treat as \"nothing to propose\" rather than \"replace the\n * node's slots with nothing\".\n *\n * `currentSlots` is optional but strongly recommended — it is the only way the\n * returned proposal can report what a wholesale write would destroy.\n */\nexport function buildSlotProposal(\n detected: readonly DetectedCliProvider[],\n currentSlots: readonly NodeCapabilitySlot[] = [],\n): SlotProposal {\n const providers = dedupeDetected(detected ?? [])\n const proposedSlots: NodeCapabilitySlot[] = []\n const entries: ProposedSlotEntry[] = []\n const unknownProviders: string[] = []\n const provisionalProviders: string[] = []\n\n for (const provider of providers) {\n const known = CLI_SLOT_RECIPES[provider.type]\n const recipes: readonly CliSlotRecipe[] = known ?? [UNKNOWN_CLI_SLOT_RECIPE]\n const isUnknown = !known\n if (isUnknown) unknownProviders.push(provider.type)\n\n let providerProvisional = false\n for (const recipe of recipes) {\n // Normalize through the SAME normalizer the daemon applies on write, so\n // a proposal can never preview a shape the write would reshape.\n const slot = normalizeNodeCapabilitySlot({\n provider: provider.type,\n model: recipe.model,\n thinkingLevel: recipe.thinkingLevel,\n difficulty: recipe.difficulty,\n maxParallel: recipe.maxParallel,\n })\n if (!slot) continue\n const provisional = recipe.provisional === true\n if (provisional) providerProvisional = true\n proposedSlots.push(slot)\n entries.push({\n slot,\n unknownProvider: isUnknown,\n provisional,\n ...(recipe.rationale ? { rationale: recipe.rationale } : {}),\n })\n }\n if (providerProvisional) provisionalProviders.push(provider.type)\n }\n\n // What a wholesale write would destroy: every currently-configured slot with\n // no exact counterpart in the draft.\n const proposedKeys = new Set(proposedSlots.map(slotKey))\n const droppedSlots = currentSlots.filter(slot => !proposedKeys.has(slotKey(slot)))\n const proposedProviders = new Set(proposedSlots.map(s => s.provider))\n const droppedProviders = [...new Set(\n droppedSlots.map(s => s.provider).filter(p => !proposedProviders.has(p)),\n )]\n\n return {\n proposedSlots,\n entries,\n unknownProviders,\n provisionalProviders,\n droppedSlots,\n droppedProviders,\n destructive: droppedSlots.length > 0,\n }\n}\n\n/**\n * Build a MAGI panel proposal from the same detections.\n *\n * ─── Deliberately narrow ──────────────────────────────────────────────────────\n *\n * MAGI's value is provider INDEPENDENCE: replicas from different providers\n * (ideally different machines) answering the same question, so agreement means\n * something. Detection tells us which providers exist — that is exactly enough\n * to propose one panel of distinct providers, and no more.\n *\n * What detection does NOT tell us is which provider suits which review KIND\n * (rca vs design vs claim_audit). Nothing in any manifest grades a provider for\n * root-cause analysis over design review, and inventing a per-kind assignment\n * would fabricate a rationale that does not exist. So this proposes ONE panel of\n * the detected providers and leaves the kind binding to the operator; the caller\n * decides which `task_kind` to bind it to via the existing dry-run tool.\n *\n * Ordering follows {@link CLI_SLOT_RECIPES} insertion order (recipe-known\n * providers first, in table order), so the panel leads with the providers whose\n * suitability is actually attested.\n */\nexport function buildMagiPanelProposal(\n detected: readonly DetectedCliProvider[],\n opts: { nodeId?: string; maxSlots?: number } = {},\n): MagiSlot[] {\n const providers = dedupeDetected(detected ?? [])\n const tableOrder = Object.keys(CLI_SLOT_RECIPES)\n const rank = (type: string): number => {\n const i = tableOrder.indexOf(type)\n return i === -1 ? Number.MAX_SAFE_INTEGER : i\n }\n const ordered = [...providers].sort((a, b) => rank(a.type) - rank(b.type))\n const limit = Number.isFinite(opts.maxSlots) && (opts.maxSlots as number) > 0\n ? Math.floor(opts.maxSlots as number)\n : ordered.length\n\n return ordered.slice(0, limit).map((p): MagiSlot => ({\n ...(opts.nodeId ? { nodeId: opts.nodeId } : {}),\n provider: p.type,\n // A model is intentionally NOT pinned: the panel's job is cross-provider\n // independence, and pinning models here would silently couple the panel\n // to this table's cost assumptions rather than to review quality.\n }))\n}\n","/**\n * Pure {{key}} template interpolation shared by the mesh command paths.\n *\n * Hoisted from packages/daemon-cloud/src/mesh/mesh-interpolation.ts — the\n * substitution is transport-agnostic (plain string/object work), so it belongs\n * in the dependency-free mesh-shared leaf where standalone can reuse it too.\n * The substitution semantics are byte-for-byte identical to the prior cloud copy.\n */\nexport function interpolateArgs(\n args: Record<string, unknown>,\n context: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(args)) {\n result[k] = typeof v === 'string' ? interpolateString(v, context) : v;\n }\n return result;\n}\n\nexport function interpolateString(template: string, ctx: Record<string, unknown>): string {\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = ctx[key];\n return val !== undefined ? String(val) : `{{${key}}}`;\n });\n}\n","/**\n * Canonical Repo Mesh coordinator tool-name registry — the single source of truth\n * the three surfaces must agree on:\n *\n * 1. mcp-server `ALL_MESH_TOOLS` (the published MCP tool schemas),\n * 2. daemon-core `coordinator-prompt.ts` `TOOLS_SECTION` (what the coordinator LLM\n * is told it can call), and\n * 3. the `NN tools` doc comments in the mesh-tools barrels.\n *\n * mcp-server does not depend on daemon-core's internal prompt, and daemon-core cannot\n * import mcp-server (dependency direction: daemon-core ← mcp-server). This dependency-\n * free leaf is the only place both can reference, so the 6-6 consistency test\n * (daemon-core coordinator-prompt.test.ts) and mcp-server both assert against THIS\n * list. Adding a new mesh tool means adding its name here first; the tests then force\n * the schema + prompt + barrel comment to catch up, which is exactly the regression\n * gate that let coordinator-prompt drift 14 tools behind the schema before.\n *\n * Order mirrors mcp-server `ALL_MESH_TOOLS` for easy visual diffing, but the consistency\n * checks are set-based (order-insensitive).\n */\nexport const CANONICAL_MESH_TOOL_NAMES = [\n 'mesh_status',\n 'mesh_route_preview',\n 'mesh_list_nodes',\n // GRAPH-ORCHESTRATION Phase F — batch before task, mirroring ALL_MESH_TOOLS.\n 'mesh_enqueue_batch',\n 'mesh_enqueue_task',\n 'mesh_view_queue',\n // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.\n 'mesh_graph_view',\n 'mesh_graph_gate_claim',\n 'mesh_graph_gate_release',\n 'mesh_graph_gate_abandon',\n 'mesh_queue_cancel',\n 'mesh_queue_requeue',\n 'mesh_send_task',\n 'mesh_read_chat',\n 'mesh_read_debug',\n 'mesh_read_terminal',\n 'mesh_send_keys',\n 'mesh_launch_session',\n 'mesh_git_status',\n 'mesh_read_node_logs',\n 'mesh_fast_forward_node',\n 'mesh_restart_daemon',\n 'mesh_checkpoint',\n 'mesh_approve',\n 'mesh_answer_question',\n 'mesh_list_pending_approvals',\n 'mesh_plan_onboarding',\n 'mesh_create',\n 'mesh_add_node',\n 'mesh_clone_node',\n 'mesh_remove_node',\n 'mesh_cleanup_worktree_nodes',\n 'mesh_refine_node',\n 'mesh_refine_batch',\n 'mesh_refine_config',\n 'mesh_change_impact_config',\n 'mesh_init',\n 'mesh_reinit',\n 'mesh_write_mesh_json_config',\n 'mesh_refine_plan',\n 'mesh_cleanup_sessions',\n 'mesh_prune_stale_direct',\n 'mesh_task_history',\n 'mesh_ledger_query',\n 'mesh_record_note',\n 'mesh_forget_note',\n 'mesh_reconcile_ledger',\n 'mesh_requeue_held_events',\n 'mesh_mission_upsert',\n 'mesh_mission_list',\n 'mesh_review_inbox',\n 'mesh_magi_review',\n 'mesh_magi_collect',\n 'mesh_magi_kind_panel_set',\n 'mesh_magi_kind_panel_list',\n 'mesh_node_slots_set',\n 'mesh_node_slots_list',\n 'mesh_node_slots_propose',\n 'mesh_coordinator_prompt_append_get',\n 'mesh_coordinator_prompt_append_set',\n] as const;\n\nexport type CanonicalMeshToolName = typeof CANONICAL_MESH_TOOL_NAMES[number];\n\n/** The count the `NN tools` barrel doc comments and consistency test assert against. */\nexport const CANONICAL_MESH_TOOL_COUNT = CANONICAL_MESH_TOOL_NAMES.length;\n","/**\n * OFFLINE-NODE-STATUS-REFRESH — the status-origin probe marker.\n *\n * A remote `git_status` (or any relayed/dispatched mesh command) that originates from\n * an explicit_refresh / mesh_status aggregate carries this marker inside its ARGS. The\n * daemon-cloud dispatch wrapper and MCP relay handler read it to grant the SHORT\n * connect-wait budget so a single offline (powered-off) peer no longer blocks the whole\n * status assembly for ~90s.\n *\n * It is deliberately an args marker rather than adding `git_status` to the global\n * probe-class command set: a user-driven / targeted `git_status` (no marker) must keep\n * waiting out the full connect deadline for a slow relay to open (rc.503 intent). Only a\n * status-origin probe opts into the short budget.\n *\n * The key is `_`-prefixed so it travels alongside the real args (workspace,\n * refreshUpstream, includeSubmodules, …) and is ignored by the git_status handler; the\n * dispatch/relay sites strip it defensively before the command executes so it never\n * reaches a handler that echoes unknown args.\n *\n * This dependency-free leaf is the single source of truth shared by daemon-core (the\n * aggregate probe producer), mcp-server (the MCP relay producer), and daemon-cloud (the\n * dispatch/relay consumer) so the key cannot drift between producer and consumer.\n */\nexport const STATUS_PROBE_ARG_KEY = '_statusProbe' as const;\n\n/** Stamp the status-origin probe marker onto a command's args (non-mutating). */\nexport function withStatusProbeMarker(\n args: Record<string, unknown> = {},\n): Record<string, unknown> {\n return { ...args, [STATUS_PROBE_ARG_KEY]: true };\n}\n\n/** True when the args carry the status-origin probe marker. */\nexport function argsCarryStatusProbeMarker(args: unknown): boolean {\n return (\n !!args &&\n typeof args === 'object' &&\n (args as Record<string, unknown>)[STATUS_PROBE_ARG_KEY] === true\n );\n}\n\n/**\n * Return a shallow copy of args with the internal marker removed so it never leaks to\n * the executing command handler. Returns the original reference when no marker is\n * present (avoids an allocation on the common path).\n */\nexport function stripStatusProbeMarker(\n args: Record<string, unknown>,\n): Record<string, unknown> {\n if (!argsCarryStatusProbeMarker(args)) return args;\n const { [STATUS_PROBE_ARG_KEY]: _drop, ...rest } = args;\n return rest;\n}\n","/**\n * Mesh RPC frame chunking — split/reassemble oversized mesh envelopes.\n *\n * WHY THIS EXISTS\n * The mesh DataChannel path (`daemon-mesh-manager`) writes every RPC envelope as a\n * SINGLE `dc.sendMessage(JSON.stringify(envelope))` frame. That was fine while every\n * mesh arg was small text, but a coordinator dispatching an IMAGE to a worker puts a\n * multi-MB base64 part inside `args` — far past what one DataChannel frame carries, so\n * the send throws or the frame is dropped and the dispatch silently dies.\n *\n * The dashboard P2P path already solved exactly this problem and has been running in\n * production: `packages/daemon-cloud/src/daemon-p2p/data-channel-router.ts` (send +\n * reassemble) and `packages/web-cloud/src/p2p.ts` (browser side). This module is a PORT\n * of that proven scheme onto the mesh envelope shape — deliberately NOT a new protocol.\n * The boundary values are carried over unchanged (see the constants below for why each\n * one is what it is).\n *\n * It lives in mesh-shared because it is pure string/JSON work with no transport, no\n * Node API and no DOM API — the same reason the normalizers live here. `daemon-cloud`\n * (sender/receiver) is the only current consumer, but keeping it in the pure leaf means\n * the standalone side can reassemble with the identical code rather than a hand-synced\n * copy, which is the drift bug class this package was created to kill.\n *\n * FAILURE POLICY (DoD): reassembly never degrades silently. A malformed, out-of-range,\n * over-budget or unparseable chunk stream yields an explicit typed failure that the\n * caller turns into an RPC error — never a partially-applied or truncated envelope.\n */\n\n/**\n * Inline ceiling: an envelope whose JSON is at or below this goes out as one frame,\n * exactly as before this module existed. 60_000 (not 65_536) matches the dashboard\n * path and leaves headroom under the common 64KiB SCTP message limit so the frame is\n * never the thing that trips a transport-level cap.\n */\nexport const MESH_MAX_INLINE_FRAME_BYTES = 60_000\n\n/**\n * Per-chunk payload slice, in CHARACTERS. Ported unchanged from the dashboard path.\n *\n * Why 16_000 chars is safe under a 60_000-BYTE ceiling, with ~3.75x of headroom:\n * `splitMeshFrame` slices the ALREADY-SERIALIZED outer frame, so the text being cut\n * is JSON output — control characters are pre-expanded to `\\u00XX` ASCII before they\n * ever reach the slicer. Re-escaping that slice inside the chunk envelope can at worst\n * double the backslashes. Measured worst cases for a 16_000-char slice:\n * backslashes / astral emoji → 32,073 bytes\n * Korean (3-byte, unescaped) → 48,073 bytes ← densest observed\n * plain ASCII → 16,073 bytes\n * All are under MESH_MAX_INLINE_FRAME_BYTES. `splitMeshFrame` still MEASURES each\n * envelope and shrinks on overflow, so the guarantee is enforced rather than assumed\n * if either constant is ever retuned — but at these values that path is unreachable,\n * which is exactly what `assertMeshChunkConstantsAreSafe` pins down.\n */\nexport const MESH_CHUNK_PAYLOAD_CHARS = 16_000\n\n/**\n * Hard cap on chunk count for one frame. 1024 × ~16KB ≈ 16MB of transferable payload,\n * which comfortably covers a screenshot while bounding what a single peer can make the\n * receiver buffer. Exceeding it is an explicit refusal, never a truncated send.\n */\nexport const MESH_MAX_CHUNKS = 1024\n\n/**\n * Reassembly budget for one frame, in bytes. Bounds receiver memory independently of\n * chunk count so a peer cannot send 1024 maximally-large chunks to force an oversized\n * allocation. Mirrors the dashboard receiver's MAX_REASSEMBLED_JSON_BYTES.\n */\nexport const MESH_MAX_REASSEMBLED_BYTES = 16_000_000\n\n/**\n * How long a partially-received frame is retained. A sender that dies mid-stream must\n * not pin receiver memory forever; the partial is swept and the frame simply never\n * completes (the RPC's own deadline then reports it).\n */\nexport const MESH_CHUNK_TTL_MS = 60_000\n\n/** Envelope `kind` for a chunk of a larger mesh frame. */\nexport const MESH_CHUNK_KIND = 'rpc_chunk'\n\nexport interface MeshChunkEnvelope {\n v: number\n kind: typeof MESH_CHUNK_KIND\n /** Groups the chunks of one logical frame. */\n chunkId: string\n /** 0-based position of this chunk. */\n index: number\n /** Total chunk count for this frame; constant across the group. */\n total: number\n /** The slice of the original envelope JSON. */\n data: string\n}\n\nexport type MeshChunkSplitResult =\n | { ok: true; chunks: MeshChunkEnvelope[] }\n | { ok: false; reason: 'too_many_chunks' | 'chunk_too_large'; detail: string }\n\nexport type MeshChunkAcceptResult =\n /** Chunk stored; the frame is not complete yet. */\n | { status: 'partial'; received: number; total: number }\n /** Final chunk landed and the frame parsed cleanly. */\n | { status: 'complete'; frame: unknown }\n /** Explicitly rejected — the caller must surface this, never ignore it. */\n | { status: 'failed'; reason: MeshChunkFailureReason; detail: string; chunkId: string }\n\nexport type MeshChunkFailureReason =\n | 'malformed_chunk'\n | 'too_many_chunks'\n | 'inconsistent_total'\n | 'duplicate_chunk_mismatch'\n | 'budget_exceeded'\n | 'reassembled_parse_failed'\n\n/** UTF-8 byte length without assuming Buffer or TextEncoder is present. */\nexport function meshUtf8ByteLength(value: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(value).byteLength\n let bytes = 0\n for (let i = 0; i < value.length; i += 1) {\n const code = value.charCodeAt(i)\n if (code < 0x80) bytes += 1\n else if (code < 0x800) bytes += 2\n else if (code >= 0xd800 && code <= 0xdbff) { bytes += 4; i += 1 }\n else bytes += 3\n }\n return bytes\n}\n\n/**\n * Worst-case size of one chunk envelope at the current constants.\n *\n * Exported so the safety margin documented on MESH_CHUNK_PAYLOAD_CHARS is a CHECKED\n * invariant rather than a comment that rots. The densest slice the splitter can produce\n * is 3-byte unescaped text (JSON.stringify leaves non-ASCII as-is), so that is what this\n * measures. If someone raises MESH_CHUNK_PAYLOAD_CHARS or lowers the frame ceiling past\n * the safe point, the accompanying test fails loudly instead of the overflow only\n * showing up as dropped frames on a live mesh.\n */\nexport function measureWorstCaseChunkEnvelopeBytes(): number {\n const densest = '한'.repeat(MESH_CHUNK_PAYLOAD_CHARS)\n return meshUtf8ByteLength(JSON.stringify(\n buildChunkEnvelope(Number.MAX_SAFE_INTEGER, 'x'.repeat(64), MESH_MAX_CHUNKS, MESH_MAX_CHUNKS, densest),\n ))\n}\n\n/** True when the serialized frame must be chunked rather than sent inline. */\nexport function meshFrameNeedsChunking(json: string): boolean {\n return meshUtf8ByteLength(json) > MESH_MAX_INLINE_FRAME_BYTES\n}\n\nfunction buildChunkEnvelope(\n version: number, chunkId: string, index: number, total: number, data: string,\n): MeshChunkEnvelope {\n return { v: version, kind: MESH_CHUNK_KIND, chunkId, index, total, data }\n}\n\n/**\n * Split a serialized mesh envelope into chunk envelopes.\n *\n * Each slice is measured AS ITS FINAL SERIALIZED ENVELOPE and shrunk (×0.8, as in the\n * dashboard implementation) until it fits the inline ceiling — so multi-byte UTF-8 and\n * the envelope overhead are both accounted for rather than assumed away. `total` is\n * stamped only after the full split is known, so every chunk in a group agrees.\n */\nexport function splitMeshFrame(json: string, chunkId: string, version: number): MeshChunkSplitResult {\n const slices: string[] = []\n let offset = 0\n while (offset < json.length) {\n let end = Math.min(json.length, offset + MESH_CHUNK_PAYLOAD_CHARS)\n // Measure against MESH_MAX_CHUNKS as the `total` placeholder: it is the widest the\n // field can serialize to, so a slice that fits here still fits once the real (never\n // larger) total is stamped in below.\n while (end > offset) {\n const candidate = json.slice(offset, end)\n const probe = JSON.stringify(buildChunkEnvelope(version, chunkId, slices.length, MESH_MAX_CHUNKS, candidate))\n if (meshUtf8ByteLength(probe) <= MESH_MAX_INLINE_FRAME_BYTES) break\n const shrunk = Math.max(1, Math.floor((end - offset) * 0.8))\n if (offset + shrunk >= end) { end -= 1; continue }\n end = offset + shrunk\n }\n if (end <= offset) {\n return { ok: false, reason: 'chunk_too_large', detail: 'a single character did not fit the chunk envelope budget' }\n }\n slices.push(json.slice(offset, end))\n if (slices.length > MESH_MAX_CHUNKS) {\n return {\n ok: false,\n reason: 'too_many_chunks',\n detail: `frame needs more than ${MESH_MAX_CHUNKS} chunks (${meshUtf8ByteLength(json)} bytes)`,\n }\n }\n offset = end\n }\n if (slices.length === 0) {\n return { ok: false, reason: 'chunk_too_large', detail: 'refusing to chunk an empty frame' }\n }\n const total = slices.length\n return { ok: true, chunks: slices.map((data, index) => buildChunkEnvelope(version, chunkId, index, total, data)) }\n}\n\ninterface MeshChunkBuffer {\n total: number\n chunks: string[]\n received: number\n bytesReceived: number\n createdAt: number\n}\n\n/**\n * Receiver-side reassembly buffer, one per peer connection.\n *\n * Keyed by chunkId only — callers construct one assembler per peer, so chunk groups\n * from different peers can never collide in the same map.\n */\nexport class MeshChunkAssembler {\n private readonly buffers = new Map<string, MeshChunkBuffer>()\n\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n /** True when the frame is a chunk envelope this assembler should handle. */\n static isChunkFrame(frame: unknown): boolean {\n return !!frame && typeof frame === 'object' && (frame as { kind?: unknown }).kind === MESH_CHUNK_KIND\n }\n\n /** Drop partials older than the TTL so a dead sender cannot pin memory. */\n private sweep(): void {\n const now = this.now()\n for (const [key, entry] of Array.from(this.buffers.entries())) {\n if (now - entry.createdAt > MESH_CHUNK_TTL_MS) this.buffers.delete(key)\n }\n }\n\n /** Discard any partial state for a peer (call on disconnect). */\n reset(): void {\n this.buffers.clear()\n }\n\n /** Number of frames currently mid-reassembly — for tests and diagnostics. */\n get pendingCount(): number {\n return this.buffers.size\n }\n\n /**\n * Accept one chunk envelope.\n *\n * Never throws and never returns a partially-applied frame: the result is exactly one\n * of partial / complete / failed, and `failed` carries a typed reason the transport\n * turns into an explicit RPC error.\n */\n accept(frame: unknown): MeshChunkAcceptResult {\n this.sweep()\n const raw = frame as Partial<MeshChunkEnvelope> | null\n const chunkId = typeof raw?.chunkId === 'string' ? raw.chunkId : ''\n const index = Number(raw?.index)\n const total = Number(raw?.total)\n const data = typeof raw?.data === 'string' ? raw.data : ''\n\n if (!chunkId || !Number.isInteger(index) || !Number.isInteger(total)\n || index < 0 || total <= 0 || index >= total || !data) {\n return {\n status: 'failed',\n reason: 'malformed_chunk',\n detail: `malformed chunk envelope (chunkId=${chunkId || '-'} index=${raw?.index} total=${raw?.total})`,\n chunkId,\n }\n }\n if (total > MESH_MAX_CHUNKS) {\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'too_many_chunks',\n detail: `chunk total ${total} exceeds the ${MESH_MAX_CHUNKS} cap`,\n chunkId,\n }\n }\n\n let entry = this.buffers.get(chunkId)\n if (!entry) {\n entry = { total, chunks: new Array(total).fill(''), received: 0, bytesReceived: 0, createdAt: this.now() }\n this.buffers.set(chunkId, entry)\n } else if (entry.total !== total) {\n // The sender disagrees with itself about the frame's shape — the stream is\n // corrupt; drop it loudly rather than reassembling a mixed frame.\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'inconsistent_total',\n detail: `chunk ${index} declares total ${total} but the group was opened with ${entry.total}`,\n chunkId,\n }\n }\n\n const existing = entry.chunks[index]\n if (existing) {\n // A benign retransmit repeats identical bytes. Different bytes for the same slot\n // means the ordering/identity guarantee is broken — refuse instead of picking one.\n if (existing === data) return { status: 'partial', received: entry.received, total: entry.total }\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'duplicate_chunk_mismatch',\n detail: `chunk ${index} arrived twice with different content`,\n chunkId,\n }\n }\n\n const chunkBytes = meshUtf8ByteLength(data)\n if (entry.bytesReceived + chunkBytes > MESH_MAX_REASSEMBLED_BYTES) {\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'budget_exceeded',\n detail: `reassembled frame would exceed ${MESH_MAX_REASSEMBLED_BYTES} bytes`,\n chunkId,\n }\n }\n\n entry.chunks[index] = data\n entry.received += 1\n entry.bytesReceived += chunkBytes\n if (entry.received < entry.total) {\n return { status: 'partial', received: entry.received, total: entry.total }\n }\n\n this.buffers.delete(chunkId)\n try {\n return { status: 'complete', frame: JSON.parse(entry.chunks.join('')) }\n } catch (error) {\n // Every slot is filled yet the join is not valid JSON — the frame is unusable.\n // Explicit failure, never a silent drop.\n return {\n status: 'failed',\n reason: 'reassembled_parse_failed',\n detail: `reassembled frame is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n chunkId,\n }\n }\n }\n}\n","/**\n * Semver precedence comparison — the single version-ordering primitive for the\n * daemon's upgrade paths.\n *\n * Why this exists: the upgrade path used to decide \"is this a no-op?\" with a\n * raw string equality check (`currentInstalled === latest`) and nothing else.\n * Equality answers \"is it the same version?\" but never \"which one is newer?\",\n * so any target that merely differed from the running build was installed —\n * including one that is OLDER. That is how a node running `1.0.49-rc.2`\n * silently got rolled back to `1.0.48`.\n *\n * String comparison cannot fix it either: `'1.0.49-rc.2' < '1.0.48'` is TRUE\n * lexicographically (the '4' in \"-rc.2\"'s prefix never gets that far — '9' vs\n * '8' at index 4 decides it, and even where it works `rc.10` sorts below\n * `rc.9`). Only field-wise numeric comparison with semver §11 prerelease rules\n * gives the right answer.\n *\n * This module is a PURE LEAF: zero imports, no I/O. It lives in mesh-shared\n * (fragmentation audit) so BOTH the OSS daemon and the proprietary Workers\n * server (`packages/server/src/utils/version-policy.ts`, previously a\n * byte-identical independent copy) can consume ONE implementation — the two\n * gate the same auto-update decision from opposite ends, and a drift between\n * them can produce an unbreakable update-nag loop (server says behind, daemon\n * gate refuses the \"upgrade\" as a rollback). daemon-core re-exports this from\n * its original `version-compare.ts` path, so existing imports keep working.\n *\n * NOTE ON PRERELEASE SEMANTICS: this is STRICT semver §11 — `1.0.49-rc.2` is\n * BELOW `1.0.49`, because a prerelease precedes its own release. That is the\n * correct rule for \"would installing this move me backwards?\", which is the\n * only question this module is used to answer. It deliberately differs from\n * `oss/packages/web-core/src/utils/version-update.ts`, whose\n * `isDaemonBehindTarget` treats an rc as up-to-date against its own base\n * release so the dashboard does not nag preview users with an update banner.\n * Those are two different questions; do not collapse them into one helper.\n */\n\n/** Parsed semver fields. Build metadata (`+…`) is discarded: it never affects precedence (§10). */\nexport interface ParsedSemver {\n readonly major: number;\n readonly minor: number;\n readonly patch: number;\n /** Dot-separated prerelease identifiers; empty for a release build. */\n readonly prerelease: readonly string[];\n}\n\nconst SEMVER_PATTERN = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\nconst NUMERIC_IDENTIFIER = /^\\d+$/;\n\n/**\n * Parse a semver string, tolerating a leading `v` (npm/CLI output carries it\n * inconsistently). Returns null for anything unparsable so callers can fail\n * closed rather than guess.\n */\nexport function parseSemver(version: unknown): ParsedSemver | null {\n if (typeof version !== 'string') return null;\n const match = version.trim().replace(/^v/, '').match(SEMVER_PATTERN);\n if (!match) return null;\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n prerelease: match[4] ? match[4].split('.') : [],\n };\n}\n\n/**\n * Semver §11 prerelease precedence: a release outranks its own prereleases;\n * numeric identifiers compare numerically (so rc.10 > rc.9) and rank below\n * alphanumeric ones; alphanumerics compare lexically; a shorter identifier\n * list ranks below an otherwise-equal longer one.\n */\nfunction comparePrerelease(a: readonly string[], b: readonly string[]): number {\n if (a.length === 0 && b.length === 0) return 0;\n // An empty prerelease list means \"release build\", which outranks any prerelease.\n if (a.length === 0) return 1;\n if (b.length === 0) return -1;\n for (let i = 0; i < Math.min(a.length, b.length); i += 1) {\n const idA = a[i];\n const idB = b[i];\n if (idA === idB) continue;\n const numA = NUMERIC_IDENTIFIER.test(idA);\n const numB = NUMERIC_IDENTIFIER.test(idB);\n if (numA && numB) return Number(idA) < Number(idB) ? -1 : 1;\n if (numA) return -1;\n if (numB) return 1;\n return idA < idB ? -1 : 1;\n }\n return a.length === b.length ? 0 : (a.length < b.length ? -1 : 1);\n}\n\n/**\n * Compare two versions by semver precedence.\n *\n * @returns -1 when `a` precedes `b`, 0 when equal, 1 when `a` succeeds `b`, and\n * **null when either side is unparsable** — callers MUST treat null as\n * \"direction unknown\" and fail closed rather than coercing it to a number\n * (`null` compares as `0` in JS numeric contexts, which would read as \"equal\").\n */\nexport function compareSemver(a: unknown, b: unknown): number | null {\n const pa = parseSemver(a);\n const pb = parseSemver(b);\n if (!pa || !pb) return null;\n if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1;\n if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1;\n if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1;\n return comparePrerelease(pa.prerelease, pb.prerelease);\n}\n\n/**\n * Would installing `target` move a daemon currently on `current` BACKWARDS?\n *\n * Returns false when the direction cannot be established (either version\n * unparsable) — an unknown direction must never block an upgrade, because the\n * cost of a false block (the whole fleet can no longer be upgraded) is far\n * higher than the cost of a missed downgrade guard. Equal versions are NOT a\n * downgrade, so a same-version reinstall stays allowed.\n */\nexport function isDowngrade(current: unknown, target: unknown): boolean {\n const direction = compareSemver(target, current);\n if (direction === null) return false;\n return direction < 0;\n}\n","/**\n * ws-protocol — shared string-literal unions for the daemon↔server WS surface\n * and the daemon↔dashboard P2P DataChannel surface.\n *\n * Fragmentation audit: these message types existed as a TypeScript union in\n * exactly ONE package (the proprietary daemon-cloud's server-connection.ts),\n * which is a leaf CONSUMER — the other two participants (the Workers server\n * and OSS daemon-core, which is the primary `status_report` producer) matched\n * bare string literals by hand. Renaming `auth_ok` server-side would compile\n * everywhere and leave the daemon reconnecting forever. Pure literals, zero\n * runtime deps — the textbook mesh-shared leaf.\n *\n * SCOPE HONESTY: this file declares the OSS-visible protocol surface. The\n * proprietary repo's server-connection.ts remains the authority for the full\n * ServerToDaemon command set; it should adopt these unions and extend them\n * (`ServerToDaemonMsg | <proprietary extras>`) rather than re-declaring the\n * shared members. Members here are the ones OSS daemon-core itself produces\n * or matches.\n */\n\n/** Messages the daemon sends UP to the Workers server over the WS bridge. */\nexport type DaemonToServerWsMsg =\n | 'auth'\n | 'status_report'\n | 'status_heartbeat'\n | 'status_event'\n | 'command_result'\n | 'error'\n | 'agent_event'\n | 'log';\n\n/** Server→daemon control messages the OSS engine reacts to. */\nexport type ServerToDaemonWsMsg =\n | 'auth_ok'\n | 'auth_error'\n | 'machine_evicted'\n | 'force_disconnect'\n | 'token_revoked'\n | 'version_mismatch'\n | 'force_update_required'\n | 'command'\n | 'agent_command'\n | 'resolve_action';\n\n/** P2P signaling relayed through the server WS. */\nexport type P2PSignalingWsMsg =\n | 'p2p_ready'\n | 'offer'\n | 'answer'\n | 'ice'\n | 'mesh_p2p_ready'\n | 'mesh_p2p_offer'\n | 'mesh_p2p_answer'\n | 'mesh_p2p_ice';\n\n/**\n * Dashboard↔daemon P2P DataChannel JSON message kinds. Previously matched as\n * hand-synced literals on both ends with NO shared symbol anywhere —\n * `p2p_evicted` had exactly two occurrences repo-wide (emit + handle).\n */\nexport type DashboardP2PMessageKind =\n | 'ping'\n | 'pong'\n | 'status_report'\n | 'status_event'\n | 'p2p_evicted'\n | 'command'\n | 'command_result'\n | 'command_result_chunk'\n | 'screenshot_start'\n | 'screenshot_stop'\n | 'pty_input'\n | 'pty_resize';\n\nexport const DAEMON_TO_SERVER_WS_MSGS: readonly DaemonToServerWsMsg[] = [\n 'auth', 'status_report', 'status_heartbeat', 'status_event', 'command_result', 'error', 'agent_event', 'log',\n];\n\nexport const SERVER_TO_DAEMON_WS_MSGS: readonly ServerToDaemonWsMsg[] = [\n 'auth_ok', 'auth_error', 'machine_evicted', 'force_disconnect', 'token_revoked',\n 'version_mismatch', 'force_update_required', 'command', 'agent_command', 'resolve_action',\n];\n\nexport function isDaemonToServerWsMsg(value: unknown): value is DaemonToServerWsMsg {\n return typeof value === 'string' && (DAEMON_TO_SERVER_WS_MSGS as readonly string[]).includes(value);\n}\n\nexport function isServerToDaemonWsMsg(value: unknown): value is ServerToDaemonWsMsg {\n return typeof value === 'string' && (SERVER_TO_DAEMON_WS_MSGS as readonly string[]).includes(value);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/json.ts","../src/git-normalize.ts","../src/session-normalize.ts","../src/node-normalize.ts","../src/node-facts.ts","../src/workspace-normalize.ts","../src/daemon-normalize.ts","../src/git-summarize.ts","../src/magi.ts","../src/brain-routing.ts","../src/slot-proposal.ts","../src/interpolation.ts","../src/mesh-tool-names.ts","../src/mesh-status-probe.ts","../src/rpc-chunking.ts","../src/semver-compare.ts","../src/ws-protocol.ts"],"sourcesContent":["/**\n * @adhdev/mesh-shared — pure, dependency-free mesh/git status normalizers shared\n * by daemon-core (standalone / local IPC) and web-core (cloud / P2P transit).\n *\n * This package exists to kill a recurring bug class: the cloud and standalone\n * transports each used to carry their own hand-synced copy of these normalizers,\n * and they drifted (cloud would strip/reshape fields, the web filter would drop\n * entries the standalone path kept). Both cores now import this single source of\n * truth. It MUST stay a pure leaf — types + pure functions on plain JS objects,\n * no Node/DOM APIs, no git exec, no transport, and an empty dependency set.\n */\n\nexport * from './json'\nexport * from './types'\nexport * from './git-normalize'\nexport * from './session-normalize'\nexport * from './node-normalize'\nexport * from './node-facts'\nexport * from './workspace-normalize'\nexport * from './daemon-normalize'\nexport * from './git-summarize'\nexport * from './magi'\nexport * from './brain-routing'\nexport * from './slot-proposal'\nexport * from './interpolation'\nexport * from './mesh-tool-names'\nexport * from './mesh-status-probe'\nexport * from './rpc-chunking'\nexport * from './semver-compare'\nexport * from './ws-protocol'\n","/**\n * Pure JSON-record reading primitives shared by the cloud (web-core / P2P transit)\n * and standalone (daemon-core / local IPC) mesh normalizers.\n *\n * These operate only on plain JS values — no Node/DOM APIs, no transport, no git\n * exec — so both cores can import them without violating the core↔core dependency\n * ban. They are the single source of truth for the field-coercion rules that the\n * two transports previously hand-synced (and drifted on).\n */\n\nexport type JsonRecord = Record<string, unknown>\n\nexport function readRecord(value: unknown): JsonRecord {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : {}\n}\n\nexport function readString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value !== 'string') continue\n const trimmed = value.trim()\n if (trimmed) return trimmed\n }\n return undefined\n}\n\nexport function readNumber(...values: unknown[]): number | undefined {\n for (const value of values) {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\nexport function readBoolean(...values: unknown[]): boolean | undefined {\n for (const value of values) {\n if (typeof value === 'boolean') return value\n }\n return undefined\n}\n\nexport function readStringArray(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)\n : []\n}\n\n/**\n * Coerce a value that is either an already-parsed plain object or a JSON string\n * into a plain object record. Arrays, primitives, parse failures and empty\n * strings all collapse to {} — callers treat the result as a best-effort record.\n *\n * This is the single source of truth for the `parseJsonRecord`/`parseJsonObject`\n * coercion that cloud mesh normalizers previously hand-redefined per file.\n */\nexport function parseJsonRecord(value: unknown): JsonRecord {\n if (!value) return {}\n if (typeof value === 'object') return readRecord(value)\n if (typeof value !== 'string') return {}\n const trimmed = value.trim()\n if (!trimmed) return {}\n try {\n return readRecord(JSON.parse(trimmed))\n } catch {\n return {}\n }\n}\n\n/**\n * Join a (possibly absent) repo root with a relative submodule path. Returns the\n * path unchanged when it is already absolute, and undefined when nothing usable\n * can be derived — callers must treat the result as optional.\n */\nexport function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {\n const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\\\/]+$/, '') : ''\n const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : ''\n if (!normalizedPath) return undefined\n if (/^(?:[A-Za-z]:[\\\\/]|\\/)/.test(normalizedPath)) return normalizedPath\n if (!normalizedRoot) return undefined\n return `${normalizedRoot}/${normalizedPath.replace(/^[\\\\/]+/, '')}`\n}\n","/**\n * Canonical git-status normalizers shared by the cloud (web-core) and standalone\n * (daemon-core router) mesh paths. Previously each transport hand-maintained its\n * own copy and they drifted (e.g. submodule drop rules, evidence checks); this is\n * the one implementation both import.\n */\n\nimport { joinRepoPath, readBoolean, readNumber, readRecord, readString, type JsonRecord } from './json'\nimport type { DaemonBuildBehind, GitRepoStatus, GitSubmoduleStatus, GitUpstreamFreshness } from './types'\n\nexport function scoreGitUpstreamFreshness(status: GitUpstreamFreshness | undefined): number {\n switch (status) {\n case 'fresh':\n return 30\n case 'no_upstream':\n return 4\n case 'unchecked':\n case undefined:\n return 0\n case 'stale':\n return -10\n case 'unavailable':\n return -15\n default:\n return 0\n }\n}\n\nexport function readGitSubmodules(value: unknown, parentRepoRoot?: string): GitSubmoduleStatus[] | undefined {\n if (!Array.isArray(value)) return undefined\n const submodules = value\n .map(entry => {\n const submodule = readRecord(entry)\n const path = readString(submodule.path)\n const commit = readString(submodule.commit)\n const repoPath = readString(submodule.repoPath, submodule.repo_root)\n ?? joinRepoPath(parentRepoRoot, path)\n // repoPath is only used for the submodule node's display workspace, which is\n // allowed to be empty. The cloud P2P transit path can deliver submodule entries\n // without repoPath (and a per-node git object without a derivable repoRoot), so\n // dropping on missing repoPath would silently strip every submodule graph node.\n // Keep any submodule that carries both path and commit.\n if (!path || !commit) return null\n const result: GitSubmoduleStatus = {\n path,\n commit,\n dirty: readBoolean(submodule.dirty) ?? false,\n outOfSync: readBoolean(submodule.outOfSync, submodule.out_of_sync) ?? false,\n lastCheckedAt: readNumber(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),\n }\n if (repoPath) result.repoPath = repoPath\n const error = readString(submodule.error)\n if (error) result.error = error\n return result\n })\n .filter((entry): entry is GitSubmoduleStatus => entry !== null)\n return submodules.length > 0 ? submodules : undefined\n}\n\nexport function hasGitStatusEvidence(status: JsonRecord): boolean {\n // BUG FIX: a transit-reshaped git status that carries only a repoRoot/workspace\n // (e.g. cloud P2P stripped the branch/upstream/counters but kept the path) must\n // NOT be dropped — otherwise the node loses its git object and any submodules\n // hanging off it. Treat a present repoRoot/repo_root/workspace as evidence too.\n return readBoolean(status.isGitRepo) !== undefined\n || Boolean(readString(status.branch, status.upstream, status.upstreamStatus, status.upstream_status, status.headCommit))\n || Boolean(readString(status.repoRoot, status.repo_root, status.workspace))\n || readNumber(\n status.ahead,\n status.behind,\n status.staged,\n status.modified,\n status.untracked,\n status.deleted,\n status.renamed,\n status.lastCheckedAt,\n status.last_checked_at,\n ) !== undefined\n || (Array.isArray(status.submodules) && status.submodules.length > 0)\n}\n\nexport function normalizeGitStatus(\n status: JsonRecord,\n node: JsonRecord,\n options?: { lastCheckedAt?: number },\n): GitRepoStatus | undefined {\n const explicitIsGitRepo = readBoolean(status.isGitRepo)\n if (!Object.keys(status).length || !hasGitStatusEvidence(status)) return undefined\n const isGitRepo = explicitIsGitRepo ?? true\n const conflictFiles = Array.isArray(status.conflictFiles)\n ? status.conflictFiles.filter((entry): entry is string => typeof entry === 'string')\n : []\n const conflictCount = readNumber(status.conflicts) ?? conflictFiles.length\n const hasConflicts = readBoolean(status.hasConflicts) ?? conflictCount > 0\n // node.workspace is in the fallback chain so a transit node carrying its path only\n // on the node (not the inner git object) still yields a parentRepoRoot for submodules.\n const repoRoot = readString(status.repoRoot, status.repo_root, node.repoRoot, node.repo_root, status.workspace, node.workspace) || undefined\n const submodules = readGitSubmodules(status.submodules, repoRoot)\n const upstreamStatus = readString(status.upstreamStatus, status.upstream_status)\n const upstreamFetchedAt = readNumber(status.upstreamFetchedAt, status.upstream_fetched_at)\n const upstreamFetchError = readString(status.upstreamFetchError, status.upstream_fetch_error)\n const error = readString(status.error)\n const staged = readNumber(status.staged) ?? 0\n const modified = readNumber(status.modified) ?? 0\n const untracked = readNumber(status.untracked) ?? 0\n const deleted = readNumber(status.deleted) ?? 0\n const renamed = readNumber(status.renamed) ?? 0\n return {\n workspace: readString(status.workspace, node.workspace) || '',\n repoRoot: repoRoot ?? null,\n isGitRepo,\n branch: readString(status.branch) ?? null,\n headCommit: readString(status.headCommit) ?? null,\n headMessage: readString(status.headMessage) ?? null,\n upstream: readString(status.upstream) ?? null,\n upstreamStatus: (upstreamStatus as GitUpstreamFreshness) ?? 'unchecked',\n ...(upstreamFetchedAt !== undefined ? { upstreamFetchedAt } : {}),\n ...(upstreamFetchError ? { upstreamFetchError } : {}),\n ahead: readNumber(status.ahead) ?? 0,\n behind: readNumber(status.behind) ?? 0,\n staged,\n modified,\n untracked,\n deleted,\n renamed,\n dirty: readBoolean(status.dirty, status.isDirty, status.is_dirty) ?? (staged + modified + untracked + deleted + renamed > 0 || hasConflicts),\n hasConflicts,\n conflictFiles,\n stashCount: readNumber(status.stashCount, status.stash_count) ?? 0,\n lastCheckedAt: options?.lastCheckedAt ?? readNumber(status.lastCheckedAt, status.last_checked_at) ?? Date.now(),\n ...(submodules ? { submodules } : {}),\n // Deploy-lag visibility: daemonBuildBehind is computed by the reporting\n // daemon's git probe (build commit vs workspace/submodule HEAD). It must\n // survive relay reassembly or downstream staleDaemonBuild surfaces\n // (dashboard Status tab badge, MCP staleDaemonBuilds warning) never fire\n // for remote nodes.\n ...(status.daemonBuildBehind && typeof status.daemonBuildBehind === 'object'\n ? { daemonBuildBehind: status.daemonBuildBehind as unknown as DaemonBuildBehind }\n : {}),\n ...(error ? { error } : {}),\n }\n}\n\nexport function scoreGitStatusCandidate(git: GitRepoStatus | undefined): number {\n if (!git) return Number.NEGATIVE_INFINITY\n let score = 0\n if (git.isGitRepo === true) score += 50\n if (git.isGitRepo === false) score -= 10\n if (git.branch) score += 20\n if (git.headCommit) score += 20\n if (git.upstream) score += 10\n score += scoreGitUpstreamFreshness(git.upstreamStatus)\n if (typeof git.ahead === 'number') score += 2\n if (typeof git.behind === 'number') score += 2\n if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length\n if (git.error) score -= 20\n return score\n}\n\n/**\n * Pick the best git status out of the four transit envelope slots a mesh node can\n * carry: lastGit.status, lastGit.result.status, lastProbe.git.status,\n * lastProbe.git.result.status. Returns undefined when none carry git evidence.\n */\nexport function pickBestTransitGitStatus(node: JsonRecord, options?: { lastCheckedAt?: number }): GitRepoStatus | undefined {\n const rawGit = readRecord(node.lastGit ?? node.last_git)\n const gitResult = readRecord(rawGit.result)\n const directStatus = readRecord(rawGit.status)\n const nestedStatus = readRecord(gitResult.status)\n const rawProbe = readRecord(node.lastProbe ?? node.last_probe)\n const probeGit = readRecord(rawProbe.git)\n const probeGitResult = readRecord(probeGit.result)\n const probeDirectStatus = readRecord(probeGit.status)\n const probeNestedStatus = readRecord(probeGitResult.status)\n const lastCheckedAt = options?.lastCheckedAt\n let best: { git: GitRepoStatus; score: number } | null = null\n for (const status of [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus]) {\n const normalized = normalizeGitStatus(status, node, { lastCheckedAt: lastCheckedAt ?? Date.now() })\n if (!normalized) continue\n const score = scoreGitStatusCandidate(normalized)\n if (!best || score > best.score) best = { git: normalized, score }\n }\n return best?.git\n}\n","/**\n * Canonical mesh-session record normalizer shared by the cloud (web-core) mesh\n * paths. Parses an already-transit-shaped session record into a typed\n * RepoMeshSessionStatus.\n */\n\nimport { readBoolean, readRecord, readString } from './json'\nimport type { RepoMeshSessionStatus } from './types'\n\n/**\n * Build a deterministic synthetic session id from a record that has no explicit\n * id. Two transit refreshes of the same logical session produce the same id so\n * downstream dedupe stays stable across refreshes (a random id would create a new\n * node every poll). Derived from the stable identifying fields available on the\n * record; prefixed \"synthetic:\" so callers can tell it apart from a real id.\n */\nfunction deriveSyntheticSessionId(record: ReturnType<typeof readRecord>): string | undefined {\n const parts = [\n readString(record.workspace),\n readString(record.providerType, record.provider),\n readString(record.role),\n readString(record.state, record.status),\n readString(record.title),\n readString(record.createdAt, record.created_at),\n readString(record.startedAt, record.started_at),\n ].filter((part): part is string => Boolean(part))\n if (parts.length === 0) return undefined\n return `synthetic:${parts.join('|')}`\n}\n\n/**\n * Session-id equivalence — the sessionId counterpart of daemonIdsEquivalent /\n * meshNodeIdMatches. A session id is SINGLE-FORM: one canonical UUID minted once\n * via crypto.randomUUID() in the provider instance and carried verbatim across\n * daemons, with no node/daemon-id style serialization variants. Equivalence is\n * therefore an exact match after trimming, never matching an absent/empty id\n * against another absent/empty id. Routing every mesh comparison site through\n * this one predicate keeps that single-form policy in one place (and gives any\n * future session-id aliasing a single seam) instead of scattering raw `===`.\n */\nexport function sessionIdsEquivalent(a: string | null | undefined, b: string | null | undefined): boolean {\n const idA = readString(a)\n const idB = readString(b)\n if (!idA || !idB) return false\n return idA === idB\n}\n\nexport function normalizeMeshSessionRecord(entry: unknown): RepoMeshSessionStatus | null {\n const record = readRecord(entry)\n // BUG FIX: cloud transit can reshape/strip the explicit id field. Fall back\n // through sessionId → session_id → id, then to a DETERMINISTIC synthetic id\n // derived from record content so the session survives the round trip instead\n // of being dropped (and so dedupe stays stable across refreshes). Return null\n // ONLY when the record carries no identifying fields at all.\n const sessionId = readString(record.sessionId, record.session_id, record.id)\n ?? deriveSyntheticSessionId(record)\n if (!sessionId) return null\n return {\n sessionId,\n ...(readString(record.providerType, record.provider) ? { providerType: readString(record.providerType, record.provider) } : {}),\n ...(readString(record.state, record.status) ? { state: readString(record.state, record.status) } : {}),\n ...(readString(record.chatStatus, record.chat_status) ? { chatStatus: readString(record.chatStatus, record.chat_status) } : {}),\n ...(readString(record.lifecycle) ? { lifecycle: readString(record.lifecycle) as RepoMeshSessionStatus['lifecycle'] } : {}),\n ...(readString(record.surfaceKind, record.surface_kind) ? { surfaceKind: readString(record.surfaceKind, record.surface_kind) as RepoMeshSessionStatus['surfaceKind'] } : {}),\n ...(readString(record.recoveryState, record.recovery_state) ? { recoveryState: readString(record.recoveryState, record.recovery_state) } : {}),\n ...(readString(record.workspace) ? { workspace: readString(record.workspace) } : {}),\n ...(readString(record.title) ? { title: readString(record.title) } : {}),\n ...(readString(record.role) ? { role: readString(record.role) } : {}),\n ...(readBoolean(record.isSelfCoordinator, record.is_self_coordinator) !== undefined ? { isSelfCoordinator: readBoolean(record.isSelfCoordinator, record.is_self_coordinator) } : {}),\n ...(readString(record.statusNote, record.status_note) ? { statusNote: readString(record.statusNote, record.status_note) } : {}),\n ...(readString(record.createdAt, record.created_at) ? { createdAt: readString(record.createdAt, record.created_at) } : {}),\n ...(readString(record.startedAt, record.started_at) ? { startedAt: readString(record.startedAt, record.started_at) } : {}),\n ...(readString(record.lastActivityAt, record.last_activity_at) ? { lastActivityAt: readString(record.lastActivityAt, record.last_activity_at) } : {}),\n ...(readBoolean(record.isCached, record.is_cached) !== undefined ? { isCached: readBoolean(record.isCached, record.is_cached) } : {}),\n }\n}\n","/**\n * Canonical mesh-node identity normalizer shared by daemon-core (standalone /\n * local IPC) and web-core (cloud / P2P transit) — the node-id counterpart of\n * session-normalize.ts.\n *\n * A mesh node record can carry its stable identifier under THREE different field\n * names depending on the serialization path it travelled, all with the same\n * value:\n * - `id` — config canonical form (mesh registry / persisted config)\n * - `nodeId` — runtime/wire camelCase form (inline-cache de-serialization,\n * mesh_status output via readStringValue(node.nodeId, node.id))\n * - `node_id` — SQLite DB column form leaked onto the object\n *\n * Comparing only `node.id` (or a 2-way `id ?? nodeId` that omits `node_id`)\n * against a target id silently drops nodes that arrived in another form — e.g. an\n * inline-cached worktree node, leaving a target-routed task permanently pending\n * with a misleading `no_node_satisfies_required_tags` skip. This module is the\n * single 3-way source of truth so every comparison site absorbs all three forms.\n */\n\nimport { readString } from './json'\nimport type { MeshNodeIdentified } from './types'\n\n/**\n * Read a mesh node's stable identifier, absorbing any of the three\n * serialization forms (`id` / `nodeId` / `node_id`). Returns undefined when the\n * record carries no usable id in any form.\n */\nexport function normalizeMeshNodeId(node: MeshNodeIdentified | null | undefined): string | undefined {\n const record = (node && typeof node === 'object' ? node : {}) as MeshNodeIdentified\n return readString(record.id, record.nodeId, record.node_id)\n}\n\n/**\n * Whether a mesh node record matches the given candidate id, comparing the\n * node's normalized id (any form) against the candidate. False when either side\n * is empty — never matches an absent id against an absent candidate.\n */\nexport function meshNodeIdMatches(\n node: MeshNodeIdentified | null | undefined,\n candidateId: string | null | undefined,\n): boolean {\n if (!candidateId) return false\n const trimmed = candidateId.trim()\n if (!trimmed) return false\n return normalizeMeshNodeId(node) === trimmed\n}\n","/**\n * MeshNodeFacts — the versioned per-machine RUNTIME facts bundle a reporting\n * daemon ships wholesale (design: root repo\n * docs/design/2026-07-25-deploy-lag-visibility.md §(a)).\n *\n * The mirror-defect class (a field reaching the dashboard for self nodes but\n * not remote ones, or vice versa) came from every fact being plumbed\n * field-by-field through six reassembly layers. The bundle rule that kills the\n * class: producers build it with ONE builder (daemon-core buildLocalNodeFacts —\n * used by BOTH the reporter envelope and the self-node stamp), and every relay\n * layer passes the object through OPAQUELY — never rebuild it field-by-field.\n * schemaVersion lets future fields ride through old relays untouched.\n *\n * Slots are deliberately NOT part of the bundle: they are coordinator-owned\n * config (REMOTE-NODE-SLOTS-COORDINATOR-LOCAL), not a reported runtime fact.\n */\n\nexport interface MeshNodeFactsDaemonBuild {\n /** Full build commit baked into the running daemon (40-hex). */\n commit?: string\n commitShort?: string\n version?: string\n builtAt?: string\n}\n\n/**\n * One rolling quota window, structurally identical to daemon-core's\n * `QuotaWindow`. Redeclared here rather than imported because this package is a\n * dependency-free leaf (see the header of index.ts) and must never import\n * daemon-core — the dependency arrow points the other way. daemon-core's\n * richer type stays assignable to this one, so the producer passes its snapshot\n * straight through with no mapping layer to drift.\n */\nexport interface MeshNodeFactsQuotaWindow {\n usedPercent: number\n windowMinutes: number\n resetsAt: number | null\n}\n\n/**\n * A provider's quota snapshot as reported by the node that owns the credentials.\n *\n * `status` is the field that matters to a reader: 'ok' means the windows are\n * usable, anything else means they are not. A node that CANNOT read a quota\n * still reports an entry (status 'unavailable'/'error' + metadata.failureKind)\n * rather than omitting the provider — an absent entry means \"this node never\n * told us\", a present failing entry means \"this node looked and could not\n * tell\", and a reader that cannot distinguish those two cannot diagnose\n * anything. Extra provider-specific fields (buckets, monthly) ride through via\n * the index signature.\n */\nexport interface MeshNodeFactsProviderQuota {\n provider: string\n status: string\n session: MeshNodeFactsQuotaWindow | null\n weekly: MeshNodeFactsQuotaWindow | null\n /** 30-day billing-style window, only for providers that report one\n * (cursor-cli). Promoted from the index signature 2026-08-24 so readers\n * can render it typed — an 'ok' cursor snapshot often carries ONLY this\n * axis, and a reader that looks at session/weekly alone misreads a\n * healthy reading as a failure. */\n monthly?: MeshNodeFactsQuotaWindow | null\n /** Per-pool quota buckets, only for providers whose plan has more than one\n * pool (antigravity-cli: Gemini vs Claude/GPT groups, each with a 5h and\n * a weekly bucket). `session`/`weekly` above collapse these to the WORST\n * bucket per window (the routing-safe headline); the buckets carry the\n * per-pool detail a reader should surface. Promoted from the index\n * signature 2026-08-24, same reasoning as `monthly`. */\n buckets?: Array<{\n name?: string\n usedPercent?: number\n windowMinutes?: number\n resetsAt?: number | null\n [extra: string]: unknown\n }> | null\n /** Unix ms of the snapshot itself — older than the bundle's reportedAt. */\n updatedAt: number\n error: string | null\n /**\n * `accountEmail` is PII and rides this bundle because the bundle is\n * P2P/local only — it must never be added to a server-bound payload. See\n * daemon-core `QuotaMetadata.accountEmail` and the regression suite that\n * pins its absence from every server allow-list.\n */\n metadata?: {\n failureKind?: string\n source?: string\n planType?: string | null\n accountEmail?: string | null\n /**\n * True when `session`/`weekly` are NOT this snapshot's own reading but a\n * retained last-good reading carried forward by daemon-core's\n * `carryForwardLastGoodWindows` (quota/refresh.ts) after a TRANSIENT\n * fetch failure (expired token, network blip, rate limit). `status` on\n * this same entry is the fresh failure, not 'ok' — the numbers are real,\n * just not from THIS tick. A reader should label them (e.g. \"· refreshing\")\n * rather than presenting them as a freshly measured value.\n */\n lastGoodWindows?: boolean\n /**\n * Unix ms when the reporting node last ATTEMPTED a refresh of this\n * provider — deliberately distinct from `updatedAt`, which dates the\n * DATA. They differ for file-source providers (claude-cli reports its\n * statusline snapshot's capture time, codex-cli the rollout entry's),\n * whose `updatedAt` does not move while the source file is unchanged\n * however often it is successfully re-read.\n *\n * A reader judging FRESHNESS wants `updatedAt`. This field answers the\n * different question \"is that node still looking?\", which is what makes\n * \"3h old but re-checked a minute ago\" distinguishable from \"3h old and\n * nobody has looked since\". Absent on entries written by daemons\n * predating the field.\n */\n fetchedAt?: number\n [extra: string]: unknown\n }\n [extra: string]: unknown\n}\n\n/**\n * One provider's enablement state on the reporting machine — see\n * `MeshNodeFacts.providerEnablement`.\n *\n * Both fields are REQUIRED booleans on the wire even though the underlying\n * config defaults are asymmetric (`enabled` defaults false, `quotaEnabled`\n * defaults true). The producer resolves those defaults before stamping, so a\n * reader never re-derives them — a second copy of the default rules is exactly\n * the drift this shape avoids. Absence is expressed by omitting the whole\n * provider entry (or the whole bundle field), never by a missing sub-field.\n */\nexport interface MeshNodeFactsProviderEnablement {\n /** \"This machine uses provider X\" — gates launching and mesh claims. */\n enabled: boolean\n /** \"...and its quota is probed here\" — an independent user opt-out. */\n quotaEnabled: boolean\n}\n\nexport interface MeshNodeFacts {\n schemaVersion: number\n reportedAt: number\n daemonBuild?: MeshNodeFactsDaemonBuild\n providerVersions?: Record<string, string>\n /**\n * Verified-channel PIN per provider type: which provider MANIFEST the node\n * actually loads. NOT the same as `providerVersions`, which is the CLI\n * BINARY version — a node can run kimi-code 1.2.3 while pinned to kimi\n * spec 1.0.0. Keep them separate; folding them would repeat the\n * multi-identifier confusion behind the canon-identity defect class.\n *\n * This is what makes a remote node's pin knowable. Provider fixes do not\n * propagate on their own (the pin advances only on an explicit\n * activation, by design), so without this field a node that never adopted\n * a published fix looks exactly like one that did.\n *\n * A missing entry means \"no pin\", never a fabricated value.\n */\n providerSpecPins?: Record<string, string>\n platform?: string\n arch?: string\n machineNickname?: string\n /**\n * Per-provider quota snapshots, keyed by QuotaProvider id ('claude-cli',\n * 'codex-cli', 'cursor-cli', 'kimi'). Consumed by ROUTING as well as observation: the\n * coordinator's quota gate / spread bonus (daemon-core mesh-quota-routing.ts,\n * thresholds in RepoMeshPolicy.quotaRouting) reads exactly this shape, so\n * field renames here are a routing-contract change, not a cosmetic one.\n * Both consumers fail open on missing/stale data.\n *\n * Freshness is `Date.now() - reportedAt` (the bundle stamp) plus each\n * entry's own `updatedAt`; there is deliberately NO ttl/expiry field here,\n * because refresh cadence is owned by the reporting node and the delivery\n * cadence by whoever calls git_status. Neither end is in a position to\n * assert a TTL, so readers judge age themselves (the routing consumer's\n * rule: quotaSnapshotAgeMs in daemon-core mesh-quota-routing.ts).\n */\n quota?: Record<string, MeshNodeFactsProviderQuota>\n /**\n * Per-provider ENABLEMENT state on the reporting machine, keyed the same\n * way as `quota`. Exists because `quota` alone cannot answer \"why is there\n * no snapshot\": a provider that is disabled — on either axis — is pruned\n * from the quota cache entirely (daemon-core quota/refresh.ts drops it and\n * refuses to restore it from disk), so a deliberate opt-out and a\n * never-yet-measured provider both arrive as the SAME absent entry. On the\n * node that owns the config that ambiguity is resolvable by reading the\n * config; for every OTHER node in the mesh it was not resolvable at all,\n * which is what this field fixes.\n *\n * The two axes mirror ProviderLoader exactly and are INDEPENDENT:\n * `enabled` is \"this machine uses provider X\" (gates launching and mesh\n * claims), `quotaEnabled` gates ONLY the quota probe — a machine can use a\n * provider and still opt out of having its usage read.\n *\n * ★An ABSENT bundle field means \"this node did not tell us\" — a daemon too\n * old to send it — and must NEVER be read as \"disabled\". The consumer\n * (daemon-core mesh-quota-routing.ts classifyAbsentQuotaReason) keeps its\n * unclassified fallback for exactly that case; treating absence as\n * disabled would invent a fail-closed verdict out of a missing field.\n *\n * Booleans keyed by provider type only — no free text, no credentials.\n */\n providerEnablement?: Record<string, MeshNodeFactsProviderEnablement>\n /** Future fields ride through opaquely — do not enumerate them in relays. */\n [extra: string]: unknown\n}\n\n/**\n * Validate the minimal envelope shape and pass EVERYTHING else through\n * untouched. Returns undefined for anything that is not a v1+ bundle so\n * callers skip the stamp instead of shipping garbage.\n */\nexport function normalizeMeshNodeFacts(raw: unknown): MeshNodeFacts | undefined {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined\n const record = raw as Record<string, unknown>\n const schemaVersion = Number(record.schemaVersion)\n const reportedAt = Number(record.reportedAt)\n if (!Number.isFinite(schemaVersion) || schemaVersion < 1) return undefined\n if (!Number.isFinite(reportedAt) || reportedAt <= 0) return undefined\n return { ...record, schemaVersion, reportedAt } as MeshNodeFacts\n}\n\n/**\n * Provider types with a SHIPPED quota fetcher — the providers whose quota can\n * actually be read on a node today.\n *\n * This must mirror `REFRESHERS` in daemon-core's `quota/refresh.ts`, which is\n * the runtime authority: a provider absent from REFRESHERS is never probed, so\n * offering it a quota switch anywhere would promise a control that does\n * nothing. It lives here, in the dependency-free leaf, for the same reason\n * `formatQuotaAccount` does — the machine page and the new-install surfaces are\n * in web-core, the fetchers and the `adhdev setup` wizard reach it from\n * daemon-core, and the dependency arrow only runs one way. This list previously\n * existed as a hand-copied literal in web-core's ProvidersTab; that copy is now\n * derived from this one.\n *\n * Deliberately NOT necessarily the same set as the `QuotaProvider` union in\n * daemon-core's quota/types.ts: that union is the set of valid keys a snapshot\n * can be carried under, while membership HERE means \"a fetcher exists\".\n *\n * Known non-members and why, so this is not re-litigated per surface:\n * - cursor-cli — permanently impossible; no personal usage API exists\n * - hermes-cli — no model-axis quota to report\n *\n * ★grok-cli WAS listed here as impossible and is not: that verdict came from\n * probing `api.x.ai` / `management-api.x.ai` (the team-API billing axis, which\n * genuinely rejects a CLI OAuth token) and from reading `grok --help`, where\n * the `/usage` view does not appear because it is a TUI slash command. The\n * subscription quota is served by the CLI's own chat proxy — see the endpoint\n * provenance note in daemon-core `quota/fetchers/grok.ts`.\n *\n * ★antigravity-cli was likewise twice judged impossible, from reading\n * `agy --help` where the usage view does not appear because it is a TUI view.\n * Its quota comes from the SHARED Gemini Code Assist backend\n * (`daily-cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary` —\n * the host `agy` itself uses; the unprefixed `cloudcode-pa` host 429s Google\n * AI Pro Antigravity accounts), and its credential lives in the OS keyring,\n * not the stale on-disk token file — see the provenance note in daemon-core\n * `quota/fetchers/antigravity.ts`. Supported on macOS and Windows; other\n * platforms report `unsupported` rather than guess at a keyring backend\n * nobody has verified.\n *\n * ★Adding a provider here without adding its fetcher to REFRESHERS re-creates\n * exactly the \"switch that does nothing\" this constant prevents.\n */\nexport const QUOTA_SUPPORTED_PROVIDERS: readonly string[] = [\n 'antigravity-cli',\n 'claude-cli',\n 'codex-cli',\n 'cursor-cli',\n 'grok-cli',\n 'kimi',\n 'opencode',\n]\n\n/** Whether this provider's quota can be probed at all — see QUOTA_SUPPORTED_PROVIDERS. */\nexport function supportsQuota(providerType: string | undefined | null): boolean {\n return !!providerType && QUOTA_SUPPORTED_PROVIDERS.includes(providerType)\n}\n\n/**\n * The account/plan label for a provider quota row: \"you@example.com · Plus\".\n *\n * Lives HERE, in the dependency-free leaf, because both renderers need it and\n * they cannot share code any other way: the dashboards are in web-core and the\n * `adhdev quota` CLI is in daemon-core, and the dependency arrow runs\n * web-core → daemon-core, never back. Duplicating the formatter in the CLI is\n * what produced the drift this function exists to prevent — the CLI showed no\n * account at all while the UI showed one.\n *\n * Both halves are optional and independent: codex reports both, kimi reports\n * neither, and Claude Code exposes no account at all. Returns null when there\n * is nothing to say, so a provider without an account renders no empty slot and\n * no \"unknown\" placeholder — the absence is simply invisible, in every surface.\n *\n * ★The email is PII travelling on a P2P-only path. Rendering it locally is\n * fine; it must never be forwarded to a server payload or a push body. See\n * daemon-core QuotaMetadata.accountEmail and the server-boundary suite.\n */\nexport function formatQuotaAccount(quota: MeshNodeFactsProviderQuota | undefined): string | null {\n const meta = quota?.metadata\n const email = typeof meta?.accountEmail === 'string' ? meta.accountEmail.trim() : ''\n const plan = typeof meta?.planType === 'string' ? meta.planType.trim() : ''\n const parts = [email, plan].filter(Boolean)\n return parts.length > 0 ? parts.join(' · ') : null\n}\n","/**\n * Canonical workspace-path normalizer for mesh node/session scope comparison,\n * shared by daemon-core (standalone / local IPC) and any other core that has to\n * tell a base node apart from a co-located worktree clone whose ONLY structural\n * difference is its distinct workspace root.\n *\n * One physical daemon can host a base node plus several worktree nodes. Session\n * records, queue claims, and read_chat requests are scoped to a node by matching\n * the session's actual workspace against the node's declared workspace. Those two\n * paths can arrive in different but equivalent spellings (back/forward slashes,\n * trailing separators, Windows case-insensitivity), so a raw string compare would\n * either falsely separate equal paths or fail to engage at all.\n *\n * This folds separator style, trailing slashes, and case into a single comparable\n * form. It was previously a module-private copy in daemon-core's\n * mesh-events-coordinator.ts (WTCLAIM fix-B); promoting it here keeps the one\n * comparison rule identical across the enqueue→claim path, the mesh_status\n * per-node session filter, and the read_chat node scope guard. Pure string ops —\n * no Node/DOM APIs — so it stays a valid mesh-shared leaf.\n */\nexport function normalizeMeshWorkspaceForCompare(dir?: string | null): string {\n if (typeof dir !== 'string') return ''\n return dir.trim().replace(/[\\\\/]+/g, '/').replace(/\\/+$/, '').toLowerCase()\n}\n\n/**\n * Whether two workspace paths refer to the same workspace root after\n * normalization. Returns false when either side is empty — an unknown workspace\n * never \"matches\" another, so callers must decide separately whether an unknown\n * workspace should be treated permissively (the WTCLAIM convention: unknown →\n * do not block).\n */\nexport function meshWorkspacesEquivalent(a?: string | null, b?: string | null): boolean {\n const left = normalizeMeshWorkspaceForCompare(a)\n const right = normalizeMeshWorkspaceForCompare(b)\n if (!left || !right) return false\n return left === right\n}\n","/**\n * Canonical coordinator/daemon-id form normalizer shared by daemon-core (the mesh\n * reconcile loop, pending-event queue, and MCP surface) — the daemon-id counterpart\n * of node-normalize.ts.\n *\n * A daemon answers to the SAME machine under three interchangeable id forms, all\n * derived from one `mach_<hex>` machine id:\n * - bare — `mach_<hex>` (loadConfig().machineId; stamped by the local\n * queue-assignment dispatch path)\n * - cloud — `daemon_mach_<hex>` (the coordinator mesh node's config-form\n * daemonId, which the MCP layer's resolveCoordinatorDaemonId\n * prefers and stamps onto a worker's meshCoordinatorDaemonId)\n * - standalone — `standalone_mach_<hex>` (a standalone daemon's status instanceId)\n *\n * A worker's completion event is scoped (`coordinator_daemon_id`) with whichever\n * form the dispatch path happened to stamp, but the coordinator that later drains /\n * surfaces that event resolves its OWN id through a different path and frequently\n * holds a DIFFERENT form. Because the scope filter is an exact-string match\n * (`coordinator_daemon_id IS NULL OR IN (...)` in SQL, `.includes()` in JS), a\n * completion stamped `daemon_mach_X` is silently skipped by a coordinator whose\n * self-id set only contains bare `mach_X` (or standalone form) — the event never\n * surfaces and the coordinator is never auto-notified, while NULL-scoped events\n * (e.g. worktree bootstrap) always pass via the `IS NULL` branch.\n *\n * This module is the single source of truth that collapses the three forms to one\n * machine core and EXPANDS a self-id set to every equivalent form, so a scope match\n * succeeds regardless of which form stamped the event. Expansion stays WITHIN a\n * single machine core (`daemon_mach_X` only ever expands to other `mach_X` forms),\n * so an event scoped to a DIFFERENT coordinator is never falsely claimed.\n */\n\nimport { readString } from './json'\n\nconst DAEMON_ID_PREFIXES = ['daemon_', 'standalone_'] as const\n\n/**\n * A daemon DO addressed by a raw 64-hex DO id (`idFromString`) rather than by a\n * canonical name (`idFromName(\"daemon_mach_<hex>\")`). Decide by FORMAT, not by\n * length — the canonical name is 43+ chars, so a `length > 32` heuristic would\n * misclassify it. Mirrors the server's session-routing `isRawDoId`.\n */\nexport function isRawDaemonDoId(id: string | null | undefined): boolean {\n const trimmed = readString(id)\n return !!trimmed && /^[0-9a-f]{64}$/i.test(trimmed)\n}\n\n/** The machine-evidence fields a real daemon reports about its hardware. */\nexport interface DaemonMachineEvidence {\n machineNickname?: string | null\n nickname?: string | null\n hostname?: string | null\n platform?: string | null\n machineId?: string | null\n machine?: { hostname?: string | null; platform?: string | null } | null\n sessions?: unknown[] | null\n}\n\n/**\n * A phantom daemon entry — a UI surface must not offer it as a real machine.\n *\n * Symptom block for the raw-DO-id ghost (GHOST-MACHINE-REGISTRATIONS). When the\n * `X-ADHDEV-Daemon` instanceId header is missing or unparseable, the /ws handler\n * falls back to a random DO name, so every reconnect mints a FRESH raw 64-hex DO\n * id. Those keys accumulate as hardware-less entries that render as a bare hash\n * where a machine name belongs.\n *\n * BOTH conditions are required, and the second is what keeps this safe:\n * 1. the id is a raw DO id — no `daemon_`/`standalone_` canonical prefix.\n * 2. no machine evidence at all — no nickname, hostname, platform, registered\n * machineId, and no sessions.\n *\n * A legacy / not-yet-reauthed daemon that ACTUALLY reports itself fails (2) and\n * therefore still renders, still attaches, and still routes — dropping on (1)\n * alone would make real daemons disappear from the picker. This is strictly a\n * PRESENTATION filter: routing tables (server `lastStatuses`, P2P relay) keep\n * the entry, because the raw key still resolves via `idFromString`.\n *\n * The server applies the same rule to its dashboard payloads via\n * `_unresolvedIdentity` (UserSession.isPhantomMachineEntry). That marker is\n * server-internal and never reaches the browser, and the client daemon store\n * never evicts an entry once injected — so a client surface reading the merged\n * P2P/WS store must re-derive the verdict from the entry SHAPE. This function is\n * that shared rule.\n */\nexport function isPhantomDaemonEntry(\n entry: (DaemonMachineEvidence & { id?: string | null }) | null | undefined,\n): boolean {\n if (!entry) return false\n if (!isRawDaemonDoId(entry.id)) return false\n const hasMachineEvidence = Boolean(\n readString(entry.machineNickname)\n || readString(entry.nickname)\n || readString(entry.hostname)\n || readString(entry.platform)\n || readString(entry.machine?.hostname)\n || readString(entry.machine?.platform)\n || readString(entry.machineId)\n || (Array.isArray(entry.sessions) && entry.sessions.length > 0),\n )\n return !hasMachineEvidence\n}\n\n/**\n * Reduce any daemon-id form to its machine core: strips a leading `daemon_` /\n * `standalone_` prefix, leaving the bare `mach_<hex>` (or returning a non-prefixed\n * id unchanged). Returns undefined for an empty/absent id.\n */\nexport function machineCoreFromDaemonId(id: string | null | undefined): string | undefined {\n const trimmed = readString(id)\n if (!trimmed) return undefined\n for (const prefix of DAEMON_ID_PREFIXES) {\n if (trimmed.startsWith(prefix)) {\n const core = trimmed.slice(prefix.length).trim()\n return core || undefined\n }\n }\n return trimmed\n}\n\n/**\n * Canonicalize any daemon-id form to the single CANON producer form\n * `daemon_mach_<core>` (the cloud `daemon_` form).\n *\n * CANON-IDENTITY double-dispatch root cause: the coordinator daemon id is stamped\n * onto a worker dispatch by TWO independent producers — the MCP-side\n * resolveCoordinatorDaemonId (which prefers the coordinator mesh node's config-form\n * `daemon_mach_X` daemonId) and the daemon-core queue dispatch (which stamps the\n * bare `loadConfig().machineId` = `mach_X`). When the SAME coordinator dispatches\n * the same task down both paths, the two worker sessions are stamped with two\n * DIFFERENT coordinator-id forms; a raw-string dedup that should recognise \"this\n * task is already dispatched by me\" fails, and the task runs twice.\n *\n * The durable fix is comparator-side (daemonIdsEquivalent / expandDaemonIdForms),\n * but unifying every PRODUCER on one canonical form shrinks the surface so even a\n * raw `===` agrees. The canon is the cloud `daemon_` form because that is what the\n * coordinator mesh node's config-form daemonId already carries and what the cloud\n * P2P signaling layer registers a daemon under — so canonicalizing the bare/standalone\n * fallback forms makes them consistent with the already-working primary path.\n *\n * Only a `mach_<…>` core is rewritten; an arbitrary/non-machine id (e.g. a custom\n * node id) is returned unchanged so it is never ballooned into a spurious `daemon_`\n * form. Idempotent. Returns undefined for an empty/absent id.\n */\nexport function canonicalDaemonId(id: string | null | undefined): string | undefined {\n const core = machineCoreFromDaemonId(id)\n if (!core) return undefined\n if (!core.startsWith('mach_')) return core\n return `daemon_${core}`\n}\n\n/** True when both ids resolve to the same machine core — i.e. they are the same\n * daemon under different id forms. False when either side is empty. */\nexport function daemonIdsEquivalent(a: string | null | undefined, b: string | null | undefined): boolean {\n const coreA = machineCoreFromDaemonId(a)\n const coreB = machineCoreFromDaemonId(b)\n if (!coreA || !coreB) return false\n return coreA === coreB\n}\n\n/**\n * Expand a set of coordinator/daemon ids to every equivalent form, so an\n * exact-string scope filter (SQL `IN (...)` or JS `.includes()`) matches a\n * completion stamped in any form of the same machine. The original ids are kept in\n * their input order and FIRST (callers that treat `[0]` as the primary — e.g.\n * per-daemon JSONL file naming — keep their original primary); the derived\n * `mach_<hex>` / `daemon_mach_<hex>` / `standalone_mach_<hex>` forms are appended.\n *\n * Derived prefixed forms are emitted ONLY for a core that looks like a real machine\n * id (`mach_<…>`), so arbitrary/test ids (e.g. `node-daemon-id`) are passed through\n * untouched and never balloon into spurious forms. Result is de-duplicated.\n */\nexport function expandDaemonIdForms(\n ids: string | null | undefined | ReadonlyArray<string | null | undefined>,\n): string[] {\n const list = Array.isArray(ids) ? ids : ids != null ? [ids] : []\n const out: string[] = []\n const seen = new Set<string>()\n const add = (value: string | undefined): void => {\n if (!value || seen.has(value)) return\n seen.add(value)\n out.push(value)\n }\n // Pass 1: originals first, in input order (preserve caller's primary at [0]).\n for (const raw of list) add(readString(raw))\n // Pass 2: derived machine-core forms for any id that resolves to a `mach_` core.\n for (const raw of list) {\n const core = machineCoreFromDaemonId(readString(raw))\n if (!core || !core.startsWith('mach_')) continue\n add(core)\n for (const prefix of DAEMON_ID_PREFIXES) add(`${prefix}${core}`)\n }\n return out\n}\n","/**\n * Canonical compact git-shape summarizer used by debug/log surfaces on both the\n * cloud (daemon-cloud mesh command summarizer) and standalone (daemon-core router\n * RepoMeshStatusDebug) paths. Produces a small, log-safe projection of a git\n * status record — commit SHAs are truncated to 12 chars.\n *\n * Transport-specific envelope unwrapping (result / result.status / top-level)\n * stays in the caller; this takes an already-unwrapped status record.\n */\n\nimport { readBoolean, readNumber, readRecord, readString } from './json'\n\nexport function summarizeGitShape(status: unknown): Record<string, unknown> | null {\n const record = readRecord(status)\n if (!Object.keys(record).length) return null\n const submodules = Array.isArray(record.submodules)\n ? record.submodules.map((entry: unknown) => {\n const sub = readRecord(entry)\n return {\n path: readString(sub.path) ?? null,\n commit: readString(sub.commit)?.slice(0, 12) ?? null,\n dirty: readBoolean(sub.dirty) ?? false,\n outOfSync: readBoolean(sub.outOfSync, sub.out_of_sync) ?? false,\n }\n })\n : []\n return {\n isGitRepo: readBoolean(record.isGitRepo),\n workspace: readString(record.workspace) ?? null,\n repoRoot: readString(record.repoRoot, record.repo_root) ?? null,\n branch: readString(record.branch) ?? null,\n upstream: readString(record.upstream) ?? null,\n upstreamStatus: readString(record.upstreamStatus, record.upstream_status) ?? null,\n headCommit: readString(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,\n ahead: readNumber(record.ahead) ?? null,\n behind: readNumber(record.behind) ?? null,\n dirtyCounts: {\n staged: readNumber(record.staged) ?? 0,\n modified: readNumber(record.modified) ?? 0,\n untracked: readNumber(record.untracked) ?? 0,\n deleted: readNumber(record.deleted) ?? 0,\n renamed: readNumber(record.renamed) ?? 0,\n },\n lastCheckedAt: readNumber(record.lastCheckedAt, record.last_checked_at) ?? null,\n submoduleCount: submodules.length,\n submodules,\n }\n}\n","/**\n * MAGI — Multi-Agent Ground-truth Insight.\n *\n * Pure shared types for the mesh cross-verification quorum: the per-task_kind\n * panel binding (machine-local config, stored in ~/.adhdev/meshes.json\n * `magiKindPanels`), the agent-agnostic common output schema every dispatched\n * replica answers with, and the synthesis result shapes. These cross the\n * daemon-core (storage / accessors) ↔ mcp-server (fan-out / synthesis) boundary,\n * so they live in the dependency-free mesh-shared leaf — no runtime, no Node/DOM APIs.\n *\n * Design: docs/design/2026-06-28-mesh-magi-review.md. Core stance: no personas,\n * no named lenses — a panel slot is just one `(node × provider)` target that\n * answers the SAME question. The value is the friction (contested / singleton /\n * source-coupled findings), NOT a majority vote.\n *\n * NOTE: the former named-panel model (MagiPanel / MagiPanelMember / MagiPanelMap\n * and inline `members`) was REMOVED. A MAGI review resolves its fan-out slots\n * EXCLUSIVELY from the `magiKindPanels` binding for the review's `task_kind`; an\n * unconfigured kind is a hard error, never a synthesized or named-panel fallback.\n */\n\n// ─── Kind → panel binding (machine-local config) ─────────\n//\n// MAGI-KIND-PANEL: an explicit, per-task_kind panel binding. `mesh_magi_review`\n// invoked with a bare `task_kind` (no panel name / inline members) resolves the\n// panel from THIS map — the user must have configured ≥1 slot for that kind in mesh\n// settings. There is NO hardcoded preset auto-synthesis fallback: an unconfigured\n// kind is a hard error (magi_kind_not_configured), never a silent synthetic panel.\n\n/**\n * One kind-panel slot: a `(node × provider [× model])` target. `provider` required;\n * `nodeId` pins a concrete mesh node; `model` optionally selects the agent model at\n * launch; `capabilityTags` route by tag when no nodeId is given; `n` is an optional\n * per-slot replica count. This is the SOLE panel-member shape — the fan-out planner\n * (buildMagiFanoutPlan) resolves a `MagiSlot[]` directly.\n *\n * ─── DELIBERATELY REDUCED SCHEMA — not a missing feature ─────────────────────\n *\n * A MagiSlot is a strict subset of a node's `NodeCapabilitySlot`. It accepts\n * `provider` + optional `model` / `nodeId` / `capabilityTags` / `n`, and it\n * deliberately does NOT accept the node-capability routing axes — `thinkingLevel`,\n * `difficulty`, `maxParallel`. That asymmetry is the design, not an oversight:\n *\n * - A node capability slot answers **\"which work goes where\"** — routing FITNESS.\n * Difficulty and thinking level exist there to match a task against the slot best\n * suited to run it.\n * - A MAGI panel slot answers **\"who answers independently\"** — cross-verification\n * DIVERSITY. Its whole value is that the replicas are NOT selected for fitness.\n *\n * Reviving the difficulty/thinking axes here would couple panel membership to routing\n * optimization, and the best-fitting provider would win every slot. The panel would\n * collapse toward one provider — which is precisely the failure MAGI exists to\n * prevent, since agreement among coupled agents carries no information (see the\n * `source_coupled` weighting in synthesis). A reduced schema is what keeps the two\n * axes orthogonal.\n *\n * Consistent with that, `mesh_magi_review` enqueues every replica with a fixed\n * `difficulty: 'freeform'` sentinel rather than a caller-chosen grade — the panel has\n * already pinned the (node, provider) target, so any difficulty would be inert at best\n * and would fight the panel's own slot selection at worst. To change how hard a\n * replica thinks or how much parallelism a node grants, edit that NODE's capability\n * slots (`mesh_node_slots_set`); it is a different axis, on purpose.\n *\n * Unknown keys are dropped rather than rejected, so slots written by another version\n * stay readable. Write paths pair the normalizer with\n * `collectIgnoredMagiSlotFields()` and report the drops as `ignoredFields`, so the\n * reduction is visible instead of silent.\n */\nexport interface MagiSlot {\n /** Optional — pin to a specific mesh node id. Absent → route by capabilityTags + provider. */\n nodeId?: string\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'gemini-cli'. */\n provider: string\n /**\n * Optional model override applied at replica launch (e.g. 'opus' | 'sonnet' for\n * claude-cli). Threaded through enqueueTask → the auto-launched session's\n * `launch_cli` payload as `initialModel`. For ACP providers it drives\n * setConfigOption('model', …); for CLI providers it is expanded via the provider\n * manifest's `modelLaunchArgs` template into launch args (a provider with no\n * template silently ignores it — model is best-effort, never fatal).\n */\n model?: string\n /** Optional routing tags, ANDed with the provider tag when nodeId is absent. */\n capabilityTags?: string[]\n /** Optional per-slot replica count; defaults to the kind-panel defaultN / global n / 1. */\n n?: number\n}\n\n/**\n * Per-task_kind panel binding for ONE mesh, stored machine-local in\n * `~/.adhdev/meshes.json` under that mesh's entry (`meshes[].magiKindPanels`). The\n * scope is per mesh: two meshes on the same machine hold independent bindings for\n * the same task_kind. (It formerly sat at the config root keyed by task_kind alone,\n * which let one mesh's write clobber another's.) A kind absent from the map has NO\n * configured panel → `mesh_magi_review({task_kind})` errors with\n * `magi_kind_not_configured` rather than synthesizing one. `freeform` MAY be bound\n * like any other kind (a direct kind→slots binding).\n */\nexport type MagiKindPanelMap = Partial<Record<MagiTaskKind, MagiSlot[]>>\n\n/**\n * The output-schema selector a MAGI fan-out injects into every replica prompt and\n * the strict parser used at collection. Code-orthogonal to the panel's slot set\n * (the fan-out planner never reads it) — it ONLY shapes the per-replica prompt /\n * parse contract. SSOT lives here (mesh-shared leaf) so both daemon-core (slot\n * normalization) and mcp-server (resolution / prompt assembly) consume one union.\n *\n * - claim_audit (default, backward-compatible), rca, design → require evidence[].\n * - freeform → no schema, no evidence; contributes NO structured claims to\n * synthesis. It MAY still be bound as a kind-panel key (a direct kind→slots\n * binding), unlike the removed named-panel `defaultKind`.\n */\nexport type MagiTaskKind = 'claim_audit' | 'rca' | 'design' | 'freeform'\n\n/**\n * Synthesis emphasis hint. Affects weighting / labels only — NEVER the agent\n * count or the common schema. (Per-mode weighting tuning is a deferred refinement;\n * the field is accepted now so callers and panels are forward-stable.)\n */\nexport type MagiMode = 'rca' | 'investigation' | 'claim_audit' | 'design_review' | 'code_audit'\n\n// ─── Common output schema (agent-agnostic) ──────\n\nexport type MagiClaimStance = 'support' | 'oppose' | 'uncertain'\n\n/**\n * One claim from one agent. `evidence` carries `file:line` or external-source\n * strings; `confidence` is 0..1. Identical regardless of which provider/machine\n * produced it — this is the forced structured-output contract injected into each\n * dispatched task prompt.\n */\nexport interface MagiClaim {\n claim: string\n stance: MagiClaimStance\n evidence: string[]\n confidence: number\n}\n\n/** The agent-agnostic response every dispatched replica returns. */\nexport interface MagiAgentResponse {\n claims: MagiClaim[]\n top_findings: string[]\n open_questions: string[]\n}\n\n// ─── Synthesis result shapes ────────────────────\n\n/**\n * Where one response came from — the `(node × provider)` identity that backs a\n * claim's independence. `ok=false` marks a replica that died / produced no\n * parseable common-schema output (excluded from clusters, counted as missing).\n */\nexport interface MagiResponseSource {\n /** Mesh task id of the dispatched replica. */\n taskId: string\n nodeId?: string\n provider?: string\n /** False when the replica failed or its output could not be parsed. */\n ok: boolean\n /** Reason when ok=false (timeout / failed / unparseable / stale). */\n error?: string\n /**\n * True when the replica was detected STALE during collection — assigned to a\n * node/session no longer present in the live mesh (so it will never reach a\n * terminal state). Distinguishes a dead-assignment replica from one that is\n * merely still generating. Always implies ok=false.\n */\n stale?: boolean\n /**\n * Git ref of the node this replica ran on, captured at collection time from the\n * live mesh node's compact git summary. Lets synthesis (and a dashboard) detect\n * GIT SKEW across the panel — if the answering replicas span different branches\n * or diverge (ahead/behind), their file:line evidence is comparing different code\n * and \"agreement\" is less meaningful. Best-effort; absent when the node carried\n * no git summary.\n */\n git?: MagiReplicaGitRef\n /**\n * The replica's raw end-user answer text as read from its session transcript at\n * collection time, truncated to MAGI_RAW_ANSWER_CAP chars (with `rawAnswerTruncated`\n * set when it was longer). This is the human-readable original a replica produced —\n * useful when the structured claim parse dropped nuance, or for a cluster member that\n * needs the source prose. GATED: stripped from the persisted `magi_synthesis` ledger\n * entry (to bound ledger payload growth) and from the default mesh_magi_collect\n * response; surfaced ONLY in mesh_magi_collect verbose. Best-effort; absent for a\n * replica that produced no readable transcript (failed / stale / unparseable).\n */\n rawAnswer?: string\n /** True when `rawAnswer` was truncated at MAGI_RAW_ANSWER_CAP. */\n rawAnswerTruncated?: boolean\n}\n\n/** Max chars of a replica's raw answer retained on MagiResponseSource.rawAnswer. */\nexport const MAGI_RAW_ANSWER_CAP = 4000\n\n/** Compact git ref of the node a replica ran on (subset of GitRepoStatus). */\nexport interface MagiReplicaGitRef {\n branch?: string | null\n /** HEAD commit sha of the node's workspace — the exact code the replica saw. */\n headCommit?: string | null\n ahead?: number\n behind?: number\n dirty?: boolean\n}\n\n/**\n * Cross-replica git divergence assessment. `skewed` is true when the answering\n * replicas span more than one branch OR any replica diverges from its upstream\n * (ahead/behind > 0) — both mean the panel is not all looking at the same code, so\n * file:line evidence and agreement are git-skewed. Always present on a synthesis\n * (skewed=false / distinctBranches≤1 when there is nothing to flag).\n */\nexport interface MagiGitSkew {\n skewed: boolean\n /** Number of distinct branches across the answering replicas with a known branch. */\n distinctBranches: number\n /** The distinct branch names (sorted), for display. */\n branches: string[]\n /** Replicas whose branch/divergence differs from the panel baseline. */\n divergentReplicas: number\n note?: string\n}\n\n/** A parsed replica response paired with its source identity. */\nexport interface MagiSynthesizedResponse {\n source: MagiResponseSource\n response: MagiAgentResponse\n}\n\n/**\n * Synthesis category for a claim cluster. `needs_verification` is the PRIMARY\n * OUTPUT = contested ∪ singleton ∪ source_coupled ∪ (high-impact claims lacking\n * independent evidence). `agreed` is the only \"safe to trust, low priority\" bucket.\n */\nexport type MagiClusterCategory =\n | 'agreed'\n | 'contested'\n | 'dissent'\n | 'singleton'\n | 'source_coupled'\n\n/** One member observation inside a claim cluster. */\nexport interface MagiClusterMember {\n taskId: string\n nodeId?: string\n provider?: string\n claim: string\n stance: MagiClaimStance\n evidence: string[]\n confidence: number\n}\n\n/**\n * A cluster of semantically-equivalent claims across responses, with its\n * stance tally and diversity-weighted independence assessment.\n */\nexport interface MagiClaimCluster {\n /** Representative (first / longest) claim text for the cluster. */\n claim: string\n category: MagiClusterCategory\n members: MagiClusterMember[]\n stance: { support: number; oppose: number; uncertain: number }\n /** Distinct providers / nodes / evidence sources backing the cluster. */\n distinctProviders: number\n distinctNodes: number\n distinctEvidence: number\n /** Diversity-weighted independence score (NOT the raw agent count). */\n independenceScore: number\n /** True when this cluster is routed to needs_verification. */\n needsVerification: boolean\n /** Why it needs verification (contested / singleton / source_coupled / no_independent_evidence). */\n reasons: string[]\n}\n\n/** The full synthesis result — N-agnostic, diversity-weighted, not a vote. */\nexport interface MagiSynthesis {\n /** How many replicas were expected vs. produced parseable output. */\n replicasExpected: number\n replicasAnswered: number\n replicasMissing: number\n /** Distinct providers / nodes across the answering replicas. */\n distinctProviders: number\n distinctNodes: number\n /**\n * Set when the resolved panel collapsed to a single provider or single\n * machine — agreements are then flagged source-coupled. Null when independence\n * was achieved.\n */\n independenceBanner: string | null\n clusters: MagiClaimCluster[]\n /** PRIMARY OUTPUT — clusters routed to needs_verification, highest priority first. */\n needsVerification: MagiClaimCluster[]\n /** High-independence agreements — safe to trust, lowest review priority. */\n agreed: MagiClaimCluster[]\n /** Union of every response's open_questions (deduped). */\n openQuestions: string[]\n /**\n * Per-replica source identity (taskId / nodeId / provider / ok / stale / git) for\n * every replica in the fan-out. Lets a consumer (the dashboard's extractMagiActivity)\n * read which node × provider answered and the git ref each ran at — the inputs behind\n * the gitSkew assessment.\n */\n replicas: MagiResponseSource[]\n /**\n * Cross-replica git divergence. When skewed, the answering replicas were not all\n * on the same code (different branches / ahead-behind), so evidence and agreement\n * should be read with that caveat. Always present.\n */\n gitSkew: MagiGitSkew\n}\n","/**\n * Brain-routing (task difficulty → provider/model/thinking) types.\n *\n * The coordinator classifies each task it enqueues by execution difficulty, and a\n * per-difficulty \"brain\" preset resolves that into a concrete provider / model /\n * thinking-level for the launched session. The goal is token economy: an `easy`\n * task runs on a cheaper model at low reasoning effort; a `difficult` task gets a\n * stronger model at high effort. This is a separate axis from MAGI's review kinds\n * (rca/design/…) — MAGI fans out review replicas; this picks the single brain that\n * *executes* a task — but it reuses the same slot shape (provider/model) and the\n * same machine-local storage (`~/.adhdev/meshes.json`).\n */\n\n/** The fixed difficulty axis the coordinator classifies a task into. */\nexport type MeshTaskDifficulty = 'easy' | 'medium' | 'difficult' | 'freeform'\n\nexport const MESH_TASK_DIFFICULTIES: MeshTaskDifficulty[] = ['easy', 'medium', 'difficult', 'freeform']\n\n/**\n * A per-difficulty brain preset: which provider / model / thinking level a task of\n * that difficulty should run on. Every field is optional — a preset may set only a\n * model (keep the routed provider) or only a thinking level. Applied at enqueue:\n * an explicit model/thinkingLevel on the task always wins over the preset.\n */\nexport interface BrainSlot {\n /** Optional provider type, e.g. 'claude-cli' | 'codex-cli'. Absent → keep the tag/priority-routed provider. */\n provider?: string\n /** Optional model, e.g. 'opus' | 'sonnet' | 'haiku'. Best-effort at launch (initialModel). */\n model?: string\n /** Optional standard thinking level, 'low' | 'medium' | 'high'. Best-effort (initialThinkingLevel). */\n thinkingLevel?: 'low' | 'medium' | 'high'\n}\n\n/**\n * Per-difficulty brain bindings for ONE mesh, stored machine-local in\n * `~/.adhdev/meshes.json` under that mesh's entry (`meshes[].difficultyBrains`,\n * sibling of `magiKindPanels`). The scope is per mesh: two meshes on the same\n * machine hold independent presets, so one can pin `difficult` to a cheaper model\n * without changing what any other mesh runs. (It formerly sat at the config root\n * keyed by difficulty alone — and since this map picks the MODEL a task runs on,\n * that meant the shipped defaults, and any one mesh's override, applied to every\n * mesh on the machine.) A difficulty absent from the map has no preset — the task\n * runs with no difficulty-derived model/thinking (ordinary routing).\n */\nexport type DifficultyBrainMap = Partial<Record<MeshTaskDifficulty, BrainSlot>>\n\n/** True for a recognized difficulty value. */\nexport function isMeshTaskDifficulty(value: unknown): value is MeshTaskDifficulty {\n return typeof value === 'string' && (MESH_TASK_DIFFICULTIES as string[]).includes(value)\n}\n\n/**\n * Shipped difficulty presets: NONE. Operator-authored capability slots are the\n * authority: easy < medium < difficult is a hard minimum (freeform is explicitly\n * unconstrained), while the preset map only supplies optional launch axes.\n *\n * WHY THIS IS EMPTY (it formerly shipped easy→haiku/low, medium→sonnet/medium,\n * difficult→opus/high):\n *\n * 1. WRONG AXIS. `opus`/`sonnet`/`haiku` are Claude model families, but the\n * preset was stamped on a task at ENQUEUE time — before routing has chosen\n * a node, let alone a provider. A `difficult` task therefore carried\n * `model: 'opus'` even when it landed on kimi / codex / antigravity. The\n * CODEX-400 guard (model-provider-compat.ts) exists ONLY to strip these\n * Claude aliases back off before a non-Anthropic launch — a workaround for\n * a value that should never have been stamped provider-blind.\n *\n * 2. IT FOUGHT THE SLOTS. Operators express model choice per node via\n * capability slots (`mesh_node_slots_set`), which know the provider. A\n * mesh-wide preset that also picks a model duplicated that decision from a\n * position with strictly less information, and needed the modelSource\n * ('explicit' vs 'preset') precedence machinery plus a fail-closed\n * slot-model guard just to keep the slot winning.\n *\n * Removing the shipped defaults does NOT remove the feature: the map is still\n * honored, so an operator who deliberately sets presets (difficulty_brains_set)\n * keeps exactly that behavior. It only stops the mesh from inventing a model\n * nobody configured. A node WITH slots is unaffected (its slots already decided);\n * a node WITHOUT slots now launches on the provider's own default model instead\n * of a Claude alias picked blind.\n */\nexport const DEFAULT_DIFFICULTY_BRAINS: DifficultyBrainMap = {}\n\n/** Normalize a raw thinking level to the standard union, or undefined. */\nexport function normalizeThinkingLevel(value: unknown): 'low' | 'medium' | 'high' | undefined {\n const v = typeof value === 'string' ? value.trim().toLowerCase() : ''\n return v === 'low' || v === 'medium' || v === 'high' ? v : undefined\n}\n\n/** Normalize a raw BrainSlot (trim strings, drop empties, clamp thinkingLevel). */\nexport function normalizeBrainSlot(raw: unknown): BrainSlot {\n const r = (raw && typeof raw === 'object') ? raw as Record<string, unknown> : {}\n const provider = typeof r.provider === 'string' ? r.provider.trim() : ''\n const model = typeof r.model === 'string' ? r.model.trim() : ''\n const thinkingLevel = normalizeThinkingLevel(r.thinkingLevel)\n return {\n ...(provider ? { provider } : {}),\n ...(model ? { model } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n }\n}\n\n/** Normalize a raw DifficultyBrainMap, dropping unknown keys and empty slots. */\nexport function normalizeDifficultyBrainMap(raw: unknown): DifficultyBrainMap {\n const out: DifficultyBrainMap = {}\n if (!raw || typeof raw !== 'object') return out\n for (const key of MESH_TASK_DIFFICULTIES) {\n const slot = normalizeBrainSlot((raw as Record<string, unknown>)[key])\n if (slot.provider || slot.model || slot.thinkingLevel) out[key] = slot\n }\n return out\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Node capability slots (ORCHESTRATION_NODE_SLOTS.md)\n//\n// A node's \"Preferred AI tools\" list is redefined as an ordered array of\n// capability slots. Each slot bundles what used to be scattered across\n// providerPriority (order), a per-provider maxParallel cap, and the\n// the owning mesh's difficultyBrains (difficulty → model/thinking). Slot order =\n// preference. This single profile is the source of truth for task routing, MAGI\n// fan-out, and orchestrator-proposed edits.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * One capability slot on a mesh node. Extends the BrainSlot shape\n * (provider/model/thinkingLevel) with the difficulty range it handles, capability\n * tags, and a per-slot parallelism cap.\n */\nexport interface NodeCapabilitySlot {\n /** Provider type this slot uses, e.g. 'claude-cli' | 'codex-cli'. Required (a slot is defined by its provider). */\n provider: string\n /** Optional model, e.g. 'opus' | 'sonnet' | 'haiku'. Best-effort at launch. */\n model?: string\n /**\n * Optional thinking level. The provider's own vocabulary (e.g. low/medium/high,\n * or codex's low/medium/high/max) passed through verbatim — best-effort at\n * launch. A string, not the standard union, so provider-declared levels like\n * 'max' are not dropped.\n */\n thinkingLevel?: string\n /**\n * Difficulty grades this slot handles. On an explicit-slot node these form a\n * hard minimum: higher grades may run lower tasks, but lower grades never run\n * higher tasks. Empty/absent is ungraded and only unconstrained freeform tasks\n * use it; legacy providerPriority-derived slots remain backward-compatible.\n */\n difficulty?: MeshTaskDifficulty[]\n /** Capability tags this slot satisfies (matched against a task's requiredTags). */\n capability?: string[]\n /** Per-node·per-slot max concurrent tasks. Omit = no per-slot cap. */\n maxParallel?: number\n}\n\n/** Normalize a raw NodeCapabilitySlot; returns null when it has no usable provider. */\nexport function normalizeNodeCapabilitySlot(raw: unknown): NodeCapabilitySlot | null {\n const r = (raw && typeof raw === 'object') ? raw as Record<string, unknown> : {}\n const provider = typeof r.provider === 'string' ? r.provider.trim() : ''\n if (!provider) return null\n const model = typeof r.model === 'string' ? r.model.trim() : ''\n // Pass the provider's own thinking-level vocabulary through verbatim (don't\n // clamp to low/medium/high — a provider may declare 'max' etc.).\n const thinkingLevel = typeof r.thinkingLevel === 'string' ? r.thinkingLevel.trim() : ''\n const difficulty = Array.isArray(r.difficulty)\n ? (r.difficulty.filter(isMeshTaskDifficulty) as MeshTaskDifficulty[])\n : []\n const capability = Array.isArray(r.capability)\n ? r.capability.filter((t): t is string => typeof t === 'string' && !!t.trim()).map(t => t.trim())\n : []\n const maxParallelNum = Number(r.maxParallel)\n const maxParallel = Number.isFinite(maxParallelNum) && maxParallelNum > 0 ? Math.floor(maxParallelNum) : undefined\n return {\n provider,\n ...(model ? { model } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n ...(difficulty.length ? { difficulty } : {}),\n ...(capability.length ? { capability } : {}),\n ...(maxParallel !== undefined ? { maxParallel } : {}),\n }\n}\n\n/** Normalize a raw slot array, dropping provider-less entries. */\nexport function normalizeNodeCapabilitySlots(raw: unknown): NodeCapabilitySlot[] {\n if (!Array.isArray(raw)) return []\n const out: NodeCapabilitySlot[] = []\n for (const entry of raw) {\n const slot = normalizeNodeCapabilitySlot(entry)\n if (slot) out.push(slot)\n }\n return out\n}\n\n/**\n * Back-compat migration: derive capability slots from the legacy fields when a\n * node has no explicit `slots`. Order follows providerPriority; difficulty/model/\n * thinking are folded in from the OWNING MESH's difficultyBrains (each difficulty\n * attaches to the slot whose provider the brain preset names, or — when the brain\n * has no provider — to every slot as a shared model/thinking default for that\n * difficulty).\n *\n * (Legacy nodes' per-provider `maxParallel` cap has been migrated onto\n * `slots[].maxParallel` at config-load time, so it is no longer folded in here.)\n *\n * Returns [] when there's nothing to derive (caller then keeps legacy behavior:\n * first available provider).\n */\nexport function deriveSlotsFromLegacy(input: {\n providerPriority?: string[]\n difficultyBrains?: DifficultyBrainMap\n}): NodeCapabilitySlot[] {\n const priority = Array.isArray(input.providerPriority)\n ? input.providerPriority.filter((p): p is string => typeof p === 'string' && !!p.trim()).map(p => p.trim())\n : []\n if (priority.length === 0) return []\n\n // Brain presets keyed by the provider they name (provider-specific), plus a\n // provider-agnostic list applied to every slot as a shared default.\n const brains = input.difficultyBrains || {}\n const byProvider = new Map<string, Array<{ difficulty: MeshTaskDifficulty; model?: string; thinkingLevel?: 'low' | 'medium' | 'high' }>>()\n const shared: Array<{ difficulty: MeshTaskDifficulty; model?: string; thinkingLevel?: 'low' | 'medium' | 'high' }> = []\n for (const diff of MESH_TASK_DIFFICULTIES) {\n const b = brains[diff]\n if (!b) continue\n const entry = { difficulty: diff, model: b.model, thinkingLevel: b.thinkingLevel }\n if (b.provider) {\n const list = byProvider.get(b.provider) ?? []\n list.push(entry)\n byProvider.set(b.provider, list)\n } else {\n shared.push(entry)\n }\n }\n\n return priority.map((provider): NodeCapabilitySlot => {\n // Provider-specific brain first, else the shared default, else nothing.\n const specific = byProvider.get(provider) || []\n const applied = specific.length ? specific : shared\n const difficulty = applied.map(a => a.difficulty)\n // Fold model/thinking from the applied presets: take the first that sets each.\n const model = applied.find(a => a.model)?.model\n const thinkingLevel = applied.find(a => a.thinkingLevel)?.thinkingLevel\n return {\n provider,\n ...(model ? { model } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n ...(difficulty.length ? { difficulty } : {}),\n }\n })\n}\n\n/**\n * The reverse direction of deriveSlotsFromLegacy: derive the legacy\n * `providerPriority` order from capability slots — each slot's provider, in\n * first-appearance order, de-duplicated. Slot order = preference is the settled\n * slots semantics (ORCHESTRATION_NODE_SLOTS.md), so this is what readers of the\n * legacy `policy.providerPriority` field must fall back to when a node declares\n * slots but no explicit providerPriority (otherwise such a node reads as\n * unlaunchable even though its slots fully determine the preference order).\n *\n * Accepts the raw `policy.slots` value (normalized internally). Returns [] when\n * there's nothing to derive (caller then keeps legacy behavior: no priority).\n */\nexport function deriveProviderPriorityFromSlots(slots: unknown): string[] {\n const seen = new Set<string>()\n const out: string[] = []\n for (const slot of normalizeNodeCapabilitySlots(slots)) {\n if (seen.has(slot.provider)) continue\n seen.add(slot.provider)\n out.push(slot.provider)\n }\n return out\n}\n","/**\n * CLI auto-detect → capability-slot / MAGI-panel PROPOSAL generator.\n *\n * Detection of installed CLI providers already exists per node (the status\n * snapshot's `availableProviders`), and applying a slot profile already exists\n * (`mesh_node_slots_set`, dry-run by default). What was missing is the bit in\n * between: turning \"these CLIs are installed on this node\" into a concrete\n * `NodeCapabilitySlot[]` draft the operator can review. This module is that\n * bridge, and nothing more — it is a PURE proposal generator. It never writes;\n * the caller feeds its output into the existing dry-run/approve tools.\n *\n * ─── Why the mapping is a static table ───────────────────────────────────────\n *\n * Provider manifests carry NO difficulty or capability-grade information. The\n * fields that look like they might (`modelOptions`, `thinkingLevelOptions`)\n * describe what a provider ACCEPTS, not what it is GOOD AT — a provider listing\n * `opus` says nothing about whether opus should get the hard tasks. So there is\n * no honest way to derive difficulty from the manifest today.\n *\n * The table below is therefore seeded from the operator's real, in-use slot\n * configuration rather than from a guess. That makes it a starting point with\n * actual provenance, and it is deliberately isolated in ONE constant so the\n * planned usage-data-driven replacement has a single, obvious swap point.\n *\n * Kept in the dependency-free mesh-shared leaf because both daemon-core (which\n * has the detection data) and mcp-server (which owns the propose/apply tools)\n * need it, and it is pure data + pure functions on plain objects.\n */\nimport {\n normalizeNodeCapabilitySlot,\n type MeshTaskDifficulty,\n type NodeCapabilitySlot,\n} from './brain-routing'\nimport type { MagiSlot } from './magi'\n\n/**\n * One provider's seeded slot recipe. A provider may map to MORE THAN ONE slot\n * (claude-cli does: a wide sonnet slot plus a narrow opus slot for the hard\n * work), which is why the table's values are arrays.\n */\nexport interface CliSlotRecipe {\n /** Optional model to pin on the slot. Best-effort at launch. */\n model?: string\n /** Optional thinking level, in the provider's own vocabulary. */\n thinkingLevel?: string\n /** Difficulty range this slot handles. Empty = general-purpose. */\n difficulty?: MeshTaskDifficulty[]\n /** Per-slot concurrency cap. */\n maxParallel?: number\n /**\n * Set when this recipe is an unvalidated guess rather than a transcription\n * of a slot the operator actually runs. Surfaced on the proposal so the\n * reviewer knows which lines to scrutinize.\n */\n provisional?: boolean\n /** Short human-readable justification, echoed into the proposal. */\n rationale?: string\n}\n\n/**\n * ★ THE MAPPING TABLE — the single swap point.\n *\n * Seeded (2026-08-03) from the operator's live slot configuration, on the\n * reasoning that a transcription of what is actually in production beats an\n * invented heuristic. Replace wholesale once usage data (success rate, cost,\n * turn latency per provider×difficulty) can drive it.\n *\n * Every entry except `hermes-cli` reflects a real configured slot. `hermes-cli`\n * is a conservative GUESS — see its `provisional` flag.\n */\nexport const CLI_SLOT_RECIPES: Readonly<Record<string, readonly CliSlotRecipe[]>> = Object.freeze<Record<string, readonly CliSlotRecipe[]>>({\n 'claude-cli': [\n {\n model: 'sonnet',\n thinkingLevel: 'high',\n difficulty: ['medium', 'easy'],\n maxParallel: 5,\n rationale: 'Primary workhorse — widest parallelism for routine work.',\n },\n {\n model: 'opus',\n thinkingLevel: 'high',\n difficulty: ['difficult'],\n maxParallel: 1,\n rationale: 'Reserved for hard tasks; capped at 1 to bound cost.',\n },\n ],\n 'kimi': [\n {\n model: 'kimi-code/k3',\n difficulty: ['medium', 'difficult'],\n maxParallel: 2,\n rationale: 'Independent second opinion on mid/hard work.',\n },\n ],\n 'codex-cli': [\n {\n difficulty: ['medium', 'difficult', 'freeform'],\n maxParallel: 2,\n rationale: 'Broad range including freeform; no model pin.',\n },\n ],\n 'antigravity-cli': [\n {\n model: 'Gemini 3.1 Pro (High)',\n difficulty: ['easy'],\n maxParallel: 2,\n rationale: 'Cheap capacity for easy tasks.',\n },\n ],\n 'cursor-cli': [\n {\n model: 'auto',\n difficulty: ['easy'],\n maxParallel: 1,\n rationale: 'Easy tasks only; auto model selection.',\n },\n ],\n 'hermes-cli': [\n {\n difficulty: ['medium'],\n maxParallel: 2,\n provisional: true,\n // NOTE: ESTIMATE, NOT OBSERVED. hermes-cli is absent from the live\n // slot configuration this table was seeded from, so `medium` is a\n // conservative placement rather than a transcription. Revisit once\n // it has real usage data.\n rationale: 'ESTIMATE — no live slot to transcribe; conservative mid placement. Adjust after real use.',\n },\n ],\n})\n\n/**\n * Fallback for a detected CLI provider absent from {@link CLI_SLOT_RECIPES}.\n * Deliberately timid: one general-purpose slot at the lowest parallelism, so an\n * unrecognized provider can be used but never silently soaks up the queue.\n */\nexport const UNKNOWN_CLI_SLOT_RECIPE: Readonly<CliSlotRecipe> = Object.freeze<CliSlotRecipe>({\n difficulty: ['medium'],\n maxParallel: 1,\n provisional: true,\n rationale: 'Unrecognized provider — conservative default (medium, maxParallel 1). Review before relying on it.',\n})\n\n/** A detected, installed CLI provider on one node — the generator's input. */\nexport interface DetectedCliProvider {\n /** Provider type id, e.g. 'claude-cli'. */\n type: string\n /** Human-readable name, for display in the proposal. */\n displayName?: string\n /** Detected version, when known. Display only — never affects the mapping. */\n version?: string\n}\n\n/** One proposed slot plus why it was proposed. */\nexport interface ProposedSlotEntry {\n slot: NodeCapabilitySlot\n /** True when the provider had no table entry and took the conservative fallback. */\n unknownProvider: boolean\n /** True when the recipe behind this slot is flagged as an unvalidated estimate. */\n provisional: boolean\n rationale?: string\n}\n\n/** The full slot proposal for one node, including what a write would DESTROY. */\nexport interface SlotProposal {\n /** The draft slot list — a WHOLESALE replacement for the node's policy.slots. */\n proposedSlots: NodeCapabilitySlot[]\n /** Per-slot provenance, index-aligned with `proposedSlots`. */\n entries: ProposedSlotEntry[]\n /** Provider types detected but not present in the mapping table. */\n unknownProviders: string[]\n /** Provider types whose proposal rests on an unvalidated estimate. */\n provisionalProviders: string[]\n /**\n * Slots currently configured on the node that the proposal does NOT\n * reproduce — i.e. what applying this proposal would DELETE. Slot writes are\n * wholesale replacements, so an operator-hand-tuned slot absent from the\n * detection-derived draft is silently destroyed unless it is named here.\n */\n droppedSlots: NodeCapabilitySlot[]\n /** Provider types that appear in `droppedSlots` but in no proposed slot at all. */\n droppedProviders: string[]\n /** True when applying the proposal would remove at least one existing slot. */\n destructive: boolean\n}\n\n/** Stable key identifying a slot's identity for current-vs-proposed diffing. */\nfunction slotKey(slot: NodeCapabilitySlot): string {\n return [\n slot.provider,\n slot.model ?? '',\n slot.thinkingLevel ?? '',\n [...(slot.difficulty ?? [])].sort().join('|'),\n [...(slot.capability ?? [])].sort().join('|'),\n slot.maxParallel ?? '',\n ].join('\u0000')\n}\n\n/** Dedupe detected providers by type, preserving first-seen order. */\nfunction dedupeDetected(detected: readonly DetectedCliProvider[]): DetectedCliProvider[] {\n const seen = new Set<string>()\n const out: DetectedCliProvider[] = []\n for (const d of detected) {\n const type = typeof d?.type === 'string' ? d.type.trim() : ''\n if (!type || seen.has(type)) continue\n seen.add(type)\n out.push({ ...d, type })\n }\n return out\n}\n\n/**\n * Build a capability-slot proposal from a node's detected CLI providers.\n *\n * Pure and total: zero detections yields an empty proposal (never throws), which\n * the caller should treat as \"nothing to propose\" rather than \"replace the\n * node's slots with nothing\".\n *\n * `currentSlots` is optional but strongly recommended — it is the only way the\n * returned proposal can report what a wholesale write would destroy.\n */\nexport function buildSlotProposal(\n detected: readonly DetectedCliProvider[],\n currentSlots: readonly NodeCapabilitySlot[] = [],\n): SlotProposal {\n const providers = dedupeDetected(detected ?? [])\n const proposedSlots: NodeCapabilitySlot[] = []\n const entries: ProposedSlotEntry[] = []\n const unknownProviders: string[] = []\n const provisionalProviders: string[] = []\n\n for (const provider of providers) {\n const known = CLI_SLOT_RECIPES[provider.type]\n const recipes: readonly CliSlotRecipe[] = known ?? [UNKNOWN_CLI_SLOT_RECIPE]\n const isUnknown = !known\n if (isUnknown) unknownProviders.push(provider.type)\n\n let providerProvisional = false\n for (const recipe of recipes) {\n // Normalize through the SAME normalizer the daemon applies on write, so\n // a proposal can never preview a shape the write would reshape.\n const slot = normalizeNodeCapabilitySlot({\n provider: provider.type,\n model: recipe.model,\n thinkingLevel: recipe.thinkingLevel,\n difficulty: recipe.difficulty,\n maxParallel: recipe.maxParallel,\n })\n if (!slot) continue\n const provisional = recipe.provisional === true\n if (provisional) providerProvisional = true\n proposedSlots.push(slot)\n entries.push({\n slot,\n unknownProvider: isUnknown,\n provisional,\n ...(recipe.rationale ? { rationale: recipe.rationale } : {}),\n })\n }\n if (providerProvisional) provisionalProviders.push(provider.type)\n }\n\n // What a wholesale write would destroy: every currently-configured slot with\n // no exact counterpart in the draft.\n const proposedKeys = new Set(proposedSlots.map(slotKey))\n const droppedSlots = currentSlots.filter(slot => !proposedKeys.has(slotKey(slot)))\n const proposedProviders = new Set(proposedSlots.map(s => s.provider))\n const droppedProviders = [...new Set(\n droppedSlots.map(s => s.provider).filter(p => !proposedProviders.has(p)),\n )]\n\n return {\n proposedSlots,\n entries,\n unknownProviders,\n provisionalProviders,\n droppedSlots,\n droppedProviders,\n destructive: droppedSlots.length > 0,\n }\n}\n\n/**\n * Build a MAGI panel proposal from the same detections.\n *\n * ─── Deliberately narrow ──────────────────────────────────────────────────────\n *\n * MAGI's value is provider INDEPENDENCE: replicas from different providers\n * (ideally different machines) answering the same question, so agreement means\n * something. Detection tells us which providers exist — that is exactly enough\n * to propose one panel of distinct providers, and no more.\n *\n * What detection does NOT tell us is which provider suits which review KIND\n * (rca vs design vs claim_audit). Nothing in any manifest grades a provider for\n * root-cause analysis over design review, and inventing a per-kind assignment\n * would fabricate a rationale that does not exist. So this proposes ONE panel of\n * the detected providers and leaves the kind binding to the operator; the caller\n * decides which `task_kind` to bind it to via the existing dry-run tool.\n *\n * Ordering follows {@link CLI_SLOT_RECIPES} insertion order (recipe-known\n * providers first, in table order), so the panel leads with the providers whose\n * suitability is actually attested.\n */\nexport function buildMagiPanelProposal(\n detected: readonly DetectedCliProvider[],\n opts: { nodeId?: string; maxSlots?: number } = {},\n): MagiSlot[] {\n const providers = dedupeDetected(detected ?? [])\n const tableOrder = Object.keys(CLI_SLOT_RECIPES)\n const rank = (type: string): number => {\n const i = tableOrder.indexOf(type)\n return i === -1 ? Number.MAX_SAFE_INTEGER : i\n }\n const ordered = [...providers].sort((a, b) => rank(a.type) - rank(b.type))\n const limit = Number.isFinite(opts.maxSlots) && (opts.maxSlots as number) > 0\n ? Math.floor(opts.maxSlots as number)\n : ordered.length\n\n return ordered.slice(0, limit).map((p): MagiSlot => ({\n ...(opts.nodeId ? { nodeId: opts.nodeId } : {}),\n provider: p.type,\n // A model is intentionally NOT pinned: the panel's job is cross-provider\n // independence, and pinning models here would silently couple the panel\n // to this table's cost assumptions rather than to review quality.\n }))\n}\n","/**\n * Pure {{key}} template interpolation shared by the mesh command paths.\n *\n * Hoisted from packages/daemon-cloud/src/mesh/mesh-interpolation.ts — the\n * substitution is transport-agnostic (plain string/object work), so it belongs\n * in the dependency-free mesh-shared leaf where standalone can reuse it too.\n * The substitution semantics are byte-for-byte identical to the prior cloud copy.\n */\nexport function interpolateArgs(\n args: Record<string, unknown>,\n context: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(args)) {\n result[k] = typeof v === 'string' ? interpolateString(v, context) : v;\n }\n return result;\n}\n\nexport function interpolateString(template: string, ctx: Record<string, unknown>): string {\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (_, key) => {\n const val = ctx[key];\n return val !== undefined ? String(val) : `{{${key}}}`;\n });\n}\n","/**\n * Canonical Repo Mesh coordinator tool-name registry — the single source of truth\n * the three surfaces must agree on:\n *\n * 1. mcp-server `ALL_MESH_TOOLS` (the published MCP tool schemas),\n * 2. daemon-core `coordinator-prompt.ts` `TOOLS_SECTION` (what the coordinator LLM\n * is told it can call), and\n * 3. the `NN tools` doc comments in the mesh-tools barrels.\n *\n * mcp-server does not depend on daemon-core's internal prompt, and daemon-core cannot\n * import mcp-server (dependency direction: daemon-core ← mcp-server). This dependency-\n * free leaf is the only place both can reference, so the 6-6 consistency test\n * (daemon-core coordinator-prompt.test.ts) and mcp-server both assert against THIS\n * list. Adding a new mesh tool means adding its name here first; the tests then force\n * the schema + prompt + barrel comment to catch up, which is exactly the regression\n * gate that let coordinator-prompt drift 14 tools behind the schema before.\n *\n * Order mirrors mcp-server `ALL_MESH_TOOLS` for easy visual diffing, but the consistency\n * checks are set-based (order-insensitive).\n */\nexport const CANONICAL_MESH_TOOL_NAMES = [\n 'mesh_status',\n 'mesh_route_preview',\n 'mesh_list_nodes',\n // GRAPH-ORCHESTRATION Phase F — batch before task, mirroring ALL_MESH_TOOLS.\n 'mesh_enqueue_batch',\n 'mesh_enqueue_task',\n 'mesh_view_queue',\n // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.\n 'mesh_graph_view',\n 'mesh_graph_gate_claim',\n 'mesh_graph_gate_release',\n 'mesh_graph_gate_abandon',\n 'mesh_queue_cancel',\n 'mesh_queue_requeue',\n 'mesh_send_task',\n 'mesh_read_chat',\n 'mesh_read_debug',\n 'mesh_read_terminal',\n 'mesh_send_keys',\n 'mesh_launch_session',\n 'mesh_git_status',\n 'mesh_read_node_logs',\n 'mesh_fast_forward_node',\n 'mesh_restart_daemon',\n 'mesh_checkpoint',\n 'mesh_approve',\n 'mesh_answer_question',\n 'mesh_list_pending_approvals',\n 'mesh_plan_onboarding',\n 'mesh_create',\n 'mesh_add_node',\n 'mesh_clone_node',\n 'mesh_remove_node',\n 'mesh_cleanup_worktree_nodes',\n 'mesh_refine_node',\n 'mesh_refine_batch',\n 'mesh_refine_config',\n 'mesh_change_impact_config',\n 'mesh_init',\n 'mesh_reinit',\n 'mesh_write_mesh_json_config',\n 'mesh_refine_plan',\n 'mesh_cleanup_sessions',\n 'mesh_prune_stale_direct',\n 'mesh_task_history',\n 'mesh_ledger_query',\n 'mesh_record_note',\n 'mesh_forget_note',\n 'mesh_reconcile_ledger',\n 'mesh_requeue_held_events',\n 'mesh_mission_upsert',\n 'mesh_mission_list',\n 'mesh_review_inbox',\n 'mesh_magi_review',\n 'mesh_magi_collect',\n 'mesh_magi_kind_panel_set',\n 'mesh_magi_kind_panel_list',\n 'mesh_node_slots_set',\n 'mesh_node_slots_list',\n 'mesh_node_slots_propose',\n 'mesh_coordinator_prompt_append_get',\n 'mesh_coordinator_prompt_append_set',\n] as const;\n\nexport type CanonicalMeshToolName = typeof CANONICAL_MESH_TOOL_NAMES[number];\n\n/** The count the `NN tools` barrel doc comments and consistency test assert against. */\nexport const CANONICAL_MESH_TOOL_COUNT = CANONICAL_MESH_TOOL_NAMES.length;\n","/**\n * OFFLINE-NODE-STATUS-REFRESH — the status-origin probe marker.\n *\n * A remote `git_status` (or any relayed/dispatched mesh command) that originates from\n * an explicit_refresh / mesh_status aggregate carries this marker inside its ARGS. The\n * daemon-cloud dispatch wrapper and MCP relay handler read it to grant the SHORT\n * connect-wait budget so a single offline (powered-off) peer no longer blocks the whole\n * status assembly for ~90s.\n *\n * It is deliberately an args marker rather than adding `git_status` to the global\n * probe-class command set: a user-driven / targeted `git_status` (no marker) must keep\n * waiting out the full connect deadline for a slow relay to open (rc.503 intent). Only a\n * status-origin probe opts into the short budget.\n *\n * The key is `_`-prefixed so it travels alongside the real args (workspace,\n * refreshUpstream, includeSubmodules, …) and is ignored by the git_status handler; the\n * dispatch/relay sites strip it defensively before the command executes so it never\n * reaches a handler that echoes unknown args.\n *\n * This dependency-free leaf is the single source of truth shared by daemon-core (the\n * aggregate probe producer), mcp-server (the MCP relay producer), and daemon-cloud (the\n * dispatch/relay consumer) so the key cannot drift between producer and consumer.\n */\nexport const STATUS_PROBE_ARG_KEY = '_statusProbe' as const;\n\n/** Stamp the status-origin probe marker onto a command's args (non-mutating). */\nexport function withStatusProbeMarker(\n args: Record<string, unknown> = {},\n): Record<string, unknown> {\n return { ...args, [STATUS_PROBE_ARG_KEY]: true };\n}\n\n/** True when the args carry the status-origin probe marker. */\nexport function argsCarryStatusProbeMarker(args: unknown): boolean {\n return (\n !!args &&\n typeof args === 'object' &&\n (args as Record<string, unknown>)[STATUS_PROBE_ARG_KEY] === true\n );\n}\n\n/**\n * Return a shallow copy of args with the internal marker removed so it never leaks to\n * the executing command handler. Returns the original reference when no marker is\n * present (avoids an allocation on the common path).\n */\nexport function stripStatusProbeMarker(\n args: Record<string, unknown>,\n): Record<string, unknown> {\n if (!argsCarryStatusProbeMarker(args)) return args;\n const { [STATUS_PROBE_ARG_KEY]: _drop, ...rest } = args;\n return rest;\n}\n","/**\n * Mesh RPC frame chunking — split/reassemble oversized mesh envelopes.\n *\n * WHY THIS EXISTS\n * The mesh DataChannel path (`daemon-mesh-manager`) writes every RPC envelope as a\n * SINGLE `dc.sendMessage(JSON.stringify(envelope))` frame. That was fine while every\n * mesh arg was small text, but a coordinator dispatching an IMAGE to a worker puts a\n * multi-MB base64 part inside `args` — far past what one DataChannel frame carries, so\n * the send throws or the frame is dropped and the dispatch silently dies.\n *\n * The dashboard P2P path already solved exactly this problem and has been running in\n * production: `packages/daemon-cloud/src/daemon-p2p/data-channel-router.ts` (send +\n * reassemble) and `packages/web-cloud/src/p2p.ts` (browser side). This module is a PORT\n * of that proven scheme onto the mesh envelope shape — deliberately NOT a new protocol.\n * The boundary values are carried over unchanged (see the constants below for why each\n * one is what it is).\n *\n * It lives in mesh-shared because it is pure string/JSON work with no transport, no\n * Node API and no DOM API — the same reason the normalizers live here. `daemon-cloud`\n * (sender/receiver) is the only current consumer, but keeping it in the pure leaf means\n * the standalone side can reassemble with the identical code rather than a hand-synced\n * copy, which is the drift bug class this package was created to kill.\n *\n * FAILURE POLICY (DoD): reassembly never degrades silently. A malformed, out-of-range,\n * over-budget or unparseable chunk stream yields an explicit typed failure that the\n * caller turns into an RPC error — never a partially-applied or truncated envelope.\n */\n\n/**\n * Inline ceiling: an envelope whose JSON is at or below this goes out as one frame,\n * exactly as before this module existed. 60_000 (not 65_536) matches the dashboard\n * path and leaves headroom under the common 64KiB SCTP message limit so the frame is\n * never the thing that trips a transport-level cap.\n */\nexport const MESH_MAX_INLINE_FRAME_BYTES = 60_000\n\n/**\n * Per-chunk payload slice, in CHARACTERS. Ported unchanged from the dashboard path.\n *\n * Why 16_000 chars is safe under a 60_000-BYTE ceiling, with ~3.75x of headroom:\n * `splitMeshFrame` slices the ALREADY-SERIALIZED outer frame, so the text being cut\n * is JSON output — control characters are pre-expanded to `\\u00XX` ASCII before they\n * ever reach the slicer. Re-escaping that slice inside the chunk envelope can at worst\n * double the backslashes. Measured worst cases for a 16_000-char slice:\n * backslashes / astral emoji → 32,073 bytes\n * Korean (3-byte, unescaped) → 48,073 bytes ← densest observed\n * plain ASCII → 16,073 bytes\n * All are under MESH_MAX_INLINE_FRAME_BYTES. `splitMeshFrame` still MEASURES each\n * envelope and shrinks on overflow, so the guarantee is enforced rather than assumed\n * if either constant is ever retuned — but at these values that path is unreachable,\n * which is exactly what `assertMeshChunkConstantsAreSafe` pins down.\n */\nexport const MESH_CHUNK_PAYLOAD_CHARS = 16_000\n\n/**\n * Hard cap on chunk count for one frame. 1024 × ~16KB ≈ 16MB of transferable payload,\n * which comfortably covers a screenshot while bounding what a single peer can make the\n * receiver buffer. Exceeding it is an explicit refusal, never a truncated send.\n */\nexport const MESH_MAX_CHUNKS = 1024\n\n/**\n * Reassembly budget for one frame, in bytes. Bounds receiver memory independently of\n * chunk count so a peer cannot send 1024 maximally-large chunks to force an oversized\n * allocation. Mirrors the dashboard receiver's MAX_REASSEMBLED_JSON_BYTES.\n */\nexport const MESH_MAX_REASSEMBLED_BYTES = 16_000_000\n\n/**\n * How long a partially-received frame is retained. A sender that dies mid-stream must\n * not pin receiver memory forever; the partial is swept and the frame simply never\n * completes (the RPC's own deadline then reports it).\n */\nexport const MESH_CHUNK_TTL_MS = 60_000\n\n/** Envelope `kind` for a chunk of a larger mesh frame. */\nexport const MESH_CHUNK_KIND = 'rpc_chunk'\n\nexport interface MeshChunkEnvelope {\n v: number\n kind: typeof MESH_CHUNK_KIND\n /** Groups the chunks of one logical frame. */\n chunkId: string\n /** 0-based position of this chunk. */\n index: number\n /** Total chunk count for this frame; constant across the group. */\n total: number\n /** The slice of the original envelope JSON. */\n data: string\n}\n\nexport type MeshChunkSplitResult =\n | { ok: true; chunks: MeshChunkEnvelope[] }\n | { ok: false; reason: 'too_many_chunks' | 'chunk_too_large'; detail: string }\n\nexport type MeshChunkAcceptResult =\n /** Chunk stored; the frame is not complete yet. */\n | { status: 'partial'; received: number; total: number }\n /** Final chunk landed and the frame parsed cleanly. */\n | { status: 'complete'; frame: unknown }\n /** Explicitly rejected — the caller must surface this, never ignore it. */\n | { status: 'failed'; reason: MeshChunkFailureReason; detail: string; chunkId: string }\n\nexport type MeshChunkFailureReason =\n | 'malformed_chunk'\n | 'too_many_chunks'\n | 'inconsistent_total'\n | 'duplicate_chunk_mismatch'\n | 'budget_exceeded'\n | 'reassembled_parse_failed'\n\n/** UTF-8 byte length without assuming Buffer or TextEncoder is present. */\nexport function meshUtf8ByteLength(value: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(value).byteLength\n let bytes = 0\n for (let i = 0; i < value.length; i += 1) {\n const code = value.charCodeAt(i)\n if (code < 0x80) bytes += 1\n else if (code < 0x800) bytes += 2\n else if (code >= 0xd800 && code <= 0xdbff) { bytes += 4; i += 1 }\n else bytes += 3\n }\n return bytes\n}\n\n/**\n * Worst-case size of one chunk envelope at the current constants.\n *\n * Exported so the safety margin documented on MESH_CHUNK_PAYLOAD_CHARS is a CHECKED\n * invariant rather than a comment that rots. The densest slice the splitter can produce\n * is 3-byte unescaped text (JSON.stringify leaves non-ASCII as-is), so that is what this\n * measures. If someone raises MESH_CHUNK_PAYLOAD_CHARS or lowers the frame ceiling past\n * the safe point, the accompanying test fails loudly instead of the overflow only\n * showing up as dropped frames on a live mesh.\n */\nexport function measureWorstCaseChunkEnvelopeBytes(): number {\n const densest = '한'.repeat(MESH_CHUNK_PAYLOAD_CHARS)\n return meshUtf8ByteLength(JSON.stringify(\n buildChunkEnvelope(Number.MAX_SAFE_INTEGER, 'x'.repeat(64), MESH_MAX_CHUNKS, MESH_MAX_CHUNKS, densest),\n ))\n}\n\n/** True when the serialized frame must be chunked rather than sent inline. */\nexport function meshFrameNeedsChunking(json: string): boolean {\n return meshUtf8ByteLength(json) > MESH_MAX_INLINE_FRAME_BYTES\n}\n\nfunction buildChunkEnvelope(\n version: number, chunkId: string, index: number, total: number, data: string,\n): MeshChunkEnvelope {\n return { v: version, kind: MESH_CHUNK_KIND, chunkId, index, total, data }\n}\n\n/**\n * Split a serialized mesh envelope into chunk envelopes.\n *\n * Each slice is measured AS ITS FINAL SERIALIZED ENVELOPE and shrunk (×0.8, as in the\n * dashboard implementation) until it fits the inline ceiling — so multi-byte UTF-8 and\n * the envelope overhead are both accounted for rather than assumed away. `total` is\n * stamped only after the full split is known, so every chunk in a group agrees.\n */\nexport function splitMeshFrame(json: string, chunkId: string, version: number): MeshChunkSplitResult {\n const slices: string[] = []\n let offset = 0\n while (offset < json.length) {\n let end = Math.min(json.length, offset + MESH_CHUNK_PAYLOAD_CHARS)\n // Measure against MESH_MAX_CHUNKS as the `total` placeholder: it is the widest the\n // field can serialize to, so a slice that fits here still fits once the real (never\n // larger) total is stamped in below.\n while (end > offset) {\n const candidate = json.slice(offset, end)\n const probe = JSON.stringify(buildChunkEnvelope(version, chunkId, slices.length, MESH_MAX_CHUNKS, candidate))\n if (meshUtf8ByteLength(probe) <= MESH_MAX_INLINE_FRAME_BYTES) break\n const shrunk = Math.max(1, Math.floor((end - offset) * 0.8))\n if (offset + shrunk >= end) { end -= 1; continue }\n end = offset + shrunk\n }\n if (end <= offset) {\n return { ok: false, reason: 'chunk_too_large', detail: 'a single character did not fit the chunk envelope budget' }\n }\n slices.push(json.slice(offset, end))\n if (slices.length > MESH_MAX_CHUNKS) {\n return {\n ok: false,\n reason: 'too_many_chunks',\n detail: `frame needs more than ${MESH_MAX_CHUNKS} chunks (${meshUtf8ByteLength(json)} bytes)`,\n }\n }\n offset = end\n }\n if (slices.length === 0) {\n return { ok: false, reason: 'chunk_too_large', detail: 'refusing to chunk an empty frame' }\n }\n const total = slices.length\n return { ok: true, chunks: slices.map((data, index) => buildChunkEnvelope(version, chunkId, index, total, data)) }\n}\n\ninterface MeshChunkBuffer {\n total: number\n chunks: string[]\n received: number\n bytesReceived: number\n createdAt: number\n}\n\n/**\n * Receiver-side reassembly buffer, one per peer connection.\n *\n * Keyed by chunkId only — callers construct one assembler per peer, so chunk groups\n * from different peers can never collide in the same map.\n */\nexport class MeshChunkAssembler {\n private readonly buffers = new Map<string, MeshChunkBuffer>()\n\n constructor(private readonly now: () => number = () => Date.now()) {}\n\n /** True when the frame is a chunk envelope this assembler should handle. */\n static isChunkFrame(frame: unknown): boolean {\n return !!frame && typeof frame === 'object' && (frame as { kind?: unknown }).kind === MESH_CHUNK_KIND\n }\n\n /** Drop partials older than the TTL so a dead sender cannot pin memory. */\n private sweep(): void {\n const now = this.now()\n for (const [key, entry] of Array.from(this.buffers.entries())) {\n if (now - entry.createdAt > MESH_CHUNK_TTL_MS) this.buffers.delete(key)\n }\n }\n\n /** Discard any partial state for a peer (call on disconnect). */\n reset(): void {\n this.buffers.clear()\n }\n\n /** Number of frames currently mid-reassembly — for tests and diagnostics. */\n get pendingCount(): number {\n return this.buffers.size\n }\n\n /**\n * Accept one chunk envelope.\n *\n * Never throws and never returns a partially-applied frame: the result is exactly one\n * of partial / complete / failed, and `failed` carries a typed reason the transport\n * turns into an explicit RPC error.\n */\n accept(frame: unknown): MeshChunkAcceptResult {\n this.sweep()\n const raw = frame as Partial<MeshChunkEnvelope> | null\n const chunkId = typeof raw?.chunkId === 'string' ? raw.chunkId : ''\n const index = Number(raw?.index)\n const total = Number(raw?.total)\n const data = typeof raw?.data === 'string' ? raw.data : ''\n\n if (!chunkId || !Number.isInteger(index) || !Number.isInteger(total)\n || index < 0 || total <= 0 || index >= total || !data) {\n return {\n status: 'failed',\n reason: 'malformed_chunk',\n detail: `malformed chunk envelope (chunkId=${chunkId || '-'} index=${raw?.index} total=${raw?.total})`,\n chunkId,\n }\n }\n if (total > MESH_MAX_CHUNKS) {\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'too_many_chunks',\n detail: `chunk total ${total} exceeds the ${MESH_MAX_CHUNKS} cap`,\n chunkId,\n }\n }\n\n let entry = this.buffers.get(chunkId)\n if (!entry) {\n entry = { total, chunks: new Array(total).fill(''), received: 0, bytesReceived: 0, createdAt: this.now() }\n this.buffers.set(chunkId, entry)\n } else if (entry.total !== total) {\n // The sender disagrees with itself about the frame's shape — the stream is\n // corrupt; drop it loudly rather than reassembling a mixed frame.\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'inconsistent_total',\n detail: `chunk ${index} declares total ${total} but the group was opened with ${entry.total}`,\n chunkId,\n }\n }\n\n const existing = entry.chunks[index]\n if (existing) {\n // A benign retransmit repeats identical bytes. Different bytes for the same slot\n // means the ordering/identity guarantee is broken — refuse instead of picking one.\n if (existing === data) return { status: 'partial', received: entry.received, total: entry.total }\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'duplicate_chunk_mismatch',\n detail: `chunk ${index} arrived twice with different content`,\n chunkId,\n }\n }\n\n const chunkBytes = meshUtf8ByteLength(data)\n if (entry.bytesReceived + chunkBytes > MESH_MAX_REASSEMBLED_BYTES) {\n this.buffers.delete(chunkId)\n return {\n status: 'failed',\n reason: 'budget_exceeded',\n detail: `reassembled frame would exceed ${MESH_MAX_REASSEMBLED_BYTES} bytes`,\n chunkId,\n }\n }\n\n entry.chunks[index] = data\n entry.received += 1\n entry.bytesReceived += chunkBytes\n if (entry.received < entry.total) {\n return { status: 'partial', received: entry.received, total: entry.total }\n }\n\n this.buffers.delete(chunkId)\n try {\n return { status: 'complete', frame: JSON.parse(entry.chunks.join('')) }\n } catch (error) {\n // Every slot is filled yet the join is not valid JSON — the frame is unusable.\n // Explicit failure, never a silent drop.\n return {\n status: 'failed',\n reason: 'reassembled_parse_failed',\n detail: `reassembled frame is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,\n chunkId,\n }\n }\n }\n}\n","/**\n * Semver precedence comparison — the single version-ordering primitive for the\n * daemon's upgrade paths.\n *\n * Why this exists: the upgrade path used to decide \"is this a no-op?\" with a\n * raw string equality check (`currentInstalled === latest`) and nothing else.\n * Equality answers \"is it the same version?\" but never \"which one is newer?\",\n * so any target that merely differed from the running build was installed —\n * including one that is OLDER. That is how a node running `1.0.49-rc.2`\n * silently got rolled back to `1.0.48`.\n *\n * String comparison cannot fix it either: `'1.0.49-rc.2' < '1.0.48'` is TRUE\n * lexicographically (the '4' in \"-rc.2\"'s prefix never gets that far — '9' vs\n * '8' at index 4 decides it, and even where it works `rc.10` sorts below\n * `rc.9`). Only field-wise numeric comparison with semver §11 prerelease rules\n * gives the right answer.\n *\n * This module is a PURE LEAF: zero imports, no I/O. It lives in mesh-shared\n * (fragmentation audit) so BOTH the OSS daemon and the proprietary Workers\n * server (`packages/server/src/utils/version-policy.ts`, previously a\n * byte-identical independent copy) can consume ONE implementation — the two\n * gate the same auto-update decision from opposite ends, and a drift between\n * them can produce an unbreakable update-nag loop (server says behind, daemon\n * gate refuses the \"upgrade\" as a rollback). daemon-core re-exports this from\n * its original `version-compare.ts` path, so existing imports keep working.\n *\n * NOTE ON PRERELEASE SEMANTICS: this is STRICT semver §11 — `1.0.49-rc.2` is\n * BELOW `1.0.49`, because a prerelease precedes its own release. That is the\n * correct rule for \"would installing this move me backwards?\", which is the\n * only question this module is used to answer. It deliberately differs from\n * `oss/packages/web-core/src/utils/version-update.ts`, whose\n * `isDaemonBehindTarget` treats an rc as up-to-date against its own base\n * release so the dashboard does not nag preview users with an update banner.\n * Those are two different questions; do not collapse them into one helper.\n */\n\n/** Parsed semver fields. Build metadata (`+…`) is discarded: it never affects precedence (§10). */\nexport interface ParsedSemver {\n readonly major: number;\n readonly minor: number;\n readonly patch: number;\n /** Dot-separated prerelease identifiers; empty for a release build. */\n readonly prerelease: readonly string[];\n}\n\nconst SEMVER_PATTERN = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\nconst NUMERIC_IDENTIFIER = /^\\d+$/;\n\n/**\n * Parse a semver string, tolerating a leading `v` (npm/CLI output carries it\n * inconsistently). Returns null for anything unparsable so callers can fail\n * closed rather than guess.\n */\nexport function parseSemver(version: unknown): ParsedSemver | null {\n if (typeof version !== 'string') return null;\n const match = version.trim().replace(/^v/, '').match(SEMVER_PATTERN);\n if (!match) return null;\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n prerelease: match[4] ? match[4].split('.') : [],\n };\n}\n\n/**\n * Semver §11 prerelease precedence: a release outranks its own prereleases;\n * numeric identifiers compare numerically (so rc.10 > rc.9) and rank below\n * alphanumeric ones; alphanumerics compare lexically; a shorter identifier\n * list ranks below an otherwise-equal longer one.\n */\nfunction comparePrerelease(a: readonly string[], b: readonly string[]): number {\n if (a.length === 0 && b.length === 0) return 0;\n // An empty prerelease list means \"release build\", which outranks any prerelease.\n if (a.length === 0) return 1;\n if (b.length === 0) return -1;\n for (let i = 0; i < Math.min(a.length, b.length); i += 1) {\n const idA = a[i];\n const idB = b[i];\n if (idA === idB) continue;\n const numA = NUMERIC_IDENTIFIER.test(idA);\n const numB = NUMERIC_IDENTIFIER.test(idB);\n if (numA && numB) return Number(idA) < Number(idB) ? -1 : 1;\n if (numA) return -1;\n if (numB) return 1;\n return idA < idB ? -1 : 1;\n }\n return a.length === b.length ? 0 : (a.length < b.length ? -1 : 1);\n}\n\n/**\n * Compare two versions by semver precedence.\n *\n * @returns -1 when `a` precedes `b`, 0 when equal, 1 when `a` succeeds `b`, and\n * **null when either side is unparsable** — callers MUST treat null as\n * \"direction unknown\" and fail closed rather than coercing it to a number\n * (`null` compares as `0` in JS numeric contexts, which would read as \"equal\").\n */\nexport function compareSemver(a: unknown, b: unknown): number | null {\n const pa = parseSemver(a);\n const pb = parseSemver(b);\n if (!pa || !pb) return null;\n if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1;\n if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1;\n if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1;\n return comparePrerelease(pa.prerelease, pb.prerelease);\n}\n\n/**\n * Would installing `target` move a daemon currently on `current` BACKWARDS?\n *\n * Returns false when the direction cannot be established (either version\n * unparsable) — an unknown direction must never block an upgrade, because the\n * cost of a false block (the whole fleet can no longer be upgraded) is far\n * higher than the cost of a missed downgrade guard. Equal versions are NOT a\n * downgrade, so a same-version reinstall stays allowed.\n */\nexport function isDowngrade(current: unknown, target: unknown): boolean {\n const direction = compareSemver(target, current);\n if (direction === null) return false;\n return direction < 0;\n}\n","/**\n * ws-protocol — shared string-literal unions for the daemon↔server WS surface\n * and the daemon↔dashboard P2P DataChannel surface.\n *\n * Fragmentation audit: these message types existed as a TypeScript union in\n * exactly ONE package (the proprietary daemon-cloud's server-connection.ts),\n * which is a leaf CONSUMER — the other two participants (the Workers server\n * and OSS daemon-core, which is the primary `status_report` producer) matched\n * bare string literals by hand. Renaming `auth_ok` server-side would compile\n * everywhere and leave the daemon reconnecting forever. Pure literals, zero\n * runtime deps — the textbook mesh-shared leaf.\n *\n * SCOPE HONESTY: this file declares the OSS-visible protocol surface. The\n * proprietary repo's server-connection.ts remains the authority for the full\n * ServerToDaemon command set; it should adopt these unions and extend them\n * (`ServerToDaemonMsg | <proprietary extras>`) rather than re-declaring the\n * shared members. Members here are the ones OSS daemon-core itself produces\n * or matches.\n */\n\n/** Messages the daemon sends UP to the Workers server over the WS bridge. */\nexport type DaemonToServerWsMsg =\n | 'auth'\n | 'status_report'\n | 'status_heartbeat'\n | 'status_event'\n | 'command_result'\n | 'error'\n | 'agent_event'\n | 'log';\n\n/** Server→daemon control messages the OSS engine reacts to. */\nexport type ServerToDaemonWsMsg =\n | 'auth_ok'\n | 'auth_error'\n | 'machine_evicted'\n | 'force_disconnect'\n | 'token_revoked'\n | 'version_mismatch'\n | 'force_update_required'\n | 'command'\n | 'agent_command'\n | 'resolve_action';\n\n/** P2P signaling relayed through the server WS. */\nexport type P2PSignalingWsMsg =\n | 'p2p_ready'\n | 'offer'\n | 'answer'\n | 'ice'\n | 'mesh_p2p_ready'\n | 'mesh_p2p_offer'\n | 'mesh_p2p_answer'\n | 'mesh_p2p_ice';\n\n/**\n * Dashboard↔daemon P2P DataChannel JSON message kinds. Previously matched as\n * hand-synced literals on both ends with NO shared symbol anywhere —\n * `p2p_evicted` had exactly two occurrences repo-wide (emit + handle).\n */\nexport type DashboardP2PMessageKind =\n | 'ping'\n | 'pong'\n | 'status_report'\n | 'status_event'\n | 'p2p_evicted'\n | 'command'\n | 'command_result'\n | 'command_result_chunk'\n | 'screenshot_start'\n | 'screenshot_stop'\n | 'pty_input'\n | 'pty_resize';\n\nexport const DAEMON_TO_SERVER_WS_MSGS: readonly DaemonToServerWsMsg[] = [\n 'auth', 'status_report', 'status_heartbeat', 'status_event', 'command_result', 'error', 'agent_event', 'log',\n];\n\nexport const SERVER_TO_DAEMON_WS_MSGS: readonly ServerToDaemonWsMsg[] = [\n 'auth_ok', 'auth_error', 'machine_evicted', 'force_disconnect', 'token_revoked',\n 'version_mismatch', 'force_update_required', 'command', 'agent_command', 'resolve_action',\n];\n\nexport function isDaemonToServerWsMsg(value: unknown): value is DaemonToServerWsMsg {\n return typeof value === 'string' && (DAEMON_TO_SERVER_WS_MSGS as readonly string[]).includes(value);\n}\n\nexport function isServerToDaemonWsMsg(value: unknown): value is ServerToDaemonWsMsg {\n return typeof value === 'string' && (SERVER_TO_DAEMON_WS_MSGS as readonly string[]).includes(value);\n}\n"],"mappings":"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","names":[]}
|