agent-relay-server 0.127.3 → 0.127.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/openapi.json +1 -1
- package/package.json +3 -3
- package/public/assets/{MessageBubble-DnXnkt4a.js → MessageBubble-D2Wzxied.js} +2 -2
- package/public/assets/{MessageBubble-DnXnkt4a.js.map → MessageBubble-D2Wzxied.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-D6CPEpQD.js → PlanGraphPanel-DH_xoWZi.js} +2 -2
- package/public/assets/{PlanGraphPanel-D6CPEpQD.js.map → PlanGraphPanel-DH_xoWZi.js.map} +1 -1
- package/public/assets/{activity-ZPwHcPLK.js → activity-B-ozOwNq.js} +2 -2
- package/public/assets/{activity-ZPwHcPLK.js.map → activity-B-ozOwNq.js.map} +1 -1
- package/public/assets/{agent-profiles-CT0E9LV1.js → agent-profiles-DZgjO25A.js} +2 -2
- package/public/assets/{agent-profiles-CT0E9LV1.js.map → agent-profiles-DZgjO25A.js.map} +1 -1
- package/public/assets/{agent-type-icon-BmBR-9CH.js → agent-type-icon-B9cLaOoz.js} +2 -2
- package/public/assets/{agent-type-icon-BmBR-9CH.js.map → agent-type-icon-B9cLaOoz.js.map} +1 -1
- package/public/assets/agents-mXBLSY6O.js +2 -0
- package/public/assets/{agents-Dt5TncjC.js.map → agents-mXBLSY6O.js.map} +1 -1
- package/public/assets/{analytics-BZtRycyL.js → analytics-5Ize_UL5.js} +3 -3
- package/public/assets/{analytics-BZtRycyL.js.map → analytics-5Ize_UL5.js.map} +1 -1
- package/public/assets/{automation-yzUJbvT2.js → automation-CidMvW2e.js} +2 -2
- package/public/assets/{automation-yzUJbvT2.js.map → automation-CidMvW2e.js.map} +1 -1
- package/public/assets/{badge-BCvp7LK3.js → badge-BkNNvFNa.js} +2 -2
- package/public/assets/{badge-BCvp7LK3.js.map → badge-BkNNvFNa.js.map} +1 -1
- package/public/assets/{branch-state-badge-Cscjqf-E.js → branch-state-badge-DnS40KCb.js} +2 -2
- package/public/assets/{branch-state-badge-Cscjqf-E.js.map → branch-state-badge-DnS40KCb.js.map} +1 -1
- package/public/assets/{button-DyHjsHY3.js → button-DUaC2wSQ.js} +2 -2
- package/public/assets/{button-DyHjsHY3.js.map → button-DUaC2wSQ.js.map} +1 -1
- package/public/assets/{card-BoqYxpVW.js → card-r-yAV4cH.js} +2 -2
- package/public/assets/{card-BoqYxpVW.js.map → card-r-yAV4cH.js.map} +1 -1
- package/public/assets/{channels-DAtOnA6o.js → channels-MPO2FOBv.js} +2 -2
- package/public/assets/{channels-DAtOnA6o.js.map → channels-MPO2FOBv.js.map} +1 -1
- package/public/assets/chat-Ca4ERso5.js +2 -0
- package/public/assets/chat-Ca4ERso5.js.map +1 -0
- package/public/assets/{connectors-B2grC-hl.js → connectors-BbYdtRkW.js} +2 -2
- package/public/assets/{connectors-B2grC-hl.js.map → connectors-BbYdtRkW.js.map} +1 -1
- package/public/assets/copy-button-u463GPZR.js +2 -0
- package/public/assets/{copy-button-C67FUG3E.js.map → copy-button-u463GPZR.js.map} +1 -1
- package/public/assets/{display-aTnwsqQh.js → display-BNI-7rdq.js} +2 -2
- package/public/assets/{display-aTnwsqQh.js.map → display-BNI-7rdq.js.map} +1 -1
- package/public/assets/{dist-D5w04z0l.js → dist-5e_is3Xr.js} +2 -2
- package/public/assets/{dist-D5w04z0l.js.map → dist-5e_is3Xr.js.map} +1 -1
- package/public/assets/{dist-BD4Xc13v.js → dist-ActI2qb5.js} +2 -2
- package/public/assets/{dist-BD4Xc13v.js.map → dist-ActI2qb5.js.map} +1 -1
- package/public/assets/dist-B-FQ0-X3.js +2 -0
- package/public/assets/{dist-G8YBD-wp.js.map → dist-B-FQ0-X3.js.map} +1 -1
- package/public/assets/{dist-f6MW74eX.js → dist-BduURKro.js} +2 -2
- package/public/assets/{dist-f6MW74eX.js.map → dist-BduURKro.js.map} +1 -1
- package/public/assets/{dist-BFDfbfNC.js → dist-Bp0-OBcd.js} +2 -2
- package/public/assets/{dist-BFDfbfNC.js.map → dist-Bp0-OBcd.js.map} +1 -1
- package/public/assets/{dist-C_VOBSaK.js → dist-CJ7e7uqU.js} +2 -2
- package/public/assets/{dist-C_VOBSaK.js.map → dist-CJ7e7uqU.js.map} +1 -1
- package/public/assets/{dist-BNB3fTO-.js → dist-CYzOsD_n.js} +2 -2
- package/public/assets/{dist-BNB3fTO-.js.map → dist-CYzOsD_n.js.map} +1 -1
- package/public/assets/{dist-CQMQ5iku.js → dist-CpcQJrKV.js} +2 -2
- package/public/assets/{dist-CQMQ5iku.js.map → dist-CpcQJrKV.js.map} +1 -1
- package/public/assets/dist-DFgmDjLv.js +2 -0
- package/public/assets/{dist-CHKra2JV.js.map → dist-DFgmDjLv.js.map} +1 -1
- package/public/assets/{dist-DG75bvPp.js → dist-DrpSpMKL.js} +2 -2
- package/public/assets/{dist-DG75bvPp.js.map → dist-DrpSpMKL.js.map} +1 -1
- package/public/assets/dist-DzWoiN2z.js +2 -0
- package/public/assets/{dist-BJKxqIQd.js.map → dist-DzWoiN2z.js.map} +1 -1
- package/public/assets/{es2015-a6fysEVz.js → es2015-DNuudVtQ.js} +2 -2
- package/public/assets/{es2015-a6fysEVz.js.map → es2015-DNuudVtQ.js.map} +1 -1
- package/public/assets/formatted-body-BEI1zP7i.js +3 -0
- package/public/assets/{formatted-body-D5-1aiyB.js.map → formatted-body-BEI1zP7i.js.map} +1 -1
- package/public/assets/{formatted-body-impl-Do5NbJUA.js → formatted-body-impl-CNQ5sqwK.js} +2 -2
- package/public/assets/{formatted-body-impl-Do5NbJUA.js.map → formatted-body-impl-CNQ5sqwK.js.map} +1 -1
- package/public/assets/index-D2Mpzpft.js +26 -0
- package/public/assets/{index-BdNafiPz.js.map → index-D2Mpzpft.js.map} +1 -1
- package/public/assets/{input-Rc4oWv4q.js → input-BvNUEkDd.js} +2 -2
- package/public/assets/{input-Rc4oWv4q.js.map → input-BvNUEkDd.js.map} +1 -1
- package/public/assets/{insights-i6AEZT_a.js → insights-5rkMFRgf.js} +2 -2
- package/public/assets/{insights-i6AEZT_a.js.map → insights-5rkMFRgf.js.map} +1 -1
- package/public/assets/{integrations-9c6IdRU4.js → integrations-BEaqjfP4.js} +2 -2
- package/public/assets/{integrations-9c6IdRU4.js.map → integrations-BEaqjfP4.js.map} +1 -1
- package/public/assets/{lib-BZ6WPg2t.js → lib-BvrbNA2I.js} +2 -2
- package/public/assets/{lib-BZ6WPg2t.js.map → lib-BvrbNA2I.js.map} +1 -1
- package/public/assets/{lucide-react-NXHPIMsX.js → lucide-react-DD_zIava.js} +2 -2
- package/public/assets/{lucide-react-NXHPIMsX.js.map → lucide-react-DD_zIava.js.map} +1 -1
- package/public/assets/maintenance-CEiURaYZ.js +2 -0
- package/public/assets/{maintenance-CJ6wikpg.js.map → maintenance-CEiURaYZ.js.map} +1 -1
- package/public/assets/{managed-agents-JZn0xNJR.js → managed-agents-CetHyuQW.js} +4 -4
- package/public/assets/{managed-agents-JZn0xNJR.js.map → managed-agents-CetHyuQW.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-BsvAkpR4.js → markdown-preview-impl-ZwGYvObh.js} +2 -2
- package/public/assets/{markdown-preview-impl-BsvAkpR4.js.map → markdown-preview-impl-ZwGYvObh.js.map} +1 -1
- package/public/assets/memory-BTm8XzCg.js +2 -0
- package/public/assets/{memory-BE9IwCdC.js.map → memory-BTm8XzCg.js.map} +1 -1
- package/public/assets/{messages-vXgdc3H4.js → messages-fJd17pzv.js} +2 -2
- package/public/assets/{messages-vXgdc3H4.js.map → messages-fJd17pzv.js.map} +1 -1
- package/public/assets/{orchestrators-CribhLg-.js → orchestrators-C5XQ2UYy.js} +2 -2
- package/public/assets/{orchestrators-CribhLg-.js.map → orchestrators-C5XQ2UYy.js.map} +1 -1
- package/public/assets/overview-Bhk-tqKc.js +2 -0
- package/public/assets/{overview-q3O9K9VK.js.map → overview-Bhk-tqKc.js.map} +1 -1
- package/public/assets/{pairs-BNOnd0IE.js → pairs-CBrszSZh.js} +2 -2
- package/public/assets/{pairs-BNOnd0IE.js.map → pairs-CBrszSZh.js.map} +1 -1
- package/public/assets/{projects-iDkxAGBW.js → projects-DXy9JviE.js} +2 -2
- package/public/assets/{projects-iDkxAGBW.js.map → projects-DXy9JviE.js.map} +1 -1
- package/public/assets/prompt-settings-DEQhAt2X.js +2 -0
- package/public/assets/{prompt-settings-Bdz_xCgD.js.map → prompt-settings-DEQhAt2X.js.map} +1 -1
- package/public/assets/{react-dom-8yaD9iJG.js → react-dom-CADlISeR.js} +2 -2
- package/public/assets/{react-dom-8yaD9iJG.js.map → react-dom-CADlISeR.js.map} +1 -1
- package/public/assets/{registry-DPcyGypi.js → registry-DsZILce1.js} +4 -4
- package/public/assets/{registry-DPcyGypi.js.map → registry-DsZILce1.js.map} +1 -1
- package/public/assets/{security-Z3l3u-Pl.js → security-C-wVZm5k.js} +2 -2
- package/public/assets/{security-Z3l3u-Pl.js.map → security-C-wVZm5k.js.map} +1 -1
- package/public/assets/{select-BX_lW49D.js → select-DsEGBLJk.js} +2 -2
- package/public/assets/{select-BX_lW49D.js.map → select-DsEGBLJk.js.map} +1 -1
- package/public/assets/settings-D3_r5IGA.js +6 -0
- package/public/assets/{settings-Y381jZt7.js.map → settings-D3_r5IGA.js.map} +1 -1
- package/public/assets/{store-D-Frk_EC.js → store-DKTb9O0v.js} +5 -5
- package/public/assets/{store-D-Frk_EC.js.map → store-DKTb9O0v.js.map} +1 -1
- package/public/assets/{task-chip-CeujtXYT.js → task-chip-DwdauC08.js} +2 -2
- package/public/assets/{task-chip-CeujtXYT.js.map → task-chip-DwdauC08.js.map} +1 -1
- package/public/assets/{tasks-Di6d0_Ch.js → tasks-DnEzBku4.js} +2 -2
- package/public/assets/{tasks-Di6d0_Ch.js.map → tasks-DnEzBku4.js.map} +1 -1
- package/public/assets/{teams-DJv4l-_l.js → teams-DpAbZ3ye.js} +2 -2
- package/public/assets/{teams-DJv4l-_l.js.map → teams-DpAbZ3ye.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-3zBLpRel.js → terminal-viewer-impl-_IU5o8ya.js} +3 -3
- package/public/assets/{terminal-viewer-impl-3zBLpRel.js.map → terminal-viewer-impl-_IU5o8ya.js.map} +1 -1
- package/public/assets/types-BqeSkWrv.js.map +1 -1
- package/public/assets/{use-keyboard-viewport-cGEmrGE7.js → use-keyboard-viewport-wTh5CR4-.js} +2 -2
- package/public/assets/{use-keyboard-viewport-cGEmrGE7.js.map → use-keyboard-viewport-wTh5CR4-.js.map} +1 -1
- package/public/assets/user-avatar-CZ3XO8ml.js +2 -0
- package/public/assets/{user-avatar-qbo-B3dQ.js.map → user-avatar-CZ3XO8ml.js.map} +1 -1
- package/public/assets/{work-queue-D73-hP0c.js → work-queue-DKcLZWRl.js} +2 -2
- package/public/assets/{work-queue-D73-hP0c.js.map → work-queue-DKcLZWRl.js.map} +1 -1
- package/public/assets/workspaces-Cs2IZZOI.js +3 -0
- package/public/assets/{workspaces-CoDz-iZd.js.map → workspaces-Cs2IZZOI.js.map} +1 -1
- package/public/index.html +24 -24
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/adapters/claude-transcript.ts +52 -3
- package/runner/src/control-server.ts +34 -17
- package/runner/src/runner-core.ts +64 -30
- package/src/db/messages.ts +20 -8
- package/public/assets/agents-Dt5TncjC.js +0 -2
- package/public/assets/chat-DhYX9BDG.js +0 -2
- package/public/assets/chat-DhYX9BDG.js.map +0 -1
- package/public/assets/copy-button-C67FUG3E.js +0 -2
- package/public/assets/dist-BJKxqIQd.js +0 -2
- package/public/assets/dist-CHKra2JV.js +0 -2
- package/public/assets/dist-G8YBD-wp.js +0 -2
- package/public/assets/formatted-body-D5-1aiyB.js +0 -3
- package/public/assets/index-BdNafiPz.js +0 -26
- package/public/assets/maintenance-CJ6wikpg.js +0 -2
- package/public/assets/memory-BE9IwCdC.js +0 -2
- package/public/assets/overview-q3O9K9VK.js +0 -2
- package/public/assets/prompt-settings-Bdz_xCgD.js +0 -2
- package/public/assets/settings-Y381jZt7.js +0 -6
- package/public/assets/user-avatar-qbo-B3dQ.js +0 -2
- package/public/assets/workspaces-CoDz-iZd.js +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-BqeSkWrv.js","names":[],"sources":["../../providers/registry.ts","../../providers/claude.ts","../../providers/codex.ts","../../dashboard/src/lib/provider-display.ts","../../sdk/src/types/guards.ts","../../sdk/src/types/constants.ts","../../sdk/src/types/workspaces.ts","../../sdk/src/types/tokens.ts","../../sdk/src/types/users.ts","../../sdk/src/types/messages.ts","../../sdk/src/types/agents.ts","../../sdk/src/types/tasks.ts"],"sourcesContent":["import type { ProviderManifest } from \"./types.js\";\n\nconst PROVIDER_ID_RE = /^[a-z][a-z0-9-]*$/;\nconst manifests = new Map<string, ProviderManifest>();\n\nexport function registerProvider(manifest: ProviderManifest): void {\n if (!PROVIDER_ID_RE.test(manifest.id)) {\n throw new Error(`invalid provider id: ${manifest.id}`);\n }\n if (!manifest.label.trim()) {\n throw new Error(`provider ${manifest.id} is missing a label`);\n }\n if (!manifest.icon.trim()) {\n throw new Error(`provider ${manifest.id} is missing an icon`);\n }\n if (manifests.has(manifest.id)) {\n throw new Error(`provider already registered: ${manifest.id}`);\n }\n manifests.set(manifest.id, manifest);\n}\n\nexport function getManifest(id: string): ProviderManifest | undefined {\n return manifests.get(id);\n}\n\nexport function getAllManifests(): ProviderManifest[] {\n return [...manifests.values()];\n}\n\nexport function isValidResumeId(providerId: string, id: string): boolean {\n if (!id) return false;\n const manifest = manifests.get(providerId);\n if (!manifest?.resume?.supported) return false;\n const pattern = manifest.resume.idPattern;\n if (!pattern) return true;\n return new RegExp(`^${pattern}$`, \"i\").test(id);\n}\n\nexport function resumeLogExtractionRe(providerId: string): RegExp | undefined {\n const manifest = manifests.get(providerId);\n if (!manifest?.resume?.supported || !manifest.resume.idPattern || !manifest.probe?.command) return undefined;\n return new RegExp(`\\\\b${manifest.probe.command}\\\\s+--resume\\\\s+(${manifest.resume.idPattern})\\\\b`, \"gi\");\n}\n\ntype AccountKeyAliasResolver = (accountKey: string) => string[];\nconst accountKeyAliasResolvers = new Map<string, AccountKeyAliasResolver>();\n\nexport function registerAccountKeyAliases(providerId: string, resolver: AccountKeyAliasResolver): void {\n accountKeyAliasResolvers.set(providerId, resolver);\n}\n\nexport function quotaAccountKeyAliases(providerId: string, accountKey: string): string[] {\n return accountKeyAliasResolvers.get(providerId)?.(accountKey) ?? [];\n}\n\nexport const PROVIDER_MANIFESTS: ReadonlyMap<string, ProviderManifest> = manifests;\n","import { registerProvider } from \"./registry.js\";\nimport type { ProviderManifest, ProviderModelCapabilities, ProviderModelLimits } from \"./types.js\";\n\nconst CLAUDE_LOW_TO_MAX = [\"low\", \"medium\", \"high\", \"max\"];\nconst CLAUDE_LOW_TO_XHIGH_MAX = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\nconst CONTEXT_200K = { value: 200_000, source: \"catalog\", confidence: \"declared\" } as const;\nconst CONTEXT_1M = { value: 1_000_000, source: \"catalog\", confidence: \"declared\" } as const;\nconst CODE_MODEL_CAPABILITIES: ProviderModelCapabilities = {\n modalities: {\n input: { text: true, image: true },\n output: { text: true },\n },\n tools: {\n code: true,\n review: true,\n debug: true,\n refactor: true,\n },\n source: \"catalog\",\n confidence: \"declared\",\n};\nconst quotaWindows = [\n { name: \"five_hour\", unit: \"%\", reset: true },\n { name: \"seven_day\", unit: \"%\", reset: true },\n] as const;\n\nfunction codeModel(limits?: ProviderModelLimits): Pick<NonNullable<ProviderManifest[\"catalog\"][\"models\"][number]>, \"limits\" | \"capabilities\"> {\n return {\n ...(limits ? { limits } : {}),\n capabilities: CODE_MODEL_CAPABILITIES,\n };\n}\n\nexport const claudeProviderManifest = {\n id: \"claude\",\n label: \"Claude\",\n icon: \"🟠\",\n launch: {\n managedPolicyPrompt: {\n alwaysOn: \"append-system-prompt\",\n },\n taskHydration: {\n channel: \"append-system-prompt\",\n },\n },\n catalog: {\n defaultModel: \"sonnet-5\",\n models: [\n { alias: \"fable-5\", label: \"Fable 5\", providerModel: \"claude-fable-5\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", unavailable: \"suspended by export-control directive, 2026-06-12\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"opus-4.8\", label: \"Opus 4.8\", providerModel: \"claude-opus-4-8\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"opus-4.8[1m]\", label: \"Opus 4.8 1M\", providerModel: \"claude-opus-4-8[1m]\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"opus-4.7\", label: \"Opus 4.7\", providerModel: \"claude-opus-4-7\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"opus-4.7[1m]\", label: \"Opus 4.7 1M\", providerModel: \"claude-opus-4-7[1m]\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"opus-4.6\", label: \"Opus 4.6\", providerModel: \"claude-opus-4-6\", efforts: CLAUDE_LOW_TO_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"opus-4.6[1m]\", label: \"Opus 4.6 1M\", providerModel: \"claude-opus-4-6[1m]\", efforts: CLAUDE_LOW_TO_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"sonnet-5\", label: \"Sonnet 5\", providerModel: \"claude-sonnet-5\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"sonnet-5[1m]\", label: \"Sonnet 5 1M\", providerModel: \"claude-sonnet-5[1m]\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"haiku\", label: \"Haiku\", providerModel: \"haiku\", efforts: [], ...codeModel() },\n ],\n },\n quotaWindows: [...quotaWindows],\n capabilityDefaults: {\n lifecycle: {\n managed: true,\n shutdownHard: { when: \"headless\", value: true, otherwise: false },\n restartHard: { when: \"headless\", value: true, otherwise: false },\n semanticStatus: true,\n reconnect: true,\n },\n session: {\n fileRead: true,\n fileWrite: { when: \"approval:not-read-only\", value: true, otherwise: false },\n shell: true,\n shellMode: { open: \"unrestricted\", guarded: \"guarded\", readOnly: \"read-only-guarded\" },\n },\n context: {\n managed: \"headless\",\n compact: { when: \"headless\", value: true, otherwise: false },\n clear: { when: \"headless\", value: true, otherwise: false },\n inject: true,\n resumePreference: [\"native\", \"clear-inject\", \"none\"],\n },\n quota: {\n supported: true,\n source: \"probe\",\n unit: \"%\",\n windows: [...quotaWindows],\n },\n terminal: {\n mode: \"live\",\n condition: \"headless\",\n live: { read: true, write: true },\n },\n liveSession: {\n capture: true,\n inject: true,\n interrupt: true,\n promptEcho: true,\n reasoning: true,\n slashCommands: true,\n },\n },\n slashCommands: [\"/clear\", \"/compact\", \"/model\", \"/help\"],\n resume: { supported: true, idFormat: \"uuid-v4\", idPattern: \"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\" },\n home: { configDir: \".claude\", envPrefix: \"CLAUDE\", configDirEnvVar: \"CLAUDE_CONFIG_DIR\", quotaCredentialFile: \".credentials.json\", authItems: [\".credentials.json\", \"statsig\"], globalInstructionFiles: [\"CLAUDE.md\"] },\n provisioning: {\n pluginManifestPath: \".claude-plugin/plugin.json\",\n bundledPlugin: {\n ref: \"agent-relay-claude-plugin\",\n dir: \"runner/plugins/claude\",\n title: \"Agent Relay Claude plugin\",\n description: \"Bundled Agent Relay Claude plugin, materialized whole through the provisioning registry.\",\n omitFiles: [\"monitors/relay-monitor.provisioned.mjs\"],\n fileOverrides: [{ path: \"monitors/relay-monitor.ts\", sourcePath: \"monitors/relay-monitor.provisioned.mjs\" }],\n },\n bundledHooks: [\n {\n ref: \"agent-relay-rg-r-guard\",\n dir: \"runner/hooks/rg-r-guard\",\n title: \"Agent Relay baseline hook: rg -r guard\",\n description: \"Baseline Agent Relay PreToolUse hook that blocks the ripgrep `rg -r` replace footgun.\",\n baseline: true,\n },\n ],\n },\n upgrade: {\n packages: [\"agent-relay-runner\", \"agent-relay-plugin\"],\n detectPackages: [\"agent-relay-runner\"],\n detectCommands: [\"claude\"],\n },\n probe: {\n command: \"claude\",\n args: [\"--version\"],\n healthCheck: \"claude --version\",\n featureChecks: [{ name: \"rig\", command: \"claude-rig\", homeDir: \".claude-rig\" }],\n },\n quotaPoll: { strategy: \"none\" },\n shim: { kind: \"claude-relay\" },\n} satisfies ProviderManifest;\n\nregisterProvider(claudeProviderManifest);\n","import { registerProvider } from \"./registry.js\";\nimport type { ProviderManifest, ProviderModelCapabilities, ProviderModelLimits } from \"./types.js\";\n\nconst CODEX_REASONING = [\"low\", \"medium\", \"high\", \"xhigh\"];\nconst CONTEXT_200K = { value: 200_000, source: \"catalog\", confidence: \"declared\" } as const;\nconst CODE_MODEL_CAPABILITIES: ProviderModelCapabilities = {\n modalities: {\n input: { text: true, image: true },\n output: { text: true },\n },\n tools: {\n code: true,\n review: true,\n debug: true,\n refactor: true,\n },\n source: \"catalog\",\n confidence: \"declared\",\n};\nconst quotaWindows = [\n { name: \"primary\", unit: \"%\", reset: true },\n { name: \"secondary\", unit: \"%\", reset: true },\n] as const;\n\nfunction codeModel(limits?: ProviderModelLimits): Pick<NonNullable<ProviderManifest[\"catalog\"][\"models\"][number]>, \"limits\" | \"capabilities\"> {\n return {\n ...(limits ? { limits } : {}),\n capabilities: CODE_MODEL_CAPABILITIES,\n };\n}\n\nexport const codexProviderManifest = {\n id: \"codex\",\n label: \"Codex\",\n icon: \"⌨️\",\n launch: {\n managedPolicyPrompt: {\n alwaysOn: \"prompt\",\n },\n taskHydration: {\n channel: \"prompt\",\n },\n },\n catalog: {\n defaultModel: \"gpt-5.5\",\n models: [\n { alias: \"gpt-5.5\", label: \"GPT-5.5\", providerModel: \"gpt-5.5\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.4\", label: \"GPT-5.4\", providerModel: \"gpt-5.4\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.4-mini\", label: \"GPT-5.4 Mini\", providerModel: \"gpt-5.4-mini\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.3-codex\", label: \"GPT-5.3 Codex\", providerModel: \"gpt-5.3-codex\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.3-codex-spark\", label: \"GPT-5.3 Codex Spark\", providerModel: \"gpt-5.3-codex-spark\", efforts: CODEX_REASONING, defaultEffort: \"high\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.2\", label: \"GPT-5.2\", providerModel: \"gpt-5.2\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n ],\n },\n quotaWindows: [...quotaWindows],\n capabilityDefaults: {\n lifecycle: {\n managed: true,\n shutdownHard: true,\n restartHard: true,\n semanticStatus: true,\n reconnect: true,\n },\n session: {\n fileRead: true,\n fileWrite: { when: \"approval:not-read-only\", value: true, otherwise: false },\n shell: { when: \"approval:not-read-only\", value: true, otherwise: false },\n shellMode: { open: \"unrestricted\", guarded: \"guarded\", readOnly: \"none\" },\n },\n context: {\n managed: \"always\",\n compact: true,\n clear: true,\n inject: true,\n resumePreference: [\"native\", \"clear-inject\", \"none\"],\n },\n quota: {\n supported: true,\n source: \"probe\",\n unit: \"%\",\n windows: [...quotaWindows],\n },\n terminal: {\n mode: \"attach\",\n attach: {\n create: true,\n read: true,\n write: true,\n detach: true,\n },\n },\n liveSession: {\n capture: true,\n inject: true,\n interrupt: true,\n promptEcho: true,\n reasoning: true,\n slashCommands: true,\n },\n },\n slashCommands: [\"/compact\", \"/new\"],\n resume: { supported: true, idFormat: \"codex-thread-id\", idPattern: \"[\\\\w-]+\" },\n home: { configDir: \".codex\", envPrefix: \"CODEX\", configDirEnvVar: \"CODEX_HOME\", quotaCredentialFile: \"auth.json\", authItems: [\"auth.json\", \"installation_id\"], globalInstructionFiles: [\"AGENTS.md\"] },\n provisioning: {\n pluginManifestPath: \".codex-plugin/plugin.json\",\n bundledSkills: {\n refPrefix: \"agent-relay-codex-\",\n dir: \"runner/plugins/codex/skills\",\n titlePrefix: \"Agent Relay Codex skill: \",\n description: \"Bundled Agent Relay Codex skill, materialized through the provisioning registry.\",\n entryFile: \"SKILL.md\",\n },\n bundledHooks: [\n {\n ref: \"agent-relay-rg-r-guard\",\n dir: \"runner/hooks/rg-r-guard\",\n title: \"Agent Relay baseline hook: rg -r guard\",\n description: \"Baseline Agent Relay PreToolUse hook that blocks the ripgrep `rg -r` replace footgun.\",\n baseline: true,\n },\n ],\n },\n upgrade: {\n packages: [\"agent-relay-runner\", \"agent-relay-codex\"],\n detectPackages: [\"agent-relay-runner\", \"agent-relay-codex\"],\n detectCommands: [\"codex-relay\", \"agent-relay-runner\"],\n },\n probe: {\n command: \"codex\",\n args: [\"--version\"],\n healthCheck: \"codex --version\",\n },\n quotaPoll: { strategy: \"codex-app-server\" },\n shim: { kind: \"codex-relay\", extraBinPaths: [\"codex/bin/codex\"], cleanLegacyHooks: true },\n} satisfies ProviderManifest;\n\nregisterProvider(codexProviderManifest);\n","import { getAllManifests, getManifest } from 'agent-relay-providers'\nimport type { ProviderManifest } from 'agent-relay-providers'\n\nconst BADGE_PALETTE = [\n 'bg-amber-500/10 text-amber-400 border-amber-500/30',\n 'bg-sky-500/10 text-sky-400 border-sky-500/30',\n 'bg-violet-500/10 text-violet-400 border-violet-500/30',\n 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30',\n 'bg-rose-500/10 text-rose-400 border-rose-500/30',\n]\n\nfunction stableHash(s: string): number {\n let h = 0\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0\n return h\n}\n\nconst badgeClassCache = new Map<string, string>(\n getAllManifests().map((m, i) => [m.id, BADGE_PALETTE[i % BADGE_PALETTE.length]!])\n)\n\nexport function getProviderLabel(id: string): string {\n return getManifest(id)?.label ?? id.charAt(0).toUpperCase() + id.slice(1)\n}\n\nexport function getProviderIcon(id: string): string {\n return getManifest(id)?.icon ?? '🔧'\n}\n\nexport function getProviderBadgeClass(id: string): string {\n const cached = badgeClassCache.get(id)\n if (cached) return cached\n const cls = BADGE_PALETTE[stableHash(id) % BADGE_PALETTE.length]!\n badgeClassCache.set(id, cls)\n return cls\n}\n\nexport function getKnownProviderIds(): string[] {\n return getAllManifests().map((m) => m.id)\n}\n\nexport function getProviderManifest(id: string): ProviderManifest | undefined {\n return getManifest(id)\n}\n","// --- Shared micro-helpers (single source of truth; do not re-declare per file) ---\n\n/** True for a non-null, non-array object. The canonical type guard for the whole repo. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Narrow `unknown` to a non-empty trimmed string, else `undefined`.\n * Settled semantics: whitespace-only is treated as empty (returns `undefined`).\n */\nexport function stringValue(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\n/** Extract a human-readable message from any thrown value. */\nexport function errMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","// --- Relay connection defaults ---\n\n/** Default port the relay server listens on. */\nconst DEFAULT_RELAY_PORT = 4850;\n\n/** Default relay base URL. Loopback spelling settled on `127.0.0.1` (not `localhost`). */\nexport const DEFAULT_RELAY_URL = `http://127.0.0.1:${DEFAULT_RELAY_PORT}`;\n\n// Shared callmux must outlive tokenlean's 90-110s async long-poll window; this\n// matches callmux's documented default instead of the MCP SDK's shorter cap.\nexport const SHARED_CALLMUX_TOOL_CALL_TIMEOUT_MS = 180_000;\n","import type { LandGateRunResult } from \"./land-gates.js\";\n\n/** Workspace modes — runtime tuple + derived type + guard. */\nexport const VALID_WORKSPACE_MODES = [\"isolated\", \"shared\", \"inherit\"] as const;\nexport type WorkspaceMode = (typeof VALID_WORKSPACE_MODES)[number];\nexport function isWorkspaceMode(value: unknown): value is WorkspaceMode {\n return typeof value === \"string\" && (VALID_WORKSPACE_MODES as readonly string[]).includes(value);\n}\n/** Narrow an unknown to a WorkspaceMode, or undefined if it isn't one. */\nexport function normalizeWorkspaceMode(value: unknown): WorkspaceMode | undefined {\n return isWorkspaceMode(value) ? value : undefined;\n}\nexport type WorkspaceStatus = \"active\" | \"ready\" | \"conflict\" | \"review_requested\" | \"merge_planned\" | \"merged\" | \"abandoned\" | \"cleanup_requested\" | \"cleaned\";\n/** Terminal workspace statuses shared across server + dashboard filters. */\nexport const TERMINAL_WORKSPACE_STATUS_VALUES = [\"cleaned\", \"merged\", \"abandoned\"] as const satisfies readonly WorkspaceStatus[];\n\n/** How a workspace's work is integrated back into its base branch. */\nexport type WorkspaceMergeStrategy = \"pr\" | \"rebase-ff\" | \"auto\";\n\n/** Policy words accepted as the user-facing landing strategy (normalised to WorkspaceMergeStrategy). */\nexport type WorkspaceLandingPolicy = \"direct\" | \"pr\";\n\n/**\n * When to auto-merge a PR that was opened by a `pr`-strategy land.\n * `on-green` — merge once all required status checks pass (default for pr lands).\n * `on-approval` — merge once a reviewer has approved.\n * `manual` — open the PR but never auto-merge; the human hits the button.\n */\nexport const AUTO_MERGE_POLICIES = [\"on-green\", \"on-approval\", \"manual\"] as const;\nexport type WorkspaceAutoMergePolicy = (typeof AUTO_MERGE_POLICIES)[number];\n\n/** Instance-wide landing configuration (stored in config-store under namespace \"landing\"). */\nexport interface LandingConfig {\n /** Default strategy for all workspace lands. \"direct\" → \"auto\" (host picks rebase-ff/pr); \"pr\" → always open a PR. */\n strategy: WorkspaceLandingPolicy;\n}\n\nexport interface WorkspaceProbeWorktree {\n path: string;\n branch?: string;\n headSha?: string;\n bare?: boolean;\n detached?: boolean;\n prunable?: boolean;\n locked?: boolean;\n reason?: string;\n}\n\nexport interface WorkspaceProbe {\n path: string;\n isGitRepo: boolean;\n repoRoot?: string;\n branch?: string;\n headSha?: string;\n dirty?: boolean;\n detached?: boolean;\n worktrees?: WorkspaceProbeWorktree[];\n error?: string;\n}\n\ninterface WorkspaceGitCommit {\n sha: string;\n message: string;\n at?: number;\n}\n\n/**\n * Live git state of a workspace worktree, computed on the host by the\n * orchestrator. Used to surface whether a worktree contains actual work\n * (commits ahead of base, dirty files) so the dashboard isn't a black box and\n * so exit-time reconcile can decide cleanup-vs-flag.\n */\nexport interface WorkspaceGitState {\n /** Commits on the agent branch not yet in base (raw `rev-list` count). */\n ahead?: number;\n /** Commits still genuinely unmerged after discounting work already present in\n * base via a squash/cherry-pick (git-cherry '+' count). Equals `ahead` for a\n * normal branch; 0 when the work landed but left `ahead` positive. */\n unmergedAhead?: number;\n /** True when `ahead` > 0 but the work is already in base (squash/cherry-pick\n * merge) — the worktree is effectively merged and safe to clean up. */\n landed?: boolean;\n /** Commits in base not in the agent branch (how far base moved on). */\n behind?: number;\n /** True if the worktree has uncommitted/untracked changes. */\n dirty?: boolean;\n /** Number of dirty (porcelain) entries. */\n dirtyCount?: number;\n /** Most recent commit in the worktree. */\n lastCommit?: WorkspaceGitCommit;\n /** Ref/sha the ahead/behind counts were computed against. */\n baseRef?: string;\n /** Branch currently checked out in the worktree (for recorded-vs-live mismatch). */\n branch?: string;\n /** Set when the worktree path no longer exists on disk. */\n missing?: boolean;\n /** Populated when git interrogation failed. */\n error?: string;\n}\n\n/**\n * Pre-flight check for integrating a workspace's work, computed on the host.\n * Reports whether the merge would be clean, conflict, or is a no-op, and which\n * strategy `auto` would pick — so the dashboard can warn before the user acts.\n */\nexport interface WorkspaceMergePreview {\n /** Strategy `auto` would resolve to given the repo's remote/gh state. */\n strategy: \"pr\" | \"rebase-ff\";\n /** Commits on the agent branch not yet in base (raw count). */\n ahead?: number;\n /** Genuinely-unmerged commits after discounting squash/cherry-pick landings. */\n unmergedAhead?: number;\n /** True when the work already landed in base via squash/cherry-pick. */\n landed?: boolean;\n /** Merged-PR state (ground truth when local git can't see a squash landing because\n * base moved on). Populated only with `checkPr` (a `gh` round-trip). */\n prState?: \"merged\" | \"open\";\n /** True when the branch's PR is merged on the remote. `prMergeSha` is its merge commit,\n * recorded as the landed SHA when the relay finalizes an out-of-band PR merge (#304). */\n prMerged?: boolean;\n prMergeSha?: string;\n /** Subject (PR title) of the merged PR, carried into the `branch.landed` notice so it\n * reads with the landed change's title, not just a bare SHA (#423). */\n prMergeSubject?: string;\n /** URL of the branch's PR, when one was found via `gh`. */\n prUrl?: string; prNumber?: number; reviewDecision?: \"APPROVED\" | \"CHANGES_REQUESTED\" | \"REVIEW_REQUIRED\"; statusCheckRollup?: unknown[]; mergeable?: string; mergeStateStatus?: string;\n /** Commits in base not in the agent branch. */\n behind?: number;\n /** Current branch tip SHA observed by the host. */\n headSha?: string;\n /** Host-local ref used to protect the worker tip while stewardship/merge recovery runs. */\n protectedTipRef?: string;\n /** SHA currently stored in the protected worker-tip ref. */\n protectedTip?: string;\n /** False when the protected tip exists but the branch no longer contains it. */\n protectedTipReachable?: boolean;\n /** True when the host restored a clean reset-to-base branch from the protected tip. */\n protectedTipRestored?: boolean;\n /** Rescue ref written before a protected-tip restore discards the previously checked-out HEAD. */\n protectedTipDiscardedRef?: string;\n /** The HEAD SHA captured in `protectedTipDiscardedRef`. */\n protectedTipDiscardedHead?: string;\n /** Why protected-tip validation/restoration failed. */\n protectedTipError?: string;\n /** Branch ref is gone, but this protected workspace-tip commit can still recover the work. */\n recoverableTip?: string;\n /** Host-local ref that holds recoverableTip. */\n recoverableTipRef?: string;\n /** Uncommitted/untracked entries in the worktree. */\n dirtyCount?: number;\n /** True if the three-way merge of base and the branch would conflict. */\n conflict?: boolean;\n /** True if base can fast-forward to the branch with no rebase needed. */\n cleanFastForward?: boolean;\n /** Whether an `origin` remote is configured. */\n hasRemote?: boolean;\n /** Whether the `gh` CLI is available on the host. */\n ghAvailable?: boolean;\n /** Resolved base branch name the work would land on. */\n baseRef?: string;\n /** Human-readable reason a merge can't proceed (no work, dirty, missing). */\n reason?: string;\n /** True when there is nothing to land: ahead=0 with a clean worktree (branch tree\n * already in base — never diverged, or already landed via squash/cherry-pick/PR).\n * A no-op land resolves the workspace to a terminal state instead of parking it in\n * the steward queue (#230). Distinct from a dirty-worktree refusal, which also sets\n * `reason` but is not a no-op. */\n noop?: boolean;\n /** Set when the worktree path no longer exists. */\n missing?: boolean;\n /** Populated when git interrogation failed. */\n error?: string;\n}\n\n/**\n * Outcome of bringing a DIRTY checked-out base worktree forward after a land advanced\n * its ref by plumbing (#644/#681/#824). The land already succeeded — this reports whether\n * the shared/primary checkout's WORKING FILES caught up to the advanced HEAD, or whether it\n * was left in a MIXED state (HEAD moved, working files didn't) that needs manual\n * reconciliation before anything reads/builds/publishes from that checkout (#824).\n */\nexport interface BaseWorktreeSyncResult {\n /** True only when every landed path in the checkout matches the advanced HEAD — no mixed\n * state. False means the checkout is stranded/mixed and must be surfaced loudly. */\n reconciled: boolean;\n /** Landed paths whose working copy still differs from the advanced HEAD after the\n * best-effort heal. A mixed state: reading/packing from this checkout sees STALE content\n * for these paths (the #824 strand that mis-fed the #823 bootstrap publish). */\n unsyncedPaths?: string[];\n /** Subset of {@link unsyncedPaths} that carried genuine uncommitted human edits — preserved\n * verbatim (never clobbered, #681), so a fix must merge by hand rather than blind-restore. */\n preservedWipPaths?: string[];\n /** Human-readable summary for logs and the operator alert. */\n message?: string;\n}\n\n/** Outcome of a `workspace.merge` command executed on the host. */\nexport interface WorkspaceMergeResult {\n workspaceId?: string;\n /** Strategy actually used. */\n strategy: \"pr\" | \"rebase-ff\";\n /** True when work was landed locally (rebase-ff into base). */\n merged: boolean;\n /** True when the land was a no-op — nothing to merge (ahead=0, clean worktree),\n * resolved to a terminal `merged` status to clear the steward queue (#230). The\n * worktree/branch are reclaimed too when the owner is gone (`deleteBranch`). */\n noop?: boolean;\n /** True when a merge was prevented by conflicts. */\n conflict?: boolean;\n /** PR URL when the pr strategy opened one. */\n prUrl?: string;\n branch?: string;\n baseRef?: string;\n /** SHA of the landed commit — the agent branch's tip, preserved verbatim (#287).\n * This is the SHA reported in the `branch.landed` notice, so it exists on base\n * exactly as the agent produced it (no rebase rewrite). On a clean fast-forward\n * it equals {@link baseSha}; on a no-ff merge it's the merge's second parent. */\n mergedSha?: string;\n /** SHA base points at after the land (#287): equals {@link mergedSha} on a\n * fast-forward, or the merge commit on a no-ff merge when base had advanced. The\n * relay records this as the recycled workspace's new base_sha. Absent on older\n * orchestrators — consumers fall back to {@link mergedSha}. */\n baseSha?: string;\n /** Subject line of the landed commit (git log -1 --format=%s of mergedSha), for\n * the `branch.landed` notification body (#239). Best-effort; absent on older\n * orchestrators or when the subject can't be read. */\n subject?: string;\n branchDeleted?: boolean;\n /** Present when branch deletion was skipped because the branch still carried unlanded work. */\n branchPreservedReason?: string;\n worktreeRemoved?: boolean;\n /** Fresh branch the worktree was recycled onto after a land-and-continue merge\n * (#206) — the relay repoints the workspace row at it. Absent for terminal lands. */\n newBranch?: string;\n /** True when the landed base branch was pushed to its upstream (origin). */\n pushed?: boolean;\n /** Resulting workspace status the relay should record. */\n status: WorkspaceStatus;\n /** Deps re-provisioned after a land-and-continue recycle when the advanced base\n * brought new dependencies (issue #51). Absent when nothing was stale. */\n depsRefresh?: WorkspaceDepsRefreshResult;\n /** True when GitHub auto-merge was successfully armed after PR creation (#305).\n * False when arming was attempted but the repo has auto-merge disabled. */\n autoMergeArmed?: boolean;\n /** True when the PR was opened but auto-merge was NOT armed because the\n * autoMerge policy is \"on-approval\" — a reviewer pipeline arms it later (#305). */\n awaitingApproval?: boolean;\n /** Populated when the merge could not complete. */\n error?: string;\n /** Present when a land advanced a DIRTY checked-out base by ref plumbing (#644). When\n * {@link BaseWorktreeSyncResult.reconciled} is false the shared checkout is in a mixed\n * state the relay surfaces as a loud operator alert (#824) — the land still succeeded. */\n baseWorktreeSync?: BaseWorktreeSyncResult;\n /** #902 — a REQUIRED land gate failed against the candidate merged tree, so the\n * land was aborted WITHOUT advancing the ref (merged:false, status\n * \"review_requested\" — NEVER \"conflict\": gate failures bounce to the worker, never\n * the steward). The relay uploads the full `output` to an artifact and notifies the\n * worker + coordinator with the gate name + `outputTail` + artifact ref. Absent\n * entirely on a repo with no `.agent-relay/land-gates.json`, so that path is\n * byte-identical to today. */\n gateFailure?: LandGateRunResult;\n /** #902 — optional land gates that FAILED but did not block: the land still\n * succeeded (merged:true). The relay surfaces these as warnings (tail + artifact ref)\n * without bouncing the land. */\n gateWarnings?: LandGateRunResult[];\n}\n\n/**\n * Joined steward briefing for one workspace (#208): the row, owner + orchestrator\n * liveness, live git state, recorded-vs-live branch mismatch, any active steward\n * claim, and a recommended next action — so a steward (or release agent) doesn't\n * reconstruct it from ad-hoc shell + API calls.\n */\nexport interface WorkspaceDiagnostics {\n workspaceId: string;\n status: WorkspaceStatus;\n mode: WorkspaceMode;\n repoRoot: string;\n worktreePath?: string;\n recordedBranch?: string;\n liveBranch?: string;\n baseRef?: string;\n branchMismatch?: boolean;\n owner?: { id?: string; status?: string; online: boolean };\n /**\n * The branch agent's coordinator — the owner's `spawnedBy` lineage resolved to a live\n * agent (#640). `id` is null when no coordinator resolves (owner unknown or not spawned\n * by anyone); steward escalation targets it before the configured fallback.\n */\n coordinator?: { id: string | null; online: boolean };\n orchestrator?: { id?: string; online: boolean };\n /** Active claim that auto-merge yields to (#208), if held and unexpired. */\n claim?: { by?: string; purpose?: string; expiresAt?: number };\n /** Live worktree git state (proxied from the host); absent when unavailable. */\n gitState?: WorkspaceGitState;\n /** Why git state is absent (host offline, no worktree, …). */\n gitStateUnavailable?: string;\n recommendation: { action: \"merge\" | \"rebase\" | \"cleanup\" | \"review\" | \"wait\" | \"none\"; confidence: \"high\" | \"medium\" | \"low\"; reason: string };\n}\n\n/** One changed file in a workspace diff against its base. */\nexport interface WorkspaceDiffFile {\n path: string;\n /** Lines added (undefined for binary files). */\n additions?: number;\n /** Lines removed (undefined for binary files). */\n deletions?: number;\n binary?: boolean;\n}\n\n/** Diff of a workspace's committed work against its base, computed on the host. */\nexport interface WorkspaceDiff {\n baseRef?: string;\n ahead?: number;\n files: WorkspaceDiffFile[];\n /** Unified patch text (may be truncated). */\n patch?: string;\n /** True when the patch was capped at the size limit. */\n truncated?: boolean;\n /** Uncommitted entries in the worktree, not part of the committed diff. */\n dirtyCount?: number;\n missing?: boolean;\n error?: string;\n}\n\n/** A worktree found on disk with no matching active workspace row. */\nexport interface WorkspaceOrphan {\n worktreePath: string;\n repoRoot: string;\n /** Orchestrator that discovered this orphan on disk. Use for cleanup routing. */\n orchestratorId?: string;\n /** Relay agent id for the discovering orchestrator. */\n orchestratorAgentId?: string;\n branch?: string;\n /** Base ref used to prove the orphan branch is landed before branch-ref cleanup. */\n baseRef?: string;\n /** Base SHA recorded for the workspace when available. */\n baseSha?: string;\n headSha?: string;\n /** True if a DB row exists for this path but is already terminal (cleaned/merged). */\n hadTerminalRow?: boolean;\n /** Work already merged into base (squash/cherry/PR). Set from the host probe. */\n landed?: boolean;\n /** Raw commits ahead of base (a squash-landed branch still shows >0). */\n ahead?: number;\n /** Commits ahead whose patch isn't already in base — the squash-aware count. */\n unmergedAhead?: number;\n /** Uncommitted working-tree changes in the worktree. */\n dirty?: boolean;\n /**\n * Safe to remove with no loss: clean tree AND (nothing ahead OR already landed).\n * `false` means the worktree holds un-landed work and must be flagged, not reaped.\n * `undefined` means the host couldn't be probed — treat as not safe.\n */\n safeToReap?: boolean;\n}\n\n/** Host-local view of an agent/* branch that may need recovery review. */\nexport interface WorkspaceRecoveryBranch {\n repoRoot: string;\n /** Orchestrator that discovered this branch. Use for branch-discard routing. */\n orchestratorId?: string;\n /** Relay agent id for the discovering orchestrator. */\n orchestratorAgentId?: string;\n branch: string;\n headSha?: string;\n baseRef?: string;\n baseSha?: string;\n ahead?: number;\n unmergedAhead?: number;\n landed?: boolean;\n lastCommit?: WorkspaceGitCommit;\n ageMs?: number;\n /** True when the branch has no unique unlanded work relative to base. */\n safeToDelete?: boolean;\n /** Why safety could not be proven or why work must be reviewed first. */\n preserveReason?: string;\n /** Server-side enrichment when a workspace row still records this branch. */\n workspaceId?: string;\n ownerAgentId?: string;\n ownerStatus?: string;\n cleanupCommandId?: string;\n cleanupPreservedReason?: string;\n preservedByCleanup?: boolean;\n activeWorkspace?: boolean;\n}\n\nexport interface WorkspaceRecoveryBranchList {\n repoRoot?: string;\n branches: WorkspaceRecoveryBranch[];\n scannedRepos?: string[];\n reason?: string;\n}\n\nexport interface WorkspaceRecoveryBranchDiscardResult {\n repoRoot: string;\n branch: string;\n branchDeleted: boolean;\n forced?: boolean;\n preserveReason?: string;\n headSha?: string;\n}\n\n/**\n * Outcome of provisioning node_modules into a freshly created isolated worktree.\n * Git worktrees do not share gitignored/untracked files, so a new worktree starts\n * with no installed deps — typecheck/test/build would see thousands of phantom\n * missing-module errors. We either symlink the source checkout's node_modules\n * (fast, default) or run a package install (when the source has none to borrow).\n */\nexport interface WorkspaceDepsProvision {\n mode: \"symlink\" | \"install\" | \"none\";\n /** Relative dirs (from repo root) whose node_modules were symlinked. */\n linked?: string[];\n /** Relative dirs where a package install ran successfully. */\n installed?: string[];\n packageManager?: string;\n error?: string;\n}\n\n/** Result of symlinking configured untracked files/dirs from main into an isolated worktree. */\nexport interface WorkspaceSymlinkProvision {\n /** Relative paths (from repo root) that were symlinked from the main checkout. */\n linked: string[];\n /** Per-pattern failures, if any (best-effort: a failure never blocks the spawn). */\n errors?: string[];\n}\n\n/** One project dir's outcome in a workspace deps refresh (issue #51). */\nexport interface WorkspaceDepsRefreshDir {\n /** Relative dir from repo root (`.` for root, e.g. `dashboard`). */\n dir: string;\n /** ok — declared deps already present; stale — missing (check-only, not installed);\n * installed — re-installed in isolation; failed — install errored. */\n status: \"ok\" | \"stale\" | \"installed\" | \"failed\";\n /** Declared deps (dependencies + devDependencies) missing from node_modules, capped sample. */\n missing?: string[];\n /** True when the dir's node_modules was a shared symlink replaced with a real install. */\n wasSymlink?: boolean;\n packageManager?: string;\n error?: string;\n}\n\n/**\n * Outcome of refreshing an isolated worktree's deps (issue #51). The default symlink\n * provisioning shares the source checkout's node_modules, so a dep added to the base\n * AFTER worktree creation is missing in the worktree. Refresh promotes each stale dir\n * to a real, isolated install — never mutating the shared source node_modules.\n */\nexport interface WorkspaceDepsRefreshResult {\n /** True when at least one dir was actually (re)installed. */\n refreshed: boolean;\n /** True (check-only) when any dir is stale, or (refresh) when any install left deps missing. */\n stale?: boolean;\n dirs: WorkspaceDepsRefreshDir[];\n error?: string;\n}\n\nexport interface WorkspaceMetadata {\n id?: string;\n mode: WorkspaceMode;\n requestedMode?: WorkspaceMode;\n repoRoot?: string;\n sourceCwd?: string;\n worktreePath?: string;\n branch?: string;\n baseRef?: string;\n baseSha?: string;\n status?: WorkspaceStatus;\n stewardAgentId?: string;\n /** Per-spawn override for autonomous workspace landing. Omit to use instance landing.strategy. */\n landStrategy?: WorkspaceLandingPolicy;\n /** PR auto-merge policy used when landStrategy resolves to `pr`. */\n autoMerge?: WorkspaceAutoMergePolicy;\n /** Optional reviewer handle or policy target for reviewer-gated PR lands. */\n reviewer?: string;\n deps?: WorkspaceDepsProvision;\n /** Configured untracked files/dirs symlinked from main (see WorkspaceConfig.symlinkPaths). */\n symlinks?: WorkspaceSymlinkProvision;\n probe?: WorkspaceProbe;\n}\n\nexport interface WorkspaceRecord {\n id: string;\n repoRoot: string;\n sourceCwd: string;\n worktreePath: string;\n /** Server-enriched project resolution for repoRoot/sourceCwd; not persisted on the workspace row. */\n projectId?: string;\n projectSlug?: string;\n projectTitle?: string;\n branch?: string;\n baseRef?: string;\n baseSha?: string;\n mode: WorkspaceMode;\n requestedMode?: WorkspaceMode;\n status: WorkspaceStatus;\n /** Durable host owner for host-local isolated worktrees. */\n orchestratorId?: string;\n ownerAgentId?: string;\n /** Server-derived from ownerAgentId via the authoritative owner liveness helper. */\n ownerOnline?: boolean;\n ownerPolicyName?: string;\n ownerAutomationRunId?: string;\n stewardAgentId?: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n readyAt?: number;\n cleanedAt?: number;\n}\n","// ──────────────────────────────────────────────────────────────────────────\n// Settings descriptors (epic #380, #382) — the self-describing contract any\n// surface (core relay, provider, channel, connector) registers so the API\n// (#383), scopes (#385), module wiring (#386), and UI (#384) all build on one\n// abstraction. This is the *declaration* layer over the existing `config`\n// table — NOT a storage change. The registry home is `src/settings/registry.ts`.\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * A typed JSON Schema (draft-07-compatible). Common keywords are spelled out so\n * descriptors get type-checked field shapes; the `[key: string]: unknown` tail\n * keeps the door open for any keyword `ajv` understands (the registry compiles\n * each schema with `ajv`, so anything ajv accepts is valid). Declared here (not\n * as `Record<string, unknown>`) so the descriptor's `schema` field is typed —\n * the sdk stays dependency-free; only the server imports `ajv` to compile it.\n */\nexport type JSONSchemaTypeName =\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"object\"\n | \"array\"\n | \"null\";\n\nexport interface JSONSchema {\n $schema?: string;\n $id?: string;\n $ref?: string;\n $defs?: Record<string, JSONSchema>;\n definitions?: Record<string, JSONSchema>;\n title?: string;\n description?: string;\n type?: JSONSchemaTypeName | JSONSchemaTypeName[];\n enum?: unknown[];\n const?: unknown;\n default?: unknown;\n examples?: unknown[];\n // numeric\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n multipleOf?: number;\n // string\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n // array\n items?: JSONSchema | JSONSchema[];\n minItems?: number;\n maxItems?: number;\n uniqueItems?: boolean;\n // object\n properties?: Record<string, JSONSchema>;\n required?: string[];\n additionalProperties?: boolean | JSONSchema;\n patternProperties?: Record<string, JSONSchema>;\n // composition\n allOf?: JSONSchema[];\n anyOf?: JSONSchema[];\n oneOf?: JSONSchema[];\n not?: JSONSchema;\n // escape hatch for any other JSON Schema keyword\n [key: string]: unknown;\n}\n\n/**\n * Where a setting lives and how it can be changed:\n * - `server-runtime` — instance-global; stored in the `config` table, mutable at runtime via the API.\n * - `bootstrap` — sourced from env/file at boot; surfaced read-only (the API exposes it but cannot write it).\n * - `browser` — a client-only preference; the server never persists it (UI/local-storage owns it).\n * - `user` — per-human preference (#391); stored in `user_settings` keyed by the resolved `users.id`,\n * mutable at runtime. The same `(namespace, key)` descriptor address resolves to a per-user value,\n * so a pref follows the human across devices. Defaults to the seeded `user:default` (#393) until\n * sessions (#389) map a request to its user.\n */\nexport type SettingTier = \"server-runtime\" | \"bootstrap\" | \"browser\" | \"user\";\n\n/**\n * The scopes (#385) a caller must hold to read or write a setting. Carried as\n * opaque scope-token strings — #385 owns the grammar and `hasScope` matching;\n * this layer only declares the requirement. Registration defaults both to the\n * fail-closed admin scope when a descriptor omits them.\n */\nexport interface SettingScope {\n read: string;\n write: string;\n}\n\n/** Display metadata for the Settings UI (#384) — grouping, labelling, ordering. */\nexport interface SettingDisplay {\n /** Grouping bucket the UI renders this setting under (e.g. \"Agents\", \"Notifications\"). */\n group: string;\n /** Human-readable label. */\n label: string;\n /** Optional longer description / help text. */\n description?: string;\n /** Sort order within the group (ascending; unset sorts last). */\n order?: number;\n}\n\n/**\n * A self-describing setting. Any surface registers one of these; the registry is\n * the single source of truth for which namespaces exist, their schema, defaults,\n * required scope, tier, and display. `scope` is always populated on a registered\n * descriptor (the registry fills the fail-closed admin default) so consumers\n * never null-check it. `namespace`/`key` map directly onto the `config` table's\n * composite PK.\n */\nexport interface SettingDescriptor<T = unknown> {\n /** Maps onto the `config` table namespace (e.g. \"steward\", \"landing\"). */\n namespace: string;\n /** Maps onto the `config` table key within the namespace (e.g. \"default\"). */\n key: string;\n /** Full JSON Schema for the value — ajv-compiled at registration (#382/#383). */\n schema: JSONSchema;\n /** Fallback value used when the config row is unset. */\n defaults: T;\n /** Read/write scopes required to access this setting (#385). */\n scope: SettingScope;\n /** Mutability/source tier. */\n tier: SettingTier;\n /** Display metadata for the Settings UI (#384). */\n display: SettingDisplay;\n /**\n * For a `type:\"object\"` value keyed by arbitrary caller-chosen ids (a per-conversation\n * map, not a fixed set of fields): a PUT merges the body's top-level keys into the\n * stored value instead of replacing it wholesale, so one client's write can never\n * clobber another client's write to a different key (#1124). The merged result is\n * capped at the schema's `maxProperties` (oldest-`updatedAt`-first eviction) so\n * unbounded per-key merges from many callers can't grow the row forever.\n */\n mergeMapKeys?: boolean;\n}\n\n/** Runtime settings API entry. `version: 0` means descriptor defaults only; no config row is stored. */\nexport interface SettingEntry<T = unknown> extends SettingDescriptor<T> {\n value: T;\n version: number;\n updatedAt: string;\n updatedBy?: string;\n readable: boolean;\n writable: boolean;\n}\n\nconst SCOPE_ALIASES: Record<string, string> = {\n \"admin:*\": \"system:admin\",\n \"system:write\": \"system:admin\",\n \"agents:read\": \"agent:read\",\n \"agents:write\": \"agent:write\",\n \"messages:read\": \"message:read\",\n \"messages:write\": \"message:send\",\n \"tasks:read\": \"task:read\",\n \"tasks:write\": \"task:write\",\n \"commands:read\": \"command:read\",\n \"commands:write\": \"command:write\",\n \"channels:read\": \"channel:read\",\n \"channels:write\": \"channel:write\",\n \"integrations:read\": \"integration:read\",\n \"integrations:write\": \"integration:write\",\n \"connectors:read\": \"integration:read\",\n \"connectors:write\": \"integration:write\",\n \"events:create\": \"integration:write\",\n \"tasks:create\": \"task:write\",\n};\n\nexport function normalizeScope(scope: string): string {\n return SCOPE_ALIASES[scope] ?? scope;\n}\n\nexport function hasScope(scopes: readonly string[] | undefined, requiredScope: string): boolean {\n const required = normalizeScope(requiredScope);\n const requiredParts = parseScopeParts(required);\n if (!requiredParts) return false;\n if (scopes?.includes(\"*\")) return true;\n for (const rawScope of scopes ?? []) {\n const scope = normalizeScope(rawScope);\n const scopeParts = parseScopeParts(scope);\n if (!scopeParts) continue;\n if (scope === \"system:admin\") return true;\n if (scope === required) return true;\n if (scope === `${requiredParts.category}:*`) return true;\n if (\n requiredParts.category === \"settings\" &&\n requiredParts.action &&\n requiredParts.namespace &&\n scopeParts.category === \"settings\" &&\n scopeParts.action === requiredParts.action &&\n scopeParts.namespace === undefined\n ) return true;\n }\n return false;\n}\n\nfunction parseScopeParts(scope: string): { category: string; action?: string; namespace?: string } | null {\n if (scope === \"*\") return { category: \"*\" };\n const parts = scope.split(\":\");\n if (parts.some((part) => !part)) return null;\n if (parts.length === 2) {\n const [category, action] = parts as [string, string];\n return { category, action };\n }\n if (parts.length === 3) {\n const [category, action, namespace] = parts as [string, string, string];\n if (category !== \"settings\") return null;\n if (action !== \"read\" && action !== \"write\") return null;\n if (namespace === \"*\") return null;\n return { category, action, namespace };\n }\n return null;\n}\n\nexport type PairStatus = \"pending\" | \"active\" | \"ended\" | \"rejected\" | \"expired\";\n\nexport interface PairSession {\n id: string;\n requesterId: string;\n targetId: string;\n status: PairStatus;\n objective?: string;\n createdAt: number;\n updatedAt: number;\n expiresAt: number;\n acceptedAt?: number;\n endedAt?: number;\n endedBy?: string;\n lastMessageAt?: number;\n meta: Record<string, unknown>;\n}\n\nexport interface CreatePairInput {\n from: string;\n target: string;\n objective?: string;\n ttlMs?: number;\n meta?: Record<string, unknown>;\n}\n\nexport interface PairActionInput {\n agentId: string;\n reason?: string;\n}\n\nexport interface PairMessageInput {\n from: string;\n body: string;\n subject?: string;\n}\n\nexport interface PollQuery {\n for: string; // agent-id\n since?: number; // unix ms (createdAt cursor)\n sinceId?: number; // monotonic message id cursor (preferred — avoids same-ms collisions)\n unread?: boolean;\n channel?: string;\n limit?: number;\n}\n\nexport interface ComponentToken {\n sub: string;\n role: \"provider\" | \"channel\" | \"orchestrator\" | \"admin\" | \"dashboard\" | string;\n scope: string[];\n constraints?: TokenConstraints;\n iat: number;\n exp?: number;\n jti?: string;\n}\n\nexport interface TokenRecord {\n jti: string;\n sub: string;\n role: string;\n scope: string[];\n constraints?: TokenConstraints;\n profileId?: string;\n issuedAt: number;\n expiresAt?: number;\n revokedAt?: number;\n /** Why the token was revoked (#484): \"stale\" | \"rotation\" | \"admin\" | \"agent-removed\" | \"pruned\". Drives self-heal eligibility. */\n revokedReason?: string;\n createdBy?: string;\n}\n\nexport interface TokenProfile {\n id: string;\n name: string;\n description?: string;\n role: string;\n scope: string[];\n constraints?: TokenConstraints;\n ttlSeconds?: number;\n builtIn: boolean;\n createdAt: number;\n updatedAt: number;\n createdBy?: string;\n}\n\nexport interface CreateTokenProfileInput {\n id?: string;\n name: string;\n description?: string;\n role: string;\n scope: string[];\n constraints?: TokenConstraints;\n ttlSeconds?: number;\n createdBy?: string;\n}\n\nexport type UpdateTokenProfileInput = Partial<Omit<CreateTokenProfileInput, \"id\">>;\n\nexport interface TokenConstraints {\n agents?: string[];\n policies?: string[];\n parentAgents?: string[];\n /** Parent agent id stamped on a child's runner token at spawn; read at registration to\n * set the child agent card's `spawnedBy` authoritatively (the child can't forge it). */\n spawnedBy?: string;\n /** Durable team id stamped on runner tokens; identity-only grouping, never an authorization gate. */\n teamId?: string;\n /** Durable project id resolved from the spawn cwd (containment match against project_roots)\n * and stamped on runner tokens; identity-only grouping (callerProject reads it), never an\n * authorization gate. Absent when the cwd is inside no registered root (#405). */\n projectId?: string;\n /** Resolved agent-profile NAME stamped on a runner token at spawn (#559). Identity-only,\n * like projectId — it lets the scoped-memory tools resolve the caller's OWN profile store\n * (self-scoped writes: an agent writes only its own profile's memory, never another's).\n * Absent for non-spawned/human tokens. */\n profile?: string;\n /** Tenant id baked into a human session token at login (#389), copied from the user's\n * `users.tenant_id`. Identity-only today: it carries the SaaS multi-tenant seam into the token\n * so isolation becomes an additive change later, but enforces NOTHING — no auth path branches on\n * it yet. Absent for single-tenant users with a null `tenant_id`. */\n tenantId?: string;\n /** Spawn quota (max concurrent live children) baked into a spawn-capable agent's token,\n * resolved from its profile's `maxSpawnedAgents`. The runtime quota check reads it here. */\n maxSpawnedAgents?: number;\n targets?: string[];\n channels?: string[];\n orchestrators?: string[];\n hosts?: string[];\n cwd?: string;\n cwdPrefixes?: string[];\n taskIds?: string[];\n memoryScopes?: string[];\n integrationNames?: string[];\n spawnRequestIds?: string[];\n terminalAttach?: boolean;\n terminalRead?: boolean;\n terminalWrite?: boolean;\n logsRead?: boolean;\n canDelegate?: boolean;\n /** Relay tool group filter (#551): only tools in these groups are listed at tools/list. Absent = full surface. */\n toolGroups?: string[];\n}\n\ntype TokenScope =\n | \"system:admin\"\n | \"token:read\"\n | \"token:write\"\n | \"agent:read\"\n | \"agent:write\"\n | \"message:read\"\n | \"message:send\"\n | \"schedule:write\"\n | \"schedule:persistent\"\n | \"task:read\"\n | \"task:write\"\n | \"command:read\"\n | \"command:write\"\n | \"command:*\"\n | \"artifact:read\"\n | \"artifact:write\"\n | \"artifact:admin\"\n | \"memory:read\"\n | \"memory:write\"\n | \"memory:admin\"\n | \"mcp:use\"\n | \"teams:read\"\n | \"teams:write\"\n | \"terminal:attach\"\n | \"terminal:read\"\n | \"terminal:write\"\n | \"logs:read\"\n | \"integration:read\"\n | \"integration:write\"\n | \"channel:read\"\n | \"channel:write\"\n | \"stats:read\"\n | \"health:read\"\n | \"events:read\"\n | \"command:spawn\"\n | \"command:shutdown\"\n | \"recipe:start\"\n | \"recipe:stop\"\n | \"admin:*\";\n\nexport interface ConfigEntry<T = unknown> {\n namespace: string;\n key: string;\n value: T;\n version: number;\n updatedAt: string;\n updatedBy?: string;\n}\n\nexport interface ConfigHistoryEntry<T = unknown> {\n id: number;\n namespace: string;\n key: string;\n value: T;\n version: number;\n changedAt: string;\n changedBy?: string;\n}\n\nexport interface MaintenanceJob {\n id: string;\n title: string;\n description?: string;\n intervalMs: number;\n timeoutMs: number;\n enabled: boolean;\n runOnStart: boolean;\n lastRunAt?: number;\n nextRunAt?: number;\n lastDurationMs?: number;\n lastStatus: \"idle\" | \"running\" | \"succeeded\" | \"failed\" | \"disabled\";\n lastError?: string;\n lastResult?: Record<string, unknown>;\n consecutiveFailures: number;\n running: boolean;\n leaseUntil?: number;\n updatedAt: number;\n}\n\nexport interface MaintenanceJobRun {\n id: string;\n status: \"succeeded\" | \"failed\" | \"skipped\";\n startedAt: number;\n finishedAt: number;\n durationMs: number;\n result?: Record<string, unknown>;\n error?: string;\n}\n\nexport interface HealthCheck {\n name: string;\n status: \"ok\" | \"warn\" | \"error\";\n detail?: string;\n count?: number;\n metrics?: Record<string, number>;\n subjects?: Array<{ id: string; label?: string; status?: string; detail?: string }>;\n}\n\nexport interface HealthReport {\n status: \"ok\" | \"degraded\" | \"error\";\n version: string;\n generatedAt: number;\n checks: HealthCheck[];\n}\n","// First-class human users (#388 / keystone #393). Promotes the human from the hardcoded\n// `'user'` sink to a real identity record. Addressing is per-user (`user:<id>`); the bare\n// `'user'` sink is the back-compat alias of the seeded `user:default` admin.\n//\n// SINGLE RESOLVER HOME (duplication ratchet): the `'user'` ≡ `user:default` ≡\n// `users.id='default'` mapping lives ONLY here. Every funnel site and every later #388 wave\n// calls these helpers rather than re-deriving the namespace — one place owns human addressing.\n\n/** Addressing prefix for the per-user human namespace: `user:<id>`. */\nexport const USER_NAMESPACE_PREFIX = \"user:\";\n\n/** Stable relational id of the admin user seeded on first boot, mapped to today's single human. */\nexport const DEFAULT_USER_ID = \"default\";\n\n/** Legacy bare human sink, retained as a back-compat alias of `user:default`. */\nexport const LEGACY_HUMAN_AGENT_ID = \"user\";\n\n/** Canonical addressing form for a human user: `user:<id>`. */\nexport function userSinkId(userId: string): string {\n return `${USER_NAMESPACE_PREFIX}${userId}`;\n}\n\n/**\n * True for any human address: the legacy bare `'user'` sink or any `user:<id>` in the\n * namespace. The human stopped being a singleton in #393 — prefer this over a literal\n * `id === 'user'` comparison so per-user sinks are recognized everywhere the human is.\n */\nexport function isHumanAgentId(id: string | undefined): boolean {\n return id === LEGACY_HUMAN_AGENT_ID || (typeof id === \"string\" && id.startsWith(USER_NAMESPACE_PREFIX));\n}\n\n/**\n * The relational `users.id` behind any human address, or `undefined` for a non-human id.\n * Both the legacy bare `'user'` and the `user:default` alias resolve to {@link DEFAULT_USER_ID};\n * `user:<id>` yields `<id>`. This is the bridge from a wire/message address to a `users` row.\n */\nexport function userIdFromAddress(id: string | undefined): string | undefined {\n if (id === LEGACY_HUMAN_AGENT_ID) return DEFAULT_USER_ID;\n if (typeof id === \"string\" && id.startsWith(USER_NAMESPACE_PREFIX)) {\n return id.slice(USER_NAMESPACE_PREFIX.length) || DEFAULT_USER_ID;\n }\n return undefined;\n}\n\n/**\n * The stored/canonical wire id for a human address, used for routing + storage so legacy bytes\n * never move (#393 Option A). The default user's canonical stored id is the legacy bare `'user'`,\n * so the working `user:default` alias collapses to it and matches every pre-existing\n * message/`operator_id`/inbox row. Every other `user:<id>` is already canonical. Non-human ids\n * pass through unchanged.\n */\nexport function canonicalHumanAgentId(id: string): string {\n // The degenerate empty address `user:` collapses to the default sink too, so it never lands\n // between resolvers: `userIdFromAddress('user:')` already yields DEFAULT_USER_ID, and without\n // this `canonicalHumanAgentId('user:')` returned `'user:'` — one helper said \"default human\",\n // the other routed to a `user:` agent a provider could squat (#393). Both now agree.\n if (id === LEGACY_HUMAN_AGENT_ID || id === userSinkId(DEFAULT_USER_ID) || id === USER_NAMESPACE_PREFIX) {\n return LEGACY_HUMAN_AGENT_ID;\n }\n return id;\n}\n\n/** Role gate for a user. Enforcement (ownership/authz) lands in #392 — this carries the value. */\nexport type UserRole = \"admin\" | \"operator\" | \"viewer\";\n\n/** Account lifecycle. `active` is the only state a fresh/seeded user takes today. */\nexport type UserStatus = \"active\" | \"suspended\" | \"deleted\";\n\nexport interface User {\n /** Stable id; the addressing form is `user:<id>` (see {@link userSinkId}). Others FK to this. */\n id: string;\n /** Short login-ish handle, unique within a tenant. */\n handle: string;\n displayName: string;\n role: UserRole;\n status: UserStatus;\n /**\n * SaaS multi-tenant seam (#393): nullable, defaults to the seeded `tenant:default`. The relay\n * is single-tenant today; the column existing now makes multi-tenant an additive migration\n * rather than a second identity refactor. Do NOT key isolation on it yet.\n */\n tenantId: string | null;\n /**\n * Content-addressed avatar via the existing artifact stack (#395/#630): nullable FK →\n * `artifacts.id`. This sub-issue adds the column only — upload/serve is out of scope.\n */\n avatarArtifactId: string | null;\n /** Authenticated API-relative image path for the current avatar, or null when unset. */\n avatarUrl: string | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CreateUserInput {\n id: string;\n handle: string;\n displayName: string;\n role?: UserRole;\n status?: UserStatus;\n tenantId?: string | null;\n avatarArtifactId?: string | null;\n}\n","import type { AttachmentRef } from \"./artifacts.js\";\nimport { isRecord } from \"./guards.js\";\nimport { isHumanAgentId } from \"./users.js\";\n\nexport type MessageKind =\n | \"chat\"\n | \"channel.event\"\n | \"task\"\n | \"pair\"\n | \"control\"\n | \"system\"\n | \"session\";\n\n/**\n * Mechanical message kinds: the relay's own lifecycle/observability lane, not\n * agent-directed messaging. They bypass delivery resolution (never re-delivered\n * into a session) and — when targeting a reserved sink — the recipient-constraint\n * auth check (a managed token's `targets`/`policies`/`agents` constraints gate which\n * *agents* it may message, not a session-mirror capture to the reserved sink; #284).\n * Keep in sync with the `MessageKind` union.\n */\nexport const MECHANICAL_MESSAGE_KINDS: readonly MessageKind[] = [\"system\", \"control\", \"session\"];\n\nexport function isMechanicalMessageKind(kind: string | undefined): boolean {\n return MECHANICAL_MESSAGE_KINDS.includes((kind ?? \"\") as MessageKind);\n}\n\n/**\n * Reserved built-in identities that are not real agents: the human chat/mirror sink(s)\n * and the `system` lifecycle sender. They are never registered, polled, or delivered to\n * like agents, so recipient constraints don't apply to mechanical posts addressed to them.\n *\n * The human is no longer a singleton (#393): the bare `'user'` sink is the legacy alias of\n * the seeded default user, and every per-user sink lives in the `user:` namespace\n * (`user:<id>`). Use {@link isHumanAgentId} / {@link isReservedAgentId} rather than comparing\n * against this list, which only carries the bare built-in ids. All `user:`-namespace\n * addressing lives in one home — see `./users.js`.\n */\nexport const RESERVED_AGENT_IDS: readonly string[] = [\"user\", \"system\"];\n\nexport function isReservedAgentId(id: string | undefined): boolean {\n return id === \"system\" || isHumanAgentId(id);\n}\n\n/**\n * Session-mirror event taxonomy. Every `kind: \"session\"` message carries a\n * `payload.session` of this shape so the dashboard can render the live provider\n * session faithfully regardless of which surface (chat box or web terminal)\n * started the turn. `prompt`/`response` render as chat bubbles; `narration` is\n * the agent's intermediate spoken text between tool calls (the terminal's `●`\n * lines) and renders inline in the turn's activity trace; `reasoning`, `tool`,\n * `notice`, and `relay-injection` render discreetly (collapsed/inline activity,\n * never bubbles).\n * A legacy session message with no `payload.session` is treated as a `response`.\n */\ntype SessionEventType = \"prompt\" | \"response\" | \"narration\" | \"reasoning\" | \"tool\" | \"notice\" | \"relay-injection\" | \"prompt-resolution\";\n\ntype SessionEventOrigin = \"chat\" | \"terminal\" | \"provider\";\nexport type MessageTurnEndReason = \"completed\" | \"interrupted\" | \"failed\";\nexport type MessageFinalityConfidence = \"definitive\" | \"reconstructed\";\nexport type MessageWorkFinalitySource = \"workspace-ready\" | \"task-deliver\" | \"task-done\" | \"explicit-reply\" | \"report-final\" | \"edge-fallback\";\nexport type RelayInjectionCategory = \"memory\" | \"instructions\" | \"tools\" | \"comms\" | \"knowledge\" | \"context\";\n\nexport interface RelayInjectionEventDetail {\n category: RelayInjectionCategory;\n summary: string;\n detail: Record<string, unknown>;\n agentId: string;\n timestamp: number;\n}\n\nexport interface PromptResolutionEventDetail {\n provider: \"claude\";\n kind: \"questions\" | \"plan\" | \"command\" | \"tool\";\n title: string;\n body: string;\n promptBody?: string;\n toolName: string;\n hookEventName: string;\n approvalId: string;\n decision: string;\n decisionLabel: string;\n questions?: unknown[];\n answers?: Record<string, string>;\n}\n\nexport interface MessageSessionMeta {\n type: SessionEventType;\n /** Where the event originated. `terminal` = typed into the provider directly. */\n origin?: SessionEventOrigin;\n /** Groups reasoning/tool steps and the final response under one provider turn. */\n turnId?: string;\n /** Monotonic provider-side sequence for this turn when the provider can supply one. */\n turnSeq?: number;\n /** True only for a provider turn-final capture; intermediate/preflush response text omits this. */\n turnFinal?: boolean;\n /** Why the provider turn ended. */\n endedBy?: MessageTurnEndReason;\n /** Whether the turn-final edge was provider-native or reconstructed from persisted state. */\n confidence?: MessageFinalityConfidence;\n /** Provider/source that supplied or reconstructed the turn-final edge. */\n source?: string;\n /** Legacy alias for `turnFinal`. Writers dual-stamp true values for old consumers. */\n final?: boolean;\n /** Short label: tool name (\"Bash\"), \"Thinking\", a slash command, etc. */\n label?: string;\n /** Tool/step lifecycle status when type is \"tool\": running | completed | failed. */\n status?: \"running\" | \"completed\" | \"failed\";\n /** True while the step body is still being streamed in (coalesced deltas). */\n streaming?: boolean;\n /** Provider that produced the event. */\n provider?: string;\n /** Relay-owned injection/provisioning action rendered as a system timeline row. */\n relayInjection?: RelayInjectionEventDetail;\n /** Resolved interactive prompt rendered as durable Q+A/approval context. */\n promptResolution?: PromptResolutionEventDetail;\n /**\n * Stable provider-side step id (e.g. a Codex app-server item id). When present, the\n * server UPSERTS the session row keyed on (from, stepId) instead of appending — so a\n * step that emits more than once (a tool's running→completed lifecycle, a streamed\n * reasoning/response that grows) persists as ONE row updated in place, not duplicate\n * rows. Absent → legacy append behavior (the Claude transcript path emits no stepId\n * and is unchanged). Newer-event-wins is enforced by occurredAt on the server.\n */\n stepId?: string;\n}\n\nexport interface MessageWorkFinality {\n windowId: string;\n source: MessageWorkFinalitySource;\n groupOrder: number;\n markedAt: number;\n}\n\nexport interface MessagePayload extends Record<string, unknown> {\n session?: MessageSessionMeta | Record<string, unknown>;\n responseCapture?: MessageSessionMeta | Record<string, unknown>;\n finality?: MessageWorkFinality | Record<string, unknown>;\n}\n\nexport function isTurnFinalSession(session: unknown): session is MessageSessionMeta {\n return isRecord(session) && (session.turnFinal === true || session.final === true);\n}\n\nexport function hasWorkFinality(payload: unknown): payload is MessagePayload & { finality: MessageWorkFinality } {\n return isRecord(payload) && isRecord(payload.finality);\n}\n\nexport function isWorkFinalMessagePayload(payload: unknown): payload is MessagePayload {\n if (!isRecord(payload)) return false;\n if (hasWorkFinality(payload)) return true;\n const session = payload.session;\n return isRecord(session) && session.final === true && session.turnFinal !== true;\n}\n\nexport type MessageDeliveryStatus = \"pending\" | \"delivered\" | \"queued\" | \"failed\" | \"dead\";\n\nexport interface Message {\n id: number;\n from: string;\n to: string; // agent-id | \"tag:<name>\" | \"team:<id>\" | \"broadcast\" | \"cap:<name>\"\n kind: MessageKind;\n channel?: string;\n subject?: string;\n body: string;\n threadId?: number;\n replyTo?: number;\n // Server-owned reply obligation (#283). Absent/true = the message wants a response (default);\n // false = a notification (FYI, merge notice, lifecycle event) that must NOT be replied to.\n // The server keys footer rendering and the reply-obligation tracker off this, not off `from`.\n replyExpected?: boolean;\n claimable?: boolean;\n claimedBy?: string;\n claimedAt?: number;\n claimExpiresAt?: number;\n idempotencyKey?: string;\n deliveryStatus?: MessageDeliveryStatus;\n deliveryAttempts?: number;\n deliveryLastError?: string;\n deliveryNextRetryAt?: number;\n deliveryPoisonReason?: string;\n deliveryUpdatedAt?: number;\n queuedAt?: number;\n maxAgeSeconds?: number;\n resolvedToAgent?: string;\n payload: MessagePayload;\n meta?: Record<string, unknown>;\n reactions?: MessageReaction[];\n readBy: string[];\n createdAt: number;\n // True event time (#196). Equals createdAt for messages posted live; differs only when a\n // Runner backfilled a message queued during an outage. Read `occurredAt ?? createdAt`.\n occurredAt?: number;\n}\n\nexport interface ReplyObligation {\n messageId: number;\n agentId: string;\n from: string;\n kind: MessageKind;\n source?: string;\n subject?: string;\n channel?: string;\n bodyPreview: string;\n createdAt: number;\n replyCommand: string;\n}\n\nexport interface MessageReaction {\n messageId: number;\n actorId: string;\n emoji: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport type RelayNotificationKind =\n | \"message\"\n | \"reply\"\n | \"error\"\n | \"agent.blocked\";\n\nexport type RelayNotificationSeverity = \"info\" | \"warning\" | \"error\";\n\nexport type NotificationRoutingSubscriptionState = \"deliver\" | \"suppress\" | \"abstain\" | \"unknown\";\n\nexport interface NotificationRoutingRecipientInput {\n agentId: string;\n matchedRule?: string;\n reason?: string;\n}\n\nexport interface NotificationRoutingExplanation {\n type: string;\n recipient: string;\n matchedRule: string;\n reason: string;\n scope: Record<string, unknown>;\n subscriptionState: NotificationRoutingSubscriptionState;\n settingsEnabled: boolean;\n}\n\nexport interface NotificationRoutingPreview {\n type: string;\n scope: Record<string, unknown>;\n recipients: NotificationRoutingExplanation[];\n suppressed: NotificationRoutingExplanation[];\n}\n\nexport interface RelayNotification {\n id: string;\n kind: RelayNotificationKind;\n severity: RelayNotificationSeverity;\n title: string;\n body: string;\n messageId?: number;\n agentId?: string;\n threadPeer?: string;\n view?: \"chat\" | \"messages\" | \"agents\" | \"work\" | \"tasks\" | \"channels\" | \"overview\";\n routing?: NotificationRoutingExplanation;\n createdAt: number;\n}\n\nexport interface MessageDeliveryAttempt {\n id: number;\n messageId: number;\n agentId?: string;\n action: \"attempt\" | \"retry-now\" | \"mark-dead\" | \"clear\";\n status: MessageDeliveryStatus;\n error?: string;\n nextRetryAt?: number;\n poisonReason?: string;\n createdAt: number;\n}\n\nexport interface MessageDeliveryState extends Pick<Message,\n | \"id\"\n | \"to\"\n | \"deliveryStatus\"\n | \"deliveryAttempts\"\n | \"deliveryLastError\"\n | \"deliveryNextRetryAt\"\n | \"deliveryPoisonReason\"\n | \"deliveryUpdatedAt\"\n | \"queuedAt\"\n | \"maxAgeSeconds\"\n | \"resolvedToAgent\"\n> {\n attempts: MessageDeliveryAttempt[];\n}\n\nexport interface SendMessageInput {\n from: string;\n to: string;\n kind?: MessageKind;\n channel?: string;\n subject?: string;\n body: string;\n replyTo?: number;\n // Defaults to true server-side. Set false to mark a notification (no reply wanted) — the\n // server suppresses the reply-scaffold footer and appends a one-line no-reply nudge (#283).\n replyExpected?: boolean;\n claimable?: boolean;\n idempotencyKey?: string;\n // TTL in seconds after which the message is stale and is dropped at the delivery/poll gate (#451).\n // `undefined` = unspecified → the server applies its default (a moderate TTL for fire-and-forget\n // fan-out, none for direct DMs / obligations / claimable). An explicit `null` = authoritative\n // never-expire: it suppresses the fan-out default (used by routeNotification for store-ahead\n // notification types whose TTL is decided by the router, not the message baseline).\n maxAgeSeconds?: number | null;\n attachments?: AttachmentRef[];\n payload?: MessagePayload;\n meta?: Record<string, unknown>;\n // Epoch-ms event time, stamped when the event actually occurred (#196). When a Runner\n // queues a message through its durable outbox during a server outage, this preserves the\n // true time rather than the (later) server receive time. Defaults server-side to created_at.\n occurredAt?: number;\n}\n\nexport interface InboxThreadState {\n operatorId: string;\n peerId: string;\n readCursorMessageId?: number;\n archivedAtMessageId?: number;\n updatedAt: number;\n}\n\nexport interface InboxDraft {\n operatorId: string;\n peerId: string;\n body: string;\n subject?: string;\n channel?: string;\n updatedAt: number;\n}\n\nexport interface InboxState {\n operatorId: string;\n threads: InboxThreadState[];\n drafts: InboxDraft[];\n}\n\nexport interface ChatHistoryImportEntry {\n position: number;\n originalMessageId: number;\n originalFrom: string;\n originalTo: string;\n originalCreatedAt: number;\n message: Message;\n}\n\nexport interface ChatHistoryImport {\n id: string;\n targetAgentId?: string;\n targetSpawnRequestId?: string;\n sourcePeerId: string;\n sourceAgentId?: string;\n sourceThreadId?: string;\n sourceAgentLabel?: string;\n importedBy: string;\n importedAt: number;\n entries: ChatHistoryImportEntry[];\n}\n\nexport type ActivityKind = \"message\" | \"reply\" | \"question\" | \"operator\" | \"pair\" | \"task\" | \"state\" | \"issue\";\n\nexport interface ActivityEvent {\n id: number;\n operatorId?: string;\n clientId?: string;\n kind: ActivityKind;\n title: string;\n body?: string;\n meta?: string;\n icon?: string;\n view?: string;\n peer?: string;\n messageId?: number;\n pairId?: string;\n taskId?: number;\n agentId?: string;\n issueRefId?: string;\n issueAction?: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n}\n\nexport interface ActivityEventInput {\n operatorId?: string;\n clientId?: string;\n kind: ActivityKind;\n title: string;\n body?: string;\n meta?: string;\n icon?: string;\n view?: string;\n peer?: string;\n messageId?: number;\n pairId?: string;\n taskId?: number;\n agentId?: string;\n issueRefId?: string;\n issueAction?: string;\n metadata?: Record<string, unknown>;\n}\n","import { getAllManifests } from \"agent-relay-providers\";\nimport type { WorkspaceMode, WorkspaceMetadata, WorkspaceStatus } from \"./workspaces.js\";\nimport type { MemoryType, Memory, TaskRoutingHints, ContextBudget } from \"./memory.js\";\nimport type { Artifact } from \"./artifacts.js\";\nexport type { AgentProfile, AgentProfileAssetRef, AgentProfileBase, AgentProfileCategoryMode, AgentProfileFilesystemScope, AgentProfileHostAssets, AgentProfileInstructionPolicy, AgentProfileNotificationResponseMode, AgentProfileProjectInstructionPolicy, AgentProfileProjectMcpPolicy, AgentProfileProjectSettingsPolicy, AgentProfileProvider, AgentProfileProviderOptions, AgentProfileProjectionEntry, AgentProfileProjectionReport, AgentProfileProjectionResult, AgentProfileSharedMcpPolicy, ContinuationBudget, ContinuationDecisionEntry, ContinuationEnvelope, ContinuationLedgerEntry, InsightObservation, InsightsConfig, InsightsStats, NotificationsConfig, SelfResumeConfig, SelfResumeMode, SpawnPolicy, StewardConfig, WorkspaceConfig } from \"./provisioning.js\";\n\nexport type AgentKind = \"provider\" | \"channel\" | \"orchestrator\" | \"system\" | \"user\";\n\nexport interface AgentCard {\n id: string;\n name: string;\n kind: AgentKind;\n label?: string; // human-friendly alias; acts as a fan-out target (\"label:foo\")\n tags: string[];\n machine?: string;\n rig?: string;\n capabilities: string[];\n ready: boolean;\n status: AgentStatus;\n instanceId?: string;\n epoch: number;\n providerCapabilities?: ProviderCapabilities;\n context?: ContextState;\n quota?: QuotaState;\n meta?: import(\"./provider-state.js\").AgentMeta;\n lifecycle: AgentLifecycle;\n /** Authoritative parent id from the child's signed runner token; powers quotas, `spawnedBy:`, scoped shutdown, and no-grandchildren. */\n spawnedBy?: string;\n /** Durable first-class team id (`team_<id>`) shared by a coordinator and its spawned members. */\n teamId?: string;\n /** Server-derived merge-back badge for the owned isolated worktree (#236); absent for non-branch agents. */\n branchState?: BranchState;\n /** Live isolated workspace id backing `branchState`, for dashboard deep links (#236). */\n branchWorkspaceId?: string;\n /** Server-derived `owner/repo` of the agent's project (cwd→project→issueRepo), so the\n * dashboard linkifies a bare `#NNN` against the originating project's repo (#533). */\n issueRepo?: string;\n lastSeen: number;\n createdAt: number;\n}\n\nexport type AgentStatus = \"online\" | \"idle\" | \"busy\" | \"stale\" | \"offline\";\n\n/**\n * Per-spawn report-up verbosity (#1037) — how much of a spawned child's turn output reaches its\n * spawner. Chosen by the spawner at spawn time and read by the lineage report-up delivery gate.\n * - `quiet` (default): only the turn-final response, and only once the child has transitioned to\n * idle/awaiting-input — mid-task narration (\"I'll read X… now Y…\") is suppressed. Lifecycle\n * signals (agent.ready/exit/merge), escalations and explicit coordinator-addressed messages\n * still flow regardless.\n * - `normal`: every turn-final response is forwarded immediately (the pre-#1037 behavior).\n * - `verbose`: every turn-final response plus all child streams (narration/reasoning/tool) are\n * forwarded, without the spawner needing an explicit per-stream subscription.\n */\nexport const REPORT_VERBOSITIES = [\"quiet\", \"normal\", \"verbose\"] as const;\nexport type ReportVerbosity = (typeof REPORT_VERBOSITIES)[number];\nexport function isReportVerbosity(value: unknown): value is ReportVerbosity {\n return typeof value === \"string\" && (REPORT_VERBOSITIES as readonly string[]).includes(value);\n}\n\nexport const VALID_AGENT_LIFECYCLES = [\"persistent\", \"transient\"] as const;\nexport type AgentLifecycle = (typeof VALID_AGENT_LIFECYCLES)[number];\nexport const isAgentLifecycle = (value: unknown): value is AgentLifecycle => typeof value === \"string\" && (VALID_AGENT_LIFECYCLES as readonly string[]).includes(value);\nexport const normalizeAgentLifecycle = (value: unknown): AgentLifecycle | undefined => isAgentLifecycle(value) ? value : undefined;\n\n/** Branch-workspace state for an isolated-workspace owner (#236), derived from `WorkspaceStatus` + claim + git ahead/dirty.\n * - `idle` ⚪ active worktree, nothing to land (0 ahead, clean tree)\n * - `changes` 🟡 active worktree with commits/dirty work — human can mark it ready\n * - `ready` 🔵 handed off; the relay auto-merge will land it (~2 min sweep)\n * - `steward` 🟠 under reconciliation (a steward holds it, or conflict/merge in flight)\n * - `blocked` 🔴 escalated/failed — needs human attention\n */\nexport type BranchState = \"idle\" | \"changes\" | \"ready\" | \"steward\" | \"blocked\";\n/** All BranchState values, for exhaustive UI maps and validation. */\nexport const BRANCH_STATE_VALUES = [\"idle\", \"changes\", \"ready\", \"steward\", \"blocked\"] as const satisfies readonly BranchState[];\n\nexport type PlanStatus = \"pending\" | \"in-progress\" | \"landed\" | \"blocked\";\nexport type PlanOwnerRef = { agentId?: string; policyName?: string; label?: string };\n\nexport interface PlanNode {\n id: string;\n label: string;\n scopeBlurb?: string;\n ownerRef?: PlanOwnerRef;\n lane?: string;\n status: PlanStatus;\n statusSource: \"manual\" | \"bound\";\n refs?: { agentId?: string; taskId?: string; issueId?: string; landingRef?: string };\n blackboardRefs?: string[];\n}\n\nexport interface PlanEdge {\n from: string;\n to: string;\n}\n\nexport interface Plan {\n id: string;\n threadId: string;\n teamId?: string;\n channel?: string; archived?: boolean;\n version: number;\n title?: string;\n objective?: string;\n nodes: PlanNode[];\n edges: PlanEdge[];\n createdBy: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport type TeamRuntimeState = \"busy\" | \"idle\" | \"degraded\" | \"stalled\" | \"dissolved\";\n\nexport interface Team {\n id: string;\n coordinatorId?: string;\n rootCoordinatorId?: string;\n title?: string;\n brief?: string;\n definitionOfDone?: string;\n status: \"active\" | \"dissolved\";\n outcomeStatus?: \"success\" | \"partial\" | \"failed\";\n outcomeSummary?: string;\n composition?: string;\n costTokens?: number;\n costUsd?: number;\n wallClockMs?: number;\n landsClean?: boolean;\n zeroMembersSince?: number;\n runtimeState?: TeamRuntimeState;\n createdAt: number;\n updatedAt: number;\n dissolvedAt?: number;\n}\n\nexport interface TeamTemplateCompositionEntry { label: string; provider: SpawnProvider; model?: string; effort?: SpawnEffort; profile?: string; count: number; }\nexport interface TeamTemplatePlanSkeleton { nodes?: PlanNode[]; edges?: PlanEdge[]; } export interface TeamTemplate { id: string; slug: string; title: string; description?: string; composition: TeamTemplateCompositionEntry[]; brief?: string; definitionOfDone?: string; planSkeleton?: TeamTemplatePlanSkeleton; createdAt?: string; updatedAt?: string; }\nexport type BlackboardEntryKind = \"decision\" | \"fact\" | \"ruled_out\"; export type BlackboardEntryStatus = \"active\" | \"superseded\" | \"retracted\";\n\nexport interface BlackboardEntry {\n id: string;\n teamId: string;\n kind: BlackboardEntryKind;\n title: string;\n body?: string;\n status: BlackboardEntryStatus;\n supersedes?: string;\n tags: string[];\n authorId: string;\n createdAt: number;\n updatedAt: number;\n}\n\n// Projects (#403/#405) — the DURABLE knowledge scope (outlives every team). The\n// data shape is a near-exact re-scope of the Team/BlackboardEntry pair above\n// (team_id → project_id); kept beside it deliberately so the parallel is obvious.\n// #559 — the knowledge-scope axis. A project anchors on repo roots (cwd→root binding);\n// a `profile` store anchors on a profile name; a `user` store on an operator identity.\n// One table, one entry/edge/cascade machine — only the anchor-resolution differs. Existing\n// rows default `scope:'project'`, so the generalization is non-regressing.\nexport const PROJECT_SCOPES = [\"project\", \"profile\", \"user\"] as const;\nexport type ProjectScope = (typeof PROJECT_SCOPES)[number];\nexport interface Project {\n id: string;\n slug: string;\n title?: string;\n description?: string;\n tenantId?: string;\n /** `owner/repo` for issue-ref linkify/land-close (#533). Auto-derived from a root's git\n * remote at seed; an explicit value set via relay_project_* wins and is never re-derived. */\n issueRepo?: string;\n /** Knowledge-scope axis (#559). `project` (default) anchors on repo roots; `profile`\n * anchors on a profile name (role memory, inherited via the contains graph); `user`\n * anchors on an operator identity (cross-project operator memory). */\n scope: ProjectScope;\n /** For `profile`/`user` scopes, the anchor key — the profile name or operator id the\n * store is bound to. Null for `project` scope (bound via project_roots instead). */\n anchorKey?: string;\n status: \"active\" | \"archived\";\n createdAt: number;\n updatedAt: number;\n}\nexport interface ProjectRoot {\n projectId: string;\n rootPath: string;\n /** Clone URL used by host acquisition when this root is missing on a target host (#410). */\n remoteUrl?: string;\n /** Optional branch/tag/ref to checkout/sync for host acquisition. Unset means remote default. */\n ref?: string;\n createdAt: number;\n}\nexport const PROJECT_ENTRY_KINDS = [\"rule\", \"fact\", \"decision\", \"memory\"] as const;\nexport type ProjectEntryKind = (typeof PROJECT_ENTRY_KINDS)[number];\n// `proposed` (#559) is the curation tier: a scoped-memory capture lands here and is NOT\n// injected at spawn (only `active` entries render into a preamble); promotion flips it to\n// `active`. Project-scope writes stay `active` by default, so #403 behavior is unchanged.\nexport type ProjectEntryStatus = \"proposed\" | \"active\" | \"superseded\" | \"retracted\";\n// `manual` = written by an agent directly; `promoted` = lifted from a team's\n// blackboard with an originRef back to its bb_ id; `ingested` = bootstrapped from\n// a root's CLAUDE.md. The source lever makes going-vanilla a no-op (#403 decision 4).\nexport const PROJECT_ENTRY_SOURCES = [\"manual\", \"promoted\", \"ingested\"] as const;\nexport type ProjectEntrySource = (typeof PROJECT_ENTRY_SOURCES)[number];\nexport interface ProjectEntry {\n id: string;\n projectId: string;\n kind: ProjectEntryKind;\n title: string;\n body?: string;\n status: ProjectEntryStatus;\n supersedes?: string;\n tags: string[];\n authorId: string;\n source: ProjectEntrySource;\n originRef?: string;\n createdAt: number;\n updatedAt: number;\n}\n// Project composition graph (#406). ASYMMETRIC by design: `contains` is the\n// umbrella→sub-project hierarchy and INHERITS (resolving a project's entries pulls\n// in its transitive contains-ancestors); `depends_on` is reference-only and NEVER\n// auto-inherits (a dependency's internal rules must not flood consuming agents).\nexport const PROJECT_EDGE_KINDS = [\"contains\", \"depends_on\"] as const;\nexport type ProjectEdgeKind = (typeof PROJECT_EDGE_KINDS)[number];\nexport interface ProjectEdge {\n fromProject: string;\n toProject: string;\n kind: ProjectEdgeKind;\n createdAt: number;\n}\n// A resolved entry carries `inherited` so a caller can tell an inherited\n// contains-ancestor rule from one the project owns directly (entry.projectId is the\n// owning project either way).\nexport interface ResolvedProjectEntry extends ProjectEntry {\n inherited: boolean;\n}\n\nexport const ISSUE_PROVIDERS = [\"github\", \"jira\", \"youtrack\", \"gitlab\", \"linear\"] as const;\nexport type IssueProvider = (typeof ISSUE_PROVIDERS)[number];\nexport const ISSUE_REF_STATUSES = [\"open\", \"closed\", \"unknown\"] as const;\nexport type IssueRefStatus = (typeof ISSUE_REF_STATUSES)[number];\nexport interface IssueRef {\n id: string;\n provider: IssueProvider;\n repo: string;\n externalKey: string;\n url: string;\n title?: string;\n status: IssueRefStatus;\n statusSyncedAt?: number;\n createdAt: number;\n updatedAt: number;\n}\nexport const ISSUE_ASSOCIATION_ENTITY_TYPES = [\"team\", \"plan-node\", \"epic\", \"task\"] as const;\nexport type IssueAssociationEntityType = (typeof ISSUE_ASSOCIATION_ENTITY_TYPES)[number];\nexport interface IssueAssociation {\n issueRefId: string;\n entityType: IssueAssociationEntityType;\n entityId: string;\n createdAt: number;\n}\nexport interface IssueAssociationIssue {\n association: IssueAssociation;\n issueRef: IssueRef;\n}\nexport interface IssueAssociationEntity {\n entityType: IssueAssociationEntityType;\n entityId: string;\n createdAt: number;\n}\n\ntype CapabilitySource = \"catalog\" | \"provider\" | \"runtime\" | \"override\" | \"estimate\";\ntype CapabilityConfidence = \"declared\" | \"reported\" | \"verified\" | \"estimated\" | \"unknown\";\nexport type QuotaSource = \"header\" | \"probe\" | \"ingest\" | \"statusline\" | \"emulated\";\nexport type QuotaUnit = \"tokens\" | \"requests\" | \"%\";\nexport type QuotaWindowName = string;\n\nexport interface ProviderCapabilities {\n lifecycle: {\n managed: boolean;\n shutdownHard: boolean;\n restartHard: boolean;\n semanticStatus?: boolean;\n reconnect?: boolean;\n hibernate?: boolean;\n resume?: boolean;\n };\n model?: {\n provider?: SpawnProvider | string;\n id?: string;\n alias?: string;\n providerModel?: string;\n effort?: SpawnEffort | string;\n source: CapabilitySource;\n confidence: CapabilityConfidence;\n lastUpdatedAt?: number;\n };\n session?: {\n approvalMode?: SpawnApprovalMode | string;\n fileRead?: boolean;\n fileWrite?: boolean;\n shell?: boolean;\n shellMode?: \"none\" | \"unrestricted\" | \"guarded\" | \"read-only-guarded\" | string;\n source: CapabilitySource;\n confidence: CapabilityConfidence;\n lastUpdatedAt?: number;\n };\n context?: {\n stats?: {\n source: \"api\" | \"statusline\" | \"hook\" | \"estimate\";\n confidence: \"exact\" | \"reported\" | \"estimated\";\n };\n windowTokens?: number;\n compact?: boolean;\n clear?: boolean;\n inject?: boolean;\n resume: \"clear-inject\" | \"native\" | \"none\";\n fork?: boolean;\n rollback?: boolean;\n archive?: boolean;\n };\n quota?: {\n supported: boolean;\n source: QuotaSource;\n unit: QuotaUnit;\n windows?: Array<{\n name: string;\n unit: QuotaUnit;\n reset?: boolean;\n }>;\n };\n liveSession?: {\n capture?: boolean;\n inject?: boolean;\n /** Provider can be interrupted mid-turn from the dashboard (ESC / turn-interrupt). */\n interrupt?: boolean;\n /** Prompts typed directly into the provider (web terminal/TUI) are mirrored into chat. */\n promptEcho?: boolean;\n /** Reasoning/tool activity steps are surfaced to chat as discreet session events. */\n reasoning?: boolean;\n /** Provider exposes a slash-command catalog for the chat input palette. */\n slashCommands?: boolean;\n };\n terminal?: {\n live?: {\n read?: boolean;\n write?: boolean;\n };\n attach?: {\n create?: boolean;\n read?: boolean;\n write?: boolean;\n detach?: boolean;\n };\n };\n source: CapabilitySource;\n confidence: CapabilityConfidence;\n lastUpdatedAt: number;\n}\n\nexport interface QuotaState {\n windows: Array<{\n name: QuotaWindowName;\n utilization: number;\n resetsAt?: number;\n unit: QuotaUnit;\n }>;\n source: QuotaSource;\n updatedAt: number;\n}\n\n/**\n * Normalized liveness verdict — the Runner↔provider contract field for \"is this busy\n * agent actually alive, or wedged?\" (epic #725; LOCKED by #746, the proof-of-contract).\n *\n * Computed by the RUNNER (each provider adapter folds its own raw transport/timeline/\n * token/work-kind signals down into this verdict) and shipped on the agent card at\n * `meta.liveness`. The server + dashboard read ONLY these three fields — never the raw\n * signals they replace (`meta.transport.*`, `meta.timelineEvent`, `meta.lastProviderEventAt`,\n * `meta.activeWork[].kind`). This single declared field collapses the six raw `meta` reads\n * that the stuck-busy detector grew across #713 (Claude transport heartbeat) and #724\n * (Codex subprocess / fresh-lastSeen veto) — the \"patched twice\" leak, since the same\n * derivation had been re-implemented client-side in the dashboard.\n *\n * The server keeps only THRESHOLD POLICY: it never re-interprets provider runtime signals\n * and contains no `provider === …` branch. A future provider quirk becomes one adapter\n * change behind this stable shape.\n */\nexport interface LivenessSignal {\n /**\n * Epoch-ms of the most recent proof the runner→server transport is alive — the runner's\n * max of its frame / heartbeat-ack timestamps. The SERVER folds its own server-stamped\n * `AgentCard.lastSeen` in as a floor when reading (the runner cannot know `lastSeen`), so\n * a fresh heartbeat on EITHER side proves liveness. A fresh value is the authoritative\n * veto: provider-token silence alone (long thinking turns, native compaction, multi-minute\n * subprocesses) never triggers stuck-busy while the link is demonstrably alive (#713/#724).\n */\n transportLiveAt: number;\n /**\n * Epoch-ms of the most recent evidence of FORWARD PROGRESS, folding (whichever is latest):\n * provider tokens, a lifecycle/timeline transition (e.g. Claude native `compacting`, which\n * is token-silent yet progressing — #713), or live tool/subprocess work (Codex #724). The\n * adapter decides what counts as progress for its provider; the server only compares it\n * against the no-progress threshold.\n */\n lastProgressAt: number;\n /**\n * True while any tool/subprocess work-kind is in flight (e.g. `bun test`, `build`,\n * `typecheck`) — i.e. active work that is NOT the bare provider turn. The adapter knows\n * its own work-kinds and classifies them; the server MUST NOT enumerate them. Live work\n * keeps the agent out of stuck-busy regardless of provider-token silence.\n */\n hasLiveWork: boolean;\n}\n\n/**\n * A structured question surfaced inside an {@link InteractivePrompt} (Claude\n * AskUserQuestion). The dashboard renders these as a form and submits the chosen\n * option label(s) back as an `answer` decision (ProviderPermissionDecisionInput).\n */\nexport interface ProviderQuestion {\n question: string;\n header?: string;\n options: { label: string; description?: string }[];\n multiSelect?: boolean;\n}\n\n/**\n * The kind of interactive prompt an adapter surfaces. The §4.2 normalized set is\n * questions|plan|command|tool|approval; the Codex app-server additionally tags its\n * approval requests file-change|permission. The server/dashboard treat `kind` as a\n * display hint only — they never branch liveness, ordering, or resolution on it.\n */\nexport type InteractivePromptKind =\n | \"questions\"\n | \"plan\"\n | \"command\"\n | \"tool\"\n | \"approval\"\n | \"file-change\"\n | \"permission\";\n\n/**\n * Normalized interactive-prompt surface — the Runner↔provider contract field for \"the\n * agent is blocked waiting on a human decision\" (epic #725, §4.2; #435/#723). Formalizes\n * what used to be an untyped `providerState.pendingApproval` bag with a per-provider\n * `provider` tag the server never needed.\n *\n * Built by the RUNNER adapter (Claude's PreToolUse hook view; the Codex app-server\n * approval view) and shipped on `meta.providerState.pendingApproval`. Resolution rides\n * the existing ProviderPermissionDecisionInput (approve|approve-session|deny|abort|answer)\n * — the server persists the blocked state and clears it on the decision; it never\n * re-derives the prompt or its ordering.\n */\nexport interface InteractivePrompt {\n id: string;\n kind: InteractivePromptKind;\n title: string;\n body: string;\n /** Present for kind \"questions\" — the structured AskUserQuestion form. */\n questions?: ProviderQuestion[];\n /** Approve/deny-style choices the dashboard renders as buttons. */\n choices?: { id: string; label: string }[];\n /**\n * ORDERING INVARIANT (#435/#723): `true` asserts the adapter has already mirrored\n * every reasoning/narrative step of the turn into the session stream BEFORE this\n * prompt surfaced. The adapter OWNS the guarantee via its own mechanism — Claude\n * pre-flushes the transcript narrative at the PreToolUse hook seam; Codex flushes its\n * buffered reasoning/narration before emitting the approval — and stamps this only\n * once that flush has run. The server/dashboard render in arrival order and never\n * re-sort, so the reasoning is already present above the form. A literal `true`\n * because a prompt that cannot make this guarantee must not be surfaced as one.\n */\n reasoningSettled: true;\n}\n\nexport type QuotaSnapshot = {\n agentId: string;\n capturedAt: number;\n} & QuotaState;\n\ntype ContextLifecycleState =\n | \"fresh\"\n | \"primed\"\n | \"working\"\n | \"cooling\"\n | \"compacting\"\n | \"clearing-context\"\n | \"hibernating\";\n\nexport interface ContextProbeMetrics {\n agentId: string;\n contextPercent: number;\n tokensUsed?: number;\n tokensMax?: number;\n quotaUsed?: number;\n quotaLimit?: number;\n quotaResetIn?: number;\n quotaWindows?: Array<{\n name: QuotaWindowName;\n used?: number;\n limit?: number;\n utilization?: number;\n resetIn?: number;\n resetsAt?: number;\n unit?: QuotaUnit;\n }>;\n model?: string;\n effort?: string;\n source: \"statusline\" | \"hook\" | \"api\" | \"estimate\";\n confidence: \"exact\" | \"reported\" | \"estimated\";\n timestamp: number;\n}\n\nexport interface ContextState {\n utilization: number;\n tokensUsed?: number;\n tokensMax?: number;\n lifecycleState: ContextLifecycleState;\n warmTopics: string[];\n activeMemories: string[];\n tasksSinceCompact: number;\n lastCompactedAt?: number; lastContextAdvisoryBand?: number; lastContextAdvisoryAt?: number;\n lastUpdatedAt: number;\n source: \"statusline\" | \"hook\" | \"api\" | \"estimate\";\n confidence: \"exact\" | \"reported\" | \"estimated\";\n}\nexport interface ContextSnapshot {\n id: number;\n agentId: string;\n context: ContextState;\n utilization: number;\n lifecycleState: ContextLifecycleState;\n tokensUsed?: number;\n tokensMax?: number;\n source: ContextState[\"source\"];\n confidence: ContextState[\"confidence\"];\n capturedAt: number;\n}\n\nexport interface ContextPackageRequest {\n task: TaskRoutingHints;\n agent: AgentCard;\n budget: ContextBudget;\n memories?: Memory[];\n}\n\nexport interface RegisterAgentInput {\n id: string;\n name: string;\n kind?: AgentKind;\n label?: string | null;\n tags?: string[];\n machine?: string;\n rig?: string;\n capabilities?: string[];\n ready?: boolean;\n status?: AgentCard[\"status\"];\n instanceId?: string;\n providerCapabilities?: ProviderCapabilities;\n context?: ContextState;\n quota?: QuotaState;\n meta?: import(\"./provider-state.js\").AgentMeta;\n lifecycle?: AgentLifecycle;\n /** Parent agent id. Server sets this authoritatively from the registering token's\n * `spawnedBy`/`parentAgents` constraint; any client-supplied value is ignored. */\n spawnedBy?: string;\n /** Team id. Server sets this authoritatively from the registering token's `teamId` constraint. */\n teamId?: string;\n}\n\nexport interface AgentSessionGuard {\n instanceId?: string;\n epoch?: number;\n}\n\n// --- Orchestrators ---\n\nexport type OrchestratorStatus = \"online\" | \"offline\";\n\n/** Spawn providers — registered provider manifests are the runtime source of truth. */\nexport const SPAWN_PROVIDERS = getAllManifests().map((manifest) => manifest.id); export type SpawnProvider = string;\nexport function isSpawnProvider(value: unknown): value is SpawnProvider {\n return typeof value === \"string\" && SPAWN_PROVIDERS.includes(value);\n}\n\n/** Approval modes — runtime tuple + derived type. */\nexport const APPROVAL_MODES = [\"open\", \"guarded\", \"read-only\"] as const;\nexport type SpawnApprovalMode = (typeof APPROVAL_MODES)[number];\nfunction isApprovalMode(value: unknown): value is SpawnApprovalMode {\n return typeof value === \"string\" && (APPROVAL_MODES as readonly string[]).includes(value);\n}\n\nexport type SpawnEffort = string;\n\n// --- Runtime contract / capability types (used by Orchestrator and its inputs) ---\n\nexport type RuntimeContractName =\n | \"relayApi\"\n | \"orchestratorProtocol\"\n | \"runnerProtocol\"\n | \"providerPluginProtocol\";\n\nexport type RuntimeContracts = Partial<Record<RuntimeContractName, number>>;\nexport type RuntimeCapabilities = Record<string, boolean>;\n\nexport interface RuntimePackageMetadata {\n name: string;\n version: string;\n}\n\nexport interface ContractCompatibilityIssue {\n contract: string;\n expected: string;\n actual?: number;\n}\n\nexport interface ContractCompatibility {\n status: \"compatible\" | \"incompatible\" | \"unknown\";\n compatible: boolean;\n issues: ContractCompatibilityIssue[];\n}\n\nexport type ManagedAgentStatus = \"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"backoff\" | \"failed\";\n\nexport interface ManagedAgentState {\n policyName: string;\n status: ManagedAgentStatus;\n agentId?: string;\n orchestratorId: string;\n provider: SpawnProvider;\n tmuxSession?: string;\n spawnRequestId?: string;\n workspaceId?: string;\n workspacePath?: string;\n workspaceBranch?: string;\n lastSpawnAt?: number;\n lastStopAt?: number;\n healthySince?: number;\n restartCount: number;\n consecutiveFailures: number;\n backoffUntil?: number;\n lastError?: string;\n updatedAt: number;\n}\n\nexport interface ProviderStatusReport {\n name: SpawnProvider;\n available: boolean;\n checkedAt: number;\n reason?: string;\n version?: string;\n features?: Record<string, boolean>;\n cli?: {\n command: string;\n path?: string;\n ok: boolean;\n version?: string;\n error?: string;\n };\n runner?: {\n command: string;\n path?: string;\n ok: boolean;\n version?: string;\n error?: string;\n };\n}\n\nexport interface ProviderCatalogSummary {\n provider: SpawnProvider;\n label: string;\n defaultModel?: string;\n models: Array<{\n alias: string;\n label: string;\n providerModel: string;\n efforts: SpawnEffort[];\n defaultEffort?: SpawnEffort;\n disabled?: boolean;\n limits?: {\n contextWindowTokens?: {\n value: number;\n source: \"catalog\" | \"provider\" | \"runtime\" | \"override\";\n confidence: \"declared\" | \"verified\" | \"estimated\" | \"unknown\";\n lastUpdatedAt?: number;\n };\n maxOutputTokens?: {\n value: number;\n source: \"catalog\" | \"provider\" | \"runtime\" | \"override\";\n confidence: \"declared\" | \"verified\" | \"estimated\" | \"unknown\";\n lastUpdatedAt?: number;\n };\n };\n contextThreshold?: { selfCompactUtilization: number; warnUtilization?: number; hardBackstopUtilization?: number }; capabilities?: {\n modalities: {\n input: {\n text: boolean;\n image?: boolean;\n audio?: boolean;\n video?: boolean;\n pdf?: boolean;\n };\n output: {\n text: boolean;\n image?: boolean;\n audio?: boolean;\n video?: boolean;\n };\n };\n tools?: {\n code?: boolean;\n review?: boolean;\n debug?: boolean;\n refactor?: boolean;\n shell?: boolean;\n fileRead?: boolean;\n fileWrite?: boolean;\n webSearch?: boolean;\n imageGeneration?: boolean;\n imageEditing?: boolean;\n };\n source: \"catalog\" | \"provider\" | \"runtime\" | \"override\";\n confidence: \"declared\" | \"verified\" | \"estimated\" | \"unknown\";\n lastUpdatedAt?: number;\n };\n }>;\n}\n\nexport interface Orchestrator {\n id: string;\n hostname: string;\n status: OrchestratorStatus;\n agentId: string; // relay agent id for messaging\n providers: SpawnProvider[];\n providerStatus?: ProviderStatusReport[];\n providerCatalog?: ProviderCatalogSummary[];\n baseDir: string;\n apiUrl?: string;\n envKeys: string[]; // names only, never values\n package?: RuntimePackageMetadata;\n contracts?: RuntimeContracts;\n capabilities?: RuntimeCapabilities;\n contractCompatibility?: ContractCompatibility;\n version?: string;\n protocolVersion?: number;\n gitSha?: string;\n health?: OrchestratorHealth;\n // Self-reported host supervision (how the orchestrator's own process is run),\n // used to target a remote self-upgrade at the right unit.\n supervisor?: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n selfUnit?: string;\n runtimePrefix?: string;\n // In-flight / last remote upgrade state, reconciled by the relay against the\n // version the orchestrator reports after it restarts.\n upgrade?: OrchestratorUpgradeState;\n meta: Record<string, unknown>;\n managedAgents: ManagedAgent[];\n lastSeen: number;\n createdAt: number;\n}\n\nexport interface OrchestratorUpgradeState {\n desiredVersion: string;\n status: \"pending\" | \"succeeded\" | \"failed\";\n commandId?: string;\n providers?: string[];\n fromVersion?: string;\n requestedBy?: string;\n requestedAt: number;\n settledAt?: number;\n error?: string;\n}\n\nexport interface OrchestratorHealth {\n status: \"ok\" | \"warn\" | \"restart-required\" | \"upgrade-required\" | \"unknown\";\n restartRequired: boolean;\n upgradeRequired?: boolean;\n issues: Array<{\n code: \"missing-version\" | \"package-drift\" | \"missing-contract\" | \"protocol-mismatch\" | \"restart-required\" | \"upgrade-required\";\n detail: string;\n }>;\n}\n\nexport interface ManagedAgent {\n agentId: string;\n provider: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n profile?: string;\n workspaceMode?: WorkspaceMode;\n workspace?: WorkspaceMetadata;\n sessionName?: string;\n supervisor?: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n systemdUnit?: string;\n terminalSession?: string;\n terminalAvailable?: boolean;\n tmuxSession: string;\n cwd: string;\n label?: string;\n approvalMode: SpawnApprovalMode;\n policyName?: string;\n spawnRequestId?: string;\n automationRunId?: string;\n pid?: number;\n startedAt: number;\n}\n\nexport interface ManagedSessionExitDiagnostics {\n agentId: string;\n provider: SpawnProvider;\n workspaceMode?: WorkspaceMode;\n workspace?: WorkspaceMetadata;\n sessionName?: string;\n tmuxSession: string;\n cwd: string;\n label?: string;\n policyName?: string;\n spawnRequestId?: string;\n automationRunId?: string;\n supervisor: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n systemdUnit?: string;\n terminalSession?: string;\n terminalAvailable?: boolean;\n pid?: number;\n currentPid?: number;\n startedAt: number;\n detectedAt: number;\n runtimeMs: number;\n logFile?: string;\n logBytes?: number;\n logEmpty?: boolean;\n logTail?: string[];\n runnerInfoFile?: string;\n runnerInfoPresent?: boolean;\n systemd?: {\n unit: string;\n activeState?: string;\n subState?: string;\n result?: string;\n execMainCode?: string;\n execMainStatus?: string;\n mainPid?: number;\n unavailable?: string;\n };\n /** Termination signal name if the supervisor reported one (e.g. \"SIGKILL\", \"SIGSEGV\"). */\n signal?: string;\n /** Process exit code if known (0 = clean exit). */\n exitCode?: number;\n /** Positive OOM-kill detection (cgroup memory.events / journal OOM-killer / systemd Result=oom-kill). */\n oom?: { source: string; detail?: string };\n unavailable?: string[];\n lastError: string;\n}\n\n/**\n * Likely-cause label for an agent's death (#636). Best-effort classification mapped from the\n * available evidence — never raw logs, always a verdict the parent can act on.\n */\nexport type AgentDeathCause =\n | \"oom\"\n | \"killed\"\n | \"clean-exit\"\n | \"token-401\"\n | \"onboarding-gate\"\n | \"provider-crash\"\n | \"transport-loss\"\n | \"unknown\";\n\nexport const AGENT_DEATH_CAUSES = [\n \"oom\",\n \"killed\",\n \"clean-exit\",\n \"token-401\",\n \"onboarding-gate\",\n \"provider-crash\",\n \"transport-loss\",\n \"unknown\",\n] as const satisfies readonly AgentDeathCause[];\n\n/**\n * Structured post-mortem for an agent that died (#636). Rides on the `agent.exited` terminal\n * event (#633) so the death notification carries the WHY, and is persisted as a {@link CrashReport}\n * so the verdict survives the worktree/session reaper.\n */\nexport interface AgentDeathDiagnosis {\n /** Best-effort likely-cause label. */\n cause: AgentDeathCause;\n /** Confidence in the label — high (conclusive signal), medium (strong hint), low (fallback guess). */\n confidence: \"high\" | \"medium\" | \"low\";\n /** One-line human verdict, e.g. \"OOM-killed (systemd Result=oom-kill)\". */\n summary: string;\n /** Supporting evidence bullets that led to the label. */\n evidence: string[];\n /** Termination signal name if known (SIGKILL/SIGTERM/SIGSEGV). */\n signal?: string;\n /** Process exit code if known (0 = clean). */\n exitCode?: number;\n /** True when an OOM kill was positively detected. */\n oom?: boolean;\n /** Last N lines of the runner stderr / provider log around death. */\n logTail?: string[];\n /** Last heartbeat timestamp the server saw before death. */\n lastHeartbeatAt?: number;\n /** Gap (ms) between last heartbeat and death detection (ties to #634). */\n heartbeatGapMs?: number;\n /** Last time the agent reported busy (turn activity). */\n lastBusyAt?: number;\n /** Active work titles at death, if any. */\n activeWork?: string[];\n /** When this diagnosis was produced. */\n detectedAt: number;\n}\n\n/**\n * Persisted crash post-mortem (#636), retrievable by `agentId` after the agent row is reaped.\n * Stored in its own table so it outlives `pruneOfflineAgents` / `deleteAgent`.\n */\nexport interface CrashReport {\n agentId: string;\n parentId?: string;\n teamId?: string;\n spawnRequestId?: string;\n provider?: string;\n label?: string;\n reason: string;\n diagnosis: AgentDeathDiagnosis;\n createdAt: number;\n}\n\nexport interface RegisterOrchestratorInput {\n id: string;\n hostname: string;\n providers: SpawnProvider[];\n providerStatus?: ProviderStatusReport[];\n providerCatalog?: ProviderCatalogSummary[];\n baseDir: string;\n apiUrl?: string;\n envKeys?: string[];\n package?: RuntimePackageMetadata;\n contracts?: RuntimeContracts;\n capabilities?: RuntimeCapabilities;\n version?: string;\n protocolVersion?: number;\n gitSha?: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface OrchestratorRuntimeInput {\n package?: RuntimePackageMetadata;\n contracts?: RuntimeContracts;\n capabilities?: RuntimeCapabilities;\n version?: string;\n protocolVersion?: number;\n gitSha?: string;\n providers?: SpawnProvider[];\n providerStatus?: ProviderStatusReport[];\n providerCatalog?: ProviderCatalogSummary[];\n}\n\ninterface OrchestratorSpawnInput {\n provider: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n cwd?: string;\n workspaceMode?: WorkspaceMode;\n label?: string;\n approvalMode?: SpawnApprovalMode;\n prompt?: string;\n systemPromptAppend?: string;\n env?: Record<string, string>;\n}\n\ninterface OrchestratorSpawnResult {\n orchestratorId: string;\n provider: SpawnProvider;\n sessionName?: string;\n supervisor?: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n systemdUnit?: string;\n terminalSession?: string;\n terminalAvailable?: boolean;\n tmuxSession: string;\n cwd: string;\n label?: string;\n approvalMode: SpawnApprovalMode;\n pid?: number;\n startedAt: number;\n}\n\nexport interface Recipe {\n name: string;\n description: string;\n version?: string;\n author?: string;\n agents: RecipeAgent[];\n workflow?: RecipeWorkflow;\n lifecycle?: RecipeLifecycle;\n}\n\nexport interface RecipeAgent {\n role: string;\n provider: SpawnProvider;\n count?: number;\n capabilities: string[];\n label?: string;\n tags?: string[];\n memoryTags?: string[];\n approvalMode?: \"open\" | \"guarded\" | \"read-only\";\n prompt?: string;\n model?: string;\n effort?: SpawnEffort;\n env?: Record<string, string>;\n}\n\ninterface RecipeWorkflow {\n trigger?: string;\n fanOut?: \"all\" | \"first\";\n collect?: string;\n routing?: RecipeRoute[];\n}\n\ninterface RecipeRoute {\n pattern: string;\n pipeline: string[];\n}\n\ninterface RecipeLifecycle {\n mode?: \"persistent\" | \"ephemeral\";\n idleTimeoutMs?: number;\n memory?: RecipeMemoryPolicy;\n}\n\nexport interface RecipeMemoryPolicy {\n injectOnAssign?: boolean;\n autoCapture?: boolean;\n captureTypes?: MemoryType[];\n memoryTags?: string[];\n alwaysReload?: string[];\n scope?: string;\n maxTokens?: number;\n maxMemories?: number;\n priorityCutoff?: 1 | 2 | 3;\n ttlMs?: number;\n}\n\nexport interface RecipeInstance {\n id: string;\n recipeName: string;\n recipeSource: \"builtin\" | \"user\";\n cwd: string;\n orchestratorId: string;\n status: \"starting\" | \"running\" | \"stopping\" | \"stopped\" | \"failed\";\n agents: RecipeAgentInstance[];\n artifacts?: Artifact[];\n startedAt: number;\n stoppedAt?: number;\n startedBy: string;\n error?: string;\n}\n\nexport interface RecipeAgentInstance {\n role: string;\n agentId: string;\n provider: string;\n status: \"spawning\" | \"running\" | \"stopping\" | \"stopped\" | \"failed\";\n index?: number;\n}\n","import type { AttachmentRef } from \"./artifacts.js\";\nimport type { WorkspaceMode } from \"./workspaces.js\";\nimport type { SpawnProvider, SpawnEffort, SpawnApprovalMode } from \"./agents.js\";\n\nexport type TaskSeverity = \"info\" | \"warning\" | \"critical\";\nexport type TaskStatus =\n | \"open\"\n | \"claimed\"\n | \"in_progress\"\n | \"blocked\"\n | \"orphaned\"\n | \"done\"\n | \"failed\"\n | \"canceled\";\n\nexport type CommandStatus =\n | \"pending\"\n | \"accepted\"\n | \"running\"\n | \"succeeded\"\n | \"failed\"\n | \"timed_out\"\n | \"rejected\"\n | \"canceled\";\n\nexport interface Command {\n id: string;\n type: string;\n source: string;\n target: string;\n params: Record<string, unknown>;\n status: CommandStatus;\n result?: Record<string, unknown>;\n error?: string;\n correlationId?: string;\n createdAt: number;\n updatedAt: number;\n expiresAt?: number;\n}\n\nexport interface CreateCommandInput {\n type: string;\n source: string;\n target: string;\n params?: Record<string, unknown>;\n correlationId?: string;\n ttlMs?: number;\n}\n\nexport interface UpdateCommandInput {\n status?: CommandStatus;\n result?: Record<string, unknown>;\n error?: string;\n}\n\n// #834 — the durable first-class Task entity. The `tasks` table carries two\n// FACETS discriminated by `kind`: `claim` is the inbound claimable work-item lane\n// (the historical behaviour — alert→claim→lease→done); `deliverable` is the durable\n// coordination Task that threads issues/branch/commits/deps/gates. The claim-lease\n// machinery is fenced to `kind='claim'` (see `claimTaskWhere`), so the two never collide.\nexport const TASK_KINDS = [\"claim\", \"deliverable\"] as const;\nexport type TaskKind = (typeof TASK_KINDS)[number];\n\n// Deliverable-position axis (#834 status-as-audit-trail): orthogonal to the execution\n// `status` axis. Populated observably from the land/deploy pipeline, not by agent memory.\nexport const TASK_DELIVERABLE_STAGES = [\n \"none\",\n \"uncommitted\",\n \"committed-on-branch\",\n \"landed-on-main\",\n \"deploying\",\n \"deployed\",\n] as const;\nexport type TaskDeliverableStage = (typeof TASK_DELIVERABLE_STAGES)[number];\n\nexport const TASK_BLOCKER_TYPES = [\"dep\", \"gate\", \"decision\", \"external\"] as const;\nexport type TaskBlockerType = (typeof TASK_BLOCKER_TYPES)[number];\nexport interface TaskBlocker {\n type: TaskBlockerType;\n ref?: string;\n note?: string;\n}\n\n// Quality-gate ledger (#834): each run is an append-only `gate:<name>` task event.\n// `latestGates` projects the deterministic current state (highest event id per gate).\nexport const TASK_GATE_STATUSES = [\"pass\", \"fail\"] as const;\nexport type TaskGateStatus = (typeof TASK_GATE_STATUSES)[number];\nexport interface TaskGateState {\n gate: string;\n status: TaskGateStatus;\n detail?: string;\n count?: number;\n commitSha?: string;\n runId?: string;\n ranAt: number;\n}\n\nexport const TASK_DELIVERY_DISPOSITIONS = [\"complete\", \"blocked\", \"failed\", \"needs-review\"] as const;\nexport type TaskDeliveryDisposition = (typeof TASK_DELIVERY_DISPOSITIONS)[number];\nexport type TaskDeliveryAcceptanceStatus = \"met\" | \"unmet\";\n\nexport interface TaskDeliveryGateReport {\n status: TaskGateStatus;\n detail?: string;\n count?: number;\n runId?: string;\n}\n\nexport interface TaskDeliveryNamedGateReport extends TaskDeliveryGateReport {\n name: string;\n}\n\nexport interface TaskDeliveryGateReports {\n typecheck?: TaskDeliveryGateReport;\n test?: TaskDeliveryGateReport;\n other?: TaskDeliveryNamedGateReport[];\n}\n\nexport interface TaskDeliverySelfAcceptance {\n criterion: string;\n status: TaskDeliveryAcceptanceStatus;\n note?: string;\n}\n\nexport interface TaskDeliveryResult {\n disposition: TaskDeliveryDisposition;\n summary: string;\n reason?: string;\n blockedBy?: TaskBlocker[];\n commits?: TaskCommitRef[];\n gates?: TaskDeliveryGateReports;\n filesChanged?: number;\n rca?: string;\n testCoverage?: string;\n risks?: string[];\n selfAcceptance?: TaskDeliverySelfAcceptance[];\n}\n\nexport interface TaskDeliverableEventMetadata extends TaskDeliveryResult {\n v: 1;\n actor: string;\n workspaceId?: string;\n idempotencyKey?: string;\n land?: {\n requested: boolean;\n triggered: boolean;\n reason?: string;\n workspaceId?: string;\n workspaceStatus?: string;\n };\n}\n\n// Declarative work-shape (#839): the small + stable taxonomy a coordinator declares\n// instead of hand-picking host/provider/model/effort. The dispatcher (#839) maps this\n// shape + live system state → a concrete spawn (see src/task-dispatcher.ts).\nexport const TASK_DISPATCH_COMPLEXITIES = [\"trivial\", \"moderate\", \"complex\"] as const;\nexport const TASK_DISPATCH_TYPES = [\"build\", \"review\", \"research\", \"ops\", \"ui\", \"refactor\", \"debug\"] as const;\nexport const TASK_DISPATCH_FOOTPRINTS = [\"small\", \"medium\", \"large\"] as const;\nexport const TASK_DISPATCH_CRITICALITIES = [\"low\", \"normal\", \"high\"] as const;\nexport const TASK_COMPLEXITIES = TASK_DISPATCH_COMPLEXITIES;\nexport type TaskDispatchComplexity = (typeof TASK_DISPATCH_COMPLEXITIES)[number];\nexport type TaskComplexity = TaskDispatchComplexity;\nexport type TaskDispatchType = (typeof TASK_DISPATCH_TYPES)[number];\nexport type TaskDispatchFootprint = (typeof TASK_DISPATCH_FOOTPRINTS)[number];\nexport type TaskDispatchCriticality = (typeof TASK_DISPATCH_CRITICALITIES)[number];\nexport interface TaskDispatch {\n complexity?: TaskDispatchComplexity;\n type?: TaskDispatchType;\n contextFootprint?: TaskDispatchFootprint;\n criticality?: TaskDispatchCriticality;\n}\n\nexport interface TaskDispatchWorkShapeProjection {\n specified: boolean;\n complexity: TaskDispatchComplexity | null;\n type: TaskDispatchType | null;\n contextFootprint: TaskDispatchFootprint | null;\n criticality: TaskDispatchCriticality | null;\n}\n\nexport interface TaskDispatchDecisionProjection {\n target: {\n provider?: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n };\n rationale: string;\n pinned: boolean;\n factors?: Record<string, unknown>;\n warnings: string[];\n source: string;\n decidedAt: number;\n}\n\nexport interface TaskBoundWorkerModelProjection {\n agentId: string;\n provider?: string;\n model?: string;\n effort?: string;\n source?: string;\n confidence?: string;\n lastUpdatedAt?: number;\n}\n\nexport interface TaskCommitRef {\n sha: string;\n subject?: string;\n}\n\n// Issue link surfaced as a {provider,repo,externalKey} object (not a bare number) so\n// the bridge is unambiguous across repos (#833/#834). Projection of issue_associations.\nexport interface TaskIssueLink {\n issueRefId: string;\n provider: string;\n repo: string;\n externalKey: string;\n url: string;\n title?: string;\n status: string;\n}\n\nexport interface TaskDependency {\n taskId: number;\n dependsOnTaskId: number;\n createdAt: number;\n}\n\nexport interface Task {\n id: number;\n label?: string;\n source: string;\n title: string;\n body: string;\n severity: TaskSeverity;\n status: TaskStatus;\n target: string;\n channel?: string;\n dedupeKey?: string;\n externalUrl?: string;\n occurrenceCount: number;\n claimedBy?: string;\n claimedAt?: number;\n claimExpiresAt?: number;\n messageId?: number;\n result?: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n lastSeenAt: number;\n // --- #834 durable-Task facet (all optional; `claim`-kind rows leave them empty) ---\n kind?: TaskKind;\n intent?: string;\n complexity?: TaskComplexity;\n context?: string;\n latestSteer?: string;\n deliverableStage?: TaskDeliverableStage;\n blockedBy?: TaskBlocker[];\n branch?: string;\n commits?: TaskCommitRef[];\n doneWhen?: string;\n teamId?: string;\n projectId?: string;\n repo?: string;\n createdBy?: string;\n ownerAgentId?: string;\n ownerAgentLabel?: string;\n dispatch?: TaskDispatch;\n reapedAt?: number;\n}\n\nexport interface TaskListItem {\n id: number;\n label: string;\n title: string;\n status: TaskStatus;\n kind?: TaskKind;\n deliverableStage?: TaskDeliverableStage;\n teamId?: string;\n projectId?: string;\n repo?: string;\n createdBy?: string;\n ownerAgentId?: string;\n ownerAgentLabel?: string;\n branch?: string;\n createdAt: number;\n updatedAt: number;\n reapedAt?: number;\n}\n\n// Hydrated view returned by the task-entity get endpoints: the row plus its derived\n// projections (issue links, dependency edges, current gate state).\nexport interface TaskDetail extends Task {\n issues: TaskIssueLink[];\n dependsOn: number[];\n dependents: number[];\n gates: TaskGateState[];\n dispatchWorkShape: TaskDispatchWorkShapeProjection;\n dispatchDecision?: TaskDispatchDecisionProjection;\n boundWorkerModel?: TaskBoundWorkerModelProjection;\n}\n\nexport interface TaskEvent {\n id: number;\n taskId: number;\n source: string;\n type: string;\n severity: TaskSeverity;\n title: string;\n body: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n}\n\nexport interface IntegrationEventInput {\n source?: string;\n type?: string;\n severity?: TaskSeverity;\n status?: TaskStatus | \"resolved\";\n title: string;\n body: string;\n target: string;\n channel?: string;\n dedupeKey?: string;\n externalUrl?: string;\n attachments?: AttachmentRef[];\n metadata?: Record<string, unknown>;\n /**\n * When false, the task message is dispatched without a reply obligation. Scheduled automations\n * (#479) signal completion via the task-status lifecycle, not a `/reply`, so an unsatisfiable\n * obligation otherwise re-injects the task into every successor session on managed-agent restart.\n * Omit (default) to keep the obligation for integrations that expect a reply.\n */\n replyExpected?: boolean;\n}\n\nexport interface AlertEventInput {\n source: string;\n type: string;\n payload: Record<string, unknown>;\n severity?: TaskSeverity;\n status?: TaskStatus | \"resolved\";\n dedupeKey?: string;\n target: string;\n channel?: string;\n externalUrl?: string;\n attachments?: AttachmentRef[];\n metadata?: Record<string, unknown>;\n replyExpected?: boolean;\n title?: string;\n body?: string;\n}\n\nexport interface IntegrationEventResult {\n task: Task;\n event: TaskEvent;\n created: boolean;\n message?: import(\"./messages.js\").Message;\n}\n\nexport interface IntegrationTaskStats {\n source: string;\n tasks: number;\n openTasks: number;\n waitingTasks: number;\n failedTasks: number;\n lastSeenAt?: number;\n lastUpdatedAt?: number;\n}\n\nexport interface IntegrationSummary {\n name: string;\n displayName?: string;\n description?: string;\n enabled: boolean;\n configured: boolean;\n observed: boolean;\n type?: string;\n icon?: string;\n accentColor?: string;\n tags: string[];\n homepageUrl?: string;\n repositoryUrl?: string;\n docsUrl?: string;\n manifest?: Record<string, unknown>;\n scopes: string[];\n targets: string[];\n channels: string[];\n callbackHost?: string;\n callbackConfigured: boolean;\n rateLimit: {\n limitPerMinute: number;\n currentWindowCount: number;\n windowStartedAt?: number;\n };\n taskStats: IntegrationTaskStats;\n}\n\nexport type AutomationKind = \"scheduled_task\";\nexport type AutomationCatchUpPolicy = \"skip\" | \"run_once\" | \"run_all\";\nexport type AutomationConcurrencyPolicy = \"skip\" | \"queue\" | \"replace\";\nexport type AutomationRunStatus =\n | \"scheduled\"\n | \"dispatching\"\n | \"waiting_agent\"\n | \"running\"\n | \"succeeded\"\n | \"failed\"\n | \"canceled\"\n | \"timed_out\";\n\nexport interface AutomationExistingAgentPolicy {\n mode: \"existing_agent\";\n selector: {\n provider?: SpawnProvider;\n label?: string;\n tags?: string[];\n capabilities?: string[];\n };\n ifNoMatch?: \"fail\" | \"spawn\";\n}\n\nexport interface AutomationOnDemandAgentPolicy {\n mode: \"on_demand_agent\";\n provider: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n cwd?: string;\n workspaceMode?: WorkspaceMode;\n profile?: string;\n approvalMode?: SpawnApprovalMode;\n keepAlive?: boolean;\n runtimeBudget?: AutomationRuntimeBudget;\n shutdownAfterMs?: number;\n}\n\nexport type AutomationTargetPolicy = AutomationExistingAgentPolicy | AutomationOnDemandAgentPolicy;\n\nexport interface AutomationRuntimeBudget {\n maxRuntimeMs: number;\n warnAtMs?: number;\n warningMessage?: string;\n}\n\nexport interface AutomationTaskTemplate {\n title: string;\n body: string;\n severity?: TaskSeverity;\n dedupeKey?: string;\n externalUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface Automation {\n id: string;\n kind: AutomationKind;\n name: string;\n description?: string;\n enabled: boolean;\n schedule: string;\n timezone: string;\n nextRunAt?: number;\n catchUpPolicy: AutomationCatchUpPolicy;\n concurrencyPolicy: AutomationConcurrencyPolicy;\n orchestratorId: string;\n targetPolicy: AutomationTargetPolicy;\n taskTemplate: AutomationTaskTemplate;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface AutomationRun {\n id: string;\n automationId: string;\n status: AutomationRunStatus;\n scheduledFor: number;\n startedAt?: number;\n finishedAt?: number;\n orchestratorId: string;\n targetAgentId?: string;\n spawnedAgentId?: string;\n taskId?: number;\n messageId?: number;\n error?: string;\n result?: Record<string, unknown>;\n meta: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n shutdownRequestedAt?: number;\n}\n\nexport interface CreateAutomationInput {\n kind?: AutomationKind;\n name: string;\n description?: string;\n enabled?: boolean;\n schedule: string;\n timezone?: string;\n catchUpPolicy?: AutomationCatchUpPolicy;\n concurrencyPolicy?: AutomationConcurrencyPolicy;\n orchestratorId: string;\n targetPolicy: AutomationTargetPolicy;\n taskTemplate: AutomationTaskTemplate;\n}\n\nexport interface UpdateAutomationInput {\n name?: string;\n description?: string | null;\n enabled?: boolean;\n schedule?: string;\n timezone?: string;\n catchUpPolicy?: AutomationCatchUpPolicy;\n concurrencyPolicy?: AutomationConcurrencyPolicy;\n orchestratorId?: string;\n targetPolicy?: AutomationTargetPolicy;\n taskTemplate?: AutomationTaskTemplate;\n}\n\nexport interface TaskStatusInput {\n status: TaskStatus;\n agentId?: string;\n instanceId?: string;\n epoch?: number;\n result?: string;\n body?: string;\n metadata?: Record<string, unknown>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types-BqeSkWrv.js","names":[],"sources":["../../providers/registry.ts","../../providers/claude.ts","../../providers/codex.ts","../../dashboard/src/lib/provider-display.ts","../../sdk/src/types/guards.ts","../../sdk/src/types/constants.ts","../../sdk/src/types/workspaces.ts","../../sdk/src/types/tokens.ts","../../sdk/src/types/users.ts","../../sdk/src/types/messages.ts","../../sdk/src/types/agents.ts","../../sdk/src/types/tasks.ts"],"sourcesContent":["import type { ProviderManifest } from \"./types.js\";\n\nconst PROVIDER_ID_RE = /^[a-z][a-z0-9-]*$/;\nconst manifests = new Map<string, ProviderManifest>();\n\nexport function registerProvider(manifest: ProviderManifest): void {\n if (!PROVIDER_ID_RE.test(manifest.id)) {\n throw new Error(`invalid provider id: ${manifest.id}`);\n }\n if (!manifest.label.trim()) {\n throw new Error(`provider ${manifest.id} is missing a label`);\n }\n if (!manifest.icon.trim()) {\n throw new Error(`provider ${manifest.id} is missing an icon`);\n }\n if (manifests.has(manifest.id)) {\n throw new Error(`provider already registered: ${manifest.id}`);\n }\n manifests.set(manifest.id, manifest);\n}\n\nexport function getManifest(id: string): ProviderManifest | undefined {\n return manifests.get(id);\n}\n\nexport function getAllManifests(): ProviderManifest[] {\n return [...manifests.values()];\n}\n\nexport function isValidResumeId(providerId: string, id: string): boolean {\n if (!id) return false;\n const manifest = manifests.get(providerId);\n if (!manifest?.resume?.supported) return false;\n const pattern = manifest.resume.idPattern;\n if (!pattern) return true;\n return new RegExp(`^${pattern}$`, \"i\").test(id);\n}\n\nexport function resumeLogExtractionRe(providerId: string): RegExp | undefined {\n const manifest = manifests.get(providerId);\n if (!manifest?.resume?.supported || !manifest.resume.idPattern || !manifest.probe?.command) return undefined;\n return new RegExp(`\\\\b${manifest.probe.command}\\\\s+--resume\\\\s+(${manifest.resume.idPattern})\\\\b`, \"gi\");\n}\n\ntype AccountKeyAliasResolver = (accountKey: string) => string[];\nconst accountKeyAliasResolvers = new Map<string, AccountKeyAliasResolver>();\n\nexport function registerAccountKeyAliases(providerId: string, resolver: AccountKeyAliasResolver): void {\n accountKeyAliasResolvers.set(providerId, resolver);\n}\n\nexport function quotaAccountKeyAliases(providerId: string, accountKey: string): string[] {\n return accountKeyAliasResolvers.get(providerId)?.(accountKey) ?? [];\n}\n\nexport const PROVIDER_MANIFESTS: ReadonlyMap<string, ProviderManifest> = manifests;\n","import { registerProvider } from \"./registry.js\";\nimport type { ProviderManifest, ProviderModelCapabilities, ProviderModelLimits } from \"./types.js\";\n\nconst CLAUDE_LOW_TO_MAX = [\"low\", \"medium\", \"high\", \"max\"];\nconst CLAUDE_LOW_TO_XHIGH_MAX = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\nconst CONTEXT_200K = { value: 200_000, source: \"catalog\", confidence: \"declared\" } as const;\nconst CONTEXT_1M = { value: 1_000_000, source: \"catalog\", confidence: \"declared\" } as const;\nconst CODE_MODEL_CAPABILITIES: ProviderModelCapabilities = {\n modalities: {\n input: { text: true, image: true },\n output: { text: true },\n },\n tools: {\n code: true,\n review: true,\n debug: true,\n refactor: true,\n },\n source: \"catalog\",\n confidence: \"declared\",\n};\nconst quotaWindows = [\n { name: \"five_hour\", unit: \"%\", reset: true },\n { name: \"seven_day\", unit: \"%\", reset: true },\n] as const;\n\nfunction codeModel(limits?: ProviderModelLimits): Pick<NonNullable<ProviderManifest[\"catalog\"][\"models\"][number]>, \"limits\" | \"capabilities\"> {\n return {\n ...(limits ? { limits } : {}),\n capabilities: CODE_MODEL_CAPABILITIES,\n };\n}\n\nexport const claudeProviderManifest = {\n id: \"claude\",\n label: \"Claude\",\n icon: \"🟠\",\n launch: {\n managedPolicyPrompt: {\n alwaysOn: \"append-system-prompt\",\n },\n taskHydration: {\n channel: \"append-system-prompt\",\n },\n },\n catalog: {\n defaultModel: \"sonnet-5\",\n models: [\n { alias: \"fable-5\", label: \"Fable 5\", providerModel: \"claude-fable-5\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", unavailable: \"suspended by export-control directive, 2026-06-12\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"opus-4.8\", label: \"Opus 4.8\", providerModel: \"claude-opus-4-8\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"opus-4.8[1m]\", label: \"Opus 4.8 1M\", providerModel: \"claude-opus-4-8[1m]\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"opus-4.7\", label: \"Opus 4.7\", providerModel: \"claude-opus-4-7\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"opus-4.7[1m]\", label: \"Opus 4.7 1M\", providerModel: \"claude-opus-4-7[1m]\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"opus-4.6\", label: \"Opus 4.6\", providerModel: \"claude-opus-4-6\", efforts: CLAUDE_LOW_TO_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"opus-4.6[1m]\", label: \"Opus 4.6 1M\", providerModel: \"claude-opus-4-6[1m]\", efforts: CLAUDE_LOW_TO_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"sonnet-5\", label: \"Sonnet 5\", providerModel: \"claude-sonnet-5\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"sonnet-5[1m]\", label: \"Sonnet 5 1M\", providerModel: \"claude-sonnet-5[1m]\", efforts: CLAUDE_LOW_TO_XHIGH_MAX, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_1M }) },\n { alias: \"haiku\", label: \"Haiku\", providerModel: \"haiku\", efforts: [], ...codeModel() },\n ],\n },\n quotaWindows: [...quotaWindows],\n capabilityDefaults: {\n lifecycle: {\n managed: true,\n shutdownHard: { when: \"headless\", value: true, otherwise: false },\n restartHard: { when: \"headless\", value: true, otherwise: false },\n semanticStatus: true,\n reconnect: true,\n },\n session: {\n fileRead: true,\n fileWrite: { when: \"approval:not-read-only\", value: true, otherwise: false },\n shell: true,\n shellMode: { open: \"unrestricted\", guarded: \"guarded\", readOnly: \"read-only-guarded\" },\n },\n context: {\n managed: \"headless\",\n compact: { when: \"headless\", value: true, otherwise: false },\n clear: { when: \"headless\", value: true, otherwise: false },\n inject: true,\n resumePreference: [\"native\", \"clear-inject\", \"none\"],\n },\n quota: {\n supported: true,\n source: \"probe\",\n unit: \"%\",\n windows: [...quotaWindows],\n },\n terminal: {\n mode: \"live\",\n condition: \"headless\",\n live: { read: true, write: true },\n },\n liveSession: {\n capture: true,\n inject: true,\n interrupt: true,\n promptEcho: true,\n reasoning: true,\n slashCommands: true,\n },\n },\n slashCommands: [\"/clear\", \"/compact\", \"/model\", \"/help\"],\n resume: { supported: true, idFormat: \"uuid-v4\", idPattern: \"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\" },\n home: { configDir: \".claude\", envPrefix: \"CLAUDE\", configDirEnvVar: \"CLAUDE_CONFIG_DIR\", quotaCredentialFile: \".credentials.json\", authItems: [\".credentials.json\", \"statsig\"], globalInstructionFiles: [\"CLAUDE.md\"] },\n provisioning: {\n pluginManifestPath: \".claude-plugin/plugin.json\",\n bundledPlugin: {\n ref: \"agent-relay-claude-plugin\",\n dir: \"runner/plugins/claude\",\n title: \"Agent Relay Claude plugin\",\n description: \"Bundled Agent Relay Claude plugin, materialized whole through the provisioning registry.\",\n omitFiles: [\"monitors/relay-monitor.provisioned.mjs\"],\n fileOverrides: [{ path: \"monitors/relay-monitor.ts\", sourcePath: \"monitors/relay-monitor.provisioned.mjs\" }],\n },\n bundledHooks: [\n {\n ref: \"agent-relay-rg-r-guard\",\n dir: \"runner/hooks/rg-r-guard\",\n title: \"Agent Relay baseline hook: rg -r guard\",\n description: \"Baseline Agent Relay PreToolUse hook that blocks the ripgrep `rg -r` replace footgun.\",\n baseline: true,\n },\n ],\n },\n upgrade: {\n packages: [\"agent-relay-runner\", \"agent-relay-plugin\"],\n detectPackages: [\"agent-relay-runner\"],\n detectCommands: [\"claude\"],\n },\n probe: {\n command: \"claude\",\n args: [\"--version\"],\n healthCheck: \"claude --version\",\n featureChecks: [{ name: \"rig\", command: \"claude-rig\", homeDir: \".claude-rig\" }],\n },\n quotaPoll: { strategy: \"none\" },\n shim: { kind: \"claude-relay\" },\n} satisfies ProviderManifest;\n\nregisterProvider(claudeProviderManifest);\n","import { registerProvider } from \"./registry.js\";\nimport type { ProviderManifest, ProviderModelCapabilities, ProviderModelLimits } from \"./types.js\";\n\nconst CODEX_REASONING = [\"low\", \"medium\", \"high\", \"xhigh\"];\nconst CONTEXT_200K = { value: 200_000, source: \"catalog\", confidence: \"declared\" } as const;\nconst CODE_MODEL_CAPABILITIES: ProviderModelCapabilities = {\n modalities: {\n input: { text: true, image: true },\n output: { text: true },\n },\n tools: {\n code: true,\n review: true,\n debug: true,\n refactor: true,\n },\n source: \"catalog\",\n confidence: \"declared\",\n};\nconst quotaWindows = [\n { name: \"primary\", unit: \"%\", reset: true },\n { name: \"secondary\", unit: \"%\", reset: true },\n] as const;\n\nfunction codeModel(limits?: ProviderModelLimits): Pick<NonNullable<ProviderManifest[\"catalog\"][\"models\"][number]>, \"limits\" | \"capabilities\"> {\n return {\n ...(limits ? { limits } : {}),\n capabilities: CODE_MODEL_CAPABILITIES,\n };\n}\n\nexport const codexProviderManifest = {\n id: \"codex\",\n label: \"Codex\",\n icon: \"⌨️\",\n launch: {\n managedPolicyPrompt: {\n alwaysOn: \"prompt\",\n },\n taskHydration: {\n channel: \"prompt\",\n },\n },\n catalog: {\n defaultModel: \"gpt-5.5\",\n models: [\n { alias: \"gpt-5.5\", label: \"GPT-5.5\", providerModel: \"gpt-5.5\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.4\", label: \"GPT-5.4\", providerModel: \"gpt-5.4\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.4-mini\", label: \"GPT-5.4 Mini\", providerModel: \"gpt-5.4-mini\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.3-codex\", label: \"GPT-5.3 Codex\", providerModel: \"gpt-5.3-codex\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.3-codex-spark\", label: \"GPT-5.3 Codex Spark\", providerModel: \"gpt-5.3-codex-spark\", efforts: CODEX_REASONING, defaultEffort: \"high\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n { alias: \"gpt-5.2\", label: \"GPT-5.2\", providerModel: \"gpt-5.2\", efforts: CODEX_REASONING, defaultEffort: \"medium\", ...codeModel({ contextWindowTokens: CONTEXT_200K }) },\n ],\n },\n quotaWindows: [...quotaWindows],\n capabilityDefaults: {\n lifecycle: {\n managed: true,\n shutdownHard: true,\n restartHard: true,\n semanticStatus: true,\n reconnect: true,\n },\n session: {\n fileRead: true,\n fileWrite: { when: \"approval:not-read-only\", value: true, otherwise: false },\n shell: { when: \"approval:not-read-only\", value: true, otherwise: false },\n shellMode: { open: \"unrestricted\", guarded: \"guarded\", readOnly: \"none\" },\n },\n context: {\n managed: \"always\",\n compact: true,\n clear: true,\n inject: true,\n resumePreference: [\"native\", \"clear-inject\", \"none\"],\n },\n quota: {\n supported: true,\n source: \"probe\",\n unit: \"%\",\n windows: [...quotaWindows],\n },\n terminal: {\n mode: \"attach\",\n attach: {\n create: true,\n read: true,\n write: true,\n detach: true,\n },\n },\n liveSession: {\n capture: true,\n inject: true,\n interrupt: true,\n promptEcho: true,\n reasoning: true,\n slashCommands: true,\n },\n },\n slashCommands: [\"/compact\", \"/new\"],\n resume: { supported: true, idFormat: \"codex-thread-id\", idPattern: \"[\\\\w-]+\" },\n home: { configDir: \".codex\", envPrefix: \"CODEX\", configDirEnvVar: \"CODEX_HOME\", quotaCredentialFile: \"auth.json\", authItems: [\"auth.json\", \"installation_id\"], globalInstructionFiles: [\"AGENTS.md\"] },\n provisioning: {\n pluginManifestPath: \".codex-plugin/plugin.json\",\n bundledSkills: {\n refPrefix: \"agent-relay-codex-\",\n dir: \"runner/plugins/codex/skills\",\n titlePrefix: \"Agent Relay Codex skill: \",\n description: \"Bundled Agent Relay Codex skill, materialized through the provisioning registry.\",\n entryFile: \"SKILL.md\",\n },\n bundledHooks: [\n {\n ref: \"agent-relay-rg-r-guard\",\n dir: \"runner/hooks/rg-r-guard\",\n title: \"Agent Relay baseline hook: rg -r guard\",\n description: \"Baseline Agent Relay PreToolUse hook that blocks the ripgrep `rg -r` replace footgun.\",\n baseline: true,\n },\n ],\n },\n upgrade: {\n packages: [\"agent-relay-runner\", \"agent-relay-codex\"],\n detectPackages: [\"agent-relay-runner\", \"agent-relay-codex\"],\n detectCommands: [\"codex-relay\", \"agent-relay-runner\"],\n },\n probe: {\n command: \"codex\",\n args: [\"--version\"],\n healthCheck: \"codex --version\",\n },\n quotaPoll: { strategy: \"codex-app-server\" },\n shim: { kind: \"codex-relay\", extraBinPaths: [\"codex/bin/codex\"], cleanLegacyHooks: true },\n} satisfies ProviderManifest;\n\nregisterProvider(codexProviderManifest);\n","import { getAllManifests, getManifest } from 'agent-relay-providers'\nimport type { ProviderManifest } from 'agent-relay-providers'\n\nconst BADGE_PALETTE = [\n 'bg-amber-500/10 text-amber-400 border-amber-500/30',\n 'bg-sky-500/10 text-sky-400 border-sky-500/30',\n 'bg-violet-500/10 text-violet-400 border-violet-500/30',\n 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30',\n 'bg-rose-500/10 text-rose-400 border-rose-500/30',\n]\n\nfunction stableHash(s: string): number {\n let h = 0\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0\n return h\n}\n\nconst badgeClassCache = new Map<string, string>(\n getAllManifests().map((m, i) => [m.id, BADGE_PALETTE[i % BADGE_PALETTE.length]!])\n)\n\nexport function getProviderLabel(id: string): string {\n return getManifest(id)?.label ?? id.charAt(0).toUpperCase() + id.slice(1)\n}\n\nexport function getProviderIcon(id: string): string {\n return getManifest(id)?.icon ?? '🔧'\n}\n\nexport function getProviderBadgeClass(id: string): string {\n const cached = badgeClassCache.get(id)\n if (cached) return cached\n const cls = BADGE_PALETTE[stableHash(id) % BADGE_PALETTE.length]!\n badgeClassCache.set(id, cls)\n return cls\n}\n\nexport function getKnownProviderIds(): string[] {\n return getAllManifests().map((m) => m.id)\n}\n\nexport function getProviderManifest(id: string): ProviderManifest | undefined {\n return getManifest(id)\n}\n","// --- Shared micro-helpers (single source of truth; do not re-declare per file) ---\n\n/** True for a non-null, non-array object. The canonical type guard for the whole repo. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Narrow `unknown` to a non-empty trimmed string, else `undefined`.\n * Settled semantics: whitespace-only is treated as empty (returns `undefined`).\n */\nexport function stringValue(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\n/** Extract a human-readable message from any thrown value. */\nexport function errMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","// --- Relay connection defaults ---\n\n/** Default port the relay server listens on. */\nconst DEFAULT_RELAY_PORT = 4850;\n\n/** Default relay base URL. Loopback spelling settled on `127.0.0.1` (not `localhost`). */\nexport const DEFAULT_RELAY_URL = `http://127.0.0.1:${DEFAULT_RELAY_PORT}`;\n\n// Shared callmux must outlive tokenlean's 90-110s async long-poll window; this\n// matches callmux's documented default instead of the MCP SDK's shorter cap.\nexport const SHARED_CALLMUX_TOOL_CALL_TIMEOUT_MS = 180_000;\n","import type { LandGateRunResult } from \"./land-gates.js\";\n\n/** Workspace modes — runtime tuple + derived type + guard. */\nexport const VALID_WORKSPACE_MODES = [\"isolated\", \"shared\", \"inherit\"] as const;\nexport type WorkspaceMode = (typeof VALID_WORKSPACE_MODES)[number];\nexport function isWorkspaceMode(value: unknown): value is WorkspaceMode {\n return typeof value === \"string\" && (VALID_WORKSPACE_MODES as readonly string[]).includes(value);\n}\n/** Narrow an unknown to a WorkspaceMode, or undefined if it isn't one. */\nexport function normalizeWorkspaceMode(value: unknown): WorkspaceMode | undefined {\n return isWorkspaceMode(value) ? value : undefined;\n}\nexport type WorkspaceStatus = \"active\" | \"ready\" | \"conflict\" | \"review_requested\" | \"merge_planned\" | \"merged\" | \"abandoned\" | \"cleanup_requested\" | \"cleaned\";\n/** Terminal workspace statuses shared across server + dashboard filters. */\nexport const TERMINAL_WORKSPACE_STATUS_VALUES = [\"cleaned\", \"merged\", \"abandoned\"] as const satisfies readonly WorkspaceStatus[];\n\n/** How a workspace's work is integrated back into its base branch. */\nexport type WorkspaceMergeStrategy = \"pr\" | \"rebase-ff\" | \"auto\";\n\n/** Policy words accepted as the user-facing landing strategy (normalised to WorkspaceMergeStrategy). */\nexport type WorkspaceLandingPolicy = \"direct\" | \"pr\";\n\n/**\n * When to auto-merge a PR that was opened by a `pr`-strategy land.\n * `on-green` — merge once all required status checks pass (default for pr lands).\n * `on-approval` — merge once a reviewer has approved.\n * `manual` — open the PR but never auto-merge; the human hits the button.\n */\nexport const AUTO_MERGE_POLICIES = [\"on-green\", \"on-approval\", \"manual\"] as const;\nexport type WorkspaceAutoMergePolicy = (typeof AUTO_MERGE_POLICIES)[number];\n\n/** Instance-wide landing configuration (stored in config-store under namespace \"landing\"). */\nexport interface LandingConfig {\n /** Default strategy for all workspace lands. \"direct\" → \"auto\" (host picks rebase-ff/pr); \"pr\" → always open a PR. */\n strategy: WorkspaceLandingPolicy;\n}\n\nexport interface WorkspaceProbeWorktree {\n path: string;\n branch?: string;\n headSha?: string;\n bare?: boolean;\n detached?: boolean;\n prunable?: boolean;\n locked?: boolean;\n reason?: string;\n}\n\nexport interface WorkspaceProbe {\n path: string;\n isGitRepo: boolean;\n repoRoot?: string;\n branch?: string;\n headSha?: string;\n dirty?: boolean;\n detached?: boolean;\n worktrees?: WorkspaceProbeWorktree[];\n error?: string;\n}\n\ninterface WorkspaceGitCommit {\n sha: string;\n message: string;\n at?: number;\n}\n\n/**\n * Live git state of a workspace worktree, computed on the host by the\n * orchestrator. Used to surface whether a worktree contains actual work\n * (commits ahead of base, dirty files) so the dashboard isn't a black box and\n * so exit-time reconcile can decide cleanup-vs-flag.\n */\nexport interface WorkspaceGitState {\n /** Commits on the agent branch not yet in base (raw `rev-list` count). */\n ahead?: number;\n /** Commits still genuinely unmerged after discounting work already present in\n * base via a squash/cherry-pick (git-cherry '+' count). Equals `ahead` for a\n * normal branch; 0 when the work landed but left `ahead` positive. */\n unmergedAhead?: number;\n /** True when `ahead` > 0 but the work is already in base (squash/cherry-pick\n * merge) — the worktree is effectively merged and safe to clean up. */\n landed?: boolean;\n /** Commits in base not in the agent branch (how far base moved on). */\n behind?: number;\n /** True if the worktree has uncommitted/untracked changes. */\n dirty?: boolean;\n /** Number of dirty (porcelain) entries. */\n dirtyCount?: number;\n /** Most recent commit in the worktree. */\n lastCommit?: WorkspaceGitCommit;\n /** Ref/sha the ahead/behind counts were computed against. */\n baseRef?: string;\n /** Branch currently checked out in the worktree (for recorded-vs-live mismatch). */\n branch?: string;\n /** Set when the worktree path no longer exists on disk. */\n missing?: boolean;\n /** Populated when git interrogation failed. */\n error?: string;\n}\n\n/**\n * Pre-flight check for integrating a workspace's work, computed on the host.\n * Reports whether the merge would be clean, conflict, or is a no-op, and which\n * strategy `auto` would pick — so the dashboard can warn before the user acts.\n */\nexport interface WorkspaceMergePreview {\n /** Strategy `auto` would resolve to given the repo's remote/gh state. */\n strategy: \"pr\" | \"rebase-ff\";\n /** Commits on the agent branch not yet in base (raw count). */\n ahead?: number;\n /** Genuinely-unmerged commits after discounting squash/cherry-pick landings. */\n unmergedAhead?: number;\n /** True when the work already landed in base via squash/cherry-pick. */\n landed?: boolean;\n /** Merged-PR state (ground truth when local git can't see a squash landing because\n * base moved on). Populated only with `checkPr` (a `gh` round-trip). */\n prState?: \"merged\" | \"open\";\n /** True when the branch's PR is merged on the remote. `prMergeSha` is its merge commit,\n * recorded as the landed SHA when the relay finalizes an out-of-band PR merge (#304). */\n prMerged?: boolean;\n prMergeSha?: string;\n /** Subject (PR title) of the merged PR, carried into the `branch.landed` notice so it\n * reads with the landed change's title, not just a bare SHA (#423). */\n prMergeSubject?: string;\n /** URL of the branch's PR, when one was found via `gh`. */\n prUrl?: string; prNumber?: number; reviewDecision?: \"APPROVED\" | \"CHANGES_REQUESTED\" | \"REVIEW_REQUIRED\"; statusCheckRollup?: unknown[]; mergeable?: string; mergeStateStatus?: string;\n /** Commits in base not in the agent branch. */\n behind?: number;\n /** Current branch tip SHA observed by the host. */\n headSha?: string;\n /** Host-local ref used to protect the worker tip while stewardship/merge recovery runs. */\n protectedTipRef?: string;\n /** SHA currently stored in the protected worker-tip ref. */\n protectedTip?: string;\n /** False when the protected tip exists but the branch no longer contains it. */\n protectedTipReachable?: boolean;\n /** True when the host restored a clean reset-to-base branch from the protected tip. */\n protectedTipRestored?: boolean;\n /** Rescue ref written before a protected-tip restore discards the previously checked-out HEAD. */\n protectedTipDiscardedRef?: string;\n /** The HEAD SHA captured in `protectedTipDiscardedRef`. */\n protectedTipDiscardedHead?: string;\n /** Why protected-tip validation/restoration failed. */\n protectedTipError?: string;\n /** Branch ref is gone, but this protected workspace-tip commit can still recover the work. */\n recoverableTip?: string;\n /** Host-local ref that holds recoverableTip. */\n recoverableTipRef?: string;\n /** Uncommitted/untracked entries in the worktree. */\n dirtyCount?: number;\n /** True if the three-way merge of base and the branch would conflict. */\n conflict?: boolean;\n /** True if base can fast-forward to the branch with no rebase needed. */\n cleanFastForward?: boolean;\n /** Whether an `origin` remote is configured. */\n hasRemote?: boolean;\n /** Whether the `gh` CLI is available on the host. */\n ghAvailable?: boolean;\n /** Resolved base branch name the work would land on. */\n baseRef?: string;\n /** Human-readable reason a merge can't proceed (no work, dirty, missing). */\n reason?: string;\n /** True when there is nothing to land: ahead=0 with a clean worktree (branch tree\n * already in base — never diverged, or already landed via squash/cherry-pick/PR).\n * A no-op land resolves the workspace to a terminal state instead of parking it in\n * the steward queue (#230). Distinct from a dirty-worktree refusal, which also sets\n * `reason` but is not a no-op. */\n noop?: boolean;\n /** Set when the worktree path no longer exists. */\n missing?: boolean;\n /** Populated when git interrogation failed. */\n error?: string;\n}\n\n/**\n * Outcome of bringing a DIRTY checked-out base worktree forward after a land advanced\n * its ref by plumbing (#644/#681/#824). The land already succeeded — this reports whether\n * the shared/primary checkout's WORKING FILES caught up to the advanced HEAD, or whether it\n * was left in a MIXED state (HEAD moved, working files didn't) that needs manual\n * reconciliation before anything reads/builds/publishes from that checkout (#824).\n */\nexport interface BaseWorktreeSyncResult {\n /** True only when every landed path in the checkout matches the advanced HEAD — no mixed\n * state. False means the checkout is stranded/mixed and must be surfaced loudly. */\n reconciled: boolean;\n /** Landed paths whose working copy still differs from the advanced HEAD after the\n * best-effort heal. A mixed state: reading/packing from this checkout sees STALE content\n * for these paths (the #824 strand that mis-fed the #823 bootstrap publish). */\n unsyncedPaths?: string[];\n /** Subset of {@link unsyncedPaths} that carried genuine uncommitted human edits — preserved\n * verbatim (never clobbered, #681), so a fix must merge by hand rather than blind-restore. */\n preservedWipPaths?: string[];\n /** Human-readable summary for logs and the operator alert. */\n message?: string;\n}\n\n/** Outcome of a `workspace.merge` command executed on the host. */\nexport interface WorkspaceMergeResult {\n workspaceId?: string;\n /** Strategy actually used. */\n strategy: \"pr\" | \"rebase-ff\";\n /** True when work was landed locally (rebase-ff into base). */\n merged: boolean;\n /** True when the land was a no-op — nothing to merge (ahead=0, clean worktree),\n * resolved to a terminal `merged` status to clear the steward queue (#230). The\n * worktree/branch are reclaimed too when the owner is gone (`deleteBranch`). */\n noop?: boolean;\n /** True when a merge was prevented by conflicts. */\n conflict?: boolean;\n /** PR URL when the pr strategy opened one. */\n prUrl?: string;\n branch?: string;\n baseRef?: string;\n /** SHA of the landed commit — the agent branch's tip, preserved verbatim (#287).\n * This is the SHA reported in the `branch.landed` notice, so it exists on base\n * exactly as the agent produced it (no rebase rewrite). On a clean fast-forward\n * it equals {@link baseSha}; on a no-ff merge it's the merge's second parent. */\n mergedSha?: string;\n /** SHA base points at after the land (#287): equals {@link mergedSha} on a\n * fast-forward, or the merge commit on a no-ff merge when base had advanced. The\n * relay records this as the recycled workspace's new base_sha. Absent on older\n * orchestrators — consumers fall back to {@link mergedSha}. */\n baseSha?: string;\n /** Subject line of the landed commit (git log -1 --format=%s of mergedSha), for\n * the `branch.landed` notification body (#239). Best-effort; absent on older\n * orchestrators or when the subject can't be read. */\n subject?: string;\n branchDeleted?: boolean;\n /** Present when branch deletion was skipped because the branch still carried unlanded work. */\n branchPreservedReason?: string;\n worktreeRemoved?: boolean;\n /** Fresh branch the worktree was recycled onto after a land-and-continue merge\n * (#206) — the relay repoints the workspace row at it. Absent for terminal lands. */\n newBranch?: string;\n /** True when the landed base branch was pushed to its upstream (origin). */\n pushed?: boolean;\n /** Resulting workspace status the relay should record. */\n status: WorkspaceStatus;\n /** Deps re-provisioned after a land-and-continue recycle when the advanced base\n * brought new dependencies (issue #51). Absent when nothing was stale. */\n depsRefresh?: WorkspaceDepsRefreshResult;\n /** True when GitHub auto-merge was successfully armed after PR creation (#305).\n * False when arming was attempted but the repo has auto-merge disabled. */\n autoMergeArmed?: boolean;\n /** True when the PR was opened but auto-merge was NOT armed because the\n * autoMerge policy is \"on-approval\" — a reviewer pipeline arms it later (#305). */\n awaitingApproval?: boolean;\n /** Populated when the merge could not complete. */\n error?: string;\n /** Present when a land advanced a DIRTY checked-out base by ref plumbing (#644). When\n * {@link BaseWorktreeSyncResult.reconciled} is false the shared checkout is in a mixed\n * state the relay surfaces as a loud operator alert (#824) — the land still succeeded. */\n baseWorktreeSync?: BaseWorktreeSyncResult;\n /** #902 — a REQUIRED land gate failed against the candidate merged tree, so the\n * land was aborted WITHOUT advancing the ref (merged:false, status\n * \"review_requested\" — NEVER \"conflict\": gate failures bounce to the worker, never\n * the steward). The relay uploads the full `output` to an artifact and notifies the\n * worker + coordinator with the gate name + `outputTail` + artifact ref. Absent\n * entirely on a repo with no `.agent-relay/land-gates.json`, so that path is\n * byte-identical to today. */\n gateFailure?: LandGateRunResult;\n /** #902 — optional land gates that FAILED but did not block: the land still\n * succeeded (merged:true). The relay surfaces these as warnings (tail + artifact ref)\n * without bouncing the land. */\n gateWarnings?: LandGateRunResult[];\n}\n\n/**\n * Joined steward briefing for one workspace (#208): the row, owner + orchestrator\n * liveness, live git state, recorded-vs-live branch mismatch, any active steward\n * claim, and a recommended next action — so a steward (or release agent) doesn't\n * reconstruct it from ad-hoc shell + API calls.\n */\nexport interface WorkspaceDiagnostics {\n workspaceId: string;\n status: WorkspaceStatus;\n mode: WorkspaceMode;\n repoRoot: string;\n worktreePath?: string;\n recordedBranch?: string;\n liveBranch?: string;\n baseRef?: string;\n branchMismatch?: boolean;\n owner?: { id?: string; status?: string; online: boolean };\n /**\n * The branch agent's coordinator — the owner's `spawnedBy` lineage resolved to a live\n * agent (#640). `id` is null when no coordinator resolves (owner unknown or not spawned\n * by anyone); steward escalation targets it before the configured fallback.\n */\n coordinator?: { id: string | null; online: boolean };\n orchestrator?: { id?: string; online: boolean };\n /** Active claim that auto-merge yields to (#208), if held and unexpired. */\n claim?: { by?: string; purpose?: string; expiresAt?: number };\n /** Live worktree git state (proxied from the host); absent when unavailable. */\n gitState?: WorkspaceGitState;\n /** Why git state is absent (host offline, no worktree, …). */\n gitStateUnavailable?: string;\n recommendation: { action: \"merge\" | \"rebase\" | \"cleanup\" | \"review\" | \"wait\" | \"none\"; confidence: \"high\" | \"medium\" | \"low\"; reason: string };\n}\n\n/** One changed file in a workspace diff against its base. */\nexport interface WorkspaceDiffFile {\n path: string;\n /** Lines added (undefined for binary files). */\n additions?: number;\n /** Lines removed (undefined for binary files). */\n deletions?: number;\n binary?: boolean;\n}\n\n/** Diff of a workspace's committed work against its base, computed on the host. */\nexport interface WorkspaceDiff {\n baseRef?: string;\n ahead?: number;\n files: WorkspaceDiffFile[];\n /** Unified patch text (may be truncated). */\n patch?: string;\n /** True when the patch was capped at the size limit. */\n truncated?: boolean;\n /** Uncommitted entries in the worktree, not part of the committed diff. */\n dirtyCount?: number;\n missing?: boolean;\n error?: string;\n}\n\n/** A worktree found on disk with no matching active workspace row. */\nexport interface WorkspaceOrphan {\n worktreePath: string;\n repoRoot: string;\n /** Orchestrator that discovered this orphan on disk. Use for cleanup routing. */\n orchestratorId?: string;\n /** Relay agent id for the discovering orchestrator. */\n orchestratorAgentId?: string;\n branch?: string;\n /** Base ref used to prove the orphan branch is landed before branch-ref cleanup. */\n baseRef?: string;\n /** Base SHA recorded for the workspace when available. */\n baseSha?: string;\n headSha?: string;\n /** True if a DB row exists for this path but is already terminal (cleaned/merged). */\n hadTerminalRow?: boolean;\n /** Work already merged into base (squash/cherry/PR). Set from the host probe. */\n landed?: boolean;\n /** Raw commits ahead of base (a squash-landed branch still shows >0). */\n ahead?: number;\n /** Commits ahead whose patch isn't already in base — the squash-aware count. */\n unmergedAhead?: number;\n /** Uncommitted working-tree changes in the worktree. */\n dirty?: boolean;\n /**\n * Safe to remove with no loss: clean tree AND (nothing ahead OR already landed).\n * `false` means the worktree holds un-landed work and must be flagged, not reaped.\n * `undefined` means the host couldn't be probed — treat as not safe.\n */\n safeToReap?: boolean;\n}\n\n/** Host-local view of an agent/* branch that may need recovery review. */\nexport interface WorkspaceRecoveryBranch {\n repoRoot: string;\n /** Orchestrator that discovered this branch. Use for branch-discard routing. */\n orchestratorId?: string;\n /** Relay agent id for the discovering orchestrator. */\n orchestratorAgentId?: string;\n branch: string;\n headSha?: string;\n baseRef?: string;\n baseSha?: string;\n ahead?: number;\n unmergedAhead?: number;\n landed?: boolean;\n lastCommit?: WorkspaceGitCommit;\n ageMs?: number;\n /** True when the branch has no unique unlanded work relative to base. */\n safeToDelete?: boolean;\n /** Why safety could not be proven or why work must be reviewed first. */\n preserveReason?: string;\n /** Server-side enrichment when a workspace row still records this branch. */\n workspaceId?: string;\n ownerAgentId?: string;\n ownerStatus?: string;\n cleanupCommandId?: string;\n cleanupPreservedReason?: string;\n preservedByCleanup?: boolean;\n activeWorkspace?: boolean;\n}\n\nexport interface WorkspaceRecoveryBranchList {\n repoRoot?: string;\n branches: WorkspaceRecoveryBranch[];\n scannedRepos?: string[];\n reason?: string;\n}\n\nexport interface WorkspaceRecoveryBranchDiscardResult {\n repoRoot: string;\n branch: string;\n branchDeleted: boolean;\n forced?: boolean;\n preserveReason?: string;\n headSha?: string;\n}\n\n/**\n * Outcome of provisioning node_modules into a freshly created isolated worktree.\n * Git worktrees do not share gitignored/untracked files, so a new worktree starts\n * with no installed deps — typecheck/test/build would see thousands of phantom\n * missing-module errors. We either symlink the source checkout's node_modules\n * (fast, default) or run a package install (when the source has none to borrow).\n */\nexport interface WorkspaceDepsProvision {\n mode: \"symlink\" | \"install\" | \"none\";\n /** Relative dirs (from repo root) whose node_modules were symlinked. */\n linked?: string[];\n /** Relative dirs where a package install ran successfully. */\n installed?: string[];\n packageManager?: string;\n error?: string;\n}\n\n/** Result of symlinking configured untracked files/dirs from main into an isolated worktree. */\nexport interface WorkspaceSymlinkProvision {\n /** Relative paths (from repo root) that were symlinked from the main checkout. */\n linked: string[];\n /** Per-pattern failures, if any (best-effort: a failure never blocks the spawn). */\n errors?: string[];\n}\n\n/** One project dir's outcome in a workspace deps refresh (issue #51). */\nexport interface WorkspaceDepsRefreshDir {\n /** Relative dir from repo root (`.` for root, e.g. `dashboard`). */\n dir: string;\n /** ok — declared deps already present; stale — missing (check-only, not installed);\n * installed — re-installed in isolation; failed — install errored. */\n status: \"ok\" | \"stale\" | \"installed\" | \"failed\";\n /** Declared deps (dependencies + devDependencies) missing from node_modules, capped sample. */\n missing?: string[];\n /** True when the dir's node_modules was a shared symlink replaced with a real install. */\n wasSymlink?: boolean;\n packageManager?: string;\n error?: string;\n}\n\n/**\n * Outcome of refreshing an isolated worktree's deps (issue #51). The default symlink\n * provisioning shares the source checkout's node_modules, so a dep added to the base\n * AFTER worktree creation is missing in the worktree. Refresh promotes each stale dir\n * to a real, isolated install — never mutating the shared source node_modules.\n */\nexport interface WorkspaceDepsRefreshResult {\n /** True when at least one dir was actually (re)installed. */\n refreshed: boolean;\n /** True (check-only) when any dir is stale, or (refresh) when any install left deps missing. */\n stale?: boolean;\n dirs: WorkspaceDepsRefreshDir[];\n error?: string;\n}\n\nexport interface WorkspaceMetadata {\n id?: string;\n mode: WorkspaceMode;\n requestedMode?: WorkspaceMode;\n repoRoot?: string;\n sourceCwd?: string;\n worktreePath?: string;\n branch?: string;\n baseRef?: string;\n baseSha?: string;\n status?: WorkspaceStatus;\n stewardAgentId?: string;\n /** Per-spawn override for autonomous workspace landing. Omit to use instance landing.strategy. */\n landStrategy?: WorkspaceLandingPolicy;\n /** PR auto-merge policy used when landStrategy resolves to `pr`. */\n autoMerge?: WorkspaceAutoMergePolicy;\n /** Optional reviewer handle or policy target for reviewer-gated PR lands. */\n reviewer?: string;\n deps?: WorkspaceDepsProvision;\n /** Configured untracked files/dirs symlinked from main (see WorkspaceConfig.symlinkPaths). */\n symlinks?: WorkspaceSymlinkProvision;\n probe?: WorkspaceProbe;\n}\n\nexport interface WorkspaceRecord {\n id: string;\n repoRoot: string;\n sourceCwd: string;\n worktreePath: string;\n /** Server-enriched project resolution for repoRoot/sourceCwd; not persisted on the workspace row. */\n projectId?: string;\n projectSlug?: string;\n projectTitle?: string;\n branch?: string;\n baseRef?: string;\n baseSha?: string;\n mode: WorkspaceMode;\n requestedMode?: WorkspaceMode;\n status: WorkspaceStatus;\n /** Durable host owner for host-local isolated worktrees. */\n orchestratorId?: string;\n ownerAgentId?: string;\n /** Server-derived from ownerAgentId via the authoritative owner liveness helper. */\n ownerOnline?: boolean;\n ownerPolicyName?: string;\n ownerAutomationRunId?: string;\n stewardAgentId?: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n readyAt?: number;\n cleanedAt?: number;\n}\n","// ──────────────────────────────────────────────────────────────────────────\n// Settings descriptors (epic #380, #382) — the self-describing contract any\n// surface (core relay, provider, channel, connector) registers so the API\n// (#383), scopes (#385), module wiring (#386), and UI (#384) all build on one\n// abstraction. This is the *declaration* layer over the existing `config`\n// table — NOT a storage change. The registry home is `src/settings/registry.ts`.\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * A typed JSON Schema (draft-07-compatible). Common keywords are spelled out so\n * descriptors get type-checked field shapes; the `[key: string]: unknown` tail\n * keeps the door open for any keyword `ajv` understands (the registry compiles\n * each schema with `ajv`, so anything ajv accepts is valid). Declared here (not\n * as `Record<string, unknown>`) so the descriptor's `schema` field is typed —\n * the sdk stays dependency-free; only the server imports `ajv` to compile it.\n */\nexport type JSONSchemaTypeName =\n | \"string\"\n | \"number\"\n | \"integer\"\n | \"boolean\"\n | \"object\"\n | \"array\"\n | \"null\";\n\nexport interface JSONSchema {\n $schema?: string;\n $id?: string;\n $ref?: string;\n $defs?: Record<string, JSONSchema>;\n definitions?: Record<string, JSONSchema>;\n title?: string;\n description?: string;\n type?: JSONSchemaTypeName | JSONSchemaTypeName[];\n enum?: unknown[];\n const?: unknown;\n default?: unknown;\n examples?: unknown[];\n // numeric\n minimum?: number;\n maximum?: number;\n exclusiveMinimum?: number;\n exclusiveMaximum?: number;\n multipleOf?: number;\n // string\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n format?: string;\n // array\n items?: JSONSchema | JSONSchema[];\n minItems?: number;\n maxItems?: number;\n uniqueItems?: boolean;\n // object\n properties?: Record<string, JSONSchema>;\n required?: string[];\n additionalProperties?: boolean | JSONSchema;\n patternProperties?: Record<string, JSONSchema>;\n // composition\n allOf?: JSONSchema[];\n anyOf?: JSONSchema[];\n oneOf?: JSONSchema[];\n not?: JSONSchema;\n // escape hatch for any other JSON Schema keyword\n [key: string]: unknown;\n}\n\n/**\n * Where a setting lives and how it can be changed:\n * - `server-runtime` — instance-global; stored in the `config` table, mutable at runtime via the API.\n * - `bootstrap` — sourced from env/file at boot; surfaced read-only (the API exposes it but cannot write it).\n * - `browser` — a client-only preference; the server never persists it (UI/local-storage owns it).\n * - `user` — per-human preference (#391); stored in `user_settings` keyed by the resolved `users.id`,\n * mutable at runtime. The same `(namespace, key)` descriptor address resolves to a per-user value,\n * so a pref follows the human across devices. Defaults to the seeded `user:default` (#393) until\n * sessions (#389) map a request to its user.\n */\nexport type SettingTier = \"server-runtime\" | \"bootstrap\" | \"browser\" | \"user\";\n\n/**\n * The scopes (#385) a caller must hold to read or write a setting. Carried as\n * opaque scope-token strings — #385 owns the grammar and `hasScope` matching;\n * this layer only declares the requirement. Registration defaults both to the\n * fail-closed admin scope when a descriptor omits them.\n */\nexport interface SettingScope {\n read: string;\n write: string;\n}\n\n/** Display metadata for the Settings UI (#384) — grouping, labelling, ordering. */\nexport interface SettingDisplay {\n /** Grouping bucket the UI renders this setting under (e.g. \"Agents\", \"Notifications\"). */\n group: string;\n /** Human-readable label. */\n label: string;\n /** Optional longer description / help text. */\n description?: string;\n /** Sort order within the group (ascending; unset sorts last). */\n order?: number;\n}\n\n/**\n * A self-describing setting. Any surface registers one of these; the registry is\n * the single source of truth for which namespaces exist, their schema, defaults,\n * required scope, tier, and display. `scope` is always populated on a registered\n * descriptor (the registry fills the fail-closed admin default) so consumers\n * never null-check it. `namespace`/`key` map directly onto the `config` table's\n * composite PK.\n */\nexport interface SettingDescriptor<T = unknown> {\n /** Maps onto the `config` table namespace (e.g. \"steward\", \"landing\"). */\n namespace: string;\n /** Maps onto the `config` table key within the namespace (e.g. \"default\"). */\n key: string;\n /** Full JSON Schema for the value — ajv-compiled at registration (#382/#383). */\n schema: JSONSchema;\n /** Fallback value used when the config row is unset. */\n defaults: T;\n /** Read/write scopes required to access this setting (#385). */\n scope: SettingScope;\n /** Mutability/source tier. */\n tier: SettingTier;\n /** Display metadata for the Settings UI (#384). */\n display: SettingDisplay;\n /**\n * For a `type:\"object\"` value keyed by arbitrary caller-chosen ids (a per-conversation\n * map, not a fixed set of fields): a PUT merges the body's top-level keys into the\n * stored value instead of replacing it wholesale, so one client's write can never\n * clobber another client's write to a different key (#1124). The merged result is\n * capped at the schema's `maxProperties` (oldest-`updatedAt`-first eviction) so\n * unbounded per-key merges from many callers can't grow the row forever.\n */\n mergeMapKeys?: boolean;\n}\n\n/** Runtime settings API entry. `version: 0` means descriptor defaults only; no config row is stored. */\nexport interface SettingEntry<T = unknown> extends SettingDescriptor<T> {\n value: T;\n version: number;\n updatedAt: string;\n updatedBy?: string;\n readable: boolean;\n writable: boolean;\n}\n\nconst SCOPE_ALIASES: Record<string, string> = {\n \"admin:*\": \"system:admin\",\n \"system:write\": \"system:admin\",\n \"agents:read\": \"agent:read\",\n \"agents:write\": \"agent:write\",\n \"messages:read\": \"message:read\",\n \"messages:write\": \"message:send\",\n \"tasks:read\": \"task:read\",\n \"tasks:write\": \"task:write\",\n \"commands:read\": \"command:read\",\n \"commands:write\": \"command:write\",\n \"channels:read\": \"channel:read\",\n \"channels:write\": \"channel:write\",\n \"integrations:read\": \"integration:read\",\n \"integrations:write\": \"integration:write\",\n \"connectors:read\": \"integration:read\",\n \"connectors:write\": \"integration:write\",\n \"events:create\": \"integration:write\",\n \"tasks:create\": \"task:write\",\n};\n\nexport function normalizeScope(scope: string): string {\n return SCOPE_ALIASES[scope] ?? scope;\n}\n\nexport function hasScope(scopes: readonly string[] | undefined, requiredScope: string): boolean {\n const required = normalizeScope(requiredScope);\n const requiredParts = parseScopeParts(required);\n if (!requiredParts) return false;\n if (scopes?.includes(\"*\")) return true;\n for (const rawScope of scopes ?? []) {\n const scope = normalizeScope(rawScope);\n const scopeParts = parseScopeParts(scope);\n if (!scopeParts) continue;\n if (scope === \"system:admin\") return true;\n if (scope === required) return true;\n if (scope === `${requiredParts.category}:*`) return true;\n if (\n requiredParts.category === \"settings\" &&\n requiredParts.action &&\n requiredParts.namespace &&\n scopeParts.category === \"settings\" &&\n scopeParts.action === requiredParts.action &&\n scopeParts.namespace === undefined\n ) return true;\n }\n return false;\n}\n\nfunction parseScopeParts(scope: string): { category: string; action?: string; namespace?: string } | null {\n if (scope === \"*\") return { category: \"*\" };\n const parts = scope.split(\":\");\n if (parts.some((part) => !part)) return null;\n if (parts.length === 2) {\n const [category, action] = parts as [string, string];\n return { category, action };\n }\n if (parts.length === 3) {\n const [category, action, namespace] = parts as [string, string, string];\n if (category !== \"settings\") return null;\n if (action !== \"read\" && action !== \"write\") return null;\n if (namespace === \"*\") return null;\n return { category, action, namespace };\n }\n return null;\n}\n\nexport type PairStatus = \"pending\" | \"active\" | \"ended\" | \"rejected\" | \"expired\";\n\nexport interface PairSession {\n id: string;\n requesterId: string;\n targetId: string;\n status: PairStatus;\n objective?: string;\n createdAt: number;\n updatedAt: number;\n expiresAt: number;\n acceptedAt?: number;\n endedAt?: number;\n endedBy?: string;\n lastMessageAt?: number;\n meta: Record<string, unknown>;\n}\n\nexport interface CreatePairInput {\n from: string;\n target: string;\n objective?: string;\n ttlMs?: number;\n meta?: Record<string, unknown>;\n}\n\nexport interface PairActionInput {\n agentId: string;\n reason?: string;\n}\n\nexport interface PairMessageInput {\n from: string;\n body: string;\n subject?: string;\n}\n\nexport interface PollQuery {\n for: string; // agent-id\n since?: number; // unix ms (createdAt cursor)\n sinceId?: number; // monotonic message id cursor (preferred — avoids same-ms collisions)\n unread?: boolean;\n channel?: string;\n limit?: number;\n}\n\nexport interface ComponentToken {\n sub: string;\n role: \"provider\" | \"channel\" | \"orchestrator\" | \"admin\" | \"dashboard\" | string;\n scope: string[];\n constraints?: TokenConstraints;\n iat: number;\n exp?: number;\n jti?: string;\n}\n\nexport interface TokenRecord {\n jti: string;\n sub: string;\n role: string;\n scope: string[];\n constraints?: TokenConstraints;\n profileId?: string;\n issuedAt: number;\n expiresAt?: number;\n revokedAt?: number;\n /** Why the token was revoked (#484): \"stale\" | \"rotation\" | \"admin\" | \"agent-removed\" | \"pruned\". Drives self-heal eligibility. */\n revokedReason?: string;\n createdBy?: string;\n}\n\nexport interface TokenProfile {\n id: string;\n name: string;\n description?: string;\n role: string;\n scope: string[];\n constraints?: TokenConstraints;\n ttlSeconds?: number;\n builtIn: boolean;\n createdAt: number;\n updatedAt: number;\n createdBy?: string;\n}\n\nexport interface CreateTokenProfileInput {\n id?: string;\n name: string;\n description?: string;\n role: string;\n scope: string[];\n constraints?: TokenConstraints;\n ttlSeconds?: number;\n createdBy?: string;\n}\n\nexport type UpdateTokenProfileInput = Partial<Omit<CreateTokenProfileInput, \"id\">>;\n\nexport interface TokenConstraints {\n agents?: string[];\n policies?: string[];\n parentAgents?: string[];\n /** Parent agent id stamped on a child's runner token at spawn; read at registration to\n * set the child agent card's `spawnedBy` authoritatively (the child can't forge it). */\n spawnedBy?: string;\n /** Durable team id stamped on runner tokens; identity-only grouping, never an authorization gate. */\n teamId?: string;\n /** Durable project id resolved from the spawn cwd (containment match against project_roots)\n * and stamped on runner tokens; identity-only grouping (callerProject reads it), never an\n * authorization gate. Absent when the cwd is inside no registered root (#405). */\n projectId?: string;\n /** Resolved agent-profile NAME stamped on a runner token at spawn (#559). Identity-only,\n * like projectId — it lets the scoped-memory tools resolve the caller's OWN profile store\n * (self-scoped writes: an agent writes only its own profile's memory, never another's).\n * Absent for non-spawned/human tokens. */\n profile?: string;\n /** Tenant id baked into a human session token at login (#389), copied from the user's\n * `users.tenant_id`. Identity-only today: it carries the SaaS multi-tenant seam into the token\n * so isolation becomes an additive change later, but enforces NOTHING — no auth path branches on\n * it yet. Absent for single-tenant users with a null `tenant_id`. */\n tenantId?: string;\n /** Spawn quota (max concurrent live children) baked into a spawn-capable agent's token,\n * resolved from its profile's `maxSpawnedAgents`. The runtime quota check reads it here. */\n maxSpawnedAgents?: number;\n targets?: string[];\n channels?: string[];\n orchestrators?: string[];\n hosts?: string[];\n cwd?: string;\n cwdPrefixes?: string[];\n taskIds?: string[];\n memoryScopes?: string[];\n integrationNames?: string[];\n spawnRequestIds?: string[];\n terminalAttach?: boolean;\n terminalRead?: boolean;\n terminalWrite?: boolean;\n logsRead?: boolean;\n canDelegate?: boolean;\n /** Relay tool group filter (#551): only tools in these groups are listed at tools/list. Absent = full surface. */\n toolGroups?: string[];\n}\n\ntype TokenScope =\n | \"system:admin\"\n | \"token:read\"\n | \"token:write\"\n | \"agent:read\"\n | \"agent:write\"\n | \"message:read\"\n | \"message:send\"\n | \"schedule:write\"\n | \"schedule:persistent\"\n | \"task:read\"\n | \"task:write\"\n | \"command:read\"\n | \"command:write\"\n | \"command:*\"\n | \"artifact:read\"\n | \"artifact:write\"\n | \"artifact:admin\"\n | \"memory:read\"\n | \"memory:write\"\n | \"memory:admin\"\n | \"mcp:use\"\n | \"teams:read\"\n | \"teams:write\"\n | \"terminal:attach\"\n | \"terminal:read\"\n | \"terminal:write\"\n | \"logs:read\"\n | \"integration:read\"\n | \"integration:write\"\n | \"channel:read\"\n | \"channel:write\"\n | \"stats:read\"\n | \"health:read\"\n | \"events:read\"\n | \"command:spawn\"\n | \"command:shutdown\"\n | \"recipe:start\"\n | \"recipe:stop\"\n | \"admin:*\";\n\nexport interface ConfigEntry<T = unknown> {\n namespace: string;\n key: string;\n value: T;\n version: number;\n updatedAt: string;\n updatedBy?: string;\n}\n\nexport interface ConfigHistoryEntry<T = unknown> {\n id: number;\n namespace: string;\n key: string;\n value: T;\n version: number;\n changedAt: string;\n changedBy?: string;\n}\n\nexport interface MaintenanceJob {\n id: string;\n title: string;\n description?: string;\n intervalMs: number;\n timeoutMs: number;\n enabled: boolean;\n runOnStart: boolean;\n lastRunAt?: number;\n nextRunAt?: number;\n lastDurationMs?: number;\n lastStatus: \"idle\" | \"running\" | \"succeeded\" | \"failed\" | \"disabled\";\n lastError?: string;\n lastResult?: Record<string, unknown>;\n consecutiveFailures: number;\n running: boolean;\n leaseUntil?: number;\n updatedAt: number;\n}\n\nexport interface MaintenanceJobRun {\n id: string;\n status: \"succeeded\" | \"failed\" | \"skipped\";\n startedAt: number;\n finishedAt: number;\n durationMs: number;\n result?: Record<string, unknown>;\n error?: string;\n}\n\nexport interface HealthCheck {\n name: string;\n status: \"ok\" | \"warn\" | \"error\";\n detail?: string;\n count?: number;\n metrics?: Record<string, number>;\n subjects?: Array<{ id: string; label?: string; status?: string; detail?: string }>;\n}\n\nexport interface HealthReport {\n status: \"ok\" | \"degraded\" | \"error\";\n version: string;\n generatedAt: number;\n checks: HealthCheck[];\n}\n","// First-class human users (#388 / keystone #393). Promotes the human from the hardcoded\n// `'user'` sink to a real identity record. Addressing is per-user (`user:<id>`); the bare\n// `'user'` sink is the back-compat alias of the seeded `user:default` admin.\n//\n// SINGLE RESOLVER HOME (duplication ratchet): the `'user'` ≡ `user:default` ≡\n// `users.id='default'` mapping lives ONLY here. Every funnel site and every later #388 wave\n// calls these helpers rather than re-deriving the namespace — one place owns human addressing.\n\n/** Addressing prefix for the per-user human namespace: `user:<id>`. */\nexport const USER_NAMESPACE_PREFIX = \"user:\";\n\n/** Stable relational id of the admin user seeded on first boot, mapped to today's single human. */\nexport const DEFAULT_USER_ID = \"default\";\n\n/** Legacy bare human sink, retained as a back-compat alias of `user:default`. */\nexport const LEGACY_HUMAN_AGENT_ID = \"user\";\n\n/** Canonical addressing form for a human user: `user:<id>`. */\nexport function userSinkId(userId: string): string {\n return `${USER_NAMESPACE_PREFIX}${userId}`;\n}\n\n/**\n * True for any human address: the legacy bare `'user'` sink or any `user:<id>` in the\n * namespace. The human stopped being a singleton in #393 — prefer this over a literal\n * `id === 'user'` comparison so per-user sinks are recognized everywhere the human is.\n */\nexport function isHumanAgentId(id: string | undefined): boolean {\n return id === LEGACY_HUMAN_AGENT_ID || (typeof id === \"string\" && id.startsWith(USER_NAMESPACE_PREFIX));\n}\n\n/**\n * The relational `users.id` behind any human address, or `undefined` for a non-human id.\n * Both the legacy bare `'user'` and the `user:default` alias resolve to {@link DEFAULT_USER_ID};\n * `user:<id>` yields `<id>`. This is the bridge from a wire/message address to a `users` row.\n */\nexport function userIdFromAddress(id: string | undefined): string | undefined {\n if (id === LEGACY_HUMAN_AGENT_ID) return DEFAULT_USER_ID;\n if (typeof id === \"string\" && id.startsWith(USER_NAMESPACE_PREFIX)) {\n return id.slice(USER_NAMESPACE_PREFIX.length) || DEFAULT_USER_ID;\n }\n return undefined;\n}\n\n/**\n * The stored/canonical wire id for a human address, used for routing + storage so legacy bytes\n * never move (#393 Option A). The default user's canonical stored id is the legacy bare `'user'`,\n * so the working `user:default` alias collapses to it and matches every pre-existing\n * message/`operator_id`/inbox row. Every other `user:<id>` is already canonical. Non-human ids\n * pass through unchanged.\n */\nexport function canonicalHumanAgentId(id: string): string {\n // The degenerate empty address `user:` collapses to the default sink too, so it never lands\n // between resolvers: `userIdFromAddress('user:')` already yields DEFAULT_USER_ID, and without\n // this `canonicalHumanAgentId('user:')` returned `'user:'` — one helper said \"default human\",\n // the other routed to a `user:` agent a provider could squat (#393). Both now agree.\n if (id === LEGACY_HUMAN_AGENT_ID || id === userSinkId(DEFAULT_USER_ID) || id === USER_NAMESPACE_PREFIX) {\n return LEGACY_HUMAN_AGENT_ID;\n }\n return id;\n}\n\n/** Role gate for a user. Enforcement (ownership/authz) lands in #392 — this carries the value. */\nexport type UserRole = \"admin\" | \"operator\" | \"viewer\";\n\n/** Account lifecycle. `active` is the only state a fresh/seeded user takes today. */\nexport type UserStatus = \"active\" | \"suspended\" | \"deleted\";\n\nexport interface User {\n /** Stable id; the addressing form is `user:<id>` (see {@link userSinkId}). Others FK to this. */\n id: string;\n /** Short login-ish handle, unique within a tenant. */\n handle: string;\n displayName: string;\n role: UserRole;\n status: UserStatus;\n /**\n * SaaS multi-tenant seam (#393): nullable, defaults to the seeded `tenant:default`. The relay\n * is single-tenant today; the column existing now makes multi-tenant an additive migration\n * rather than a second identity refactor. Do NOT key isolation on it yet.\n */\n tenantId: string | null;\n /**\n * Content-addressed avatar via the existing artifact stack (#395/#630): nullable FK →\n * `artifacts.id`. This sub-issue adds the column only — upload/serve is out of scope.\n */\n avatarArtifactId: string | null;\n /** Authenticated API-relative image path for the current avatar, or null when unset. */\n avatarUrl: string | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface CreateUserInput {\n id: string;\n handle: string;\n displayName: string;\n role?: UserRole;\n status?: UserStatus;\n tenantId?: string | null;\n avatarArtifactId?: string | null;\n}\n","import type { AttachmentRef } from \"./artifacts.js\";\nimport { isRecord } from \"./guards.js\";\nimport { isHumanAgentId } from \"./users.js\";\n\nexport type MessageKind =\n | \"chat\"\n | \"channel.event\"\n | \"task\"\n | \"pair\"\n | \"control\"\n | \"system\"\n | \"session\";\n\n/**\n * Mechanical message kinds: the relay's own lifecycle/observability lane, not\n * agent-directed messaging. They bypass delivery resolution (never re-delivered\n * into a session) and — when targeting a reserved sink — the recipient-constraint\n * auth check (a managed token's `targets`/`policies`/`agents` constraints gate which\n * *agents* it may message, not a session-mirror capture to the reserved sink; #284).\n * Keep in sync with the `MessageKind` union.\n */\nexport const MECHANICAL_MESSAGE_KINDS: readonly MessageKind[] = [\"system\", \"control\", \"session\"];\n\nexport function isMechanicalMessageKind(kind: string | undefined): boolean {\n return MECHANICAL_MESSAGE_KINDS.includes((kind ?? \"\") as MessageKind);\n}\n\n/**\n * Reserved built-in identities that are not real agents: the human chat/mirror sink(s)\n * and the `system` lifecycle sender. They are never registered, polled, or delivered to\n * like agents, so recipient constraints don't apply to mechanical posts addressed to them.\n *\n * The human is no longer a singleton (#393): the bare `'user'` sink is the legacy alias of\n * the seeded default user, and every per-user sink lives in the `user:` namespace\n * (`user:<id>`). Use {@link isHumanAgentId} / {@link isReservedAgentId} rather than comparing\n * against this list, which only carries the bare built-in ids. All `user:`-namespace\n * addressing lives in one home — see `./users.js`.\n */\nexport const RESERVED_AGENT_IDS: readonly string[] = [\"user\", \"system\"];\n\nexport function isReservedAgentId(id: string | undefined): boolean {\n return id === \"system\" || isHumanAgentId(id);\n}\n\n/**\n * Session-mirror event taxonomy. Every `kind: \"session\"` message carries a\n * `payload.session` of this shape so the dashboard can render the live provider\n * session faithfully regardless of which surface (chat box or web terminal)\n * started the turn. `prompt`/`response` render as chat bubbles; `narration` is\n * the agent's intermediate spoken text between tool calls (the terminal's `●`\n * lines) and renders inline in the turn's activity trace; `reasoning`, `tool`,\n * `notice`, and `relay-injection` render discreetly (collapsed/inline activity,\n * never bubbles).\n * A legacy session message with no `payload.session` is treated as a `response`.\n */\ntype SessionEventType = \"prompt\" | \"response\" | \"narration\" | \"reasoning\" | \"tool\" | \"notice\" | \"relay-injection\" | \"prompt-resolution\";\n\ntype SessionEventOrigin = \"chat\" | \"terminal\" | \"provider\";\nexport type MessageTurnEndReason = \"completed\" | \"interrupted\" | \"failed\";\nexport type MessageFinalityConfidence = \"definitive\" | \"reconstructed\";\nexport type MessageWorkFinalitySource = \"workspace-ready\" | \"task-deliver\" | \"task-done\" | \"explicit-reply\" | \"report-final\" | \"edge-fallback\";\nexport type RelayInjectionCategory = \"memory\" | \"instructions\" | \"tools\" | \"comms\" | \"knowledge\" | \"context\";\n\nexport interface RelayInjectionEventDetail {\n category: RelayInjectionCategory;\n summary: string;\n detail: Record<string, unknown>;\n agentId: string;\n timestamp: number;\n}\n\nexport interface PromptResolutionEventDetail {\n provider: \"claude\";\n kind: \"questions\" | \"plan\" | \"command\" | \"tool\";\n title: string;\n body: string;\n promptBody?: string;\n toolName: string;\n hookEventName: string;\n approvalId: string;\n decision: string;\n decisionLabel: string;\n /** Provider-native time when the prompt/question was asked. */\n occurredAt?: number;\n /** Time when the user/dashboard resolved the prompt. */\n resolvedAt?: number;\n questions?: unknown[];\n answers?: Record<string, string>;\n}\n\nexport interface MessageSessionMeta {\n type: SessionEventType;\n /** Where the event originated. `terminal` = typed into the provider directly. */\n origin?: SessionEventOrigin;\n /** Groups reasoning/tool steps and the final response under one provider turn. */\n turnId?: string;\n /** Monotonic provider-side sequence for this turn when the provider can supply one. */\n turnSeq?: number;\n /** True only for a provider turn-final capture; intermediate/preflush response text omits this. */\n turnFinal?: boolean;\n /** Why the provider turn ended. */\n endedBy?: MessageTurnEndReason;\n /** Whether the turn-final edge was provider-native or reconstructed from persisted state. */\n confidence?: MessageFinalityConfidence;\n /** Provider/source that supplied or reconstructed the turn-final edge. */\n source?: string;\n /** Legacy alias for `turnFinal`. Writers dual-stamp true values for old consumers. */\n final?: boolean;\n /** Short label: tool name (\"Bash\"), \"Thinking\", a slash command, etc. */\n label?: string;\n /** Tool/step lifecycle status when type is \"tool\": running | completed | failed. */\n status?: \"running\" | \"completed\" | \"failed\";\n /** Completion time for step rows whose original occurredAt is preserved across upserts. */\n completedAt?: number;\n /** True while the step body is still being streamed in (coalesced deltas). */\n streaming?: boolean;\n /** Provider that produced the event. */\n provider?: string;\n /** Relay-owned injection/provisioning action rendered as a system timeline row. */\n relayInjection?: RelayInjectionEventDetail;\n /** Resolved interactive prompt rendered as durable Q+A/approval context. */\n promptResolution?: PromptResolutionEventDetail;\n /**\n * Stable provider-side step id (e.g. a Codex app-server item id). When present, the\n * server UPSERTS the session row keyed on (from, stepId) instead of appending — so a\n * step that emits more than once (a tool's running→completed lifecycle, a streamed\n * reasoning/response that grows) persists as ONE row updated in place, not duplicate\n * rows. Absent → legacy append behavior (the Claude transcript path emits no stepId\n * and is unchanged). The first row's occurredAt is preserved; completion time\n * is carried separately as completedAt when available.\n */\n stepId?: string;\n}\n\nexport interface MessageWorkFinality {\n windowId: string;\n source: MessageWorkFinalitySource;\n groupOrder: number;\n markedAt: number;\n}\n\nexport interface MessagePayload extends Record<string, unknown> {\n session?: MessageSessionMeta | Record<string, unknown>;\n responseCapture?: MessageSessionMeta | Record<string, unknown>;\n finality?: MessageWorkFinality | Record<string, unknown>;\n}\n\nexport function isTurnFinalSession(session: unknown): session is MessageSessionMeta {\n return isRecord(session) && (session.turnFinal === true || session.final === true);\n}\n\nexport function hasWorkFinality(payload: unknown): payload is MessagePayload & { finality: MessageWorkFinality } {\n return isRecord(payload) && isRecord(payload.finality);\n}\n\nexport function isWorkFinalMessagePayload(payload: unknown): payload is MessagePayload {\n if (!isRecord(payload)) return false;\n if (hasWorkFinality(payload)) return true;\n const session = payload.session;\n return isRecord(session) && session.final === true && session.turnFinal !== true;\n}\n\nexport type MessageDeliveryStatus = \"pending\" | \"delivered\" | \"queued\" | \"failed\" | \"dead\";\n\nexport interface Message {\n id: number;\n from: string;\n to: string; // agent-id | \"tag:<name>\" | \"team:<id>\" | \"broadcast\" | \"cap:<name>\"\n kind: MessageKind;\n channel?: string;\n subject?: string;\n body: string;\n threadId?: number;\n replyTo?: number;\n // Server-owned reply obligation (#283). Absent/true = the message wants a response (default);\n // false = a notification (FYI, merge notice, lifecycle event) that must NOT be replied to.\n // The server keys footer rendering and the reply-obligation tracker off this, not off `from`.\n replyExpected?: boolean;\n claimable?: boolean;\n claimedBy?: string;\n claimedAt?: number;\n claimExpiresAt?: number;\n idempotencyKey?: string;\n deliveryStatus?: MessageDeliveryStatus;\n deliveryAttempts?: number;\n deliveryLastError?: string;\n deliveryNextRetryAt?: number;\n deliveryPoisonReason?: string;\n deliveryUpdatedAt?: number;\n queuedAt?: number;\n maxAgeSeconds?: number;\n resolvedToAgent?: string;\n payload: MessagePayload;\n meta?: Record<string, unknown>;\n reactions?: MessageReaction[];\n readBy: string[];\n createdAt: number;\n // True event time (#196). Equals createdAt for messages posted live; differs only when a\n // Runner backfilled a message queued during an outage. Read `occurredAt ?? createdAt`.\n occurredAt?: number;\n}\n\nexport interface ReplyObligation {\n messageId: number;\n agentId: string;\n from: string;\n kind: MessageKind;\n source?: string;\n subject?: string;\n channel?: string;\n bodyPreview: string;\n createdAt: number;\n replyCommand: string;\n}\n\nexport interface MessageReaction {\n messageId: number;\n actorId: string;\n emoji: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport type RelayNotificationKind =\n | \"message\"\n | \"reply\"\n | \"error\"\n | \"agent.blocked\";\n\nexport type RelayNotificationSeverity = \"info\" | \"warning\" | \"error\";\n\nexport type NotificationRoutingSubscriptionState = \"deliver\" | \"suppress\" | \"abstain\" | \"unknown\";\n\nexport interface NotificationRoutingRecipientInput {\n agentId: string;\n matchedRule?: string;\n reason?: string;\n}\n\nexport interface NotificationRoutingExplanation {\n type: string;\n recipient: string;\n matchedRule: string;\n reason: string;\n scope: Record<string, unknown>;\n subscriptionState: NotificationRoutingSubscriptionState;\n settingsEnabled: boolean;\n}\n\nexport interface NotificationRoutingPreview {\n type: string;\n scope: Record<string, unknown>;\n recipients: NotificationRoutingExplanation[];\n suppressed: NotificationRoutingExplanation[];\n}\n\nexport interface RelayNotification {\n id: string;\n kind: RelayNotificationKind;\n severity: RelayNotificationSeverity;\n title: string;\n body: string;\n messageId?: number;\n agentId?: string;\n threadPeer?: string;\n view?: \"chat\" | \"messages\" | \"agents\" | \"work\" | \"tasks\" | \"channels\" | \"overview\";\n routing?: NotificationRoutingExplanation;\n createdAt: number;\n}\n\nexport interface MessageDeliveryAttempt {\n id: number;\n messageId: number;\n agentId?: string;\n action: \"attempt\" | \"retry-now\" | \"mark-dead\" | \"clear\";\n status: MessageDeliveryStatus;\n error?: string;\n nextRetryAt?: number;\n poisonReason?: string;\n createdAt: number;\n}\n\nexport interface MessageDeliveryState extends Pick<Message,\n | \"id\"\n | \"to\"\n | \"deliveryStatus\"\n | \"deliveryAttempts\"\n | \"deliveryLastError\"\n | \"deliveryNextRetryAt\"\n | \"deliveryPoisonReason\"\n | \"deliveryUpdatedAt\"\n | \"queuedAt\"\n | \"maxAgeSeconds\"\n | \"resolvedToAgent\"\n> {\n attempts: MessageDeliveryAttempt[];\n}\n\nexport interface SendMessageInput {\n from: string;\n to: string;\n kind?: MessageKind;\n channel?: string;\n subject?: string;\n body: string;\n replyTo?: number;\n // Defaults to true server-side. Set false to mark a notification (no reply wanted) — the\n // server suppresses the reply-scaffold footer and appends a one-line no-reply nudge (#283).\n replyExpected?: boolean;\n claimable?: boolean;\n idempotencyKey?: string;\n // TTL in seconds after which the message is stale and is dropped at the delivery/poll gate (#451).\n // `undefined` = unspecified → the server applies its default (a moderate TTL for fire-and-forget\n // fan-out, none for direct DMs / obligations / claimable). An explicit `null` = authoritative\n // never-expire: it suppresses the fan-out default (used by routeNotification for store-ahead\n // notification types whose TTL is decided by the router, not the message baseline).\n maxAgeSeconds?: number | null;\n attachments?: AttachmentRef[];\n payload?: MessagePayload;\n meta?: Record<string, unknown>;\n // Epoch-ms event time, stamped when the event actually occurred (#196). When a Runner\n // queues a message through its durable outbox during a server outage, this preserves the\n // true time rather than the (later) server receive time. Defaults server-side to created_at.\n occurredAt?: number;\n}\n\nexport interface InboxThreadState {\n operatorId: string;\n peerId: string;\n readCursorMessageId?: number;\n archivedAtMessageId?: number;\n updatedAt: number;\n}\n\nexport interface InboxDraft {\n operatorId: string;\n peerId: string;\n body: string;\n subject?: string;\n channel?: string;\n updatedAt: number;\n}\n\nexport interface InboxState {\n operatorId: string;\n threads: InboxThreadState[];\n drafts: InboxDraft[];\n}\n\nexport interface ChatHistoryImportEntry {\n position: number;\n originalMessageId: number;\n originalFrom: string;\n originalTo: string;\n originalCreatedAt: number;\n message: Message;\n}\n\nexport interface ChatHistoryImport {\n id: string;\n targetAgentId?: string;\n targetSpawnRequestId?: string;\n sourcePeerId: string;\n sourceAgentId?: string;\n sourceThreadId?: string;\n sourceAgentLabel?: string;\n importedBy: string;\n importedAt: number;\n entries: ChatHistoryImportEntry[];\n}\n\nexport type ActivityKind = \"message\" | \"reply\" | \"question\" | \"operator\" | \"pair\" | \"task\" | \"state\" | \"issue\";\n\nexport interface ActivityEvent {\n id: number;\n operatorId?: string;\n clientId?: string;\n kind: ActivityKind;\n title: string;\n body?: string;\n meta?: string;\n icon?: string;\n view?: string;\n peer?: string;\n messageId?: number;\n pairId?: string;\n taskId?: number;\n agentId?: string;\n issueRefId?: string;\n issueAction?: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n}\n\nexport interface ActivityEventInput {\n operatorId?: string;\n clientId?: string;\n kind: ActivityKind;\n title: string;\n body?: string;\n meta?: string;\n icon?: string;\n view?: string;\n peer?: string;\n messageId?: number;\n pairId?: string;\n taskId?: number;\n agentId?: string;\n issueRefId?: string;\n issueAction?: string;\n metadata?: Record<string, unknown>;\n}\n","import { getAllManifests } from \"agent-relay-providers\";\nimport type { WorkspaceMode, WorkspaceMetadata, WorkspaceStatus } from \"./workspaces.js\";\nimport type { MemoryType, Memory, TaskRoutingHints, ContextBudget } from \"./memory.js\";\nimport type { Artifact } from \"./artifacts.js\";\nexport type { AgentProfile, AgentProfileAssetRef, AgentProfileBase, AgentProfileCategoryMode, AgentProfileFilesystemScope, AgentProfileHostAssets, AgentProfileInstructionPolicy, AgentProfileNotificationResponseMode, AgentProfileProjectInstructionPolicy, AgentProfileProjectMcpPolicy, AgentProfileProjectSettingsPolicy, AgentProfileProvider, AgentProfileProviderOptions, AgentProfileProjectionEntry, AgentProfileProjectionReport, AgentProfileProjectionResult, AgentProfileSharedMcpPolicy, ContinuationBudget, ContinuationDecisionEntry, ContinuationEnvelope, ContinuationLedgerEntry, InsightObservation, InsightsConfig, InsightsStats, NotificationsConfig, SelfResumeConfig, SelfResumeMode, SpawnPolicy, StewardConfig, WorkspaceConfig } from \"./provisioning.js\";\n\nexport type AgentKind = \"provider\" | \"channel\" | \"orchestrator\" | \"system\" | \"user\";\n\nexport interface AgentCard {\n id: string;\n name: string;\n kind: AgentKind;\n label?: string; // human-friendly alias; acts as a fan-out target (\"label:foo\")\n tags: string[];\n machine?: string;\n rig?: string;\n capabilities: string[];\n ready: boolean;\n status: AgentStatus;\n instanceId?: string;\n epoch: number;\n providerCapabilities?: ProviderCapabilities;\n context?: ContextState;\n quota?: QuotaState;\n meta?: import(\"./provider-state.js\").AgentMeta;\n lifecycle: AgentLifecycle;\n /** Authoritative parent id from the child's signed runner token; powers quotas, `spawnedBy:`, scoped shutdown, and no-grandchildren. */\n spawnedBy?: string;\n /** Durable first-class team id (`team_<id>`) shared by a coordinator and its spawned members. */\n teamId?: string;\n /** Server-derived merge-back badge for the owned isolated worktree (#236); absent for non-branch agents. */\n branchState?: BranchState;\n /** Live isolated workspace id backing `branchState`, for dashboard deep links (#236). */\n branchWorkspaceId?: string;\n /** Server-derived `owner/repo` of the agent's project (cwd→project→issueRepo), so the\n * dashboard linkifies a bare `#NNN` against the originating project's repo (#533). */\n issueRepo?: string;\n lastSeen: number;\n createdAt: number;\n}\n\nexport type AgentStatus = \"online\" | \"idle\" | \"busy\" | \"stale\" | \"offline\";\n\n/**\n * Per-spawn report-up verbosity (#1037) — how much of a spawned child's turn output reaches its\n * spawner. Chosen by the spawner at spawn time and read by the lineage report-up delivery gate.\n * - `quiet` (default): only the turn-final response, and only once the child has transitioned to\n * idle/awaiting-input — mid-task narration (\"I'll read X… now Y…\") is suppressed. Lifecycle\n * signals (agent.ready/exit/merge), escalations and explicit coordinator-addressed messages\n * still flow regardless.\n * - `normal`: every turn-final response is forwarded immediately (the pre-#1037 behavior).\n * - `verbose`: every turn-final response plus all child streams (narration/reasoning/tool) are\n * forwarded, without the spawner needing an explicit per-stream subscription.\n */\nexport const REPORT_VERBOSITIES = [\"quiet\", \"normal\", \"verbose\"] as const;\nexport type ReportVerbosity = (typeof REPORT_VERBOSITIES)[number];\nexport function isReportVerbosity(value: unknown): value is ReportVerbosity {\n return typeof value === \"string\" && (REPORT_VERBOSITIES as readonly string[]).includes(value);\n}\n\nexport const VALID_AGENT_LIFECYCLES = [\"persistent\", \"transient\"] as const;\nexport type AgentLifecycle = (typeof VALID_AGENT_LIFECYCLES)[number];\nexport const isAgentLifecycle = (value: unknown): value is AgentLifecycle => typeof value === \"string\" && (VALID_AGENT_LIFECYCLES as readonly string[]).includes(value);\nexport const normalizeAgentLifecycle = (value: unknown): AgentLifecycle | undefined => isAgentLifecycle(value) ? value : undefined;\n\n/** Branch-workspace state for an isolated-workspace owner (#236), derived from `WorkspaceStatus` + claim + git ahead/dirty.\n * - `idle` ⚪ active worktree, nothing to land (0 ahead, clean tree)\n * - `changes` 🟡 active worktree with commits/dirty work — human can mark it ready\n * - `ready` 🔵 handed off; the relay auto-merge will land it (~2 min sweep)\n * - `steward` 🟠 under reconciliation (a steward holds it, or conflict/merge in flight)\n * - `blocked` 🔴 escalated/failed — needs human attention\n */\nexport type BranchState = \"idle\" | \"changes\" | \"ready\" | \"steward\" | \"blocked\";\n/** All BranchState values, for exhaustive UI maps and validation. */\nexport const BRANCH_STATE_VALUES = [\"idle\", \"changes\", \"ready\", \"steward\", \"blocked\"] as const satisfies readonly BranchState[];\n\nexport type PlanStatus = \"pending\" | \"in-progress\" | \"landed\" | \"blocked\";\nexport type PlanOwnerRef = { agentId?: string; policyName?: string; label?: string };\n\nexport interface PlanNode {\n id: string;\n label: string;\n scopeBlurb?: string;\n ownerRef?: PlanOwnerRef;\n lane?: string;\n status: PlanStatus;\n statusSource: \"manual\" | \"bound\";\n refs?: { agentId?: string; taskId?: string; issueId?: string; landingRef?: string };\n blackboardRefs?: string[];\n}\n\nexport interface PlanEdge {\n from: string;\n to: string;\n}\n\nexport interface Plan {\n id: string;\n threadId: string;\n teamId?: string;\n channel?: string; archived?: boolean;\n version: number;\n title?: string;\n objective?: string;\n nodes: PlanNode[];\n edges: PlanEdge[];\n createdBy: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport type TeamRuntimeState = \"busy\" | \"idle\" | \"degraded\" | \"stalled\" | \"dissolved\";\n\nexport interface Team {\n id: string;\n coordinatorId?: string;\n rootCoordinatorId?: string;\n title?: string;\n brief?: string;\n definitionOfDone?: string;\n status: \"active\" | \"dissolved\";\n outcomeStatus?: \"success\" | \"partial\" | \"failed\";\n outcomeSummary?: string;\n composition?: string;\n costTokens?: number;\n costUsd?: number;\n wallClockMs?: number;\n landsClean?: boolean;\n zeroMembersSince?: number;\n runtimeState?: TeamRuntimeState;\n createdAt: number;\n updatedAt: number;\n dissolvedAt?: number;\n}\n\nexport interface TeamTemplateCompositionEntry { label: string; provider: SpawnProvider; model?: string; effort?: SpawnEffort; profile?: string; count: number; }\nexport interface TeamTemplatePlanSkeleton { nodes?: PlanNode[]; edges?: PlanEdge[]; } export interface TeamTemplate { id: string; slug: string; title: string; description?: string; composition: TeamTemplateCompositionEntry[]; brief?: string; definitionOfDone?: string; planSkeleton?: TeamTemplatePlanSkeleton; createdAt?: string; updatedAt?: string; }\nexport type BlackboardEntryKind = \"decision\" | \"fact\" | \"ruled_out\"; export type BlackboardEntryStatus = \"active\" | \"superseded\" | \"retracted\";\n\nexport interface BlackboardEntry {\n id: string;\n teamId: string;\n kind: BlackboardEntryKind;\n title: string;\n body?: string;\n status: BlackboardEntryStatus;\n supersedes?: string;\n tags: string[];\n authorId: string;\n createdAt: number;\n updatedAt: number;\n}\n\n// Projects (#403/#405) — the DURABLE knowledge scope (outlives every team). The\n// data shape is a near-exact re-scope of the Team/BlackboardEntry pair above\n// (team_id → project_id); kept beside it deliberately so the parallel is obvious.\n// #559 — the knowledge-scope axis. A project anchors on repo roots (cwd→root binding);\n// a `profile` store anchors on a profile name; a `user` store on an operator identity.\n// One table, one entry/edge/cascade machine — only the anchor-resolution differs. Existing\n// rows default `scope:'project'`, so the generalization is non-regressing.\nexport const PROJECT_SCOPES = [\"project\", \"profile\", \"user\"] as const;\nexport type ProjectScope = (typeof PROJECT_SCOPES)[number];\nexport interface Project {\n id: string;\n slug: string;\n title?: string;\n description?: string;\n tenantId?: string;\n /** `owner/repo` for issue-ref linkify/land-close (#533). Auto-derived from a root's git\n * remote at seed; an explicit value set via relay_project_* wins and is never re-derived. */\n issueRepo?: string;\n /** Knowledge-scope axis (#559). `project` (default) anchors on repo roots; `profile`\n * anchors on a profile name (role memory, inherited via the contains graph); `user`\n * anchors on an operator identity (cross-project operator memory). */\n scope: ProjectScope;\n /** For `profile`/`user` scopes, the anchor key — the profile name or operator id the\n * store is bound to. Null for `project` scope (bound via project_roots instead). */\n anchorKey?: string;\n status: \"active\" | \"archived\";\n createdAt: number;\n updatedAt: number;\n}\nexport interface ProjectRoot {\n projectId: string;\n rootPath: string;\n /** Clone URL used by host acquisition when this root is missing on a target host (#410). */\n remoteUrl?: string;\n /** Optional branch/tag/ref to checkout/sync for host acquisition. Unset means remote default. */\n ref?: string;\n createdAt: number;\n}\nexport const PROJECT_ENTRY_KINDS = [\"rule\", \"fact\", \"decision\", \"memory\"] as const;\nexport type ProjectEntryKind = (typeof PROJECT_ENTRY_KINDS)[number];\n// `proposed` (#559) is the curation tier: a scoped-memory capture lands here and is NOT\n// injected at spawn (only `active` entries render into a preamble); promotion flips it to\n// `active`. Project-scope writes stay `active` by default, so #403 behavior is unchanged.\nexport type ProjectEntryStatus = \"proposed\" | \"active\" | \"superseded\" | \"retracted\";\n// `manual` = written by an agent directly; `promoted` = lifted from a team's\n// blackboard with an originRef back to its bb_ id; `ingested` = bootstrapped from\n// a root's CLAUDE.md. The source lever makes going-vanilla a no-op (#403 decision 4).\nexport const PROJECT_ENTRY_SOURCES = [\"manual\", \"promoted\", \"ingested\"] as const;\nexport type ProjectEntrySource = (typeof PROJECT_ENTRY_SOURCES)[number];\nexport interface ProjectEntry {\n id: string;\n projectId: string;\n kind: ProjectEntryKind;\n title: string;\n body?: string;\n status: ProjectEntryStatus;\n supersedes?: string;\n tags: string[];\n authorId: string;\n source: ProjectEntrySource;\n originRef?: string;\n createdAt: number;\n updatedAt: number;\n}\n// Project composition graph (#406). ASYMMETRIC by design: `contains` is the\n// umbrella→sub-project hierarchy and INHERITS (resolving a project's entries pulls\n// in its transitive contains-ancestors); `depends_on` is reference-only and NEVER\n// auto-inherits (a dependency's internal rules must not flood consuming agents).\nexport const PROJECT_EDGE_KINDS = [\"contains\", \"depends_on\"] as const;\nexport type ProjectEdgeKind = (typeof PROJECT_EDGE_KINDS)[number];\nexport interface ProjectEdge {\n fromProject: string;\n toProject: string;\n kind: ProjectEdgeKind;\n createdAt: number;\n}\n// A resolved entry carries `inherited` so a caller can tell an inherited\n// contains-ancestor rule from one the project owns directly (entry.projectId is the\n// owning project either way).\nexport interface ResolvedProjectEntry extends ProjectEntry {\n inherited: boolean;\n}\n\nexport const ISSUE_PROVIDERS = [\"github\", \"jira\", \"youtrack\", \"gitlab\", \"linear\"] as const;\nexport type IssueProvider = (typeof ISSUE_PROVIDERS)[number];\nexport const ISSUE_REF_STATUSES = [\"open\", \"closed\", \"unknown\"] as const;\nexport type IssueRefStatus = (typeof ISSUE_REF_STATUSES)[number];\nexport interface IssueRef {\n id: string;\n provider: IssueProvider;\n repo: string;\n externalKey: string;\n url: string;\n title?: string;\n status: IssueRefStatus;\n statusSyncedAt?: number;\n createdAt: number;\n updatedAt: number;\n}\nexport const ISSUE_ASSOCIATION_ENTITY_TYPES = [\"team\", \"plan-node\", \"epic\", \"task\"] as const;\nexport type IssueAssociationEntityType = (typeof ISSUE_ASSOCIATION_ENTITY_TYPES)[number];\nexport interface IssueAssociation {\n issueRefId: string;\n entityType: IssueAssociationEntityType;\n entityId: string;\n createdAt: number;\n}\nexport interface IssueAssociationIssue {\n association: IssueAssociation;\n issueRef: IssueRef;\n}\nexport interface IssueAssociationEntity {\n entityType: IssueAssociationEntityType;\n entityId: string;\n createdAt: number;\n}\n\ntype CapabilitySource = \"catalog\" | \"provider\" | \"runtime\" | \"override\" | \"estimate\";\ntype CapabilityConfidence = \"declared\" | \"reported\" | \"verified\" | \"estimated\" | \"unknown\";\nexport type QuotaSource = \"header\" | \"probe\" | \"ingest\" | \"statusline\" | \"emulated\";\nexport type QuotaUnit = \"tokens\" | \"requests\" | \"%\";\nexport type QuotaWindowName = string;\n\nexport interface ProviderCapabilities {\n lifecycle: {\n managed: boolean;\n shutdownHard: boolean;\n restartHard: boolean;\n semanticStatus?: boolean;\n reconnect?: boolean;\n hibernate?: boolean;\n resume?: boolean;\n };\n model?: {\n provider?: SpawnProvider | string;\n id?: string;\n alias?: string;\n providerModel?: string;\n effort?: SpawnEffort | string;\n source: CapabilitySource;\n confidence: CapabilityConfidence;\n lastUpdatedAt?: number;\n };\n session?: {\n approvalMode?: SpawnApprovalMode | string;\n fileRead?: boolean;\n fileWrite?: boolean;\n shell?: boolean;\n shellMode?: \"none\" | \"unrestricted\" | \"guarded\" | \"read-only-guarded\" | string;\n source: CapabilitySource;\n confidence: CapabilityConfidence;\n lastUpdatedAt?: number;\n };\n context?: {\n stats?: {\n source: \"api\" | \"statusline\" | \"hook\" | \"estimate\";\n confidence: \"exact\" | \"reported\" | \"estimated\";\n };\n windowTokens?: number;\n compact?: boolean;\n clear?: boolean;\n inject?: boolean;\n resume: \"clear-inject\" | \"native\" | \"none\";\n fork?: boolean;\n rollback?: boolean;\n archive?: boolean;\n };\n quota?: {\n supported: boolean;\n source: QuotaSource;\n unit: QuotaUnit;\n windows?: Array<{\n name: string;\n unit: QuotaUnit;\n reset?: boolean;\n }>;\n };\n liveSession?: {\n capture?: boolean;\n inject?: boolean;\n /** Provider can be interrupted mid-turn from the dashboard (ESC / turn-interrupt). */\n interrupt?: boolean;\n /** Prompts typed directly into the provider (web terminal/TUI) are mirrored into chat. */\n promptEcho?: boolean;\n /** Reasoning/tool activity steps are surfaced to chat as discreet session events. */\n reasoning?: boolean;\n /** Provider exposes a slash-command catalog for the chat input palette. */\n slashCommands?: boolean;\n };\n terminal?: {\n live?: {\n read?: boolean;\n write?: boolean;\n };\n attach?: {\n create?: boolean;\n read?: boolean;\n write?: boolean;\n detach?: boolean;\n };\n };\n source: CapabilitySource;\n confidence: CapabilityConfidence;\n lastUpdatedAt: number;\n}\n\nexport interface QuotaState {\n windows: Array<{\n name: QuotaWindowName;\n utilization: number;\n resetsAt?: number;\n unit: QuotaUnit;\n }>;\n source: QuotaSource;\n updatedAt: number;\n}\n\n/**\n * Normalized liveness verdict — the Runner↔provider contract field for \"is this busy\n * agent actually alive, or wedged?\" (epic #725; LOCKED by #746, the proof-of-contract).\n *\n * Computed by the RUNNER (each provider adapter folds its own raw transport/timeline/\n * token/work-kind signals down into this verdict) and shipped on the agent card at\n * `meta.liveness`. The server + dashboard read ONLY these three fields — never the raw\n * signals they replace (`meta.transport.*`, `meta.timelineEvent`, `meta.lastProviderEventAt`,\n * `meta.activeWork[].kind`). This single declared field collapses the six raw `meta` reads\n * that the stuck-busy detector grew across #713 (Claude transport heartbeat) and #724\n * (Codex subprocess / fresh-lastSeen veto) — the \"patched twice\" leak, since the same\n * derivation had been re-implemented client-side in the dashboard.\n *\n * The server keeps only THRESHOLD POLICY: it never re-interprets provider runtime signals\n * and contains no `provider === …` branch. A future provider quirk becomes one adapter\n * change behind this stable shape.\n */\nexport interface LivenessSignal {\n /**\n * Epoch-ms of the most recent proof the runner→server transport is alive — the runner's\n * max of its frame / heartbeat-ack timestamps. The SERVER folds its own server-stamped\n * `AgentCard.lastSeen` in as a floor when reading (the runner cannot know `lastSeen`), so\n * a fresh heartbeat on EITHER side proves liveness. A fresh value is the authoritative\n * veto: provider-token silence alone (long thinking turns, native compaction, multi-minute\n * subprocesses) never triggers stuck-busy while the link is demonstrably alive (#713/#724).\n */\n transportLiveAt: number;\n /**\n * Epoch-ms of the most recent evidence of FORWARD PROGRESS, folding (whichever is latest):\n * provider tokens, a lifecycle/timeline transition (e.g. Claude native `compacting`, which\n * is token-silent yet progressing — #713), or live tool/subprocess work (Codex #724). The\n * adapter decides what counts as progress for its provider; the server only compares it\n * against the no-progress threshold.\n */\n lastProgressAt: number;\n /**\n * True while any tool/subprocess work-kind is in flight (e.g. `bun test`, `build`,\n * `typecheck`) — i.e. active work that is NOT the bare provider turn. The adapter knows\n * its own work-kinds and classifies them; the server MUST NOT enumerate them. Live work\n * keeps the agent out of stuck-busy regardless of provider-token silence.\n */\n hasLiveWork: boolean;\n}\n\n/**\n * A structured question surfaced inside an {@link InteractivePrompt} (Claude\n * AskUserQuestion). The dashboard renders these as a form and submits the chosen\n * option label(s) back as an `answer` decision (ProviderPermissionDecisionInput).\n */\nexport interface ProviderQuestion {\n question: string;\n header?: string;\n options: { label: string; description?: string }[];\n multiSelect?: boolean;\n}\n\n/**\n * The kind of interactive prompt an adapter surfaces. The §4.2 normalized set is\n * questions|plan|command|tool|approval; the Codex app-server additionally tags its\n * approval requests file-change|permission. The server/dashboard treat `kind` as a\n * display hint only — they never branch liveness, ordering, or resolution on it.\n */\nexport type InteractivePromptKind =\n | \"questions\"\n | \"plan\"\n | \"command\"\n | \"tool\"\n | \"approval\"\n | \"file-change\"\n | \"permission\";\n\n/**\n * Normalized interactive-prompt surface — the Runner↔provider contract field for \"the\n * agent is blocked waiting on a human decision\" (epic #725, §4.2; #435/#723). Formalizes\n * what used to be an untyped `providerState.pendingApproval` bag with a per-provider\n * `provider` tag the server never needed.\n *\n * Built by the RUNNER adapter (Claude's PreToolUse hook view; the Codex app-server\n * approval view) and shipped on `meta.providerState.pendingApproval`. Resolution rides\n * the existing ProviderPermissionDecisionInput (approve|approve-session|deny|abort|answer)\n * — the server persists the blocked state and clears it on the decision; it never\n * re-derives the prompt or its ordering.\n */\nexport interface InteractivePrompt {\n id: string;\n kind: InteractivePromptKind;\n title: string;\n body: string;\n /** Present for kind \"questions\" — the structured AskUserQuestion form. */\n questions?: ProviderQuestion[];\n /** Approve/deny-style choices the dashboard renders as buttons. */\n choices?: { id: string; label: string }[];\n /**\n * ORDERING SIGNAL (#435/#723, honesty-corrected by #1116): `true` asserts the adapter\n * settle-polled the transcript for the exact tool_use entry that triggered this prompt\n * and drained every reasoning/narrative step of the turn into the session stream BEFORE\n * this prompt surfaced. `false` means the adapter hit its bounded settle-poll timeout\n * (never blocks the question more than a few seconds) without confirming the drain —\n * some narration may still be in flight. The server/dashboard render in arrival order\n * and never re-sort, so `true` guarantees the reasoning is already present above the\n * form; `false` is an honest \"may still be arriving\" signal, not a dropped guarantee —\n * a prompt whose model emitted no reasoning at all (#757) still settles `true` the\n * moment the anchor is found, so this never fails the vacuous case.\n */\n reasoningSettled: boolean;\n}\n\nexport type QuotaSnapshot = {\n agentId: string;\n capturedAt: number;\n} & QuotaState;\n\ntype ContextLifecycleState =\n | \"fresh\"\n | \"primed\"\n | \"working\"\n | \"cooling\"\n | \"compacting\"\n | \"clearing-context\"\n | \"hibernating\";\n\nexport interface ContextProbeMetrics {\n agentId: string;\n contextPercent: number;\n tokensUsed?: number;\n tokensMax?: number;\n quotaUsed?: number;\n quotaLimit?: number;\n quotaResetIn?: number;\n quotaWindows?: Array<{\n name: QuotaWindowName;\n used?: number;\n limit?: number;\n utilization?: number;\n resetIn?: number;\n resetsAt?: number;\n unit?: QuotaUnit;\n }>;\n model?: string;\n effort?: string;\n source: \"statusline\" | \"hook\" | \"api\" | \"estimate\";\n confidence: \"exact\" | \"reported\" | \"estimated\";\n timestamp: number;\n}\n\nexport interface ContextState {\n utilization: number;\n tokensUsed?: number;\n tokensMax?: number;\n lifecycleState: ContextLifecycleState;\n warmTopics: string[];\n activeMemories: string[];\n tasksSinceCompact: number;\n lastCompactedAt?: number; lastContextAdvisoryBand?: number; lastContextAdvisoryAt?: number;\n lastUpdatedAt: number;\n source: \"statusline\" | \"hook\" | \"api\" | \"estimate\";\n confidence: \"exact\" | \"reported\" | \"estimated\";\n}\nexport interface ContextSnapshot {\n id: number;\n agentId: string;\n context: ContextState;\n utilization: number;\n lifecycleState: ContextLifecycleState;\n tokensUsed?: number;\n tokensMax?: number;\n source: ContextState[\"source\"];\n confidence: ContextState[\"confidence\"];\n capturedAt: number;\n}\n\nexport interface ContextPackageRequest {\n task: TaskRoutingHints;\n agent: AgentCard;\n budget: ContextBudget;\n memories?: Memory[];\n}\n\nexport interface RegisterAgentInput {\n id: string;\n name: string;\n kind?: AgentKind;\n label?: string | null;\n tags?: string[];\n machine?: string;\n rig?: string;\n capabilities?: string[];\n ready?: boolean;\n status?: AgentCard[\"status\"];\n instanceId?: string;\n providerCapabilities?: ProviderCapabilities;\n context?: ContextState;\n quota?: QuotaState;\n meta?: import(\"./provider-state.js\").AgentMeta;\n lifecycle?: AgentLifecycle;\n /** Parent agent id. Server sets this authoritatively from the registering token's\n * `spawnedBy`/`parentAgents` constraint; any client-supplied value is ignored. */\n spawnedBy?: string;\n /** Team id. Server sets this authoritatively from the registering token's `teamId` constraint. */\n teamId?: string;\n}\n\nexport interface AgentSessionGuard {\n instanceId?: string;\n epoch?: number;\n}\n\n// --- Orchestrators ---\n\nexport type OrchestratorStatus = \"online\" | \"offline\";\n\n/** Spawn providers — registered provider manifests are the runtime source of truth. */\nexport const SPAWN_PROVIDERS = getAllManifests().map((manifest) => manifest.id); export type SpawnProvider = string;\nexport function isSpawnProvider(value: unknown): value is SpawnProvider {\n return typeof value === \"string\" && SPAWN_PROVIDERS.includes(value);\n}\n\n/** Approval modes — runtime tuple + derived type. */\nexport const APPROVAL_MODES = [\"open\", \"guarded\", \"read-only\"] as const;\nexport type SpawnApprovalMode = (typeof APPROVAL_MODES)[number];\nfunction isApprovalMode(value: unknown): value is SpawnApprovalMode {\n return typeof value === \"string\" && (APPROVAL_MODES as readonly string[]).includes(value);\n}\n\nexport type SpawnEffort = string;\n\n// --- Runtime contract / capability types (used by Orchestrator and its inputs) ---\n\nexport type RuntimeContractName =\n | \"relayApi\"\n | \"orchestratorProtocol\"\n | \"runnerProtocol\"\n | \"providerPluginProtocol\";\n\nexport type RuntimeContracts = Partial<Record<RuntimeContractName, number>>;\nexport type RuntimeCapabilities = Record<string, boolean>;\n\nexport interface RuntimePackageMetadata {\n name: string;\n version: string;\n}\n\nexport interface ContractCompatibilityIssue {\n contract: string;\n expected: string;\n actual?: number;\n}\n\nexport interface ContractCompatibility {\n status: \"compatible\" | \"incompatible\" | \"unknown\";\n compatible: boolean;\n issues: ContractCompatibilityIssue[];\n}\n\nexport type ManagedAgentStatus = \"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"backoff\" | \"failed\";\n\nexport interface ManagedAgentState {\n policyName: string;\n status: ManagedAgentStatus;\n agentId?: string;\n orchestratorId: string;\n provider: SpawnProvider;\n tmuxSession?: string;\n spawnRequestId?: string;\n workspaceId?: string;\n workspacePath?: string;\n workspaceBranch?: string;\n lastSpawnAt?: number;\n lastStopAt?: number;\n healthySince?: number;\n restartCount: number;\n consecutiveFailures: number;\n backoffUntil?: number;\n lastError?: string;\n updatedAt: number;\n}\n\nexport interface ProviderStatusReport {\n name: SpawnProvider;\n available: boolean;\n checkedAt: number;\n reason?: string;\n version?: string;\n features?: Record<string, boolean>;\n cli?: {\n command: string;\n path?: string;\n ok: boolean;\n version?: string;\n error?: string;\n };\n runner?: {\n command: string;\n path?: string;\n ok: boolean;\n version?: string;\n error?: string;\n };\n}\n\nexport interface ProviderCatalogSummary {\n provider: SpawnProvider;\n label: string;\n defaultModel?: string;\n models: Array<{\n alias: string;\n label: string;\n providerModel: string;\n efforts: SpawnEffort[];\n defaultEffort?: SpawnEffort;\n disabled?: boolean;\n limits?: {\n contextWindowTokens?: {\n value: number;\n source: \"catalog\" | \"provider\" | \"runtime\" | \"override\";\n confidence: \"declared\" | \"verified\" | \"estimated\" | \"unknown\";\n lastUpdatedAt?: number;\n };\n maxOutputTokens?: {\n value: number;\n source: \"catalog\" | \"provider\" | \"runtime\" | \"override\";\n confidence: \"declared\" | \"verified\" | \"estimated\" | \"unknown\";\n lastUpdatedAt?: number;\n };\n };\n contextThreshold?: { selfCompactUtilization: number; warnUtilization?: number; hardBackstopUtilization?: number }; capabilities?: {\n modalities: {\n input: {\n text: boolean;\n image?: boolean;\n audio?: boolean;\n video?: boolean;\n pdf?: boolean;\n };\n output: {\n text: boolean;\n image?: boolean;\n audio?: boolean;\n video?: boolean;\n };\n };\n tools?: {\n code?: boolean;\n review?: boolean;\n debug?: boolean;\n refactor?: boolean;\n shell?: boolean;\n fileRead?: boolean;\n fileWrite?: boolean;\n webSearch?: boolean;\n imageGeneration?: boolean;\n imageEditing?: boolean;\n };\n source: \"catalog\" | \"provider\" | \"runtime\" | \"override\";\n confidence: \"declared\" | \"verified\" | \"estimated\" | \"unknown\";\n lastUpdatedAt?: number;\n };\n }>;\n}\n\nexport interface Orchestrator {\n id: string;\n hostname: string;\n status: OrchestratorStatus;\n agentId: string; // relay agent id for messaging\n providers: SpawnProvider[];\n providerStatus?: ProviderStatusReport[];\n providerCatalog?: ProviderCatalogSummary[];\n baseDir: string;\n apiUrl?: string;\n envKeys: string[]; // names only, never values\n package?: RuntimePackageMetadata;\n contracts?: RuntimeContracts;\n capabilities?: RuntimeCapabilities;\n contractCompatibility?: ContractCompatibility;\n version?: string;\n protocolVersion?: number;\n gitSha?: string;\n health?: OrchestratorHealth;\n // Self-reported host supervision (how the orchestrator's own process is run),\n // used to target a remote self-upgrade at the right unit.\n supervisor?: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n selfUnit?: string;\n runtimePrefix?: string;\n // In-flight / last remote upgrade state, reconciled by the relay against the\n // version the orchestrator reports after it restarts.\n upgrade?: OrchestratorUpgradeState;\n meta: Record<string, unknown>;\n managedAgents: ManagedAgent[];\n lastSeen: number;\n createdAt: number;\n}\n\nexport interface OrchestratorUpgradeState {\n desiredVersion: string;\n status: \"pending\" | \"succeeded\" | \"failed\";\n commandId?: string;\n providers?: string[];\n fromVersion?: string;\n requestedBy?: string;\n requestedAt: number;\n settledAt?: number;\n error?: string;\n}\n\nexport interface OrchestratorHealth {\n status: \"ok\" | \"warn\" | \"restart-required\" | \"upgrade-required\" | \"unknown\";\n restartRequired: boolean;\n upgradeRequired?: boolean;\n issues: Array<{\n code: \"missing-version\" | \"package-drift\" | \"missing-contract\" | \"protocol-mismatch\" | \"restart-required\" | \"upgrade-required\";\n detail: string;\n }>;\n}\n\nexport interface ManagedAgent {\n agentId: string;\n provider: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n profile?: string;\n workspaceMode?: WorkspaceMode;\n workspace?: WorkspaceMetadata;\n sessionName?: string;\n supervisor?: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n systemdUnit?: string;\n terminalSession?: string;\n terminalAvailable?: boolean;\n tmuxSession: string;\n cwd: string;\n label?: string;\n approvalMode: SpawnApprovalMode;\n policyName?: string;\n spawnRequestId?: string;\n automationRunId?: string;\n pid?: number;\n startedAt: number;\n}\n\nexport interface ManagedSessionExitDiagnostics {\n agentId: string;\n provider: SpawnProvider;\n workspaceMode?: WorkspaceMode;\n workspace?: WorkspaceMetadata;\n sessionName?: string;\n tmuxSession: string;\n cwd: string;\n label?: string;\n policyName?: string;\n spawnRequestId?: string;\n automationRunId?: string;\n supervisor: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n systemdUnit?: string;\n terminalSession?: string;\n terminalAvailable?: boolean;\n pid?: number;\n currentPid?: number;\n startedAt: number;\n detectedAt: number;\n runtimeMs: number;\n logFile?: string;\n logBytes?: number;\n logEmpty?: boolean;\n logTail?: string[];\n runnerInfoFile?: string;\n runnerInfoPresent?: boolean;\n systemd?: {\n unit: string;\n activeState?: string;\n subState?: string;\n result?: string;\n execMainCode?: string;\n execMainStatus?: string;\n mainPid?: number;\n unavailable?: string;\n };\n /** Termination signal name if the supervisor reported one (e.g. \"SIGKILL\", \"SIGSEGV\"). */\n signal?: string;\n /** Process exit code if known (0 = clean exit). */\n exitCode?: number;\n /** Positive OOM-kill detection (cgroup memory.events / journal OOM-killer / systemd Result=oom-kill). */\n oom?: { source: string; detail?: string };\n unavailable?: string[];\n lastError: string;\n}\n\n/**\n * Likely-cause label for an agent's death (#636). Best-effort classification mapped from the\n * available evidence — never raw logs, always a verdict the parent can act on.\n */\nexport type AgentDeathCause =\n | \"oom\"\n | \"killed\"\n | \"clean-exit\"\n | \"token-401\"\n | \"onboarding-gate\"\n | \"provider-crash\"\n | \"transport-loss\"\n | \"unknown\";\n\nexport const AGENT_DEATH_CAUSES = [\n \"oom\",\n \"killed\",\n \"clean-exit\",\n \"token-401\",\n \"onboarding-gate\",\n \"provider-crash\",\n \"transport-loss\",\n \"unknown\",\n] as const satisfies readonly AgentDeathCause[];\n\n/**\n * Structured post-mortem for an agent that died (#636). Rides on the `agent.exited` terminal\n * event (#633) so the death notification carries the WHY, and is persisted as a {@link CrashReport}\n * so the verdict survives the worktree/session reaper.\n */\nexport interface AgentDeathDiagnosis {\n /** Best-effort likely-cause label. */\n cause: AgentDeathCause;\n /** Confidence in the label — high (conclusive signal), medium (strong hint), low (fallback guess). */\n confidence: \"high\" | \"medium\" | \"low\";\n /** One-line human verdict, e.g. \"OOM-killed (systemd Result=oom-kill)\". */\n summary: string;\n /** Supporting evidence bullets that led to the label. */\n evidence: string[];\n /** Termination signal name if known (SIGKILL/SIGTERM/SIGSEGV). */\n signal?: string;\n /** Process exit code if known (0 = clean). */\n exitCode?: number;\n /** True when an OOM kill was positively detected. */\n oom?: boolean;\n /** Last N lines of the runner stderr / provider log around death. */\n logTail?: string[];\n /** Last heartbeat timestamp the server saw before death. */\n lastHeartbeatAt?: number;\n /** Gap (ms) between last heartbeat and death detection (ties to #634). */\n heartbeatGapMs?: number;\n /** Last time the agent reported busy (turn activity). */\n lastBusyAt?: number;\n /** Active work titles at death, if any. */\n activeWork?: string[];\n /** When this diagnosis was produced. */\n detectedAt: number;\n}\n\n/**\n * Persisted crash post-mortem (#636), retrievable by `agentId` after the agent row is reaped.\n * Stored in its own table so it outlives `pruneOfflineAgents` / `deleteAgent`.\n */\nexport interface CrashReport {\n agentId: string;\n parentId?: string;\n teamId?: string;\n spawnRequestId?: string;\n provider?: string;\n label?: string;\n reason: string;\n diagnosis: AgentDeathDiagnosis;\n createdAt: number;\n}\n\nexport interface RegisterOrchestratorInput {\n id: string;\n hostname: string;\n providers: SpawnProvider[];\n providerStatus?: ProviderStatusReport[];\n providerCatalog?: ProviderCatalogSummary[];\n baseDir: string;\n apiUrl?: string;\n envKeys?: string[];\n package?: RuntimePackageMetadata;\n contracts?: RuntimeContracts;\n capabilities?: RuntimeCapabilities;\n version?: string;\n protocolVersion?: number;\n gitSha?: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface OrchestratorRuntimeInput {\n package?: RuntimePackageMetadata;\n contracts?: RuntimeContracts;\n capabilities?: RuntimeCapabilities;\n version?: string;\n protocolVersion?: number;\n gitSha?: string;\n providers?: SpawnProvider[];\n providerStatus?: ProviderStatusReport[];\n providerCatalog?: ProviderCatalogSummary[];\n}\n\ninterface OrchestratorSpawnInput {\n provider: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n cwd?: string;\n workspaceMode?: WorkspaceMode;\n label?: string;\n approvalMode?: SpawnApprovalMode;\n prompt?: string;\n systemPromptAppend?: string;\n env?: Record<string, string>;\n}\n\ninterface OrchestratorSpawnResult {\n orchestratorId: string;\n provider: SpawnProvider;\n sessionName?: string;\n supervisor?: \"process\" | \"systemd\" | \"launchd\" | \"unknown\";\n systemdUnit?: string;\n terminalSession?: string;\n terminalAvailable?: boolean;\n tmuxSession: string;\n cwd: string;\n label?: string;\n approvalMode: SpawnApprovalMode;\n pid?: number;\n startedAt: number;\n}\n\nexport interface Recipe {\n name: string;\n description: string;\n version?: string;\n author?: string;\n agents: RecipeAgent[];\n workflow?: RecipeWorkflow;\n lifecycle?: RecipeLifecycle;\n}\n\nexport interface RecipeAgent {\n role: string;\n provider: SpawnProvider;\n count?: number;\n capabilities: string[];\n label?: string;\n tags?: string[];\n memoryTags?: string[];\n approvalMode?: \"open\" | \"guarded\" | \"read-only\";\n prompt?: string;\n model?: string;\n effort?: SpawnEffort;\n env?: Record<string, string>;\n}\n\ninterface RecipeWorkflow {\n trigger?: string;\n fanOut?: \"all\" | \"first\";\n collect?: string;\n routing?: RecipeRoute[];\n}\n\ninterface RecipeRoute {\n pattern: string;\n pipeline: string[];\n}\n\ninterface RecipeLifecycle {\n mode?: \"persistent\" | \"ephemeral\";\n idleTimeoutMs?: number;\n memory?: RecipeMemoryPolicy;\n}\n\nexport interface RecipeMemoryPolicy {\n injectOnAssign?: boolean;\n autoCapture?: boolean;\n captureTypes?: MemoryType[];\n memoryTags?: string[];\n alwaysReload?: string[];\n scope?: string;\n maxTokens?: number;\n maxMemories?: number;\n priorityCutoff?: 1 | 2 | 3;\n ttlMs?: number;\n}\n\nexport interface RecipeInstance {\n id: string;\n recipeName: string;\n recipeSource: \"builtin\" | \"user\";\n cwd: string;\n orchestratorId: string;\n status: \"starting\" | \"running\" | \"stopping\" | \"stopped\" | \"failed\";\n agents: RecipeAgentInstance[];\n artifacts?: Artifact[];\n startedAt: number;\n stoppedAt?: number;\n startedBy: string;\n error?: string;\n}\n\nexport interface RecipeAgentInstance {\n role: string;\n agentId: string;\n provider: string;\n status: \"spawning\" | \"running\" | \"stopping\" | \"stopped\" | \"failed\";\n index?: number;\n}\n","import type { AttachmentRef } from \"./artifacts.js\";\nimport type { WorkspaceMode } from \"./workspaces.js\";\nimport type { SpawnProvider, SpawnEffort, SpawnApprovalMode } from \"./agents.js\";\n\nexport type TaskSeverity = \"info\" | \"warning\" | \"critical\";\nexport type TaskStatus =\n | \"open\"\n | \"claimed\"\n | \"in_progress\"\n | \"blocked\"\n | \"orphaned\"\n | \"done\"\n | \"failed\"\n | \"canceled\";\n\nexport type CommandStatus =\n | \"pending\"\n | \"accepted\"\n | \"running\"\n | \"succeeded\"\n | \"failed\"\n | \"timed_out\"\n | \"rejected\"\n | \"canceled\";\n\nexport interface Command {\n id: string;\n type: string;\n source: string;\n target: string;\n params: Record<string, unknown>;\n status: CommandStatus;\n result?: Record<string, unknown>;\n error?: string;\n correlationId?: string;\n createdAt: number;\n updatedAt: number;\n expiresAt?: number;\n}\n\nexport interface CreateCommandInput {\n type: string;\n source: string;\n target: string;\n params?: Record<string, unknown>;\n correlationId?: string;\n ttlMs?: number;\n}\n\nexport interface UpdateCommandInput {\n status?: CommandStatus;\n result?: Record<string, unknown>;\n error?: string;\n}\n\n// #834 — the durable first-class Task entity. The `tasks` table carries two\n// FACETS discriminated by `kind`: `claim` is the inbound claimable work-item lane\n// (the historical behaviour — alert→claim→lease→done); `deliverable` is the durable\n// coordination Task that threads issues/branch/commits/deps/gates. The claim-lease\n// machinery is fenced to `kind='claim'` (see `claimTaskWhere`), so the two never collide.\nexport const TASK_KINDS = [\"claim\", \"deliverable\"] as const;\nexport type TaskKind = (typeof TASK_KINDS)[number];\n\n// Deliverable-position axis (#834 status-as-audit-trail): orthogonal to the execution\n// `status` axis. Populated observably from the land/deploy pipeline, not by agent memory.\nexport const TASK_DELIVERABLE_STAGES = [\n \"none\",\n \"uncommitted\",\n \"committed-on-branch\",\n \"landed-on-main\",\n \"deploying\",\n \"deployed\",\n] as const;\nexport type TaskDeliverableStage = (typeof TASK_DELIVERABLE_STAGES)[number];\n\nexport const TASK_BLOCKER_TYPES = [\"dep\", \"gate\", \"decision\", \"external\"] as const;\nexport type TaskBlockerType = (typeof TASK_BLOCKER_TYPES)[number];\nexport interface TaskBlocker {\n type: TaskBlockerType;\n ref?: string;\n note?: string;\n}\n\n// Quality-gate ledger (#834): each run is an append-only `gate:<name>` task event.\n// `latestGates` projects the deterministic current state (highest event id per gate).\nexport const TASK_GATE_STATUSES = [\"pass\", \"fail\"] as const;\nexport type TaskGateStatus = (typeof TASK_GATE_STATUSES)[number];\nexport interface TaskGateState {\n gate: string;\n status: TaskGateStatus;\n detail?: string;\n count?: number;\n commitSha?: string;\n runId?: string;\n ranAt: number;\n}\n\nexport const TASK_DELIVERY_DISPOSITIONS = [\"complete\", \"blocked\", \"failed\", \"needs-review\"] as const;\nexport type TaskDeliveryDisposition = (typeof TASK_DELIVERY_DISPOSITIONS)[number];\nexport type TaskDeliveryAcceptanceStatus = \"met\" | \"unmet\";\n\nexport interface TaskDeliveryGateReport {\n status: TaskGateStatus;\n detail?: string;\n count?: number;\n runId?: string;\n}\n\nexport interface TaskDeliveryNamedGateReport extends TaskDeliveryGateReport {\n name: string;\n}\n\nexport interface TaskDeliveryGateReports {\n typecheck?: TaskDeliveryGateReport;\n test?: TaskDeliveryGateReport;\n other?: TaskDeliveryNamedGateReport[];\n}\n\nexport interface TaskDeliverySelfAcceptance {\n criterion: string;\n status: TaskDeliveryAcceptanceStatus;\n note?: string;\n}\n\nexport interface TaskDeliveryResult {\n disposition: TaskDeliveryDisposition;\n summary: string;\n reason?: string;\n blockedBy?: TaskBlocker[];\n commits?: TaskCommitRef[];\n gates?: TaskDeliveryGateReports;\n filesChanged?: number;\n rca?: string;\n testCoverage?: string;\n risks?: string[];\n selfAcceptance?: TaskDeliverySelfAcceptance[];\n}\n\nexport interface TaskDeliverableEventMetadata extends TaskDeliveryResult {\n v: 1;\n actor: string;\n workspaceId?: string;\n idempotencyKey?: string;\n land?: {\n requested: boolean;\n triggered: boolean;\n reason?: string;\n workspaceId?: string;\n workspaceStatus?: string;\n };\n}\n\n// Declarative work-shape (#839): the small + stable taxonomy a coordinator declares\n// instead of hand-picking host/provider/model/effort. The dispatcher (#839) maps this\n// shape + live system state → a concrete spawn (see src/task-dispatcher.ts).\nexport const TASK_DISPATCH_COMPLEXITIES = [\"trivial\", \"moderate\", \"complex\"] as const;\nexport const TASK_DISPATCH_TYPES = [\"build\", \"review\", \"research\", \"ops\", \"ui\", \"refactor\", \"debug\"] as const;\nexport const TASK_DISPATCH_FOOTPRINTS = [\"small\", \"medium\", \"large\"] as const;\nexport const TASK_DISPATCH_CRITICALITIES = [\"low\", \"normal\", \"high\"] as const;\nexport const TASK_COMPLEXITIES = TASK_DISPATCH_COMPLEXITIES;\nexport type TaskDispatchComplexity = (typeof TASK_DISPATCH_COMPLEXITIES)[number];\nexport type TaskComplexity = TaskDispatchComplexity;\nexport type TaskDispatchType = (typeof TASK_DISPATCH_TYPES)[number];\nexport type TaskDispatchFootprint = (typeof TASK_DISPATCH_FOOTPRINTS)[number];\nexport type TaskDispatchCriticality = (typeof TASK_DISPATCH_CRITICALITIES)[number];\nexport interface TaskDispatch {\n complexity?: TaskDispatchComplexity;\n type?: TaskDispatchType;\n contextFootprint?: TaskDispatchFootprint;\n criticality?: TaskDispatchCriticality;\n}\n\nexport interface TaskDispatchWorkShapeProjection {\n specified: boolean;\n complexity: TaskDispatchComplexity | null;\n type: TaskDispatchType | null;\n contextFootprint: TaskDispatchFootprint | null;\n criticality: TaskDispatchCriticality | null;\n}\n\nexport interface TaskDispatchDecisionProjection {\n target: {\n provider?: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n };\n rationale: string;\n pinned: boolean;\n factors?: Record<string, unknown>;\n warnings: string[];\n source: string;\n decidedAt: number;\n}\n\nexport interface TaskBoundWorkerModelProjection {\n agentId: string;\n provider?: string;\n model?: string;\n effort?: string;\n source?: string;\n confidence?: string;\n lastUpdatedAt?: number;\n}\n\nexport interface TaskCommitRef {\n sha: string;\n subject?: string;\n}\n\n// Issue link surfaced as a {provider,repo,externalKey} object (not a bare number) so\n// the bridge is unambiguous across repos (#833/#834). Projection of issue_associations.\nexport interface TaskIssueLink {\n issueRefId: string;\n provider: string;\n repo: string;\n externalKey: string;\n url: string;\n title?: string;\n status: string;\n}\n\nexport interface TaskDependency {\n taskId: number;\n dependsOnTaskId: number;\n createdAt: number;\n}\n\nexport interface Task {\n id: number;\n label?: string;\n source: string;\n title: string;\n body: string;\n severity: TaskSeverity;\n status: TaskStatus;\n target: string;\n channel?: string;\n dedupeKey?: string;\n externalUrl?: string;\n occurrenceCount: number;\n claimedBy?: string;\n claimedAt?: number;\n claimExpiresAt?: number;\n messageId?: number;\n result?: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n lastSeenAt: number;\n // --- #834 durable-Task facet (all optional; `claim`-kind rows leave them empty) ---\n kind?: TaskKind;\n intent?: string;\n complexity?: TaskComplexity;\n context?: string;\n latestSteer?: string;\n deliverableStage?: TaskDeliverableStage;\n blockedBy?: TaskBlocker[];\n branch?: string;\n commits?: TaskCommitRef[];\n doneWhen?: string;\n teamId?: string;\n projectId?: string;\n repo?: string;\n createdBy?: string;\n ownerAgentId?: string;\n ownerAgentLabel?: string;\n dispatch?: TaskDispatch;\n reapedAt?: number;\n}\n\nexport interface TaskListItem {\n id: number;\n label: string;\n title: string;\n status: TaskStatus;\n kind?: TaskKind;\n deliverableStage?: TaskDeliverableStage;\n teamId?: string;\n projectId?: string;\n repo?: string;\n createdBy?: string;\n ownerAgentId?: string;\n ownerAgentLabel?: string;\n branch?: string;\n createdAt: number;\n updatedAt: number;\n reapedAt?: number;\n}\n\n// Hydrated view returned by the task-entity get endpoints: the row plus its derived\n// projections (issue links, dependency edges, current gate state).\nexport interface TaskDetail extends Task {\n issues: TaskIssueLink[];\n dependsOn: number[];\n dependents: number[];\n gates: TaskGateState[];\n dispatchWorkShape: TaskDispatchWorkShapeProjection;\n dispatchDecision?: TaskDispatchDecisionProjection;\n boundWorkerModel?: TaskBoundWorkerModelProjection;\n}\n\nexport interface TaskEvent {\n id: number;\n taskId: number;\n source: string;\n type: string;\n severity: TaskSeverity;\n title: string;\n body: string;\n metadata: Record<string, unknown>;\n createdAt: number;\n}\n\nexport interface IntegrationEventInput {\n source?: string;\n type?: string;\n severity?: TaskSeverity;\n status?: TaskStatus | \"resolved\";\n title: string;\n body: string;\n target: string;\n channel?: string;\n dedupeKey?: string;\n externalUrl?: string;\n attachments?: AttachmentRef[];\n metadata?: Record<string, unknown>;\n /**\n * When false, the task message is dispatched without a reply obligation. Scheduled automations\n * (#479) signal completion via the task-status lifecycle, not a `/reply`, so an unsatisfiable\n * obligation otherwise re-injects the task into every successor session on managed-agent restart.\n * Omit (default) to keep the obligation for integrations that expect a reply.\n */\n replyExpected?: boolean;\n}\n\nexport interface AlertEventInput {\n source: string;\n type: string;\n payload: Record<string, unknown>;\n severity?: TaskSeverity;\n status?: TaskStatus | \"resolved\";\n dedupeKey?: string;\n target: string;\n channel?: string;\n externalUrl?: string;\n attachments?: AttachmentRef[];\n metadata?: Record<string, unknown>;\n replyExpected?: boolean;\n title?: string;\n body?: string;\n}\n\nexport interface IntegrationEventResult {\n task: Task;\n event: TaskEvent;\n created: boolean;\n message?: import(\"./messages.js\").Message;\n}\n\nexport interface IntegrationTaskStats {\n source: string;\n tasks: number;\n openTasks: number;\n waitingTasks: number;\n failedTasks: number;\n lastSeenAt?: number;\n lastUpdatedAt?: number;\n}\n\nexport interface IntegrationSummary {\n name: string;\n displayName?: string;\n description?: string;\n enabled: boolean;\n configured: boolean;\n observed: boolean;\n type?: string;\n icon?: string;\n accentColor?: string;\n tags: string[];\n homepageUrl?: string;\n repositoryUrl?: string;\n docsUrl?: string;\n manifest?: Record<string, unknown>;\n scopes: string[];\n targets: string[];\n channels: string[];\n callbackHost?: string;\n callbackConfigured: boolean;\n rateLimit: {\n limitPerMinute: number;\n currentWindowCount: number;\n windowStartedAt?: number;\n };\n taskStats: IntegrationTaskStats;\n}\n\nexport type AutomationKind = \"scheduled_task\";\nexport type AutomationCatchUpPolicy = \"skip\" | \"run_once\" | \"run_all\";\nexport type AutomationConcurrencyPolicy = \"skip\" | \"queue\" | \"replace\";\nexport type AutomationRunStatus =\n | \"scheduled\"\n | \"dispatching\"\n | \"waiting_agent\"\n | \"running\"\n | \"succeeded\"\n | \"failed\"\n | \"canceled\"\n | \"timed_out\";\n\nexport interface AutomationExistingAgentPolicy {\n mode: \"existing_agent\";\n selector: {\n provider?: SpawnProvider;\n label?: string;\n tags?: string[];\n capabilities?: string[];\n };\n ifNoMatch?: \"fail\" | \"spawn\";\n}\n\nexport interface AutomationOnDemandAgentPolicy {\n mode: \"on_demand_agent\";\n provider: SpawnProvider;\n model?: string;\n effort?: SpawnEffort;\n cwd?: string;\n workspaceMode?: WorkspaceMode;\n profile?: string;\n approvalMode?: SpawnApprovalMode;\n keepAlive?: boolean;\n runtimeBudget?: AutomationRuntimeBudget;\n shutdownAfterMs?: number;\n}\n\nexport type AutomationTargetPolicy = AutomationExistingAgentPolicy | AutomationOnDemandAgentPolicy;\n\nexport interface AutomationRuntimeBudget {\n maxRuntimeMs: number;\n warnAtMs?: number;\n warningMessage?: string;\n}\n\nexport interface AutomationTaskTemplate {\n title: string;\n body: string;\n severity?: TaskSeverity;\n dedupeKey?: string;\n externalUrl?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface Automation {\n id: string;\n kind: AutomationKind;\n name: string;\n description?: string;\n enabled: boolean;\n schedule: string;\n timezone: string;\n nextRunAt?: number;\n catchUpPolicy: AutomationCatchUpPolicy;\n concurrencyPolicy: AutomationConcurrencyPolicy;\n orchestratorId: string;\n targetPolicy: AutomationTargetPolicy;\n taskTemplate: AutomationTaskTemplate;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface AutomationRun {\n id: string;\n automationId: string;\n status: AutomationRunStatus;\n scheduledFor: number;\n startedAt?: number;\n finishedAt?: number;\n orchestratorId: string;\n targetAgentId?: string;\n spawnedAgentId?: string;\n taskId?: number;\n messageId?: number;\n error?: string;\n result?: Record<string, unknown>;\n meta: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n shutdownRequestedAt?: number;\n}\n\nexport interface CreateAutomationInput {\n kind?: AutomationKind;\n name: string;\n description?: string;\n enabled?: boolean;\n schedule: string;\n timezone?: string;\n catchUpPolicy?: AutomationCatchUpPolicy;\n concurrencyPolicy?: AutomationConcurrencyPolicy;\n orchestratorId: string;\n targetPolicy: AutomationTargetPolicy;\n taskTemplate: AutomationTaskTemplate;\n}\n\nexport interface UpdateAutomationInput {\n name?: string;\n description?: string | null;\n enabled?: boolean;\n schedule?: string;\n timezone?: string;\n catchUpPolicy?: AutomationCatchUpPolicy;\n concurrencyPolicy?: AutomationConcurrencyPolicy;\n orchestratorId?: string;\n targetPolicy?: AutomationTargetPolicy;\n taskTemplate?: AutomationTaskTemplate;\n}\n\nexport interface TaskStatusInput {\n status: TaskStatus;\n agentId?: string;\n instanceId?: string;\n epoch?: number;\n result?: string;\n body?: string;\n metadata?: Record<string, unknown>;\n}\n"],"mappings":"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"}
|