agentix-cli 0.120.0 → 0.121.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{App-XMSNIAU6.js → App-AMIGKAXO.js} +2 -2
- package/dist/{ChatApp-BFYTIQF3.js → ChatApp-WMI4HQSY.js} +2 -2
- package/dist/{agent-63RXZRL2.js → agent-TQQOAVMU.js} +2 -2
- package/dist/article-sync-TLZTHRZB.js +2 -0
- package/dist/article-sync-TLZTHRZB.js.map +1 -0
- package/dist/{board-dashboard-XQEZBQL5.js → board-dashboard-FNMZAP7A.js} +4 -4
- package/dist/{chunk-B6OXEIOF.js → chunk-25XR6QK2.js} +2 -2
- package/dist/{chunk-4PQREYBU.js → chunk-2IMK3NGJ.js} +2 -2
- package/dist/{chunk-247SOQLP.js → chunk-2NGXOYTS.js} +67 -67
- package/dist/chunk-2NGXOYTS.js.map +1 -0
- package/dist/{chunk-6PC4EZ4P.js → chunk-43CLFC4B.js} +2 -2
- package/dist/{chunk-NDPU6AX2.js → chunk-4GDLXWCH.js} +2 -2
- package/dist/{chunk-2G227TF3.js → chunk-6TUQHOO3.js} +2 -2
- package/dist/{chunk-RJBRYUKU.js → chunk-7KY7EEEI.js} +2 -2
- package/dist/{chunk-IOYRRGK7.js → chunk-BLEW5WQL.js} +2 -2
- package/dist/{chunk-S6NQB7VV.js → chunk-BWHHZJNJ.js} +3 -3
- package/dist/chunk-BWHHZJNJ.js.map +1 -0
- package/dist/{chunk-KQX3U3H2.js → chunk-CAS2IWSM.js} +2 -2
- package/dist/chunk-DJQMG6TH.js +4 -0
- package/dist/chunk-DJQMG6TH.js.map +1 -0
- package/dist/{chunk-U46G24CD.js → chunk-EI7D5V2L.js} +2 -2
- package/dist/{chunk-FTZCNID3.js → chunk-ELUEF4NC.js} +2 -2
- package/dist/{chunk-FSXWNVGL.js → chunk-H5Z42X7W.js} +2 -2
- package/dist/{chunk-PCOXSCYT.js → chunk-I4IDZSGF.js} +5 -5
- package/dist/{chunk-HK7TU5NX.js → chunk-IKBWV7YJ.js} +2 -2
- package/dist/{chunk-UAPWOE2W.js → chunk-MORRCYJE.js} +9 -9
- package/dist/chunk-MORRCYJE.js.map +1 -0
- package/dist/{chunk-IMLLHT7U.js → chunk-OPNZWQ6Y.js} +9 -9
- package/dist/{chunk-IMLLHT7U.js.map → chunk-OPNZWQ6Y.js.map} +1 -1
- package/dist/{chunk-NG677JHW.js → chunk-PAWCJIPZ.js} +2 -2
- package/dist/{chunk-AXFE34OV.js → chunk-RMA7KKR7.js} +2 -2
- package/dist/{chunk-6JJJ5MUU.js → chunk-Y4PR5LR3.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{client-TSYZXEH7.js → client-ICDRAZBU.js} +2 -2
- package/dist/{config-QAE3LNFQ.js → config-K4UYPR7W.js} +2 -2
- package/dist/config-mutator-IGA6D4S5.js +2 -0
- package/dist/{doctor-PVLVKLRA.js → doctor-LRTOOCM6.js} +2 -2
- package/dist/index.d.ts +76 -2
- package/dist/index.js +1 -1
- package/dist/{program-E3NTPNCZ.js → program-OKPC77BN.js} +220 -220
- package/dist/program-OKPC77BN.js.map +1 -0
- package/dist/{promote-IRBBEXGG.js → promote-4LW53AMH.js} +2 -2
- package/dist/{query-R6YELRQN.js → query-4OPS5PET.js} +2 -2
- package/dist/{registry-HIZIFAVH.js → registry-YHSHGJ4K.js} +2 -2
- package/dist/{request-planner-CRG3OGGG.js → request-planner-GDXSBNN4.js} +2 -2
- package/dist/{review-candidates-6YIBOEEG.js → review-candidates-TUVDG2LR.js} +2 -2
- package/dist/{routing-G33LLOTF.js → routing-OKDZ4VYH.js} +2 -2
- package/dist/schedule-tool-MJLXMXN5.js +2 -0
- package/dist/scheduler-E5SASFAH.js +2 -0
- package/dist/{seat-IJN7NETF.js → seat-K6FNHYMY.js} +2 -2
- package/dist/{select-D7AZ2XZF.js → select-QMJXHNUP.js} +2 -2
- package/dist/{set-enabled-5YSDIQXU.js → set-enabled-I77LPL6Q.js} +2 -2
- package/dist/{tool-XADSW6NL.js → tool-BQUKONSF.js} +2 -2
- package/dist/{tool-GQIQWHWB.js → tool-QTGNWXUA.js} +2 -2
- package/dist/wiki-F6EZBBUH.js +2 -0
- package/package.json +1 -1
- package/dist/chunk-23SQSUBI.js +0 -3
- package/dist/chunk-23SQSUBI.js.map +0 -1
- package/dist/chunk-247SOQLP.js.map +0 -1
- package/dist/chunk-S6NQB7VV.js.map +0 -1
- package/dist/chunk-UAPWOE2W.js.map +0 -1
- package/dist/config-mutator-WRQ7MFQL.js +0 -2
- package/dist/program-E3NTPNCZ.js.map +0 -1
- package/dist/schedule-tool-IP7ZCQKI.js +0 -2
- package/dist/scheduler-QXLPYSNJ.js +0 -2
- package/dist/wiki-R4MNLBXE.js +0 -2
- /package/dist/{App-XMSNIAU6.js.map → App-AMIGKAXO.js.map} +0 -0
- /package/dist/{ChatApp-BFYTIQF3.js.map → ChatApp-WMI4HQSY.js.map} +0 -0
- /package/dist/{agent-63RXZRL2.js.map → agent-TQQOAVMU.js.map} +0 -0
- /package/dist/{board-dashboard-XQEZBQL5.js.map → board-dashboard-FNMZAP7A.js.map} +0 -0
- /package/dist/{chunk-B6OXEIOF.js.map → chunk-25XR6QK2.js.map} +0 -0
- /package/dist/{chunk-4PQREYBU.js.map → chunk-2IMK3NGJ.js.map} +0 -0
- /package/dist/{chunk-6PC4EZ4P.js.map → chunk-43CLFC4B.js.map} +0 -0
- /package/dist/{chunk-NDPU6AX2.js.map → chunk-4GDLXWCH.js.map} +0 -0
- /package/dist/{chunk-2G227TF3.js.map → chunk-6TUQHOO3.js.map} +0 -0
- /package/dist/{chunk-RJBRYUKU.js.map → chunk-7KY7EEEI.js.map} +0 -0
- /package/dist/{chunk-IOYRRGK7.js.map → chunk-BLEW5WQL.js.map} +0 -0
- /package/dist/{chunk-KQX3U3H2.js.map → chunk-CAS2IWSM.js.map} +0 -0
- /package/dist/{chunk-U46G24CD.js.map → chunk-EI7D5V2L.js.map} +0 -0
- /package/dist/{chunk-FTZCNID3.js.map → chunk-ELUEF4NC.js.map} +0 -0
- /package/dist/{chunk-FSXWNVGL.js.map → chunk-H5Z42X7W.js.map} +0 -0
- /package/dist/{chunk-PCOXSCYT.js.map → chunk-I4IDZSGF.js.map} +0 -0
- /package/dist/{chunk-HK7TU5NX.js.map → chunk-IKBWV7YJ.js.map} +0 -0
- /package/dist/{chunk-NG677JHW.js.map → chunk-PAWCJIPZ.js.map} +0 -0
- /package/dist/{chunk-AXFE34OV.js.map → chunk-RMA7KKR7.js.map} +0 -0
- /package/dist/{chunk-6JJJ5MUU.js.map → chunk-Y4PR5LR3.js.map} +0 -0
- /package/dist/{client-TSYZXEH7.js.map → client-ICDRAZBU.js.map} +0 -0
- /package/dist/{config-QAE3LNFQ.js.map → config-K4UYPR7W.js.map} +0 -0
- /package/dist/{config-mutator-WRQ7MFQL.js.map → config-mutator-IGA6D4S5.js.map} +0 -0
- /package/dist/{doctor-PVLVKLRA.js.map → doctor-LRTOOCM6.js.map} +0 -0
- /package/dist/{promote-IRBBEXGG.js.map → promote-4LW53AMH.js.map} +0 -0
- /package/dist/{query-R6YELRQN.js.map → query-4OPS5PET.js.map} +0 -0
- /package/dist/{registry-HIZIFAVH.js.map → registry-YHSHGJ4K.js.map} +0 -0
- /package/dist/{request-planner-CRG3OGGG.js.map → request-planner-GDXSBNN4.js.map} +0 -0
- /package/dist/{review-candidates-6YIBOEEG.js.map → review-candidates-TUVDG2LR.js.map} +0 -0
- /package/dist/{routing-G33LLOTF.js.map → routing-OKDZ4VYH.js.map} +0 -0
- /package/dist/{schedule-tool-IP7ZCQKI.js.map → schedule-tool-MJLXMXN5.js.map} +0 -0
- /package/dist/{scheduler-QXLPYSNJ.js.map → scheduler-E5SASFAH.js.map} +0 -0
- /package/dist/{seat-IJN7NETF.js.map → seat-K6FNHYMY.js.map} +0 -0
- /package/dist/{select-D7AZ2XZF.js.map → select-QMJXHNUP.js.map} +0 -0
- /package/dist/{set-enabled-5YSDIQXU.js.map → set-enabled-I77LPL6Q.js.map} +0 -0
- /package/dist/{tool-XADSW6NL.js.map → tool-BQUKONSF.js.map} +0 -0
- /package/dist/{tool-GQIQWHWB.js.map → tool-QTGNWXUA.js.map} +0 -0
- /package/dist/{wiki-R4MNLBXE.js.map → wiki-F6EZBBUH.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/business/config.ts","../src/boards/config.ts","../src/voice/hotkey.ts","../src/voice/orb-palettes.ts","../src/voice/language.ts","../src/voice/speakable.ts","../src/voice/noise.ts","../src/whatsapp-triage/config.ts","../src/requests/store.ts","../src/requests/tracker.ts","../src/requests/operator.ts","../src/people/people.ts","../src/mcp/tool-names.ts","../src/daemon/config.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Business layer configuration schema (EXPERIMENTAL) ---\n// Optional top-level `business` key in agentx.json.\n// When enabled, AgentX simulates a team with roles, schedules, and a work pool.\n// Experimental: off by default, undocumented while the model settles —\n// config shape may change between minor versions without migration.\n\nconst dayEnum = z.enum([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"])\n\nconst scheduleSchema = z.object({\n days: z.array(dayEnum).default([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"]),\n start: z.string().regex(/^\\d{2}:\\d{2}$/), // \"09:00\"\n end: z.string().regex(/^\\d{2}:\\d{2}$/), // \"17:00\"\n lunch: z.object({\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n }).optional(),\n})\n\nconst roleSchema = z.object({\n title: z.string(),\n responsibilities: z.array(z.string()).default([]),\n sopPath: z.string().optional(),\n kpis: z.array(z.string()).default([]),\n})\n\nconst orgEntrySchema = z.object({\n role: z.string(),\n reportsTo: z.string().optional(),\n schedule: scheduleSchema,\n utilizationTarget: z.number().min(0).max(1).default(0.8),\n})\n\nconst channelRefSchema = z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n})\n\nconst workSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"backlog\"),\n path: z.string().default(\".agentx/backlog.md\"),\n }),\n z.object({\n type: z.literal(\"gitlab\"),\n projects: z.array(z.string()).default([]), // empty = all configured gitlab projects\n }),\n z.object({\n type: z.literal(\"wiki\"),\n path: z.string(),\n glob: z.string().default(\"**/*.md\"),\n }),\n])\n\n/** Per-project metadata. Phase 3 of the architectural rescue —\n * `pm` is the agentId responsible for gating dispatches on this\n * project. Optional; when null, dispatches don't go through a PM\n * gate. The wiring lives in `decideAndCommit` (gated by a flag,\n * separate commit). */\nconst projectSchema = z.object({\n /** Project identifier. For GitLab/GitHub: \"owner/repo\". For\n * acme-internal projects: a stable string. */\n id: z.string(),\n /** AgentId of the PM for this project. Looked up by\n * `Organization.pmFor(project)` at dispatch time. */\n pm: z.string().optional(),\n /** Optional client this project belongs to. Used by the activity\n * graph to attribute non-projected events (Telegram chats, free-\n * text WhatsApp, cron jobs) to the right client. Falls back to\n * the leading segment of `id` (split on \"/\") when unset. */\n client: z.string().optional(),\n})\n\n/** Maps a chat or sender to a client/project so free-text channels\n * (telegram/whatsapp/slack/discord/email) get attributed correctly\n * in the activity graph. Without this, every chat lands in the\n * fallback \"internal\" bucket — the operator can't tell which client\n * the agent was helping.\n *\n * Match priority: chatId > username > channel-default. First match\n * wins. All fields except `client` are optional; `project` falls\n * back to `${client}/_chat` when unset. */\nconst contactMapSchema = z.object({\n /** Channel kind, e.g. \"telegram\", \"whatsapp\", \"slack\", \"discord\". */\n channel: z.string().optional(),\n /** Native chat id (\"-100...\" for tg groups, JID for WhatsApp). */\n chatId: z.string().optional(),\n /** Sender username/handle as exposed by the channel adapter. */\n username: z.string().optional(),\n /** Numeric sender id when username is unstable. */\n senderId: z.string().optional(),\n /** Client this contact belongs to. Required. */\n client: z.string(),\n /** Project this contact's traffic should attribute to. Defaults to\n * `${client}/_chat`. */\n project: z.string().optional(),\n /** Display name override for the initiator pill. */\n displayName: z.string().optional(),\n})\n\n/** Per-client policy. Clients are DERIVED from `projects` and `contactMap`\n * — one exists the moment either mentions it — so nothing needs declaring\n * up front. This record only carries overrides for the two things a\n * heuristic cannot know: how fast a delay costs you, and what an agent may\n * do for this client without asking first. */\nconst clientPolicySchema = z.object({\n /** Display name. Defaults to the client id. */\n name: z.string().optional(),\n /** \"client\" is someone who is waiting on you and can be let down; \"own\"\n * is your own product; \"internal\" is machinery with no external clock. */\n kind: z.enum([\"client\", \"internal\", \"own\"]).optional(),\n /** How long a wait may last before it starts costing: \"4h\", \"90m\", \"2d\".\n * Unset means no external clock, so delay is flat. */\n respondWithin: z.string().optional(),\n /** Capabilities an agent may exercise unattended. \"*\" means all. Ignored\n * for kind \"client\" — see mayProceedUnattended(). */\n standing: z.array(z.string()).default([]),\n})\n\nexport const businessConfigSchema = z.object({\n enabled: z.boolean().default(false),\n timezone: z.string().default(\"UTC\"),\n mainChannel: channelRefSchema,\n workSource: workSourceSchema,\n roles: z.record(z.string(), roleSchema).default({}),\n orgChart: z.record(z.string(), orgEntrySchema).default({}),\n /** Per-project metadata. Phase 3 — defaults to empty array, so\n * existing agentx.json files validate without modification. */\n projects: z.array(projectSchema).default([]),\n /** Maps free-text chat senders to a client/project so the activity\n * graph can attribute Telegram/WhatsApp/etc. to the right client\n * instead of the catch-all \"internal\" bucket. */\n contactMap: z.array(contactMapSchema).default([]),\n /** Overrides for derived clients. Keyed by client id; absent entries get\n * defaults, so this stays empty until you need a clock or an\n * authorization. */\n clients: z.record(z.string(), clientPolicySchema).default({}),\n /** Work-tick cadence in minutes during business hours. Default 15. */\n workTickMinutes: z.number().int().min(1).max(60).default(15),\n /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */\n idleQueueThreshold: z.number().int().min(0).default(0),\n /** Standup-tick gating + plan-driven priorities. The day-cycle's morning\n * STANDUP used to fire unconditionally per agent — operators reported it\n * as \"mechanical\" because it dispatched a Claude turn even when no human\n * had set a plan for the day. Now: the cycle reads\n * .agentx/plans/<date>.md (day → week → month fallback) and either\n * injects the plan as standup priorities, or posts a \"no plan today\"\n * notification to mainChannel and dispatches no agent. */\n standup: z.object({\n /** Master switch. When false, fireStandup is a no-op (silent). */\n enabled: z.boolean().default(true),\n /** Plans directory, relative to the daemon CWD. */\n plansDir: z.string().default(\".agentx/plans\"),\n /** Hard ceiling on how many agents the standup tick can dispatch in a\n * single morning — guards against an org-chart misconfiguration\n * fanning out to dozens of Claude calls before anyone notices. */\n maxAgentsPerDay: z.number().int().min(1).max(100).default(20),\n /** When true, even with a plan present the cycle won't dispatch — only\n * posts the resolved plan to mainChannel for human review. Useful as\n * a kill-switch while iterating on plan formats. */\n dryRun: z.boolean().default(false),\n }).default({}),\n})\n\nexport type BusinessConfig = z.infer<typeof businessConfigSchema>\nexport type BusinessRole = z.infer<typeof roleSchema>\nexport type BusinessOrgEntry = z.infer<typeof orgEntrySchema>\nexport type BusinessSchedule = z.infer<typeof scheduleSchema>\nexport type BusinessWorkSource = z.infer<typeof workSourceSchema>\nexport type BusinessChannelRef = z.infer<typeof channelRefSchema>\nexport type BusinessProject = z.infer<typeof projectSchema>\n","import { z } from \"zod\"\n\n// --- Kanban board configuration ---\n//\n// Top-level optional keys in agentx.json, independent of the business layer:\n//\n// boards: array of kanban boards, each backed by a WorkSource\n// dashboard: HTTP server controlling whether the board UI serves\n//\n// Both default to empty / disabled so existing installs see no change.\n\nexport const boardLabelSchema = z.object({\n /** Label name — used verbatim as the GitLab label string. */\n name: z.string(),\n color: z.string().regex(/^#[0-9a-f]{6}$/i).default(\"#6366f1\"),\n description: z.string().optional(),\n})\n\n/**\n * A board column. `kind` controls what issues land here and how drag-to-this-column\n * mutates the issue:\n *\n * - \"open-backlog\": opened issues that have NO scoped label matching `scopedPrefix`\n * (default \"Status\"). Dragging INTO this column removes any existing scoped label.\n * - \"scoped-label\": opened issues carrying `scopedLabel` (e.g. \"Status::Doing\").\n * Scoped labels are mutually exclusive by prefix, so adding the new one lets\n * GitLab auto-remove the old one (Premium), but we still explicitly strip the\n * previous column's label for compatibility.\n * - \"closed\": all closed issues. Dragging here closes the issue; dragging away\n * reopens it.\n * - \"label\" (default, legacy): opened issues with `mapsToLabel`; flat add/remove.\n */\nexport const boardColumnSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"column id must be lowercase slug\"),\n title: z.string(),\n kind: z.enum([\"open-backlog\", \"scoped-label\", \"closed\", \"label\"]).default(\"label\"),\n /** For kind=\"label\": label written on entry, removed on exit. */\n mapsToLabel: z.string().optional(),\n /** For kind=\"scoped-label\": the full scoped label (e.g. \"Status::Doing\"). */\n scopedLabel: z.string().optional(),\n /** For kind=\"open-backlog\": scoped prefix that marks \"claimed\" cards to exclude. */\n scopedPrefix: z.string().default(\"Status\"),\n /** CSS color or hex — shown as the column's top accent bar. */\n accent: z.string().optional(),\n})\nexport type BoardColumnId = string\n\nexport const boardReconcileSchema = z.object({\n enabled: z.boolean().default(true),\n staleDoingMinutes: z.number().int().min(1).default(45),\n respectLunchBreak: z.boolean().default(true),\n respectSchedule: z.boolean().default(true),\n /** Badge only for P1/P3; auto-notify + auto-demote deferred. */\n action: z.enum([\"badge\", \"notify\"]).default(\"badge\"),\n})\n\nexport const boardSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"gitlab\"),\n /** GitLab project paths (e.g. \"globex/globex-system-v2\"). At least one required. */\n projects: z.array(z.string()).min(1),\n }),\n // future: backlog, wiki\n])\n\n/**\n * GitLab-style default board — six columns:\n * Open (backlog) → To Do → Doing → On Hold → Review → Closed\n *\n * Drives the `Status::*` scoped label taxonomy. Moving a card adds/removes the\n * column's scoped label; moving INTO Closed closes the issue; moving OUT of\n * Closed reopens it; moving INTO Open strips any Status::* label.\n */\nexport const DEFAULT_COLUMNS: Array<z.infer<typeof boardColumnSchema>> = [\n { id: \"open\", title: \"Open\", kind: \"open-backlog\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n { id: \"todo\", title: \"To Do\", kind: \"scoped-label\", scopedLabel: \"Status::To Do\", scopedPrefix: \"Status\", accent: \"#fb7a35\" },\n { id: \"doing\", title: \"Doing\", kind: \"scoped-label\", scopedLabel: \"Status::Doing\", scopedPrefix: \"Status\", accent: \"#22c55e\" },\n { id: \"onhold\", title: \"On Hold\", kind: \"scoped-label\", scopedLabel: \"Status::On Hold\", scopedPrefix: \"Status\", accent: \"#f59e0b\" },\n { id: \"review\", title: \"Review\", kind: \"scoped-label\", scopedLabel: \"Status::Review\", scopedPrefix: \"Status\", accent: \"#3b82f6\" },\n { id: \"closed\", title: \"Closed\", kind: \"closed\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n]\n\nexport const boardSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"board id must be lowercase slug\"),\n name: z.string(),\n source: boardSourceSchema,\n /** If set, ANDed into every listAll query and shown as a baseline chip in the UI. */\n primaryToolLabel: z.string().optional(),\n labels: z.array(boardLabelSchema).default([]),\n columns: z.array(boardColumnSchema).min(1).default(DEFAULT_COLUMNS),\n timeRangeDays: z.number().int().min(1).max(365).default(30),\n /** Maximum closed-issue window (days) shown in the Closed column. */\n closedWindowDays: z.number().int().min(1).max(365).default(30),\n reconciliation: boardReconcileSchema.default({}),\n})\n\nexport const boardsConfigSchema = z.array(boardSchema).default([])\n\nexport const dashboardDaemonSchema = z.object({\n name: z.string(),\n /** Daemon HTTP URL (the one serving /agents, /health, /task). */\n url: z.string(),\n /** Optional board-dashboard URL if we want to proxy admin writes to this\n * peer. Defaults to `url` with port :18800/:19900 rewritten to :4202,\n * matching our convention that the dashboard runs alongside the daemon. */\n dashboardUrl: z.string().optional(),\n /** Bearer for the daemon's HTTP API (live snapshot, task stream). */\n token: z.string().optional(),\n /** Bearer for the peer's dashboard when proxying admin calls. Defaults to\n * `token` when unset. */\n dashboardToken: z.string().optional(),\n})\n\nexport const dashboardConfigSchema = z.object({\n enabled: z.boolean().default(false),\n port: z.number().int().default(4202),\n bind: z.string().default(\"127.0.0.1\"),\n /** Opt-in bearer token for writes. If unset, writes are unauthenticated. */\n token: z.string().optional(),\n /** Primary daemon the dashboard talks to for live view + A2A. */\n daemonUrl: z.string().default(\"http://localhost:18800\"),\n /** Additional daemons to poll (beyond peers discovered via primary's /mesh). */\n daemons: z.array(dashboardDaemonSchema).default([]),\n /** Default agent used for AI-assisted drafting in the create-issue flow. */\n draftAgent: z.string().optional(),\n}).default({})\n\nexport type BoardConfig = z.infer<typeof boardSchema>\nexport type BoardLabel = z.infer<typeof boardLabelSchema>\nexport type BoardColumn = z.infer<typeof boardColumnSchema>\nexport type BoardSource = z.infer<typeof boardSourceSchema>\nexport type BoardReconcile = z.infer<typeof boardReconcileSchema>\nexport type DashboardConfig = z.infer<typeof dashboardConfigSchema>\n\n/**\n * Decide which column an item lives in. Walks columns in order; the first\n * matching column wins. Items with `state: \"closed\"` always land in the\n * first `kind: \"closed\"` column; otherwise the first `scoped-label` whose\n * `scopedLabel` is present wins; otherwise the first `label` column whose\n * `mapsToLabel` is present; otherwise the first `open-backlog` column.\n *\n * Returns `null` when the item carries a scoped label matching an\n * `open-backlog` column's `scopedPrefix` but no `scoped-label` column claims\n * it — those are off-board workflow states (e.g. `Status::Done` when no\n * Done column exists) and must not leak into the backlog.\n */\nexport function deriveStage(\n item: { state?: \"opened\" | \"closed\"; labels?: string[] },\n columns: BoardColumn[],\n): BoardColumnId | null {\n const set = new Set(item.labels || [])\n if (item.state === \"closed\") {\n const closed = columns.find((c) => c.kind === \"closed\")\n if (closed) return closed.id\n }\n for (const c of columns) {\n if (c.kind === \"scoped-label\" && c.scopedLabel && set.has(c.scopedLabel)) return c.id\n }\n for (const c of columns) {\n if (c.kind === \"label\" && c.mapsToLabel && set.has(c.mapsToLabel)) return c.id\n }\n const backlog = columns.find((c) => c.kind === \"open-backlog\")\n if (backlog) {\n // If the item has any scoped-prefix label, it's not an open-backlog candidate.\n const prefix = backlog.scopedPrefix + \"::\"\n const hasScoped = [...set].some((l) => l.startsWith(prefix))\n return hasScoped ? null : backlog.id\n }\n return columns[0]?.id ?? null\n}\n\n/**\n * Describes how to mutate an issue to move it between two columns.\n * `closeIssue` / `reopen` take precedence over label mutations on the same action.\n */\nexport interface ColumnTransition {\n addLabels?: string[]\n removeLabels?: string[]\n closeIssue?: boolean\n reopen?: boolean\n}\n\nexport function transitionDiff(\n fromColumn: BoardColumn | undefined,\n toColumn: BoardColumn,\n): ColumnTransition {\n const add: string[] = []\n const remove: string[] = []\n let closeIssue = false\n let reopen = false\n\n // Handle target column.\n if (toColumn.kind === \"scoped-label\" && toColumn.scopedLabel) {\n add.push(toColumn.scopedLabel)\n } else if (toColumn.kind === \"label\" && toColumn.mapsToLabel) {\n add.push(toColumn.mapsToLabel)\n } else if (toColumn.kind === \"closed\") {\n closeIssue = true\n }\n\n // Handle source column — strip its label; if it was \"closed\", reopen.\n if (fromColumn) {\n if (fromColumn.kind === \"scoped-label\" && fromColumn.scopedLabel) {\n remove.push(fromColumn.scopedLabel)\n } else if (fromColumn.kind === \"label\" && fromColumn.mapsToLabel) {\n remove.push(fromColumn.mapsToLabel)\n } else if (fromColumn.kind === \"closed\" && toColumn.kind !== \"closed\") {\n reopen = true\n }\n }\n\n return {\n addLabels: add.length ? add : undefined,\n removeLabels: remove.length ? remove : undefined,\n closeIssue: closeIssue || undefined,\n reopen: reopen || undefined,\n }\n}\n","// --- Keyboard shortcuts for AgentX Voice, as stored in agentx.json ---\n//\n// A shortcut is written as modifiers and one key joined by \"+\", for\n// example \"opt+space\", \"cmd+opt+period\" or \"ctrl+opt+1\". The Mac app\n// (apps/mac-voice Hotkey.swift) reads the same form. Stored in one\n// normal order (ctrl, opt, shift, cmd, then the key) so two ways of\n// writing a shortcut compare equal.\n\nconst MODIFIERS: Record<string, \"ctrl\" | \"opt\" | \"shift\" | \"cmd\"> = {\n ctrl: \"ctrl\", control: \"ctrl\",\n opt: \"opt\", option: \"opt\", alt: \"opt\",\n shift: \"shift\",\n cmd: \"cmd\", command: \"cmd\",\n}\nconst ORDER = [\"ctrl\", \"opt\", \"shift\", \"cmd\"] as const\n\nconst NAMED_KEYS = [\n \"space\", \"period\", \"comma\", \"slash\", \"semicolon\", \"quote\", \"minus\", \"equal\",\n \"leftbracket\", \"rightbracket\", \"backslash\", \"grave\", \"return\", \"tab\",\n]\nconst KEY_ALIASES: Record<string, string> = { \".\": \"period\", \",\": \"comma\", \"/\": \"slash\", \";\": \"semicolon\", \"'\": \"quote\", \"-\": \"minus\", \"=\": \"equal\", \"`\": \"grave\", enter: \"return\" }\n\nconst isFKey = (k: string) => /^f([1-9]|1[0-9]|20)$/.test(k)\nconst isKey = (k: string) => /^[a-z0-9]$/.test(k) || NAMED_KEYS.includes(k) || isFKey(k)\n\nexport type HotkeyParse = { ok: true; value: string } | { ok: false; error: string }\n\n/** A shortcut in its stored form, or why it cannot be one. */\nexport function parseHotkey(input: string): HotkeyParse {\n const raw = String(input ?? \"\").trim().toLowerCase()\n if (!raw) return { ok: false, error: \"is empty\" }\n // \"+\" itself is not a key we accept, so splitting on it is safe.\n const parts = raw.split(\"+\").map((p) => p.trim())\n if (parts.some((p) => !p)) return { ok: false, error: `\"${input}\" has an empty part; write it like opt+space` }\n const key = KEY_ALIASES[parts[parts.length - 1]] ?? parts[parts.length - 1]\n const mods = new Set<string>()\n for (const p of parts.slice(0, -1)) {\n const m = MODIFIERS[p]\n if (!m) return { ok: false, error: `\"${p}\" is not a modifier; use ctrl, opt, shift or cmd` }\n if (mods.has(m)) return { ok: false, error: `\"${input}\" names ${m} twice` }\n mods.add(m)\n }\n if (!isKey(key)) return { ok: false, error: `\"${key}\" is not a key AgentX Voice can use; use a letter, a digit, f1 to f20, or space, period, comma, slash, return, tab` }\n // Without ctrl, opt or cmd the shortcut would swallow ordinary typing.\n if (!isFKey(key) && ![\"ctrl\", \"opt\", \"cmd\"].some((m) => mods.has(m))) {\n return { ok: false, error: `\"${input}\" needs ctrl, opt or cmd, or it would take that key away from your typing` }\n }\n return { ok: true, value: [...ORDER.filter((m) => mods.has(m)), key].join(\"+\") }\n}\n\n/** Zod refinement message helper: undefined when fine. */\nexport function hotkeyError(input: string): string | undefined {\n const r = parseHotkey(input)\n return r.ok ? undefined : r.error\n}\n\n/** \"⌃⌥1\" for \"ctrl+opt+1\", as the Mac shows shortcuts. */\nexport function describeHotkey(value: string): string {\n const sym: Record<string, string> = { ctrl: \"⌃\", opt: \"⌥\", shift: \"⇧\", cmd: \"⌘\" }\n const names: Record<string, string> = { space: \"Space\", period: \".\", comma: \",\", slash: \"/\", semicolon: \";\", quote: \"'\", minus: \"-\", equal: \"=\", leftbracket: \"[\", rightbracket: \"]\", backslash: \"\\\\\", grave: \"`\", return: \"Return\", tab: \"Tab\" }\n const parts = value.split(\"+\")\n const key = parts.pop() ?? \"\"\n return parts.map((m) => sym[m] ?? m).join(\"\") + (names[key] ?? key.toUpperCase())\n}\n\nexport const DEFAULT_HOTKEYS = { talk: \"opt+space\", stop: \"cmd+opt+period\", paste: \"cmd+opt+v\" } as const\n","// --- The voice orb's colour palettes, drawn from nature ---\n//\n// Each agent's orb in AgentX Voice flows through one of these gradients.\n// An agent picks one with presence.palette; without one it gets the\n// palette whose hue is nearest its own presence.color, so it stays\n// recognisable as the same agent as its on-screen pointer; with neither\n// it gets the default, voice.palette. The app draws whatever colours the\n// daemon sends, so this file is the only list.\n\nexport interface OrbPalette {\n id: string\n label: string\n /** Five stops, deep to light. The orb's mesh uses all five; the last\n * is its bright core. */\n colors: [string, string, string, string, string]\n /** The hue (0–360) an agent colour is matched against. */\n hue: number\n}\n\nexport const ORB_PALETTES: OrbPalette[] = [\n { id: \"sunrise\", label: \"Sunrise\", hue: 18, colors: [\"#E8505B\", \"#F2726F\", \"#F79A5A\", \"#FBBF4A\", \"#FFE3A3\"] },\n { id: \"desert\", label: \"Desert\", hue: 36, colors: [\"#A4492C\", \"#C8683E\", \"#D9905E\", \"#E7B98A\", \"#F6E3C4\"] },\n { id: \"forest\", label: \"Forest\", hue: 100, colors: [\"#2F5D34\", \"#4C7D3A\", \"#6FA046\", \"#9CC25E\", \"#D5EBA4\"] },\n { id: \"lagoon\", label: \"Lagoon\", hue: 172, colors: [\"#0B6E73\", \"#0E9594\", \"#1FBFB2\", \"#56DCCB\", \"#B8F4EA\"] },\n { id: \"ocean\", label: \"Ocean\", hue: 205, colors: [\"#15457E\", \"#1C6BA8\", \"#2B93CF\", \"#5DBBE8\", \"#BFE6FA\"] },\n { id: \"dusk\", label: \"Dusk\", hue: 268, colors: [\"#2E2A7A\", \"#5B3E9E\", \"#8C4FB0\", \"#C8649E\", \"#F4A7B9\"] },\n { id: \"blossom\", label: \"Blossom\", hue: 330, colors: [\"#A3245B\", \"#CF3F78\", \"#E8699A\", \"#F49BB8\", \"#FFD6E0\"] },\n]\n\n/** What shows the assistant's state in AgentX Voice (voice.look): the\n * orb, or the character that wears the same palette. */\nexport const VOICE_LOOKS = [\"orb\", \"character\"] as const\nexport type VoiceLook = (typeof VOICE_LOOKS)[number]\n\n/** How often the character plays a small animation by itself when idle. */\nexport const VOICE_ANIMATIONS = [\"off\", \"rarely\", \"sometimes\", \"often\"] as const\nexport type VoiceAnimations = (typeof VOICE_ANIMATIONS)[number]\n\nexport const ORB_PALETTE_IDS = ORB_PALETTES.map((p) => p.id) as [string, ...string[]]\n\n/** #RRGGBB to its hue in degrees, or null when it is grey or not a colour. */\nfunction hueOf(hex: string): number | null {\n const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex)\n if (!m) return null\n const [r, g, b] = m.slice(1).map((x) => parseInt(x, 16) / 255)\n const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min\n if (d < 0.04) return null\n const h = max === r ? ((g - b) / d) % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4\n return (h * 60 + 360) % 360\n}\n\n/** The palette nearest an agent colour by hue. Grey or invalid: lagoon. */\nexport function paletteForColor(hex: string): OrbPalette {\n const hue = hueOf(hex)\n if (hue === null) return ORB_PALETTES.find((p) => p.id === \"lagoon\")!\n let best = ORB_PALETTES[0], bestDistance = 360\n for (const p of ORB_PALETTES) {\n const d = Math.abs(p.hue - hue)\n const distance = Math.min(d, 360 - d)\n if (distance < bestDistance) { best = p; bestDistance = distance }\n }\n return best\n}\n\n/** The palette of an agent that chose neither a palette nor a colour,\n * unless voice.palette names another. */\nexport const DEFAULT_PALETTE = \"lagoon\"\n\n/** An agent's palette: the one it chose (presence.palette), else the one\n * nearest the colour it chose (presence.color), else `fallback`\n * (voice.palette). `set` says whether it chose the palette itself. */\nexport function agentPalette(\n presence: { palette?: string; color?: string } | undefined,\n fallback: string = DEFAULT_PALETTE,\n): OrbPalette & { set: boolean } {\n const byId = (id?: string) => ORB_PALETTES.find((p) => p.id === id)\n const picked = byId(presence?.palette)\n if (picked) return { ...picked, set: true }\n const color = presence?.color\n const unchosen = color && hueOf(color) !== null ? paletteForColor(color) : byId(fallback) ?? byId(DEFAULT_PALETTE)!\n return { ...unchosen, set: false }\n}\n","// --- Which language a spoken line is in: English, French or Arabic ---\n//\n// An agent may hold one system voice per language, so a French answer is\n// read by a French voice. Replies are short and the choice is between\n// three very different languages, so a script check and a handful of\n// common words are enough; a model would be slower and no more useful.\n\nexport type Language = \"en\" | \"fr\" | \"ar\"\n\nconst EN = new Set(\"the and is are was were i you he she we they it this that to of in for with not but yes what have has will would can your my\".split(\" \"))\nconst FR = new Set(\"le la les des du de un une et est sont je tu il elle nous vous ils elles ce cette c'est pas pour que qui avec dans sur au aux mais très oui non merci bonjour votre mon\".split(\" \"))\nconst ACCENT = /[éèêëàâçùûîïôœ]/\n\n/** The line's language, or null when nothing in it says which. */\nexport function detectLanguage(text: string): Language | null {\n const arabic = (text.match(/[-ۿ]/g) ?? []).length\n const latin = (text.match(/[a-zA-ZÀ-ÿ]/g) ?? []).length\n if (arabic && arabic >= latin) return \"ar\"\n let en = 0, fr = 0\n for (const w of text.toLowerCase().replace(/[’]/g, \"'\").match(/[a-zà-ÿœ']+/g) ?? []) {\n if (EN.has(w)) en++\n if (FR.has(w) || w.startsWith(\"l'\") || w.startsWith(\"d'\") || w.startsWith(\"qu'\")) fr++\n else if (ACCENT.test(w)) fr += 0.5\n }\n if (en === fr) return null\n return en > fr ? \"en\" : \"fr\"\n}\n","// --- Written answer to spoken answer ---\n//\n// Shared by the daemon's voice replies (/ask, talk, voice history) and the\n// phone app's spoken answers (voice-io-api.ts). Moved out of index.ts\n// unchanged, with the length cap as a parameter.\n\nimport { detectLanguage, type Language } from \"./language\"\n\n/** voice.spokenMaxChars' default: about three short sentences, half a\n * minute of speech. */\nexport const SPOKEN_MAX_CHARS = 500\n\n/** Said after an answer cut for speech, so the listener knows to read on. */\nconst MORE: Record<Language, string> = {\n en: \"The rest is on screen.\",\n fr: \"La suite est à l'écran.\",\n ar: \"الباقي على الشاشة.\",\n}\n\n/**\n * Convert text to TTS-friendly speech.\n * Strips markdown, expands technical terms, removes code, handles symbols.\n */\nexport function toSpeakable(text: string, max = SPOKEN_MAX_CHARS): string {\n let s = text\n\n // Remove code blocks entirely (don't read code aloud)\n s = s.replace(/```[\\s\\S]*?```/g, \". \")\n s = s.replace(/`[^`]*`/g, \"\") // inline code\n\n // Remove markdown syntax characters\n s = s.replace(/\\*\\*(.*?)\\*\\*/g, \"$1\") // bold\n s = s.replace(/__(.*?)__/g, \"$1\") // bold\n s = s.replace(/\\*([^*]+)\\*/g, \"$1\") // italic\n s = s.replace(/_([^_]+)_/g, \"$1\") // italic\n s = s.replace(/~~(.*?)~~/g, \"$1\") // strikethrough\n\n // Links: keep text, drop URL\n s = s.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n\n // Images: describe\n s = s.replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"image$1 \")\n\n // Headers: remove # markers\n s = s.replace(/^#{1,6}\\s+/gm, \"\")\n\n // List bullets: convert to pauses\n s = s.replace(/^[\\s]*[-*+]\\s+/gm, \". \")\n s = s.replace(/^[\\s]*\\d+\\.\\s+/gm, \". \")\n\n // Horizontal rules\n s = s.replace(/^[-=_]{3,}$/gm, \"\")\n\n // Blockquotes\n s = s.replace(/^>\\s*/gm, \"\")\n\n // URLs in plain text: replace with \"link\"\n s = s.replace(/https?:\\/\\/\\S+/g, \"link\")\n\n // File paths: keep readable\n s = s.replace(/\\/\\w+(\\/\\w+)+/g, (match) => match.split(\"/\").filter(Boolean).join(\" slash \"))\n\n // Technical symbols → words\n s = s.replace(/&/g, \" and \")\n s = s.replace(/@/g, \" at \")\n s = s.replace(/=>/g, \" returns \")\n s = s.replace(/->/g, \" to \")\n s = s.replace(/\\|/g, \" or \")\n\n // Common abbreviations\n s = s.replace(/\\be\\.g\\./gi, \"for example\")\n s = s.replace(/\\bi\\.e\\./gi, \"that is\")\n s = s.replace(/\\betc\\./gi, \"etcetera\")\n s = s.replace(/\\bvs\\.?\\b/gi, \"versus\")\n\n // Collapse whitespace\n s = s.replace(/\\n{2,}/g, \". \")\n s = s.replace(/\\n/g, \" \")\n s = s.replace(/\\s+/g, \" \")\n s = s.replace(/\\.\\s*\\./g, \".\")\n s = s.replace(/\\s+([.,!?])/g, \"$1\")\n\n // Strip leading/trailing whitespace and punctuation\n s = s.trim().replace(/^[.,:;]+\\s*/, \"\")\n\n // Cap for voice UX. A long answer ends on its last whole sentence inside\n // the cap, then says there is more; the written answer is shown whole.\n // Only a sentence longer than half the cap is cut between words.\n if (s.length > max) {\n const head = s.slice(0, max)\n const end = ([...s.slice(0, max + 1).matchAll(/[.!?؟](?=\\s)/g)].at(-1)?.index ?? -1) + 1\n s = (end >= max / 2 ? head.slice(0, end) : head.replace(/\\s+\\S*$/, \"\") + \"...\") + \" \" + MORE[detectLanguage(head) ?? \"en\"]\n }\n\n return s\n}\n","// --- Transcripts with no words ---\n//\n// Speech to text writes what it heard when nobody spoke: \"[background\n// noise]\", \"(mumbling)\". /ask drops such a transcript before any agent is\n// woken (#614: each one cost a full agent turn to answer \"I didn't catch\n// that\").\n\n/** voice.noiseFilter.markers' default: what the transcribers wrote on the\n * Mac in three days of voice turns, plus Whisper's own. */\nexport const NOISE_MARKERS = [\n \"background noise\", \"noise\", \"mumbling\", \"babbling\", \"unintelligible\",\n \"inaudible\", \"muffled speech\", \"pause\", \"silence\", \"music\",\n \"outro jingle\", \"blank audio\",\n]\n\n/** What the caller hears instead of an agent's answer. */\nexport const NOISE_REPLY = \"I didn't catch that.\"\n\nconst key = (s: string) => s.toLowerCase().replace(/[_\\s]+/g, \" \").trim()\n\n/**\n * True when a transcript holds no words: empty after trimming, or nothing\n * but bracketed markers from the list (\"[background noise]\", \"(Mumbling)\n * [pause]\"). A marker beside real speech is speech, and so is a short\n * answer such as \"Yes\".\n */\nexport function isNoiseTranscript(text: string, markers: string[] = NOISE_MARKERS): boolean {\n const known = new Set(markers.map(key))\n const rest = text.replace(/\\[([^\\][]*)\\]|\\(([^()]*)\\)/g, (group, square, round) =>\n known.has(key(square ?? round)) ? \"\" : group)\n return !/[\\p{L}\\p{N}]/u.test(rest)\n}\n","import { z } from \"zod\"\n\n// --- WhatsApp inbound triage settings (agentx.json → whatsappTriage) ---\n//\n// wacli posts each new WhatsApp message to POST /webhook/wacli. A watch\n// rule picks the messages an agent should look at; everything else is\n// dropped without being stored. Nothing is ever sent to the contact\n// unless the owner approves it (see drafts.ts).\n\nconst hhmm = z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\")\n\nexport const watchRuleSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, \"lowercase letters, digits, - and _\"),\n enabled: z.boolean().default(true),\n /** Chats to watch: a contact's or a group's JID, or a phone number. */\n chats: z.array(z.string().min(1)).default([]),\n /** Only messages from these people (JID or phone number), in any chat\n * the rule covers. Empty: anyone. */\n senders: z.array(z.string().min(1)).default([]),\n /** The agent that triages the messages. */\n agent: z.string().min(1),\n /** Extra instructions for the agent: who this contact is, where issues\n * go, the tone of a reply. */\n prompt: z.string().max(4000).optional(),\n /** No notifications between these times. Triage and drafts still happen. */\n quietHours: z.object({ start: hhmm, end: hhmm }).optional(),\n /** Send the agent's short acknowledgement without asking. Needs\n * whatsappTriage.allowAutoAck too. */\n autoAck: z.boolean().default(false),\n}).refine((r) => r.chats.length > 0 || r.senders.length > 0, {\n message: \"a rule needs at least one chat or sender\",\n})\n\nexport const whatsappTriageSchema = z.object({\n enabled: z.boolean().default(false),\n /** Environment variable holding the webhook secret given to\n * `wacli sync --webhook-secret`. Unsigned requests are refused. */\n secretEnv: z.string().regex(/^[A-Z_][A-Z0-9_]*$/).default(\"WACLI_WEBHOOK_SECRET\"),\n /** Messages from one chat within this many seconds become one task. */\n batchSeconds: z.number().int().min(0).max(600).default(20),\n /** Time zone for quiet hours. Default: this computer's. */\n timezone: z.string().optional(),\n /** Second switch for rules with autoAck. Both must be on. */\n allowAutoAck: z.boolean().default(false),\n /** Download images and voice notes so the agent can read or hear them. */\n describeMedia: z.boolean().default(true),\n wacli: z.object({\n bin: z.string().default(\"wacli\"),\n /** `wacli --account NAME`, when more than one is paired. */\n account: z.string().optional(),\n /** `wacli --store DIR`. */\n store: z.string().optional(),\n }).default({}),\n rules: z.array(watchRuleSchema).default([]),\n}).default({})\n\nexport type WatchRule = z.infer<typeof watchRuleSchema>\nexport type WhatsappTriageConfig = z.infer<typeof whatsappTriageSchema>\nexport type WacliSettings = WhatsappTriageConfig[\"wacli\"]\n","import type Database from \"better-sqlite3\"\nimport { ensureColumns, PERSON_COLUMNS } from \"@/storage/sqlite\"\n\n// --- Open requests: what a person asked for that is not finished (#356) ---\n//\n// One row per request a person made to an agent, kept until someone closes\n// it. A turn from a person starts as a `candidate`; it becomes an open\n// request when the turn leaves work behind (a delegation, a failure, a\n// restart) or the agent says so. A candidate whose turn simply answered\n// and ended is deleted.\n//\n// The store is plain sqlite in .agentx/db.sqlite. Every call is one small\n// synchronous statement: nothing here calls a model or waits on I/O.\n\nexport type OpenState = \"in_progress\" | \"waiting_owner\" | \"waiting_other\" | \"needs_attention\"\nexport type ClosedState = \"done\" | \"declined\" | \"dropped\"\nexport type RequestState = \"candidate\" | OpenState | ClosedState\n\nexport const OPEN_STATES: readonly OpenState[] = [\"in_progress\", \"waiting_owner\", \"waiting_other\", \"needs_attention\"]\nexport const CLOSED_STATES: readonly ClosedState[] = [\"done\", \"declined\", \"dropped\"]\n\nexport type LinkKind = \"run\" | \"delegation\" | \"card\"\n\nexport interface RequestRecord {\n id: string\n state: RequestState\n channel: string\n chatId: string\n sender: string | null\n /** The known person who asked (people, #384), when the sender is one. */\n person: string | null\n agentId: string\n /** The request in the person's words. */\n text: string\n createdAt: number\n /** Last time anything happened under this request. */\n updatedAt: number\n /** Why it needs attention, in words. */\n attentionReason: string | null\n /** When the owner was told it needs attention. Told once. */\n notifiedAt: number | null\n /** What the owner is being asked, while waiting on them. */\n question: string | null\n closedAt: number | null\n /** Link to the proof of a finished request (PR, issue, message, deploy). */\n evidence: string | null\n /** Why it was declined or dropped. */\n closeReason: string | null\n /** What the owner last wrote on it (a reply, or a note with a hand-off). */\n ownerNote: string | null\n /** That note was given to the agent in a pick-up: it is not said again. */\n noteSaid: boolean\n /** The agent it was asked of, once the owner handed it on. The chat it\n * was asked in is that agent's: another agent may not be able to write\n * there (#481). Null: never handed on. */\n askedAgent: string | null\n}\n\nexport const TEXT_MAX = 4000\nconst REASON_MAX = 500\n\n/** Creates the tables when missing. Named, not numbered, so it cannot\n * clash with a migration number another branch claimed. */\nexport function ensureRequestTables(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS requests (\n id TEXT PRIMARY KEY,\n state TEXT NOT NULL,\n channel TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n sender TEXT,\n agent_id TEXT NOT NULL,\n text TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n attention_reason TEXT,\n notified_at INTEGER,\n question TEXT,\n closed_at INTEGER,\n evidence TEXT,\n close_reason TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_requests_state_created ON requests(state, created_at);\n CREATE INDEX IF NOT EXISTS idx_requests_chat ON requests(agent_id, channel, chat_id, created_at);\n CREATE TABLE IF NOT EXISTS request_links (\n request_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n ref TEXT NOT NULL,\n at INTEGER NOT NULL,\n PRIMARY KEY (kind, ref),\n FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE\n );\n CREATE INDEX IF NOT EXISTS idx_request_links_request ON request_links(request_id);\n `)\n ensureColumns(db, \"requests\", PERSON_COLUMNS)\n // Added after the first version: a database that already has the table\n // gets the columns here.\n const cols = db.prepare(\"PRAGMA table_info(requests)\").all() as Array<{ name: string }>\n for (const [name, type] of [[\"pickup_at\", \"INTEGER\"], [\"owner_note\", \"TEXT\"], [\"note_said\", \"INTEGER\"], [\"asked_agent\", \"TEXT\"]]) {\n if (cols.some((c) => c.name === name)) continue\n try {\n db.exec(`ALTER TABLE requests ADD COLUMN ${name} ${type}`)\n } catch (e: any) {\n // Daemon and dashboard starting together: the other one added it first.\n if (!/duplicate column name/i.test(String(e?.message))) throw e\n }\n }\n}\n\n/** For readers: a database without the table has no requests, and reading\n * it must not create one. */\nexport function hasRequestTables(db: Database.Database): boolean {\n return !!db.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'requests'\").get()\n}\n\nfunction toRecord(r: any): RequestRecord {\n return {\n id: r.id, state: r.state, channel: r.channel, chatId: r.chat_id, sender: r.sender ?? null, person: r.person ?? null,\n agentId: r.agent_id, text: r.text, createdAt: r.created_at, updatedAt: r.updated_at,\n attentionReason: r.attention_reason ?? null, notifiedAt: r.notified_at ?? null,\n question: r.question ?? null, closedAt: r.closed_at ?? null,\n evidence: r.evidence ?? null, closeReason: r.close_reason ?? null, ownerNote: r.owner_note ?? null, noteSaid: !!r.note_said,\n askedAgent: r.asked_agent ?? null,\n }\n}\n\nconst placeholders = (n: number) => Array(n).fill(\"?\").join(\",\")\n\nexport class RequestStore {\n constructor(private db: Database.Database) {\n ensureRequestTables(db)\n db.pragma(\"foreign_keys = ON\")\n }\n\n /** Note a turn a person started. Linked to its run. */\n addCandidate(input: {\n id: string; runId: string; channel: string; chatId: string; sender?: string | null\n person?: string | null; agentId: string; text: string; now: number\n }): void {\n this.db.prepare(\n `INSERT OR IGNORE INTO requests (id, state, channel, chat_id, sender, person, agent_id, text, created_at, updated_at)\n VALUES (?, 'candidate', ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(input.id, input.channel, input.chatId, input.sender ?? null, input.person ?? null, input.agentId, input.text.slice(0, TEXT_MAX), input.now, input.now)\n this.link(input.id, \"run\", input.runId, input.now)\n }\n\n get(id: string): RequestRecord | null {\n const row = this.db.prepare(\"SELECT * FROM requests WHERE id = ?\").get(id)\n return row ? toRecord(row) : null\n }\n\n /** The request a run, delegation or card belongs to. */\n byLink(kind: LinkKind, ref: string): RequestRecord | null {\n const row = this.db.prepare(\n \"SELECT r.* FROM requests r JOIN request_links l ON l.request_id = r.id WHERE l.kind = ? AND l.ref = ?\",\n ).get(kind, ref)\n return row ? toRecord(row) : null\n }\n\n link(requestId: string, kind: LinkKind, ref: string, now: number): void {\n this.db.prepare(\"INSERT OR IGNORE INTO request_links (request_id, kind, ref, at) VALUES (?, ?, ?, ?)\").run(requestId, kind, ref, now)\n }\n\n links(requestId: string): Array<{ kind: LinkKind; ref: string; at: number }> {\n return this.db.prepare(\"SELECT kind, ref, at FROM request_links WHERE request_id = ? ORDER BY at, rowid\").all(requestId) as any\n }\n\n /** The agent's newest answer under this request: the final reply of its\n * latest linked run that has one. Null when nothing was recorded, or\n * when this database has no traces. */\n lastAnswer(requestId: string): { agentId: string; text: string; at: number } | null {\n try {\n const row = this.db.prepare(\n `SELECT t.agent_id AS agentId, t.final_response AS text, COALESCE(t.finished_at, t.started_at) AS at\n FROM request_links l JOIN task_traces t ON t.task_id = l.ref\n WHERE l.request_id = ? AND l.kind = 'run' AND t.final_response IS NOT NULL AND t.final_response != ''\n ORDER BY t.started_at DESC LIMIT 1`,\n ).get(requestId) as { agentId: string; text: string; at: number } | undefined\n return row ?? null\n } catch {\n return null\n }\n }\n\n /** Something happened under this request: it is not quiet. */\n touch(id: string, now: number): void {\n this.db.prepare(\"UPDATE requests SET updated_at = ? WHERE id = ?\").run(now, id)\n }\n\n /** Work is under way. Opens a candidate; clears an earlier \"needs\n * attention\" so a later failure is raised again. Closed stays closed. */\n progress(id: string, now: number, state: \"in_progress\" | \"waiting_other\" = \"in_progress\"): void {\n this.db.prepare(\n `UPDATE requests SET state = ?, updated_at = ?, attention_reason = NULL, notified_at = NULL, question = NULL\n WHERE id = ? AND state NOT IN (${placeholders(CLOSED_STATES.length)})`,\n ).run(state, now, id, ...CLOSED_STATES)\n }\n\n /** The next step is the owner's answer. */\n waitOnOwner(id: string, question: string, now: number): void {\n this.db.prepare(\n `UPDATE requests SET state = 'waiting_owner', updated_at = ?, question = ?, attention_reason = NULL, notified_at = NULL\n WHERE id = ? AND state NOT IN (${placeholders(CLOSED_STATES.length)})`,\n ).run(now, question.slice(0, TEXT_MAX), id, ...CLOSED_STATES)\n }\n\n /** The work failed, timed out, was cut off, or went quiet. A request\n * already waiting for attention keeps its first reason and is not\n * raised a second time. */\n needsAttention(id: string, reason: string, now: number): boolean {\n const res = this.db.prepare(\n `UPDATE requests SET state = 'needs_attention', updated_at = ?, attention_reason = ?, notified_at = NULL\n WHERE id = ? AND state NOT IN ('needs_attention', ${placeholders(CLOSED_STATES.length)})`,\n ).run(now, reason.replace(/\\s+/g, \" \").trim().slice(0, REASON_MAX), id, ...CLOSED_STATES)\n return res.changes > 0\n }\n\n /** Close it: done needs evidence, declined and dropped need a reason. */\n close(id: string, state: ClosedState, detail: string, now: number): boolean {\n const text = detail.trim()\n if (!text) throw new Error(state === \"done\" ? \"a finished request needs a link to the evidence\" : `a ${state} request needs a reason`)\n const res = this.db.prepare(\n `UPDATE requests SET state = ?, closed_at = ?, updated_at = ?, evidence = ?, close_reason = ?\n WHERE id = ? AND state NOT IN (${placeholders(CLOSED_STATES.length)})`,\n ).run(state, now, now, state === \"done\" ? text.slice(0, REASON_MAX) : null, state === \"done\" ? null : text.slice(0, REASON_MAX), id, ...CLOSED_STATES)\n return res.changes > 0\n }\n\n /** A candidate whose turn ended with nothing left to follow. */\n discardCandidate(id: string): boolean {\n return this.db.prepare(\"DELETE FROM requests WHERE id = ? AND state = 'candidate'\").run(id).changes > 0\n }\n\n /** Open requests, oldest first. */\n listOpen(): RequestRecord[] {\n return this.db.prepare(\n `SELECT * FROM requests WHERE state IN (${placeholders(OPEN_STATES.length)}) ORDER BY created_at, rowid`,\n ).all(...OPEN_STATES).map(toRecord)\n }\n\n /** Requests in these states that nothing has touched since `before`. */\n quietSince(states: RequestState[], before: number): RequestRecord[] {\n return this.db.prepare(\n `SELECT * FROM requests WHERE state IN (${placeholders(states.length)}) AND updated_at < ? ORDER BY created_at, rowid`,\n ).all(...states, before).map(toRecord)\n }\n\n /** Requests that need attention and were not raised yet. */\n awaitingNotice(): RequestRecord[] {\n return this.db.prepare(\n \"SELECT * FROM requests WHERE state = 'needs_attention' AND notified_at IS NULL ORDER BY created_at, rowid\",\n ).all().map(toRecord)\n }\n\n markNotified(id: string, now: number | null): void {\n this.db.prepare(\"UPDATE requests SET notified_at = ? WHERE id = ?\").run(now, id)\n }\n\n /** Requests in one state, oldest first. */\n listByState(state: RequestState): RequestRecord[] {\n return this.db.prepare(\"SELECT * FROM requests WHERE state = ? ORDER BY created_at, rowid\").all(state).map(toRecord)\n }\n\n /** The owner said \"pick it up again\": back in progress, and the agent\n * is to be told at the next check. */\n requestPickup(id: string, now: number): boolean {\n return this.db.prepare(\n `UPDATE requests SET state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL, pickup_at = ?\n WHERE id = ? AND state = 'needs_attention'`,\n ).run(now, now, id).changes > 0\n }\n\n /** The owner replied, or handed it to an agent (the same or another):\n * that agent has it, in progress, and is told at the next check. Any\n * open request, not only one that needs attention. The agent it was\n * asked of is kept from the first time: channel and chat stay its own. */\n handOff(id: string, agentId: string, note: string, now: number): boolean {\n return this.db.prepare(\n `UPDATE requests SET agent_id = ?, state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL,\n question = NULL, pickup_at = ?, owner_note = ?, note_said = NULL, asked_agent = COALESCE(asked_agent, agent_id)\n WHERE id = ? AND state IN (${placeholders(OPEN_STATES.length)})`,\n ).run(agentId, now, now, note.trim().slice(0, TEXT_MAX) || null, id, ...OPEN_STATES).changes > 0\n }\n\n /** This note of the owner is given to the agent in a pick-up, or (false)\n * was not after all. Only while it is still the note on the request: a\n * reply written since has its own pick-up and its own mark. */\n setNoteSaid(id: string, note: string, said: boolean): void {\n this.db.prepare(`UPDATE requests SET note_said = ? WHERE id = ? AND owner_note = ?${said ? \" AND pickup_at IS NULL\" : \"\"}`).run(said ? 1 : null, id, note)\n }\n\n /** Requests whose agent has not been told to pick them up yet. Each is\n * returned once. One statement: a mark the dashboard (another process)\n * writes meanwhile is either returned here or kept for the next read. */\n takePickups(): RequestRecord[] {\n return this.db.prepare(\"UPDATE requests SET pickup_at = NULL WHERE pickup_at IS NOT NULL RETURNING *\").all()\n .map(toRecord)\n .filter((r) => r.state === \"in_progress\")\n .sort((a, b) => a.createdAt - b.createdAt)\n }\n\n /** Delete closed requests older than `before`. Open ones never age out. */\n pruneClosed(before: number): number {\n return this.db.prepare(\n `DELETE FROM requests WHERE state IN (${placeholders(CLOSED_STATES.length)}) AND closed_at < ?`,\n ).run(...CLOSED_STATES, before).changes\n }\n}\n","import type { AgentXEvents } from \"@/events/bus\"\nimport { OPEN_STATES, type OpenState, type RequestStore } from \"./store\"\n\n// --- Follow a person's request from the turn that received it (#356) ---\n//\n// Fed by signals the daemon already has; no model call, no waiting:\n// - a turn from a person starts → a candidate, linked to its run\n// - that turn ends cleanly, nothing left → the candidate is deleted\n// - the turn started a delegation → open, in progress\n// - a linked run fails or times out → needs attention\n// - a delegation fails, times out, is lost → needs attention\n// - a restart cut a linked run → resumed: in progress;\n// not resumed: needs attention\n//\n// Every entry point swallows its own errors: following requests must never\n// break the run it is following.\n\nexport interface RequestSettings {\n enabled: boolean\n /** Channels to capture on. Empty: every channel a person writes on. */\n channels: string[]\n /** Who counts as the owner on channels other people can reach, as\n * \"channel:id\": the login on a forge, the sender id elsewhere. */\n from: string[]\n staleAfterHours: number\n retentionDays: number\n}\n\n/** Channel of the turn that hands a request back to its agent. */\nexport const PICKUP_CHANNEL = \"requests\"\n\n/** Marks the context of the pick-up turn the daemon starts. A symbol key\n * cannot arrive in a JSON body, so a caller of /task cannot set it. */\nconst PICKUP_MARK = Symbol(\"requests.pickup\")\n\n/** The context of the turn that hands request `requestId` back. */\nexport function pickupContext(requestId: string): { channel: string; chatId: string; sender: string } {\n return Object.assign({ channel: PICKUP_CHANNEL, chatId: requestId, sender: \"operator\" }, { [PICKUP_MARK]: true })\n}\n\n/** Did the daemon itself start this turn as a pick-up? */\nexport function isPickup(ctx: object | undefined | null): boolean {\n return !!ctx && (ctx as Record<symbol, unknown>)[PICKUP_MARK] === true\n}\n\n/** This node's own surfaces: only its operator can reach them. */\nexport const OPERATOR_CHANNELS: ReadonlySet<string> = new Set([\"voice\", \"app\", \"dashboard\", \"webrtc\"])\n\n/** Senders the daemon itself uses on those surfaces. Their turns are not\n * something the owner asked for (a camera frame, say). */\nconst DAEMON_SENDERS: ReadonlySet<string> = new Set([\"camera\"])\n\nconst base = (channel: string) => channel.toLowerCase().split(\"@\")[0]\n\n/** Channels whose sender `id` is the thread, not the person. */\nconst FORGES: ReadonlySet<string> = new Set([\"github\", \"gitlab\"])\n\n/** One comparable form. WhatsApp numbers compare on digits, so\n * \"+216 20 123 456\" and \"21620123456@s.whatsapp.net\" are the same. */\nconst comparable = (ch: string, v: string) =>\n ch === \"whatsapp\" ? v.replace(/@.*$/, \"\").replace(/\\D/g, \"\") : v.trim().toLowerCase().replace(/^@/, \"\")\n\n/** Does a turn on this channel from this sender count as the owner's? */\nexport function isOwnerTurn(\n settings: Pick<RequestSettings, \"channels\" | \"from\">,\n channel: string,\n sender: { name?: string; id?: string; username?: string } | undefined,\n /** The daemon marked the turn as the owner's (requests/operator). A\n * caller of /task can name any channel, so the name alone is not\n * enough on this node's own surfaces (#393). */\n operator = false,\n): boolean {\n const ch = base(channel)\n if (settings.channels.length && !settings.channels.map(base).includes(ch)) return false\n if (OPERATOR_CHANNELS.has(ch)) return operator && !DAEMON_SENDERS.has((sender?.name ?? \"\").toLowerCase())\n // Display names are not matched: anyone can pick one. Entries are\n // \"channel:id\": an id means nothing outside its own channel. One field\n // names the person: the login on a forge, the sender id elsewhere. A\n // username is never matched beside an id: it is the person's to choose,\n // so it could be set to someone else's id.\n const raw = FORGES.has(ch) ? sender?.username : sender?.id\n const who = raw ? comparable(ch, raw) : \"\"\n if (!who) return false\n return settings.from.some((entry) => {\n const e = entry.trim().toLowerCase()\n const i = e.indexOf(\":\")\n return i > 0 && e.slice(0, i) === ch && comparable(ch, e.slice(i + 1)) === who\n })\n}\n\n/** The sender fields of a task context, for the task:started event. */\nexport function senderOf(ctx: Record<string, unknown> | undefined | null): { name?: string; id?: string; username?: string } | undefined {\n if (!ctx) return undefined\n const pick = (v: unknown) => (typeof v === \"string\" && v ? v.slice(0, 120) : undefined)\n const sender = { name: pick(ctx.sender), id: pick(ctx.senderId), username: pick(ctx.senderUsername) }\n return sender.name || sender.id || sender.username ? sender : undefined\n}\n\n/** \"timed out\" for a timeout, \"failed\" otherwise. */\nfunction failureWord(error: string): string {\n return /\\btimed? ?out\\b|timeout/i.test(error) ? \"timed out\" : \"failed\"\n}\n\nconst clip = (s: string, n = 200) => {\n const flat = s.replace(/\\s+/g, \" \").trim()\n return flat.length > n ? `${flat.slice(0, n - 1)}…` : flat\n}\n\nconst chatKey = (agentId: string, channel: string, chatId: string) => `${agentId}\\u0000${channel}\\u0000${chatId}`\n\nexport interface DelegationSignal {\n id: string\n caller: string\n callee: string\n peer?: string\n origin: { channel: string; chatId: string }\n}\n\nexport class RequestTracker {\n constructor(\n private store: RequestStore,\n private settings: () => RequestSettings,\n private log: (msg: string) => void,\n private now: () => number = Date.now,\n ) {}\n\n /** Requests whose turn is running now, by agent and chat. A delegation\n * is linked through this: it can only start from a running turn. */\n private live = new Map<string, { requestId: string; runId: string }>()\n\n private guard(what: string, fn: () => void): void {\n try { fn() } catch (e: any) { this.log(`[requests] ${what} failed: ${e?.message ?? e}`) }\n }\n\n /** A turn from a person listed in `people` with the owner role (#384),\n * on a channel other people can reach. `from` keeps working beside it. */\n private isListedOwner(p: AgentXEvents[\"task:started\"]): boolean {\n const ch = base(p.channel)\n const channels = this.settings().channels\n if (channels.length && !channels.map(base).includes(ch)) return false\n return p.person?.role === \"owner\" && !OPERATOR_CHANNELS.has(ch)\n }\n\n /** The request of the turn this agent is running in this chat, if any. */\n liveRequestId(agentId: string, channel: string, chatId: string): string | null {\n return this.live.get(chatKey(agentId, channel, chatId))?.requestId ?? null\n }\n\n taskStarted(p: AgentXEvents[\"task:started\"]): void {\n if (!p.taskId) return\n this.guard(\"capture\", () => {\n if (!this.settings().enabled) return\n const now = this.now()\n // A run that continues one a restart cut off belongs to its request.\n const earlier = p.resumedFrom ? this.store.byLink(\"run\", p.resumedFrom) : null\n const key = chatKey(p.agentId, p.channel, p.chatId)\n if (earlier) {\n this.store.link(earlier.id, \"run\", p.taskId!, now)\n // A candidate stays one: if the resumed turn just answers and\n // ends, nothing is left to follow.\n if (earlier.state === \"candidate\") this.store.touch(earlier.id, now)\n else this.store.progress(earlier.id, now)\n this.live.set(key, { requestId: earlier.id, runId: p.taskId! })\n return\n }\n // The turn the daemon starts when the owner says \"pick it up again\".\n // Only the daemon's own: any caller of /task can name the channel.\n const pickup = p.pickup && p.channel === PICKUP_CHANNEL ? this.store.get(p.chatId) : null\n if (pickup && pickup.agentId === p.agentId) {\n this.store.link(pickup.id, \"run\", p.taskId!, now)\n this.store.touch(pickup.id, now)\n // Live like any other turn of the request: what it hands on or asks is linked.\n this.live.set(key, { requestId: pickup.id, runId: p.taskId! })\n return\n }\n if (!p.humanRoot || !(isOwnerTurn(this.settings(), p.channel, p.sender, p.operator === true) || this.isListedOwner(p))) return\n this.live.set(key, { requestId: `req-${p.taskId}`, runId: p.taskId! })\n this.store.addCandidate({\n id: `req-${p.taskId}`, runId: p.taskId!, channel: p.channel, chatId: p.chatId,\n sender: p.sender?.name ?? p.sender?.username ?? p.sender?.id ?? null,\n person: p.person?.id ?? null,\n agentId: p.agentId, text: p.requestText ?? p.fullMessage ?? p.messagePreview, now,\n })\n })\n }\n\n taskCompleted(p: AgentXEvents[\"task:completed\"]): void {\n if (!p.taskId) return\n this.guard(\"run end\", () => {\n const key = chatKey(p.agentId, p.channel, p.chatId)\n if (this.live.get(key)?.runId === p.taskId) this.live.delete(key)\n const req = this.store.byLink(\"run\", p.taskId!)\n if (!req) return\n const now = this.now()\n // Cut by a shutdown: the next boot decides (resumeOutcome).\n if (p.interrupted) return\n // Stopped on purpose is not a failure.\n if (p.error && p.errorKind !== \"cancelled\") {\n const word = failureWord(p.error)\n if (this.store.needsAttention(req.id, `${p.agentId} ${word}: ${clip(p.error)}`, now)) {\n this.log(`[requests] ${req.id} needs attention: run ${p.taskId} ${word}`)\n }\n return\n }\n if (req.state === \"candidate\") this.store.discardCandidate(req.id)\n else this.store.touch(req.id, now)\n })\n }\n\n delegationStarted(d: DelegationSignal): void {\n this.guard(\"delegation start\", () => {\n if (!this.settings().enabled) return\n const turn = this.live.get(chatKey(d.caller, d.origin.channel, d.origin.chatId))\n if (!turn) return\n const now = this.now()\n this.store.link(turn.requestId, \"delegation\", d.id, now)\n this.store.progress(turn.requestId, now, \"waiting_other\")\n })\n }\n\n delegationDone(d: DelegationSignal, status: \"done\" | \"error\" | \"timeout\" | \"lost\", text: string): void {\n this.guard(\"delegation end\", () => {\n const req = this.store.byLink(\"delegation\", d.id)\n if (!req) return\n const now = this.now()\n const who = d.peer ? `${d.callee} on ${d.peer}` : d.callee\n if (status === \"done\") {\n // The answer is back with the asking agent. Still open: closing is explicit.\n this.store.progress(req.id, now)\n return\n }\n const what = { error: \"failed\", timeout: \"did not answer in time\", lost: \"was lost in a restart\" }[status]\n if (this.store.needsAttention(req.id, `The work handed to ${who} ${what}: ${clip(text)}`, now)) {\n this.log(`[requests] ${req.id} needs attention: delegation ${d.id} ${status}`)\n }\n })\n }\n\n /** The agent raised a decision card from the turn of a request: the\n * request now waits on the owner, with the card's question. The card's\n * own reminders (inbox, Mac card, check-ins, digest) do the reminding.\n * `turn` is the running turn the call proved, never the chat the card\n * names: anyone can write another agent's id and chat on a card. */\n cardRaised(card: { id: string; raised_by: string; ask: string }, turn: { channel: string; chatId: string } | null): void {\n if (!turn) return\n this.guard(\"card\", () => {\n if (!this.settings().enabled) return\n const requestId = this.live.get(chatKey(card.raised_by, turn.channel, turn.chatId))?.requestId\n if (!requestId) return\n const now = this.now()\n this.store.link(requestId, \"card\", card.id, now)\n this.store.waitOnOwner(requestId, card.ask, now)\n })\n }\n\n /** A linked card was answered or expired. An answer lets the work go\n * on. An expiry means the answer never came (no card approves itself,\n * #741): the request needs attention, whatever an earlier\n * card's answer moved it to. It does not close. */\n cardResolved(card: { id: string; status: string; ask: string; if_silent?: string; outcome?: string }): void {\n this.guard(\"card result\", () => {\n const req = this.store.byLink(\"card\", card.id)\n if (!req) return\n const now = this.now()\n const applied = card.outcome ?? card.if_silent\n if (card.status === \"decided\") {\n if (req.state === \"waiting_owner\") this.store.progress(req.id, now)\n } else if (this.store.needsAttention(req.id, `Your answer did not come before the card expired (${clip(card.ask)}); \"${applied}\" was applied`, now)) {\n this.log(`[requests] ${req.id} needs attention: card ${card.id} expired unanswered`)\n }\n })\n }\n\n /** Added to the turn that brings a delegated answer back: the request\n * stays on the owner's list until the agent closes it. */\n closingNote(delegationId: string): string | undefined {\n try {\n const req = this.store.byLink(\"delegation\", delegationId)\n if (!req || !OPEN_STATES.includes(req.state as OpenState)) return undefined\n return `This is part of the owner's open request ${req.id}. It stays on their list until it is closed. If it is finished now, close it with agentx_request: {action:\"done\", id:\"${req.id}\", evidence:\"<link to the proof>\"}. If you will not do it, use decline with the reason. If work goes on, leave it open.`\n } catch {\n return undefined\n }\n }\n\n /** What the boot-time resume step did with a run the restart cut off.\n * \"already-claimed\" is left alone: the process that claimed it decides. */\n resumeOutcome(o: { taskId: string; decision: string; reason: string }): void {\n this.guard(\"restart\", () => {\n const req = this.store.byLink(\"run\", o.taskId)\n if (!req) return\n const now = this.now()\n if (o.decision === \"resumed\") {\n if (req.state === \"candidate\") this.store.touch(req.id, now)\n else this.store.progress(req.id, now)\n } else if (o.decision === \"reported\" || o.decision === \"resume-failed\" || o.decision === \"skipped\") {\n if (this.store.needsAttention(req.id, `Cut off by a restart and not picked up again (${clip(o.reason)})`, now)) {\n this.log(`[requests] ${req.id} needs attention: run ${o.taskId} cut off by a restart`)\n }\n }\n })\n }\n}\n","import { randomBytes, timingSafeEqual } from \"crypto\"\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\"\nimport { networkInterfaces } from \"os\"\nimport { dirname, resolve } from \"path\"\n\n// --- Proof that a turn on one of this node's own surfaces is the owner's (#393) ---\n//\n// The voice widget, the phone app and the dashboard are the owner's\n// surfaces: a turn there is a request of theirs. But `POST /task` takes\n// the caller's `context` as it is, so any local caller, an agent included,\n// could name `channel: \"dashboard\"` and be recorded as the owner.\n//\n// A turn counts as the owner's on those surfaces only when the daemon\n// marked it. The daemon marks the turns it starts itself (/ask, the\n// dashboard's assistant) and a /task whose caller showed the operator key:\n// a secret in `.agentx/operator.key`, readable by this user only, that the\n// dashboard process presents for the phone app. The mark is a symbol, so it\n// cannot arrive in a JSON body; a bare `context.channel` proves nothing.\n\n/** The header a /task caller presents the operator key in. */\nexport const OPERATOR_HEADER = \"x-agentx-operator\"\n\nconst OPERATOR_MARK = Symbol(\"requests.operator\")\n\n/** Marks `ctx` as a turn the owner started on one of this node's own\n * surfaces. Only the daemon calls this. */\nexport function operatorContext<T extends object>(ctx: T): T {\n return Object.assign(ctx, { [OPERATOR_MARK]: true })\n}\n\n/** Did the daemon mark this turn as the owner's? */\nexport function isOperatorTurn(ctx: object | undefined | null): boolean {\n return !!ctx && (ctx as Record<symbol, unknown>)[OPERATOR_MARK] === true\n}\n\n/** Where this install keeps its operator key. */\nexport function operatorKeyPath(root: string = process.cwd()): string {\n return resolve(root, \".agentx\", \"operator.key\")\n}\n\n/** The operator key of this install: created on first use when `create`\n * is set, null when there is none. The file is readable by this user only. */\nexport function loadOperatorKey(root: string = process.cwd(), opts: { create?: boolean } = {}): string | null {\n const path = operatorKeyPath(root)\n try {\n if (existsSync(path)) {\n const key = readFileSync(path, \"utf-8\").trim()\n if (key) return key\n }\n if (!opts.create) return null\n const key = randomBytes(32).toString(\"hex\")\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, key + \"\\n\", { mode: 0o600 })\n chmodSync(path, 0o600)\n return key\n } catch {\n return null\n }\n}\n\n/** Does what the caller presented match this node's key? A missing key\n * on either side never matches. */\nexport function operatorKeyMatches(key: string | null | undefined, presented: string | string[] | undefined): boolean {\n const p = Array.isArray(presented) ? presented[0] : presented\n if (!key || !p) return false\n const a = Buffer.from(key)\n const b = Buffer.from(p.trim())\n return a.length === b.length && timingSafeEqual(a, b)\n}\n\n// ── A mesh peer vouching for the owner (#407) ────────────────────────────\n//\n// The phone app paired with node A talks to an agent on node B through\n// node A's /mesh/task. Node A's operator key means nothing to node B, so\n// node A, having checked its own key, tells node B the turn is the\n// owner's. Node B believes that only from a request that carries one of\n// its peers' tokens and comes from another machine: never from this\n// machine, the dashboard token or a bare header. An agent on node A that\n// calls /mesh/task without the key gets no vouching, so the rule of #393\n// holds on node A. On node B the mesh token is in every agent's\n// environment, so a caller on node B itself is never believed, whichever\n// of this machine's addresses it dials.\n\n/** The header a forwarding node sets after checking its own operator key. */\nexport const OPERATOR_VOUCH_HEADER = \"x-agentx-operator-vouch\"\n\n/** Does this /mesh/task call come from the owner's surface with the key?\n * True only for a context on one of this node's own surfaces whose\n * caller showed this node's operator key. */\nexport function operatorVouch(\n context: unknown,\n headers: Record<string, string | string[] | undefined>,\n key: string | null | undefined,\n operatorChannels: ReadonlySet<string>,\n): boolean {\n if (!context || typeof context !== \"object\") return false\n const channel = String((context as Record<string, unknown>).channel ?? \"\").toLowerCase().split(\"@\")[0]\n if (!operatorChannels.has(channel)) return false\n return operatorKeyMatches(key, headers[OPERATOR_HEADER])\n}\n\n/** Does a peer vouch for the owner on this request? The vouch header must\n * be set and the bearer must be one of this node's peer tokens. */\nexport function peerVouches(\n headers: Record<string, string | string[] | undefined>,\n peerTokens: Iterable<string>,\n): boolean {\n const v = headers[OPERATOR_VOUCH_HEADER]\n if ((Array.isArray(v) ? v[0] : v) !== \"1\") return false\n const auth = headers.authorization\n const h = (Array.isArray(auth) ? auth[0] : auth) || \"\"\n const bearer = h.toLowerCase().startsWith(\"bearer \") ? h.slice(7).trim() : \"\"\n if (!bearer) return false\n const b = Buffer.from(bearer)\n for (const t of peerTokens) {\n if (!t) continue\n const a = Buffer.from(t)\n if (a.length === b.length && timingSafeEqual(a, b)) return true\n }\n return false\n}\n\n/** Is this the address of a caller on this machine? Loopback, or one of\n * this machine's own addresses: a local caller can dial those too. */\nexport function isThisMachine(\n remoteAddress: string | undefined,\n own: Iterable<string> = ownAddresses(),\n): boolean {\n const addr = plainAddress(remoteAddress ?? \"\")\n // No address to judge: treated as local, the side that believes nothing.\n if (!addr || addr === \"::1\" || addr.startsWith(\"127.\")) return true\n for (const a of own) if (plainAddress(a) === addr) return true\n return false\n}\n\nconst plainAddress = (a: string) => a.toLowerCase().replace(/^::ffff:/, \"\").replace(/%.*$/, \"\")\n\nfunction ownAddresses(): string[] {\n return Object.values(networkInterfaces()).flatMap((list) => (list ?? []).map((i) => i.address))\n}\n\n/** The whole rule of `POST /task`: is this turn the owner's? On one of\n * this node's own surfaces, and either the caller showed this node's\n * operator key, or a peer on another machine vouches with one of this\n * node's peer tokens. */\nexport function ownerProven(\n context: unknown,\n req: { headers: Record<string, string | string[] | undefined>; remoteAddress?: string },\n node: { key: string | null | undefined; peerTokens: Iterable<string>; operatorChannels: ReadonlySet<string>; ownAddresses?: Iterable<string> },\n): boolean {\n if (operatorVouch(context, req.headers, node.key, node.operatorChannels)) return true\n if (!context || typeof context !== \"object\") return false\n const channel = String((context as Record<string, unknown>).channel ?? \"\").toLowerCase().split(\"@\")[0]\n if (!node.operatorChannels.has(channel)) return false\n if (isThisMachine(req.remoteAddress, node.ownAddresses)) return false\n return peerVouches(req.headers, node.peerTokens)\n}\n","import { classifyInitiator, propagatedRootOf, type InitiatorContext } from \"@/a2a/initiator\"\nimport { OPERATOR_CHANNELS } from \"@/requests/tracker\"\nimport { isOperatorTurn } from \"@/requests/operator\"\n\n// --- People: one identity per human, whatever channel they use (#384) ---\n//\n// `people` in agentx.json lists the humans who talk to the agents. Each has\n// channel identities written \"channel:id\" (\"gitlab:sara\", \"whatsapp:21620123456\").\n// A turn a person starts is stamped with that person's id; the id travels\n// with the root initiator across agents and mesh peers.\n//\n// The rules:\n// - Only platform ids and logins are matched. Display names never are:\n// anyone can pick one.\n// - A sender that matches nobody is unknown (null). Never guessed.\n// - This machine's own surfaces (voice, app, dashboard) are the owner.\n// With no owner listed that is the built-in person \"owner\", so an\n// install with no `people` needs nothing configured.\n// - Turns software starts (cron, workflows, agent comments) have no person.\n//\n// Roles (#453): `owner` runs this install; `member` is a teammate; `client`\n// is someone the owner does work for, who gets a page of their own\n// (/member shows \"Your project\" instead of \"My work\"); `guest` is the\n// operator of another organisation's mesh. Only `owner` changes what a\n// turn may do; the other three are told apart on the pages they see.\n\nexport type PersonRole = \"owner\" | \"member\" | \"client\" | \"guest\"\nexport const PERSON_ROLES: readonly PersonRole[] = [\"owner\", \"member\", \"client\", \"guest\"]\n\nexport interface Person {\n id: string\n name: string\n /** How their name is said aloud (#433); see voice.pronunciations. */\n say?: string\n role: PersonRole\n /** \"channel:id\" entries: a login, a Telegram id, a WhatsApp number. */\n identities: string[]\n /** The agents this person may reach. Empty: every agent (#379). */\n agents?: string[]\n /** Tools and skills this person's turns may not use (#379). */\n deny?: PersonDeny\n}\n\n/** One list per level of control. A further level is a new key. */\nexport interface PersonDeny {\n tools?: string[]\n skills?: string[]\n}\n\n/** The person a turn is stamped with. `role` is set only on a turn this\n * node resolved itself; an id carried by a root has none. */\nexport interface PersonRef {\n id: string\n role?: PersonRole\n}\n\n/** The owner's id on an install that lists no owner. */\nexport const IMPLICIT_OWNER = \"owner\"\n\n/** The name shown for a stored person id: the listed name, \"Owner\" for the\n * built-in owner, else the id (a person removed from the list since). */\nexport function personName(people: Person[], id: string): string {\n return people.find((p) => p.id === id)?.name ?? (id === IMPLICIT_OWNER ? \"Owner\" : id)\n}\n\nconst base = (channel: string) => channel.toLowerCase().split(\"@\")[0]\n\n/** One comparable form per channel. WhatsApp numbers compare on digits, so\n * \"+216 20 123 456\" and \"21620123456@s.whatsapp.net\" are the same. */\nfunction normalize(channel: string, value: string): string {\n const v = value.trim().toLowerCase()\n return channel === \"whatsapp\" ? v.replace(/@.*$/, \"\").replace(/\\D/g, \"\") : v.replace(/^@/, \"\")\n}\n\nexport function splitIdentity(identity: string): { channel: string; value: string } | null {\n const i = identity.indexOf(\":\")\n if (i <= 0 || i === identity.length - 1) return null\n const channel = base(identity.slice(0, i).trim())\n const value = normalize(channel, identity.slice(i + 1))\n return value ? { channel, value } : null\n}\n\n/** What is wrong with a `people` list, or null. */\nexport function peopleProblem(people: Person[]): string | null {\n const ids = new Set<string>()\n const claimed = new Map<string, string>()\n for (const p of people) {\n if (ids.has(p.id)) return `person \"${p.id}\" is listed twice`\n ids.add(p.id)\n if (p.id === IMPLICIT_OWNER && p.role !== \"owner\") return `the id \"${IMPLICIT_OWNER}\" is kept for a person with the owner role`\n for (const identity of p.identities) {\n const parts = splitIdentity(identity)\n if (!parts) return `identity \"${identity}\" of \"${p.id}\" must be written channel:id`\n const key = `${parts.channel}:${parts.value}`\n const other = claimed.get(key)\n if (other && other !== p.id) return `identity \"${identity}\" belongs to both \"${other}\" and \"${p.id}\"`\n claimed.set(key, p.id)\n }\n }\n return null\n}\n\n/** The person behind a sender on a channel, or null when nobody matches. */\nexport function resolvePerson(\n people: Person[],\n channel: string,\n sender: { id?: string; username?: string } | undefined,\n): Person | null {\n const ch = base(channel)\n // On WhatsApp `username` is the chat's number, which for a message the\n // account owner sends is the other person. Only `id` is the sender.\n const given = ch === \"whatsapp\" ? [sender?.id] : [sender?.id, sender?.username]\n const ids = given.filter((v): v is string => typeof v === \"string\" && !!v).map((v) => normalize(ch, v)).filter(Boolean)\n if (!ids.length) return null\n return people.find((p) => p.identities.some((identity) => {\n const parts = splitIdentity(identity)\n return !!parts && parts.channel === ch && ids.includes(parts.value)\n })) ?? null\n}\n\n/** Who is at this machine's own surfaces. Two listed owners cannot be told\n * apart there, so the answer is nobody. */\nexport function operatorPerson(people: Person[]): PersonRef | null {\n const owners = people.filter((p) => p.role === \"owner\")\n if (owners.length === 0) return { id: IMPLICIT_OWNER, role: \"owner\" }\n return owners.length === 1 ? { id: owners[0].id, role: \"owner\" } : null\n}\n\n/** The person who started the chain this turn belongs to. A delegated hop\n * takes the id from the root it carries, with no role: a root is not\n * authenticated (a caller of the daemon's API or a mesh peer writes it), and\n * ids are per machine. A root turn is resolved here from the channel's own\n * sender fields. `context.person` is never read, for the same reason. */\nexport function personOfTurn(people: Person[], ctx: InitiatorContext | undefined | null): PersonRef | null {\n const root = propagatedRootOf(ctx)\n if (root) return root.person ? { id: root.person } : null\n if (!ctx || classifyInitiator(ctx) !== \"human\") return null\n const channel = String(ctx.channel ?? \"\")\n // This node's own surfaces name the operator only when the daemon marked\n // the turn as theirs (requests/operator, #393): a /task caller can name\n // the channel, not the person.\n if (OPERATOR_CHANNELS.has(base(channel))) return isOperatorTurn(ctx) ? operatorPerson(people) : null\n const str = (v: unknown) => (typeof v === \"string\" ? v : undefined)\n const person = resolvePerson(people, channel, { id: str(ctx.senderId), username: str(ctx.senderUsername) })\n return person ? { id: person.id, role: person.role } : null\n}\n\n// ── Permissions, first slice: per agent (#379) ───────────────────────────\n//\n// A listed person may be limited to named agents. The default is open:\n// a person with no list reaches every agent, and an unknown sender is\n// not limited here (channels decide who they answer at all). The limit\n// follows the person through a delegation too: work they started cannot\n// reach an agent they may not talk to. An owner is never limited: their\n// own surfaces (voice, app, dashboard) must keep reaching every agent.\n\n/** May this person reach `agentId`? */\nexport function agentAllowed(person: Pick<Person, \"agents\" | \"role\">, agentId: string): boolean {\n const list = person.agents ?? []\n return person.role === \"owner\" || list.length === 0 || list.includes(agentId)\n}\n\n/** Who a refused turn belongs to and the note it is answered with, or null\n * when the turn may run. The note names what the person can reach instead,\n * so the refusal is not a dead end. */\nexport function refusedPerson(people: Person[], agentId: string, ctx: InitiatorContext | undefined | null): { person: Person; note: string } | null {\n const ref = personOfTurn(people, ctx)\n if (!ref) return null\n const person = people.find((p) => p.id === ref.id)\n if (!person || agentAllowed(person, agentId)) return null\n const list = (person.agents ?? []).join(\", \")\n return { person, note: `${person.name}, you can reach ${list} here, not ${agentId}. Ask the owner if you need ${agentId}.` }\n}\n\n/** The note alone. */\nexport function personRefusal(people: Person[], agentId: string, ctx: InitiatorContext | undefined | null): string | null {\n return refusedPerson(people, agentId, ctx)?.note ?? null\n}\n\n// ── Permissions: per tool and per skill (#379) ───────────────────────────\n//\n// A listed person may be denied named tools and skills. Same rules as the\n// per-agent limit: open by default, never an owner, and it follows the\n// person through a delegation (the root's person id). Unlike a refused\n// agent, the turn still runs: the limit is a hard one, enforced on every\n// tool call by a per-run hook (guard/person-limits), not an instruction.\n\n/** The limits one run carries to the hook. */\nexport interface PersonLimits {\n personId: string\n name: string\n tools: string[]\n skills: string[]\n}\n\n/** Case-insensitive name match; `*` stands for any run of characters. */\nexport function nameMatches(pattern: string, name: string): boolean {\n const re = pattern.trim().split(\"*\").map((s) => s.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\".*\")\n return new RegExp(`^${re}$`, \"i\").test(name.trim())\n}\n\n/** The limits a turn runs under, or null when it has none. */\nexport function personLimitsOf(people: Person[], ctx: InitiatorContext | undefined | null): PersonLimits | null {\n const ref = personOfTurn(people, ctx)\n if (!ref) return null\n const person = people.find((p) => p.id === ref.id)\n if (!person || person.role === \"owner\") return null\n const tools = person.deny?.tools ?? []\n const skills = person.deny?.skills ?? []\n if (!tools.length && !skills.length) return null\n return { personId: person.id, name: person.name, tools: [...tools], skills: [...skills] }\n}\n","/** Every tool the agentx MCP server (src/mcp/index.ts) defines, by name.\n * Kept apart from that file so config validation can check a name\n * without loading the server. test/session-profile-agentx-tools.test.ts\n * fails when the two drift. */\nexport const AGENTX_TOOL_NAMES: readonly string[] = [\n \"agentx_generate\",\n \"agentx_inspect\",\n \"agentx_skill_match\",\n \"agentx_detect_output_type\",\n \"agentx_channel_reply\",\n \"agentx_channel_label\",\n \"agentx_send\",\n \"agentx_send_agent\",\n \"agentx_recent\",\n \"agentx_events\",\n \"agentx_send_contact\",\n \"agentx_task\",\n \"agentx_agents\",\n \"agentx_voice_queue\",\n \"agentx_call_owner\",\n \"agentx_camera_ask\",\n \"agentx_camera_look\",\n \"agentx_health\",\n \"agentx_attach_next\",\n \"agentx_attach_answer\",\n \"agentx_crons\",\n \"agentx_schedule\",\n \"agentx_approval\",\n \"agentx_request\",\n \"agentx_debug\",\n \"agentx_wiki_query\",\n \"agentx_wiki_patch\",\n \"agentx_wiki_interview\",\n \"agentx_graph_review\",\n]\n\n/** The names in `list` that are not agentx tools. */\nexport function unknownAgentxTools(list: readonly string[]): string[] {\n const known = new Set(AGENTX_TOOL_NAMES)\n return list.map((t) => t.trim()).filter((t) => t && !known.has(t))\n}\n","import { z } from \"zod\"\nimport { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { businessConfigSchema } from \"@/business/config\"\nimport { boardsConfigSchema, dashboardConfigSchema } from \"@/boards/config\"\nimport { autonomyLevelSchema } from \"@/guard/autonomy\"\nimport { DEFAULT_HOTKEYS, hotkeyError } from \"@/voice/hotkey\"\nimport { DEFAULT_PALETTE, ORB_PALETTE_IDS, VOICE_ANIMATIONS, VOICE_LOOKS } from \"@/voice/orb-palettes\"\nimport { SPOKEN_MAX_CHARS } from \"@/voice/speakable\"\nimport { NOISE_MARKERS } from \"@/voice/noise\"\nimport { whatsappTriageSchema } from \"@/whatsapp-triage/config\"\nimport { peopleProblem } from \"@/people/people\"\nimport { unknownAgentxTools } from \"@/mcp/tool-names\"\n\n/**\n * Load .env file into process.env (simple, no dependency).\n */\nfunction loadDotEnv(dir: string): void {\n const envPath = resolve(dir, \".env\")\n if (!existsSync(envPath)) return\n\n const content = readFileSync(envPath, \"utf-8\")\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim()\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n}\n\n// --- Daemon configuration schema & loader ---\n\n/** A list of agentx MCP tool names (session.lean.agentxTools). An unknown\n * name is an error, not ignored: a misspelt `agentx_channel_reply` would\n * otherwise leave a lean GitHub session with no way to reply. */\nconst agentxToolListSchema = z.array(z.string().min(1)).superRefine((list, ctx) => {\n const unknown = unknownAgentxTools(list)\n if (unknown.length) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `not an agentx tool: ${unknown.join(\", \")}. Use the names the agentx server lists, without \"mcp__agentx__\".`,\n })\n }\n})\n\nconst providerConfigSchema = z.object({\n apiKey: z.string().optional(),\n defaultModel: z.string().optional(),\n baseUrl: z.string().optional(),\n /** Toggle thinking-mode output on backends that support it (DeepSeek\n * V4 `reasoning_content`). When `true` (the DeepSeek default), the\n * agent streams its reasoning live in the dashboard task modal and\n * the agentic round-trip echoes it back on the next iteration. Set\n * to `false` to disable for cost/latency-sensitive agents. Ignored\n * on backends that don't speak the thinking-mode protocol. */\n thinking: z.boolean().optional(),\n})\n\nconst mcpStdioServerSchema = z.object({\n type: z.literal(\"stdio\").optional(),\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).optional(),\n})\n\nconst mcpHttpServerSchema = z.object({\n type: z.literal(\"http\"),\n url: z.string().url(),\n headers: z.record(z.string(), z.string()).optional(),\n})\n\n// HTTP variant first so a record with `type: \"http\"` matches the HTTP\n// shape; stdio is the default fallback when `type` is absent (keeps the\n// original `{ command, args, env }` configs valid without a migration).\nconst mcpServerSchema = z.union([mcpHttpServerSchema, mcpStdioServerSchema])\n\n/**\n * Per-agent integration registration. Declares which third-party services\n * an agent has credentials for — e.g. \"@cx-bot has a Telegram bot token,\n * a HubSpot private app token, and a Gmail account\". The actual secret is\n * NEVER stored in agentx.json: only an env-var reference (`tokenEnv`) or a\n * keyring directive. Skills, channel adapters, and the action layer read\n * from this registry to discover what an agent can use.\n *\n * The `kind` field is open-string (well-known values listed below in\n * INTEGRATION_KINDS) so new services can be declared without a schema\n * bump. Doctor warns on unknown kinds.\n */\nconst integrationCredentialsSchema = z.object({\n /** Env var holding the secret. Uppercase identifier; daemon resolves at use-time. */\n tokenEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n /** Alternate env-var keys for credential schemes that need >1 secret\n * (e.g. HubSpot private app + portal id, OAuth refresh tokens). */\n privateAppTokenEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n apiKeyEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n refreshTokenEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n clientIdEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n clientSecretEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n /** OS keyring directive — credential lives in macOS Keychain / Linux\n * Secret Service / Windows Credential Manager. The skill that reads\n * this integration is responsible for resolving the keyring entry\n * (typically keyed by agentId+kind). */\n auth: z.literal(\"keyring\").optional(),\n /** File-based session directory (WhatsApp, Telegram MTProto, etc.). */\n sessionDir: z.string().optional(),\n}).strict()\n\nconst integrationSchema = z.object({\n /** Service identifier — open string, well-known values in INTEGRATION_KINDS. */\n kind: z.string().min(1),\n /** Human-readable label (e.g. \"@cx_bot\", \"Acme CRM\", \"alex@example.com\"). Unique per (agent, kind). */\n label: z.string().min(1),\n credentials: integrationCredentialsSchema.default({}),\n /** Non-secret metadata — username, email, host URL, portal id, JID, etc.\n * Visible in the dashboard and to skills. */\n metadata: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}),\n /** Operator-set toggle without removing the entry. */\n enabled: z.boolean().default(true),\n})\n\nexport type Integration = z.infer<typeof integrationSchema>\n\n/**\n * Canonical kind list — for autocomplete + doctor validation. Open string\n * schema so new services drop in without a bump; doctor warns when a kind\n * is outside this list.\n */\nexport const INTEGRATION_KINDS = [\n // Inbound channels\n \"telegram-bot\",\n \"whatsapp\",\n // Code platforms (user-scoped tokens, not channel routing)\n \"gitlab-user\",\n \"github-user\",\n // Email\n \"gmail\",\n \"hotmail\",\n \"smtp\",\n // CRM / ERP\n \"hubspot\",\n \"salesforce\",\n \"pipedrive\",\n \"odoo\",\n // Project management\n \"trello\",\n \"linear\",\n \"asana\",\n \"jira\",\n \"notion\",\n // Payments\n \"stripe\",\n // Observability / hosting\n \"sentry\",\n \"vercel\",\n // Voice / SMS\n \"twilio\",\n // Catch-all\n \"custom\",\n] as const\n\nconst voiceProviderSchema = z.enum([\"system\", \"elevenlabs\"])\n\n/** A macOS voice: a name (\"Daniel\", \"Ava (Premium)\") or identifier;\n * \"system\" for the OS default voice (the only way to get a Siri voice);\n * or one per language, picked by the language of each line:\n * { \"en\": \"Samantha\", \"fr\": \"Thomas\", \"ar\": \"Majed\" }. */\nconst systemVoiceSchema = z.union([z.string(), z.record(z.string(), z.string())])\n\n/** A keyboard shortcut for AgentX Voice, e.g. \"opt+space\" or\n * \"ctrl+opt+1\". See src/voice/hotkey.ts. */\nconst hotkeySchema = z.string().superRefine((s, ctx) => {\n const error = hotkeyError(s)\n if (error) ctx.addIssue({ code: z.ZodIssueCode.custom, message: `shortcut ${error}` })\n})\n\nconst voiceSchema = z.object({\n /** Overrides the global voice.provider for this agent. */\n provider: voiceProviderSchema.optional(),\n /** See systemVoiceSchema. Unset: one is assigned, of the agent's\n * gender when set, different from every other agent's. */\n system: systemVoiceSchema.optional(),\n /** Voices to try, in order, when `system` names one that is not\n * installed (e.g. [\"Ava (Premium)\", \"Allison\"]). Unset, or none\n * installed: the best installed voice of its language and gender. */\n fallbacks: z.array(z.string().min(1)).optional(),\n elevenlabsVoiceId: z.string().optional(),\n gender: z.enum([\"female\", \"male\", \"neutral\"]).optional(),\n /** A few words on manner, e.g. \"warm, upbeat, a little playful\". */\n style: z.string().optional(),\n /** The one-line self-introduction used on first contact. */\n intro: z.string().optional(),\n /** Speak short updates from this agent's real tool steps while it\n * works: \"on\" for everything but cron, \"all\" to include cron. */\n narrate: z.enum([\"off\", \"on\", \"all\"]).optional(),\n /** Speaking speed, 1 = normal. System voices: 175 words a minute times\n * this. ElevenLabs accepts 0.7 to 1.2 and is held to that range. */\n rate: z.number().min(0.75).max(1.5).optional(),\n /** Place in the speaking queue: \"high\" lines go ahead of waiting\n * \"normal\" ones, \"low\" lines after them. Unset: normal. */\n priority: z.enum([\"high\", \"normal\", \"low\"]).optional(),\n /** AgentX Voice: hold this shortcut and speak to ask this agent, without\n * changing the agent picked in the menu. */\n hotkey: hotkeySchema.optional(),\n})\n\n/** One event subscription (src/events/subscriptions.ts). An envelope\n * matches when its kind or type is in `kinds` and every other set filter\n * agrees. Every subscription is readable with `agentx_events`; `digest`\n * also briefs a fresh session and `wake` also starts a turn. */\nexport const eventSubscriptionSchema = z.object({\n /** Envelope `kind` (\"agent\", \"run\") or `type` (\"task:completed\"); \"*\" matches all. */\n kinds: z.array(z.string().min(1)).min(1),\n /** Only events published for these agent ids. */\n agents: z.array(z.string().min(1)).optional(),\n /** Only events published by these nodes (`node.name`). */\n nodes: z.array(z.string().min(1)).optional(),\n /** Only events whose summary contains this text (case-insensitive). */\n match: z.string().min(1).optional(),\n delivery: z.enum([\"pull\", \"digest\", \"wake\"]).default(\"pull\"),\n /** wake only: no wake when the agent was already woken this many times\n * in the past hour. */\n maxPerHour: z.number().int().min(1).max(60).default(4),\n})\n\nconst agentConfigSchema = z.object({\n name: z.string(),\n workspace: z.string(),\n tier: z.enum([\"claude-code\", \"codex-cli\", \"opencode\", \"sdk\", \"orchestrator\"]).default(\"claude-code\"),\n provider: z.string().optional(),\n model: z.string().optional(),\n systemPrompt: z.string().optional(),\n mentions: z.array(z.string()).default([]),\n /** Phase 5 — typed capabilities (drop-condition fallback). Free-form\n * list of intent strings this agent is allowed to handle. When set,\n * the org-chart `canHandle(agentId, project, intent)` check rejects\n * dispatches with intents not in this list. When empty/unset\n * (the default), the agent is permissive — handles any intent.\n * Intent matching is exact-string for now; a glob/prefix layer can\n * come later if it produces real rejections.\n * Example: [\"issue.opened\", \"issue.commented\", \"merge_request.opened\"]\n * Example: [\"cron.fired\", \"message.received\"] */\n intents: z.array(z.string()).default([]),\n /** Phase 8 — capability-bounded security. Max distinct upstream\n * agents in the delegation chain on the same (project, subject)\n * before a dispatch to THIS agent is refused. The ledger itself\n * provides the chain — `decideAndCommit` walks recent decisions on\n * the subject and counts distinct agents. Default 5; set to 0 to\n * disable for an agent that's always called as the bottom of a\n * chain. The check prevents cascade loops where A → B → A → B → ...\n * blows past sane chain depth. */\n maxDelegationDepth: z.number().int().min(0).max(50).default(5),\n /** MCP servers this agent's Claude Code session should load. Synced\n * to <workspace>/.mcp.json at daemon boot via agent-mcp.ts. Operator\n * edits to .mcp.json are respected (see SyncResult.skipped-operator-owned). */\n mcp: z.record(z.string(), mcpServerSchema).optional(),\n /** Enable CodeGraph (https://github.com/colbymchenry/codegraph) for this\n * agent's claude-code or codex-cli workspace. When true, agentx (a) adds\n * a codegraph MCP server to <workspace>/.mcp.json, (b) extends\n * .claude/settings.json permissions.allow with the codegraph_* tools,\n * (c) appends a CodeGraph instruction section to the managed CLAUDE.md,\n * and (d) background-indexes the workspace at daemon boot if .codegraph/\n * is missing. Off by default — flip on for coding agents (globex-v2-coder,\n * initech-v2-coder, umbrella-coding, etc.) where token-volume on exploration\n * loops is the bottleneck. */\n codegraph: z.boolean().default(false),\n /** Per-agent override for the global `session.contextStrategy`. Lets\n * one agent run `planner` (smaller upfront context, more tool-driven\n * exploration) while siblings stay on `layered`. Used for agents that\n * consistently bloat their cache via large workspace reads. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).optional(),\n /** When true, the registry resolves references-recipes for this agent's\n * workspace and renders a deterministic [Verified References] block at\n * priority 4.7. Off by default — flip on per agent (pm-initech, devops-agent,\n * etc.) once a `references/` registry exists in the agent's workspace\n * or repo root. See src/agents/references/. */\n contextReferences: z.boolean().default(false),\n /** Whether this agent may emit in-band `agentx:ui` rich-message directives\n * (buttons/polls/media) on chat channels. Default true — set false to make\n * the render layer strip any directive and send plain text only. */\n richMessages: z.boolean().default(true),\n maxConcurrent: z.number().default(1),\n /** Hard wall-clock cap on a single Claude Code invocation. Exceeding the\n * cap sends SIGTERM (exit 143). Default 20 min — bump for devops/coder\n * agents that do long investigations or multi-file refactors. */\n maxExecutionMinutes: z.number().int().min(1).max(240).default(20),\n /** Time a run may spend between taking a slot and starting its agent\n * process (request gate, classifier, compaction, context planning, model\n * routing, dispatch gates). Past it the run is aborted, its slot freed and\n * its record marked `timeout` with the step it was stuck in. Applies to\n * every run, whatever started it. */\n preSpawnTimeoutSec: z.number().int().min(10).max(3600).default(300),\n /** How long a daemon stop waits for this agent's runs before cutting\n * them off, when that is longer than shutdown.drainTimeoutSeconds. For\n * agents whose runs are long (renders, builds). */\n drainTimeoutSeconds: z.number().int().min(0).max(86_400).optional(),\n permissionMode: z.string().default(\"default\"),\n /** How this agent's `claude` CLI is billed (claude-code tier). Default\n * \"subscription\": the shared OAuth login, ANTHROPIC_API_KEY stripped.\n * \"api\": bill ANTHROPIC_API_KEY instead and drop the OAuth token, so\n * e.g. benchmark agents never draw on the fleet's subscription quota.\n * An \"api\" agent with no key fails its task rather than falling back. */\n billing: z.enum([\"subscription\", \"api\"]).default(\"subscription\"),\n /** Run GitHub coding tasks as Claude cloud sessions (`claude --cloud`)\n * instead of local runs (#622). Off by default. Needs\n * `channels.github.cloudSessions: true` too. Only a task from the GitHub\n * channel for an issue or pull request of a repository this node has a\n * checkout of (the project rule's `runbook` path, or the agent's\n * workspace) goes to the cloud; everything else runs locally. The result\n * is a pull request, not a reply. A launch that fails falls back to a\n * local run. See src/agents/cloud-sessions.ts. */\n cloudSessions: z.object({\n enabled: z.boolean().default(false),\n /** Launches per calendar day for this agent; 0 = no cap. */\n maxPerDay: z.number().int().min(0).default(0),\n /** Hours a launched session counts as open: no local run starts for\n * its issue, and comments on the issue are forwarded to it. */\n openHours: z.number().min(1).max(168).default(24),\n /** Seconds `claude --cloud` may take to print the session id before\n * the launch is given up and the task runs locally. */\n launchTimeoutSeconds: z.number().int().min(10).max(600).default(120),\n }).default({}),\n /** Improvement plan #3 — tool-use-required preset. When set,\n * AgentX inspects the stream-json events from each task and\n * fails the response with `tool_required_not_called: <name>`\n * when none of the listed tool names was invoked. Catches the\n * silent-degrade pattern observed when a model below the\n * capability bar (e.g. Haiku on a Write-required prompt)\n * produces a plausible-looking text response without ever\n * calling the required tool. Caller can retry with a stronger\n * model or sharpen the prompt.\n *\n * Tool names match the canonical Claude Code names (\"Write\",\n * \"Edit\", \"Bash\", \"Read\", …). Free-form so future tools work\n * without a schema bump. Empty/unset means no enforcement\n * (default — backward compatible). */\n toolUseRequired: z.array(z.string()).default([]),\n /** Per-agent override for the channel-level `autoReplyLegacy` on\n * gitlab/github. When set, it wins over `channels.<name>.autoReplyLegacy`\n * for this agent's replies: `true` makes the daemon auto-post the agent's\n * final text as a comment (right for coder agents that end their turn with\n * a summary and don't call `agentx_channel_reply`); `false` suppresses it\n * (right for chatty agents whose reasoning shouldn't land as a comment).\n * Unset → fall back to the channel default. */\n gitlabAutoReply: z.boolean().optional(),\n /** Opt into process reuse for claude-code (stream-json) or codex-cli\n * (app-server), or opencode (dedicated v2 server). Conversations have isolated processes. Unsupported Codex\n * versions fall back to exec before submitting any turn. Other tiers\n * ignore this flag. Codex loads the operator's Codex config, unlike exec's\n * --ignore-user-config path: review additional MCP servers before enabling.\n * Codex and OpenCode each use an 8-process cap and 5-minute idle eviction; processPool\n * settings apply to Claude only. Default false for gradual rollout.\n * See docs/architecture/persistent-codex-process.md and\n * docs/architecture/persistent-claude-process.md and\n * docs/architecture/persistent-opencode-process.md for details. */\n persistentProcess: z.boolean().default(false),\n queueMode: z.enum([\"collect\", \"followup\", \"drop\"]).default(\"collect\"),\n heartbeat: z.object({\n enabled: z.boolean().default(false),\n intervalMinutes: z.number().default(30),\n prompt: z.string().default(\"Check inbox, pending tasks, and system health. Report anything that needs attention.\"),\n channel: z.string().default(\"heartbeat\"),\n }).default({}),\n /** Reachability via the external public API.\n * - \"private\" (default): this agent is daemon-internal + channel-bound only.\n * - \"public\": external apps can POST /api/public/agents/<id>/messages\n * with a scoped token (agent:<id> or agent:*). */\n access: z.enum([\"private\", \"public\"]).default(\"private\"),\n /** Governance override for agent-facing management tools. An admin agent\n * may pause, resume and request deletion of schedules created by other\n * agents or by the operator. It never approves anything: approval of a\n * create/delete request stays operator-only (`agentx schedule approve`). */\n admin: z.boolean().optional(),\n /** Events on the bus this agent wants to hear about (issue #165). */\n subscriptions: z.array(eventSubscriptionSchema).default([]),\n /** Per-agent third-party integrations registry. Declares which services\n * this agent has credentials for (telegram-bot, hubspot, gitlab-user,\n * gmail, etc.). Secrets stay in env vars / keyring; this block holds\n * declarations + non-secret metadata only. Skills and channel adapters\n * read from here to know what the agent can use. See INTEGRATION_KINDS. */\n integrations: z.array(integrationSchema).default([]),\n /** How this agent sounds when it speaks (voice widget, `agentx teach`).\n * Every field is optional: no voice id falls back to the global\n * AGENTX_VOICE_ID, no intro is derived from the system prompt. See\n * src/voice/agent-voice.ts. */\n voice: voiceSchema.optional(),\n /** How this agent appears on screen: its own cursor, drawn by the Mac\n * helper and click-through, never the person's real mouse. See\n * src/voice/presence.ts. */\n presence: z.object({\n /** Cursor colour, as #RRGGBB. Default: derived from the agent id. */\n color: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),\n /** The voice orb's gradient, one of src/voice/orb-palettes.ts.\n * Default: the palette nearest `color`. */\n palette: z.enum(ORB_PALETTE_IDS).optional(),\n /** One or two letters on the cursor. Default: from the name. */\n initial: z.string().max(2).optional(),\n /** Name shown under the cursor. Default: the agent's name. */\n label: z.string().optional(),\n /** May this agent click and type for the person (presence mode \"act\")?\n * Off by default: without it, \"act\" becomes \"teach\". */\n allowActions: z.boolean().optional(),\n }).optional(),\n})\n\nconst telegramAccountSchema = z.object({\n token: z.string(),\n agentBinding: z.string(),\n /** Per-account sender allowlist. When set, OVERRIDES the global\n * `policy.allowFrom`. Entries accept:\n * - numeric user id (\"1816212449\") — matches sender (from.id)\n * - numeric chat id (\"-1003861455814\") — matches chat (chat.id)\n * - \"@username\" — matches sender username\n * - \"*\" — matches everyone (public bot)\n * A message is dispatched iff at least one entry matches. */\n allowFrom: z.array(z.string()).optional(),\n /** When false, the daemon keeps the bot token registered so outbound\n * `send()` / ring notifications work, but does NOT long-poll for inbound\n * updates. Use this on nodes where the bound agent lives on a different\n * daemon — otherwise two daemons race on the same `getUpdates` cursor and\n * Telegram responds 409 Conflict on every poll.\n * Defaults to true (poll) so existing single-node setups are unchanged. */\n pollInbound: z.boolean().default(true),\n})\n\n/** An agent watching the phone camera (#325 phase 2, src/camera/watch.ts). */\nexport const cameraBotSchema = z.object({\n /** Seconds between frames the agent gets by itself. 0: only when asked\n * (\"Look now\" on the phone, or the agent's own `agentx camera look`).\n * Each frame handed over by itself is a turn of the agent, so keep\n * this high or off. */\n frameIntervalSeconds: z.number().int().min(0).max(3600).default(0),\n /** The agent's watch ends after this many minutes, whatever the phone does. */\n maxSessionMinutes: z.number().int().min(1).max(240).default(10),\n /** Frames are shrunk so their longer side is at most this many pixels\n * before the agent sees them. */\n maxFrameEdge: z.number().int().min(160).max(3840).default(1024),\n /** Keep the frame files in the agent's workspace after the share ends.\n * Off: every frame is deleted when the share ends. */\n keepFrames: z.boolean().default(false),\n /** \"Keep watching\" on the phone (#687): for a task that needs a stream\n * (\"watch while I do this\"), the agent gets a frame every this many\n * seconds, only while the owner has it on. */\n streamFrameSeconds: z.number().int().min(2).max(60).default(5),\n /** \"Keep watching\" stops by itself after this many seconds. */\n streamMaxSeconds: z.number().int().min(10).max(600).default(60),\n}).default({})\n\nexport type CameraBotConfig = z.infer<typeof cameraBotSchema>\n\nconst channelsConfigSchema = z.object({\n telegram: z.object({\n enabled: z.boolean().default(false),\n accounts: z.record(z.string(), telegramAccountSchema).default({}),\n policy: z.object({\n dm: z.enum([\"pair\", \"block\"]).default(\"pair\"),\n group: z.enum([\"mention-required\", \"all\"]).default(\"mention-required\"),\n /** Global sender allowlist applied to every account that doesn't set\n * its own `allowFrom`. When neither is configured, every incoming\n * message is dropped — closed by default. Same entry forms as the\n * per-account list (user id, chat id, @username, or \"*\" for everyone). */\n allowFrom: z.array(z.string()).optional(),\n }).default({}),\n }).default({}),\n whatsapp: z.object({\n enabled: z.boolean().default(false),\n sessionDir: z.string().default(\".agentx/whatsapp-sessions\"),\n defaultAgent: z.string().optional(),\n allowFrom: z.array(z.string()).optional(),\n routes: z.array(z.object({\n contact: z.string().optional(),\n group: z.string().optional(),\n agent: z.string(),\n })).default([]),\n /** Data-source ingestion — turns WhatsApp from a messaging-only channel\n * into a source that seeds the wiki with contact/group metadata (and\n * optionally bounded message windows) per an explicit allowlist.\n * Default-deny: `enabled: false` and empty allowlists mean nothing is\n * ingested. See docs/reference/whatsapp-ingest.md for the full story. */\n ingest: z.object({\n enabled: z.boolean().default(false),\n /** `metadata-only` pulls contact/group info; `messages` additionally\n * pulls the last `messageCap` messages per allowlisted chat. */\n mode: z.enum([\"metadata-only\", \"messages\"]).default(\"metadata-only\"),\n /** Phone numbers or JIDs. Substring match, same semantics as allowFrom. */\n allowContacts: z.array(z.string()).default([]),\n allowGroups: z.array(z.string()).default([]),\n denyContacts: z.array(z.string()).default([]),\n denyGroups: z.array(z.string()).default([]),\n /** Per-chat cap when mode = \"messages\". Keeps the raw-entry size bounded. */\n messageCap: z.number().int().min(1).max(500).default(50),\n /** Max age (days) of messages to consider for the bounded window. */\n historyDays: z.number().int().min(1).max(365).default(30),\n /** Skip re-writing a contact entry unless this many days have passed\n * OR the profile hash differs. Prevents churn on unchanged profiles. */\n contactRefreshDays: z.number().int().min(1).max(90).default(7),\n /** Safety rails on live Baileys reads — personal-account accounts\n * can get throttled/banned under burst reads. */\n throttle: z.object({\n minMsBetweenCalls: z.number().int().min(100).default(1500),\n maxCallsPerMinute: z.number().int().min(1).default(20),\n maxChatsPerSweep: z.number().int().min(1).default(25),\n }).default({}),\n /** Purge absorbed raw entries older than this many days. `0` = never. */\n retentionDays: z.number().int().min(0).default(0),\n }).default({}),\n }).default({}),\n gitlab: z.object({\n enabled: z.boolean().default(false),\n webhookPort: z.number().default(18810),\n webhookSecret: z.string().optional(),\n host: z.string().default(\"https://gitlab.com\"),\n token: z.string().optional(),\n /** Legacy auto-reply: when true (default), the router posts the agent's\n * `response.content` as a comment automatically. When false, the agent\n * must call the `channel.reply` action explicitly to reply — agent\n * reasoning text is logged but not posted. Flip to false once the\n * project's agents have migrated their gitlab skill to use channel.reply.\n * Default true preserves today's behaviour for unmigrated projects. */\n autoReplyLegacy: z.boolean().default(true),\n routes: z.array(z.object({\n project: z.string(),\n agent: z.string(),\n })).default([]),\n agentMappings: z.array(z.object({\n agentId: z.string(),\n gitlabUsernames: z.array(z.string()).default([]),\n keywords: z.array(z.string()).default([]),\n token: z.string().optional(),\n /** If set, the agent lives on a remote mesh peer (node id). Forces the\n * username→agent map to resolve to this mapping even when a local\n * agent's token resolves to the same GitLab user — prevents collisions\n * like two agents both claiming @devops-acme. */\n node: z.string().optional(),\n })).default([]),\n /** Extra GitLab usernames each agent answers to, as prefixes. With\n * [\"team-\"], an agent with no agentMappings row answers to @<id> and\n * @team-<id>. Default: only @<id>. */\n agentUsernamePrefixes: z.array(z.string().min(1)).default([]),\n }).default({}),\n github: z.object({\n enabled: z.boolean().default(false),\n /** Legacy auto-reply — same semantics as gitlab.autoReplyLegacy.\n * Defaults true; set false once agent skills use channel.reply. */\n autoReplyLegacy: z.boolean().default(true),\n /** Let agents whose `cloudSessions.enabled` is on send this channel's\n * issue and pull request tasks to Claude cloud sessions (#622). Off by\n * default: both this and the agent setting must be on. */\n cloudSessions: z.boolean().default(false),\n /** GitHub PAT or env var (${GITHUB_TOKEN}) for posting comments back. */\n token: z.string().optional(),\n /** Path to file containing the token (first line read at startup). */\n tokenFile: z.string().optional(),\n /** GitHub App ID (legacy issuer). */\n appId: z.number().optional(),\n /** GitHub App Client ID (preferred JWT issuer per GitHub's updated docs). */\n clientId: z.string().optional(),\n /** Path to the GitHub App private key PEM file. */\n privateKeyFile: z.string().optional(),\n /** Webhook secret for validating X-Hub-Signature-256. */\n webhookSecret: z.string().optional(),\n /** Repo → agent routing. Use \"owner/repo\" or \"*\" for default. */\n routes: z.array(z.object({\n repo: z.string(),\n agent: z.string(),\n })).default([]),\n /** Per-agent identity mappings (GitHub usernames, tokens, mesh nodes). */\n agentMappings: z.array(z.object({\n agentId: z.string(),\n githubUsernames: z.array(z.string()).default([]),\n token: z.string().optional(),\n tokenFile: z.string().optional(),\n node: z.string().optional(),\n })).default([]),\n // --- Which issue and pull request events start a run (#612) ---\n /** Issue actions that start a run. A project rule whose `actions`\n * list is set replaces this list for its repository; a rule without\n * one keeps it, so `closed` never starts a run unless asked for. */\n issueActions: z.array(z.string().min(1)).default([\"opened\", \"reopened\", \"assigned\"]),\n /** Pull request actions that start a run. Same precedence as issueActions. */\n pullRequestActions: z.array(z.string().min(1)).default([\"opened\", \"reopened\", \"ready_for_review\"]),\n /** A label, assignment, close or edit made by an account AgentX posts\n * with (the App bot, a token owner, a mapped username, a mesh peer)\n * does not start a run: the owner sweep's own `agent:<id>` label woke\n * the agent it was filed for. Opened and reopened always count. */\n ignoreOwnChanges: z.boolean().default(true),\n /** Events on one issue or pull request within this many seconds become\n * one run, carrying the latest state. The window restarts with each\n * event. 0 starts a run per event. */\n debounceSeconds: z.number().min(0).max(3600).default(30),\n }).default({}),\n /** ntfy push notifications — outbound only. The operator-facing tap on\n * the shoulder: cron failures, task errors, and anything an agent decides\n * is worth interrupting a human for. `chatId` on an outgoing message is\n * the topic; empty falls back to `topic` here. */\n ntfy: z.object({\n enabled: z.boolean().default(false),\n /** Self-hosted ntfy base URL. Defaults to the public server. */\n server: z.string().default(\"https://ntfy.sh\"),\n /** Default topic. Treat it as a secret — on ntfy.sh, knowing the topic\n * is the only thing needed to read or publish to it. */\n topic: z.string().optional(),\n /** Access token for protected topics. */\n token: z.string().optional(),\n /** 1 (min) .. 5 (max). ntfy's own default is 3. */\n defaultPriority: z.number().int().min(1).max(5).default(3),\n defaultTitle: z.string().optional(),\n }).default({}),\n /** Web Push to the phone app (/app) — outbound only. The node that hosts\n * the app holds the subscriptions and sends; any other node sets\n * `relayTo` and forwards over the mesh. `chatId` \"default\" means every\n * subscribed phone, or a device id (tok_…) for one. */\n push: z.object({\n enabled: z.boolean().default(false),\n /** VAPID key pair written by `agentx app push-keys`. Relative paths\n * resolve from the folder AgentX runs in (the one holding agentx.json). */\n keysFile: z.string().default(\".agentx/push-keys.json\"),\n /** Contact for the push services: `mailto:you@example.com` or an\n * https:// URL. Required on the hosting node. */\n subject: z.string().regex(/^(mailto:|https:\\/\\/)/, \"must start with mailto: or https://\").optional(),\n /** Mesh peer (id or name) that hosts the phone app. Set it on every\n * other node; leave it unset on the host. */\n relayTo: z.string().optional(),\n /** How long a push service keeps trying an offline phone. */\n ttlSeconds: z.number().int().min(0).max(2419200).default(86400),\n /** Recent pushes kept for the app's Alerts tab. */\n keepRecent: z.number().int().min(0).max(1000).default(50),\n /** Push-service hosts a phone may subscribe with (a host or any\n * subdomain of it). The daemon POSTs to the stored address, so anything\n * else is refused. Defaults cover Chrome/Android, Firefox, Safari/iOS\n * and Edge. */\n allowedHosts: z.array(z.string().min(1)).default([\n \"fcm.googleapis.com\", \"push.services.mozilla.com\", \"push.apple.com\", \"notify.windows.com\",\n ]),\n }).default({}),\n webrtc: z.object({\n enabled: z.boolean().default(false),\n /** ICE STUN servers for NAT discovery. Default is Google's public STUN. */\n stunServers: z.array(z.string()).default([\"stun:stun.l.google.com:19302\"]),\n /** Optional TURN relays (required when both peers are behind symmetric NAT). */\n turnServers: z.array(z.object({\n urls: z.string(),\n username: z.string().optional(),\n credential: z.string().optional(),\n })).default([]),\n /** Peer names permitted to *initiate* a call into this daemon. Empty = allow all mesh peers. */\n allowedCallers: z.array(z.string()).default([]),\n /** Where to send \"someone is calling\" notifications when an inbound ring\n * arrives. Each entry is delivered via the matching channel adapter —\n * same plumbing as any other outbound message. Empty disables notifications. */\n ringNotify: z.array(z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n })).default([]),\n /** Base URL used when building the tap-to-join link in ring notifications.\n * Defaults to `http://<node.bind>` — override when the daemon's public\n * hostname differs (e.g. HTTPS-terminated tunnel, Tailscale MagicDNS). */\n callUrlBase: z.string().optional(),\n /** AI participant (\"bot\") joins calls when a browser opens with `?bot=<id>`.\n * v1 is transcribe-only — bot consumes remote audio, transcribes via\n * Whisper, posts chunks to the configured channel. No TTS-back. */\n bot: z.object({\n enabled: z.boolean().default(false),\n /** Default agent id used to attribute the transcript when the URL\n * doesn't override via `?bot=<other-agent>`. */\n defaultAgentId: z.string().optional(),\n /** \"auto\" tries mlx-whisper, falls back to OpenAI; explicit forces. */\n whisperBackend: z.enum([\"auto\", \"mlx\", \"openai\"]).default(\"auto\"),\n whisperModel: z.string().optional(),\n whisperLanguage: z.string().default(\"auto\"),\n /** Absolute path to mlx_whisper if not on the daemon's PATH (common\n * on macOS launchd, where ~/.pyenv/shims is missing from the env). */\n mlxBinary: z.string().optional(),\n /** Where to send each transcribed chunk. Same shape as ringNotify[]. */\n transcriptChannel: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Hard cap so a forgotten bot doesn't run forever. */\n maxCallMinutes: z.number().int().min(1).max(240).default(30),\n }).default({}),\n /** The phone app's Share camera (#325). The phone asks for this size and\n * rate; the browser picks the nearest the camera supports. The share\n * stops by itself after maxSeconds. */\n camera: z.object({\n width: z.number().int().min(160).max(3840).default(1280),\n height: z.number().int().min(120).max(2160).default(720),\n frameRate: z.number().int().min(1).max(60).default(15),\n maxSeconds: z.number().int().min(10).max(7200).default(600),\n /** Ask the watching agent by voice: hold or tap Talk (#687). Off: the\n * phone shows the text box only. The microphone opens only while\n * you talk and is never part of the share. */\n voiceInput: z.boolean().default(true),\n /** Say the watching agent's answers aloud on the phone, in the\n * agent's voice or the phone's own. The owner can mute it there. */\n speakAnswers: z.boolean().default(true),\n /** An agent watching the phone camera. The bot keeps only the newest\n * frame; the agent gets one when asked, and every\n * frameIntervalSeconds when that is set. */\n bot: cameraBotSchema,\n }).default({}),\n }).default({}),\n})\n\nconst cronJobSchema = z.object({\n enabled: z.boolean().default(true),\n schedule: z.string(),\n timezone: z.string().default(\"UTC\"),\n agent: z.string(),\n prompt: z.string().default(\"\"),\n /**\n * A shell command to run INSTEAD of dispatching the agent.\n *\n * Seven of this fleet's twenty-six crons were a single command wrapped\n * in an agent, and every one of them carried a paragraph like \"Run this\n * EXACT command. Do NOT substitute, rewrite, shorten, or fall back to\n * alternative commands.\" That paragraph is a scar: it exists because\n * models kept substituting, and no amount of prompt severity makes a\n * language model a deterministic executor.\n *\n * Measured before this existed: ~$0.31 and ~24s per run to execute one\n * `node script.js` and repeat ten lines of its output — a 114k-token\n * context for a job with no judgement in it at all.\n *\n * When set, the scheduler runs the command directly. No model, no\n * tokens, no improvisation. `agent` is still required and is who gets\n * told when it fails.\n */\n command: z.string().optional(),\n timeout: z.number().default(600),\n model: z.string().optional(),\n /** Soft cap on output length. Claude Code CLI has no hard flag for this,\n * but appending an instruction to the prompt is reliably honored. Use\n * 1500 for briefs, 500 for status pings, 300 for pure classifiers. */\n maxOutputTokens: z.number().int().min(50).max(8000).optional(),\n /** How much this routine may do: `report` (read-only), `propose` (branch,\n * commit, MR/draft; never merge/deploy/delete) or `act` (the agent's full\n * permissions — the default). Enforced per run by the guard, not by the\n * prompt; only the claude-code tier can enforce it, others refuse. */\n autonomy: autonomyLevelSchema.optional(),\n onError: z.union([\n z.enum([\"log\", \"notify\", \"disable\"]),\n z.array(z.enum([\"log\", \"notify\", \"disable\"])),\n ]).default(\"log\").transform(v => Array.isArray(v) ? v : [v]),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Whether each successful run's answer is sent to `notify` (failures\n * are alerted there either way). Default on: a schedule with somewhere\n * to report reports there, including the chat an agent's schedule was\n * requested from (#738). `false` keeps `notify` for failure alerts only. */\n deliverResult: z.boolean().optional(),\n /** Secret that lets an external system fire this job now via\n * `POST /routines/<id>/fire`. Reference an env var (`\"${MY_TOKEN}\"`);\n * a job without one cannot be fired. See src/daemon/routine-fire.ts. */\n fireToken: z.string().optional(),\n /** Agent id that created this job through the agent `schedule` tool.\n * Absent for operator-created jobs. Drives the ownership rule: an agent\n * manages only the routines it created, unless it is `admin`. */\n createdBy: z.string().optional(),\n /** Pending operator approval for an agent-requested change. While a\n * `create` is pending the job never runs, whatever `enabled` says; a\n * pending `delete` leaves the job as it was until approved. Cleared by\n * `agentx schedule approve|reject <id>`. */\n approval: z.object({\n action: z.enum([\"create\", \"delete\"]),\n requestedBy: z.string(),\n requestedAt: z.string(),\n }).optional(),\n}).refine((j) => Boolean(j.command?.trim() || j.prompt?.trim()), {\n message: \"a cron needs either a prompt (dispatch an agent) or a command (run it directly)\",\n}).refine((j) => !(j.command?.trim() && j.autonomy && j.autonomy !== \"act\"), {\n message: \"autonomy applies to agent routines; a command cron runs no agent (remove autonomy or the command)\",\n})\n\nconst serviceSchema = z.object({\n name: z.string(),\n triggers: z.array(z.object({\n pattern: z.string(),\n channel: z.string().optional(),\n })),\n allowedContacts: z.array(z.string()).optional(),\n agent: z.string(),\n prompt: z.string(),\n schedule: z.string().optional(),\n timezone: z.string().default(\"UTC\"),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst meshPeerSchema = z.object({\n url: z.string(),\n name: z.string(),\n token: z.string().optional(),\n})\n\n/** A named, operator-declared destination that remote mesh nodes may send\n * to. Deliberately minimal: the published surface is the name and whether\n * it accepts, so there is nothing here that could leak local state if it\n * were echoed back. Which agent handles it stays private to this node. */\nconst meshInboxSchema = z.object({\n name: z.string(),\n agent: z.string(),\n enabled: z.boolean().default(true),\n})\n\nconst meshConfigSchema = z.object({\n enabled: z.boolean().default(false),\n peers: z.array(meshPeerSchema).default([]),\n /** Empty by default — a node publishes no inboxes until an operator\n * declares one, so this feature is opt-in per node. */\n inboxes: z.array(meshInboxSchema).default([]),\n discovery: z.enum([\"static\", \"mdns\"]).default(\"static\"),\n healthCheck: z.object({\n interval: z.number().default(60),\n timeout: z.number().default(10),\n }).default({}),\n /** Peer event feed (#166): follow each healthy peer's /events and merge\n * its own events into this node's bus. `skipTypes` are left out on the\n * peer's side; per-step agent activity is too chatty to cross the mesh. */\n feed: z.object({\n enabled: z.boolean().default(true),\n skipTypes: z.array(z.string().min(1)).default([\"task:step\"]),\n }).default({}),\n /** Agent-to-agent delegation (#277), local or across peers. When a person\n * started the conversation, a delegation returns at once and the answer\n * comes back to the asking agent as a new turn in the same chat. */\n delegation: z.object({\n /** Off: every delegation waits for its answer, as before. */\n asyncWhenHuman: z.boolean().default(true),\n /** A delegation with no answer after this long is reported as timed out. */\n timeoutMinutes: z.number().int().min(1).max(240).default(30),\n }).default({}),\n})\n\n/** Intent Knowledge Graph — fixed-axis, LLM-proposed taxonomy used by the\n * Intent layer in context.ts and (eventually) wiki retrieval. Off by default\n * so existing installs see no change. */\nconst graphConfigSchema = z.object({\n enabled: z.boolean().default(false),\n /** Where the schema/nodes/classifications live. Relative to cwd. */\n baseDir: z.string().default(\".agentx/graph\"),\n /** Which agent makes LLM classification proposals. Falls back to\n * `dashboard.draftAgent` at call sites if unset. */\n draftAgent: z.string().optional(),\n /** Agent used by `agentx graph review` to triage pending classifications.\n * Should be an agent with the wiki skill so it can call `wiki query` for\n * context before deciding approve/reject. Falls back to draftAgent. */\n reviewAgent: z.string().optional(),\n /** Structural auto-approval policy. Evaluated against each classification\n * independently of `autoApproveConfidence`; either triggers approval.\n * - \"strict\" : never approve via structure — every classification\n * waits for human review\n * - \"extend-leaves\" : auto-approve when the proposed path (a) reuses only\n * existing nodes, or (b) adds exactly one NEW node at\n * the deepest level. Structural changes (new mid-path\n * node, new root) still queue for review. Default.\n * - \"any\" : auto-approve every classification regardless of\n * structural change\n */\n autoApproveStructure: z.enum([\"strict\", \"extend-leaves\", \"any\"]).default(\"extend-leaves\"),\n /** Minimum classifier confidence (0..1) to auto-approve. OR'd with the\n * structural policy — either hitting the threshold lets the classification\n * bypass the pending queue. 1.0 (default) disables this knob, so approval\n * is driven by `autoApproveStructure` alone. Lower it to e.g. 0.7 if you\n * also want high-confidence structural changes auto-approved. */\n autoApproveConfidence: z.number().min(0).max(1).default(1.0),\n /** Anthropic model id used for the direct classifier call (Phase 2 of\n * classifier-retire). Default haiku — classification is metadata, not\n * work, so we use the cheapest fast model. Override only if Haiku is\n * rate-limited or you want to A/B test. */\n classifierModel: z.string().default(\"claude-haiku-4-5-20251001\"),\n /** Weights for the wiki hybrid retrieval score. Path-ancestry match vs\n * BM25 over article text. Sum need not be 1. */\n retrievalWeights: z.object({\n graph: z.number().min(0).default(0.6),\n bm25: z.number().min(0).default(0.4),\n }).default({}),\n}).default({})\n\n// --- Typed-decision seats (src/decisions) ---\n//\n// Everything here defaults off. A seat in \"shadow\" runs alongside the\n// incumbent and only records; \"active\" lets its answer steer behaviour, and\n// no seat should reach it before its calibration report says what threshold\n// to use and what that threshold costs.\nconst decisionSeatSchema = z.object({\n /** off: the seat is unreachable. shadow: runs and records, incumbent stays\n * authoritative. active: the answer is used. */\n mode: z.enum([\"off\", \"shadow\", \"active\"]).default(\"off\"),\n /** Backend name from the decisions registry. Per-seat, so one seat can be\n * graded against a different backend than another at the same time. */\n backend: z.string().optional(),\n model: z.string().optional(),\n /** Unset: 10 s (DEFAULT_SEAT_TIMEOUT_MS), except where a call site sets\n * its own default, as request-gate and request-context do (3 s). Left\n * without a schema default so those seats can tell a value the operator\n * wrote from one nobody chose. */\n timeoutMs: z.number().int().min(100).optional(),\n /** Post-hoc temperature fitted on this seat's own labeled rows. 1 means\n * \"not calibrated yet\", which is where every seat starts — see\n * `agentx decisions calibrate`. */\n temperature: z.number().min(0.01).default(1),\n /** Fraction of would-be-skips to run anyway. Not a tuning knob: a skipped\n * decision can never be graded, so without exploration an active seat's\n * metrics are computed on the biased sample it selected for itself.\n * Setting this to 0 in active mode means the calibration report stops\n * being trustworthy the day you promote the seat. */\n explore: z.number().min(0).max(1).default(0.15),\n /** Fraction of turns that bypass the seat as a randomized control group.\n * Unset lets the seat pick its own default; only seats that run an\n * experiment read it (today: request-gate, default 0.1). */\n holdout: z.number().min(0).max(1).optional(),\n}).default({})\n\nconst decisionsConfigSchema = z.object({\n enabled: z.boolean().default(false),\n dbPath: z.string().default(\".agentx/decisions/decisions.sqlite\"),\n /** State can contain message text. With this on, only the hash is kept,\n * which costs the ability to replay a decision offline. */\n redactState: z.boolean().default(false),\n /** Keep the serialized state for this many rows per seat, then prune to\n * hashes. Enough to replay and to grade an alternative backend on\n * identical inputs, without growing without bound. */\n keepStateRows: z.number().int().min(0).default(2000),\n defaultBackend: z.string().default(\"local\"),\n backends: z.object({\n local: z.object({\n provider: z.string().default(\"claude-code\"),\n model: z.string().default(\"claude-haiku-4-5-20251001\"),\n /** auto resolves to a forced tool where the provider guarantees one,\n * and JSON-in-text otherwise (claude-code on OAuth cannot force a\n * tool). Never resolves to logprobs: that costs one call per\n * question, so it has to be asked for. */\n structureMode: z.enum([\"auto\", \"tool\", \"text\"]).default(\"auto\"),\n normalizeProbabilities: z.boolean().default(true),\n nRetryMalformedStructure: z.number().int().min(0).max(3).default(1),\n maxStateChars: z.number().int().min(500).default(24_000),\n }).default({}),\n /** A simple-jev server (featherless-ai/simple-jev). Reads the model's\n * next-token logits rather than asking it to write a probability, so\n * the distribution is the model's posterior instead of a number it\n * chose. Still not calibrated to correctness — that comes from\n * `agentx decisions recalibrate`. */\n simpleJev: z.object({\n baseUrl: z.string().default(\"http://127.0.0.1:8000/v1\"),\n model: z.string().optional(),\n apiKeyEnv: z.string().optional(),\n timeoutMs: z.number().int().min(100).default(30_000),\n maxStateChars: z.number().int().min(500).default(6_000),\n /** Their Choice takes 2-50 candidates, well under Jev's 255. */\n maxChoiceOptions: z.number().int().min(2).max(255).default(50),\n }).default({}),\n /** TypeSafe Jev through OpenRouter's alpha decisions endpoint.\n * Verified live 2026-09-19 — resolves to typesafe/jev-1.13-20260917,\n * provider \"TypeSafe\", ~0.5s round trip. The id is \"jev-latest\";\n * the namespaced \"typesafe/jev-latest\" 400s, and neither appears in\n * OpenRouter's public model list. Needs OPENROUTER_API_KEY. */\n jev: z.object({\n baseUrl: z.string().default(\"https://openrouter.ai/api/alpha\"),\n path: z.string().default(\"/decisions\"),\n model: z.string().default(\"jev-latest\"),\n apiKeyEnv: z.string().default(\"OPENROUTER_API_KEY\"),\n timeoutMs: z.number().int().min(100).default(30_000),\n maxStateChars: z.number().int().min(500).default(90_000),\n maxChoiceOptions: z.number().int().min(2).max(255).default(255),\n }).default({}),\n /** Jev direct from TypeSafe rather than through OpenRouter. Same\n * contract, different billing and rate limits. Needs TYPESAFE_API_KEY. */\n typesafe: z.object({\n baseUrl: z.string().default(\"https://api.typesafe.ai/v1\"),\n path: z.string().default(\"/systemone\"),\n model: z.string().default(\"jev-latest\"),\n apiKeyEnv: z.string().default(\"TYPESAFE_API_KEY\"),\n timeoutMs: z.number().int().min(100).default(30_000),\n maxStateChars: z.number().int().min(500).default(90_000),\n maxChoiceOptions: z.number().int().min(2).max(255).default(255),\n }).default({}),\n }).default({}),\n seats: z.record(z.string(), decisionSeatSchema).default({}),\n /** Model routing driven by the task-tier seat. Models are scoped to their\n * CLI engine; the legacy cheapModel applies only to Claude Code. */\n routing: z.object({\n cheapModel: z.string().optional(),\n cheapModels: z.object({\n \"claude-code\": z.string().optional(),\n \"codex-cli\": z.string().optional(),\n }).optional(),\n }).default({}),\n}).default({})\n\n/** Agents ringing the owner for a live voice call (src/calls). */\nexport const callsSchema = z.object({\n /** Agents allowed to call: ids, or \"*\" for every agent on this node.\n * Empty: nobody can call until the owner allows it. */\n allow: z.array(z.string()).default([]),\n /** Most calls one agent may place in an hour. */\n maxPerHour: z.number().int().min(1).max(60).default(3),\n /** How long a call rings before it counts as missed. */\n ringSeconds: z.number().int().min(10).max(300).default(45),\n /** An answered call nobody hung up (widget quit or crashed, the Mac\n * slept, `agentx call answer` without a conversation) ends after this,\n * so the agent can call again. */\n maxCallMinutes: z.number().int().min(1).max(240).default(30),\n /** Ring sound: a name from /System/Library/Sounds, without the extension. */\n ringSound: z.string().regex(/^[\\w -]+$/).default(\"Submarine\"),\n /** After hang-up, ask the agent for a short summary and file it in the\n * dashboard's conversation history. */\n summary: z.boolean().default(true),\n}).default({})\n\nexport type CallsConfig = z.infer<typeof callsSchema>\n\nconst notificationsSchema = z.object({\n /** Send notification when task takes longer than this (seconds). 0 = disabled. */\n longTaskThreshold: z.number().default(30),\n /** Where to send notifications */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Channel `agentx notify` and Focus digests use when none is given.\n * Unset: `push` (the phone app) when channels.push is enabled, else\n * `ntfy`, so upgrading never points notify at a channel that is off. */\n channel: z.string().optional(),\n /** Notify on these events */\n on: z.object({\n taskComplete: z.boolean().default(true),\n taskError: z.boolean().default(true),\n taskQueued: z.boolean().default(false),\n }).default({}),\n /** What `agentx notify` does on this machine when it delivers: a desktop\n * banner and a system sound. macOS only; see src/notify/local.ts. */\n local: z.object({\n banner: z.boolean().default(true),\n sound: z.boolean().default(true),\n /** A name from /System/Library/Sounds, without the extension. */\n soundName: z.string().regex(/^[\\w -]+$/).default(\"Glass\"),\n volume: z.number().min(0).max(1).default(0.4),\n /** Image for the AgentX Helper icon on banners (.png, .jpg, .icns).\n * Unset: the AgentX logo. Applied by `agentx desktop install`. */\n icon: z.string().optional(),\n }).default({}),\n}).default({})\n\nconst screenRectSchema = z.object({\n x: z.number(), y: z.number(), width: z.number().positive(), height: z.number().positive(),\n})\n\n/** Screen capture for agents: how captures are cropped and scaled, how\n * long they wait for the right moment, named regions, and the opt-in\n * in-memory buffer. See src/computer-use/capture.ts. */\nconst screenSchema = z.object({\n /** Pixel budget for a captured frame; larger captures are downscaled. */\n maxPixels: z.number().int().positive().default(1_200_000),\n /** Longest a capture waits for a change or for the screen to settle. */\n timeoutMs: z.number().int().positive().default(5_000),\n /** Time between samples while waiting. */\n intervalMs: z.number().int().positive().default(100),\n /** Mean difference (0–1) above which two samples count as different. */\n changeThreshold: z.number().min(0).max(1).default(0.015),\n /** How long a region must stay unchanged to count as stable. */\n stableMs: z.number().int().nonnegative().default(400),\n /** Named regions in screen points, e.g. { \"chat\": { x, y, width, height } }.\n * A name here overrides the built-in region of the same name. */\n regions: z.record(z.string().regex(/^[a-z][\\w-]*$/), screenRectSchema).default({}),\n /** The last few seconds of a region, kept in memory by the daemon. Off\n * unless enabled; frames are written out only when asked for. */\n buffer: z.object({\n enabled: z.boolean().default(false),\n seconds: z.number().positive().max(120).default(10),\n fps: z.number().positive().max(10).default(2),\n region: z.string().default(\"screen\"),\n maxPixels: z.number().int().positive().default(300_000),\n }).default({}),\n}).default({})\n\n/** The Approvals inbox (src/approvals). Decision cards agents raise, and\n * when the operator hears about what is waiting. */\nexport const requestsConfigSchema = z.object({\n enabled: z.boolean().default(false),\n /** Channels to capture on. Empty: every channel a person writes on\n * (telegram, whatsapp, slack, discord, gitlab, github, app, voice,\n * dashboard, webrtc). */\n channels: z.array(z.string().min(1)).default([]),\n /** Who counts as the owner on channels other people can reach, as\n * \"channel:id\": the login on GitLab and GitHub, the sender id elsewhere.\n * Usernames beside an id and display names are not matched. Empty: only\n * this node's own surfaces (voice, app, dashboard, webrtc) are captured. */\n from: z.array(z.string().regex(/^[a-z0-9_-]+:\\S+$/i, \"use channel:id, for example telegram:123456789\")).default([]),\n /** An open request with no activity for this long comes back to the owner. */\n staleAfterHours: z.number().positive().max(24 * 365).default(24),\n /** Closed requests are deleted after this many days. Open ones never are. */\n retentionDays: z.number().positive().max(3650).default(90),\n}).default({})\n\n/** People (src/people, #384): the humans who talk to the agents, one entry\n * per person whatever channel they use. Empty: this machine's owner is the\n * only known person and nothing else changes. */\nexport const personSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,39}$/, \"lower-case letters, digits, - and _\"),\n name: z.string().min(1).max(80),\n /** How this person's name is said aloud, e.g. \"A-neess Ma-roo-shee\"\n * (#433). Written forms stay as they are; see voice.pronunciations. */\n say: z.string().trim().min(1).max(120).optional(),\n /** `client` (#453): someone the owner works for; /member shows them\n * \"Your project\", not a teammate's \"My work\". */\n role: z.enum([\"owner\", \"member\", \"client\", \"guest\"]).default(\"member\"),\n /** \"channel:id\": a GitLab or GitHub login, a Telegram id or username, a\n * WhatsApp number. Display names are not matched. */\n identities: z.array(z.string().regex(/^[A-Za-z][\\w-]*:\\S.*$/, \"write it as channel:id\")).default([]),\n /** The agents this person may reach, by id. Empty: every agent (#379). */\n agents: z.array(z.string().min(1)).default([]),\n /** What this person's turns may not use (#379), enforced by a per-run\n * guard hook. Names match without case; `*` is a wildcard. Empty: no\n * limit. A further level is a new key here. */\n deny: z.object({\n /** Tool names: \"Bash\", \"WebFetch\", \"mcp__mail__*\". */\n tools: z.array(z.string().min(1)).default([]),\n /** Skill names, as the Skill tool and auto-injection name them. */\n skills: z.array(z.string().min(1)).default([]),\n }).strict().default({}),\n})\n\nexport const peopleConfigSchema = z.array(personSchema).default([]).superRefine((people, ctx) => {\n const problem = peopleProblem(people)\n if (problem) ctx.addIssue({ code: z.ZodIssueCode.custom, message: problem })\n})\n\n/** Teammates' machines and their trail (src/members, #385, #379). */\nexport const membersConfigSchema = z.object({\n /** Days the per-person log (invites, pairings, sign-ins, refusals,\n * removals) is kept. Older lines are dropped. */\n logRetentionDays: z.number().int().min(1).max(3650).default(90),\n}).default({})\n\nexport const requestStatusConfigSchema = z.object({\n /** Channels that show it. On gitlab and github: one comment per request,\n * kept up to date by the daemon. Empty: off. */\n channels: z.array(z.string().min(1)).default([]),\n}).default({})\n\nexport const approvalsConfigSchema = z.object({\n /** A card without an `expires` gets this many days. */\n defaultExpiryDays: z.number().positive().max(365).default(3),\n /** No card waits longer than this, whatever the agent asked for. */\n maxExpiryDays: z.number().positive().max(365).default(30),\n /** How long \"later\" hides an item. */\n laterHours: z.number().positive().max(24 * 30).default(24),\n /** Tell the agent that raised a card when it is decided or expires. */\n notifyAgent: z.boolean().default(true),\n /** The mesh peer (`mesh.peers[].name`) whose inbox and popup take the\n * cards agents on this node raise (#668). The result comes back here\n * and reaches the agent. Unset: cards stay on this node. */\n forwardTo: z.string().min(1).optional(),\n /** At most one message a day: how many are waiting, and the most urgent. */\n digest: z.object({\n enabled: z.boolean().default(true),\n /** Local time, 24-hour HH:MM. */\n time: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\").default(\"09:00\"),\n /** IANA timezone for `time`. Unset: this machine's. */\n timezone: z.string().refine((tz) => {\n try { new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }); return true } catch { return false }\n }, \"unknown timezone\").optional(),\n /** Where it goes. Unset: notifications.destination. */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n }).default({}),\n /** A popup on this Mac for new decision cards: sound, a spoken line,\n * ready-made choices. Held during Focus. macOS only. */\n popup: z.object({\n enabled: z.boolean().default(false),\n /** \"card\": a small web window; \"dialog\": plain macOS dialogs. */\n style: z.enum([\"card\", \"dialog\"]).default(\"card\"),\n /** The card's colours; \"system\" follows light or dark mode. */\n theme: z.enum([\"system\", \"light\", \"dark\"]).default(\"system\"),\n /** Speak a short line when it opens. */\n speak: z.boolean().default(true),\n /** A macOS voice name for `say -v`. Unset: the system voice. */\n voice: z.string().regex(/^[\\w .()-]+$/, \"a voice name, as `say -v '?'` lists them\").optional(),\n /** \"chime\" (a soft chime played by the card), a system sound from\n * /System/Library/Sounds like Glass, or \"\" for none. */\n sound: z.string().regex(/^[\\w -]*$/, \"a sound name, like chime or Glass\").default(\"chime\"),\n volume: z.number().min(0).max(1).default(0.4),\n /** Seconds the popup waits for an answer; then the card stays in the inbox. */\n timeoutSeconds: z.number().int().min(10).max(3600).default(600),\n }).default({}),\n /** Check-ins (src/approvals/checkin.ts): a few times a day, waiting cards\n * come back to the Mac card and the operator's open Apple Reminders get\n * a card written by the agent that owns them. macOS only. */\n checkin: z.object({\n enabled: z.boolean().default(false),\n /** Local times of a normal pass: reminders due soon, and waiting cards. */\n times: z.array(z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\")).default([\"11:00\", \"14:00\", \"17:00\"]),\n /** Local time of the daily pass: every open reminder. */\n dailyAt: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\").default(\"09:00\"),\n /** IANA timezone for the times. Unset: this machine's. */\n timezone: z.string().refine((tz) => {\n try { new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }); return true } catch { return false }\n }, \"unknown timezone\").optional(),\n /** The operator's Reminders lists to look at. */\n lists: z.array(z.string().min(1)).default([\"Reminders\"]),\n /** Agent that writes cards for reminders without an agentx trailer. */\n agent: z.string().min(1).optional(),\n /** A normal pass takes reminders due within this many hours, or overdue. */\n dueWithinHours: z.number().positive().max(24 * 30).default(24),\n /** Most agents one pass asks to write a card, whatever they answer. */\n maxAsksPerPass: z.number().int().min(1).max(20).default(5),\n /** How long an agent may take to write one card, counted from the start\n * of its turn. The turn is stopped at the limit. */\n composeTimeoutSeconds: z.number().int().min(30).max(3600).default(300),\n }).default({}),\n}).default({})\n\nexport const daemonConfigSchema = z.object({\n node: z.object({\n id: z.string(),\n name: z.string(),\n bind: z.string().default(\"127.0.0.1:18800\"),\n /** Default agent for voice/API calls that don't specify one (e.g. Siri) */\n defaultAgent: z.string().optional(),\n }),\n providers: z.record(z.string(), providerConfigSchema).default({}),\n agents: z.record(z.string(), agentConfigSchema).default({}),\n channels: channelsConfigSchema.default({}),\n crons: z.record(z.string(), cronJobSchema).default({}),\n services: z.record(z.string(), serviceSchema).default({}),\n notifications: notificationsSchema,\n calls: callsSchema,\n /** Watched WhatsApp chats triaged by an agent (src/whatsapp-triage). */\n whatsappTriage: whatsappTriageSchema,\n approvals: approvalsConfigSchema,\n /** How a daemon stop treats runs still in flight. */\n shutdown: z.object({\n /** Wait this long for runs to finish before cutting them off (they are\n * then resumed or reported after the restart). Unset: the\n * AGENTX_DRAIN_TIMEOUT_MS env var, else 300 s. Keep the service\n * manager's stop timeout above it, or it kills the daemon mid-wait. */\n drainTimeoutSeconds: z.number().int().min(0).max(86_400).optional(),\n /** Who may ask for \"restart when idle\", and what happens to the others.\n * Unset: any caller may, and onTimeout \"restart\" is honoured. */\n restart: z.object({\n /** Regular expression matched against the request's `by`. Requests\n * from anyone else are held until `window`, or refused when there\n * is no window. */\n allowBy: z.string().optional(),\n /** Local time \"HH:MM\" at which held requests start an idle-only wait. */\n window: z.string().regex(/^\\d{1,2}:\\d{2}$/, \"window must be HH:MM\").optional(),\n /** How long that wait lasts before it gives up. */\n windowWaitMinutes: z.number().int().min(1).max(1440).default(180),\n /** Turn every request into an idle-only one: a wait that runs out\n * gives up instead of restarting over running work. */\n forbidOnTimeoutRestart: z.boolean().default(false),\n }).default({}),\n }).default({}),\n /** What happens to runs a restart cuts off (agents/resume). Chat messages\n * are resumed in their chat; scheduled jobs never are (the next run\n * covers them); everything else is reported unless its channel is in\n * directChannels. */\n resume: z.object({\n enabled: z.boolean().default(true),\n /** Older runs are reported, not resumed. */\n maxAgeMinutes: z.number().int().positive().default(30),\n /** A run cut off while resuming is not retried past this. */\n maxAttempts: z.number().int().min(0).default(1),\n /** Channels whose runs are only reported, even from a chat. */\n reportOnlyChannels: z.array(z.string()).default([]),\n /** Non-chat runs (voice, agent-to-agent, webhooks…) resumed on these\n * channels. Their answer isn't delivered anywhere; opt in only for\n * runs whose work is the point. */\n directChannels: z.array(z.string()).default([]),\n /** This many restarts within the window pauses resuming. */\n crashLoop: z.object({\n restarts: z.number().int().positive().default(3),\n windowMinutes: z.number().positive().default(10),\n }).default({}),\n }).default({}),\n /** Open requests (src/requests, #356): what a person asked an agent for\n * is recorded and followed until it is done, declined or dropped. Off\n * by default. */\n requests: requestsConfigSchema,\n people: peopleConfigSchema,\n members: membersConfigSchema,\n /** Request status where the request was made (src/requests/status-board,\n * #383): each person sees the state of their own request in the place\n * they asked. Off by default. */\n requestStatus: requestStatusConfigSchema,\n /** Hand due Apple Reminders back to the agent that created them\n * (src/reminders). macOS only; reads reminders whose notes end with the\n * mac-pim skill's `agentx: agent=<id>` trailer. */\n reminders: z.object({\n enabled: z.boolean().default(false),\n /** Reminders lists to watch. */\n lists: z.array(z.string().min(1)).default([\"AgentX\"]),\n pollSeconds: z.number().int().min(15).default(60),\n /** Overdue by more than this (the daemon was down): reported, not run. */\n lookbackHours: z.number().positive().default(24),\n /** The remindctl binary, when it isn't on the daemon's PATH. */\n command: z.string().min(1).default(\"remindctl\"),\n }).default({}),\n /** The phone app (/app) beyond pairing and notifications (#676). */\n app: z.object({\n /** The AgentX Android app (apps/android). Chrome shows it full screen,\n * without an address bar, only when this computer vouches for it at\n * /.well-known/assetlinks.json; with no fingerprints there is nothing\n * to vouch for and that address answers 404. */\n android: z.object({\n /** applicationId the Android app was built with. */\n packageName: z.string().regex(/^[a-zA-Z][\\w]*(\\.[a-zA-Z][\\w]*)+$/, \"must look like com.example.app\").default(\"dev.agentx.phone\"),\n /** SHA-256 fingerprints of the key(s) that signed it, as AB:CD:… */\n certFingerprints: z.array(z.string().regex(/^([0-9A-Fa-f]{2}:){31}[0-9A-Fa-f]{2}$/, \"must be 32 hex pairs separated by colons\")).default([]),\n }).default({}),\n /** Place reminders: places the Android app watches as geofences, and\n * what to do when the phone arrives or leaves (src/places). */\n places: z.object({\n enabled: z.boolean().default(true),\n /** Where places and their reminders are kept. Relative paths resolve\n * from the folder holding agentx.json. */\n file: z.string().min(1).default(\".agentx/places.json\"),\n defaultRadiusMeters: z.number().int().min(50).max(50_000).default(150),\n /** Android rarely notices a smaller circle reliably. */\n minRadiusMeters: z.number().int().min(50).max(50_000).default(100),\n maxRadiusMeters: z.number().int().min(50).max(50_000).default(5000),\n /** Android lets one app watch at most 100 places. */\n maxPlaces: z.number().int().min(1).max(100).default(50),\n maxRulesPerPlace: z.number().int().min(1).max(50).default(10),\n /** A repeating reminder fires at most once in this window, so a\n * phone hovering at the edge of a place doesn't buzz again and again. */\n cooldownMinutes: z.number().int().min(0).max(1440).default(10),\n /** A crossing the phone could only report later (no signal) is\n * dropped once it is older than this. */\n maxEventAgeMinutes: z.number().int().min(1).max(1440).default(30),\n /** How often the Android app checks for changed places, in minutes.\n * Android runs background checks at most every 15 minutes. */\n syncMinutes: z.number().int().min(15).max(1440).default(60),\n }).default({}),\n }).default({}),\n /** `agentx demo`, read from the agentx.json in the folder it runs from. */\n demo: z.object({\n /** Seconds each startup step (node /health, dashboard /live, mesh\n * discovery) may take. Unset: AGENTX_DEMO_STARTUP_TIMEOUT, else 60 s\n * scaled up by the load average (src/commands/demo-startup.ts). */\n startupTimeoutSeconds: z.number().int().positive().max(3600).optional(),\n }).default({}),\n /** The in-process event bus (src/events). `ringSize` bounds how many\n * recent events GET /events/recent can return. */\n events: z.object({\n ringSize: z.number().int().min(1).max(100_000).default(1000),\n }).default({}),\n screen: screenSchema,\n mesh: meshConfigSchema.default({}),\n /** Voices for agents on mesh peers, keyed by remote agent id, or by\n * \"<peer>/<id>\" for one peer's agent (it wins over the id). The Mac\n * speaks for them, so their nodes need no ElevenLabs key. Unset fields\n * are derived from the agent card; see src/voice/mesh-voice.ts. */\n meshVoices: z.record(z.string(), voiceSchema.omit({ rate: true, priority: true, hotkey: true }).extend({\n /** What to call the agent aloud, e.g. \"Atlas\" for \"Main Agent\". */\n name: z.string().optional(),\n })).default({}),\n /** How agents speak. The free system voices by default; ElevenLabs is an\n * opt-in upgrade. Each agent's `voice` block can override the provider\n * and pick its voice; see src/voice/agent-voice.ts. */\n voice: z.object({\n provider: voiceProviderSchema.default(\"system\"),\n /** When ElevenLabs cannot speak (no key, an error): the system voice,\n * or silence. */\n fallback: z.enum([\"system\", \"none\"]).default(\"system\"),\n /** The system voice for agents without one of their own (see\n * systemVoiceSchema). Unset: each agent gets its own. */\n system: systemVoiceSchema.optional(),\n /** Language for assigned system voices, e.g. \"en\", \"fr-FR\". */\n locale: z.string().default(\"en\"),\n /** What agents call the person they talk with, e.g. a first name.\n * Unset: \"the user\". */\n listener: z.string().optional(),\n /** How a word is said aloud without changing how it is written\n * (#433): each pair's `written` form, matched as a whole word\n * without case, is spoken as `spoken`. `languages` (\"en\", \"fr\",\n * \"ar\") limits a pair to lines in those languages. Only speech\n * changes; cards, transcripts and messages keep the written form.\n * See src/voice/pronounce.ts. */\n pronunciations: z.array(z.object({\n written: z.string().trim().min(1).max(80),\n spoken: z.string().trim().min(1).max(120),\n languages: z.array(z.enum([\"en\", \"fr\", \"ar\"])).optional(),\n }).strict()).max(200).default([]),\n /** The agent's drawn pointer and name tag during a lesson. false: it\n * is never drawn, and a lesson is spoken only. */\n pointer: z.boolean().default(true),\n /** AgentX Voice speech to text: \"auto\" uses ElevenLabs when a key is\n * set and the local Whisper otherwise; \"elevenlabs\" or \"local\" pick\n * one (ElevenLabs still falls back to local when it fails). */\n stt: z.enum([\"auto\", \"elevenlabs\", \"local\"]).default(\"auto\"),\n /** Phone voice input where the daemon has no ffmpeg to measure a\n * recording's length: refused by default, since the 2 MB cap alone\n * lets through ~40 minutes of low-bitrate audio. true accepts it. */\n allowUnmeasured: z.boolean().default(false),\n /** Longest answer said aloud, in characters. A longer one stops at\n * its last whole sentence inside this and says the rest is on\n * screen; the written answer is always shown whole. The minimum\n * keeps a cut answer over the 280 characters the Mac pill opens\n * at (AnswerView.isWorthShowing), so \"on screen\" stays true. */\n spokenMaxChars: z.number().int().min(300).max(1500).default(SPOKEN_MAX_CHARS),\n /** A transcript with no words (empty, or only bracketed markers such\n * as \"[background noise]\") gets a fixed reply from /ask and wakes no\n * agent. `markers` is what counts as a marker, without the brackets. */\n noiseFilter: z.object({\n enabled: z.boolean().default(true),\n markers: z.array(z.string()).default(NOISE_MARKERS),\n }).default({}),\n /** The on-device engine behind \"local\" and every fallback:\n * mlx-whisper (Python, all languages) or Parakeet (Core ML, 25\n * European languages, no Arabic; 483 MB downloaded on first use into\n * ~/.agentx/models). mlx-whisper answers until Parakeet is ready. */\n localStt: z.enum([\"mlx-whisper\", \"parakeet\"]).default(\"mlx-whisper\"),\n /** How AgentX Voice hears the end of a hands-free turn: \"vad\" (Silero\n * voice detection, 0.9 MB downloaded on first use; the volume\n * threshold until then) or \"volume\" (the old fixed threshold). */\n endOfTurn: z.enum([\"vad\", \"volume\"]).default(\"vad\"),\n /** AgentX Voice shortcuts: hold `talk` to speak, `stop` silences every\n * voice, `paste` is smart paste. */\n hotkeys: z.object({\n talk: hotkeySchema.default(DEFAULT_HOTKEYS.talk),\n stop: hotkeySchema.default(DEFAULT_HOTKEYS.stop),\n paste: hotkeySchema.default(DEFAULT_HOTKEYS.paste),\n }).default({}),\n /** What shows the assistant's state in AgentX Voice: the \"orb\" at\n * the pill's head, or the \"character\", the orb grown into a small\n * creature that hovers above the bottom edge of the screen. */\n look: z.enum(VOICE_LOOKS).default(\"orb\"),\n /** AgentX Voice starts with its pill reduced to the orb alone: a\n * small circle the person can drag. Off: the full pill. */\n startReduced: z.boolean().default(false),\n /** The character takes a slow stroll beside where it rests when it\n * has nothing to do. Off: it moves only out of the pointer's way. */\n stroll: z.boolean().default(false),\n /** How often the character plays a small animation by itself when\n * it has nothing to do: a look around, a hop, a sway, and a yawn\n * before it dozes. \"off\": none. */\n animations: z.enum(VOICE_ANIMATIONS).default(\"sometimes\"),\n /** The orb and character palette of an agent that set neither\n * presence.palette nor presence.color. */\n palette: z.enum(ORB_PALETTE_IDS).default(DEFAULT_PALETTE),\n /** The answer text AgentX Voice shows inside its pill. */\n card: z.object({\n /** Seconds the answer stays open once it has been spoken; 0 keeps\n * it open until closed. */\n timeout: z.number().min(0).max(600).default(30),\n /** Tallest the answer grows, in points, before it scrolls. */\n maxHeight: z.number().min(120).max(800).default(320),\n }).default({}),\n }).default({}),\n business: businessConfigSchema.optional(),\n boards: boardsConfigSchema,\n dashboard: dashboardConfigSchema,\n graph: graphConfigSchema,\n decisions: decisionsConfigSchema,\n /** Workflow engine — declarative state machines that bind channel events\n * to agents. Off by default; existing installs see no change until\n * flipped. Definitions live under `dir` (default .agentx/workflows/). */\n workflows: z.object({\n enabled: z.boolean().default(false),\n dir: z.string().default(\".agentx/workflows\"),\n /** n8n keeps its several hundred connectors; we keep the agents. Point\n * this at your n8n and the workflow builder lists YOUR n8n workflows as\n * steps, so nobody has to copy a webhook URL by hand. */\n n8n: z.object({\n baseUrl: z.string().default(\"\"),\n apiKey: z.string().default(\"\"),\n }).default({ baseUrl: \"\", apiKey: \"\" }),\n matching: z.object({\n enabled: z.boolean().default(false),\n mode: z.enum([\"suggest\", \"auto\"]).default(\"suggest\"),\n autoRunThreshold: z.number().min(0).max(1).default(0.85),\n suggestThreshold: z.number().min(0).max(1).default(0.65),\n }).default({}),\n /** Controls whether the dashboard exposes the visual editor. \"readonly\"\n * serves the list + run timelines but strips write controls from the\n * page. \"disabled\" hides the tab entirely. */\n editor: z.enum([\"disabled\", \"readonly\", \"edit\"]).default(\"edit\"),\n }).default({}),\n /** Procedures — user-perspective SOPs mined from recurring activity.\n * The extraction cadence itself lives in `crons.procedure-extract`\n * (written by `agentx procedure watch`); this block holds the miner's\n * thresholds and the injection gates the daemon reads. */\n procedures: z.object({\n enabled: z.boolean().default(true),\n dir: z.string().default(\".agentx/procedures\"),\n extraction: z.object({\n enabled: z.boolean().default(false),\n /** Count patterns live after each completed task (no LLM cost). */\n onTaskCompletion: z.boolean().default(false),\n minOccurrences: z.number().int().min(2).default(3),\n sinceDays: z.number().int().min(1).default(7),\n maxClusters: z.number().int().min(1).default(5),\n /** Agent whose session distills drafts (LLM path). */\n via: z.string().optional(),\n }).default({}),\n /** Inject matching procedures into fresh agent sessions as guidance. */\n injection: z.object({\n enabled: z.boolean().default(true),\n maxProcedures: z.number().int().min(1).default(2),\n minScore: z.number().min(0).max(1).default(0.5),\n }).default({}),\n }).default({}),\n /** Registered inbound webhooks — an inventory the dashboard manages. Each\n * entry binds an (agent, source) pair to an optional signing secret. The\n * actual inbound URL is always POST /webhook/<agentId>/<source>. */\n webhooks: z.array(z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"webhook id must be lowercase slug\"),\n // Open-string source — runtime + admin-panel + CLI all share a\n // canonical list (kept in sync via WEBHOOK_SOURCES). Loosened from a\n // strict enum so adding a new first-class service (the 5-step recipe)\n // doesn't require a schema bump in three places. Doctor + admin\n // validator still gate writes on the canonical list.\n source: z.string().min(1),\n agentId: z.string(),\n secretEnv: z.string().optional(),\n description: z.string().optional(),\n enabled: z.boolean().default(true),\n /** Route to a mesh peer instead of local execution. */\n node: z.string().optional(),\n /** Per-event-type workflow routing. Keys are platform event-type\n * strings — the format the platform's HTTP header uses, optionally\n * joined with the action. Examples:\n * GitHub: \"issues.opened\", \"pull_request.synchronize\",\n * \"push\" (action-less events use the bare type)\n * GitLab: \"Note Hook\", \"Merge Request Hook\"\n * Values are workflow ids registered via `agentx workflow create`.\n * When the inbound event-type matches a key, the named workflow is\n * dispatched; if no key matches, the webhook falls through to\n * `defaultWorkflow` (or the agent itself). Closes the recurring\n * GitHub problem of multiple event types collapsing to a single\n * workflow. */\n triggers: z.record(z.string(), z.string()).optional(),\n /** Workflow id used when no `triggers` entry matches the event-type.\n * Backward-compatible: pre-existing webhooks without `triggers`\n * always hit this path. */\n defaultWorkflow: z.string().optional(),\n })).default([]),\n /** Session cache-reuse policy. Controls when we drop a Claude `--resume`\n * session and rebuild the prompt from scratch. Every rotation captures a\n * continuity memo that's injected into the chat's next fresh session.\n * - `staleMinutes`: idle timeout before rotation. 12 h default — chat\n * conversations must survive a workday's pauses; overnight silence\n * starts fresh.\n * - `maxTurnsPerSession`: backstop cap on turns per Claude session.\n * Size-based rotation measures real context now, so this rarely fires\n * first.\n * - `tierTwoThresholdTokens`: rotate when the prior turn's END-OF-TURN\n * per-request context (last API call's input + cacheRead + cacheCreate)\n * reaches this. Claude bills the 1.5× long-context rate above 200K\n * per REQUEST — 180K leaves headroom. NOT the cumulative turn total:\n * that sums cache reads across every call and reads 10-20× too high. */\n session: z.object({\n staleMinutes: z.number().int().min(1).max(1440).default(720),\n maxTurnsPerSession: z.number().int().min(2).max(200).default(40),\n tierTwoThresholdTokens: z.number().int().min(50_000).max(200_000).default(180_000),\n /** Per-channel context rotation limits; omitted channels use the global limit. */\n tierTwoThresholdTokensByChannel: z.record(z.number().int().min(50_000).max(200_000)).default({}),\n /** Context assembly strategy:\n * - \"layered\" (default): the classic stacked layers — session history,\n * memory, cross-chat, wiki hint all appended every turn.\n * - \"planner\": a Haiku pre-call decides what to retrieve before the\n * main agent runs. Always-on core is kept (channel/scope/landscape/\n * identity/intent + last 3 turns verbatim); history blob, wiki hint,\n * and cross-chat are replaced by planner-selected bundles. Per-task\n * overrides via AgentTask.contextStrategy for A/B benchmarking. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).default(\"layered\"),\n /** Optional hard ceilings on new claude-code runs across the fleet (all\n * claude-code agents share one Claude subscription, so one counter).\n * The plan's own limit is read from Claude Code's rate_limit_event and\n * gates cold dispatches on its own; these caps sit on top for operators\n * who want a local ceiling. Unset or 0 = off. Warm sessions always go\n * through. Changing them applies on save, no restart. */\n maxClaudeCodeDispatchesPerHour: z.number().int().min(0).max(10_000).optional(),\n maxClaudeCodeDispatchesPer5h: z.number().int().min(0).max(50_000).optional(),\n /** How many requests BEFORE the immediate predecessor to show the\n * session-continuity seat. 0 keeps today's two-message state exactly.\n *\n * Off by default on purpose. A probe over 27 hand-written turns found\n * that at 3, the seat's safety question (`needsHistory`) becomes\n * cleanly separable where two messages leave it straddling zero — but\n * that rests on 7 positive examples from synthetic scenarios, and this\n * seat's failure mode is the agent silently losing conversation\n * context. The promotion gate for that wants labelled production rows,\n * not a good afternoon on a fixture. Raise it to collect them. */\n continuityStateTurns: z.number().int().min(0).max(5).default(0),\n /** How much a session is given when it starts, per channel (#615).\n * `full` is the classic start. `lean` starts Claude Code with only\n * the `agentx` MCP server (no user-level connectors), only the\n * project's settings (no global CLAUDE.md, user skills or plugins),\n * and asks for the landscape, chat history and cross-chat context\n * through MCP tools instead of pushing them into the prompt. A\n * channel missing here keeps the built-in default: github, a2a,\n * workflow and cron are lean; every other channel is full. Only\n * claude-code and codex-cli agents have a lean start; other tiers\n * are always full. See src/agents/session-profile.ts. */\n profileByChannel: z.record(z.enum([\"full\", \"lean\"])).default({}),\n /** What a lean start keeps. */\n lean: z.object({\n /** MCP servers from the workspace's .mcp.json kept in a lean\n * session, by name. `agentx` is always added. */\n mcpServers: z.array(z.string().min(1)).default([\"agentx\"]),\n /** Claude Code setting sources a lean session reads. Without `user`,\n * the global CLAUDE.md, user skills, plugins and user-level MCP\n * connectors are not loaded. `project` is the workspace. */\n settingSources: z.array(z.enum([\"user\", \"project\", \"local\"])).default([\"project\", \"local\"]),\n /** Replace the pushed landscape, chat history and cross-chat\n * context with one line naming the tools that fetch them. */\n contextOnDemand: z.boolean().default(true),\n /** Built-in Claude Code tools a lean session gets, passed as\n * `--tools` next to `--strict-mcp-config` (#615). Empty, the\n * default, keeps every built-in tool. The tool schemas are about\n * 14k tokens of a first turn, so a short list is what brings a lean\n * start under 20k; an agent that lacks a tool it needs fails\n * mid-task, so this stays opt-in. The agentx MCP tools are not\n * affected: ToolSearch is always added to the list, so they stay\n * deferred. claude-code agents only. */\n tools: z.array(z.string().min(1)).default([]),\n /** The same per channel; a channel's non-empty list wins over\n * `tools`. */\n toolsByChannel: z.record(z.array(z.string().min(1))).default({}),\n /** The `agentx_` MCP tools a lean session lists (#699). Empty, the\n * default, lists every agentx tool. Claude Code already defers\n * them while ToolSearch is available, so a list saves only their\n * names (about 300 tokens) unless tool search is off. A tool left\n * off cannot be found even through ToolSearch. `agentx_approval`,\n * `agentx_request`, `agentx_events` and `agentx_attach_next` are\n * always kept, and so are `agentx_agents`, `agentx_recent` and\n * `agentx_wiki_query` while `contextOnDemand` is on. A name that is\n * not an agentx tool is rejected, so a typo cannot silently drop a\n * reply tool. Only an agentx server started as a command (stdio)\n * can be told; an http entry lists every tool. claude-code agents\n * only. */\n agentxTools: agentxToolListSchema.default([]),\n /** The same per channel; a channel's non-empty list wins over\n * `agentxTools`. */\n agentxToolsByChannel: z.record(agentxToolListSchema).default({}),\n }).default({}),\n /** A cheaper model for runs started only by triage events (#615): a\n * GitHub label added or removed, an issue or PR closed. A run that\n * collapsed any other action (\"opened, labeled\") keeps the agent's\n * model, and so does a follow-up whose session ran within the last\n * hour (swapping models drops the prompt cache). A model set on the\n * task itself, such as a cron job's, wins. Off until a model is set\n * for the agent's engine. See triageModelFor in src/agents/routing.ts. */\n triage: z.object({\n /** The model per CLI engine: a Claude model for claude-code agents,\n * an OpenAI model for codex-cli agents. Unset: no triage model. */\n models: z.object({\n \"claude-code\": z.string().min(1).optional(),\n \"codex-cli\": z.string().min(1).optional(),\n }).default({}),\n /** Event actions that count as triage. An action only reaches an\n * agent when `channels.github.issueActions` /\n * `pullRequestActions` (or a project rule) let it through. */\n actions: z.array(z.string().min(1)).default([\"labeled\", \"unlabeled\", \"closed\"]),\n }).default({}),\n /** Longest the agent-memory index (MEMORY.md) may be where it is\n * loaded on every session: merged into each workspace's CLAUDE.md\n * and inlined in the system prompt (#615). 0, the default, keeps the\n * whole index. Cut whole lines only; a closing line counts the\n * entries left out and points at `.agentx-memory.md`, which always\n * holds the full index, and at `agentx memory index`. Applies to the\n * prompt on save; the CLAUDE.md block follows at the next daemon\n * start or memory change. */\n memoryIndexMaxChars: z.number().int().min(0).max(200_000).default(0),\n /** ObservationPack (#621). A large tool result stays in the context\n * and is re-read on every later request. With this on, a Claude Code\n * PostToolUse hook saves a text result over `limitBytes` to\n * `.agentx/observations/<agent id>/` and shows the model its first and last\n * bytes plus the path of the saved original, which it can Read or\n * grep. Off by default. claude-code agents only. Turning it on, or\n * changing `tools`, takes a daemon restart (the hook is written into\n * each workspace's .claude/settings.json at start); turning it off\n * applies on save. See src/agents/observation-pack.ts. */\n observationPack: z.object({\n enabled: z.boolean().default(false),\n /** A text result larger than this many bytes is packed. */\n limitBytes: z.number().int().min(1024).max(1_048_576).default(10_240),\n /** Bytes of the start and of the end of the original the model sees. */\n headBytes: z.number().int().min(0).max(65_536).default(1024),\n tailBytes: z.number().int().min(0).max(65_536).default(1024),\n /** Tools the pack applies to. Each entry must match the whole tool\n * name and may be a regular expression. `Read` is in by default\n * (owner decision on #621, 2026-10-05): a file read over the limit\n * is cut to its first and last lines, with the file's own path and\n * line numbers, and the agent reads the lines it needs again with\n * offset and limit. No copy of the file is saved. Take `Read` out\n * of the list for an agent that edits large files all day. */\n tools: z.array(z.string().min(1)).default([\"Bash\", \"Grep\", \"Read\", \"WebFetch\", \"mcp__.*\"]),\n /** Days a saved original is kept. 0 keeps every original. */\n retentionDays: z.number().int().min(0).max(3650).default(0),\n }).default({}),\n }).default({}),\n /** Move B — JS/TS plugins. Each entry is an installed npm package name\n * (e.g. `agentx-plugin-mattermost` or `@acme/plugin-mattermost`); the\n * loader does a dynamic `import(name)` at boot, validates the manifest,\n * and calls plugin.setup(ctx). Plugins can register channel adapters\n * via ctx.addChannel() and subscribe to bus events via ctx.on(). Empty\n * default — installs that don't list plugins are unaffected. Plugin\n * authoring guide: docs/architecture/plugins.md. */\n plugins: z.array(z.string()).default([]),\n /** Persistent-process pool eviction knobs (handoff #8). The pool keeps\n * one warm Claude subprocess per (agent, channel, chatId) so latency\n * stays low; without bounds, an idle process can serve traffic days\n * later with stale context. These knobs cap that risk. All values are\n * in seconds for operator-friendliness; the registry uses ms internally.\n *\n * - `maxIdleSeconds`: a handle becomes eligible for cap-pressured\n * eviction this many seconds after its last turn. Default 30s\n * (matches the registry's pre-config behavior).\n * - `maxAgeSeconds`: a handle is killed unconditionally on the next\n * sweep once its total wall-clock idleness exceeds this. Default\n * 2700s (45min) — matches the registry's pre-config staleTimeoutMs.\n * - `sweepIntervalSeconds`: how often the sweeper checks. Default 5s.\n *\n * See `agents.<id>.persistentProcess` to opt an agent into the pool. */\n processPool: z.object({\n maxIdleSeconds: z.number().int().min(5).max(86_400).default(30),\n maxAgeSeconds: z.number().int().min(60).max(86_400).default(2700),\n sweepIntervalSeconds: z.number().int().min(1).max(300).default(5),\n }).default({}),\n})\n\nexport type DaemonConfig = z.infer<typeof daemonConfigSchema>\nexport type AgentDef = z.infer<typeof agentConfigSchema>\nexport type EventSubscription = z.infer<typeof eventSubscriptionSchema>\nexport type CronJobDef = z.infer<typeof cronJobSchema>\nexport type MeshPeer = z.infer<typeof meshPeerSchema>\n\n/**\n * Expand environment variables in strings: ${VAR_NAME} -> process.env.VAR_NAME\n */\nexport function expandEnvVars(obj: unknown): unknown {\n if (typeof obj === \"string\") {\n return obj.replace(/\\$\\{(\\w+)\\}/g, (_match, name) => process.env[name] || \"\")\n }\n if (Array.isArray(obj)) {\n return obj.map(expandEnvVars)\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = expandEnvVars(value)\n }\n return result\n }\n return obj\n}\n\n/**\n * Translate raw Zod issues into actionable, copy-pasteable fixes for the\n * patterns we see in real first-run failures (missing tokens, missing\n * required env vars, etc.). Falls through silently for unknown patterns —\n * the raw `issues` block is always shown above this hint block.\n */\nfunction friendlyConfigHints(issues: ReadonlyArray<z.ZodIssue>, configPath: string): string[] {\n const hints: string[] = []\n const seen = new Set<string>()\n for (const issue of issues) {\n const path = issue.path.join(\".\")\n // Telegram account block exists but token is empty/missing — by far the\n // most common first-run trip. Tell the operator exactly which two\n // recoveries are valid, with the literal config path to edit.\n const tgTokenMatch = /^channels\\.telegram\\.accounts\\.([^.]+)\\.token$/.exec(path)\n if (tgTokenMatch && issue.message.toLowerCase().includes(\"required\")) {\n const account = tgTokenMatch[1]\n const key = `tg:${account}`\n if (seen.has(key)) continue\n seen.add(key)\n hints.push(\n `Hint: Telegram account \"${account}\" is enabled but has no bot token.\\n` +\n ` Either:\\n` +\n ` A) Open ${configPath} and set channels.telegram.accounts.${account}.token to your bot token (or \"\\${TG_${account.toUpperCase()}_BOT_TOKEN}\" + the matching .env entry).\\n` +\n ` B) If you don't need Telegram on this instance, remove the entire \"telegram\" block under \"channels\" (or set channels.telegram.enabled to false AND drop accounts.${account}).`\n )\n }\n }\n return hints\n}\n\n/**\n * Load daemon config from agentx.json, with env var expansion and validation.\n */\nexport function loadDaemonConfig(configPath?: string): DaemonConfig {\n const paths = configPath\n ? [configPath]\n : [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n\n // Load .env from same directory as config search\n loadDotEnv(process.cwd())\n\n let raw: string | undefined\n let foundPath: string | undefined\n\n for (const p of paths) {\n if (existsSync(p)) {\n raw = readFileSync(p, \"utf-8\")\n foundPath = p\n break\n }\n }\n\n if (!raw || !foundPath) {\n throw new Error(\n `No config found. Create agentx.json or .agentx/config.json\\n` +\n `Searched: ${paths.join(\", \")}`\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (e: any) {\n throw new Error(`Invalid JSON in ${foundPath}: ${e.message}`)\n }\n\n // Expand environment variables\n const expanded = expandEnvVars(parsed)\n\n // Validate\n const result = daemonConfigSchema.safeParse(expanded)\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\")\n const hints = friendlyConfigHints(result.error.issues, foundPath)\n const hintBlock = hints.length ? `\\n\\n${hints.join(\"\\n\\n\")}` : \"\"\n throw new Error(`Config validation failed (${foundPath}):\\n${issues}${hintBlock}`)\n }\n\n return result.data\n}\n\n/**\n * Validate agent workspace directories exist and have .claude/ setup.\n */\nexport function validateWorkspaces(config: DaemonConfig): string[] {\n const warnings: string[] = []\n\n for (const [id, agent] of Object.entries(config.agents)) {\n if (!existsSync(agent.workspace)) {\n warnings.push(`Agent \"${id}\": workspace not found at ${agent.workspace}`)\n continue\n }\n\n if (agent.tier === \"claude-code\") {\n const claudeDir = resolve(agent.workspace, \".claude\")\n if (!existsSync(claudeDir)) {\n warnings.push(\n `Agent \"${id}\": no .claude/ directory in workspace ${agent.workspace}. ` +\n `Claude Code native features (hooks, MCP, skills) won't be available.`\n )\n }\n }\n\n // Check provider availability\n const providerName = agent.provider || \"claude\"\n const providerConfig = config.providers[providerName]\n if (![\"claude-code\", \"codex-cli\", \"opencode\"].includes(agent.tier) && (!providerConfig || !providerConfig.apiKey)) {\n warnings.push(\n `Agent \"${id}\": provider \"${providerName}\" has no API key configured. ` +\n `Set providers.${providerName}.apiKey in config or use a CLI-backed tier (\"claude-code\", \"codex-cli\", or \"opencode\").`\n )\n }\n }\n\n // Validate cron agent bindings\n for (const [id, cron] of Object.entries(config.crons)) {\n if (!config.agents[cron.agent]) {\n warnings.push(`Cron \"${id}\": references unknown agent \"${cron.agent}\"`)\n }\n }\n\n // Validate channel agent bindings\n if (config.channels.telegram.enabled) {\n for (const [name, account] of Object.entries(config.channels.telegram.accounts)) {\n if (!config.agents[account.agentBinding]) {\n warnings.push(\n `Telegram account \"${name}\": references unknown agent \"${account.agentBinding}\"`\n )\n }\n }\n }\n\n return warnings\n}\n"],"mappings":"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","names":["z","dayEnum","scheduleSchema","roleSchema","orgEntrySchema","channelRefSchema","workSourceSchema","projectSchema","contactMapSchema","clientPolicySchema","businessConfigSchema","init_config","__esmMin","z","deriveStage","item","columns","set","closed","c","backlog","prefix","transitionDiff","fromColumn","toColumn","add","remove","closeIssue","reopen","boardLabelSchema","boardColumnSchema","boardReconcileSchema","boardSourceSchema","DEFAULT_COLUMNS","boardSchema","boardsConfigSchema","dashboardDaemonSchema","dashboardConfigSchema","init_config","__esmMin","parseHotkey","input","raw","parts","p","key","KEY_ALIASES","mods","m","MODIFIERS","isKey","isFKey","ORDER","hotkeyError","r","describeHotkey","value","sym","names","NAMED_KEYS","DEFAULT_HOTKEYS","init_hotkey","__esmMin","k","hueOf","hex","m","r","g","b","x","max","min","d","paletteForColor","hue","ORB_PALETTES","p","best","bestDistance","distance","agentPalette","presence","fallback","DEFAULT_PALETTE","byId","id","picked","color","VOICE_LOOKS","VOICE_ANIMATIONS","ORB_PALETTE_IDS","init_orb_palettes","__esmMin","detectLanguage","text","arabic","latin","en","fr","w","EN","FR","ACCENT","init_language","__esmMin","toSpeakable","text","max","SPOKEN_MAX_CHARS","s","match","head","end","MORE","detectLanguage","init_speakable","__esmMin","init_language","isNoiseTranscript","text","markers","NOISE_MARKERS","known","key","rest","group","square","round","NOISE_REPLY","init_noise","__esmMin","s","z","hhmm","watchRuleSchema","whatsappTriageSchema","init_config","__esmMin","ensureRequestTables","db","ensureColumns","PERSON_COLUMNS","cols","name","type","c","e","hasRequestTables","toRecord","OPEN_STATES","CLOSED_STATES","TEXT_MAX","REASON_MAX","placeholders","RequestStore","init_store","__esmMin","init_sqlite","n","input","id","row","kind","ref","requestId","now","state","question","reason","detail","text","states","before","agentId","note","said","r","a","b","pickupContext","requestId","PICKUP_CHANNEL","PICKUP_MARK","isPickup","ctx","isOwnerTurn","settings","channel","sender","operator","ch","base","OPERATOR_CHANNELS","DAEMON_SENDERS","raw","FORGES","who","comparable","entry","e","i","senderOf","pick","v","failureWord","error","clip","chatKey","RequestTracker","init_tracker","__esmMin","init_store","s","flat","agentId","chatId","store","log","now","what","fn","p","channels","earlier","key","pickup","req","word","d","turn","status","text","card","applied","delegationId","OPEN_STATES","o","randomBytes","timingSafeEqual","chmodSync","existsSync","mkdirSync","readFileSync","writeFileSync","networkInterfaces","dirname","resolve","operatorContext","ctx","OPERATOR_MARK","isOperatorTurn","operatorKeyPath","root","loadOperatorKey","opts","path","key","operatorKeyMatches","presented","p","b","operatorVouch","context","headers","operatorChannels","channel","OPERATOR_HEADER","peerVouches","peerTokens","v","OPERATOR_VOUCH_HEADER","auth","h","bearer","t","a","isThisMachine","remoteAddress","own","ownAddresses","addr","plainAddress","list","i","ownerProven","req","node","init_operator","__esmMin","personName","people","id","p","IMPLICIT_OWNER","normalize","channel","value","v","splitIdentity","identity","i","base","peopleProblem","ids","claimed","parts","key","other","resolvePerson","sender","ch","operatorPerson","owners","personOfTurn","ctx","root","propagatedRootOf","classifyInitiator","OPERATOR_CHANNELS","isOperatorTurn","str","person","agentAllowed","agentId","list","refusedPerson","ref","nameMatches","pattern","name","re","s","personLimitsOf","tools","skills","PERSON_ROLES","init_people","__esmMin","init_initiator","init_tracker","init_operator","unknownAgentxTools","list","known","AGENTX_TOOL_NAMES","init_tool_names","__esmMin","config_exports","__export","INTEGRATION_KINDS","approvalsConfigSchema","callsSchema","cameraBotSchema","daemonConfigSchema","eventSubscriptionSchema","expandEnvVars","loadDaemonConfig","membersConfigSchema","peopleConfigSchema","personSchema","requestStatusConfigSchema","requestsConfigSchema","validateWorkspaces","z","readFileSync","existsSync","resolve","loadDotEnv","dir","envPath","content","line","trimmed","eqIdx","key","value","obj","_match","name","result","friendlyConfigHints","issues","configPath","hints","seen","issue","path","tgTokenMatch","account","paths","raw","foundPath","p","parsed","e","expanded","i","hintBlock","config","warnings","id","agent","claudeDir","providerName","providerConfig","cron","agentxToolListSchema","providerConfigSchema","mcpStdioServerSchema","mcpHttpServerSchema","mcpServerSchema","integrationCredentialsSchema","integrationSchema","voiceProviderSchema","systemVoiceSchema","hotkeySchema","voiceSchema","agentConfigSchema","telegramAccountSchema","channelsConfigSchema","cronJobSchema","serviceSchema","meshPeerSchema","meshInboxSchema","meshConfigSchema","graphConfigSchema","decisionSeatSchema","decisionsConfigSchema","notificationsSchema","screenRectSchema","screenSchema","init_config","__esmMin","init_autonomy","init_hotkey","init_orb_palettes","init_speakable","init_noise","init_people","init_tool_names","list","ctx","unknown","unknownAgentxTools","s","error","hotkeyError","ORB_PALETTE_IDS","autonomyLevelSchema","v","j","people","problem","peopleProblem","tz","whatsappTriageSchema","SPOKEN_MAX_CHARS","NOISE_MARKERS","DEFAULT_HOTKEYS","VOICE_LOOKS","VOICE_ANIMATIONS","DEFAULT_PALETTE","businessConfigSchema","boardsConfigSchema","dashboardConfigSchema"]}
|
|
1
|
+
{"version":3,"sources":["../src/business/config.ts","../src/boards/config.ts","../src/voice/hotkey.ts","../src/voice/orb-palettes.ts","../src/voice/language.ts","../src/voice/speakable.ts","../src/voice/noise.ts","../src/whatsapp-triage/config.ts","../src/requests/store.ts","../src/requests/tracker.ts","../src/requests/operator.ts","../src/people/people.ts","../src/mcp/tool-names.ts","../src/daemon/config.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Business layer configuration schema (EXPERIMENTAL) ---\n// Optional top-level `business` key in agentx.json.\n// When enabled, AgentX simulates a team with roles, schedules, and a work pool.\n// Experimental: off by default, undocumented while the model settles —\n// config shape may change between minor versions without migration.\n\nconst dayEnum = z.enum([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"])\n\nconst scheduleSchema = z.object({\n days: z.array(dayEnum).default([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"]),\n start: z.string().regex(/^\\d{2}:\\d{2}$/), // \"09:00\"\n end: z.string().regex(/^\\d{2}:\\d{2}$/), // \"17:00\"\n lunch: z.object({\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n }).optional(),\n})\n\nconst roleSchema = z.object({\n title: z.string(),\n responsibilities: z.array(z.string()).default([]),\n sopPath: z.string().optional(),\n kpis: z.array(z.string()).default([]),\n})\n\nconst orgEntrySchema = z.object({\n role: z.string(),\n reportsTo: z.string().optional(),\n schedule: scheduleSchema,\n utilizationTarget: z.number().min(0).max(1).default(0.8),\n})\n\nconst channelRefSchema = z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n})\n\nconst workSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"backlog\"),\n path: z.string().default(\".agentx/backlog.md\"),\n }),\n z.object({\n type: z.literal(\"gitlab\"),\n projects: z.array(z.string()).default([]), // empty = all configured gitlab projects\n }),\n z.object({\n type: z.literal(\"wiki\"),\n path: z.string(),\n glob: z.string().default(\"**/*.md\"),\n }),\n])\n\n/** Per-project metadata. Phase 3 of the architectural rescue —\n * `pm` is the agentId responsible for gating dispatches on this\n * project. Optional; when null, dispatches don't go through a PM\n * gate. The wiring lives in `decideAndCommit` (gated by a flag,\n * separate commit). */\nconst projectSchema = z.object({\n /** Project identifier. For GitLab/GitHub: \"owner/repo\". For\n * acme-internal projects: a stable string. */\n id: z.string(),\n /** AgentId of the PM for this project. Looked up by\n * `Organization.pmFor(project)` at dispatch time. */\n pm: z.string().optional(),\n /** Optional client this project belongs to. Used by the activity\n * graph to attribute non-projected events (Telegram chats, free-\n * text WhatsApp, cron jobs) to the right client. Falls back to\n * the leading segment of `id` (split on \"/\") when unset. */\n client: z.string().optional(),\n})\n\n/** Maps a chat or sender to a client/project so free-text channels\n * (telegram/whatsapp/slack/discord/email) get attributed correctly\n * in the activity graph. Without this, every chat lands in the\n * fallback \"internal\" bucket — the operator can't tell which client\n * the agent was helping.\n *\n * Match priority: chatId > username > channel-default. First match\n * wins. All fields except `client` are optional; `project` falls\n * back to `${client}/_chat` when unset. */\nconst contactMapSchema = z.object({\n /** Channel kind, e.g. \"telegram\", \"whatsapp\", \"slack\", \"discord\". */\n channel: z.string().optional(),\n /** Native chat id (\"-100...\" for tg groups, JID for WhatsApp). */\n chatId: z.string().optional(),\n /** Sender username/handle as exposed by the channel adapter. */\n username: z.string().optional(),\n /** Numeric sender id when username is unstable. */\n senderId: z.string().optional(),\n /** Client this contact belongs to. Required. */\n client: z.string(),\n /** Project this contact's traffic should attribute to. Defaults to\n * `${client}/_chat`. */\n project: z.string().optional(),\n /** Display name override for the initiator pill. */\n displayName: z.string().optional(),\n})\n\n/** Per-client policy. Clients are DERIVED from `projects` and `contactMap`\n * — one exists the moment either mentions it — so nothing needs declaring\n * up front. This record only carries overrides for the two things a\n * heuristic cannot know: how fast a delay costs you, and what an agent may\n * do for this client without asking first. */\nconst clientPolicySchema = z.object({\n /** Display name. Defaults to the client id. */\n name: z.string().optional(),\n /** \"client\" is someone who is waiting on you and can be let down; \"own\"\n * is your own product; \"internal\" is machinery with no external clock. */\n kind: z.enum([\"client\", \"internal\", \"own\"]).optional(),\n /** How long a wait may last before it starts costing: \"4h\", \"90m\", \"2d\".\n * Unset means no external clock, so delay is flat. */\n respondWithin: z.string().optional(),\n /** Capabilities an agent may exercise unattended. \"*\" means all. Ignored\n * for kind \"client\" — see mayProceedUnattended(). */\n standing: z.array(z.string()).default([]),\n})\n\nexport const businessConfigSchema = z.object({\n enabled: z.boolean().default(false),\n timezone: z.string().default(\"UTC\"),\n mainChannel: channelRefSchema,\n workSource: workSourceSchema,\n roles: z.record(z.string(), roleSchema).default({}),\n orgChart: z.record(z.string(), orgEntrySchema).default({}),\n /** Per-project metadata. Phase 3 — defaults to empty array, so\n * existing agentx.json files validate without modification. */\n projects: z.array(projectSchema).default([]),\n /** Maps free-text chat senders to a client/project so the activity\n * graph can attribute Telegram/WhatsApp/etc. to the right client\n * instead of the catch-all \"internal\" bucket. */\n contactMap: z.array(contactMapSchema).default([]),\n /** Overrides for derived clients. Keyed by client id; absent entries get\n * defaults, so this stays empty until you need a clock or an\n * authorization. */\n clients: z.record(z.string(), clientPolicySchema).default({}),\n /** Work-tick cadence in minutes during business hours. Default 15. */\n workTickMinutes: z.number().int().min(1).max(60).default(15),\n /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */\n idleQueueThreshold: z.number().int().min(0).default(0),\n /** Standup-tick gating + plan-driven priorities. The day-cycle's morning\n * STANDUP used to fire unconditionally per agent — operators reported it\n * as \"mechanical\" because it dispatched a Claude turn even when no human\n * had set a plan for the day. Now: the cycle reads\n * .agentx/plans/<date>.md (day → week → month fallback) and either\n * injects the plan as standup priorities, or posts a \"no plan today\"\n * notification to mainChannel and dispatches no agent. */\n standup: z.object({\n /** Master switch. When false, fireStandup is a no-op (silent). */\n enabled: z.boolean().default(true),\n /** Plans directory, relative to the daemon CWD. */\n plansDir: z.string().default(\".agentx/plans\"),\n /** Hard ceiling on how many agents the standup tick can dispatch in a\n * single morning — guards against an org-chart misconfiguration\n * fanning out to dozens of Claude calls before anyone notices. */\n maxAgentsPerDay: z.number().int().min(1).max(100).default(20),\n /** When true, even with a plan present the cycle won't dispatch — only\n * posts the resolved plan to mainChannel for human review. Useful as\n * a kill-switch while iterating on plan formats. */\n dryRun: z.boolean().default(false),\n }).default({}),\n})\n\nexport type BusinessConfig = z.infer<typeof businessConfigSchema>\nexport type BusinessRole = z.infer<typeof roleSchema>\nexport type BusinessOrgEntry = z.infer<typeof orgEntrySchema>\nexport type BusinessSchedule = z.infer<typeof scheduleSchema>\nexport type BusinessWorkSource = z.infer<typeof workSourceSchema>\nexport type BusinessChannelRef = z.infer<typeof channelRefSchema>\nexport type BusinessProject = z.infer<typeof projectSchema>\n","import { z } from \"zod\"\n\n// --- Kanban board configuration ---\n//\n// Top-level optional keys in agentx.json, independent of the business layer:\n//\n// boards: array of kanban boards, each backed by a WorkSource\n// dashboard: HTTP server controlling whether the board UI serves\n//\n// Both default to empty / disabled so existing installs see no change.\n\nexport const boardLabelSchema = z.object({\n /** Label name — used verbatim as the GitLab label string. */\n name: z.string(),\n color: z.string().regex(/^#[0-9a-f]{6}$/i).default(\"#6366f1\"),\n description: z.string().optional(),\n})\n\n/**\n * A board column. `kind` controls what issues land here and how drag-to-this-column\n * mutates the issue:\n *\n * - \"open-backlog\": opened issues that have NO scoped label matching `scopedPrefix`\n * (default \"Status\"). Dragging INTO this column removes any existing scoped label.\n * - \"scoped-label\": opened issues carrying `scopedLabel` (e.g. \"Status::Doing\").\n * Scoped labels are mutually exclusive by prefix, so adding the new one lets\n * GitLab auto-remove the old one (Premium), but we still explicitly strip the\n * previous column's label for compatibility.\n * - \"closed\": all closed issues. Dragging here closes the issue; dragging away\n * reopens it.\n * - \"label\" (default, legacy): opened issues with `mapsToLabel`; flat add/remove.\n */\nexport const boardColumnSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"column id must be lowercase slug\"),\n title: z.string(),\n kind: z.enum([\"open-backlog\", \"scoped-label\", \"closed\", \"label\"]).default(\"label\"),\n /** For kind=\"label\": label written on entry, removed on exit. */\n mapsToLabel: z.string().optional(),\n /** For kind=\"scoped-label\": the full scoped label (e.g. \"Status::Doing\"). */\n scopedLabel: z.string().optional(),\n /** For kind=\"open-backlog\": scoped prefix that marks \"claimed\" cards to exclude. */\n scopedPrefix: z.string().default(\"Status\"),\n /** CSS color or hex — shown as the column's top accent bar. */\n accent: z.string().optional(),\n})\nexport type BoardColumnId = string\n\nexport const boardReconcileSchema = z.object({\n enabled: z.boolean().default(true),\n staleDoingMinutes: z.number().int().min(1).default(45),\n respectLunchBreak: z.boolean().default(true),\n respectSchedule: z.boolean().default(true),\n /** Badge only for P1/P3; auto-notify + auto-demote deferred. */\n action: z.enum([\"badge\", \"notify\"]).default(\"badge\"),\n})\n\nexport const boardSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"gitlab\"),\n /** GitLab project paths (e.g. \"globex/globex-system-v2\"). At least one required. */\n projects: z.array(z.string()).min(1),\n }),\n // future: backlog, wiki\n])\n\n/**\n * GitLab-style default board — six columns:\n * Open (backlog) → To Do → Doing → On Hold → Review → Closed\n *\n * Drives the `Status::*` scoped label taxonomy. Moving a card adds/removes the\n * column's scoped label; moving INTO Closed closes the issue; moving OUT of\n * Closed reopens it; moving INTO Open strips any Status::* label.\n */\nexport const DEFAULT_COLUMNS: Array<z.infer<typeof boardColumnSchema>> = [\n { id: \"open\", title: \"Open\", kind: \"open-backlog\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n { id: \"todo\", title: \"To Do\", kind: \"scoped-label\", scopedLabel: \"Status::To Do\", scopedPrefix: \"Status\", accent: \"#fb7a35\" },\n { id: \"doing\", title: \"Doing\", kind: \"scoped-label\", scopedLabel: \"Status::Doing\", scopedPrefix: \"Status\", accent: \"#22c55e\" },\n { id: \"onhold\", title: \"On Hold\", kind: \"scoped-label\", scopedLabel: \"Status::On Hold\", scopedPrefix: \"Status\", accent: \"#f59e0b\" },\n { id: \"review\", title: \"Review\", kind: \"scoped-label\", scopedLabel: \"Status::Review\", scopedPrefix: \"Status\", accent: \"#3b82f6\" },\n { id: \"closed\", title: \"Closed\", kind: \"closed\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n]\n\nexport const boardSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"board id must be lowercase slug\"),\n name: z.string(),\n source: boardSourceSchema,\n /** If set, ANDed into every listAll query and shown as a baseline chip in the UI. */\n primaryToolLabel: z.string().optional(),\n labels: z.array(boardLabelSchema).default([]),\n columns: z.array(boardColumnSchema).min(1).default(DEFAULT_COLUMNS),\n timeRangeDays: z.number().int().min(1).max(365).default(30),\n /** Maximum closed-issue window (days) shown in the Closed column. */\n closedWindowDays: z.number().int().min(1).max(365).default(30),\n reconciliation: boardReconcileSchema.default({}),\n})\n\nexport const boardsConfigSchema = z.array(boardSchema).default([])\n\nexport const dashboardDaemonSchema = z.object({\n name: z.string(),\n /** Daemon HTTP URL (the one serving /agents, /health, /task). */\n url: z.string(),\n /** Optional board-dashboard URL if we want to proxy admin writes to this\n * peer. Defaults to `url` with port :18800/:19900 rewritten to :4202,\n * matching our convention that the dashboard runs alongside the daemon. */\n dashboardUrl: z.string().optional(),\n /** Bearer for the daemon's HTTP API (live snapshot, task stream). */\n token: z.string().optional(),\n /** Bearer for the peer's dashboard when proxying admin calls. Defaults to\n * `token` when unset. */\n dashboardToken: z.string().optional(),\n})\n\nexport const dashboardConfigSchema = z.object({\n enabled: z.boolean().default(false),\n port: z.number().int().default(4202),\n bind: z.string().default(\"127.0.0.1\"),\n /** Opt-in bearer token for writes. If unset, writes are unauthenticated. */\n token: z.string().optional(),\n /** Primary daemon the dashboard talks to for live view + A2A. */\n daemonUrl: z.string().default(\"http://localhost:18800\"),\n /** Additional daemons to poll (beyond peers discovered via primary's /mesh). */\n daemons: z.array(dashboardDaemonSchema).default([]),\n /** Default agent used for AI-assisted drafting in the create-issue flow. */\n draftAgent: z.string().optional(),\n}).default({})\n\nexport type BoardConfig = z.infer<typeof boardSchema>\nexport type BoardLabel = z.infer<typeof boardLabelSchema>\nexport type BoardColumn = z.infer<typeof boardColumnSchema>\nexport type BoardSource = z.infer<typeof boardSourceSchema>\nexport type BoardReconcile = z.infer<typeof boardReconcileSchema>\nexport type DashboardConfig = z.infer<typeof dashboardConfigSchema>\n\n/**\n * Decide which column an item lives in. Walks columns in order; the first\n * matching column wins. Items with `state: \"closed\"` always land in the\n * first `kind: \"closed\"` column; otherwise the first `scoped-label` whose\n * `scopedLabel` is present wins; otherwise the first `label` column whose\n * `mapsToLabel` is present; otherwise the first `open-backlog` column.\n *\n * Returns `null` when the item carries a scoped label matching an\n * `open-backlog` column's `scopedPrefix` but no `scoped-label` column claims\n * it — those are off-board workflow states (e.g. `Status::Done` when no\n * Done column exists) and must not leak into the backlog.\n */\nexport function deriveStage(\n item: { state?: \"opened\" | \"closed\"; labels?: string[] },\n columns: BoardColumn[],\n): BoardColumnId | null {\n const set = new Set(item.labels || [])\n if (item.state === \"closed\") {\n const closed = columns.find((c) => c.kind === \"closed\")\n if (closed) return closed.id\n }\n for (const c of columns) {\n if (c.kind === \"scoped-label\" && c.scopedLabel && set.has(c.scopedLabel)) return c.id\n }\n for (const c of columns) {\n if (c.kind === \"label\" && c.mapsToLabel && set.has(c.mapsToLabel)) return c.id\n }\n const backlog = columns.find((c) => c.kind === \"open-backlog\")\n if (backlog) {\n // If the item has any scoped-prefix label, it's not an open-backlog candidate.\n const prefix = backlog.scopedPrefix + \"::\"\n const hasScoped = [...set].some((l) => l.startsWith(prefix))\n return hasScoped ? null : backlog.id\n }\n return columns[0]?.id ?? null\n}\n\n/**\n * Describes how to mutate an issue to move it between two columns.\n * `closeIssue` / `reopen` take precedence over label mutations on the same action.\n */\nexport interface ColumnTransition {\n addLabels?: string[]\n removeLabels?: string[]\n closeIssue?: boolean\n reopen?: boolean\n}\n\nexport function transitionDiff(\n fromColumn: BoardColumn | undefined,\n toColumn: BoardColumn,\n): ColumnTransition {\n const add: string[] = []\n const remove: string[] = []\n let closeIssue = false\n let reopen = false\n\n // Handle target column.\n if (toColumn.kind === \"scoped-label\" && toColumn.scopedLabel) {\n add.push(toColumn.scopedLabel)\n } else if (toColumn.kind === \"label\" && toColumn.mapsToLabel) {\n add.push(toColumn.mapsToLabel)\n } else if (toColumn.kind === \"closed\") {\n closeIssue = true\n }\n\n // Handle source column — strip its label; if it was \"closed\", reopen.\n if (fromColumn) {\n if (fromColumn.kind === \"scoped-label\" && fromColumn.scopedLabel) {\n remove.push(fromColumn.scopedLabel)\n } else if (fromColumn.kind === \"label\" && fromColumn.mapsToLabel) {\n remove.push(fromColumn.mapsToLabel)\n } else if (fromColumn.kind === \"closed\" && toColumn.kind !== \"closed\") {\n reopen = true\n }\n }\n\n return {\n addLabels: add.length ? add : undefined,\n removeLabels: remove.length ? remove : undefined,\n closeIssue: closeIssue || undefined,\n reopen: reopen || undefined,\n }\n}\n","// --- Keyboard shortcuts for AgentX Voice, as stored in agentx.json ---\n//\n// A shortcut is written as modifiers and one key joined by \"+\", for\n// example \"opt+space\", \"cmd+opt+period\" or \"ctrl+opt+1\". The Mac app\n// (apps/mac-voice Hotkey.swift) reads the same form. Stored in one\n// normal order (ctrl, opt, shift, cmd, then the key) so two ways of\n// writing a shortcut compare equal.\n\nconst MODIFIERS: Record<string, \"ctrl\" | \"opt\" | \"shift\" | \"cmd\"> = {\n ctrl: \"ctrl\", control: \"ctrl\",\n opt: \"opt\", option: \"opt\", alt: \"opt\",\n shift: \"shift\",\n cmd: \"cmd\", command: \"cmd\",\n}\nconst ORDER = [\"ctrl\", \"opt\", \"shift\", \"cmd\"] as const\n\nconst NAMED_KEYS = [\n \"space\", \"period\", \"comma\", \"slash\", \"semicolon\", \"quote\", \"minus\", \"equal\",\n \"leftbracket\", \"rightbracket\", \"backslash\", \"grave\", \"return\", \"tab\",\n]\nconst KEY_ALIASES: Record<string, string> = { \".\": \"period\", \",\": \"comma\", \"/\": \"slash\", \";\": \"semicolon\", \"'\": \"quote\", \"-\": \"minus\", \"=\": \"equal\", \"`\": \"grave\", enter: \"return\" }\n\nconst isFKey = (k: string) => /^f([1-9]|1[0-9]|20)$/.test(k)\nconst isKey = (k: string) => /^[a-z0-9]$/.test(k) || NAMED_KEYS.includes(k) || isFKey(k)\n\nexport type HotkeyParse = { ok: true; value: string } | { ok: false; error: string }\n\n/** A shortcut in its stored form, or why it cannot be one. */\nexport function parseHotkey(input: string): HotkeyParse {\n const raw = String(input ?? \"\").trim().toLowerCase()\n if (!raw) return { ok: false, error: \"is empty\" }\n // \"+\" itself is not a key we accept, so splitting on it is safe.\n const parts = raw.split(\"+\").map((p) => p.trim())\n if (parts.some((p) => !p)) return { ok: false, error: `\"${input}\" has an empty part; write it like opt+space` }\n const key = KEY_ALIASES[parts[parts.length - 1]] ?? parts[parts.length - 1]\n const mods = new Set<string>()\n for (const p of parts.slice(0, -1)) {\n const m = MODIFIERS[p]\n if (!m) return { ok: false, error: `\"${p}\" is not a modifier; use ctrl, opt, shift or cmd` }\n if (mods.has(m)) return { ok: false, error: `\"${input}\" names ${m} twice` }\n mods.add(m)\n }\n if (!isKey(key)) return { ok: false, error: `\"${key}\" is not a key AgentX Voice can use; use a letter, a digit, f1 to f20, or space, period, comma, slash, return, tab` }\n // Without ctrl, opt or cmd the shortcut would swallow ordinary typing.\n if (!isFKey(key) && ![\"ctrl\", \"opt\", \"cmd\"].some((m) => mods.has(m))) {\n return { ok: false, error: `\"${input}\" needs ctrl, opt or cmd, or it would take that key away from your typing` }\n }\n return { ok: true, value: [...ORDER.filter((m) => mods.has(m)), key].join(\"+\") }\n}\n\n/** Zod refinement message helper: undefined when fine. */\nexport function hotkeyError(input: string): string | undefined {\n const r = parseHotkey(input)\n return r.ok ? undefined : r.error\n}\n\n/** \"⌃⌥1\" for \"ctrl+opt+1\", as the Mac shows shortcuts. */\nexport function describeHotkey(value: string): string {\n const sym: Record<string, string> = { ctrl: \"⌃\", opt: \"⌥\", shift: \"⇧\", cmd: \"⌘\" }\n const names: Record<string, string> = { space: \"Space\", period: \".\", comma: \",\", slash: \"/\", semicolon: \";\", quote: \"'\", minus: \"-\", equal: \"=\", leftbracket: \"[\", rightbracket: \"]\", backslash: \"\\\\\", grave: \"`\", return: \"Return\", tab: \"Tab\" }\n const parts = value.split(\"+\")\n const key = parts.pop() ?? \"\"\n return parts.map((m) => sym[m] ?? m).join(\"\") + (names[key] ?? key.toUpperCase())\n}\n\nexport const DEFAULT_HOTKEYS = { talk: \"opt+space\", stop: \"cmd+opt+period\", paste: \"cmd+opt+v\" } as const\n","// --- The voice orb's colour palettes, drawn from nature ---\n//\n// Each agent's orb in AgentX Voice flows through one of these gradients.\n// An agent picks one with presence.palette; without one it gets the\n// palette whose hue is nearest its own presence.color, so it stays\n// recognisable as the same agent as its on-screen pointer; with neither\n// it gets the default, voice.palette. The app draws whatever colours the\n// daemon sends, so this file is the only list.\n\nexport interface OrbPalette {\n id: string\n label: string\n /** Five stops, deep to light. The orb's mesh uses all five; the last\n * is its bright core. */\n colors: [string, string, string, string, string]\n /** The hue (0–360) an agent colour is matched against. */\n hue: number\n}\n\nexport const ORB_PALETTES: OrbPalette[] = [\n { id: \"sunrise\", label: \"Sunrise\", hue: 18, colors: [\"#E8505B\", \"#F2726F\", \"#F79A5A\", \"#FBBF4A\", \"#FFE3A3\"] },\n { id: \"desert\", label: \"Desert\", hue: 36, colors: [\"#A4492C\", \"#C8683E\", \"#D9905E\", \"#E7B98A\", \"#F6E3C4\"] },\n { id: \"forest\", label: \"Forest\", hue: 100, colors: [\"#2F5D34\", \"#4C7D3A\", \"#6FA046\", \"#9CC25E\", \"#D5EBA4\"] },\n { id: \"lagoon\", label: \"Lagoon\", hue: 172, colors: [\"#0B6E73\", \"#0E9594\", \"#1FBFB2\", \"#56DCCB\", \"#B8F4EA\"] },\n { id: \"ocean\", label: \"Ocean\", hue: 205, colors: [\"#15457E\", \"#1C6BA8\", \"#2B93CF\", \"#5DBBE8\", \"#BFE6FA\"] },\n { id: \"dusk\", label: \"Dusk\", hue: 268, colors: [\"#2E2A7A\", \"#5B3E9E\", \"#8C4FB0\", \"#C8649E\", \"#F4A7B9\"] },\n { id: \"blossom\", label: \"Blossom\", hue: 330, colors: [\"#A3245B\", \"#CF3F78\", \"#E8699A\", \"#F49BB8\", \"#FFD6E0\"] },\n]\n\n/** What shows the assistant's state in AgentX Voice (voice.look): the\n * orb, or the character that wears the same palette. */\nexport const VOICE_LOOKS = [\"orb\", \"character\"] as const\nexport type VoiceLook = (typeof VOICE_LOOKS)[number]\n\n/** How often the character plays a small animation by itself when idle. */\nexport const VOICE_ANIMATIONS = [\"off\", \"rarely\", \"sometimes\", \"often\"] as const\nexport type VoiceAnimations = (typeof VOICE_ANIMATIONS)[number]\n\nexport const ORB_PALETTE_IDS = ORB_PALETTES.map((p) => p.id) as [string, ...string[]]\n\n/** #RRGGBB to its hue in degrees, or null when it is grey or not a colour. */\nfunction hueOf(hex: string): number | null {\n const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex)\n if (!m) return null\n const [r, g, b] = m.slice(1).map((x) => parseInt(x, 16) / 255)\n const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min\n if (d < 0.04) return null\n const h = max === r ? ((g - b) / d) % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4\n return (h * 60 + 360) % 360\n}\n\n/** The palette nearest an agent colour by hue. Grey or invalid: lagoon. */\nexport function paletteForColor(hex: string): OrbPalette {\n const hue = hueOf(hex)\n if (hue === null) return ORB_PALETTES.find((p) => p.id === \"lagoon\")!\n let best = ORB_PALETTES[0], bestDistance = 360\n for (const p of ORB_PALETTES) {\n const d = Math.abs(p.hue - hue)\n const distance = Math.min(d, 360 - d)\n if (distance < bestDistance) { best = p; bestDistance = distance }\n }\n return best\n}\n\n/** The palette of an agent that chose neither a palette nor a colour,\n * unless voice.palette names another. */\nexport const DEFAULT_PALETTE = \"lagoon\"\n\n/** An agent's palette: the one it chose (presence.palette), else the one\n * nearest the colour it chose (presence.color), else `fallback`\n * (voice.palette). `set` says whether it chose the palette itself. */\nexport function agentPalette(\n presence: { palette?: string; color?: string } | undefined,\n fallback: string = DEFAULT_PALETTE,\n): OrbPalette & { set: boolean } {\n const byId = (id?: string) => ORB_PALETTES.find((p) => p.id === id)\n const picked = byId(presence?.palette)\n if (picked) return { ...picked, set: true }\n const color = presence?.color\n const unchosen = color && hueOf(color) !== null ? paletteForColor(color) : byId(fallback) ?? byId(DEFAULT_PALETTE)!\n return { ...unchosen, set: false }\n}\n","// --- Which language a spoken line is in: English, French or Arabic ---\n//\n// An agent may hold one system voice per language, so a French answer is\n// read by a French voice. Replies are short and the choice is between\n// three very different languages, so a script check and a handful of\n// common words are enough; a model would be slower and no more useful.\n\nexport type Language = \"en\" | \"fr\" | \"ar\"\n\nconst EN = new Set(\"the and is are was were i you he she we they it this that to of in for with not but yes what have has will would can your my\".split(\" \"))\nconst FR = new Set(\"le la les des du de un une et est sont je tu il elle nous vous ils elles ce cette c'est pas pour que qui avec dans sur au aux mais très oui non merci bonjour votre mon\".split(\" \"))\nconst ACCENT = /[éèêëàâçùûîïôœ]/\n\n/** The line's language, or null when nothing in it says which. */\nexport function detectLanguage(text: string): Language | null {\n const arabic = (text.match(/[-ۿ]/g) ?? []).length\n const latin = (text.match(/[a-zA-ZÀ-ÿ]/g) ?? []).length\n if (arabic && arabic >= latin) return \"ar\"\n let en = 0, fr = 0\n for (const w of text.toLowerCase().replace(/[’]/g, \"'\").match(/[a-zà-ÿœ']+/g) ?? []) {\n if (EN.has(w)) en++\n if (FR.has(w) || w.startsWith(\"l'\") || w.startsWith(\"d'\") || w.startsWith(\"qu'\")) fr++\n else if (ACCENT.test(w)) fr += 0.5\n }\n if (en === fr) return null\n return en > fr ? \"en\" : \"fr\"\n}\n","// --- Written answer to spoken answer ---\n//\n// Shared by the daemon's voice replies (/ask, talk, voice history) and the\n// phone app's spoken answers (voice-io-api.ts). Moved out of index.ts\n// unchanged, with the length cap as a parameter.\n\nimport { detectLanguage, type Language } from \"./language\"\n\n/** voice.spokenMaxChars' default: about three short sentences, half a\n * minute of speech. */\nexport const SPOKEN_MAX_CHARS = 500\n\n/** Said after an answer cut for speech, so the listener knows to read on. */\nconst MORE: Record<Language, string> = {\n en: \"The rest is on screen.\",\n fr: \"La suite est à l'écran.\",\n ar: \"الباقي على الشاشة.\",\n}\n\n/**\n * Convert text to TTS-friendly speech.\n * Strips markdown, expands technical terms, removes code, handles symbols.\n */\nexport function toSpeakable(text: string, max = SPOKEN_MAX_CHARS): string {\n let s = text\n\n // Remove code blocks entirely (don't read code aloud)\n s = s.replace(/```[\\s\\S]*?```/g, \". \")\n s = s.replace(/`[^`]*`/g, \"\") // inline code\n\n // Remove markdown syntax characters\n s = s.replace(/\\*\\*(.*?)\\*\\*/g, \"$1\") // bold\n s = s.replace(/__(.*?)__/g, \"$1\") // bold\n s = s.replace(/\\*([^*]+)\\*/g, \"$1\") // italic\n s = s.replace(/_([^_]+)_/g, \"$1\") // italic\n s = s.replace(/~~(.*?)~~/g, \"$1\") // strikethrough\n\n // Links: keep text, drop URL\n s = s.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n\n // Images: describe\n s = s.replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"image$1 \")\n\n // Headers: remove # markers\n s = s.replace(/^#{1,6}\\s+/gm, \"\")\n\n // List bullets: convert to pauses\n s = s.replace(/^[\\s]*[-*+]\\s+/gm, \". \")\n s = s.replace(/^[\\s]*\\d+\\.\\s+/gm, \". \")\n\n // Horizontal rules\n s = s.replace(/^[-=_]{3,}$/gm, \"\")\n\n // Blockquotes\n s = s.replace(/^>\\s*/gm, \"\")\n\n // URLs in plain text: replace with \"link\"\n s = s.replace(/https?:\\/\\/\\S+/g, \"link\")\n\n // File paths: keep readable\n s = s.replace(/\\/\\w+(\\/\\w+)+/g, (match) => match.split(\"/\").filter(Boolean).join(\" slash \"))\n\n // Technical symbols → words\n s = s.replace(/&/g, \" and \")\n s = s.replace(/@/g, \" at \")\n s = s.replace(/=>/g, \" returns \")\n s = s.replace(/->/g, \" to \")\n s = s.replace(/\\|/g, \" or \")\n\n // Common abbreviations\n s = s.replace(/\\be\\.g\\./gi, \"for example\")\n s = s.replace(/\\bi\\.e\\./gi, \"that is\")\n s = s.replace(/\\betc\\./gi, \"etcetera\")\n s = s.replace(/\\bvs\\.?\\b/gi, \"versus\")\n\n // Collapse whitespace\n s = s.replace(/\\n{2,}/g, \". \")\n s = s.replace(/\\n/g, \" \")\n s = s.replace(/\\s+/g, \" \")\n s = s.replace(/\\.\\s*\\./g, \".\")\n s = s.replace(/\\s+([.,!?])/g, \"$1\")\n\n // Strip leading/trailing whitespace and punctuation\n s = s.trim().replace(/^[.,:;]+\\s*/, \"\")\n\n // Cap for voice UX. A long answer ends on its last whole sentence inside\n // the cap, then says there is more; the written answer is shown whole.\n // Only a sentence longer than half the cap is cut between words.\n if (s.length > max) {\n const head = s.slice(0, max)\n const end = ([...s.slice(0, max + 1).matchAll(/[.!?؟](?=\\s)/g)].at(-1)?.index ?? -1) + 1\n s = (end >= max / 2 ? head.slice(0, end) : head.replace(/\\s+\\S*$/, \"\") + \"...\") + \" \" + MORE[detectLanguage(head) ?? \"en\"]\n }\n\n return s\n}\n","// --- Transcripts with no words ---\n//\n// Speech to text writes what it heard when nobody spoke: \"[background\n// noise]\", \"(mumbling)\". /ask drops such a transcript before any agent is\n// woken (#614: each one cost a full agent turn to answer \"I didn't catch\n// that\").\n\n/** voice.noiseFilter.markers' default: what the transcribers wrote on the\n * Mac in three days of voice turns, plus Whisper's own. */\nexport const NOISE_MARKERS = [\n \"background noise\", \"noise\", \"mumbling\", \"babbling\", \"unintelligible\",\n \"inaudible\", \"muffled speech\", \"pause\", \"silence\", \"music\",\n \"outro jingle\", \"blank audio\",\n]\n\n/** What the caller hears instead of an agent's answer. */\nexport const NOISE_REPLY = \"I didn't catch that.\"\n\nconst key = (s: string) => s.toLowerCase().replace(/[_\\s]+/g, \" \").trim()\n\n/**\n * True when a transcript holds no words: empty after trimming, or nothing\n * but bracketed markers from the list (\"[background noise]\", \"(Mumbling)\n * [pause]\"). A marker beside real speech is speech, and so is a short\n * answer such as \"Yes\".\n */\nexport function isNoiseTranscript(text: string, markers: string[] = NOISE_MARKERS): boolean {\n const known = new Set(markers.map(key))\n const rest = text.replace(/\\[([^\\][]*)\\]|\\(([^()]*)\\)/g, (group, square, round) =>\n known.has(key(square ?? round)) ? \"\" : group)\n return !/[\\p{L}\\p{N}]/u.test(rest)\n}\n","import { z } from \"zod\"\n\n// --- WhatsApp inbound triage settings (agentx.json → whatsappTriage) ---\n//\n// wacli posts each new WhatsApp message to POST /webhook/wacli. A watch\n// rule picks the messages an agent should look at; everything else is\n// dropped without being stored. Nothing is ever sent to the contact\n// unless the owner approves it (see drafts.ts).\n\nconst hhmm = z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\")\n\nexport const watchRuleSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/, \"lowercase letters, digits, - and _\"),\n enabled: z.boolean().default(true),\n /** Chats to watch: a contact's or a group's JID, or a phone number. */\n chats: z.array(z.string().min(1)).default([]),\n /** Only messages from these people (JID or phone number), in any chat\n * the rule covers. Empty: anyone. */\n senders: z.array(z.string().min(1)).default([]),\n /** The agent that triages the messages. */\n agent: z.string().min(1),\n /** Extra instructions for the agent: who this contact is, where issues\n * go, the tone of a reply. */\n prompt: z.string().max(4000).optional(),\n /** No notifications between these times. Triage and drafts still happen. */\n quietHours: z.object({ start: hhmm, end: hhmm }).optional(),\n /** Send the agent's short acknowledgement without asking. Needs\n * whatsappTriage.allowAutoAck too. */\n autoAck: z.boolean().default(false),\n}).refine((r) => r.chats.length > 0 || r.senders.length > 0, {\n message: \"a rule needs at least one chat or sender\",\n})\n\nexport const whatsappTriageSchema = z.object({\n enabled: z.boolean().default(false),\n /** Environment variable holding the webhook secret given to\n * `wacli sync --webhook-secret`. Unsigned requests are refused. */\n secretEnv: z.string().regex(/^[A-Z_][A-Z0-9_]*$/).default(\"WACLI_WEBHOOK_SECRET\"),\n /** Messages from one chat within this many seconds become one task. */\n batchSeconds: z.number().int().min(0).max(600).default(20),\n /** Time zone for quiet hours. Default: this computer's. */\n timezone: z.string().optional(),\n /** Second switch for rules with autoAck. Both must be on. */\n allowAutoAck: z.boolean().default(false),\n /** Download images and voice notes so the agent can read or hear them. */\n describeMedia: z.boolean().default(true),\n wacli: z.object({\n bin: z.string().default(\"wacli\"),\n /** `wacli --account NAME`, when more than one is paired. */\n account: z.string().optional(),\n /** `wacli --store DIR`. */\n store: z.string().optional(),\n }).default({}),\n rules: z.array(watchRuleSchema).default([]),\n}).default({})\n\nexport type WatchRule = z.infer<typeof watchRuleSchema>\nexport type WhatsappTriageConfig = z.infer<typeof whatsappTriageSchema>\nexport type WacliSettings = WhatsappTriageConfig[\"wacli\"]\n","import type Database from \"better-sqlite3\"\nimport { ensureColumns, PERSON_COLUMNS } from \"@/storage/sqlite\"\n\n// --- Open requests: what a person asked for that is not finished (#356) ---\n//\n// One row per request a person made to an agent, kept until someone closes\n// it. A turn from a person starts as a `candidate`; it becomes an open\n// request when the turn leaves work behind (a delegation, a failure, a\n// restart) or the agent says so. A candidate whose turn simply answered\n// and ended is deleted.\n//\n// The store is plain sqlite in .agentx/db.sqlite. Every call is one small\n// synchronous statement: nothing here calls a model or waits on I/O.\n\nexport type OpenState = \"in_progress\" | \"waiting_owner\" | \"waiting_other\" | \"needs_attention\"\nexport type ClosedState = \"done\" | \"declined\" | \"dropped\"\nexport type RequestState = \"candidate\" | OpenState | ClosedState\n\nexport const OPEN_STATES: readonly OpenState[] = [\"in_progress\", \"waiting_owner\", \"waiting_other\", \"needs_attention\"]\nexport const CLOSED_STATES: readonly ClosedState[] = [\"done\", \"declined\", \"dropped\"]\n\nexport type LinkKind = \"run\" | \"delegation\" | \"card\"\n\nexport interface RequestRecord {\n id: string\n state: RequestState\n channel: string\n chatId: string\n sender: string | null\n /** The known person who asked (people, #384), when the sender is one. */\n person: string | null\n agentId: string\n /** The request in the person's words. */\n text: string\n createdAt: number\n /** Last time anything happened under this request. */\n updatedAt: number\n /** Why it needs attention, in words. */\n attentionReason: string | null\n /** When the owner was told it needs attention. Told once. */\n notifiedAt: number | null\n /** What the owner is being asked, while waiting on them. */\n question: string | null\n closedAt: number | null\n /** Link to the proof of a finished request (PR, issue, message, deploy). */\n evidence: string | null\n /** Why it was declined or dropped. */\n closeReason: string | null\n /** What the owner last wrote on it (a reply, or a note with a hand-off). */\n ownerNote: string | null\n /** That note was given to the agent in a pick-up: it is not said again. */\n noteSaid: boolean\n /** The agent it was asked of, once the owner handed it on. The chat it\n * was asked in is that agent's: another agent may not be able to write\n * there (#481). Null: never handed on. */\n askedAgent: string | null\n}\n\nexport const TEXT_MAX = 4000\nconst REASON_MAX = 500\n\n/** Creates the tables when missing. Named, not numbered, so it cannot\n * clash with a migration number another branch claimed. */\nexport function ensureRequestTables(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS requests (\n id TEXT PRIMARY KEY,\n state TEXT NOT NULL,\n channel TEXT NOT NULL,\n chat_id TEXT NOT NULL,\n sender TEXT,\n agent_id TEXT NOT NULL,\n text TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n attention_reason TEXT,\n notified_at INTEGER,\n question TEXT,\n closed_at INTEGER,\n evidence TEXT,\n close_reason TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_requests_state_created ON requests(state, created_at);\n CREATE INDEX IF NOT EXISTS idx_requests_chat ON requests(agent_id, channel, chat_id, created_at);\n CREATE TABLE IF NOT EXISTS request_links (\n request_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n ref TEXT NOT NULL,\n at INTEGER NOT NULL,\n PRIMARY KEY (kind, ref),\n FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE\n );\n CREATE INDEX IF NOT EXISTS idx_request_links_request ON request_links(request_id);\n `)\n ensureColumns(db, \"requests\", PERSON_COLUMNS)\n // Added after the first version: a database that already has the table\n // gets the columns here.\n const cols = db.prepare(\"PRAGMA table_info(requests)\").all() as Array<{ name: string }>\n for (const [name, type] of [[\"pickup_at\", \"INTEGER\"], [\"owner_note\", \"TEXT\"], [\"note_said\", \"INTEGER\"], [\"asked_agent\", \"TEXT\"]]) {\n if (cols.some((c) => c.name === name)) continue\n try {\n db.exec(`ALTER TABLE requests ADD COLUMN ${name} ${type}`)\n } catch (e: any) {\n // Daemon and dashboard starting together: the other one added it first.\n if (!/duplicate column name/i.test(String(e?.message))) throw e\n }\n }\n}\n\n/** For readers: a database without the table has no requests, and reading\n * it must not create one. */\nexport function hasRequestTables(db: Database.Database): boolean {\n return !!db.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'requests'\").get()\n}\n\nfunction toRecord(r: any): RequestRecord {\n return {\n id: r.id, state: r.state, channel: r.channel, chatId: r.chat_id, sender: r.sender ?? null, person: r.person ?? null,\n agentId: r.agent_id, text: r.text, createdAt: r.created_at, updatedAt: r.updated_at,\n attentionReason: r.attention_reason ?? null, notifiedAt: r.notified_at ?? null,\n question: r.question ?? null, closedAt: r.closed_at ?? null,\n evidence: r.evidence ?? null, closeReason: r.close_reason ?? null, ownerNote: r.owner_note ?? null, noteSaid: !!r.note_said,\n askedAgent: r.asked_agent ?? null,\n }\n}\n\nconst placeholders = (n: number) => Array(n).fill(\"?\").join(\",\")\n\nexport class RequestStore {\n constructor(private db: Database.Database) {\n ensureRequestTables(db)\n db.pragma(\"foreign_keys = ON\")\n }\n\n /** Note a turn a person started. Linked to its run. */\n addCandidate(input: {\n id: string; runId: string; channel: string; chatId: string; sender?: string | null\n person?: string | null; agentId: string; text: string; now: number\n }): void {\n this.db.prepare(\n `INSERT OR IGNORE INTO requests (id, state, channel, chat_id, sender, person, agent_id, text, created_at, updated_at)\n VALUES (?, 'candidate', ?, ?, ?, ?, ?, ?, ?, ?)`,\n ).run(input.id, input.channel, input.chatId, input.sender ?? null, input.person ?? null, input.agentId, input.text.slice(0, TEXT_MAX), input.now, input.now)\n this.link(input.id, \"run\", input.runId, input.now)\n }\n\n get(id: string): RequestRecord | null {\n const row = this.db.prepare(\"SELECT * FROM requests WHERE id = ?\").get(id)\n return row ? toRecord(row) : null\n }\n\n /** The request a run, delegation or card belongs to. */\n byLink(kind: LinkKind, ref: string): RequestRecord | null {\n const row = this.db.prepare(\n \"SELECT r.* FROM requests r JOIN request_links l ON l.request_id = r.id WHERE l.kind = ? AND l.ref = ?\",\n ).get(kind, ref)\n return row ? toRecord(row) : null\n }\n\n link(requestId: string, kind: LinkKind, ref: string, now: number): void {\n this.db.prepare(\"INSERT OR IGNORE INTO request_links (request_id, kind, ref, at) VALUES (?, ?, ?, ?)\").run(requestId, kind, ref, now)\n }\n\n links(requestId: string): Array<{ kind: LinkKind; ref: string; at: number }> {\n return this.db.prepare(\"SELECT kind, ref, at FROM request_links WHERE request_id = ? ORDER BY at, rowid\").all(requestId) as any\n }\n\n /** The agent's newest answer under this request: the final reply of its\n * latest linked run that has one. Null when nothing was recorded, or\n * when this database has no traces. */\n lastAnswer(requestId: string): { agentId: string; text: string; at: number } | null {\n try {\n const row = this.db.prepare(\n `SELECT t.agent_id AS agentId, t.final_response AS text, COALESCE(t.finished_at, t.started_at) AS at\n FROM request_links l JOIN task_traces t ON t.task_id = l.ref\n WHERE l.request_id = ? AND l.kind = 'run' AND t.final_response IS NOT NULL AND t.final_response != ''\n ORDER BY t.started_at DESC LIMIT 1`,\n ).get(requestId) as { agentId: string; text: string; at: number } | undefined\n return row ?? null\n } catch {\n return null\n }\n }\n\n /** Something happened under this request: it is not quiet. */\n touch(id: string, now: number): void {\n this.db.prepare(\"UPDATE requests SET updated_at = ? WHERE id = ?\").run(now, id)\n }\n\n /** Work is under way. Opens a candidate; clears an earlier \"needs\n * attention\" so a later failure is raised again. Closed stays closed. */\n progress(id: string, now: number, state: \"in_progress\" | \"waiting_other\" = \"in_progress\"): void {\n this.db.prepare(\n `UPDATE requests SET state = ?, updated_at = ?, attention_reason = NULL, notified_at = NULL, question = NULL\n WHERE id = ? AND state NOT IN (${placeholders(CLOSED_STATES.length)})`,\n ).run(state, now, id, ...CLOSED_STATES)\n }\n\n /** The next step is the owner's answer. */\n waitOnOwner(id: string, question: string, now: number): void {\n this.db.prepare(\n `UPDATE requests SET state = 'waiting_owner', updated_at = ?, question = ?, attention_reason = NULL, notified_at = NULL\n WHERE id = ? AND state NOT IN (${placeholders(CLOSED_STATES.length)})`,\n ).run(now, question.slice(0, TEXT_MAX), id, ...CLOSED_STATES)\n }\n\n /** The work failed, timed out, was cut off, or went quiet. A request\n * already waiting for attention keeps its first reason and is not\n * raised a second time. */\n needsAttention(id: string, reason: string, now: number): boolean {\n const res = this.db.prepare(\n `UPDATE requests SET state = 'needs_attention', updated_at = ?, attention_reason = ?, notified_at = NULL\n WHERE id = ? AND state NOT IN ('needs_attention', ${placeholders(CLOSED_STATES.length)})`,\n ).run(now, reason.replace(/\\s+/g, \" \").trim().slice(0, REASON_MAX), id, ...CLOSED_STATES)\n return res.changes > 0\n }\n\n /** Close it: done needs evidence, declined and dropped need a reason. */\n close(id: string, state: ClosedState, detail: string, now: number): boolean {\n const text = detail.trim()\n if (!text) throw new Error(state === \"done\" ? \"a finished request needs a link to the evidence\" : `a ${state} request needs a reason`)\n const res = this.db.prepare(\n `UPDATE requests SET state = ?, closed_at = ?, updated_at = ?, evidence = ?, close_reason = ?\n WHERE id = ? AND state NOT IN (${placeholders(CLOSED_STATES.length)})`,\n ).run(state, now, now, state === \"done\" ? text.slice(0, REASON_MAX) : null, state === \"done\" ? null : text.slice(0, REASON_MAX), id, ...CLOSED_STATES)\n return res.changes > 0\n }\n\n /** A candidate whose turn ended with nothing left to follow. */\n discardCandidate(id: string): boolean {\n return this.db.prepare(\"DELETE FROM requests WHERE id = ? AND state = 'candidate'\").run(id).changes > 0\n }\n\n /** Open requests, oldest first. */\n listOpen(): RequestRecord[] {\n return this.db.prepare(\n `SELECT * FROM requests WHERE state IN (${placeholders(OPEN_STATES.length)}) ORDER BY created_at, rowid`,\n ).all(...OPEN_STATES).map(toRecord)\n }\n\n /** Requests in these states that nothing has touched since `before`. */\n quietSince(states: RequestState[], before: number): RequestRecord[] {\n return this.db.prepare(\n `SELECT * FROM requests WHERE state IN (${placeholders(states.length)}) AND updated_at < ? ORDER BY created_at, rowid`,\n ).all(...states, before).map(toRecord)\n }\n\n /** Requests that need attention and were not raised yet. */\n awaitingNotice(): RequestRecord[] {\n return this.db.prepare(\n \"SELECT * FROM requests WHERE state = 'needs_attention' AND notified_at IS NULL ORDER BY created_at, rowid\",\n ).all().map(toRecord)\n }\n\n markNotified(id: string, now: number | null): void {\n this.db.prepare(\"UPDATE requests SET notified_at = ? WHERE id = ?\").run(now, id)\n }\n\n /** Requests in one state, oldest first. */\n listByState(state: RequestState): RequestRecord[] {\n return this.db.prepare(\"SELECT * FROM requests WHERE state = ? ORDER BY created_at, rowid\").all(state).map(toRecord)\n }\n\n /** The owner said \"pick it up again\": back in progress, and the agent\n * is to be told at the next check. */\n requestPickup(id: string, now: number): boolean {\n return this.db.prepare(\n `UPDATE requests SET state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL, pickup_at = ?\n WHERE id = ? AND state = 'needs_attention'`,\n ).run(now, now, id).changes > 0\n }\n\n /** The owner replied, or handed it to an agent (the same or another):\n * that agent has it, in progress, and is told at the next check. Any\n * open request, not only one that needs attention. The agent it was\n * asked of is kept from the first time: channel and chat stay its own. */\n handOff(id: string, agentId: string, note: string, now: number): boolean {\n return this.db.prepare(\n `UPDATE requests SET agent_id = ?, state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL,\n question = NULL, pickup_at = ?, owner_note = ?, note_said = NULL, asked_agent = COALESCE(asked_agent, agent_id)\n WHERE id = ? AND state IN (${placeholders(OPEN_STATES.length)})`,\n ).run(agentId, now, now, note.trim().slice(0, TEXT_MAX) || null, id, ...OPEN_STATES).changes > 0\n }\n\n /** This note of the owner is given to the agent in a pick-up, or (false)\n * was not after all. Only while it is still the note on the request: a\n * reply written since has its own pick-up and its own mark. */\n setNoteSaid(id: string, note: string, said: boolean): void {\n this.db.prepare(`UPDATE requests SET note_said = ? WHERE id = ? AND owner_note = ?${said ? \" AND pickup_at IS NULL\" : \"\"}`).run(said ? 1 : null, id, note)\n }\n\n /** Requests whose agent has not been told to pick them up yet. Each is\n * returned once. One statement: a mark the dashboard (another process)\n * writes meanwhile is either returned here or kept for the next read. */\n takePickups(): RequestRecord[] {\n return this.db.prepare(\"UPDATE requests SET pickup_at = NULL WHERE pickup_at IS NOT NULL RETURNING *\").all()\n .map(toRecord)\n .filter((r) => r.state === \"in_progress\")\n .sort((a, b) => a.createdAt - b.createdAt)\n }\n\n /** Delete closed requests older than `before`. Open ones never age out. */\n pruneClosed(before: number): number {\n return this.db.prepare(\n `DELETE FROM requests WHERE state IN (${placeholders(CLOSED_STATES.length)}) AND closed_at < ?`,\n ).run(...CLOSED_STATES, before).changes\n }\n}\n","import type { AgentXEvents } from \"@/events/bus\"\nimport { OPEN_STATES, type OpenState, type RequestStore } from \"./store\"\n\n// --- Follow a person's request from the turn that received it (#356) ---\n//\n// Fed by signals the daemon already has; no model call, no waiting:\n// - a turn from a person starts → a candidate, linked to its run\n// - that turn ends cleanly, nothing left → the candidate is deleted\n// - the turn started a delegation → open, in progress\n// - a linked run fails or times out → needs attention\n// - a delegation fails, times out, is lost → needs attention\n// - a restart cut a linked run → resumed: in progress;\n// not resumed: needs attention\n//\n// Every entry point swallows its own errors: following requests must never\n// break the run it is following.\n\nexport interface RequestSettings {\n enabled: boolean\n /** Channels to capture on. Empty: every channel a person writes on. */\n channels: string[]\n /** Who counts as the owner on channels other people can reach, as\n * \"channel:id\": the login on a forge, the sender id elsewhere. */\n from: string[]\n staleAfterHours: number\n retentionDays: number\n}\n\n/** Channel of the turn that hands a request back to its agent. */\nexport const PICKUP_CHANNEL = \"requests\"\n\n/** Marks the context of the pick-up turn the daemon starts. A symbol key\n * cannot arrive in a JSON body, so a caller of /task cannot set it. */\nconst PICKUP_MARK = Symbol(\"requests.pickup\")\n\n/** The context of the turn that hands request `requestId` back. */\nexport function pickupContext(requestId: string): { channel: string; chatId: string; sender: string } {\n return Object.assign({ channel: PICKUP_CHANNEL, chatId: requestId, sender: \"operator\" }, { [PICKUP_MARK]: true })\n}\n\n/** Did the daemon itself start this turn as a pick-up? */\nexport function isPickup(ctx: object | undefined | null): boolean {\n return !!ctx && (ctx as Record<symbol, unknown>)[PICKUP_MARK] === true\n}\n\n/** This node's own surfaces: only its operator can reach them. */\nexport const OPERATOR_CHANNELS: ReadonlySet<string> = new Set([\"voice\", \"app\", \"dashboard\", \"webrtc\"])\n\n/** Senders the daemon itself uses on those surfaces. Their turns are not\n * something the owner asked for (a camera frame, say). */\nconst DAEMON_SENDERS: ReadonlySet<string> = new Set([\"camera\"])\n\nconst base = (channel: string) => channel.toLowerCase().split(\"@\")[0]\n\n/** Channels whose sender `id` is the thread, not the person. */\nconst FORGES: ReadonlySet<string> = new Set([\"github\", \"gitlab\"])\n\n/** One comparable form. WhatsApp numbers compare on digits, so\n * \"+216 20 123 456\" and \"21620123456@s.whatsapp.net\" are the same. */\nconst comparable = (ch: string, v: string) =>\n ch === \"whatsapp\" ? v.replace(/@.*$/, \"\").replace(/\\D/g, \"\") : v.trim().toLowerCase().replace(/^@/, \"\")\n\n/** Does a turn on this channel from this sender count as the owner's? */\nexport function isOwnerTurn(\n settings: Pick<RequestSettings, \"channels\" | \"from\">,\n channel: string,\n sender: { name?: string; id?: string; username?: string } | undefined,\n /** The daemon marked the turn as the owner's (requests/operator). A\n * caller of /task can name any channel, so the name alone is not\n * enough on this node's own surfaces (#393). */\n operator = false,\n): boolean {\n const ch = base(channel)\n if (settings.channels.length && !settings.channels.map(base).includes(ch)) return false\n if (OPERATOR_CHANNELS.has(ch)) return operator && !DAEMON_SENDERS.has((sender?.name ?? \"\").toLowerCase())\n // Display names are not matched: anyone can pick one. Entries are\n // \"channel:id\": an id means nothing outside its own channel. One field\n // names the person: the login on a forge, the sender id elsewhere. A\n // username is never matched beside an id: it is the person's to choose,\n // so it could be set to someone else's id.\n const raw = FORGES.has(ch) ? sender?.username : sender?.id\n const who = raw ? comparable(ch, raw) : \"\"\n if (!who) return false\n return settings.from.some((entry) => {\n const e = entry.trim().toLowerCase()\n const i = e.indexOf(\":\")\n return i > 0 && e.slice(0, i) === ch && comparable(ch, e.slice(i + 1)) === who\n })\n}\n\n/** The sender fields of a task context, for the task:started event. */\nexport function senderOf(ctx: Record<string, unknown> | undefined | null): { name?: string; id?: string; username?: string } | undefined {\n if (!ctx) return undefined\n const pick = (v: unknown) => (typeof v === \"string\" && v ? v.slice(0, 120) : undefined)\n const sender = { name: pick(ctx.sender), id: pick(ctx.senderId), username: pick(ctx.senderUsername) }\n return sender.name || sender.id || sender.username ? sender : undefined\n}\n\n/** \"timed out\" for a timeout, \"failed\" otherwise. */\nfunction failureWord(error: string): string {\n return /\\btimed? ?out\\b|timeout/i.test(error) ? \"timed out\" : \"failed\"\n}\n\nconst clip = (s: string, n = 200) => {\n const flat = s.replace(/\\s+/g, \" \").trim()\n return flat.length > n ? `${flat.slice(0, n - 1)}…` : flat\n}\n\nconst chatKey = (agentId: string, channel: string, chatId: string) => `${agentId}\\u0000${channel}\\u0000${chatId}`\n\nexport interface DelegationSignal {\n id: string\n caller: string\n callee: string\n peer?: string\n origin: { channel: string; chatId: string }\n}\n\nexport class RequestTracker {\n constructor(\n private store: RequestStore,\n private settings: () => RequestSettings,\n private log: (msg: string) => void,\n private now: () => number = Date.now,\n ) {}\n\n /** Requests whose turn is running now, by agent and chat. A delegation\n * is linked through this: it can only start from a running turn. */\n private live = new Map<string, { requestId: string; runId: string }>()\n\n private guard(what: string, fn: () => void): void {\n try { fn() } catch (e: any) { this.log(`[requests] ${what} failed: ${e?.message ?? e}`) }\n }\n\n /** A turn from a person listed in `people` with the owner role (#384),\n * on a channel other people can reach. `from` keeps working beside it. */\n private isListedOwner(p: AgentXEvents[\"task:started\"]): boolean {\n const ch = base(p.channel)\n const channels = this.settings().channels\n if (channels.length && !channels.map(base).includes(ch)) return false\n return p.person?.role === \"owner\" && !OPERATOR_CHANNELS.has(ch)\n }\n\n /** The request of the turn this agent is running in this chat, if any. */\n liveRequestId(agentId: string, channel: string, chatId: string): string | null {\n return this.live.get(chatKey(agentId, channel, chatId))?.requestId ?? null\n }\n\n taskStarted(p: AgentXEvents[\"task:started\"]): void {\n if (!p.taskId) return\n this.guard(\"capture\", () => {\n if (!this.settings().enabled) return\n const now = this.now()\n // A run that continues one a restart cut off belongs to its request.\n const earlier = p.resumedFrom ? this.store.byLink(\"run\", p.resumedFrom) : null\n const key = chatKey(p.agentId, p.channel, p.chatId)\n if (earlier) {\n this.store.link(earlier.id, \"run\", p.taskId!, now)\n // A candidate stays one: if the resumed turn just answers and\n // ends, nothing is left to follow.\n if (earlier.state === \"candidate\") this.store.touch(earlier.id, now)\n else this.store.progress(earlier.id, now)\n this.live.set(key, { requestId: earlier.id, runId: p.taskId! })\n return\n }\n // The turn the daemon starts when the owner says \"pick it up again\".\n // Only the daemon's own: any caller of /task can name the channel.\n const pickup = p.pickup && p.channel === PICKUP_CHANNEL ? this.store.get(p.chatId) : null\n if (pickup && pickup.agentId === p.agentId) {\n this.store.link(pickup.id, \"run\", p.taskId!, now)\n this.store.touch(pickup.id, now)\n // Live like any other turn of the request: what it hands on or asks is linked.\n this.live.set(key, { requestId: pickup.id, runId: p.taskId! })\n return\n }\n if (!p.humanRoot || !(isOwnerTurn(this.settings(), p.channel, p.sender, p.operator === true) || this.isListedOwner(p))) return\n this.live.set(key, { requestId: `req-${p.taskId}`, runId: p.taskId! })\n this.store.addCandidate({\n id: `req-${p.taskId}`, runId: p.taskId!, channel: p.channel, chatId: p.chatId,\n sender: p.sender?.name ?? p.sender?.username ?? p.sender?.id ?? null,\n person: p.person?.id ?? null,\n agentId: p.agentId, text: p.requestText ?? p.fullMessage ?? p.messagePreview, now,\n })\n })\n }\n\n taskCompleted(p: AgentXEvents[\"task:completed\"]): void {\n if (!p.taskId) return\n this.guard(\"run end\", () => {\n const key = chatKey(p.agentId, p.channel, p.chatId)\n if (this.live.get(key)?.runId === p.taskId) this.live.delete(key)\n const req = this.store.byLink(\"run\", p.taskId!)\n if (!req) return\n const now = this.now()\n // Cut by a shutdown: the next boot decides (resumeOutcome).\n if (p.interrupted) return\n // Stopped on purpose is not a failure.\n if (p.error && p.errorKind !== \"cancelled\") {\n const word = failureWord(p.error)\n if (this.store.needsAttention(req.id, `${p.agentId} ${word}: ${clip(p.error)}`, now)) {\n this.log(`[requests] ${req.id} needs attention: run ${p.taskId} ${word}`)\n }\n return\n }\n if (req.state === \"candidate\") this.store.discardCandidate(req.id)\n else this.store.touch(req.id, now)\n })\n }\n\n delegationStarted(d: DelegationSignal): void {\n this.guard(\"delegation start\", () => {\n if (!this.settings().enabled) return\n const turn = this.live.get(chatKey(d.caller, d.origin.channel, d.origin.chatId))\n if (!turn) return\n const now = this.now()\n this.store.link(turn.requestId, \"delegation\", d.id, now)\n this.store.progress(turn.requestId, now, \"waiting_other\")\n })\n }\n\n delegationDone(d: DelegationSignal, status: \"done\" | \"error\" | \"timeout\" | \"lost\", text: string): void {\n this.guard(\"delegation end\", () => {\n const req = this.store.byLink(\"delegation\", d.id)\n if (!req) return\n const now = this.now()\n const who = d.peer ? `${d.callee} on ${d.peer}` : d.callee\n if (status === \"done\") {\n // The answer is back with the asking agent. Still open: closing is explicit.\n this.store.progress(req.id, now)\n return\n }\n const what = { error: \"failed\", timeout: \"did not answer in time\", lost: \"was lost in a restart\" }[status]\n if (this.store.needsAttention(req.id, `The work handed to ${who} ${what}: ${clip(text)}`, now)) {\n this.log(`[requests] ${req.id} needs attention: delegation ${d.id} ${status}`)\n }\n })\n }\n\n /** The agent raised a decision card from the turn of a request: the\n * request now waits on the owner, with the card's question. The card's\n * own reminders (inbox, Mac card, check-ins, digest) do the reminding.\n * `turn` is the running turn the call proved, never the chat the card\n * names: anyone can write another agent's id and chat on a card. */\n cardRaised(card: { id: string; raised_by: string; ask: string }, turn: { channel: string; chatId: string } | null): void {\n if (!turn) return\n this.guard(\"card\", () => {\n if (!this.settings().enabled) return\n const requestId = this.live.get(chatKey(card.raised_by, turn.channel, turn.chatId))?.requestId\n if (!requestId) return\n const now = this.now()\n this.store.link(requestId, \"card\", card.id, now)\n this.store.waitOnOwner(requestId, card.ask, now)\n })\n }\n\n /** A linked card was answered or expired. An answer lets the work go\n * on. An expiry means the answer never came (no card approves itself,\n * #741): the request needs attention, whatever an earlier\n * card's answer moved it to. It does not close. */\n cardResolved(card: { id: string; status: string; ask: string; if_silent?: string; outcome?: string }): void {\n this.guard(\"card result\", () => {\n const req = this.store.byLink(\"card\", card.id)\n if (!req) return\n const now = this.now()\n const applied = card.outcome ?? card.if_silent\n if (card.status === \"decided\") {\n if (req.state === \"waiting_owner\") this.store.progress(req.id, now)\n } else if (this.store.needsAttention(req.id, `Your answer did not come before the card expired (${clip(card.ask)}); \"${applied}\" was applied`, now)) {\n this.log(`[requests] ${req.id} needs attention: card ${card.id} expired unanswered`)\n }\n })\n }\n\n /** Added to the turn that brings a delegated answer back: the request\n * stays on the owner's list until the agent closes it. */\n closingNote(delegationId: string): string | undefined {\n try {\n const req = this.store.byLink(\"delegation\", delegationId)\n if (!req || !OPEN_STATES.includes(req.state as OpenState)) return undefined\n return `This is part of the owner's open request ${req.id}. It stays on their list until it is closed. If it is finished now, close it with agentx_request: {action:\"done\", id:\"${req.id}\", evidence:\"<link to the proof>\"}. If you will not do it, use decline with the reason. If work goes on, leave it open.`\n } catch {\n return undefined\n }\n }\n\n /** What the boot-time resume step did with a run the restart cut off.\n * \"already-claimed\" is left alone: the process that claimed it decides. */\n resumeOutcome(o: { taskId: string; decision: string; reason: string }): void {\n this.guard(\"restart\", () => {\n const req = this.store.byLink(\"run\", o.taskId)\n if (!req) return\n const now = this.now()\n if (o.decision === \"resumed\") {\n if (req.state === \"candidate\") this.store.touch(req.id, now)\n else this.store.progress(req.id, now)\n } else if (o.decision === \"reported\" || o.decision === \"resume-failed\" || o.decision === \"skipped\") {\n if (this.store.needsAttention(req.id, `Cut off by a restart and not picked up again (${clip(o.reason)})`, now)) {\n this.log(`[requests] ${req.id} needs attention: run ${o.taskId} cut off by a restart`)\n }\n }\n })\n }\n}\n","import { randomBytes, timingSafeEqual } from \"crypto\"\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\"\nimport { networkInterfaces } from \"os\"\nimport { dirname, resolve } from \"path\"\n\n// --- Proof that a turn on one of this node's own surfaces is the owner's (#393) ---\n//\n// The voice widget, the phone app and the dashboard are the owner's\n// surfaces: a turn there is a request of theirs. But `POST /task` takes\n// the caller's `context` as it is, so any local caller, an agent included,\n// could name `channel: \"dashboard\"` and be recorded as the owner.\n//\n// A turn counts as the owner's on those surfaces only when the daemon\n// marked it. The daemon marks the turns it starts itself (/ask, the\n// dashboard's assistant) and a /task whose caller showed the operator key:\n// a secret in `.agentx/operator.key`, readable by this user only, that the\n// dashboard process presents for the phone app. The mark is a symbol, so it\n// cannot arrive in a JSON body; a bare `context.channel` proves nothing.\n\n/** The header a /task caller presents the operator key in. */\nexport const OPERATOR_HEADER = \"x-agentx-operator\"\n\nconst OPERATOR_MARK = Symbol(\"requests.operator\")\n\n/** Marks `ctx` as a turn the owner started on one of this node's own\n * surfaces. Only the daemon calls this. */\nexport function operatorContext<T extends object>(ctx: T): T {\n return Object.assign(ctx, { [OPERATOR_MARK]: true })\n}\n\n/** Did the daemon mark this turn as the owner's? */\nexport function isOperatorTurn(ctx: object | undefined | null): boolean {\n return !!ctx && (ctx as Record<symbol, unknown>)[OPERATOR_MARK] === true\n}\n\n/** Where this install keeps its operator key. */\nexport function operatorKeyPath(root: string = process.cwd()): string {\n return resolve(root, \".agentx\", \"operator.key\")\n}\n\n/** The operator key of this install: created on first use when `create`\n * is set, null when there is none. The file is readable by this user only. */\nexport function loadOperatorKey(root: string = process.cwd(), opts: { create?: boolean } = {}): string | null {\n const path = operatorKeyPath(root)\n try {\n if (existsSync(path)) {\n const key = readFileSync(path, \"utf-8\").trim()\n if (key) return key\n }\n if (!opts.create) return null\n const key = randomBytes(32).toString(\"hex\")\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, key + \"\\n\", { mode: 0o600 })\n chmodSync(path, 0o600)\n return key\n } catch {\n return null\n }\n}\n\n/** Does what the caller presented match this node's key? A missing key\n * on either side never matches. */\nexport function operatorKeyMatches(key: string | null | undefined, presented: string | string[] | undefined): boolean {\n const p = Array.isArray(presented) ? presented[0] : presented\n if (!key || !p) return false\n const a = Buffer.from(key)\n const b = Buffer.from(p.trim())\n return a.length === b.length && timingSafeEqual(a, b)\n}\n\n// ── A mesh peer vouching for the owner (#407) ────────────────────────────\n//\n// The phone app paired with node A talks to an agent on node B through\n// node A's /mesh/task. Node A's operator key means nothing to node B, so\n// node A, having checked its own key, tells node B the turn is the\n// owner's. Node B believes that only from a request that carries one of\n// its peers' tokens and comes from another machine: never from this\n// machine, the dashboard token or a bare header. An agent on node A that\n// calls /mesh/task without the key gets no vouching, so the rule of #393\n// holds on node A. On node B the mesh token is in every agent's\n// environment, so a caller on node B itself is never believed, whichever\n// of this machine's addresses it dials.\n\n/** The header a forwarding node sets after checking its own operator key. */\nexport const OPERATOR_VOUCH_HEADER = \"x-agentx-operator-vouch\"\n\n/** Does this /mesh/task call come from the owner's surface with the key?\n * True only for a context on one of this node's own surfaces whose\n * caller showed this node's operator key. */\nexport function operatorVouch(\n context: unknown,\n headers: Record<string, string | string[] | undefined>,\n key: string | null | undefined,\n operatorChannels: ReadonlySet<string>,\n): boolean {\n if (!context || typeof context !== \"object\") return false\n const channel = String((context as Record<string, unknown>).channel ?? \"\").toLowerCase().split(\"@\")[0]\n if (!operatorChannels.has(channel)) return false\n return operatorKeyMatches(key, headers[OPERATOR_HEADER])\n}\n\n/** Does a peer vouch for the owner on this request? The vouch header must\n * be set and the bearer must be one of this node's peer tokens. */\nexport function peerVouches(\n headers: Record<string, string | string[] | undefined>,\n peerTokens: Iterable<string>,\n): boolean {\n const v = headers[OPERATOR_VOUCH_HEADER]\n if ((Array.isArray(v) ? v[0] : v) !== \"1\") return false\n const auth = headers.authorization\n const h = (Array.isArray(auth) ? auth[0] : auth) || \"\"\n const bearer = h.toLowerCase().startsWith(\"bearer \") ? h.slice(7).trim() : \"\"\n if (!bearer) return false\n const b = Buffer.from(bearer)\n for (const t of peerTokens) {\n if (!t) continue\n const a = Buffer.from(t)\n if (a.length === b.length && timingSafeEqual(a, b)) return true\n }\n return false\n}\n\n/** Is this the address of a caller on this machine? Loopback, or one of\n * this machine's own addresses: a local caller can dial those too. */\nexport function isThisMachine(\n remoteAddress: string | undefined,\n own: Iterable<string> = ownAddresses(),\n): boolean {\n const addr = plainAddress(remoteAddress ?? \"\")\n // No address to judge: treated as local, the side that believes nothing.\n if (!addr || addr === \"::1\" || addr.startsWith(\"127.\")) return true\n for (const a of own) if (plainAddress(a) === addr) return true\n return false\n}\n\nconst plainAddress = (a: string) => a.toLowerCase().replace(/^::ffff:/, \"\").replace(/%.*$/, \"\")\n\nfunction ownAddresses(): string[] {\n return Object.values(networkInterfaces()).flatMap((list) => (list ?? []).map((i) => i.address))\n}\n\n/** The whole rule of `POST /task`: is this turn the owner's? On one of\n * this node's own surfaces, and either the caller showed this node's\n * operator key, or a peer on another machine vouches with one of this\n * node's peer tokens. */\nexport function ownerProven(\n context: unknown,\n req: { headers: Record<string, string | string[] | undefined>; remoteAddress?: string },\n node: { key: string | null | undefined; peerTokens: Iterable<string>; operatorChannels: ReadonlySet<string>; ownAddresses?: Iterable<string> },\n): boolean {\n if (operatorVouch(context, req.headers, node.key, node.operatorChannels)) return true\n if (!context || typeof context !== \"object\") return false\n const channel = String((context as Record<string, unknown>).channel ?? \"\").toLowerCase().split(\"@\")[0]\n if (!node.operatorChannels.has(channel)) return false\n if (isThisMachine(req.remoteAddress, node.ownAddresses)) return false\n return peerVouches(req.headers, node.peerTokens)\n}\n","import { classifyInitiator, propagatedRootOf, type InitiatorContext } from \"@/a2a/initiator\"\nimport { OPERATOR_CHANNELS } from \"@/requests/tracker\"\nimport { isOperatorTurn } from \"@/requests/operator\"\n\n// --- People: one identity per human, whatever channel they use (#384) ---\n//\n// `people` in agentx.json lists the humans who talk to the agents. Each has\n// channel identities written \"channel:id\" (\"gitlab:sara\", \"whatsapp:21620123456\").\n// A turn a person starts is stamped with that person's id; the id travels\n// with the root initiator across agents and mesh peers.\n//\n// The rules:\n// - Only platform ids and logins are matched. Display names never are:\n// anyone can pick one.\n// - A sender that matches nobody is unknown (null). Never guessed.\n// - This machine's own surfaces (voice, app, dashboard) are the owner.\n// With no owner listed that is the built-in person \"owner\", so an\n// install with no `people` needs nothing configured.\n// - Turns software starts (cron, workflows, agent comments) have no person.\n//\n// Roles (#453): `owner` runs this install; `member` is a teammate; `client`\n// is someone the owner does work for, who gets a page of their own\n// (/member shows \"Your project\" instead of \"My work\"); `guest` is the\n// operator of another organisation's mesh. Only `owner` changes what a\n// turn may do; the other three are told apart on the pages they see.\n\nexport type PersonRole = \"owner\" | \"member\" | \"client\" | \"guest\"\nexport const PERSON_ROLES: readonly PersonRole[] = [\"owner\", \"member\", \"client\", \"guest\"]\n\nexport interface Person {\n id: string\n name: string\n /** How their name is said aloud (#433); see voice.pronunciations. */\n say?: string\n role: PersonRole\n /** \"channel:id\" entries: a login, a Telegram id, a WhatsApp number. */\n identities: string[]\n /** The agents this person may reach. Empty: every agent (#379). */\n agents?: string[]\n /** Tools and skills this person's turns may not use (#379). */\n deny?: PersonDeny\n}\n\n/** One list per level of control. A further level is a new key. */\nexport interface PersonDeny {\n tools?: string[]\n skills?: string[]\n}\n\n/** The person a turn is stamped with. `role` is set only on a turn this\n * node resolved itself; an id carried by a root has none. */\nexport interface PersonRef {\n id: string\n role?: PersonRole\n}\n\n/** The owner's id on an install that lists no owner. */\nexport const IMPLICIT_OWNER = \"owner\"\n\n/** The name shown for a stored person id: the listed name, \"Owner\" for the\n * built-in owner, else the id (a person removed from the list since). */\nexport function personName(people: Person[], id: string): string {\n return people.find((p) => p.id === id)?.name ?? (id === IMPLICIT_OWNER ? \"Owner\" : id)\n}\n\nconst base = (channel: string) => channel.toLowerCase().split(\"@\")[0]\n\n/** One comparable form per channel. WhatsApp numbers compare on digits, so\n * \"+216 20 123 456\" and \"21620123456@s.whatsapp.net\" are the same. */\nfunction normalize(channel: string, value: string): string {\n const v = value.trim().toLowerCase()\n return channel === \"whatsapp\" ? v.replace(/@.*$/, \"\").replace(/\\D/g, \"\") : v.replace(/^@/, \"\")\n}\n\nexport function splitIdentity(identity: string): { channel: string; value: string } | null {\n const i = identity.indexOf(\":\")\n if (i <= 0 || i === identity.length - 1) return null\n const channel = base(identity.slice(0, i).trim())\n const value = normalize(channel, identity.slice(i + 1))\n return value ? { channel, value } : null\n}\n\n/** What is wrong with a `people` list, or null. */\nexport function peopleProblem(people: Person[]): string | null {\n const ids = new Set<string>()\n const claimed = new Map<string, string>()\n for (const p of people) {\n if (ids.has(p.id)) return `person \"${p.id}\" is listed twice`\n ids.add(p.id)\n if (p.id === IMPLICIT_OWNER && p.role !== \"owner\") return `the id \"${IMPLICIT_OWNER}\" is kept for a person with the owner role`\n for (const identity of p.identities) {\n const parts = splitIdentity(identity)\n if (!parts) return `identity \"${identity}\" of \"${p.id}\" must be written channel:id`\n const key = `${parts.channel}:${parts.value}`\n const other = claimed.get(key)\n if (other && other !== p.id) return `identity \"${identity}\" belongs to both \"${other}\" and \"${p.id}\"`\n claimed.set(key, p.id)\n }\n }\n return null\n}\n\n/** The person behind a sender on a channel, or null when nobody matches. */\nexport function resolvePerson(\n people: Person[],\n channel: string,\n sender: { id?: string; username?: string } | undefined,\n): Person | null {\n const ch = base(channel)\n // On WhatsApp `username` is the chat's number, which for a message the\n // account owner sends is the other person. Only `id` is the sender.\n const given = ch === \"whatsapp\" ? [sender?.id] : [sender?.id, sender?.username]\n const ids = given.filter((v): v is string => typeof v === \"string\" && !!v).map((v) => normalize(ch, v)).filter(Boolean)\n if (!ids.length) return null\n return people.find((p) => p.identities.some((identity) => {\n const parts = splitIdentity(identity)\n return !!parts && parts.channel === ch && ids.includes(parts.value)\n })) ?? null\n}\n\n/** Who is at this machine's own surfaces. Two listed owners cannot be told\n * apart there, so the answer is nobody. */\nexport function operatorPerson(people: Person[]): PersonRef | null {\n const owners = people.filter((p) => p.role === \"owner\")\n if (owners.length === 0) return { id: IMPLICIT_OWNER, role: \"owner\" }\n return owners.length === 1 ? { id: owners[0].id, role: \"owner\" } : null\n}\n\n/** The person who started the chain this turn belongs to. A delegated hop\n * takes the id from the root it carries, with no role: a root is not\n * authenticated (a caller of the daemon's API or a mesh peer writes it), and\n * ids are per machine. A root turn is resolved here from the channel's own\n * sender fields. `context.person` is never read, for the same reason. */\nexport function personOfTurn(people: Person[], ctx: InitiatorContext | undefined | null): PersonRef | null {\n const root = propagatedRootOf(ctx)\n if (root) return root.person ? { id: root.person } : null\n if (!ctx || classifyInitiator(ctx) !== \"human\") return null\n const channel = String(ctx.channel ?? \"\")\n // This node's own surfaces name the operator only when the daemon marked\n // the turn as theirs (requests/operator, #393): a /task caller can name\n // the channel, not the person.\n if (OPERATOR_CHANNELS.has(base(channel))) return isOperatorTurn(ctx) ? operatorPerson(people) : null\n const str = (v: unknown) => (typeof v === \"string\" ? v : undefined)\n const person = resolvePerson(people, channel, { id: str(ctx.senderId), username: str(ctx.senderUsername) })\n return person ? { id: person.id, role: person.role } : null\n}\n\n// ── Permissions, first slice: per agent (#379) ───────────────────────────\n//\n// A listed person may be limited to named agents. The default is open:\n// a person with no list reaches every agent, and an unknown sender is\n// not limited here (channels decide who they answer at all). The limit\n// follows the person through a delegation too: work they started cannot\n// reach an agent they may not talk to. An owner is never limited: their\n// own surfaces (voice, app, dashboard) must keep reaching every agent.\n\n/** May this person reach `agentId`? */\nexport function agentAllowed(person: Pick<Person, \"agents\" | \"role\">, agentId: string): boolean {\n const list = person.agents ?? []\n return person.role === \"owner\" || list.length === 0 || list.includes(agentId)\n}\n\n/** Who a refused turn belongs to and the note it is answered with, or null\n * when the turn may run. The note names what the person can reach instead,\n * so the refusal is not a dead end. */\nexport function refusedPerson(people: Person[], agentId: string, ctx: InitiatorContext | undefined | null): { person: Person; note: string } | null {\n const ref = personOfTurn(people, ctx)\n if (!ref) return null\n const person = people.find((p) => p.id === ref.id)\n if (!person || agentAllowed(person, agentId)) return null\n const list = (person.agents ?? []).join(\", \")\n return { person, note: `${person.name}, you can reach ${list} here, not ${agentId}. Ask the owner if you need ${agentId}.` }\n}\n\n/** The note alone. */\nexport function personRefusal(people: Person[], agentId: string, ctx: InitiatorContext | undefined | null): string | null {\n return refusedPerson(people, agentId, ctx)?.note ?? null\n}\n\n// ── Permissions: per tool and per skill (#379) ───────────────────────────\n//\n// A listed person may be denied named tools and skills. Same rules as the\n// per-agent limit: open by default, never an owner, and it follows the\n// person through a delegation (the root's person id). Unlike a refused\n// agent, the turn still runs: the limit is a hard one, enforced on every\n// tool call by a per-run hook (guard/person-limits), not an instruction.\n\n/** The limits one run carries to the hook. */\nexport interface PersonLimits {\n personId: string\n name: string\n tools: string[]\n skills: string[]\n}\n\n/** Case-insensitive name match; `*` stands for any run of characters. */\nexport function nameMatches(pattern: string, name: string): boolean {\n const re = pattern.trim().split(\"*\").map((s) => s.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\".*\")\n return new RegExp(`^${re}$`, \"i\").test(name.trim())\n}\n\n/** The limits a turn runs under, or null when it has none. */\nexport function personLimitsOf(people: Person[], ctx: InitiatorContext | undefined | null): PersonLimits | null {\n const ref = personOfTurn(people, ctx)\n if (!ref) return null\n const person = people.find((p) => p.id === ref.id)\n if (!person || person.role === \"owner\") return null\n const tools = person.deny?.tools ?? []\n const skills = person.deny?.skills ?? []\n if (!tools.length && !skills.length) return null\n return { personId: person.id, name: person.name, tools: [...tools], skills: [...skills] }\n}\n","/** Every tool the agentx MCP server (src/mcp/index.ts) defines, by name.\n * Kept apart from that file so config validation can check a name\n * without loading the server. test/session-profile-agentx-tools.test.ts\n * fails when the two drift. */\nexport const AGENTX_TOOL_NAMES: readonly string[] = [\n \"agentx_generate\",\n \"agentx_inspect\",\n \"agentx_skill_match\",\n \"agentx_detect_output_type\",\n \"agentx_channel_reply\",\n \"agentx_channel_label\",\n \"agentx_send\",\n \"agentx_send_agent\",\n \"agentx_recent\",\n \"agentx_events\",\n \"agentx_send_contact\",\n \"agentx_task\",\n \"agentx_agents\",\n \"agentx_voice_queue\",\n \"agentx_call_owner\",\n \"agentx_camera_ask\",\n \"agentx_camera_look\",\n \"agentx_health\",\n \"agentx_attach_next\",\n \"agentx_attach_answer\",\n \"agentx_crons\",\n \"agentx_schedule\",\n \"agentx_approval\",\n \"agentx_request\",\n \"agentx_debug\",\n \"agentx_wiki_query\",\n \"agentx_wiki_patch\",\n \"agentx_wiki_interview\",\n \"agentx_graph_review\",\n]\n\n/** The names in `list` that are not agentx tools. */\nexport function unknownAgentxTools(list: readonly string[]): string[] {\n const known = new Set(AGENTX_TOOL_NAMES)\n return list.map((t) => t.trim()).filter((t) => t && !known.has(t))\n}\n","import { z } from \"zod\"\nimport { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { businessConfigSchema } from \"@/business/config\"\nimport { boardsConfigSchema, dashboardConfigSchema } from \"@/boards/config\"\nimport { autonomyLevelSchema } from \"@/guard/autonomy\"\nimport { DEFAULT_HOTKEYS, hotkeyError } from \"@/voice/hotkey\"\nimport { DEFAULT_PALETTE, ORB_PALETTE_IDS, VOICE_ANIMATIONS, VOICE_LOOKS } from \"@/voice/orb-palettes\"\nimport { SPOKEN_MAX_CHARS } from \"@/voice/speakable\"\nimport { NOISE_MARKERS } from \"@/voice/noise\"\nimport { whatsappTriageSchema } from \"@/whatsapp-triage/config\"\nimport { peopleProblem } from \"@/people/people\"\nimport { unknownAgentxTools } from \"@/mcp/tool-names\"\n\n/**\n * Load .env file into process.env (simple, no dependency).\n */\nfunction loadDotEnv(dir: string): void {\n const envPath = resolve(dir, \".env\")\n if (!existsSync(envPath)) return\n\n const content = readFileSync(envPath, \"utf-8\")\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim()\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n}\n\n// --- Daemon configuration schema & loader ---\n\n/** A list of agentx MCP tool names (session.lean.agentxTools). An unknown\n * name is an error, not ignored: a misspelt `agentx_channel_reply` would\n * otherwise leave a lean GitHub session with no way to reply. */\nconst agentxToolListSchema = z.array(z.string().min(1)).superRefine((list, ctx) => {\n const unknown = unknownAgentxTools(list)\n if (unknown.length) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `not an agentx tool: ${unknown.join(\", \")}. Use the names the agentx server lists, without \"mcp__agentx__\".`,\n })\n }\n})\n\nconst providerConfigSchema = z.object({\n apiKey: z.string().optional(),\n defaultModel: z.string().optional(),\n baseUrl: z.string().optional(),\n /** Toggle thinking-mode output on backends that support it (DeepSeek\n * V4 `reasoning_content`). When `true` (the DeepSeek default), the\n * agent streams its reasoning live in the dashboard task modal and\n * the agentic round-trip echoes it back on the next iteration. Set\n * to `false` to disable for cost/latency-sensitive agents. Ignored\n * on backends that don't speak the thinking-mode protocol. */\n thinking: z.boolean().optional(),\n})\n\nconst mcpStdioServerSchema = z.object({\n type: z.literal(\"stdio\").optional(),\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).optional(),\n})\n\nconst mcpHttpServerSchema = z.object({\n type: z.literal(\"http\"),\n url: z.string().url(),\n headers: z.record(z.string(), z.string()).optional(),\n})\n\n// HTTP variant first so a record with `type: \"http\"` matches the HTTP\n// shape; stdio is the default fallback when `type` is absent (keeps the\n// original `{ command, args, env }` configs valid without a migration).\nconst mcpServerSchema = z.union([mcpHttpServerSchema, mcpStdioServerSchema])\n\n/**\n * Per-agent integration registration. Declares which third-party services\n * an agent has credentials for — e.g. \"@cx-bot has a Telegram bot token,\n * a HubSpot private app token, and a Gmail account\". The actual secret is\n * NEVER stored in agentx.json: only an env-var reference (`tokenEnv`) or a\n * keyring directive. Skills, channel adapters, and the action layer read\n * from this registry to discover what an agent can use.\n *\n * The `kind` field is open-string (well-known values listed below in\n * INTEGRATION_KINDS) so new services can be declared without a schema\n * bump. Doctor warns on unknown kinds.\n */\nconst integrationCredentialsSchema = z.object({\n /** Env var holding the secret. Uppercase identifier; daemon resolves at use-time. */\n tokenEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n /** Alternate env-var keys for credential schemes that need >1 secret\n * (e.g. HubSpot private app + portal id, OAuth refresh tokens). */\n privateAppTokenEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n apiKeyEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n refreshTokenEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n clientIdEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n clientSecretEnv: z.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),\n /** OS keyring directive — credential lives in macOS Keychain / Linux\n * Secret Service / Windows Credential Manager. The skill that reads\n * this integration is responsible for resolving the keyring entry\n * (typically keyed by agentId+kind). */\n auth: z.literal(\"keyring\").optional(),\n /** File-based session directory (WhatsApp, Telegram MTProto, etc.). */\n sessionDir: z.string().optional(),\n}).strict()\n\nconst integrationSchema = z.object({\n /** Service identifier — open string, well-known values in INTEGRATION_KINDS. */\n kind: z.string().min(1),\n /** Human-readable label (e.g. \"@cx_bot\", \"Acme CRM\", \"alex@example.com\"). Unique per (agent, kind). */\n label: z.string().min(1),\n credentials: integrationCredentialsSchema.default({}),\n /** Non-secret metadata — username, email, host URL, portal id, JID, etc.\n * Visible in the dashboard and to skills. */\n metadata: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}),\n /** Operator-set toggle without removing the entry. */\n enabled: z.boolean().default(true),\n})\n\nexport type Integration = z.infer<typeof integrationSchema>\n\n/**\n * Canonical kind list — for autocomplete + doctor validation. Open string\n * schema so new services drop in without a bump; doctor warns when a kind\n * is outside this list.\n */\nexport const INTEGRATION_KINDS = [\n // Inbound channels\n \"telegram-bot\",\n \"whatsapp\",\n // Code platforms (user-scoped tokens, not channel routing)\n \"gitlab-user\",\n \"github-user\",\n // Email\n \"gmail\",\n \"hotmail\",\n \"smtp\",\n // CRM / ERP\n \"hubspot\",\n \"salesforce\",\n \"pipedrive\",\n \"odoo\",\n // Project management\n \"trello\",\n \"linear\",\n \"asana\",\n \"jira\",\n \"notion\",\n // Payments\n \"stripe\",\n // Observability / hosting\n \"sentry\",\n \"vercel\",\n // Voice / SMS\n \"twilio\",\n // Catch-all\n \"custom\",\n] as const\n\nconst voiceProviderSchema = z.enum([\"system\", \"elevenlabs\"])\n\n/** A macOS voice: a name (\"Daniel\", \"Ava (Premium)\") or identifier;\n * \"system\" for the OS default voice (the only way to get a Siri voice);\n * or one per language, picked by the language of each line:\n * { \"en\": \"Samantha\", \"fr\": \"Thomas\", \"ar\": \"Majed\" }. */\nconst systemVoiceSchema = z.union([z.string(), z.record(z.string(), z.string())])\n\n/** A keyboard shortcut for AgentX Voice, e.g. \"opt+space\" or\n * \"ctrl+opt+1\". See src/voice/hotkey.ts. */\nconst hotkeySchema = z.string().superRefine((s, ctx) => {\n const error = hotkeyError(s)\n if (error) ctx.addIssue({ code: z.ZodIssueCode.custom, message: `shortcut ${error}` })\n})\n\nconst voiceSchema = z.object({\n /** Overrides the global voice.provider for this agent. */\n provider: voiceProviderSchema.optional(),\n /** See systemVoiceSchema. Unset: one is assigned, of the agent's\n * gender when set, different from every other agent's. */\n system: systemVoiceSchema.optional(),\n /** Voices to try, in order, when `system` names one that is not\n * installed (e.g. [\"Ava (Premium)\", \"Allison\"]). Unset, or none\n * installed: the best installed voice of its language and gender. */\n fallbacks: z.array(z.string().min(1)).optional(),\n elevenlabsVoiceId: z.string().optional(),\n gender: z.enum([\"female\", \"male\", \"neutral\"]).optional(),\n /** A few words on manner, e.g. \"warm, upbeat, a little playful\". */\n style: z.string().optional(),\n /** The one-line self-introduction used on first contact. */\n intro: z.string().optional(),\n /** Speak short updates from this agent's real tool steps while it\n * works: \"on\" for everything but cron, \"all\" to include cron. */\n narrate: z.enum([\"off\", \"on\", \"all\"]).optional(),\n /** Speaking speed, 1 = normal. System voices: 175 words a minute times\n * this. ElevenLabs accepts 0.7 to 1.2 and is held to that range. */\n rate: z.number().min(0.75).max(1.5).optional(),\n /** Place in the speaking queue: \"high\" lines go ahead of waiting\n * \"normal\" ones, \"low\" lines after them. Unset: normal. */\n priority: z.enum([\"high\", \"normal\", \"low\"]).optional(),\n /** AgentX Voice: hold this shortcut and speak to ask this agent, without\n * changing the agent picked in the menu. */\n hotkey: hotkeySchema.optional(),\n})\n\n/** One event subscription (src/events/subscriptions.ts). An envelope\n * matches when its kind or type is in `kinds` and every other set filter\n * agrees. Every subscription is readable with `agentx_events`; `digest`\n * also briefs a fresh session and `wake` also starts a turn. */\nexport const eventSubscriptionSchema = z.object({\n /** Envelope `kind` (\"agent\", \"run\") or `type` (\"task:completed\"); \"*\" matches all. */\n kinds: z.array(z.string().min(1)).min(1),\n /** Only events published for these agent ids. */\n agents: z.array(z.string().min(1)).optional(),\n /** Only events published by these nodes (`node.name`). */\n nodes: z.array(z.string().min(1)).optional(),\n /** Only events whose summary contains this text (case-insensitive). */\n match: z.string().min(1).optional(),\n delivery: z.enum([\"pull\", \"digest\", \"wake\"]).default(\"pull\"),\n /** wake only: no wake when the agent was already woken this many times\n * in the past hour. */\n maxPerHour: z.number().int().min(1).max(60).default(4),\n})\n\nconst agentConfigSchema = z.object({\n name: z.string(),\n workspace: z.string(),\n tier: z.enum([\"claude-code\", \"codex-cli\", \"opencode\", \"sdk\", \"orchestrator\"]).default(\"claude-code\"),\n provider: z.string().optional(),\n model: z.string().optional(),\n systemPrompt: z.string().optional(),\n mentions: z.array(z.string()).default([]),\n /** Phase 5 — typed capabilities (drop-condition fallback). Free-form\n * list of intent strings this agent is allowed to handle. When set,\n * the org-chart `canHandle(agentId, project, intent)` check rejects\n * dispatches with intents not in this list. When empty/unset\n * (the default), the agent is permissive — handles any intent.\n * Intent matching is exact-string for now; a glob/prefix layer can\n * come later if it produces real rejections.\n * Example: [\"issue.opened\", \"issue.commented\", \"merge_request.opened\"]\n * Example: [\"cron.fired\", \"message.received\"] */\n intents: z.array(z.string()).default([]),\n /** Phase 8 — capability-bounded security. Max distinct upstream\n * agents in the delegation chain on the same (project, subject)\n * before a dispatch to THIS agent is refused. The ledger itself\n * provides the chain — `decideAndCommit` walks recent decisions on\n * the subject and counts distinct agents. Default 5; set to 0 to\n * disable for an agent that's always called as the bottom of a\n * chain. The check prevents cascade loops where A → B → A → B → ...\n * blows past sane chain depth. */\n maxDelegationDepth: z.number().int().min(0).max(50).default(5),\n /** MCP servers this agent's Claude Code session should load. Synced\n * to <workspace>/.mcp.json at daemon boot via agent-mcp.ts. Operator\n * edits to .mcp.json are respected (see SyncResult.skipped-operator-owned). */\n mcp: z.record(z.string(), mcpServerSchema).optional(),\n /** Enable CodeGraph (https://github.com/colbymchenry/codegraph) for this\n * agent's claude-code or codex-cli workspace. When true, agentx (a) adds\n * a codegraph MCP server to <workspace>/.mcp.json, (b) extends\n * .claude/settings.json permissions.allow with the codegraph_* tools,\n * (c) appends a CodeGraph instruction section to the managed CLAUDE.md,\n * and (d) background-indexes the workspace at daemon boot if .codegraph/\n * is missing. Off by default — flip on for coding agents (globex-v2-coder,\n * initech-v2-coder, umbrella-coding, etc.) where token-volume on exploration\n * loops is the bottleneck. */\n codegraph: z.boolean().default(false),\n /** Per-agent override for the global `session.contextStrategy`. Lets\n * one agent run `planner` (smaller upfront context, more tool-driven\n * exploration) while siblings stay on `layered`. Used for agents that\n * consistently bloat their cache via large workspace reads. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).optional(),\n /** When true, the registry resolves references-recipes for this agent's\n * workspace and renders a deterministic [Verified References] block at\n * priority 4.7. Off by default — flip on per agent (pm-initech, devops-agent,\n * etc.) once a `references/` registry exists in the agent's workspace\n * or repo root. See src/agents/references/. */\n contextReferences: z.boolean().default(false),\n /** Whether this agent may emit in-band `agentx:ui` rich-message directives\n * (buttons/polls/media) on chat channels. Default true — set false to make\n * the render layer strip any directive and send plain text only. */\n richMessages: z.boolean().default(true),\n maxConcurrent: z.number().default(1),\n /** Hard wall-clock cap on a single Claude Code invocation. Exceeding the\n * cap sends SIGTERM (exit 143). Default 20 min — bump for devops/coder\n * agents that do long investigations or multi-file refactors. */\n maxExecutionMinutes: z.number().int().min(1).max(240).default(20),\n /** Time a run may spend between taking a slot and starting its agent\n * process (request gate, classifier, compaction, context planning, model\n * routing, dispatch gates). Past it the run is aborted, its slot freed and\n * its record marked `timeout` with the step it was stuck in. Applies to\n * every run, whatever started it. */\n preSpawnTimeoutSec: z.number().int().min(10).max(3600).default(300),\n /** How long a daemon stop waits for this agent's runs before cutting\n * them off, when that is longer than shutdown.drainTimeoutSeconds. For\n * agents whose runs are long (renders, builds). */\n drainTimeoutSeconds: z.number().int().min(0).max(86_400).optional(),\n permissionMode: z.string().default(\"default\"),\n /** How this agent's `claude` CLI is billed (claude-code tier). Default\n * \"subscription\": the shared OAuth login, ANTHROPIC_API_KEY stripped.\n * \"api\": bill ANTHROPIC_API_KEY instead and drop the OAuth token, so\n * e.g. benchmark agents never draw on the fleet's subscription quota.\n * An \"api\" agent with no key fails its task rather than falling back. */\n billing: z.enum([\"subscription\", \"api\"]).default(\"subscription\"),\n /** Run GitHub coding tasks as Claude cloud sessions (`claude --cloud`)\n * instead of local runs (#622). Off by default. Needs\n * `channels.github.cloudSessions: true` too. Only a task from the GitHub\n * channel for an issue or pull request of a repository this node has a\n * checkout of (the project rule's `runbook` path, or the agent's\n * workspace) goes to the cloud; everything else runs locally. The result\n * is a pull request, not a reply. A launch that fails falls back to a\n * local run. See src/agents/cloud-sessions.ts. */\n cloudSessions: z.object({\n enabled: z.boolean().default(false),\n /** Launches per calendar day for this agent; 0 = no cap. */\n maxPerDay: z.number().int().min(0).default(0),\n /** Hours a launched session counts as open: no local run starts for\n * its issue, and comments on the issue are forwarded to it. */\n openHours: z.number().min(1).max(168).default(24),\n /** Seconds `claude --cloud` may take to print the session id before\n * the launch is given up and the task runs locally. */\n launchTimeoutSeconds: z.number().int().min(10).max(600).default(120),\n }).default({}),\n /** Improvement plan #3 — tool-use-required preset. When set,\n * AgentX inspects the stream-json events from each task and\n * fails the response with `tool_required_not_called: <name>`\n * when none of the listed tool names was invoked. Catches the\n * silent-degrade pattern observed when a model below the\n * capability bar (e.g. Haiku on a Write-required prompt)\n * produces a plausible-looking text response without ever\n * calling the required tool. Caller can retry with a stronger\n * model or sharpen the prompt.\n *\n * Tool names match the canonical Claude Code names (\"Write\",\n * \"Edit\", \"Bash\", \"Read\", …). Free-form so future tools work\n * without a schema bump. Empty/unset means no enforcement\n * (default — backward compatible). */\n toolUseRequired: z.array(z.string()).default([]),\n /** Per-agent override for the channel-level `autoReplyLegacy` on\n * gitlab/github. When set, it wins over `channels.<name>.autoReplyLegacy`\n * for this agent's replies: `true` makes the daemon auto-post the agent's\n * final text as a comment (right for coder agents that end their turn with\n * a summary and don't call `agentx_channel_reply`); `false` suppresses it\n * (right for chatty agents whose reasoning shouldn't land as a comment).\n * Unset → fall back to the channel default. */\n gitlabAutoReply: z.boolean().optional(),\n /** Opt into process reuse for claude-code (stream-json) or codex-cli\n * (app-server), or opencode (dedicated v2 server). Conversations have isolated processes. Unsupported Codex\n * versions fall back to exec before submitting any turn. Other tiers\n * ignore this flag. Codex loads the operator's Codex config, unlike exec's\n * --ignore-user-config path: review additional MCP servers before enabling.\n * Codex and OpenCode each use an 8-process cap and 5-minute idle eviction; processPool\n * settings apply to Claude only. Default false for gradual rollout.\n * See docs/architecture/persistent-codex-process.md and\n * docs/architecture/persistent-claude-process.md and\n * docs/architecture/persistent-opencode-process.md for details. */\n persistentProcess: z.boolean().default(false),\n queueMode: z.enum([\"collect\", \"followup\", \"drop\"]).default(\"collect\"),\n heartbeat: z.object({\n enabled: z.boolean().default(false),\n intervalMinutes: z.number().default(30),\n prompt: z.string().default(\"Check inbox, pending tasks, and system health. Report anything that needs attention.\"),\n channel: z.string().default(\"heartbeat\"),\n }).default({}),\n /** Reachability via the external public API.\n * - \"private\" (default): this agent is daemon-internal + channel-bound only.\n * - \"public\": external apps can POST /api/public/agents/<id>/messages\n * with a scoped token (agent:<id> or agent:*). */\n access: z.enum([\"private\", \"public\"]).default(\"private\"),\n /** Governance override for agent-facing management tools. An admin agent\n * may pause, resume and request deletion of schedules created by other\n * agents or by the operator. It never approves anything: approval of a\n * create/delete request stays operator-only (`agentx schedule approve`). */\n admin: z.boolean().optional(),\n /** Events on the bus this agent wants to hear about (issue #165). */\n subscriptions: z.array(eventSubscriptionSchema).default([]),\n /** Per-agent third-party integrations registry. Declares which services\n * this agent has credentials for (telegram-bot, hubspot, gitlab-user,\n * gmail, etc.). Secrets stay in env vars / keyring; this block holds\n * declarations + non-secret metadata only. Skills and channel adapters\n * read from here to know what the agent can use. See INTEGRATION_KINDS. */\n integrations: z.array(integrationSchema).default([]),\n /** How this agent sounds when it speaks (voice widget, `agentx teach`).\n * Every field is optional: no voice id falls back to the global\n * AGENTX_VOICE_ID, no intro is derived from the system prompt. See\n * src/voice/agent-voice.ts. */\n voice: voiceSchema.optional(),\n /** How this agent appears on screen: its own cursor, drawn by the Mac\n * helper and click-through, never the person's real mouse. See\n * src/voice/presence.ts. */\n presence: z.object({\n /** Cursor colour, as #RRGGBB. Default: derived from the agent id. */\n color: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),\n /** The voice orb's gradient, one of src/voice/orb-palettes.ts.\n * Default: the palette nearest `color`. */\n palette: z.enum(ORB_PALETTE_IDS).optional(),\n /** One or two letters on the cursor. Default: from the name. */\n initial: z.string().max(2).optional(),\n /** Name shown under the cursor. Default: the agent's name. */\n label: z.string().optional(),\n /** May this agent click and type for the person (presence mode \"act\")?\n * Off by default: without it, \"act\" becomes \"teach\". */\n allowActions: z.boolean().optional(),\n }).optional(),\n})\n\nconst telegramAccountSchema = z.object({\n token: z.string(),\n agentBinding: z.string(),\n /** Per-account sender allowlist. When set, OVERRIDES the global\n * `policy.allowFrom`. Entries accept:\n * - numeric user id (\"1816212449\") — matches sender (from.id)\n * - numeric chat id (\"-1003861455814\") — matches chat (chat.id)\n * - \"@username\" — matches sender username\n * - \"*\" — matches everyone (public bot)\n * A message is dispatched iff at least one entry matches. */\n allowFrom: z.array(z.string()).optional(),\n /** When false, the daemon keeps the bot token registered so outbound\n * `send()` / ring notifications work, but does NOT long-poll for inbound\n * updates. Use this on nodes where the bound agent lives on a different\n * daemon — otherwise two daemons race on the same `getUpdates` cursor and\n * Telegram responds 409 Conflict on every poll.\n * Defaults to true (poll) so existing single-node setups are unchanged. */\n pollInbound: z.boolean().default(true),\n})\n\n/** An agent watching the phone camera (#325 phase 2, src/camera/watch.ts). */\nexport const cameraBotSchema = z.object({\n /** Seconds between frames the agent gets by itself. 0: only when asked\n * (\"Look now\" on the phone, or the agent's own `agentx camera look`).\n * Each frame handed over by itself is a turn of the agent, so keep\n * this high or off. */\n frameIntervalSeconds: z.number().int().min(0).max(3600).default(0),\n /** The agent's watch ends after this many minutes, whatever the phone does. */\n maxSessionMinutes: z.number().int().min(1).max(240).default(10),\n /** Frames are shrunk so their longer side is at most this many pixels\n * before the agent sees them. */\n maxFrameEdge: z.number().int().min(160).max(3840).default(1024),\n /** Keep the frame files in the agent's workspace after the share ends.\n * Off: every frame is deleted when the share ends. */\n keepFrames: z.boolean().default(false),\n /** \"Keep watching\" on the phone (#687): for a task that needs a stream\n * (\"watch while I do this\"), the agent gets a frame every this many\n * seconds, only while the owner has it on. */\n streamFrameSeconds: z.number().int().min(2).max(60).default(5),\n /** \"Keep watching\" stops by itself after this many seconds. */\n streamMaxSeconds: z.number().int().min(10).max(600).default(60),\n}).default({})\n\nexport type CameraBotConfig = z.infer<typeof cameraBotSchema>\n\nconst channelsConfigSchema = z.object({\n telegram: z.object({\n enabled: z.boolean().default(false),\n accounts: z.record(z.string(), telegramAccountSchema).default({}),\n policy: z.object({\n dm: z.enum([\"pair\", \"block\"]).default(\"pair\"),\n group: z.enum([\"mention-required\", \"all\"]).default(\"mention-required\"),\n /** Global sender allowlist applied to every account that doesn't set\n * its own `allowFrom`. When neither is configured, every incoming\n * message is dropped — closed by default. Same entry forms as the\n * per-account list (user id, chat id, @username, or \"*\" for everyone). */\n allowFrom: z.array(z.string()).optional(),\n }).default({}),\n }).default({}),\n whatsapp: z.object({\n enabled: z.boolean().default(false),\n sessionDir: z.string().default(\".agentx/whatsapp-sessions\"),\n defaultAgent: z.string().optional(),\n allowFrom: z.array(z.string()).optional(),\n routes: z.array(z.object({\n contact: z.string().optional(),\n group: z.string().optional(),\n agent: z.string(),\n })).default([]),\n /** Data-source ingestion — turns WhatsApp from a messaging-only channel\n * into a source that seeds the wiki with contact/group metadata (and\n * optionally bounded message windows) per an explicit allowlist.\n * Default-deny: `enabled: false` and empty allowlists mean nothing is\n * ingested. See docs/reference/whatsapp-ingest.md for the full story. */\n ingest: z.object({\n enabled: z.boolean().default(false),\n /** `metadata-only` pulls contact/group info; `messages` additionally\n * pulls the last `messageCap` messages per allowlisted chat. */\n mode: z.enum([\"metadata-only\", \"messages\"]).default(\"metadata-only\"),\n /** Phone numbers or JIDs. Substring match, same semantics as allowFrom. */\n allowContacts: z.array(z.string()).default([]),\n allowGroups: z.array(z.string()).default([]),\n denyContacts: z.array(z.string()).default([]),\n denyGroups: z.array(z.string()).default([]),\n /** Per-chat cap when mode = \"messages\". Keeps the raw-entry size bounded. */\n messageCap: z.number().int().min(1).max(500).default(50),\n /** Max age (days) of messages to consider for the bounded window. */\n historyDays: z.number().int().min(1).max(365).default(30),\n /** Skip re-writing a contact entry unless this many days have passed\n * OR the profile hash differs. Prevents churn on unchanged profiles. */\n contactRefreshDays: z.number().int().min(1).max(90).default(7),\n /** Safety rails on live Baileys reads — personal-account accounts\n * can get throttled/banned under burst reads. */\n throttle: z.object({\n minMsBetweenCalls: z.number().int().min(100).default(1500),\n maxCallsPerMinute: z.number().int().min(1).default(20),\n maxChatsPerSweep: z.number().int().min(1).default(25),\n }).default({}),\n /** Purge absorbed raw entries older than this many days. `0` = never. */\n retentionDays: z.number().int().min(0).default(0),\n }).default({}),\n }).default({}),\n gitlab: z.object({\n enabled: z.boolean().default(false),\n webhookPort: z.number().default(18810),\n webhookSecret: z.string().optional(),\n host: z.string().default(\"https://gitlab.com\"),\n token: z.string().optional(),\n /** Legacy auto-reply: when true (default), the router posts the agent's\n * `response.content` as a comment automatically. When false, the agent\n * must call the `channel.reply` action explicitly to reply — agent\n * reasoning text is logged but not posted. Flip to false once the\n * project's agents have migrated their gitlab skill to use channel.reply.\n * Default true preserves today's behaviour for unmigrated projects. */\n autoReplyLegacy: z.boolean().default(true),\n routes: z.array(z.object({\n project: z.string(),\n agent: z.string(),\n })).default([]),\n agentMappings: z.array(z.object({\n agentId: z.string(),\n gitlabUsernames: z.array(z.string()).default([]),\n keywords: z.array(z.string()).default([]),\n token: z.string().optional(),\n /** If set, the agent lives on a remote mesh peer (node id). Forces the\n * username→agent map to resolve to this mapping even when a local\n * agent's token resolves to the same GitLab user — prevents collisions\n * like two agents both claiming @devops-acme. */\n node: z.string().optional(),\n })).default([]),\n /** Extra GitLab usernames each agent answers to, as prefixes. With\n * [\"team-\"], an agent with no agentMappings row answers to @<id> and\n * @team-<id>. Default: only @<id>. */\n agentUsernamePrefixes: z.array(z.string().min(1)).default([]),\n }).default({}),\n github: z.object({\n enabled: z.boolean().default(false),\n /** Legacy auto-reply — same semantics as gitlab.autoReplyLegacy.\n * Defaults true; set false once agent skills use channel.reply. */\n autoReplyLegacy: z.boolean().default(true),\n /** Let agents whose `cloudSessions.enabled` is on send this channel's\n * issue and pull request tasks to Claude cloud sessions (#622). Off by\n * default: both this and the agent setting must be on. */\n cloudSessions: z.boolean().default(false),\n /** GitHub PAT or env var (${GITHUB_TOKEN}) for posting comments back. */\n token: z.string().optional(),\n /** Path to file containing the token (first line read at startup). */\n tokenFile: z.string().optional(),\n /** GitHub App ID (legacy issuer). */\n appId: z.number().optional(),\n /** GitHub App Client ID (preferred JWT issuer per GitHub's updated docs). */\n clientId: z.string().optional(),\n /** Path to the GitHub App private key PEM file. */\n privateKeyFile: z.string().optional(),\n /** Webhook secret for validating X-Hub-Signature-256. */\n webhookSecret: z.string().optional(),\n /** Repo → agent routing. Use \"owner/repo\" or \"*\" for default. */\n routes: z.array(z.object({\n repo: z.string(),\n agent: z.string(),\n })).default([]),\n /** Per-agent identity mappings (GitHub usernames, tokens, mesh nodes). */\n agentMappings: z.array(z.object({\n agentId: z.string(),\n githubUsernames: z.array(z.string()).default([]),\n token: z.string().optional(),\n tokenFile: z.string().optional(),\n node: z.string().optional(),\n })).default([]),\n // --- Which issue and pull request events start a run (#612) ---\n /** Issue actions that start a run. A project rule whose `actions`\n * list is set replaces this list for its repository; a rule without\n * one keeps it, so `closed` never starts a run unless asked for. */\n issueActions: z.array(z.string().min(1)).default([\"opened\", \"reopened\", \"assigned\"]),\n /** Pull request actions that start a run. Same precedence as issueActions. */\n pullRequestActions: z.array(z.string().min(1)).default([\"opened\", \"reopened\", \"ready_for_review\"]),\n /** A label, assignment, close or edit made by an account AgentX posts\n * with (the App bot, a token owner, a mapped username, a mesh peer)\n * does not start a run: the owner sweep's own `agent:<id>` label woke\n * the agent it was filed for. Opened and reopened always count. */\n ignoreOwnChanges: z.boolean().default(true),\n /** Events on one issue or pull request within this many seconds become\n * one run, carrying the latest state. The window restarts with each\n * event. 0 starts a run per event. */\n debounceSeconds: z.number().min(0).max(3600).default(30),\n }).default({}),\n /** ntfy push notifications — outbound only. The operator-facing tap on\n * the shoulder: cron failures, task errors, and anything an agent decides\n * is worth interrupting a human for. `chatId` on an outgoing message is\n * the topic; empty falls back to `topic` here. */\n ntfy: z.object({\n enabled: z.boolean().default(false),\n /** Self-hosted ntfy base URL. Defaults to the public server. */\n server: z.string().default(\"https://ntfy.sh\"),\n /** Default topic. Treat it as a secret — on ntfy.sh, knowing the topic\n * is the only thing needed to read or publish to it. */\n topic: z.string().optional(),\n /** Access token for protected topics. */\n token: z.string().optional(),\n /** 1 (min) .. 5 (max). ntfy's own default is 3. */\n defaultPriority: z.number().int().min(1).max(5).default(3),\n defaultTitle: z.string().optional(),\n }).default({}),\n /** Web Push to the phone app (/app) — outbound only. The node that hosts\n * the app holds the subscriptions and sends; any other node sets\n * `relayTo` and forwards over the mesh. `chatId` \"default\" means every\n * subscribed phone, or a device id (tok_…) for one. */\n push: z.object({\n enabled: z.boolean().default(false),\n /** VAPID key pair written by `agentx app push-keys`. Relative paths\n * resolve from the folder AgentX runs in (the one holding agentx.json). */\n keysFile: z.string().default(\".agentx/push-keys.json\"),\n /** Contact for the push services: `mailto:you@example.com` or an\n * https:// URL. Required on the hosting node. */\n subject: z.string().regex(/^(mailto:|https:\\/\\/)/, \"must start with mailto: or https://\").optional(),\n /** Mesh peer (id or name) that hosts the phone app. Set it on every\n * other node; leave it unset on the host. */\n relayTo: z.string().optional(),\n /** How long a push service keeps trying an offline phone. */\n ttlSeconds: z.number().int().min(0).max(2419200).default(86400),\n /** Recent pushes kept for the app's Alerts tab. */\n keepRecent: z.number().int().min(0).max(1000).default(50),\n /** Push-service hosts a phone may subscribe with (a host or any\n * subdomain of it). The daemon POSTs to the stored address, so anything\n * else is refused. Defaults cover Chrome/Android, Firefox, Safari/iOS\n * and Edge. */\n allowedHosts: z.array(z.string().min(1)).default([\n \"fcm.googleapis.com\", \"push.services.mozilla.com\", \"push.apple.com\", \"notify.windows.com\",\n ]),\n }).default({}),\n webrtc: z.object({\n enabled: z.boolean().default(false),\n /** ICE STUN servers for NAT discovery. Default is Google's public STUN. */\n stunServers: z.array(z.string()).default([\"stun:stun.l.google.com:19302\"]),\n /** Optional TURN relays (required when both peers are behind symmetric NAT). */\n turnServers: z.array(z.object({\n urls: z.string(),\n username: z.string().optional(),\n credential: z.string().optional(),\n })).default([]),\n /** Peer names permitted to *initiate* a call into this daemon. Empty = allow all mesh peers. */\n allowedCallers: z.array(z.string()).default([]),\n /** Where to send \"someone is calling\" notifications when an inbound ring\n * arrives. Each entry is delivered via the matching channel adapter —\n * same plumbing as any other outbound message. Empty disables notifications. */\n ringNotify: z.array(z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n })).default([]),\n /** Base URL used when building the tap-to-join link in ring notifications.\n * Defaults to `http://<node.bind>` — override when the daemon's public\n * hostname differs (e.g. HTTPS-terminated tunnel, Tailscale MagicDNS). */\n callUrlBase: z.string().optional(),\n /** AI participant (\"bot\") joins calls when a browser opens with `?bot=<id>`.\n * v1 is transcribe-only — bot consumes remote audio, transcribes via\n * Whisper, posts chunks to the configured channel. No TTS-back. */\n bot: z.object({\n enabled: z.boolean().default(false),\n /** Default agent id used to attribute the transcript when the URL\n * doesn't override via `?bot=<other-agent>`. */\n defaultAgentId: z.string().optional(),\n /** \"auto\" tries mlx-whisper, falls back to OpenAI; explicit forces. */\n whisperBackend: z.enum([\"auto\", \"mlx\", \"openai\"]).default(\"auto\"),\n whisperModel: z.string().optional(),\n whisperLanguage: z.string().default(\"auto\"),\n /** Absolute path to mlx_whisper if not on the daemon's PATH (common\n * on macOS launchd, where ~/.pyenv/shims is missing from the env). */\n mlxBinary: z.string().optional(),\n /** Where to send each transcribed chunk. Same shape as ringNotify[]. */\n transcriptChannel: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Hard cap so a forgotten bot doesn't run forever. */\n maxCallMinutes: z.number().int().min(1).max(240).default(30),\n }).default({}),\n /** The phone app's Share camera (#325). The phone asks for this size and\n * rate; the browser picks the nearest the camera supports. The share\n * stops by itself after maxSeconds. */\n camera: z.object({\n width: z.number().int().min(160).max(3840).default(1280),\n height: z.number().int().min(120).max(2160).default(720),\n frameRate: z.number().int().min(1).max(60).default(15),\n maxSeconds: z.number().int().min(10).max(7200).default(600),\n /** Ask the watching agent by voice: hold or tap Talk (#687). Off: the\n * phone shows the text box only. The microphone opens only while\n * you talk and is never part of the share. */\n voiceInput: z.boolean().default(true),\n /** Say the watching agent's answers aloud on the phone, in the\n * agent's voice or the phone's own. The owner can mute it there. */\n speakAnswers: z.boolean().default(true),\n /** An agent watching the phone camera. The bot keeps only the newest\n * frame; the agent gets one when asked, and every\n * frameIntervalSeconds when that is set. */\n bot: cameraBotSchema,\n }).default({}),\n }).default({}),\n})\n\nconst cronJobSchema = z.object({\n enabled: z.boolean().default(true),\n schedule: z.string(),\n timezone: z.string().default(\"UTC\"),\n agent: z.string(),\n prompt: z.string().default(\"\"),\n /**\n * A shell command to run INSTEAD of dispatching the agent.\n *\n * Seven of this fleet's twenty-six crons were a single command wrapped\n * in an agent, and every one of them carried a paragraph like \"Run this\n * EXACT command. Do NOT substitute, rewrite, shorten, or fall back to\n * alternative commands.\" That paragraph is a scar: it exists because\n * models kept substituting, and no amount of prompt severity makes a\n * language model a deterministic executor.\n *\n * Measured before this existed: ~$0.31 and ~24s per run to execute one\n * `node script.js` and repeat ten lines of its output — a 114k-token\n * context for a job with no judgement in it at all.\n *\n * When set, the scheduler runs the command directly. No model, no\n * tokens, no improvisation. `agent` is still required and is who gets\n * told when it fails.\n */\n command: z.string().optional(),\n timeout: z.number().default(600),\n model: z.string().optional(),\n /** Soft cap on output length. Claude Code CLI has no hard flag for this,\n * but appending an instruction to the prompt is reliably honored. Use\n * 1500 for briefs, 500 for status pings, 300 for pure classifiers. */\n maxOutputTokens: z.number().int().min(50).max(8000).optional(),\n /** How much this routine may do: `report` (read-only), `propose` (branch,\n * commit, MR/draft; never merge/deploy/delete) or `act` (the agent's full\n * permissions — the default). Enforced per run by the guard, not by the\n * prompt; only the claude-code tier can enforce it, others refuse. */\n autonomy: autonomyLevelSchema.optional(),\n onError: z.union([\n z.enum([\"log\", \"notify\", \"disable\"]),\n z.array(z.enum([\"log\", \"notify\", \"disable\"])),\n ]).default(\"log\").transform(v => Array.isArray(v) ? v : [v]),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Whether each successful run's answer is sent to `notify` (failures\n * are alerted there either way). Default on: a schedule with somewhere\n * to report reports there, including the chat an agent's schedule was\n * requested from (#738). `false` keeps `notify` for failure alerts only. */\n deliverResult: z.boolean().optional(),\n /** Secret that lets an external system fire this job now via\n * `POST /routines/<id>/fire`. Reference an env var (`\"${MY_TOKEN}\"`);\n * a job without one cannot be fired. See src/daemon/routine-fire.ts. */\n fireToken: z.string().optional(),\n /** Agent id that created this job through the agent `schedule` tool.\n * Absent for operator-created jobs. Drives the ownership rule: an agent\n * manages only the routines it created, unless it is `admin`. */\n createdBy: z.string().optional(),\n /** Pending operator approval for an agent-requested change. While a\n * `create` is pending the job never runs, whatever `enabled` says; a\n * pending `delete` leaves the job as it was until approved. Cleared by\n * `agentx schedule approve|reject <id>`. */\n approval: z.object({\n action: z.enum([\"create\", \"delete\"]),\n requestedBy: z.string(),\n requestedAt: z.string(),\n }).optional(),\n}).refine((j) => Boolean(j.command?.trim() || j.prompt?.trim()), {\n message: \"a cron needs either a prompt (dispatch an agent) or a command (run it directly)\",\n}).refine((j) => !(j.command?.trim() && j.autonomy && j.autonomy !== \"act\"), {\n message: \"autonomy applies to agent routines; a command cron runs no agent (remove autonomy or the command)\",\n})\n\nconst serviceSchema = z.object({\n name: z.string(),\n triggers: z.array(z.object({\n pattern: z.string(),\n channel: z.string().optional(),\n })),\n allowedContacts: z.array(z.string()).optional(),\n agent: z.string(),\n prompt: z.string(),\n schedule: z.string().optional(),\n timezone: z.string().default(\"UTC\"),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst meshPeerSchema = z.object({\n url: z.string(),\n name: z.string(),\n token: z.string().optional(),\n})\n\n/** A named, operator-declared destination that remote mesh nodes may send\n * to. Deliberately minimal: the published surface is the name and whether\n * it accepts, so there is nothing here that could leak local state if it\n * were echoed back. Which agent handles it stays private to this node. */\nconst meshInboxSchema = z.object({\n name: z.string(),\n agent: z.string(),\n enabled: z.boolean().default(true),\n})\n\nconst meshConfigSchema = z.object({\n enabled: z.boolean().default(false),\n peers: z.array(meshPeerSchema).default([]),\n /** Empty by default — a node publishes no inboxes until an operator\n * declares one, so this feature is opt-in per node. */\n inboxes: z.array(meshInboxSchema).default([]),\n discovery: z.enum([\"static\", \"mdns\"]).default(\"static\"),\n healthCheck: z.object({\n interval: z.number().default(60),\n timeout: z.number().default(10),\n }).default({}),\n /** Peer event feed (#166): follow each healthy peer's /events and merge\n * its own events into this node's bus. `skipTypes` are left out on the\n * peer's side; per-step agent activity is too chatty to cross the mesh. */\n feed: z.object({\n enabled: z.boolean().default(true),\n skipTypes: z.array(z.string().min(1)).default([\"task:step\"]),\n }).default({}),\n /** Agent-to-agent delegation (#277), local or across peers. When a person\n * started the conversation, a delegation returns at once and the answer\n * comes back to the asking agent as a new turn in the same chat. */\n delegation: z.object({\n /** Off: every delegation waits for its answer, as before. */\n asyncWhenHuman: z.boolean().default(true),\n /** A delegation with no answer after this long is reported as timed out. */\n timeoutMinutes: z.number().int().min(1).max(240).default(30),\n }).default({}),\n})\n\n/** Intent Knowledge Graph — fixed-axis, LLM-proposed taxonomy used by the\n * Intent layer in context.ts and (eventually) wiki retrieval. Off by default\n * so existing installs see no change. */\nconst graphConfigSchema = z.object({\n enabled: z.boolean().default(false),\n /** Where the schema/nodes/classifications live. Relative to cwd. */\n baseDir: z.string().default(\".agentx/graph\"),\n /** Which agent makes LLM classification proposals. Falls back to\n * `dashboard.draftAgent` at call sites if unset. */\n draftAgent: z.string().optional(),\n /** Agent used by `agentx graph review` to triage pending classifications.\n * Should be an agent with the wiki skill so it can call `wiki query` for\n * context before deciding approve/reject. Falls back to draftAgent. */\n reviewAgent: z.string().optional(),\n /** Structural auto-approval policy. Evaluated against each classification\n * independently of `autoApproveConfidence`; either triggers approval.\n * - \"strict\" : never approve via structure — every classification\n * waits for human review\n * - \"extend-leaves\" : auto-approve when the proposed path (a) reuses only\n * existing nodes, or (b) adds exactly one NEW node at\n * the deepest level. Structural changes (new mid-path\n * node, new root) still queue for review. Default.\n * - \"any\" : auto-approve every classification regardless of\n * structural change\n */\n autoApproveStructure: z.enum([\"strict\", \"extend-leaves\", \"any\"]).default(\"extend-leaves\"),\n /** Minimum classifier confidence (0..1) to auto-approve. OR'd with the\n * structural policy — either hitting the threshold lets the classification\n * bypass the pending queue. 1.0 (default) disables this knob, so approval\n * is driven by `autoApproveStructure` alone. Lower it to e.g. 0.7 if you\n * also want high-confidence structural changes auto-approved. */\n autoApproveConfidence: z.number().min(0).max(1).default(1.0),\n /** Anthropic model id used for the direct classifier call (Phase 2 of\n * classifier-retire). Default haiku — classification is metadata, not\n * work, so we use the cheapest fast model. Override only if Haiku is\n * rate-limited or you want to A/B test. */\n classifierModel: z.string().default(\"claude-haiku-4-5-20251001\"),\n /** Weights for the wiki hybrid retrieval score. Path-ancestry match vs\n * BM25 over article text. Sum need not be 1. */\n retrievalWeights: z.object({\n graph: z.number().min(0).default(0.6),\n bm25: z.number().min(0).default(0.4),\n }).default({}),\n}).default({})\n\n// --- Typed-decision seats (src/decisions) ---\n//\n// Everything here defaults off. A seat in \"shadow\" runs alongside the\n// incumbent and only records; \"active\" lets its answer steer behaviour, and\n// no seat should reach it before its calibration report says what threshold\n// to use and what that threshold costs.\nconst decisionSeatSchema = z.object({\n /** off: the seat is unreachable. shadow: runs and records, incumbent stays\n * authoritative. active: the answer is used. */\n mode: z.enum([\"off\", \"shadow\", \"active\"]).default(\"off\"),\n /** Backend name from the decisions registry. Per-seat, so one seat can be\n * graded against a different backend than another at the same time. */\n backend: z.string().optional(),\n model: z.string().optional(),\n /** Unset: 10 s (DEFAULT_SEAT_TIMEOUT_MS), except where a call site sets\n * its own default, as request-gate and request-context do (3 s). Left\n * without a schema default so those seats can tell a value the operator\n * wrote from one nobody chose. */\n timeoutMs: z.number().int().min(100).optional(),\n /** Post-hoc temperature fitted on this seat's own labeled rows. 1 means\n * \"not calibrated yet\", which is where every seat starts — see\n * `agentx decisions calibrate`. */\n temperature: z.number().min(0.01).default(1),\n /** Fraction of would-be-skips to run anyway. Not a tuning knob: a skipped\n * decision can never be graded, so without exploration an active seat's\n * metrics are computed on the biased sample it selected for itself.\n * Setting this to 0 in active mode means the calibration report stops\n * being trustworthy the day you promote the seat. */\n explore: z.number().min(0).max(1).default(0.15),\n /** Fraction of turns that bypass the seat as a randomized control group.\n * Unset lets the seat pick its own default; only seats that run an\n * experiment read it (today: request-gate, default 0.1). */\n holdout: z.number().min(0).max(1).optional(),\n}).default({})\n\nconst decisionsConfigSchema = z.object({\n enabled: z.boolean().default(false),\n dbPath: z.string().default(\".agentx/decisions/decisions.sqlite\"),\n /** State can contain message text. With this on, only the hash is kept,\n * which costs the ability to replay a decision offline. */\n redactState: z.boolean().default(false),\n /** Keep the serialized state for this many rows per seat, then prune to\n * hashes. Enough to replay and to grade an alternative backend on\n * identical inputs, without growing without bound. */\n keepStateRows: z.number().int().min(0).default(2000),\n defaultBackend: z.string().default(\"local\"),\n backends: z.object({\n local: z.object({\n provider: z.string().default(\"claude-code\"),\n model: z.string().default(\"claude-haiku-4-5-20251001\"),\n /** auto resolves to a forced tool where the provider guarantees one,\n * and JSON-in-text otherwise (claude-code on OAuth cannot force a\n * tool). Never resolves to logprobs: that costs one call per\n * question, so it has to be asked for. */\n structureMode: z.enum([\"auto\", \"tool\", \"text\"]).default(\"auto\"),\n normalizeProbabilities: z.boolean().default(true),\n nRetryMalformedStructure: z.number().int().min(0).max(3).default(1),\n maxStateChars: z.number().int().min(500).default(24_000),\n }).default({}),\n /** A simple-jev server (featherless-ai/simple-jev). Reads the model's\n * next-token logits rather than asking it to write a probability, so\n * the distribution is the model's posterior instead of a number it\n * chose. Still not calibrated to correctness — that comes from\n * `agentx decisions recalibrate`. */\n simpleJev: z.object({\n baseUrl: z.string().default(\"http://127.0.0.1:8000/v1\"),\n model: z.string().optional(),\n apiKeyEnv: z.string().optional(),\n timeoutMs: z.number().int().min(100).default(30_000),\n maxStateChars: z.number().int().min(500).default(6_000),\n /** Their Choice takes 2-50 candidates, well under Jev's 255. */\n maxChoiceOptions: z.number().int().min(2).max(255).default(50),\n }).default({}),\n /** TypeSafe Jev through OpenRouter's alpha decisions endpoint.\n * Verified live 2026-09-19 — resolves to typesafe/jev-1.13-20260917,\n * provider \"TypeSafe\", ~0.5s round trip. The id is \"jev-latest\";\n * the namespaced \"typesafe/jev-latest\" 400s, and neither appears in\n * OpenRouter's public model list. Needs OPENROUTER_API_KEY. */\n jev: z.object({\n baseUrl: z.string().default(\"https://openrouter.ai/api/alpha\"),\n path: z.string().default(\"/decisions\"),\n model: z.string().default(\"jev-latest\"),\n apiKeyEnv: z.string().default(\"OPENROUTER_API_KEY\"),\n timeoutMs: z.number().int().min(100).default(30_000),\n maxStateChars: z.number().int().min(500).default(90_000),\n maxChoiceOptions: z.number().int().min(2).max(255).default(255),\n }).default({}),\n /** Jev direct from TypeSafe rather than through OpenRouter. Same\n * contract, different billing and rate limits. Needs TYPESAFE_API_KEY. */\n typesafe: z.object({\n baseUrl: z.string().default(\"https://api.typesafe.ai/v1\"),\n path: z.string().default(\"/systemone\"),\n model: z.string().default(\"jev-latest\"),\n apiKeyEnv: z.string().default(\"TYPESAFE_API_KEY\"),\n timeoutMs: z.number().int().min(100).default(30_000),\n maxStateChars: z.number().int().min(500).default(90_000),\n maxChoiceOptions: z.number().int().min(2).max(255).default(255),\n }).default({}),\n }).default({}),\n seats: z.record(z.string(), decisionSeatSchema).default({}),\n /** Model routing driven by the task-tier seat. Models are scoped to their\n * CLI engine; the legacy cheapModel applies only to Claude Code. */\n routing: z.object({\n cheapModel: z.string().optional(),\n cheapModels: z.object({\n \"claude-code\": z.string().optional(),\n \"codex-cli\": z.string().optional(),\n }).optional(),\n }).default({}),\n}).default({})\n\n/** Agents ringing the owner for a live voice call (src/calls). */\nexport const callsSchema = z.object({\n /** Agents allowed to call: ids, or \"*\" for every agent on this node.\n * Empty: nobody can call until the owner allows it. */\n allow: z.array(z.string()).default([]),\n /** Most calls one agent may place in an hour. */\n maxPerHour: z.number().int().min(1).max(60).default(3),\n /** How long a call rings before it counts as missed. */\n ringSeconds: z.number().int().min(10).max(300).default(45),\n /** An answered call nobody hung up (widget quit or crashed, the Mac\n * slept, `agentx call answer` without a conversation) ends after this,\n * so the agent can call again. */\n maxCallMinutes: z.number().int().min(1).max(240).default(30),\n /** Ring sound: a name from /System/Library/Sounds, without the extension. */\n ringSound: z.string().regex(/^[\\w -]+$/).default(\"Submarine\"),\n /** After hang-up, ask the agent for a short summary and file it in the\n * dashboard's conversation history. */\n summary: z.boolean().default(true),\n}).default({})\n\nexport type CallsConfig = z.infer<typeof callsSchema>\n\nconst notificationsSchema = z.object({\n /** Send notification when task takes longer than this (seconds). 0 = disabled. */\n longTaskThreshold: z.number().default(30),\n /** Where to send notifications */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Channel `agentx notify` and Focus digests use when none is given.\n * Unset: `push` (the phone app) when channels.push is enabled, else\n * `ntfy`, so upgrading never points notify at a channel that is off. */\n channel: z.string().optional(),\n /** Notify on these events */\n on: z.object({\n taskComplete: z.boolean().default(true),\n taskError: z.boolean().default(true),\n taskQueued: z.boolean().default(false),\n }).default({}),\n /** What `agentx notify` does on this machine when it delivers: a desktop\n * banner and a system sound. macOS only; see src/notify/local.ts. */\n local: z.object({\n banner: z.boolean().default(true),\n sound: z.boolean().default(true),\n /** A name from /System/Library/Sounds, without the extension. */\n soundName: z.string().regex(/^[\\w -]+$/).default(\"Glass\"),\n volume: z.number().min(0).max(1).default(0.4),\n /** Image for the AgentX Helper icon on banners (.png, .jpg, .icns).\n * Unset: the AgentX logo. Applied by `agentx desktop install`. */\n icon: z.string().optional(),\n }).default({}),\n}).default({})\n\nconst screenRectSchema = z.object({\n x: z.number(), y: z.number(), width: z.number().positive(), height: z.number().positive(),\n})\n\n/** Screen capture for agents: how captures are cropped and scaled, how\n * long they wait for the right moment, named regions, and the opt-in\n * in-memory buffer. See src/computer-use/capture.ts. */\nconst screenSchema = z.object({\n /** Pixel budget for a captured frame; larger captures are downscaled. */\n maxPixels: z.number().int().positive().default(1_200_000),\n /** Longest a capture waits for a change or for the screen to settle. */\n timeoutMs: z.number().int().positive().default(5_000),\n /** Time between samples while waiting. */\n intervalMs: z.number().int().positive().default(100),\n /** Mean difference (0–1) above which two samples count as different. */\n changeThreshold: z.number().min(0).max(1).default(0.015),\n /** How long a region must stay unchanged to count as stable. */\n stableMs: z.number().int().nonnegative().default(400),\n /** Named regions in screen points, e.g. { \"chat\": { x, y, width, height } }.\n * A name here overrides the built-in region of the same name. */\n regions: z.record(z.string().regex(/^[a-z][\\w-]*$/), screenRectSchema).default({}),\n /** The last few seconds of a region, kept in memory by the daemon. Off\n * unless enabled; frames are written out only when asked for. */\n buffer: z.object({\n enabled: z.boolean().default(false),\n seconds: z.number().positive().max(120).default(10),\n fps: z.number().positive().max(10).default(2),\n region: z.string().default(\"screen\"),\n maxPixels: z.number().int().positive().default(300_000),\n }).default({}),\n}).default({})\n\n/** The Approvals inbox (src/approvals). Decision cards agents raise, and\n * when the operator hears about what is waiting. */\nexport const requestsConfigSchema = z.object({\n enabled: z.boolean().default(false),\n /** Channels to capture on. Empty: every channel a person writes on\n * (telegram, whatsapp, slack, discord, gitlab, github, app, voice,\n * dashboard, webrtc). */\n channels: z.array(z.string().min(1)).default([]),\n /** Who counts as the owner on channels other people can reach, as\n * \"channel:id\": the login on GitLab and GitHub, the sender id elsewhere.\n * Usernames beside an id and display names are not matched. Empty: only\n * this node's own surfaces (voice, app, dashboard, webrtc) are captured. */\n from: z.array(z.string().regex(/^[a-z0-9_-]+:\\S+$/i, \"use channel:id, for example telegram:123456789\")).default([]),\n /** An open request with no activity for this long comes back to the owner. */\n staleAfterHours: z.number().positive().max(24 * 365).default(24),\n /** Closed requests are deleted after this many days. Open ones never are. */\n retentionDays: z.number().positive().max(3650).default(90),\n}).default({})\n\n/** People (src/people, #384): the humans who talk to the agents, one entry\n * per person whatever channel they use. Empty: this machine's owner is the\n * only known person and nothing else changes. */\nexport const personSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,39}$/, \"lower-case letters, digits, - and _\"),\n name: z.string().min(1).max(80),\n /** How this person's name is said aloud, e.g. \"A-neess Ma-roo-shee\"\n * (#433). Written forms stay as they are; see voice.pronunciations. */\n say: z.string().trim().min(1).max(120).optional(),\n /** `client` (#453): someone the owner works for; /member shows them\n * \"Your project\", not a teammate's \"My work\". */\n role: z.enum([\"owner\", \"member\", \"client\", \"guest\"]).default(\"member\"),\n /** \"channel:id\": a GitLab or GitHub login, a Telegram id or username, a\n * WhatsApp number. Display names are not matched. */\n identities: z.array(z.string().regex(/^[A-Za-z][\\w-]*:\\S.*$/, \"write it as channel:id\")).default([]),\n /** The agents this person may reach, by id. Empty: every agent (#379). */\n agents: z.array(z.string().min(1)).default([]),\n /** What this person's turns may not use (#379), enforced by a per-run\n * guard hook. Names match without case; `*` is a wildcard. Empty: no\n * limit. A further level is a new key here. */\n deny: z.object({\n /** Tool names: \"Bash\", \"WebFetch\", \"mcp__mail__*\". */\n tools: z.array(z.string().min(1)).default([]),\n /** Skill names, as the Skill tool and auto-injection name them. */\n skills: z.array(z.string().min(1)).default([]),\n }).strict().default({}),\n})\n\nexport const peopleConfigSchema = z.array(personSchema).default([]).superRefine((people, ctx) => {\n const problem = peopleProblem(people)\n if (problem) ctx.addIssue({ code: z.ZodIssueCode.custom, message: problem })\n})\n\n/** Teammates' machines and their trail (src/members, #385, #379). */\nexport const membersConfigSchema = z.object({\n /** Days the per-person log (invites, pairings, sign-ins, refusals,\n * removals) is kept. Older lines are dropped. */\n logRetentionDays: z.number().int().min(1).max(3650).default(90),\n}).default({})\n\nexport const requestStatusConfigSchema = z.object({\n /** Channels that show it. On gitlab and github: one comment per request,\n * kept up to date by the daemon. Empty: off. */\n channels: z.array(z.string().min(1)).default([]),\n}).default({})\n\nexport const approvalsConfigSchema = z.object({\n /** A card without an `expires` gets this many days. */\n defaultExpiryDays: z.number().positive().max(365).default(3),\n /** No card waits longer than this, whatever the agent asked for. */\n maxExpiryDays: z.number().positive().max(365).default(30),\n /** How long \"later\" hides an item. */\n laterHours: z.number().positive().max(24 * 30).default(24),\n /** Tell the agent that raised a card when it is decided or expires. */\n notifyAgent: z.boolean().default(true),\n /** The mesh peer (`mesh.peers[].name`) whose inbox and popup take the\n * cards agents on this node raise (#668). The result comes back here\n * and reaches the agent. Unset: cards stay on this node. */\n forwardTo: z.string().min(1).optional(),\n /** At most one message a day: how many are waiting, and the most urgent. */\n digest: z.object({\n enabled: z.boolean().default(true),\n /** Local time, 24-hour HH:MM. */\n time: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\").default(\"09:00\"),\n /** IANA timezone for `time`. Unset: this machine's. */\n timezone: z.string().refine((tz) => {\n try { new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }); return true } catch { return false }\n }, \"unknown timezone\").optional(),\n /** Where it goes. Unset: notifications.destination. */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n }).default({}),\n /** A popup on this Mac for new decision cards: sound, a spoken line,\n * ready-made choices. Held during Focus. macOS only. */\n popup: z.object({\n enabled: z.boolean().default(false),\n /** \"card\": a small web window; \"dialog\": plain macOS dialogs. */\n style: z.enum([\"card\", \"dialog\"]).default(\"card\"),\n /** The card's colours; \"system\" follows light or dark mode. */\n theme: z.enum([\"system\", \"light\", \"dark\"]).default(\"system\"),\n /** Speak a short line when it opens. */\n speak: z.boolean().default(true),\n /** A macOS voice name for `say -v`. Unset: the system voice. */\n voice: z.string().regex(/^[\\w .()-]+$/, \"a voice name, as `say -v '?'` lists them\").optional(),\n /** \"chime\" (a soft chime played by the card), a system sound from\n * /System/Library/Sounds like Glass, or \"\" for none. */\n sound: z.string().regex(/^[\\w -]*$/, \"a sound name, like chime or Glass\").default(\"chime\"),\n volume: z.number().min(0).max(1).default(0.4),\n /** Seconds the popup waits for an answer; then the card stays in the inbox. */\n timeoutSeconds: z.number().int().min(10).max(3600).default(600),\n }).default({}),\n /** Check-ins (src/approvals/checkin.ts): a few times a day, waiting cards\n * come back to the Mac card and the operator's open Apple Reminders get\n * a card written by the agent that owns them. macOS only. */\n checkin: z.object({\n enabled: z.boolean().default(false),\n /** Local times of a normal pass: reminders due soon, and waiting cards. */\n times: z.array(z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\")).default([\"11:00\", \"14:00\", \"17:00\"]),\n /** Local time of the daily pass: every open reminder. */\n dailyAt: z.string().regex(/^([01]\\d|2[0-3]):[0-5]\\d$/, \"use HH:MM, 24-hour\").default(\"09:00\"),\n /** IANA timezone for the times. Unset: this machine's. */\n timezone: z.string().refine((tz) => {\n try { new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }); return true } catch { return false }\n }, \"unknown timezone\").optional(),\n /** The operator's Reminders lists to look at. */\n lists: z.array(z.string().min(1)).default([\"Reminders\"]),\n /** Agent that writes cards for reminders without an agentx trailer. */\n agent: z.string().min(1).optional(),\n /** A normal pass takes reminders due within this many hours, or overdue. */\n dueWithinHours: z.number().positive().max(24 * 30).default(24),\n /** Most agents one pass asks to write a card, whatever they answer. */\n maxAsksPerPass: z.number().int().min(1).max(20).default(5),\n /** How long an agent may take to write one card, counted from the start\n * of its turn. The turn is stopped at the limit. */\n composeTimeoutSeconds: z.number().int().min(30).max(3600).default(300),\n }).default({}),\n}).default({})\n\nexport const daemonConfigSchema = z.object({\n node: z.object({\n id: z.string(),\n name: z.string(),\n bind: z.string().default(\"127.0.0.1:18800\"),\n /** Default agent for voice/API calls that don't specify one (e.g. Siri) */\n defaultAgent: z.string().optional(),\n }),\n providers: z.record(z.string(), providerConfigSchema).default({}),\n agents: z.record(z.string(), agentConfigSchema).default({}),\n channels: channelsConfigSchema.default({}),\n crons: z.record(z.string(), cronJobSchema).default({}),\n services: z.record(z.string(), serviceSchema).default({}),\n notifications: notificationsSchema,\n calls: callsSchema,\n /** Watched WhatsApp chats triaged by an agent (src/whatsapp-triage). */\n whatsappTriage: whatsappTriageSchema,\n approvals: approvalsConfigSchema,\n /** How a daemon stop treats runs still in flight. */\n shutdown: z.object({\n /** Wait this long for runs to finish before cutting them off (they are\n * then resumed or reported after the restart). Unset: the\n * AGENTX_DRAIN_TIMEOUT_MS env var, else 300 s. Keep the service\n * manager's stop timeout above it, or it kills the daemon mid-wait. */\n drainTimeoutSeconds: z.number().int().min(0).max(86_400).optional(),\n /** Who may ask for \"restart when idle\", and what happens to the others.\n * Unset: any caller may, and onTimeout \"restart\" is honoured. */\n restart: z.object({\n /** Regular expression matched against the request's `by`. Requests\n * from anyone else are held until `window`, or refused when there\n * is no window. */\n allowBy: z.string().optional(),\n /** Local time \"HH:MM\" at which held requests start an idle-only wait. */\n window: z.string().regex(/^\\d{1,2}:\\d{2}$/, \"window must be HH:MM\").optional(),\n /** How long that wait lasts before it gives up. */\n windowWaitMinutes: z.number().int().min(1).max(1440).default(180),\n /** Turn every request into an idle-only one: a wait that runs out\n * gives up instead of restarting over running work. */\n forbidOnTimeoutRestart: z.boolean().default(false),\n }).default({}),\n }).default({}),\n /** What happens to runs a restart cuts off (agents/resume). Chat messages\n * are resumed in their chat; scheduled jobs never are (the next run\n * covers them); everything else is reported unless its channel is in\n * directChannels. */\n resume: z.object({\n enabled: z.boolean().default(true),\n /** Older runs are reported, not resumed. */\n maxAgeMinutes: z.number().int().positive().default(30),\n /** A run cut off while resuming is not retried past this. */\n maxAttempts: z.number().int().min(0).default(1),\n /** Channels whose runs are only reported, even from a chat. */\n reportOnlyChannels: z.array(z.string()).default([]),\n /** Non-chat runs (voice, agent-to-agent, webhooks…) resumed on these\n * channels. Their answer isn't delivered anywhere; opt in only for\n * runs whose work is the point. */\n directChannels: z.array(z.string()).default([]),\n /** This many restarts within the window pauses resuming. */\n crashLoop: z.object({\n restarts: z.number().int().positive().default(3),\n windowMinutes: z.number().positive().default(10),\n }).default({}),\n }).default({}),\n /** Open requests (src/requests, #356): what a person asked an agent for\n * is recorded and followed until it is done, declined or dropped. Off\n * by default. */\n requests: requestsConfigSchema,\n people: peopleConfigSchema,\n members: membersConfigSchema,\n /** Request status where the request was made (src/requests/status-board,\n * #383): each person sees the state of their own request in the place\n * they asked. Off by default. */\n requestStatus: requestStatusConfigSchema,\n /** Hand due Apple Reminders back to the agent that created them\n * (src/reminders). macOS only; reads reminders whose notes end with the\n * mac-pim skill's `agentx: agent=<id>` trailer. */\n reminders: z.object({\n enabled: z.boolean().default(false),\n /** Reminders lists to watch. */\n lists: z.array(z.string().min(1)).default([\"AgentX\"]),\n pollSeconds: z.number().int().min(15).default(60),\n /** Overdue by more than this (the daemon was down): reported, not run. */\n lookbackHours: z.number().positive().default(24),\n /** The remindctl binary, when it isn't on the daemon's PATH. */\n command: z.string().min(1).default(\"remindctl\"),\n }).default({}),\n /** The phone app (/app) beyond pairing and notifications (#676). */\n app: z.object({\n /** The AgentX Android app (apps/android). Chrome shows it full screen,\n * without an address bar, only when this computer vouches for it at\n * /.well-known/assetlinks.json; with no fingerprints there is nothing\n * to vouch for and that address answers 404. */\n android: z.object({\n /** applicationId the Android app was built with. */\n packageName: z.string().regex(/^[a-zA-Z][\\w]*(\\.[a-zA-Z][\\w]*)+$/, \"must look like com.example.app\").default(\"dev.agentx.phone\"),\n /** SHA-256 fingerprints of the key(s) that signed it, as AB:CD:… */\n certFingerprints: z.array(z.string().regex(/^([0-9A-Fa-f]{2}:){31}[0-9A-Fa-f]{2}$/, \"must be 32 hex pairs separated by colons\")).default([]),\n }).default({}),\n /** Place reminders: places the Android app watches as geofences, and\n * what to do when the phone arrives or leaves (src/places). */\n places: z.object({\n enabled: z.boolean().default(true),\n /** Where places and their reminders are kept. Relative paths resolve\n * from the folder holding agentx.json. */\n file: z.string().min(1).default(\".agentx/places.json\"),\n defaultRadiusMeters: z.number().int().min(50).max(50_000).default(150),\n /** Android rarely notices a smaller circle reliably. */\n minRadiusMeters: z.number().int().min(50).max(50_000).default(100),\n maxRadiusMeters: z.number().int().min(50).max(50_000).default(5000),\n /** Android lets one app watch at most 100 places. */\n maxPlaces: z.number().int().min(1).max(100).default(50),\n maxRulesPerPlace: z.number().int().min(1).max(50).default(10),\n /** A repeating reminder fires at most once in this window, so a\n * phone hovering at the edge of a place doesn't buzz again and again. */\n cooldownMinutes: z.number().int().min(0).max(1440).default(10),\n /** A crossing the phone could only report later (no signal) is\n * dropped once it is older than this. */\n maxEventAgeMinutes: z.number().int().min(1).max(1440).default(30),\n /** How often the Android app checks for changed places, in minutes.\n * Android runs background checks at most every 15 minutes. */\n syncMinutes: z.number().int().min(15).max(1440).default(60),\n }).default({}),\n }).default({}),\n /** `agentx demo`, read from the agentx.json in the folder it runs from. */\n demo: z.object({\n /** Seconds each startup step (node /health, dashboard /live, mesh\n * discovery) may take. Unset: AGENTX_DEMO_STARTUP_TIMEOUT, else 60 s\n * scaled up by the load average (src/commands/demo-startup.ts). */\n startupTimeoutSeconds: z.number().int().positive().max(3600).optional(),\n }).default({}),\n /** The in-process event bus (src/events). `ringSize` bounds how many\n * recent events GET /events/recent can return. */\n events: z.object({\n ringSize: z.number().int().min(1).max(100_000).default(1000),\n }).default({}),\n screen: screenSchema,\n mesh: meshConfigSchema.default({}),\n /** Voices for agents on mesh peers, keyed by remote agent id, or by\n * \"<peer>/<id>\" for one peer's agent (it wins over the id). The Mac\n * speaks for them, so their nodes need no ElevenLabs key. Unset fields\n * are derived from the agent card; see src/voice/mesh-voice.ts. */\n meshVoices: z.record(z.string(), voiceSchema.omit({ rate: true, priority: true, hotkey: true }).extend({\n /** What to call the agent aloud, e.g. \"Atlas\" for \"Main Agent\". */\n name: z.string().optional(),\n })).default({}),\n /** How agents speak. The free system voices by default; ElevenLabs is an\n * opt-in upgrade. Each agent's `voice` block can override the provider\n * and pick its voice; see src/voice/agent-voice.ts. */\n voice: z.object({\n provider: voiceProviderSchema.default(\"system\"),\n /** When ElevenLabs cannot speak (no key, an error): the system voice,\n * or silence. */\n fallback: z.enum([\"system\", \"none\"]).default(\"system\"),\n /** The system voice for agents without one of their own (see\n * systemVoiceSchema). Unset: each agent gets its own. */\n system: systemVoiceSchema.optional(),\n /** Language for assigned system voices, e.g. \"en\", \"fr-FR\". */\n locale: z.string().default(\"en\"),\n /** What agents call the person they talk with, e.g. a first name.\n * Unset: \"the user\". */\n listener: z.string().optional(),\n /** How a word is said aloud without changing how it is written\n * (#433): each pair's `written` form, matched as a whole word\n * without case, is spoken as `spoken`. `languages` (\"en\", \"fr\",\n * \"ar\") limits a pair to lines in those languages. Only speech\n * changes; cards, transcripts and messages keep the written form.\n * See src/voice/pronounce.ts. */\n pronunciations: z.array(z.object({\n written: z.string().trim().min(1).max(80),\n spoken: z.string().trim().min(1).max(120),\n languages: z.array(z.enum([\"en\", \"fr\", \"ar\"])).optional(),\n }).strict()).max(200).default([]),\n /** The agent's drawn pointer and name tag during a lesson. false: it\n * is never drawn, and a lesson is spoken only. */\n pointer: z.boolean().default(true),\n /** AgentX Voice speech to text: \"auto\" uses ElevenLabs when a key is\n * set and the local Whisper otherwise; \"elevenlabs\" or \"local\" pick\n * one (ElevenLabs still falls back to local when it fails). */\n stt: z.enum([\"auto\", \"elevenlabs\", \"local\"]).default(\"auto\"),\n /** Phone voice input where the daemon has no ffmpeg to measure a\n * recording's length: refused by default, since the 2 MB cap alone\n * lets through ~40 minutes of low-bitrate audio. true accepts it. */\n allowUnmeasured: z.boolean().default(false),\n /** Longest answer said aloud, in characters. A longer one stops at\n * its last whole sentence inside this and says the rest is on\n * screen; the written answer is always shown whole. The minimum\n * keeps a cut answer over the 280 characters the Mac pill opens\n * at (AnswerView.isWorthShowing), so \"on screen\" stays true. */\n spokenMaxChars: z.number().int().min(300).max(1500).default(SPOKEN_MAX_CHARS),\n /** A transcript with no words (empty, or only bracketed markers such\n * as \"[background noise]\") gets a fixed reply from /ask and wakes no\n * agent. `markers` is what counts as a marker, without the brackets. */\n noiseFilter: z.object({\n enabled: z.boolean().default(true),\n markers: z.array(z.string()).default(NOISE_MARKERS),\n }).default({}),\n /** The on-device engine behind \"local\" and every fallback:\n * mlx-whisper (Python, all languages) or Parakeet (Core ML, 25\n * European languages, no Arabic; 483 MB downloaded on first use into\n * ~/.agentx/models). mlx-whisper answers until Parakeet is ready. */\n localStt: z.enum([\"mlx-whisper\", \"parakeet\"]).default(\"mlx-whisper\"),\n /** How AgentX Voice hears the end of a hands-free turn: \"vad\" (Silero\n * voice detection, 0.9 MB downloaded on first use; the volume\n * threshold until then) or \"volume\" (the old fixed threshold). */\n endOfTurn: z.enum([\"vad\", \"volume\"]).default(\"vad\"),\n /** AgentX Voice shortcuts: hold `talk` to speak, `stop` silences every\n * voice, `paste` is smart paste. */\n hotkeys: z.object({\n talk: hotkeySchema.default(DEFAULT_HOTKEYS.talk),\n stop: hotkeySchema.default(DEFAULT_HOTKEYS.stop),\n paste: hotkeySchema.default(DEFAULT_HOTKEYS.paste),\n }).default({}),\n /** What shows the assistant's state in AgentX Voice: the \"orb\" at\n * the pill's head, or the \"character\", the orb grown into a small\n * creature that hovers above the bottom edge of the screen. */\n look: z.enum(VOICE_LOOKS).default(\"orb\"),\n /** AgentX Voice starts with its pill reduced to the orb alone: a\n * small circle the person can drag. Off: the full pill. */\n startReduced: z.boolean().default(false),\n /** The character takes a slow stroll beside where it rests when it\n * has nothing to do. Off: it moves only out of the pointer's way. */\n stroll: z.boolean().default(false),\n /** How often the character plays a small animation by itself when\n * it has nothing to do: a look around, a hop, a sway, and a yawn\n * before it dozes. \"off\": none. */\n animations: z.enum(VOICE_ANIMATIONS).default(\"sometimes\"),\n /** The orb and character palette of an agent that set neither\n * presence.palette nor presence.color. */\n palette: z.enum(ORB_PALETTE_IDS).default(DEFAULT_PALETTE),\n /** The answer text AgentX Voice shows inside its pill. */\n card: z.object({\n /** Seconds the answer stays open once it has been spoken; 0 keeps\n * it open until closed. */\n timeout: z.number().min(0).max(600).default(30),\n /** Tallest the answer grows, in points, before it scrolls. */\n maxHeight: z.number().min(120).max(800).default(320),\n }).default({}),\n }).default({}),\n business: businessConfigSchema.optional(),\n boards: boardsConfigSchema,\n dashboard: dashboardConfigSchema,\n graph: graphConfigSchema,\n decisions: decisionsConfigSchema,\n /** Workflow engine — declarative state machines that bind channel events\n * to agents. Off by default; existing installs see no change until\n * flipped. Definitions live under `dir` (default .agentx/workflows/). */\n workflows: z.object({\n enabled: z.boolean().default(false),\n dir: z.string().default(\".agentx/workflows\"),\n /** n8n keeps its several hundred connectors; we keep the agents. Point\n * this at your n8n and the workflow builder lists YOUR n8n workflows as\n * steps, so nobody has to copy a webhook URL by hand. */\n n8n: z.object({\n baseUrl: z.string().default(\"\"),\n apiKey: z.string().default(\"\"),\n }).default({ baseUrl: \"\", apiKey: \"\" }),\n matching: z.object({\n enabled: z.boolean().default(false),\n mode: z.enum([\"suggest\", \"auto\"]).default(\"suggest\"),\n autoRunThreshold: z.number().min(0).max(1).default(0.85),\n suggestThreshold: z.number().min(0).max(1).default(0.65),\n }).default({}),\n /** Controls whether the dashboard exposes the visual editor. \"readonly\"\n * serves the list + run timelines but strips write controls from the\n * page. \"disabled\" hides the tab entirely. */\n editor: z.enum([\"disabled\", \"readonly\", \"edit\"]).default(\"edit\"),\n }).default({}),\n /** Procedures — user-perspective SOPs mined from recurring activity.\n * The extraction cadence itself lives in `crons.procedure-extract`\n * (written by `agentx procedure watch`); this block holds the miner's\n * thresholds and the injection gates the daemon reads. */\n procedures: z.object({\n enabled: z.boolean().default(true),\n dir: z.string().default(\".agentx/procedures\"),\n extraction: z.object({\n enabled: z.boolean().default(false),\n /** Count patterns live after each completed task (no LLM cost). */\n onTaskCompletion: z.boolean().default(false),\n minOccurrences: z.number().int().min(2).default(3),\n sinceDays: z.number().int().min(1).default(7),\n maxClusters: z.number().int().min(1).default(5),\n /** Agent whose session distills drafts (LLM path). */\n via: z.string().optional(),\n }).default({}),\n /** Inject matching procedures into fresh agent sessions as guidance. */\n injection: z.object({\n enabled: z.boolean().default(true),\n maxProcedures: z.number().int().min(1).default(2),\n minScore: z.number().min(0).max(1).default(0.5),\n }).default({}),\n }).default({}),\n /** Registered inbound webhooks — an inventory the dashboard manages. Each\n * entry binds an (agent, source) pair to an optional signing secret. The\n * actual inbound URL is always POST /webhook/<agentId>/<source>. */\n webhooks: z.array(z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"webhook id must be lowercase slug\"),\n // Open-string source — runtime + admin-panel + CLI all share a\n // canonical list (kept in sync via WEBHOOK_SOURCES). Loosened from a\n // strict enum so adding a new first-class service (the 5-step recipe)\n // doesn't require a schema bump in three places. Doctor + admin\n // validator still gate writes on the canonical list.\n source: z.string().min(1),\n agentId: z.string(),\n secretEnv: z.string().optional(),\n description: z.string().optional(),\n enabled: z.boolean().default(true),\n /** Route to a mesh peer instead of local execution. */\n node: z.string().optional(),\n /** Per-event-type workflow routing. Keys are platform event-type\n * strings — the format the platform's HTTP header uses, optionally\n * joined with the action. Examples:\n * GitHub: \"issues.opened\", \"pull_request.synchronize\",\n * \"push\" (action-less events use the bare type)\n * GitLab: \"Note Hook\", \"Merge Request Hook\"\n * Values are workflow ids registered via `agentx workflow create`.\n * When the inbound event-type matches a key, the named workflow is\n * dispatched; if no key matches, the webhook falls through to\n * `defaultWorkflow` (or the agent itself). Closes the recurring\n * GitHub problem of multiple event types collapsing to a single\n * workflow. */\n triggers: z.record(z.string(), z.string()).optional(),\n /** Workflow id used when no `triggers` entry matches the event-type.\n * Backward-compatible: pre-existing webhooks without `triggers`\n * always hit this path. */\n defaultWorkflow: z.string().optional(),\n })).default([]),\n /** Session cache-reuse policy. Controls when we drop a Claude `--resume`\n * session and rebuild the prompt from scratch. Every rotation captures a\n * continuity memo that's injected into the chat's next fresh session.\n * - `staleMinutes`: idle timeout before rotation. 12 h default — chat\n * conversations must survive a workday's pauses; overnight silence\n * starts fresh.\n * - `maxTurnsPerSession`: backstop cap on turns per Claude session.\n * Size-based rotation measures real context now, so this rarely fires\n * first.\n * - `tierTwoThresholdTokens`: rotate when the prior turn's END-OF-TURN\n * per-request context (last API call's input + cacheRead + cacheCreate)\n * reaches this. Claude bills the 1.5× long-context rate above 200K\n * per REQUEST — 180K leaves headroom. NOT the cumulative turn total:\n * that sums cache reads across every call and reads 10-20× too high. */\n session: z.object({\n staleMinutes: z.number().int().min(1).max(1440).default(720),\n maxTurnsPerSession: z.number().int().min(2).max(200).default(40),\n tierTwoThresholdTokens: z.number().int().min(50_000).max(200_000).default(180_000),\n /** Per-channel context rotation limits; omitted channels use the global limit. */\n tierTwoThresholdTokensByChannel: z.record(z.number().int().min(50_000).max(200_000)).default({}),\n /** Context assembly strategy:\n * - \"layered\" (default): the classic stacked layers — session history,\n * memory, cross-chat, wiki hint all appended every turn.\n * - \"planner\": a Haiku pre-call decides what to retrieve before the\n * main agent runs. Always-on core is kept (channel/scope/landscape/\n * identity/intent + last 3 turns verbatim); history blob, wiki hint,\n * and cross-chat are replaced by planner-selected bundles. Per-task\n * overrides via AgentTask.contextStrategy for A/B benchmarking. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).default(\"layered\"),\n /** Optional hard ceilings on new claude-code runs across the fleet (all\n * claude-code agents share one Claude subscription, so one counter).\n * The plan's own limit is read from Claude Code's rate_limit_event and\n * gates cold dispatches on its own; these caps sit on top for operators\n * who want a local ceiling. Unset or 0 = off. Warm sessions always go\n * through. Changing them applies on save, no restart. */\n maxClaudeCodeDispatchesPerHour: z.number().int().min(0).max(10_000).optional(),\n maxClaudeCodeDispatchesPer5h: z.number().int().min(0).max(50_000).optional(),\n /** How many requests BEFORE the immediate predecessor to show the\n * session-continuity seat. 0 keeps today's two-message state exactly.\n *\n * Off by default on purpose. A probe over 27 hand-written turns found\n * that at 3, the seat's safety question (`needsHistory`) becomes\n * cleanly separable where two messages leave it straddling zero — but\n * that rests on 7 positive examples from synthetic scenarios, and this\n * seat's failure mode is the agent silently losing conversation\n * context. The promotion gate for that wants labelled production rows,\n * not a good afternoon on a fixture. Raise it to collect them. */\n continuityStateTurns: z.number().int().min(0).max(5).default(0),\n /** How much a session is given when it starts, per channel (#615).\n * `full` is the classic start. `lean` starts Claude Code with only\n * the `agentx` MCP server (no user-level connectors), only the\n * project's settings (no global CLAUDE.md, user skills or plugins),\n * and asks for the landscape, chat history and cross-chat context\n * through MCP tools instead of pushing them into the prompt. A\n * channel missing here keeps the built-in default: github, a2a,\n * workflow and cron are lean; every other channel is full. Only\n * claude-code and codex-cli agents have a lean start; other tiers\n * are always full. See src/agents/session-profile.ts. */\n profileByChannel: z.record(z.enum([\"full\", \"lean\"])).default({}),\n /** What a lean start keeps. */\n lean: z.object({\n /** MCP servers from the workspace's .mcp.json kept in a lean\n * session, by name. `agentx` is always added. */\n mcpServers: z.array(z.string().min(1)).default([\"agentx\"]),\n /** Claude Code setting sources a lean session reads. Without `user`,\n * the global CLAUDE.md, user skills, plugins and user-level MCP\n * connectors are not loaded. `project` is the workspace. */\n settingSources: z.array(z.enum([\"user\", \"project\", \"local\"])).default([\"project\", \"local\"]),\n /** Replace the pushed landscape, chat history and cross-chat\n * context with one line naming the tools that fetch them. */\n contextOnDemand: z.boolean().default(true),\n /** Built-in Claude Code tools a lean session gets, passed as\n * `--tools` next to `--strict-mcp-config` (#615). Empty, the\n * default, keeps every built-in tool. The tool schemas are about\n * 14k tokens of a first turn, so a short list is what brings a lean\n * start under 20k; an agent that lacks a tool it needs fails\n * mid-task, so this stays opt-in. The agentx MCP tools are not\n * affected: ToolSearch is always added to the list, so they stay\n * deferred. claude-code agents only. */\n tools: z.array(z.string().min(1)).default([]),\n /** The same per channel; a channel's non-empty list wins over\n * `tools`. */\n toolsByChannel: z.record(z.array(z.string().min(1))).default({}),\n /** The `agentx_` MCP tools a lean session lists (#699). Empty, the\n * default, lists every agentx tool. Claude Code already defers\n * them while ToolSearch is available, so a list saves only their\n * names (about 300 tokens) unless tool search is off. A tool left\n * off cannot be found even through ToolSearch. `agentx_approval`,\n * `agentx_request`, `agentx_events` and `agentx_attach_next` are\n * always kept, and so are `agentx_agents`, `agentx_recent` and\n * `agentx_wiki_query` while `contextOnDemand` is on. A name that is\n * not an agentx tool is rejected, so a typo cannot silently drop a\n * reply tool. Only an agentx server started as a command (stdio)\n * can be told; an http entry lists every tool. claude-code agents\n * only. */\n agentxTools: agentxToolListSchema.default([]),\n /** The same per channel; a channel's non-empty list wins over\n * `agentxTools`. */\n agentxToolsByChannel: z.record(agentxToolListSchema).default({}),\n }).default({}),\n /** A cheaper model for runs started only by triage events (#615): a\n * GitHub label added or removed, an issue or PR closed. A run that\n * collapsed any other action (\"opened, labeled\") keeps the agent's\n * model, and so does a follow-up whose session ran within the last\n * hour (swapping models drops the prompt cache). A model set on the\n * task itself, such as a cron job's, wins. Off until a model is set\n * for the agent's engine. See triageModelFor in src/agents/routing.ts. */\n triage: z.object({\n /** The model per CLI engine: a Claude model for claude-code agents,\n * an OpenAI model for codex-cli agents. Unset: no triage model. */\n models: z.object({\n \"claude-code\": z.string().min(1).optional(),\n \"codex-cli\": z.string().min(1).optional(),\n }).default({}),\n /** Event actions that count as triage. An action only reaches an\n * agent when `channels.github.issueActions` /\n * `pullRequestActions` (or a project rule) let it through. */\n actions: z.array(z.string().min(1)).default([\"labeled\", \"unlabeled\", \"closed\"]),\n }).default({}),\n /** Longest the agent-memory index (MEMORY.md) may be where it is\n * loaded on every session: merged into each workspace's CLAUDE.md\n * and inlined in the system prompt (#615). 0, the default, keeps the\n * whole index. Cut whole lines only; a closing line counts the\n * entries left out and points at `.agentx-memory.md`, which always\n * holds the full index, and at `agentx memory index`. Applies to the\n * prompt on save; the CLAUDE.md block follows at the next daemon\n * start or memory change. */\n memoryIndexMaxChars: z.number().int().min(0).max(200_000).default(0),\n /** Resume or start fresh (#621, step 3). Before a claude-code session\n * is resumed, compare what the coming turn costs when it replays the\n * transcript with what a fresh lean start costs, and start fresh when\n * that is cheaper by `margin`. Runs after the stale, tier-2 and\n * max-turns rules and can only rotate earlier than they would.\n * `shadow` only logs what it would do. Off by default. Applies on\n * save. See src/agents/resume-gate.ts. */\n resumeGate: z.object({\n mode: z.enum([\"off\", \"shadow\", \"active\"]).default(\"off\"),\n /** Tokens a fresh lean session starts with (~27k measured in #615). */\n freshTokens: z.number().int().min(1000).max(200_000).default(30_000),\n /** How long the cached transcript is assumed to survive. Longer is the\n * safe side: it treats more sessions as warm, and keeps them. */\n cacheTtlMinutes: z.number().int().min(1).max(1440).default(60),\n /** Cache-write price as a multiple of the input price (2 for the\n * one-hour cache, 1.25 for the five-minute one). */\n cacheWriteFactor: z.number().min(1).max(4).default(2),\n /** A fresh start must be at least this many times cheaper. */\n margin: z.number().min(1).max(10).default(1.5),\n }).default({}),\n /** ObservationPack (#621). A large tool result stays in the context\n * and is re-read on every later request. With this on, a Claude Code\n * PostToolUse hook saves a text result over `limitBytes` to\n * `.agentx/observations/<agent id>/` and shows the model its first and last\n * bytes plus the path of the saved original, which it can Read or\n * grep. Off by default. claude-code agents only. Turning it on, or\n * changing `tools`, takes a daemon restart (the hook is written into\n * each workspace's .claude/settings.json at start); turning it off\n * applies on save. See src/agents/observation-pack.ts. */\n observationPack: z.object({\n enabled: z.boolean().default(false),\n /** A text result larger than this many bytes is packed. */\n limitBytes: z.number().int().min(1024).max(1_048_576).default(10_240),\n /** Bytes of the start and of the end of the original the model sees. */\n headBytes: z.number().int().min(0).max(65_536).default(1024),\n tailBytes: z.number().int().min(0).max(65_536).default(1024),\n /** Tools the pack applies to. Each entry must match the whole tool\n * name and may be a regular expression. `Read` is in by default\n * (owner decision on #621, 2026-10-05): a file read over the limit\n * is cut to its first and last lines, with the file's own path and\n * line numbers, and the agent reads the lines it needs again with\n * offset and limit. No copy of the file is saved. Take `Read` out\n * of the list for an agent that edits large files all day. */\n tools: z.array(z.string().min(1)).default([\"Bash\", \"Grep\", \"Read\", \"WebFetch\", \"mcp__.*\"]),\n /** Days a saved original is kept. 0 keeps every original. */\n retentionDays: z.number().int().min(0).max(3650).default(0),\n }).default({}),\n }).default({}),\n /** Move B — JS/TS plugins. Each entry is an installed npm package name\n * (e.g. `agentx-plugin-mattermost` or `@acme/plugin-mattermost`); the\n * loader does a dynamic `import(name)` at boot, validates the manifest,\n * and calls plugin.setup(ctx). Plugins can register channel adapters\n * via ctx.addChannel() and subscribe to bus events via ctx.on(). Empty\n * default — installs that don't list plugins are unaffected. Plugin\n * authoring guide: docs/architecture/plugins.md. */\n plugins: z.array(z.string()).default([]),\n /** Persistent-process pool eviction knobs (handoff #8). The pool keeps\n * one warm Claude subprocess per (agent, channel, chatId) so latency\n * stays low; without bounds, an idle process can serve traffic days\n * later with stale context. These knobs cap that risk. All values are\n * in seconds for operator-friendliness; the registry uses ms internally.\n *\n * - `maxIdleSeconds`: a handle becomes eligible for cap-pressured\n * eviction this many seconds after its last turn. Default 30s\n * (matches the registry's pre-config behavior).\n * - `maxAgeSeconds`: a handle is killed unconditionally on the next\n * sweep once its total wall-clock idleness exceeds this. Default\n * 2700s (45min) — matches the registry's pre-config staleTimeoutMs.\n * - `sweepIntervalSeconds`: how often the sweeper checks. Default 5s.\n *\n * See `agents.<id>.persistentProcess` to opt an agent into the pool. */\n processPool: z.object({\n maxIdleSeconds: z.number().int().min(5).max(86_400).default(30),\n maxAgeSeconds: z.number().int().min(60).max(86_400).default(2700),\n sweepIntervalSeconds: z.number().int().min(1).max(300).default(5),\n }).default({}),\n})\n\nexport type DaemonConfig = z.infer<typeof daemonConfigSchema>\nexport type AgentDef = z.infer<typeof agentConfigSchema>\nexport type EventSubscription = z.infer<typeof eventSubscriptionSchema>\nexport type CronJobDef = z.infer<typeof cronJobSchema>\nexport type MeshPeer = z.infer<typeof meshPeerSchema>\n\n/**\n * Expand environment variables in strings: ${VAR_NAME} -> process.env.VAR_NAME\n */\nexport function expandEnvVars(obj: unknown): unknown {\n if (typeof obj === \"string\") {\n return obj.replace(/\\$\\{(\\w+)\\}/g, (_match, name) => process.env[name] || \"\")\n }\n if (Array.isArray(obj)) {\n return obj.map(expandEnvVars)\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = expandEnvVars(value)\n }\n return result\n }\n return obj\n}\n\n/**\n * Translate raw Zod issues into actionable, copy-pasteable fixes for the\n * patterns we see in real first-run failures (missing tokens, missing\n * required env vars, etc.). Falls through silently for unknown patterns —\n * the raw `issues` block is always shown above this hint block.\n */\nfunction friendlyConfigHints(issues: ReadonlyArray<z.ZodIssue>, configPath: string): string[] {\n const hints: string[] = []\n const seen = new Set<string>()\n for (const issue of issues) {\n const path = issue.path.join(\".\")\n // Telegram account block exists but token is empty/missing — by far the\n // most common first-run trip. Tell the operator exactly which two\n // recoveries are valid, with the literal config path to edit.\n const tgTokenMatch = /^channels\\.telegram\\.accounts\\.([^.]+)\\.token$/.exec(path)\n if (tgTokenMatch && issue.message.toLowerCase().includes(\"required\")) {\n const account = tgTokenMatch[1]\n const key = `tg:${account}`\n if (seen.has(key)) continue\n seen.add(key)\n hints.push(\n `Hint: Telegram account \"${account}\" is enabled but has no bot token.\\n` +\n ` Either:\\n` +\n ` A) Open ${configPath} and set channels.telegram.accounts.${account}.token to your bot token (or \"\\${TG_${account.toUpperCase()}_BOT_TOKEN}\" + the matching .env entry).\\n` +\n ` B) If you don't need Telegram on this instance, remove the entire \"telegram\" block under \"channels\" (or set channels.telegram.enabled to false AND drop accounts.${account}).`\n )\n }\n }\n return hints\n}\n\n/**\n * Load daemon config from agentx.json, with env var expansion and validation.\n */\nexport function loadDaemonConfig(configPath?: string): DaemonConfig {\n const paths = configPath\n ? [configPath]\n : [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n\n // Load .env from same directory as config search\n loadDotEnv(process.cwd())\n\n let raw: string | undefined\n let foundPath: string | undefined\n\n for (const p of paths) {\n if (existsSync(p)) {\n raw = readFileSync(p, \"utf-8\")\n foundPath = p\n break\n }\n }\n\n if (!raw || !foundPath) {\n throw new Error(\n `No config found. Create agentx.json or .agentx/config.json\\n` +\n `Searched: ${paths.join(\", \")}`\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (e: any) {\n throw new Error(`Invalid JSON in ${foundPath}: ${e.message}`)\n }\n\n // Expand environment variables\n const expanded = expandEnvVars(parsed)\n\n // Validate\n const result = daemonConfigSchema.safeParse(expanded)\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\")\n const hints = friendlyConfigHints(result.error.issues, foundPath)\n const hintBlock = hints.length ? `\\n\\n${hints.join(\"\\n\\n\")}` : \"\"\n throw new Error(`Config validation failed (${foundPath}):\\n${issues}${hintBlock}`)\n }\n\n return result.data\n}\n\n/**\n * Validate agent workspace directories exist and have .claude/ setup.\n */\nexport function validateWorkspaces(config: DaemonConfig): string[] {\n const warnings: string[] = []\n\n for (const [id, agent] of Object.entries(config.agents)) {\n if (!existsSync(agent.workspace)) {\n warnings.push(`Agent \"${id}\": workspace not found at ${agent.workspace}`)\n continue\n }\n\n if (agent.tier === \"claude-code\") {\n const claudeDir = resolve(agent.workspace, \".claude\")\n if (!existsSync(claudeDir)) {\n warnings.push(\n `Agent \"${id}\": no .claude/ directory in workspace ${agent.workspace}. ` +\n `Claude Code native features (hooks, MCP, skills) won't be available.`\n )\n }\n }\n\n // Check provider availability\n const providerName = agent.provider || \"claude\"\n const providerConfig = config.providers[providerName]\n if (![\"claude-code\", \"codex-cli\", \"opencode\"].includes(agent.tier) && (!providerConfig || !providerConfig.apiKey)) {\n warnings.push(\n `Agent \"${id}\": provider \"${providerName}\" has no API key configured. ` +\n `Set providers.${providerName}.apiKey in config or use a CLI-backed tier (\"claude-code\", \"codex-cli\", or \"opencode\").`\n )\n }\n }\n\n // Validate cron agent bindings\n for (const [id, cron] of Object.entries(config.crons)) {\n if (!config.agents[cron.agent]) {\n warnings.push(`Cron \"${id}\": references unknown agent \"${cron.agent}\"`)\n }\n }\n\n // Validate channel agent bindings\n if (config.channels.telegram.enabled) {\n for (const [name, account] of Object.entries(config.channels.telegram.accounts)) {\n if (!config.agents[account.agentBinding]) {\n warnings.push(\n `Telegram account \"${name}\": references unknown agent \"${account.agentBinding}\"`\n )\n }\n }\n }\n\n return warnings\n}\n"],"mappings":"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","names":["z","dayEnum","scheduleSchema","roleSchema","orgEntrySchema","channelRefSchema","workSourceSchema","projectSchema","contactMapSchema","clientPolicySchema","businessConfigSchema","init_config","__esmMin","z","deriveStage","item","columns","set","closed","c","backlog","prefix","transitionDiff","fromColumn","toColumn","add","remove","closeIssue","reopen","boardLabelSchema","boardColumnSchema","boardReconcileSchema","boardSourceSchema","DEFAULT_COLUMNS","boardSchema","boardsConfigSchema","dashboardDaemonSchema","dashboardConfigSchema","init_config","__esmMin","parseHotkey","input","raw","parts","p","key","KEY_ALIASES","mods","m","MODIFIERS","isKey","isFKey","ORDER","hotkeyError","r","describeHotkey","value","sym","names","NAMED_KEYS","DEFAULT_HOTKEYS","init_hotkey","__esmMin","k","hueOf","hex","m","r","g","b","x","max","min","d","paletteForColor","hue","ORB_PALETTES","p","best","bestDistance","distance","agentPalette","presence","fallback","DEFAULT_PALETTE","byId","id","picked","color","VOICE_LOOKS","VOICE_ANIMATIONS","ORB_PALETTE_IDS","init_orb_palettes","__esmMin","detectLanguage","text","arabic","latin","en","fr","w","EN","FR","ACCENT","init_language","__esmMin","toSpeakable","text","max","SPOKEN_MAX_CHARS","s","match","head","end","MORE","detectLanguage","init_speakable","__esmMin","init_language","isNoiseTranscript","text","markers","NOISE_MARKERS","known","key","rest","group","square","round","NOISE_REPLY","init_noise","__esmMin","s","z","hhmm","watchRuleSchema","whatsappTriageSchema","init_config","__esmMin","ensureRequestTables","db","ensureColumns","PERSON_COLUMNS","cols","name","type","c","e","hasRequestTables","toRecord","OPEN_STATES","CLOSED_STATES","TEXT_MAX","REASON_MAX","placeholders","RequestStore","init_store","__esmMin","init_sqlite","n","input","id","row","kind","ref","requestId","now","state","question","reason","detail","text","states","before","agentId","note","said","r","a","b","pickupContext","requestId","PICKUP_CHANNEL","PICKUP_MARK","isPickup","ctx","isOwnerTurn","settings","channel","sender","operator","ch","base","OPERATOR_CHANNELS","DAEMON_SENDERS","raw","FORGES","who","comparable","entry","e","i","senderOf","pick","v","failureWord","error","clip","chatKey","RequestTracker","init_tracker","__esmMin","init_store","s","flat","agentId","chatId","store","log","now","what","fn","p","channels","earlier","key","pickup","req","word","d","turn","status","text","card","applied","delegationId","OPEN_STATES","o","randomBytes","timingSafeEqual","chmodSync","existsSync","mkdirSync","readFileSync","writeFileSync","networkInterfaces","dirname","resolve","operatorContext","ctx","OPERATOR_MARK","isOperatorTurn","operatorKeyPath","root","loadOperatorKey","opts","path","key","operatorKeyMatches","presented","p","b","operatorVouch","context","headers","operatorChannels","channel","OPERATOR_HEADER","peerVouches","peerTokens","v","OPERATOR_VOUCH_HEADER","auth","h","bearer","t","a","isThisMachine","remoteAddress","own","ownAddresses","addr","plainAddress","list","i","ownerProven","req","node","init_operator","__esmMin","personName","people","id","p","IMPLICIT_OWNER","normalize","channel","value","v","splitIdentity","identity","i","base","peopleProblem","ids","claimed","parts","key","other","resolvePerson","sender","ch","operatorPerson","owners","personOfTurn","ctx","root","propagatedRootOf","classifyInitiator","OPERATOR_CHANNELS","isOperatorTurn","str","person","agentAllowed","agentId","list","refusedPerson","ref","nameMatches","pattern","name","re","s","personLimitsOf","tools","skills","PERSON_ROLES","init_people","__esmMin","init_initiator","init_tracker","init_operator","unknownAgentxTools","list","known","AGENTX_TOOL_NAMES","init_tool_names","__esmMin","config_exports","__export","INTEGRATION_KINDS","approvalsConfigSchema","callsSchema","cameraBotSchema","daemonConfigSchema","eventSubscriptionSchema","expandEnvVars","loadDaemonConfig","membersConfigSchema","peopleConfigSchema","personSchema","requestStatusConfigSchema","requestsConfigSchema","validateWorkspaces","z","readFileSync","existsSync","resolve","loadDotEnv","dir","envPath","content","line","trimmed","eqIdx","key","value","obj","_match","name","result","friendlyConfigHints","issues","configPath","hints","seen","issue","path","tgTokenMatch","account","paths","raw","foundPath","p","parsed","e","expanded","i","hintBlock","config","warnings","id","agent","claudeDir","providerName","providerConfig","cron","agentxToolListSchema","providerConfigSchema","mcpStdioServerSchema","mcpHttpServerSchema","mcpServerSchema","integrationCredentialsSchema","integrationSchema","voiceProviderSchema","systemVoiceSchema","hotkeySchema","voiceSchema","agentConfigSchema","telegramAccountSchema","channelsConfigSchema","cronJobSchema","serviceSchema","meshPeerSchema","meshInboxSchema","meshConfigSchema","graphConfigSchema","decisionSeatSchema","decisionsConfigSchema","notificationsSchema","screenRectSchema","screenSchema","init_config","__esmMin","init_autonomy","init_hotkey","init_orb_palettes","init_speakable","init_noise","init_people","init_tool_names","list","ctx","unknown","unknownAgentxTools","s","error","hotkeyError","ORB_PALETTE_IDS","autonomyLevelSchema","v","j","people","problem","peopleProblem","tz","whatsappTriageSchema","SPOKEN_MAX_CHARS","NOISE_MARKERS","DEFAULT_HOTKEYS","VOICE_LOOKS","VOICE_ANIMATIONS","DEFAULT_PALETTE","businessConfigSchema","boardsConfigSchema","dashboardConfigSchema"]}
|