@adhdev/mesh-shared 0.9.82-rc.469 → 0.9.82-rc.471

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
@@ -418,6 +418,7 @@ var CANONICAL_MESH_TOOL_NAMES = [
418
418
  "mesh_restart_daemon",
419
419
  "mesh_checkpoint",
420
420
  "mesh_approve",
421
+ "mesh_list_pending_approvals",
421
422
  "mesh_clone_node",
422
423
  "mesh_remove_node",
423
424
  "mesh_refine_node",
@@ -431,10 +432,12 @@ var CANONICAL_MESH_TOOL_NAMES = [
431
432
  "mesh_cleanup_sessions",
432
433
  "mesh_prune_stale_direct",
433
434
  "mesh_task_history",
435
+ "mesh_ledger_query",
434
436
  "mesh_record_note",
435
437
  "mesh_forget_note",
436
438
  "mesh_reconcile_ledger",
437
439
  "mesh_requeue_held_events",
440
+ "mesh_wait_events",
438
441
  "mesh_mission_upsert",
439
442
  "mesh_mission_list",
440
443
  "mesh_review_inbox",
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/workspace-normalize.ts","../src/daemon-normalize.ts","../src/git-summarize.ts","../src/magi.ts","../src/interpolation.ts","../src/mesh-tool-names.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 './workspace-normalize'\nexport * from './daemon-normalize'\nexport * from './git-summarize'\nexport * from './magi'\nexport * from './interpolation'\nexport * from './mesh-tool-names'\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 { 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 ...(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 * 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 * 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: panel definitions\n * (machine-local config, stored in ~/.adhdev/meshes.json `magiPanels`), the\n * agent-agnostic common output schema every dispatched replica answers with,\n * and the synthesis result shapes. These cross the daemon-core (storage /\n * accessors) ↔ mcp-server (fan-out / synthesis) boundary, so they live in the\n * 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 member 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\n// ─── Panel model (machine-local config) ─────────\n\n/**\n * One panel member: a `(node × provider)` target that answers the shared\n * question. `provider` is required; `nodeId` pins a concrete mesh node (forbidden\n * in the repo-portable abstract form), `capabilityTags` route by tag when no\n * nodeId is given. `n` is an optional per-member replica count.\n */\nexport interface MagiPanelMember {\n /** Optional — pin to a specific mesh node id. Absent → route by capabilityTags + provider. */\n nodeId?: string\n /** Optional routing tags (e.g. 'os=darwin'), ANDed with the provider tag when nodeId is absent. */\n capabilityTags?: string[]\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'hermes-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\n * provider manifest's `modelLaunchArgs` template into launch args (a provider\n * with no template silently ignores it — model is best-effort, never fatal).\n */\n model?: string\n /** Optional per-member replica count; defaults to the panel.defaultN / global n / 1. */\n n?: number\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, structurally the same\n * shape as a {@link MagiPanelMember}. `provider` required; `nodeId` pins a concrete\n * mesh node; `model` optionally selects the agent model at launch; `n` is an optional\n * per-slot replica count.\n */\nexport interface MagiSlot {\n /** Optional — pin to a specific mesh node id. */\n nodeId?: string\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'gemini-cli'. */\n provider: string\n /** Optional model override applied at replica launch (see MagiPanelMember.model). */\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 1. */\n n?: number\n}\n\n/**\n * Per-task_kind panel binding, stored machine-local in `~/.adhdev/meshes.json`\n * under the top-level `magiKindPanels` map (sibling to `magiPanels`). A kind absent\n * from the map has NO configured panel → `mesh_magi_review({task_kind})` errors\n * with `magi_kind_not_configured` rather than synthesizing one. `freeform` MAY be\n * bound like any other kind (unlike a named panel's `defaultKind`, this is a direct\n * kind→slots binding, not a panel-level default).\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 member 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 (panel\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, so it is NOT a valid panel `defaultKind` (a panel is a\n * cross-verification tool; a default that zeroes out cross-verification is\n * self-contradictory). Normalization drops/rejects defaultKind === 'freeform'.\n */\nexport type MagiTaskKind = 'claim_audit' | 'rca' | 'design' | 'freeform'\n\n/**\n * The kinds valid as a panel `defaultKind` — every MagiTaskKind EXCEPT 'freeform'\n * (which contributes no structured claims, so it must not be a panel-level default).\n */\nexport type MagiPanelDefaultKind = Exclude<MagiTaskKind, 'freeform'>\n\n/**\n * A named MAGI panel. Stored machine-local in `~/.adhdev/meshes.json` under the\n * top-level `magiPanels` map (sibling to `meshes`), because a member binds\n * concrete node identity + provider availability — both machine-dependent facts.\n */\nexport interface MagiPanel {\n /** Optional human label, e.g. 'design-review'. */\n description?: string\n members: MagiPanelMember[]\n /** Replicas per member when member.n is absent; default 1. */\n defaultN?: number\n /** Marks the panel's fan-out as intentional same-prompt duplication (always true in practice). */\n dedupExempt?: boolean\n /**\n * Optional, NON-binding default output kind for fan-outs invoked through this\n * panel. NOT a first-class panel axis (task_kind is code-orthogonal to the\n * member set) — just a fallback applied when a review omits an explicit\n * task_kind. Resolution priority is strictly\n * `args.task_kind > panel.defaultKind > 'claim_audit'`, so it never changes the\n * schema of an automation that already passes task_kind. 'freeform' is rejected\n * at normalization (see MagiPanelDefaultKind).\n */\n defaultKind?: MagiPanelDefaultKind\n}\n\n/** Top-level `magiPanels` map in meshes.json, keyed by panel name. */\nexport type MagiPanelMap = Record<string, MagiPanel>\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 * 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_view_queue',\n 'mesh_queue_cancel',\n 'mesh_queue_requeue',\n 'mesh_send_task',\n 'mesh_read_chat',\n 'mesh_read_debug',\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_clone_node',\n 'mesh_remove_node',\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_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_panel_set',\n 'mesh_magi_panel_list',\n 'mesh_magi_kind_panel_set',\n 'mesh_magi_kind_panel_list',\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"],"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/workspace-normalize.ts","../src/daemon-normalize.ts","../src/git-summarize.ts","../src/magi.ts","../src/interpolation.ts","../src/mesh-tool-names.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 './workspace-normalize'\nexport * from './daemon-normalize'\nexport * from './git-summarize'\nexport * from './magi'\nexport * from './interpolation'\nexport * from './mesh-tool-names'\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 { 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 ...(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 * 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 * 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: panel definitions\n * (machine-local config, stored in ~/.adhdev/meshes.json `magiPanels`), the\n * agent-agnostic common output schema every dispatched replica answers with,\n * and the synthesis result shapes. These cross the daemon-core (storage /\n * accessors) ↔ mcp-server (fan-out / synthesis) boundary, so they live in the\n * 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 member 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\n// ─── Panel model (machine-local config) ─────────\n\n/**\n * One panel member: a `(node × provider)` target that answers the shared\n * question. `provider` is required; `nodeId` pins a concrete mesh node (forbidden\n * in the repo-portable abstract form), `capabilityTags` route by tag when no\n * nodeId is given. `n` is an optional per-member replica count.\n */\nexport interface MagiPanelMember {\n /** Optional — pin to a specific mesh node id. Absent → route by capabilityTags + provider. */\n nodeId?: string\n /** Optional routing tags (e.g. 'os=darwin'), ANDed with the provider tag when nodeId is absent. */\n capabilityTags?: string[]\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'hermes-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\n * provider manifest's `modelLaunchArgs` template into launch args (a provider\n * with no template silently ignores it — model is best-effort, never fatal).\n */\n model?: string\n /** Optional per-member replica count; defaults to the panel.defaultN / global n / 1. */\n n?: number\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, structurally the same\n * shape as a {@link MagiPanelMember}. `provider` required; `nodeId` pins a concrete\n * mesh node; `model` optionally selects the agent model at launch; `n` is an optional\n * per-slot replica count.\n */\nexport interface MagiSlot {\n /** Optional — pin to a specific mesh node id. */\n nodeId?: string\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'gemini-cli'. */\n provider: string\n /** Optional model override applied at replica launch (see MagiPanelMember.model). */\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 1. */\n n?: number\n}\n\n/**\n * Per-task_kind panel binding, stored machine-local in `~/.adhdev/meshes.json`\n * under the top-level `magiKindPanels` map (sibling to `magiPanels`). A kind absent\n * from the map has NO configured panel → `mesh_magi_review({task_kind})` errors\n * with `magi_kind_not_configured` rather than synthesizing one. `freeform` MAY be\n * bound like any other kind (unlike a named panel's `defaultKind`, this is a direct\n * kind→slots binding, not a panel-level default).\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 member 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 (panel\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, so it is NOT a valid panel `defaultKind` (a panel is a\n * cross-verification tool; a default that zeroes out cross-verification is\n * self-contradictory). Normalization drops/rejects defaultKind === 'freeform'.\n */\nexport type MagiTaskKind = 'claim_audit' | 'rca' | 'design' | 'freeform'\n\n/**\n * The kinds valid as a panel `defaultKind` — every MagiTaskKind EXCEPT 'freeform'\n * (which contributes no structured claims, so it must not be a panel-level default).\n */\nexport type MagiPanelDefaultKind = Exclude<MagiTaskKind, 'freeform'>\n\n/**\n * A named MAGI panel. Stored machine-local in `~/.adhdev/meshes.json` under the\n * top-level `magiPanels` map (sibling to `meshes`), because a member binds\n * concrete node identity + provider availability — both machine-dependent facts.\n */\nexport interface MagiPanel {\n /** Optional human label, e.g. 'design-review'. */\n description?: string\n members: MagiPanelMember[]\n /** Replicas per member when member.n is absent; default 1. */\n defaultN?: number\n /** Marks the panel's fan-out as intentional same-prompt duplication (always true in practice). */\n dedupExempt?: boolean\n /**\n * Optional, NON-binding default output kind for fan-outs invoked through this\n * panel. NOT a first-class panel axis (task_kind is code-orthogonal to the\n * member set) — just a fallback applied when a review omits an explicit\n * task_kind. Resolution priority is strictly\n * `args.task_kind > panel.defaultKind > 'claim_audit'`, so it never changes the\n * schema of an automation that already passes task_kind. 'freeform' is rejected\n * at normalization (see MagiPanelDefaultKind).\n */\n defaultKind?: MagiPanelDefaultKind\n}\n\n/** Top-level `magiPanels` map in meshes.json, keyed by panel name. */\nexport type MagiPanelMap = Record<string, MagiPanel>\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 * 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_view_queue',\n 'mesh_queue_cancel',\n 'mesh_queue_requeue',\n 'mesh_send_task',\n 'mesh_read_chat',\n 'mesh_read_debug',\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_list_pending_approvals',\n 'mesh_clone_node',\n 'mesh_remove_node',\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_wait_events',\n 'mesh_mission_upsert',\n 'mesh_mission_list',\n 'mesh_review_inbox',\n 'mesh_magi_review',\n 'mesh_magi_collect',\n 'mesh_magi_panel_set',\n 'mesh_magi_panel_list',\n 'mesh_magi_kind_panel_set',\n 'mesh_magi_kind_panel_list',\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"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -364,6 +364,7 @@ var CANONICAL_MESH_TOOL_NAMES = [
364
364
  "mesh_restart_daemon",
365
365
  "mesh_checkpoint",
366
366
  "mesh_approve",
367
+ "mesh_list_pending_approvals",
367
368
  "mesh_clone_node",
368
369
  "mesh_remove_node",
369
370
  "mesh_refine_node",
@@ -377,10 +378,12 @@ var CANONICAL_MESH_TOOL_NAMES = [
377
378
  "mesh_cleanup_sessions",
378
379
  "mesh_prune_stale_direct",
379
380
  "mesh_task_history",
381
+ "mesh_ledger_query",
380
382
  "mesh_record_note",
381
383
  "mesh_forget_note",
382
384
  "mesh_reconcile_ledger",
383
385
  "mesh_requeue_held_events",
386
+ "mesh_wait_events",
384
387
  "mesh_mission_upsert",
385
388
  "mesh_mission_list",
386
389
  "mesh_review_inbox",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/json.ts","../src/git-normalize.ts","../src/session-normalize.ts","../src/node-normalize.ts","../src/workspace-normalize.ts","../src/daemon-normalize.ts","../src/git-summarize.ts","../src/magi.ts","../src/interpolation.ts","../src/mesh-tool-names.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 { 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 ...(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 * 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 * 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: panel definitions\n * (machine-local config, stored in ~/.adhdev/meshes.json `magiPanels`), the\n * agent-agnostic common output schema every dispatched replica answers with,\n * and the synthesis result shapes. These cross the daemon-core (storage /\n * accessors) ↔ mcp-server (fan-out / synthesis) boundary, so they live in the\n * 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 member 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\n// ─── Panel model (machine-local config) ─────────\n\n/**\n * One panel member: a `(node × provider)` target that answers the shared\n * question. `provider` is required; `nodeId` pins a concrete mesh node (forbidden\n * in the repo-portable abstract form), `capabilityTags` route by tag when no\n * nodeId is given. `n` is an optional per-member replica count.\n */\nexport interface MagiPanelMember {\n /** Optional — pin to a specific mesh node id. Absent → route by capabilityTags + provider. */\n nodeId?: string\n /** Optional routing tags (e.g. 'os=darwin'), ANDed with the provider tag when nodeId is absent. */\n capabilityTags?: string[]\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'hermes-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\n * provider manifest's `modelLaunchArgs` template into launch args (a provider\n * with no template silently ignores it — model is best-effort, never fatal).\n */\n model?: string\n /** Optional per-member replica count; defaults to the panel.defaultN / global n / 1. */\n n?: number\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, structurally the same\n * shape as a {@link MagiPanelMember}. `provider` required; `nodeId` pins a concrete\n * mesh node; `model` optionally selects the agent model at launch; `n` is an optional\n * per-slot replica count.\n */\nexport interface MagiSlot {\n /** Optional — pin to a specific mesh node id. */\n nodeId?: string\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'gemini-cli'. */\n provider: string\n /** Optional model override applied at replica launch (see MagiPanelMember.model). */\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 1. */\n n?: number\n}\n\n/**\n * Per-task_kind panel binding, stored machine-local in `~/.adhdev/meshes.json`\n * under the top-level `magiKindPanels` map (sibling to `magiPanels`). A kind absent\n * from the map has NO configured panel → `mesh_magi_review({task_kind})` errors\n * with `magi_kind_not_configured` rather than synthesizing one. `freeform` MAY be\n * bound like any other kind (unlike a named panel's `defaultKind`, this is a direct\n * kind→slots binding, not a panel-level default).\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 member 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 (panel\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, so it is NOT a valid panel `defaultKind` (a panel is a\n * cross-verification tool; a default that zeroes out cross-verification is\n * self-contradictory). Normalization drops/rejects defaultKind === 'freeform'.\n */\nexport type MagiTaskKind = 'claim_audit' | 'rca' | 'design' | 'freeform'\n\n/**\n * The kinds valid as a panel `defaultKind` — every MagiTaskKind EXCEPT 'freeform'\n * (which contributes no structured claims, so it must not be a panel-level default).\n */\nexport type MagiPanelDefaultKind = Exclude<MagiTaskKind, 'freeform'>\n\n/**\n * A named MAGI panel. Stored machine-local in `~/.adhdev/meshes.json` under the\n * top-level `magiPanels` map (sibling to `meshes`), because a member binds\n * concrete node identity + provider availability — both machine-dependent facts.\n */\nexport interface MagiPanel {\n /** Optional human label, e.g. 'design-review'. */\n description?: string\n members: MagiPanelMember[]\n /** Replicas per member when member.n is absent; default 1. */\n defaultN?: number\n /** Marks the panel's fan-out as intentional same-prompt duplication (always true in practice). */\n dedupExempt?: boolean\n /**\n * Optional, NON-binding default output kind for fan-outs invoked through this\n * panel. NOT a first-class panel axis (task_kind is code-orthogonal to the\n * member set) — just a fallback applied when a review omits an explicit\n * task_kind. Resolution priority is strictly\n * `args.task_kind > panel.defaultKind > 'claim_audit'`, so it never changes the\n * schema of an automation that already passes task_kind. 'freeform' is rejected\n * at normalization (see MagiPanelDefaultKind).\n */\n defaultKind?: MagiPanelDefaultKind\n}\n\n/** Top-level `magiPanels` map in meshes.json, keyed by panel name. */\nexport type MagiPanelMap = Record<string, MagiPanel>\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 * 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_view_queue',\n 'mesh_queue_cancel',\n 'mesh_queue_requeue',\n 'mesh_send_task',\n 'mesh_read_chat',\n 'mesh_read_debug',\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_clone_node',\n 'mesh_remove_node',\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_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_panel_set',\n 'mesh_magi_panel_list',\n 'mesh_magi_kind_panel_set',\n 'mesh_magi_kind_panel_list',\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"],"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/workspace-normalize.ts","../src/daemon-normalize.ts","../src/git-summarize.ts","../src/magi.ts","../src/interpolation.ts","../src/mesh-tool-names.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 { 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 ...(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 * 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 * 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: panel definitions\n * (machine-local config, stored in ~/.adhdev/meshes.json `magiPanels`), the\n * agent-agnostic common output schema every dispatched replica answers with,\n * and the synthesis result shapes. These cross the daemon-core (storage /\n * accessors) ↔ mcp-server (fan-out / synthesis) boundary, so they live in the\n * 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 member 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\n// ─── Panel model (machine-local config) ─────────\n\n/**\n * One panel member: a `(node × provider)` target that answers the shared\n * question. `provider` is required; `nodeId` pins a concrete mesh node (forbidden\n * in the repo-portable abstract form), `capabilityTags` route by tag when no\n * nodeId is given. `n` is an optional per-member replica count.\n */\nexport interface MagiPanelMember {\n /** Optional — pin to a specific mesh node id. Absent → route by capabilityTags + provider. */\n nodeId?: string\n /** Optional routing tags (e.g. 'os=darwin'), ANDed with the provider tag when nodeId is absent. */\n capabilityTags?: string[]\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'hermes-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\n * provider manifest's `modelLaunchArgs` template into launch args (a provider\n * with no template silently ignores it — model is best-effort, never fatal).\n */\n model?: string\n /** Optional per-member replica count; defaults to the panel.defaultN / global n / 1. */\n n?: number\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, structurally the same\n * shape as a {@link MagiPanelMember}. `provider` required; `nodeId` pins a concrete\n * mesh node; `model` optionally selects the agent model at launch; `n` is an optional\n * per-slot replica count.\n */\nexport interface MagiSlot {\n /** Optional — pin to a specific mesh node id. */\n nodeId?: string\n /** REQUIRED — provider type, e.g. 'claude-cli' | 'codex-cli' | 'gemini-cli'. */\n provider: string\n /** Optional model override applied at replica launch (see MagiPanelMember.model). */\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 1. */\n n?: number\n}\n\n/**\n * Per-task_kind panel binding, stored machine-local in `~/.adhdev/meshes.json`\n * under the top-level `magiKindPanels` map (sibling to `magiPanels`). A kind absent\n * from the map has NO configured panel → `mesh_magi_review({task_kind})` errors\n * with `magi_kind_not_configured` rather than synthesizing one. `freeform` MAY be\n * bound like any other kind (unlike a named panel's `defaultKind`, this is a direct\n * kind→slots binding, not a panel-level default).\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 member 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 (panel\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, so it is NOT a valid panel `defaultKind` (a panel is a\n * cross-verification tool; a default that zeroes out cross-verification is\n * self-contradictory). Normalization drops/rejects defaultKind === 'freeform'.\n */\nexport type MagiTaskKind = 'claim_audit' | 'rca' | 'design' | 'freeform'\n\n/**\n * The kinds valid as a panel `defaultKind` — every MagiTaskKind EXCEPT 'freeform'\n * (which contributes no structured claims, so it must not be a panel-level default).\n */\nexport type MagiPanelDefaultKind = Exclude<MagiTaskKind, 'freeform'>\n\n/**\n * A named MAGI panel. Stored machine-local in `~/.adhdev/meshes.json` under the\n * top-level `magiPanels` map (sibling to `meshes`), because a member binds\n * concrete node identity + provider availability — both machine-dependent facts.\n */\nexport interface MagiPanel {\n /** Optional human label, e.g. 'design-review'. */\n description?: string\n members: MagiPanelMember[]\n /** Replicas per member when member.n is absent; default 1. */\n defaultN?: number\n /** Marks the panel's fan-out as intentional same-prompt duplication (always true in practice). */\n dedupExempt?: boolean\n /**\n * Optional, NON-binding default output kind for fan-outs invoked through this\n * panel. NOT a first-class panel axis (task_kind is code-orthogonal to the\n * member set) — just a fallback applied when a review omits an explicit\n * task_kind. Resolution priority is strictly\n * `args.task_kind > panel.defaultKind > 'claim_audit'`, so it never changes the\n * schema of an automation that already passes task_kind. 'freeform' is rejected\n * at normalization (see MagiPanelDefaultKind).\n */\n defaultKind?: MagiPanelDefaultKind\n}\n\n/** Top-level `magiPanels` map in meshes.json, keyed by panel name. */\nexport type MagiPanelMap = Record<string, MagiPanel>\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 * 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_view_queue',\n 'mesh_queue_cancel',\n 'mesh_queue_requeue',\n 'mesh_send_task',\n 'mesh_read_chat',\n 'mesh_read_debug',\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_list_pending_approvals',\n 'mesh_clone_node',\n 'mesh_remove_node',\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_wait_events',\n 'mesh_mission_upsert',\n 'mesh_mission_list',\n 'mesh_review_inbox',\n 'mesh_magi_review',\n 'mesh_magi_collect',\n 'mesh_magi_panel_set',\n 'mesh_magi_panel_list',\n 'mesh_magi_kind_panel_set',\n 'mesh_magi_kind_panel_list',\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"],"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_view_queue", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_clone_node", "mesh_remove_node", "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_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_panel_set", "mesh_magi_panel_list", "mesh_magi_kind_panel_set", "mesh_magi_kind_panel_list"];
21
+ export declare const CANONICAL_MESH_TOOL_NAMES: readonly ["mesh_status", "mesh_list_nodes", "mesh_enqueue_task", "mesh_view_queue", "mesh_queue_cancel", "mesh_queue_requeue", "mesh_send_task", "mesh_read_chat", "mesh_read_debug", "mesh_launch_session", "mesh_git_status", "mesh_read_node_logs", "mesh_fast_forward_node", "mesh_restart_daemon", "mesh_checkpoint", "mesh_approve", "mesh_list_pending_approvals", "mesh_clone_node", "mesh_remove_node", "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_wait_events", "mesh_mission_upsert", "mesh_mission_list", "mesh_review_inbox", "mesh_magi_review", "mesh_magi_collect", "mesh_magi_panel_set", "mesh_magi_panel_list", "mesh_magi_kind_panel_set", "mesh_magi_kind_panel_list"];
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: 42;
24
+ export declare const CANONICAL_MESH_TOOL_COUNT: 45;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/mesh-shared",
3
- "version": "0.9.82-rc.469",
3
+ "version": "0.9.82-rc.471",
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",
@@ -35,6 +35,7 @@ export const CANONICAL_MESH_TOOL_NAMES = [
35
35
  'mesh_restart_daemon',
36
36
  'mesh_checkpoint',
37
37
  'mesh_approve',
38
+ 'mesh_list_pending_approvals',
38
39
  'mesh_clone_node',
39
40
  'mesh_remove_node',
40
41
  'mesh_refine_node',
@@ -48,10 +49,12 @@ export const CANONICAL_MESH_TOOL_NAMES = [
48
49
  'mesh_cleanup_sessions',
49
50
  'mesh_prune_stale_direct',
50
51
  'mesh_task_history',
52
+ 'mesh_ledger_query',
51
53
  'mesh_record_note',
52
54
  'mesh_forget_note',
53
55
  'mesh_reconcile_ledger',
54
56
  'mesh_requeue_held_events',
57
+ 'mesh_wait_events',
55
58
  'mesh_mission_upsert',
56
59
  'mesh_mission_list',
57
60
  'mesh_review_inbox',