@adhdev/mesh-shared 1.0.56-rc.3 → 1.0.56-rc.4

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 CHANGED
@@ -735,6 +735,10 @@ var CANONICAL_MESH_TOOL_NAMES = [
735
735
  "mesh_enqueue_task",
736
736
  "mesh_enqueue_batch",
737
737
  "mesh_view_queue",
738
+ // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
739
+ "mesh_graph_view",
740
+ "mesh_graph_gate_claim",
741
+ "mesh_graph_gate_release",
738
742
  "mesh_queue_cancel",
739
743
  "mesh_queue_requeue",
740
744
  "mesh_send_task",
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"],"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'\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 [extra: string]: unknown\n }\n [extra: string]: unknown\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 /** 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 * (`cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary`), and its\n * credential lives in the OS keyring, not the stale on-disk token file — see\n * the provenance note in daemon-core `quota/fetchers/antigravity.ts`. It is\n * macOS-only by design; other platforms report `unsupported` rather than guess\n * at a keyring backend 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 */\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_list_nodes',\n 'mesh_enqueue_task',\n 'mesh_enqueue_batch',\n 'mesh_view_queue',\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"],"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"],"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'\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 [extra: string]: unknown\n }\n [extra: string]: unknown\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 /** 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 * (`cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary`), and its\n * credential lives in the OS keyring, not the stale on-disk token file — see\n * the provenance note in daemon-core `quota/fetchers/antigravity.ts`. It is\n * macOS-only by design; other platforms report `unsupported` rather than guess\n * at a keyring backend 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 */\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_list_nodes',\n 'mesh_enqueue_task',\n 'mesh_enqueue_batch',\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_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"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -657,6 +657,10 @@ var CANONICAL_MESH_TOOL_NAMES = [
657
657
  "mesh_enqueue_task",
658
658
  "mesh_enqueue_batch",
659
659
  "mesh_view_queue",
660
+ // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
661
+ "mesh_graph_view",
662
+ "mesh_graph_gate_claim",
663
+ "mesh_graph_gate_release",
660
664
  "mesh_queue_cancel",
661
665
  "mesh_queue_requeue",
662
666
  "mesh_send_task",
@@ -1 +1 @@
1
- {"version":3,"sources":["../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"],"sourcesContent":["/**\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 [extra: string]: unknown\n }\n [extra: string]: unknown\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 /** 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 * (`cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary`), and its\n * credential lives in the OS keyring, not the stale on-disk token file — see\n * the provenance note in daemon-core `quota/fetchers/antigravity.ts`. It is\n * macOS-only by design; other platforms report `unsupported` rather than guess\n * at a keyring backend 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 */\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_list_nodes',\n 'mesh_enqueue_task',\n 'mesh_enqueue_batch',\n 'mesh_view_queue',\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"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../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"],"sourcesContent":["/**\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 [extra: string]: unknown\n }\n [extra: string]: unknown\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 /** 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 * (`cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary`), and its\n * credential lives in the OS keyring, not the stale on-disk token file — see\n * the provenance note in daemon-core `quota/fetchers/antigravity.ts`. It is\n * macOS-only by design; other platforms report `unsupported` rather than guess\n * at a keyring backend 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 */\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_list_nodes',\n 'mesh_enqueue_task',\n 'mesh_enqueue_batch',\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_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"],"mappings":"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","names":[]}
@@ -18,7 +18,7 @@
18
18
  * Order mirrors mcp-server `ALL_MESH_TOOLS` for easy visual diffing, but the consistency
19
19
  * checks are set-based (order-insensitive).
20
20
  */
21
- export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_list_nodes", "mesh_enqueue_task", "mesh_enqueue_batch", "mesh_view_queue", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_read_terminal", "mesh_send_keys", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_answer_question", "mesh_list_pending_approvals", "mesh_plan_onboarding", "mesh_create", "mesh_add_node", "mesh_clone_node", "mesh_remove_node", "mesh_cleanup_worktree_nodes", "mesh_refine_node", "mesh_refine_batch", "mesh_refine_config", "mesh_change_impact_config", "mesh_init", "mesh_reinit", "mesh_write_mesh_json_config", "mesh_refine_plan", "mesh_cleanup_sessions", "mesh_prune_stale_direct", "mesh_task_history", "mesh_ledger_query", "mesh_record_note", "mesh_forget_note", "mesh_reconcile_ledger", "mesh_requeue_held_events", "mesh_mission_upsert", "mesh_mission_list", "mesh_review_inbox", "mesh_magi_review", "mesh_magi_collect", "mesh_magi_kind_panel_set", "mesh_magi_kind_panel_list", "mesh_node_slots_set", "mesh_node_slots_list", "mesh_node_slots_propose", "mesh_coordinator_prompt_append_get", "mesh_coordinator_prompt_append_set"];
21
+ export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_list_nodes", "mesh_enqueue_task", "mesh_enqueue_batch", "mesh_view_queue", "mesh_graph_view", "mesh_graph_gate_claim", "mesh_graph_gate_release", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_read_terminal", "mesh_send_keys", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_answer_question", "mesh_list_pending_approvals", "mesh_plan_onboarding", "mesh_create", "mesh_add_node", "mesh_clone_node", "mesh_remove_node", "mesh_cleanup_worktree_nodes", "mesh_refine_node", "mesh_refine_batch", "mesh_refine_config", "mesh_change_impact_config", "mesh_init", "mesh_reinit", "mesh_write_mesh_json_config", "mesh_refine_plan", "mesh_cleanup_sessions", "mesh_prune_stale_direct", "mesh_task_history", "mesh_ledger_query", "mesh_record_note", "mesh_forget_note", "mesh_reconcile_ledger", "mesh_requeue_held_events", "mesh_mission_upsert", "mesh_mission_list", "mesh_review_inbox", "mesh_magi_review", "mesh_magi_collect", "mesh_magi_kind_panel_set", "mesh_magi_kind_panel_list", "mesh_node_slots_set", "mesh_node_slots_list", "mesh_node_slots_propose", "mesh_coordinator_prompt_append_get", "mesh_coordinator_prompt_append_set"];
22
22
  export type CanonicalMeshToolName = typeof CANONICAL_MESH_TOOL_NAMES[number];
23
23
  /** The count the `NN tools` barrel doc comments and consistency test assert against. */
24
- export declare const CANONICAL_MESH_TOOL_COUNT: 55;
24
+ export declare const CANONICAL_MESH_TOOL_COUNT: 58;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/mesh-shared",
3
- "version": "1.0.56-rc.3",
3
+ "version": "1.0.56-rc.4",
4
4
  "description": "ADHDev mesh-shared — pure mesh/git status normalizers shared by daemon-core and web-core",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,6 +24,10 @@ export const CANONICAL_MESH_TOOL_NAMES = [
24
24
  'mesh_enqueue_task',
25
25
  'mesh_enqueue_batch',
26
26
  'mesh_view_queue',
27
+ // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
28
+ 'mesh_graph_view',
29
+ 'mesh_graph_gate_claim',
30
+ 'mesh_graph_gate_release',
27
31
  'mesh_queue_cancel',
28
32
  'mesh_queue_requeue',
29
33
  'mesh_send_task',