agentix-cli 0.131.0 → 0.133.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-WZ3HAKES.js → App-WQPA77VJ.js} +2 -2
- package/dist/{ChatApp-K4HDJL2S.js → ChatApp-TGW66I4Y.js} +2 -2
- package/dist/{absorb-eval-ER27DHHK.js → absorb-eval-37XZJACN.js} +2 -2
- package/dist/{agent-AQSVISXX.js → agent-FWHYHXWC.js} +2 -2
- package/dist/{board-dashboard-3AI2Z2SP.js → board-dashboard-GKE4KWJH.js} +435 -270
- package/dist/board-dashboard-GKE4KWJH.js.map +1 -0
- package/dist/{builtin-W6KNX7RS.js → builtin-YD7MC572.js} +2 -2
- package/dist/chunk-2BF6HJJD.js +54 -0
- package/dist/chunk-2BF6HJJD.js.map +1 -0
- package/dist/{chunk-MFIM5Y3J.js → chunk-3TETB22A.js} +2 -2
- package/dist/{chunk-7UPFZJIT.js → chunk-4NEO5KB2.js} +30 -30
- package/dist/chunk-4NEO5KB2.js.map +1 -0
- package/dist/{chunk-B2Q3PTPS.js → chunk-4XPVU3N2.js} +2 -2
- package/dist/chunk-57Y2WZT2.js +55 -0
- package/dist/chunk-57Y2WZT2.js.map +1 -0
- package/dist/{chunk-E2VDBT3T.js → chunk-ANC5QXFB.js} +2 -2
- package/dist/chunk-BI6VG4DV.js +37 -0
- package/dist/chunk-BI6VG4DV.js.map +1 -0
- package/dist/{chunk-YQRWUF4Y.js → chunk-D5IHICX3.js} +2 -2
- package/dist/{chunk-IA7INHXY.js → chunk-F5PVUB4Y.js} +2 -2
- package/dist/{chunk-CL33CRH5.js → chunk-F7GKG5ZO.js} +3 -3
- package/dist/chunk-F7GKG5ZO.js.map +1 -0
- package/dist/{chunk-L2WVQREN.js → chunk-GUEIF7ZF.js} +2 -2
- package/dist/{chunk-APZIH3AY.js → chunk-GZY5DYXT.js} +2 -2
- package/dist/chunk-HF5EF335.js +8 -0
- package/dist/chunk-HF5EF335.js.map +1 -0
- package/dist/{chunk-AKOXTIJS.js → chunk-HIUHKOKK.js} +2 -2
- package/dist/chunk-IDZXAOBC.js +156 -0
- package/dist/chunk-IDZXAOBC.js.map +1 -0
- package/dist/chunk-IZDKRNXC.js +52 -0
- package/dist/chunk-IZDKRNXC.js.map +1 -0
- package/dist/{chunk-CSWGYSC4.js → chunk-JA5Y4LDP.js} +2 -2
- package/dist/{chunk-ERNBJLIQ.js → chunk-KUOH5MFS.js} +2 -2
- package/dist/{chunk-YA6E47BR.js → chunk-OQV3LWJI.js} +2 -2
- package/dist/{chunk-2YB2G2BP.js → chunk-PHCWYKQW.js} +2 -2
- package/dist/{chunk-GLQLCBJS.js → chunk-QSKG754X.js} +6 -3
- package/dist/chunk-QSKG754X.js.map +1 -0
- package/dist/{chunk-6O6NW3ZG.js → chunk-RJFPCW7T.js} +2 -2
- package/dist/{chunk-YVR6652A.js → chunk-RTHZQGFC.js} +2 -2
- package/dist/{chunk-KRK7OOTK.js → chunk-S2UXDEGW.js} +2 -2
- package/dist/{chunk-L4ZS4HGJ.js → chunk-SJPQQ4OF.js} +255 -247
- package/dist/chunk-SJPQQ4OF.js.map +1 -0
- package/dist/{chunk-WKQQIYOE.js → chunk-V4IFJLQS.js} +2 -2
- package/dist/{chunk-L4ALHWII.js → chunk-VH2ASPFQ.js} +2 -2
- package/dist/{chunk-AD4QZZVN.js → chunk-YXFTUOZR.js} +2 -2
- package/dist/{chunk-KIYV4ZHG.js → chunk-ZN5DC6WS.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{client-YIR3ELVN.js → client-7UW2ELPP.js} +2 -2
- package/dist/config-TNYVPT4R.js +2 -0
- package/dist/{config-mutate-KDRHKQE4.js → config-mutate-GV7BVG3I.js} +2 -2
- package/dist/config-mutator-AKCR3VKM.js +2 -0
- package/dist/{contributions-MTKXXLIP.js → contributions-J57B3KIX.js} +2 -2
- package/dist/{doctor-FJ5RCG4J.js → doctor-ROKIYJRT.js} +2 -2
- package/dist/{failure-candidates-JNGUVBDG.js → failure-candidates-XZSZP4HT.js} +2 -2
- package/dist/follow-up-tool-S2U7DFSW.js +7 -0
- package/dist/follow-up-tool-S2U7DFSW.js.map +1 -0
- package/dist/{hub-CF4VFSRT.js → hub-HOVJC4HR.js} +2 -2
- package/dist/index.d.ts +1432 -6
- package/dist/index.js +1 -1
- package/dist/{program-ZPNEAACQ.js → program-LAOU5DQ3.js} +225 -217
- package/dist/program-LAOU5DQ3.js.map +1 -0
- package/dist/{promote-G6BOY7K5.js → promote-FVWRM4BE.js} +2 -2
- package/dist/query-HBCL3FHO.js +2 -0
- package/dist/query-settings-RD6GEN2V.js +3 -0
- package/dist/query-settings-RD6GEN2V.js.map +1 -0
- package/dist/{registry-TAUHYJCK.js → registry-NT4H7HYP.js} +2 -2
- package/dist/{request-planner-V3R5J3V4.js → request-planner-JOXSAAHO.js} +2 -2
- package/dist/{review-candidates-TC7RJB4W.js → review-candidates-6N5435V7.js} +2 -2
- package/dist/{routing-XQD74ERK.js → routing-6G2QVFIG.js} +2 -2
- package/dist/schedule-tool-3NYUPKSC.js +2 -0
- package/dist/{scheduler-WOOXIOER.js → scheduler-WOH2O3EP.js} +2 -2
- package/dist/{seat-B75JIAS3.js → seat-I2FBJDTV.js} +2 -2
- package/dist/{select-I6BSBQXP.js → select-5YZQJLLD.js} +2 -2
- package/dist/{set-enabled-THNVITGU.js → set-enabled-CQEUZLHU.js} +2 -2
- package/dist/{tool-LFWTZHB7.js → tool-PIJXUBEC.js} +2 -2
- package/dist/{tool-CEBIS6GK.js → tool-R77KFLZ3.js} +2 -2
- package/dist/wiki-GCOFERTB.js +2 -0
- package/package.json +2 -1
- package/dist/board-dashboard-3AI2Z2SP.js.map +0 -1
- package/dist/chunk-2SXF66O6.js +0 -55
- package/dist/chunk-2SXF66O6.js.map +0 -1
- package/dist/chunk-2ZDV3DCT.js +0 -52
- package/dist/chunk-2ZDV3DCT.js.map +0 -1
- package/dist/chunk-76Z4VHDX.js +0 -8
- package/dist/chunk-76Z4VHDX.js.map +0 -1
- package/dist/chunk-7UPFZJIT.js.map +0 -1
- package/dist/chunk-CBFBGN7J.js +0 -54
- package/dist/chunk-CBFBGN7J.js.map +0 -1
- package/dist/chunk-CL33CRH5.js.map +0 -1
- package/dist/chunk-GLQLCBJS.js.map +0 -1
- package/dist/chunk-L4ZS4HGJ.js.map +0 -1
- package/dist/chunk-VSL2SSBH.js +0 -156
- package/dist/chunk-VSL2SSBH.js.map +0 -1
- package/dist/chunk-YPCZ5TNK.js +0 -37
- package/dist/chunk-YPCZ5TNK.js.map +0 -1
- package/dist/config-4DOJYA2K.js +0 -2
- package/dist/config-mutator-4PPBACWU.js +0 -2
- package/dist/follow-up-tool-SVZDSUIU.js +0 -6
- package/dist/follow-up-tool-SVZDSUIU.js.map +0 -1
- package/dist/program-ZPNEAACQ.js.map +0 -1
- package/dist/query-H24BI3BI.js +0 -2
- package/dist/query-settings-IJB3QMDW.js +0 -3
- package/dist/query-settings-IJB3QMDW.js.map +0 -1
- package/dist/schedule-tool-U6KLY5MT.js +0 -2
- package/dist/wiki-3ATPAHC2.js +0 -2
- /package/dist/{App-WZ3HAKES.js.map → App-WQPA77VJ.js.map} +0 -0
- /package/dist/{ChatApp-K4HDJL2S.js.map → ChatApp-TGW66I4Y.js.map} +0 -0
- /package/dist/{absorb-eval-ER27DHHK.js.map → absorb-eval-37XZJACN.js.map} +0 -0
- /package/dist/{agent-AQSVISXX.js.map → agent-FWHYHXWC.js.map} +0 -0
- /package/dist/{builtin-W6KNX7RS.js.map → builtin-YD7MC572.js.map} +0 -0
- /package/dist/{chunk-MFIM5Y3J.js.map → chunk-3TETB22A.js.map} +0 -0
- /package/dist/{chunk-B2Q3PTPS.js.map → chunk-4XPVU3N2.js.map} +0 -0
- /package/dist/{chunk-E2VDBT3T.js.map → chunk-ANC5QXFB.js.map} +0 -0
- /package/dist/{chunk-YQRWUF4Y.js.map → chunk-D5IHICX3.js.map} +0 -0
- /package/dist/{chunk-IA7INHXY.js.map → chunk-F5PVUB4Y.js.map} +0 -0
- /package/dist/{chunk-L2WVQREN.js.map → chunk-GUEIF7ZF.js.map} +0 -0
- /package/dist/{chunk-APZIH3AY.js.map → chunk-GZY5DYXT.js.map} +0 -0
- /package/dist/{chunk-AKOXTIJS.js.map → chunk-HIUHKOKK.js.map} +0 -0
- /package/dist/{chunk-CSWGYSC4.js.map → chunk-JA5Y4LDP.js.map} +0 -0
- /package/dist/{chunk-ERNBJLIQ.js.map → chunk-KUOH5MFS.js.map} +0 -0
- /package/dist/{chunk-YA6E47BR.js.map → chunk-OQV3LWJI.js.map} +0 -0
- /package/dist/{chunk-2YB2G2BP.js.map → chunk-PHCWYKQW.js.map} +0 -0
- /package/dist/{chunk-6O6NW3ZG.js.map → chunk-RJFPCW7T.js.map} +0 -0
- /package/dist/{chunk-YVR6652A.js.map → chunk-RTHZQGFC.js.map} +0 -0
- /package/dist/{chunk-KRK7OOTK.js.map → chunk-S2UXDEGW.js.map} +0 -0
- /package/dist/{chunk-WKQQIYOE.js.map → chunk-V4IFJLQS.js.map} +0 -0
- /package/dist/{chunk-L4ALHWII.js.map → chunk-VH2ASPFQ.js.map} +0 -0
- /package/dist/{chunk-AD4QZZVN.js.map → chunk-YXFTUOZR.js.map} +0 -0
- /package/dist/{chunk-KIYV4ZHG.js.map → chunk-ZN5DC6WS.js.map} +0 -0
- /package/dist/{client-YIR3ELVN.js.map → client-7UW2ELPP.js.map} +0 -0
- /package/dist/{config-4DOJYA2K.js.map → config-TNYVPT4R.js.map} +0 -0
- /package/dist/{config-mutate-KDRHKQE4.js.map → config-mutate-GV7BVG3I.js.map} +0 -0
- /package/dist/{config-mutator-4PPBACWU.js.map → config-mutator-AKCR3VKM.js.map} +0 -0
- /package/dist/{contributions-MTKXXLIP.js.map → contributions-J57B3KIX.js.map} +0 -0
- /package/dist/{doctor-FJ5RCG4J.js.map → doctor-ROKIYJRT.js.map} +0 -0
- /package/dist/{failure-candidates-JNGUVBDG.js.map → failure-candidates-XZSZP4HT.js.map} +0 -0
- /package/dist/{hub-CF4VFSRT.js.map → hub-HOVJC4HR.js.map} +0 -0
- /package/dist/{promote-G6BOY7K5.js.map → promote-FVWRM4BE.js.map} +0 -0
- /package/dist/{query-H24BI3BI.js.map → query-HBCL3FHO.js.map} +0 -0
- /package/dist/{registry-TAUHYJCK.js.map → registry-NT4H7HYP.js.map} +0 -0
- /package/dist/{request-planner-V3R5J3V4.js.map → request-planner-JOXSAAHO.js.map} +0 -0
- /package/dist/{review-candidates-TC7RJB4W.js.map → review-candidates-6N5435V7.js.map} +0 -0
- /package/dist/{routing-XQD74ERK.js.map → routing-6G2QVFIG.js.map} +0 -0
- /package/dist/{schedule-tool-U6KLY5MT.js.map → schedule-tool-3NYUPKSC.js.map} +0 -0
- /package/dist/{scheduler-WOOXIOER.js.map → scheduler-WOH2O3EP.js.map} +0 -0
- /package/dist/{seat-B75JIAS3.js.map → seat-I2FBJDTV.js.map} +0 -0
- /package/dist/{select-I6BSBQXP.js.map → select-5YZQJLLD.js.map} +0 -0
- /package/dist/{set-enabled-THNVITGU.js.map → set-enabled-CQEUZLHU.js.map} +0 -0
- /package/dist/{tool-LFWTZHB7.js.map → tool-PIJXUBEC.js.map} +0 -0
- /package/dist/{tool-CEBIS6GK.js.map → tool-R77KFLZ3.js.map} +0 -0
- /package/dist/{wiki-3ATPAHC2.js.map → wiki-GCOFERTB.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/channels/handover-store.ts","../src/agents/memory-store.ts","../src/agents/memory-context.ts","../src/a2a/mesh-errors.ts","../src/agents/queued.ts","../src/workflows/store.ts","../src/workflows/types.ts","../src/workflows/yaml.ts","../src/workflows/yaml-roundtrip.ts","../src/workflows/run-store.ts","../src/workflows/layout-store.ts","../src/workflows/matcher.ts","../src/workflows/engine.ts","../src/workflows/dispatcher.ts","../src/workflows/nodes/classify.ts","../src/workflows/template.ts","../src/workflows/node-concurrency.ts","../src/workflows/nodes/types.ts","../src/workflows/nodes/follow-up.ts","../src/workflows/nodes/handlers.ts","../src/workflows/signals.ts","../src/intent/sources/workflow.ts","../src/workflows/hooks.ts","../src/workflows/loop-guard.ts","../src/workflows/triggers.ts","../src/workflows/poll.ts","../src/workflows/inputs.ts","../src/workflows/follow-up.ts","../src/workflows/required.ts","../src/members/store.ts","../src/utils/artifact-sentinel.ts","../src/utils/app-outbox.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from \"fs\"\nimport { resolve, dirname } from \"path\"\n\n// --- Runtime routing overrides (per-chat handovers) ------------------------\n//\n// A \"handover\" is an operator-initiated override: \"from now on, route\n// messages from <channel, chatId[, accountId]> to <toAgent>, regardless of\n// the config's static routes.\" Lightweight — persisted as plain JSON, read\n// at the top of the router's resolveAgent, consulted before config routes.\n//\n// Survival: on daemon restart, overrides stay in effect. The file IS the\n// source of truth.\n//\n// Context carryover: each override may carry a one-shot `summary` string.\n// The registry reads + clears it the first time it injects it into the\n// target agent's context, so the new agent sees \"[Handover note: …]\" once,\n// then subsequent messages just flow to the new agent without repetition.\n\nexport interface HandoverOverride {\n channel: string // \"telegram\" | \"whatsapp\" | ...\n chatId: string // DM: sender.id; group: group.id\n accountId?: string // telegram multi-account discriminator\n fromAgent: string\n toAgent: string\n summary?: string // one-shot note injected into target context\n createdAt: string // ISO\n createdBy?: string // operator that made the call\n /** When set, overrides past this time are treated as expired (ignored on\n * read, optionally pruned on access). null = indefinite. */\n expiresAt?: string | null\n /** Set after the summary has been injected once. Route stays active. */\n summaryConsumedAt?: string\n}\n\ninterface HandoverFile {\n version: number\n overrides: Record<string, HandoverOverride>\n}\n\nexport class HandoverStore {\n readonly filePath: string\n private log: (...args: unknown[]) => void\n\n constructor(opts: { baseDir?: string; log?: (...args: unknown[]) => void } = {}) {\n const base = opts.baseDir ?? resolve(process.cwd(), \".agentx\")\n this.filePath = resolve(base, \"handover.json\")\n this.log = opts.log ?? console.error.bind(console, \"[handover]\")\n mkdirSync(base, { recursive: true })\n }\n\n /** Canonical key for an override. Using \"-\" for missing accountId keeps\n * the key shape uniform across channels. */\n key(channel: string, chatId: string, accountId?: string): string {\n return `${channel}::${chatId}::${accountId || \"-\"}`\n }\n\n load(): HandoverFile {\n if (!existsSync(this.filePath)) return { version: 1, overrides: {} }\n try {\n const raw = readFileSync(this.filePath, \"utf-8\")\n const parsed = JSON.parse(raw) as HandoverFile\n if (!parsed || typeof parsed !== \"object\") return { version: 1, overrides: {} }\n if (!parsed.overrides) parsed.overrides = {}\n return parsed\n } catch (e: any) {\n this.log(`load failed, starting empty: ${e?.message || e}`)\n return { version: 1, overrides: {} }\n }\n }\n\n private save(file: HandoverFile): void {\n mkdirSync(dirname(this.filePath), { recursive: true })\n const tmp = `${this.filePath}.tmp-${process.pid}`\n writeFileSync(tmp, JSON.stringify(file, null, 2))\n renameSync(tmp, this.filePath)\n }\n\n /** Return the active override for a chat, or undefined. Skips expired. */\n get(channel: string, chatId: string, accountId?: string): HandoverOverride | undefined {\n const file = this.load()\n const o = file.overrides[this.key(channel, chatId, accountId)]\n if (!o) return undefined\n if (o.expiresAt && Date.parse(o.expiresAt) < Date.now()) return undefined\n return o\n }\n\n /** Create or replace an override. If `fromAgent` isn't supplied the caller\n * can pass \"\" and the router will fill it in from current routing. */\n set(o: Omit<HandoverOverride, \"createdAt\"> & { createdAt?: string }): HandoverOverride {\n if (!o.channel || !o.chatId || !o.toAgent) {\n throw new Error(\"channel, chatId and toAgent are required\")\n }\n const file = this.load()\n const rec: HandoverOverride = {\n ...o,\n createdAt: o.createdAt ?? new Date().toISOString(),\n }\n file.overrides[this.key(rec.channel, rec.chatId, rec.accountId)] = rec\n this.save(file)\n return rec\n }\n\n /** Remove an override (release the handover). Idempotent. */\n remove(channel: string, chatId: string, accountId?: string): boolean {\n const file = this.load()\n const k = this.key(channel, chatId, accountId)\n if (!(k in file.overrides)) return false\n delete file.overrides[k]\n this.save(file)\n return true\n }\n\n /** Mark the summary as consumed (after it's been injected once). Route\n * stays active; subsequent messages just skip the note. */\n consumeSummary(channel: string, chatId: string, accountId?: string): string | undefined {\n const file = this.load()\n const o = file.overrides[this.key(channel, chatId, accountId)]\n if (!o || !o.summary || o.summaryConsumedAt) return undefined\n const summary = o.summary\n o.summaryConsumedAt = new Date().toISOString()\n this.save(file)\n return summary\n }\n\n /** List all non-expired overrides. Used by the admin UI. */\n listActive(): HandoverOverride[] {\n const file = this.load()\n const now = Date.now()\n return Object.values(file.overrides).filter(\n (o) => !o.expiresAt || Date.parse(o.expiresAt) >= now,\n )\n }\n\n /** Overrides pointing at this agent (incoming) or away from it (outgoing). */\n listByAgent(agentId: string): { incoming: HandoverOverride[]; outgoing: HandoverOverride[] } {\n const all = this.listActive()\n return {\n incoming: all.filter((o) => o.toAgent === agentId),\n outgoing: all.filter((o) => o.fromAgent === agentId),\n }\n }\n}\n","import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync, statSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { buildIndexCached, scoreAll } from \"../memory/bm25\"\nimport { FactLedger } from \"@/wiki/facts/ledger\"\nimport type { Provenance } from \"./fact-freshness\"\nimport { memoryContextLines, renderMemoryBlock, type ContextLine } from \"./memory-context\"\nimport { containsSecret, factTrust, initialReview, isInjectable, trustForChannel, type FactReview, type SourceTrust } from \"./memory-trust\"\n\n// --- Persistent agent memory store ---\n// JSONL-based. One file per agent: .agentx/memory/{agentId}.jsonl\n// Extracted by Haiku post-conversation, injected pre-conversation.\n// What may be stored and injected: memory-trust.ts.\n\nexport interface MemoryFact {\n id: string\n agentId: string\n /** \"secret\" is legacy: no longer extracted, never injected. */\n category: \"fact\" | \"secret\" | \"preference\" | \"commitment\" | \"task-state\"\n content: string\n keywords: string[]\n source: { channel: string; chatId: string; sender: string; date: string; trust?: SourceTrust }\n /** \"held\" facts wait for an operator before they are injected. */\n review?: FactReview\n reviewedBy?: string\n reviewedAt?: string\n createdAt: string\n expiresAt?: string\n /** Where the fact was checked (#273). Absent on facts nobody checked. */\n provenance?: Partial<Provenance>\n /** Flagged by `agentx memory facts flag-unsourced`: volatile, no source. */\n unverified?: { since: string; reason: string }\n // Spaced repetition fields\n accessCount?: number\n lastAccessed?: string\n nextReview?: string\n /** Review interval in days — doubles on each successful recall */\n intervalDays?: number\n}\n\nconst MAX_MEMORIES = 200\nconst TASK_STATE_TTL_DAYS = 7\n\n\nexport class MemoryStore {\n private memoryDir: string\n /** The wiki's fact ledger: memory lines that restate one of its facts\n * are rendered as references to it (memory-context.ts). */\n private ledger: FactLedger\n\n constructor(baseDir: string = process.cwd()) {\n this.memoryDir = resolve(baseDir, \".agentx/memory\")\n this.ledger = new FactLedger(resolve(baseDir, \".agentx/wiki\"))\n if (!existsSync(this.memoryDir)) {\n mkdirSync(this.memoryDir, { recursive: true })\n }\n }\n\n private filePath(agentId: string): string {\n return resolve(this.memoryDir, `${agentId}.jsonl`)\n }\n\n /** Store a fact. Refuses secrets (returns false). Stamps the source's\n * trust, and holds facts from external sources for review. */\n addMemory(agentId: string, fact: Omit<MemoryFact, \"id\" | \"createdAt\">): boolean {\n if (fact.category === \"secret\" || containsSecret(fact.content)) return false\n const trust = fact.source.trust ?? trustForChannel(fact.source.channel)\n const review = fact.review ?? initialReview(trust)\n const entry: MemoryFact = {\n ...fact,\n source: { ...fact.source, trust },\n ...(review ? { review } : {}),\n id: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,\n createdAt: new Date().toISOString(),\n }\n appendFileSync(this.filePath(agentId), JSON.stringify(entry) + \"\\n\")\n return true\n }\n\n /** Parsed facts per file, valid while the file's mtime and size hold.\n * The store is read on every turn and the file only grows, so without\n * this each task re-parsed a hundred-kilobyte log on the event loop. */\n private parsedCache = new Map<string, { mtimeMs: number; size: number; facts: MemoryFact[] }>()\n\n getAll(agentId: string): MemoryFact[] {\n const file = this.filePath(agentId)\n if (!existsSync(file)) return []\n\n try {\n const { mtimeMs, size } = statSync(file)\n const hit = this.parsedCache.get(file)\n if (hit && hit.mtimeMs === mtimeMs && hit.size === size) return hit.facts.slice()\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\").filter(l => l.trim())\n const facts = lines.map(l => {\n try { return JSON.parse(l) as MemoryFact } catch { return null }\n }).filter((m): m is MemoryFact => m !== null)\n this.parsedCache.set(file, { mtimeMs, size, facts })\n return facts.slice()\n } catch {\n return []\n }\n }\n\n findRelevant(message: string, agentId: string, limit: number = 8): MemoryFact[] {\n const memories = this.getAll(agentId).filter(isInjectable)\n if (memories.length === 0) return []\n\n // BM25 over keywords + content (cached)\n const docs = memories.map((m) => `${m.keywords.join(\" \")} ${m.content}`)\n const cachePath = resolve(this.memoryDir, `${agentId}_bm25_cache.json`)\n const index = buildIndexCached(docs, cachePath)\n const bm25Scores = scoreAll(message, index)\n const scoreMap = new Map(bm25Scores.map((r) => [r.docIndex, r.score]))\n\n const scored = memories.map((m, i) => {\n let s = scoreMap.get(i) ?? 0\n if (m.category === \"commitment\") s += 1\n return { memory: m, score: s }\n })\n\n const matched = scored.filter((s) => s.score > 0)\n if (matched.length === 0) return memories.slice(-limit) // fallback: recent\n\n const selected = matched\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map((s) => s.memory)\n\n // Track access for spaced repetition (fire-and-forget)\n if (selected.length > 0) {\n try { this.recordAccess(agentId, selected.map(m => m.id)) } catch {}\n }\n\n return selected\n }\n\n buildContext(memories: MemoryFact[]): string {\n return renderMemoryBlock(this.contextLines(memories))\n }\n\n /** The facts buildContext actually renders — the rest fall past its\n * character budget. Trace capture records these ids, not the candidates. */\n contextFacts(memories: MemoryFact[]): MemoryFact[] {\n return this.contextLines(memories).map((l) => l.fact)\n }\n\n private contextLines(memories: MemoryFact[]): ContextLine[] {\n let wikiFacts: ReturnType<FactLedger[\"list\"]> = []\n try { wikiFacts = this.ledger.list() } catch { /* no ledger: plain memory */ }\n return memoryContextLines(memories, wikiFacts)\n }\n\n /**\n * Record that memories were accessed (for spaced repetition tracking).\n */\n recordAccess(agentId: string, memoryIds: string[]): void {\n if (memoryIds.length === 0) return\n const memories = this.getAll(agentId)\n const idSet = new Set(memoryIds)\n let changed = false\n\n for (const m of memories) {\n if (idSet.has(m.id)) {\n m.accessCount = (m.accessCount || 0) + 1\n m.lastAccessed = new Date().toISOString()\n // Double the review interval (spaced repetition: 1 → 2 → 4 → 8 → 16 days)\n const currentInterval = m.intervalDays || 1\n m.intervalDays = Math.min(currentInterval * 2, 30)\n m.nextReview = new Date(Date.now() + m.intervalDays * 86400000).toISOString()\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(\n this.filePath(agentId),\n memories.map(m => JSON.stringify(m)).join(\"\\n\") + \"\\n\",\n )\n }\n }\n\n /**\n * Get memories due for review (spaced repetition).\n * Returns facts whose nextReview date has passed or that have never been reviewed.\n */\n getDueForReview(agentId: string, limit: number = 5): MemoryFact[] {\n const memories = this.getAll(agentId).filter(isInjectable)\n const now = Date.now()\n\n return memories\n .filter(m => {\n // Skip task-state (ephemeral)\n if (m.category === \"task-state\") return false\n // Never reviewed → due\n if (!m.nextReview) return true\n // Past review date → due\n return new Date(m.nextReview).getTime() <= now\n })\n .sort((a, b) => {\n // Prioritize: never reviewed, then oldest nextReview, then most accessed\n if (!a.nextReview && b.nextReview) return -1\n if (a.nextReview && !b.nextReview) return 1\n if (a.nextReview && b.nextReview) {\n return new Date(a.nextReview).getTime() - new Date(b.nextReview).getTime()\n }\n return (a.accessCount || 0) - (b.accessCount || 0)\n })\n .slice(0, limit)\n }\n\n /**\n * Build a recall prompt for memories due for review.\n * Injected into heartbeat context so the agent proactively recalls facts.\n */\n buildRecallContext(agentId: string): string {\n const due = this.getDueForReview(agentId, 5)\n if (due.length === 0) return \"\"\n\n const lines = [\"[Memory Recall — facts due for review, verify they are still accurate]\"]\n for (const m of due) {\n const age = Math.round((Date.now() - new Date(m.createdAt).getTime()) / 86400000)\n lines.push(`- [${m.category}] ${m.content} (${age}d old, accessed ${m.accessCount || 0}x)`)\n }\n lines.push(\"[If any fact is outdated, note the correction. Otherwise, acknowledge recall.]\")\n return lines.join(\"\\n\")\n }\n\n /** Facts waiting for an operator's review, oldest first. Includes\n * external facts stored before review existed: they have no review\n * state, and are kept out of prompts all the same. */\n held(agentId: string): MemoryFact[] {\n return this.getAll(agentId).filter((m) =>\n m.review === \"held\" || (m.review === undefined && factTrust(m) === \"external\"))\n }\n\n /** Approve or reject a fact. Returns false when the id is unknown. */\n review(agentId: string, id: string, decision: \"approved\" | \"rejected\", by = \"operator\"): boolean {\n const memories = this.getAll(agentId)\n const m = memories.find((f) => f.id === id)\n if (!m) return false\n m.review = decision\n m.reviewedBy = by\n m.reviewedAt = new Date().toISOString()\n this.rewrite(agentId, memories)\n return true\n }\n\n /** Facts that contain a credential, stored before extraction stopped\n * keeping them. They are never injected; `apply` also deletes them. */\n scrubSecrets(agentId: string, apply = false): number {\n const memories = this.getAll(agentId)\n const keep = memories.filter((m) => m.category !== \"secret\" && !containsSecret(m.content))\n const removed = memories.length - keep.length\n if (apply && removed > 0) this.rewrite(agentId, keep)\n return removed\n }\n\n /** Trust and review counts, for the CLI. */\n trustSummary(agentId: string): Record<string, number> {\n const out: Record<string, number> = {}\n for (const m of this.getAll(agentId)) {\n const key = `${factTrust(m)}${m.review ? `/${m.review}` : \"\"}`\n out[key] = (out[key] ?? 0) + 1\n }\n return out\n }\n\n /** The agent's facts file, for backups. */\n fileFor(agentId: string): string {\n return this.filePath(agentId)\n }\n\n /** Replace every fact of an agent. Callers back the file up first. */\n rewrite(agentId: string, memories: MemoryFact[]): void {\n // Lines getAll could not parse are kept as they are, not dropped:\n // a rewrite must never lose what it did not understand.\n let unreadable: string[] = []\n try {\n unreadable = readFileSync(this.filePath(agentId), \"utf-8\").split(\"\\n\")\n .filter((l) => { if (!l.trim()) return false; try { JSON.parse(l); return false } catch { return true } })\n } catch { /* no file yet */ }\n const lines = [...memories.map((m) => JSON.stringify(m)), ...unreadable]\n writeFileSync(this.filePath(agentId), lines.join(\"\\n\") + (lines.length ? \"\\n\" : \"\"))\n }\n\n /**\n * Check if a substantially similar memory already exists.\n */\n hasSimilar(agentId: string, content: string): boolean {\n const memories = this.getAll(agentId)\n const contentLower = content.toLowerCase()\n const contentWords = contentLower.split(/\\s+/).filter(w => w.length > 3)\n\n return memories.some(m => {\n const mLower = m.content.toLowerCase()\n // Check for high word overlap\n const overlap = contentWords.filter(w => mLower.includes(w)).length\n return overlap >= contentWords.length * 0.7\n })\n }\n\n /**\n * Prune old/expired memories. Cap at MAX_MEMORIES per agent.\n */\n prune(agentId: string): void {\n let memories = this.getAll(agentId)\n if (memories.length === 0) return\n\n const now = Date.now()\n const ttlMs = TASK_STATE_TTL_DAYS * 24 * 60 * 60 * 1000\n\n // Remove expired task-state memories\n memories = memories.filter(m => {\n if (m.category === \"task-state\" && m.expiresAt) {\n return new Date(m.expiresAt).getTime() > now\n }\n if (m.category === \"task-state\" && !m.expiresAt) {\n return now - new Date(m.createdAt).getTime() < ttlMs\n }\n return true\n })\n\n // Cap at MAX_MEMORIES (keep most recent)\n if (memories.length > MAX_MEMORIES) {\n memories = memories.slice(-MAX_MEMORIES)\n }\n\n // Rewrite file\n const file = this.filePath(agentId)\n writeFileSync(file, memories.map(m => JSON.stringify(m)).join(\"\\n\") + \"\\n\")\n }\n}\n","import { daysSince, needsRecheck, ageDays, VERIFY_OR_ASK_RULE } from \"./fact-freshness\"\nimport { isStaleFact, matchFact, type WikiFact } from \"@/wiki/facts/ledger\"\nimport type { MemoryFact } from \"./memory-store\"\n\n// --- The Agent Memory block: flag stale lines, reference the wiki ---\n//\n// Two rules from #273 apply when memory is rendered into a prompt:\n// - a volatile fact past its TTL is marked UNVERIFIED, with the\n// verify-or-ask rule once at the top of the block;\n// - a memory line that restates a wiki fact is replaced by a reference\n// to that fact, which carries its source and check date. Several lines\n// about one fact become one reference, so the copy never outranks the\n// source and the block gets shorter, not longer.\n\nexport const MEMORY_HEADER = \"[Agent Memory — persistent facts from past conversations]\"\nconst BUDGET = 2400\n\nexport interface ContextLine {\n fact: MemoryFact\n line: string\n unverified: boolean\n /** The wiki fact rendered instead of the memory line. */\n wikiRef?: string\n}\n\nfunction flag(unverified: boolean, age: number | null): string {\n return unverified ? `UNVERIFIED (${age === null ? \"age unknown\" : `${age}d old`}) ` : \"\"\n}\n\nexport function wikiRefLine(f: WikiFact, now = Date.now()): { line: string; unverified: boolean } {\n const unverified = isStaleFact(f, now)\n const tag = f.undated ? \"UNVERIFIED (never checked) \" : flag(unverified, daysSince(f.verifiedAt, now))\n const line = `- [wiki ${f.id}] ${tag}` +\n `${f.subject} · ${f.attribute}: ${f.value} (${f.source}, checked ${f.verifiedAt.slice(0, 10)} by ${f.verifiedBy})`\n return { line, unverified }\n}\n\nfunction memoryLine(m: MemoryFact, now: number): { line: string; unverified: boolean } {\n const isDM = !m.source.chatId.startsWith(\"-\") && /^\\d+$/.test(m.source.chatId)\n const scope = isDM ? \"DM\" : m.source.chatId\n const unverified = needsRecheck(m, now)\n return { line: `- [${m.category}] ${flag(unverified, ageDays(m, now))}${m.content} (${scope}, ${m.source.date})`, unverified }\n}\n\n/** The lines the memory block renders, within its character budget. */\nexport function memoryContextLines(memories: MemoryFact[], wikiFacts: WikiFact[] = [], now = Date.now()): ContextLine[] {\n const kept: ContextLine[] = []\n const referenced = new Set<string>()\n let chars = MEMORY_HEADER.length\n let ruleCounted = false\n\n for (const m of memories) {\n const wf = wikiFacts.length > 0 ? matchFact(m.content, wikiFacts) : null\n let rendered: { line: string; unverified: boolean }\n let wikiRef: string | undefined\n // A memory written after the wiki's last check that says something\n // else may be news; it stays, marked, rather than being hidden.\n const newerNews = wf\n && Date.parse(m.createdAt) > Date.parse(wf.verifiedAt)\n && !m.content.toLowerCase().includes(wf.value.toLowerCase())\n if (wf && !newerNews) {\n if (referenced.has(wf.id)) continue\n referenced.add(wf.id)\n rendered = wikiRefLine(wf, now)\n wikiRef = wf.id\n } else {\n rendered = memoryLine(m, now)\n if (wf) rendered.line += ` [differs from wiki ${wf.id}]`\n }\n\n const extra = rendered.unverified && !ruleCounted ? VERIFY_OR_ASK_RULE.length : 0\n if (chars + extra + rendered.line.length > BUDGET) break\n kept.push({ fact: m, ...rendered, ...(wikiRef ? { wikiRef } : {}) })\n chars += extra + rendered.line.length\n if (extra) ruleCounted = true\n }\n return kept\n}\n\nexport function renderMemoryBlock(kept: ContextLine[]): string {\n if (kept.length === 0) return \"\"\n const rule = kept.some((l) => l.unverified) ? [VERIFY_OR_ASK_RULE] : []\n return [MEMORY_HEADER, ...rule, ...kept.map((l) => l.line), \"[End Memory]\"].join(\"\\n\")\n}\n","// --- Mesh error wrappers ---\n//\n// A failure that crosses the mesh arrives wrapped in our own plumbing. A\n// fallback hop wraps the origin peer's message, e.g.\n// Peer \"a\" /task error: 500: mesh fallback failed: Peer \"b\" /task error: 500: <real cause>\n// and a peer that answered 200 with an error gives\n// Peer \"a\" agent error: <real cause>\n// (both shapes come from mesh.sendTask and registry's mesh fallback).\n//\n// One peel, shared by the router's failure notice (cleanMeshError) and the\n// queued-answer check (parseQueued), so the two never disagree about what\n// the peer actually said.\n\n/** The innermost cause of a mesh error, without peer names or status codes.\n * Runs until it stops making progress; bounded for safety. */\nexport function unwrapMeshError(raw: string): string {\n let out = (raw || \"\").trim()\n for (let i = 0; i < 5; i++) {\n const before = out\n out = out\n .replace(/^Peer\\s+\"[^\"]*\"\\s+\\/task error:\\s*\\d{3}:\\s*/i, \"\")\n .replace(/^Peer\\s+\"[^\"]*\"\\s+agent error:\\s*/i, \"\")\n .replace(/^mesh fallback failed:\\s*/i, \"\")\n .trim()\n if (out === before) break\n }\n return out\n}\n","// The registry's \"queued\" answer.\n//\n// When an agent is busy on a chat, registry.execute puts the message in the\n// chat's queue and returns `error: \"__queued__:<mode>:<pending>\"`. The\n// message was ACCEPTED: the queue flush runs it once the current turn ends\n// and posts the reply on the original channel. It is not a failure.\n//\n// The marker travels in the `error` field, so every caller that reads an\n// error has to recognise it first. A mesh hop wraps it on the way back\n// (`Peer \"p\" /task error: 500: __queued__:collect:1`, possibly nested inside\n// a `mesh fallback failed:` chain), so matching with startsWith on the local\n// answer alone missed it and the router posted \"I couldn't complete that —\n// queued:collect:1\" on the thread (#282). This is the one place that knows\n// the marker's shape.\n\nimport { unwrapMeshError } from \"@/a2a/mesh-errors\"\n\nexport const QUEUED_MARKER = \"__queued__\"\n\nexport interface QueuedAnswer {\n /** The queue mode that took the message: \"collect\" or \"followup\". */\n mode: string\n /** Messages waiting on that chat, this one included. */\n pending: number\n}\n\n/** The marker registry.execute returns for a message it queued. */\nexport function queuedMarker(mode: string, pending: number): string {\n return `${QUEUED_MARKER}:${mode}:${pending}`\n}\n\n// After the mesh wrappers are peeled (the same peel the router's failure\n// notice uses), the whole remaining text must be the marker: anything\n// else, including an error that merely ends in it, is a real error.\nconst QUEUED_RE = /^__queued__(?::([A-Za-z-]+):(\\d+))?$/\n\n/** The queued answer inside `text` (a registry error or a mesh error\n * message), or null when it is anything else. */\nexport function parseQueued(text: string | null | undefined): QueuedAnswer | null {\n if (!text) return null\n const m = QUEUED_RE.exec(unwrapMeshError(text))\n if (!m) return null\n return { mode: m[1] || \"collect\", pending: m[2] ? Number(m[2]) : 1 }\n}\n\n/** True when `text` is the queued answer, local or wrapped by a mesh hop. */\nexport function isQueued(text: string | null | undefined): boolean {\n return parseQueued(text) !== null\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve, relative } from \"path\"\nimport { lintWorkflow, workflowSchema, type Workflow } from \"./types\"\nimport { parseYamlWorkflow, WorkflowYamlError } from \"./yaml\"\nimport { renderWorkflowYamlPreservingComments } from \"./yaml-roundtrip\"\n\n/** Parsed workflow lists per directory, keyed on a signature of the\n * directory's file names, mtimes and sizes. Module-level because the\n * store itself is rebuilt on every message. */\nconst listCache = new Map<string, { signature: string; workflows: Workflow[] }>()\n\n// --- WorkflowStore (V2) ---\n//\n// Filesystem persistence for workflow definitions. JSON on disk, one file\n// per workflow, same convention as .agentx/procedures/ and .agentx/wiki/.\n// V2 workflows are DAGs (nodes + edges); see types.ts. The matching\n// dispatcher lives in dispatcher.ts and walks wf.nodes for the trigger.\n//\n// Move C — YAML files (`.yaml`/`.yml`) are loaded alongside JSON. The\n// editor still saves JSON; YAML is read-only authoring. When a workflow\n// has BOTH `<id>.yaml` and `<id>.json`, list()/validateAll() report the\n// collision rather than silently picking one — operators delete one.\n\nexport interface WorkflowStoreOptions {\n baseDir?: string\n}\n\nexport interface WorkflowValidation {\n workflow: Workflow\n issues: string[]\n isValid: boolean\n}\n\nexport class WorkflowStore {\n readonly baseDir: string\n\n constructor(opts: WorkflowStoreOptions = {}) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n mkdirSync(this.baseDir, { recursive: true })\n }\n\n private pathFor(id: string): string {\n return this.pathForJson(id)\n }\n\n private pathForJson(id: string): string {\n return resolve(this.baseDir, `${id}.json`)\n }\n\n private pathForYaml(id: string): string {\n return resolve(this.baseDir, `${id}.yaml`)\n }\n\n /** True for any *.json / *.yaml / *.yml file that doesn't start with `_`\n * (which is how run logs / index files are filtered out). */\n private isWorkflowFile(name: string): boolean {\n if (name.startsWith(\"_\")) return false\n return name.endsWith(\".json\") || name.endsWith(\".yaml\") || name.endsWith(\".yml\")\n }\n\n /** Strip the format extension to recover the workflow id. */\n private idFromFilename(name: string): string {\n return name.replace(/\\.(json|ya?ml)$/, \"\")\n }\n\n /** Parse a workflow file from disk based on its extension. JSON is the\n * canonical shape; YAML runs through the desugar pass first. */\n private readAndParse(name: string): unknown {\n const full = resolve(this.baseDir, name)\n const text = readFileSync(full, \"utf-8\")\n if (name.endsWith(\".json\")) return JSON.parse(text)\n return parseYamlWorkflow(text, { filePath: name })\n }\n\n /** Returns every well-formed workflow. Malformed files are skipped; the\n * CLI `validate` subcommand is where authors get details.\n *\n * Coexisting `<id>.json` + `<id>.yaml` is treated as a hard ambiguity\n * — neither variant is loaded, and the id is omitted from list().\n * validateAll() surfaces the collision with a clear message. */\n list(): Workflow[] {\n if (!existsSync(this.baseDir)) return []\n const byId = new Map<string, string[]>()\n const sig: string[] = []\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n const id = this.idFromFilename(entry.name)\n const list = byId.get(id) ?? []\n list.push(entry.name)\n byId.set(id, list)\n try {\n const st = statSync(resolve(this.baseDir, entry.name))\n sig.push(`${entry.name}:${st.mtimeMs}:${st.size}`)\n } catch {\n sig.push(`${entry.name}:?`)\n }\n }\n // The router and the registry both list workflows on every message,\n // and the store is rebuilt per call. Parsing is the expensive part,\n // so it is keyed on the directory's names, mtimes and sizes.\n const signature = sig.sort().join(\"|\")\n const cached = listCache.get(this.baseDir)\n if (cached && cached.signature === signature) return cached.workflows.slice()\n const out: Workflow[] = []\n for (const [, files] of byId) {\n // Skip ambiguous ids — list() must never pick a winner silently.\n if (files.length > 1) continue\n try {\n const raw = this.readAndParse(files[0])\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // intentional: skip malformed\n }\n }\n const workflows = out.sort((a, b) => a.id.localeCompare(b.id))\n listCache.set(this.baseDir, { signature, workflows })\n return workflows.slice()\n }\n\n validateAll(): Array<WorkflowValidation | { id: string; path: string; issues: string[]; isValid: false }> {\n const results: Array<WorkflowValidation | { id: string; path: string; issues: string[]; isValid: false }> = []\n if (!existsSync(this.baseDir)) return results\n\n const byId = new Map<string, string[]>()\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n const id = this.idFromFilename(entry.name)\n const list = byId.get(id) ?? []\n list.push(entry.name)\n byId.set(id, list)\n }\n\n for (const [id, files] of byId) {\n // Coexisting <id>.json + <id>.yaml — surface BOTH paths with a\n // duplicate-id error attached to each so the operator sees what\n // to delete. Sort for deterministic output.\n if (files.length > 1) {\n const sorted = [...files].sort()\n for (const name of sorted) {\n results.push({\n id,\n path: relative(this.baseDir, resolve(this.baseDir, name)),\n issues: [`duplicate workflow id \"${id}\" — found ${sorted.join(\" and \")}; delete one to disambiguate`],\n isValid: false,\n })\n }\n continue\n }\n\n const name = files[0]\n const full = resolve(this.baseDir, name)\n const rel = relative(this.baseDir, full)\n let raw: unknown\n try {\n raw = this.readAndParse(name)\n } catch (e: any) {\n const reason =\n e instanceof WorkflowYamlError\n ? e.message\n : `parse error: ${e?.message ?? e}`\n results.push({ id, path: rel, issues: [reason], isValid: false })\n continue\n }\n const parsed = workflowSchema.safeParse(raw)\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\n results.push({ id: String((raw as any)?.id ?? id), path: rel, issues, isValid: false })\n continue\n }\n const lintIssues = lintWorkflow(parsed.data)\n results.push({ workflow: parsed.data, issues: lintIssues, isValid: lintIssues.length === 0 })\n }\n return results\n }\n\n get(id: string): Workflow | null {\n const jsonPath = this.pathForJson(id)\n const yamlPath = this.pathForYaml(id)\n const ymlPath = resolve(this.baseDir, `${id}.yml`)\n const haveJson = existsSync(jsonPath)\n const haveYaml = existsSync(yamlPath) || existsSync(ymlPath)\n // Coexistence is ambiguous at validateAll time; here we refuse to\n // pick a winner so callers don't silently get whichever the file\n // system orders first. Returning null mirrors \"no such workflow\".\n if (haveJson && haveYaml) return null\n if (haveJson) {\n try {\n const raw = JSON.parse(readFileSync(jsonPath, \"utf-8\"))\n const parsed = workflowSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n const yPath = existsSync(yamlPath) ? yamlPath : (existsSync(ymlPath) ? ymlPath : null)\n if (yPath) {\n try {\n const text = readFileSync(yPath, \"utf-8\")\n const raw = parseYamlWorkflow(text, { filePath: relative(this.baseDir, yPath) })\n const parsed = workflowSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n // Filename ≠ id fallback. Authors sometimes pick a short filename\n // for an `id:` that's longer / differently namespaced (e.g.\n // `initech-mr-fix-loop.yaml` containing `id: initech-int-initech-tn-mr-fix-loop`).\n // list() finds these because it scans the directory; the per-id\n // fast paths above don't. Scan as a last resort so the editor's\n // GET /api/workflows/:id can still load the workflow.\n if (!existsSync(this.baseDir)) return null\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n const filenameId = this.idFromFilename(entry.name)\n if (filenameId === id) continue // already tried above\n try {\n const raw = this.readAndParse(entry.name)\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success && parsed.data.id === id) return parsed.data\n } catch {\n // skip malformed file\n }\n }\n return null\n }\n\n save(workflow: Workflow, opts: { convertFromYaml?: boolean } = {}): Workflow {\n const now = new Date().toISOString().slice(0, 10)\n const normalized = workflowSchema.parse({\n ...workflow,\n created: workflow.created || now,\n updated: now,\n })\n // YAML-authored workflows round-trip back to YAML so the editor\n // doesn't silently strip the operator's comments. yaml@2's Document\n // API preserves commentBefore on top-level keys and per-node items,\n // so per-node `# ...` blocks survive a structural edit. Comments tied\n // to deleted keys / removed nodes are dropped by design (no honest\n // place to relocate them).\n //\n // `convertFromYaml: true` is the explicit \"I want JSON\" opt-in: the\n // editor surfaces a confirm modal (\"you'll lose comments\"), then\n // retries with this flag set. We delete the YAML and write JSON.\n const yamlPath = this.pathForYaml(normalized.id)\n const ymlPath = resolve(this.baseDir, `${normalized.id}.yml`)\n let existingYaml = existsSync(yamlPath) ? yamlPath : (existsSync(ymlPath) ? ymlPath : null)\n // Filename ≠ id fallback (matches get()'s fallback): if no YAML at\n // <id>.yaml, scan for any *.ya?ml whose parsed `id:` equals this\n // workflow's id and round-trip THAT file. Without this, a YAML\n // named `initech-mr-fix-loop.yaml` (id `initech-int-initech-tn-mr-fix-loop`)\n // would slip through to the JSON-write path below, leaving the\n // YAML as an orphan and creating a coexistence trap on next load.\n if (!existingYaml && existsSync(this.baseDir)) {\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n if (entry.name.endsWith(\".json\")) continue\n try {\n const raw = this.readAndParse(entry.name)\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success && parsed.data.id === normalized.id) {\n existingYaml = resolve(this.baseDir, entry.name)\n break\n }\n } catch { /* skip malformed */ }\n }\n }\n if (existingYaml && opts.convertFromYaml) {\n unlinkSync(existingYaml)\n writeFileSync(this.pathForJson(normalized.id), JSON.stringify(normalized, null, 2) + \"\\n\")\n return normalized\n }\n if (existingYaml) {\n const originalText = readFileSync(existingYaml, \"utf-8\")\n try {\n const out = renderWorkflowYamlPreservingComments(originalText, normalized)\n writeFileSync(existingYaml, out)\n return normalized\n } catch (e: any) {\n // Round-trip should never fail on a file we just successfully\n // parsed elsewhere, but if the merge throws (e.g. the source has\n // a structure we don't understand), surface the failure rather\n // than silently fall through to JSON.\n const err = new Error(\n `yaml round-trip failed for \"${normalized.id}\" at ${existingYaml}: ${e?.message ?? e}`,\n ) as Error & { yamlPath?: string }\n err.yamlPath = existingYaml\n throw err\n }\n }\n writeFileSync(this.pathForJson(normalized.id), JSON.stringify(normalized, null, 2) + \"\\n\")\n return normalized\n }\n\n delete(id: string): boolean {\n const jsonPath = this.pathForJson(id)\n const yamlPath = this.pathForYaml(id)\n const ymlPath = resolve(this.baseDir, `${id}.yml`)\n let removed = false\n if (existsSync(jsonPath)) { unlinkSync(jsonPath); removed = true }\n if (existsSync(yamlPath)) { unlinkSync(yamlPath); removed = true }\n if (existsSync(ymlPath)) { unlinkSync(ymlPath); removed = true }\n return removed\n }\n}\n","import { z } from \"zod\"\n\n// --- Workflows (V2 — dataflow DAG) ---\n//\n// A workflow is a directed acyclic graph of nodes connected by edges.\n// A run executes the graph by walking from the trigger, accumulating each\n// node's output into a shared context. Prompts and action params template\n// against the context via `{{<nodeId>.<path>}}`.\n//\n// This replaces the previous state-machine model (states + transitions +\n// external label backend). The label-as-state pattern is re-expressible\n// via a `checkpoint` node + `action.setLabel` + `action.readLabel`.\n//\n// Shape on disk (.agentx/workflows/):\n// <id>.json — one workflow definition\n// _runs/<run-id>.jsonl — append-only run events (home-node only)\n// _index/<backend>__<entity>.json — entity -> active runId lookup\n//\n// JSON-on-disk: no YAML dep.\n\n// --------------------------- Node types -----------------------------------\n\nexport const nodeTypeSchema = z.enum([\n // Entry points\n \"trigger.channel\",\n \"trigger.manual\",\n \"trigger.cron\",\n \"trigger.hook\",\n \"trigger.form\",\n // Runs a registered action on an interval; one run per new item (poll.ts).\n \"trigger.poll\",\n // Compute\n \"agent\",\n \"transform\",\n // A typed classification, answered by the decision seat rather than by\n // an agent emitting a RESULT token. Doubles as a branch: it fires the\n // port named after the label it chose, or `unsure` when nothing clears\n // its confidence threshold.\n \"classify\",\n // Control flow\n \"branch\",\n \"gateway.parallel\",\n \"rule\",\n // Side-effect sinks (per channel verb). Each action.* maps 1:1 to an\n // existing channel-adapter method. New verbs arrive as new entries.\n \"action.send\",\n \"action.createIssue\",\n \"action.setLabel\",\n \"action.readLabel\",\n \"action.react\",\n \"action.editMessage\",\n \"action.logTime\",\n \"action.callHTTP\",\n // Registered action invocation — references an entry from the action\n // registry (.agentx/actions/<id>.json) by id, with templated inputs.\n // Lets operators define a reusable shell/http action once and call it\n // from many workflows without duplicating the command/url/headers.\n \"action.run\",\n // Built-in typed action invocation (improvement plan #6 + #9). Calls\n // a daemon-shipped action (http.fetch, mesh.delegate, extract.structured,\n // etc.) by name. Input is templated against the run context the same\n // way action.run is. Output is the validated builtin response so\n // downstream nodes can pipe typed data forward without reparsing.\n \"action.builtin\",\n // BPM: human tasks + composition + signals + intermediate timer\n \"subProcess\",\n \"signal.emit\",\n \"signal.wait\",\n \"timer.boundary\",\n // Follow-up steps (#788): the owner and the people a workflow works\n // with. Each has a main port (yes, sent, reply) and a fallback port\n // (no / expired, declined, timeout); see FOLLOW_UP_PORTS.\n \"owner.notify\",\n \"owner.ask\",\n \"person.message\",\n \"person.wait\",\n // Persistence / pause\n \"checkpoint\",\n // Terminal\n \"end\",\n])\nexport type NodeType = z.infer<typeof nodeTypeSchema>\n\n// --------------------------- Condition (branch evaluator) -----------------\n\nexport const conditionKindSchema = z.enum([\n \"equals\", // params: { path, value }\n \"contains\", // params: { path, value }\n \"matches\", // params: { path, regex }\n \"exists\", // params: { path }\n])\nexport type ConditionKind = z.infer<typeof conditionKindSchema>\n\nexport const conditionSchema = z.object({\n kind: conditionKindSchema,\n params: z.record(z.unknown()).default({}),\n})\nexport type Condition = z.infer<typeof conditionSchema>\n\n// --------------------------- Nodes + edges --------------------------------\n\n/** Improvement plan #9b — per-node retry policy. When a node's\n * handler returns `{ error }` (or throws), the dispatcher re-runs it\n * up to `maxAttempts - 1` more times with exponential backoff\n * (`backoffMs * 2^(attempt-1)`). Once all attempts are exhausted,\n * the LAST error is what gets recorded as the node's failure.\n * Defaults: maxAttempts=1 (no retry), backoffMs=1000.\n *\n * Pause results (signalWait, timerWait) are NEVER retried —\n * pausing is a normal lifecycle transition, not a failure. Only\n * hard errors (`{error}` or thrown exceptions) trigger the retry. */\nexport const retryPolicySchema = z.object({\n maxAttempts: z.number().int().min(1).max(10).default(1),\n backoffMs: z.number().int().min(0).max(60_000).default(1000),\n}).default({ maxAttempts: 1, backoffMs: 1000 })\nexport type RetryPolicy = z.infer<typeof retryPolicySchema>\n\nexport const workflowNodeSchema = z.object({\n id: z.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/, \"node id must be identifier-safe\"),\n type: nodeTypeSchema,\n /** Free-form per-type config. Each node handler validates its own shape\n * at execution time via a local Zod schema; keeping the top-level schema\n * permissive lets new node types ship without a round-trip through here. */\n config: z.record(z.unknown()).default({}),\n /** Per-node retry on hard errors. See RetryPolicy. */\n retry: retryPolicySchema,\n /** Optional UI position. Ignored by the engine; persisted to keep the\n * editor layout alongside the workflow (no separate _layouts file). */\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n})\nexport type WorkflowNode = z.infer<typeof workflowNodeSchema>\n\nexport const workflowEdgeSchema = z.object({\n /** Source node id. */\n from: z.string(),\n /** Optional source port. Used by `branch` nodes to select an outgoing\n * case; default `\"out\"` for single-port nodes. */\n fromPort: z.string().optional(),\n /** Target node id. */\n to: z.string(),\n /** Reserved — `\"in\"` in v1. Kept for future multi-input nodes. */\n toPort: z.string().optional(),\n /** Optional label shown on the edge in the editor. */\n label: z.string().optional(),\n})\nexport type WorkflowEdge = z.infer<typeof workflowEdgeSchema>\n\n// --------------------------- Workflow -------------------------------------\n\n/** A date that may arrive as a string OR as a JS Date. YAML parsers coerce\n * an unquoted `2026-05-10` into a Date; JSON never does. Normalises to an\n * ISO string so downstream consumers see one type. */\nconst isoDateString = z.union([\n z.string(),\n z.date().transform((d) => d.toISOString()),\n])\n\nexport const workflowSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"workflow id must be lower-kebab\"),\n /** Schema version. V2 = dataflow DAG. V1 workflows are not loadable. */\n version: z.literal(2).default(2),\n title: z.string().min(1),\n description: z.string().optional(),\n /** REVIEW METADATA ONLY — `status` never gates dispatch. The one and only\n * dispatch gate is `state` below: a workflow with status=draft but\n * state=active WILL fire; status=active with state=disabled will NOT.\n * (The matcher in matcher.ts is the single exception: it requires both\n * to be active before suggesting.) Generated workflows are saved as\n * status=draft + state=disabled so they validate but never fire until\n * an operator promotes them. If you're deciding \"will this run?\", read\n * `state`; if you're deciding \"has a human reviewed this?\", read\n * `status`. */\n status: z.enum([\"draft\", \"review\", \"active\", \"deprecated\"]).default(\"active\"),\n tags: z.array(z.string()).default([]),\n /** Project this workflow is scoped to, in `<org>/<repo>` form for\n * GitLab/GitHub or any operator-defined slug for non-VCS workflows.\n * Optional — workflows without a project field are global and match\n * any project's events. When set, the field is BOTH metadata (UI\n * grouping, project badges) AND a hard scope: the dispatcher and\n * the per-workflow hook handler in `triggers.ts` drop events whose\n * `ctx.project` differs from this field. Without that gate, two\n * workflows tagged for different projects but subscribed to the\n * same hook (e.g. on:gitlab-issue) both fire on every event — see\n * the cross-tenant leak that prompted this commit. Project rules\n * in `.agentx/projects/` still pre-filter at the webhook layer; the\n * workflow-level scope is a defence in depth. */\n project: z.string().optional(),\n entity: z.string().optional(),\n intentPath: z.array(z.string()).default([]),\n generatedFrom: z.string().optional(),\n sourceTaskIds: z.array(z.string()).default([]),\n confidence: z.number().min(0).max(1).optional(),\n workflowVersion: z.string().optional(),\n ownerAgent: z.string().optional(),\n lastMatchedAt: z.string().optional(),\n matchCount: z.number().int().min(0).default(0),\n /** THE dispatch gate (see `status` above for the review-metadata axis).\n * The dispatcher and trigger registrar honor this:\n * - active: normal — triggers register, runs create + advance\n * - disabled: operator kill switch (config or admin action). No new\n * triggers, no new runs. In-flight runs continue.\n * - quarantined: system set by the conflict detector when this workflow\n * would race with another dispatch path (e.g. a gitlab\n * agentMapping handles the same project the workflow\n * triggers on). Treated like `disabled` at runtime;\n * resolved by either fixing the conflict or operator\n * explicitly flipping the state back to `active`. */\n state: z.enum([\"active\", \"disabled\", \"quarantined\"]).default(\"active\"),\n /** Higher priority wins when multiple workflows match the same event.\n * Default 0. Ties broken by id. */\n priority: z.number().int().default(0),\n /** If true, every matching workflow runs in parallel (each with its own\n * run). Default: only the highest-priority match runs. */\n fanOut: z.boolean().default(false),\n nodes: z.array(workflowNodeSchema).refine(\n (ns) => ns.length > 0, { message: \"workflow must have at least one node\" },\n ),\n edges: z.array(workflowEdgeSchema).default([]),\n /** Env vars that templates + node configs may read via {{env.*}}. */\n envAllow: z.array(z.string()).default([]),\n retention: z.object({\n maxRuns: z.number().int().positive().default(500),\n maxDays: z.number().int().positive().default(90),\n }).default({ maxRuns: 500, maxDays: 90 }),\n /** Safety rail on sub-process nesting depth. A parent at depth N may\n * spawn children up to depth maxChildDepth - 1. Default 5. Override per\n * workflow if a deeper composition is genuinely needed. */\n maxChildDepth: z.number().int().positive().default(5),\n /** Mesh integration. By default workflows are local — only triggers\n * observed on the same node can fire them. Set `mesh.allowRemote: true`\n * to opt the workflow into cross-mesh trigger fan-out: when a peer\n * receives a channel event, it broadcasts it to mesh peers and any\n * matching workflow with allowRemote runs on the peer that owns its\n * definition. The opt-in is required because remote events carry\n * reduced trust (sender provenance is mesh-token only) and can\n * surprise authors who expected isolation. */\n mesh: z.object({\n allowRemote: z.boolean().default(false),\n /** Optional peer-name allowlist. When present, only triggers\n * broadcast from these named peers are honored. Empty/unset = any\n * authenticated peer. Useful for production workflows that should\n * only accept events from a specific upstream node. */\n peers: z.array(z.string()).optional(),\n }).default({ allowRemote: false }),\n // YAML turns an unquoted `2026-05-10` into a JS Date, so a hand-authored\n // workflow that omits the quotes was silently rejected by the schema and\n // dropped by WorkflowStore.list() — which swallows parse failures. Accept\n // both shapes and normalise to an ISO string.\n created: isoDateString.optional(),\n updated: isoDateString.optional(),\n /** Follow-up (#788): an agent that picks this workflow for a request\n * starts it at once. Off: the owner is told which workflow it chose\n * first. */\n autoStart: z.boolean().default(false),\n /** When the owner approves `person.message` steps: all at once when the\n * run starts, or each one before it is sent. Unset: the node's\n * `workflows.followUp.approval` setting. A message to a person never\n * goes out without one of the two. */\n approval: z.enum([\"start\", \"step\"]).optional(),\n /** Stall time and nudges for this workflow's agent steps, over the\n * node's `workflows.followUp` settings. */\n followUp: z.object({\n stallMinutes: z.number().positive().max(7 * 24 * 60).optional(),\n maxNudges: z.number().int().min(0).max(20).optional(),\n }).optional(),\n})\nexport type Workflow = z.infer<typeof workflowSchema>\n\n/** The main port and the other ports of each follow-up step. An edge with\n * no `fromPort` leaves on the main port; an edge with one leaves on that\n * port only. A step that ends on another port with no edge for it stops\n * the run, and the owner is told why. */\nexport const FOLLOW_UP_PORTS: Record<string, { main: string; other: string[] }> = {\n \"owner.ask\": { main: \"yes\", other: [\"no\", \"expired\"] },\n \"person.message\": { main: \"sent\", other: [\"declined\"] },\n \"person.wait\": { main: \"reply\", other: [\"timeout\"] },\n}\n\n// --------------------------- DAG linter -----------------------------------\n//\n// Replaces the FSM-era `lintWorkflow` from V1. Checks:\n// - exactly one trigger.* node (v1 rule; multi-trigger out of scope)\n// - every edge's from/to references an existing node\n// - no unreachable nodes (except the trigger) — DFS from trigger\n// - no cycles unless every cycle has an agent or checkpoint on it (those\n// are the nodes that can pause or consume external events; cycles\n// without either would infinite-loop the walker)\n// - every branch node's edge.fromPort matches a case in its config\n// - at least one `end` or `checkpoint` is reachable (runs must terminate)\n\nexport function lintWorkflow(wf: Workflow): string[] {\n const issues: string[] = []\n const byId = new Map(wf.nodes.map((n) => [n.id, n] as const))\n\n // 1. exactly one trigger\n const triggers = wf.nodes.filter((n) => n.type.startsWith(\"trigger.\"))\n if (triggers.length === 0) issues.push(\"workflow must have exactly one trigger.* node (found 0)\")\n if (triggers.length > 1) issues.push(`workflow has ${triggers.length} trigger.* nodes; v1 supports exactly one`)\n\n // 2. edge endpoints exist\n for (const e of wf.edges) {\n if (!byId.has(e.from)) issues.push(`edge references missing node: from=\"${e.from}\"`)\n if (!byId.has(e.to)) issues.push(`edge references missing node: to=\"${e.to}\"`)\n }\n\n // 3. reachability from trigger\n const trigger = triggers[0]\n if (trigger) {\n const adj = new Map<string, string[]>()\n for (const e of wf.edges) {\n if (!adj.has(e.from)) adj.set(e.from, [])\n adj.get(e.from)!.push(e.to)\n }\n const seen = new Set<string>()\n const stack = [trigger.id]\n while (stack.length) {\n const id = stack.pop()!\n if (seen.has(id)) continue\n seen.add(id)\n for (const next of adj.get(id) ?? []) stack.push(next)\n }\n for (const n of wf.nodes) {\n if (!seen.has(n.id)) issues.push(`node \"${n.id}\" is unreachable from trigger \"${trigger.id}\"`)\n }\n if (!wf.nodes.some((n) => seen.has(n.id) && isTerminalOrPauseNode(n.type))) {\n issues.push(\"no reachable `end`, `checkpoint`, `subProcess`, `signal.wait`, or `timer.boundary` node — the run cannot terminate or pause\")\n }\n }\n\n // 4. branch ports (also applies to DMN-style `rule` nodes whose rows\n // carry the same `{ to }` shape and a shared `default`).\n for (const n of wf.nodes) {\n if (n.type !== \"branch\" && n.type !== \"rule\") continue\n const cases = n.type === \"branch\"\n ? ((n.config.cases as Array<{ to: string }> | undefined) ?? [])\n : ((n.config.rules as Array<{ to?: string }> | undefined) ?? []).filter((r): r is { to: string } => typeof r.to === \"string\")\n const defaultPort = n.type === \"branch\"\n ? (n.config.default as string | undefined)\n : ((n.config.default as { to?: string } | undefined)?.to)\n const declared = new Set<string>(cases.map((c) => c.to))\n if (defaultPort) declared.add(defaultPort)\n const outgoing = wf.edges.filter((e) => e.from === n.id)\n for (const edge of outgoing) {\n const port = edge.fromPort ?? \"\"\n if (!declared.has(port)) {\n issues.push(`${n.type} \"${n.id}\" has outgoing edge with port \"${port}\" not declared in cases or default`)\n }\n }\n }\n\n // 5. cycle detection — only flag cycles that would infinite-loop (no agent\n // or checkpoint on the cycle).\n const onCycle = detectCyclesWithoutPauseNodes(wf)\n for (const nodeIds of onCycle) {\n issues.push(`cycle without agent/checkpoint: ${nodeIds.join(\" -> \")}`)\n }\n\n return issues\n}\n\nfunction isTerminalOrPauseNode(type: NodeType): boolean {\n return type === \"end\"\n || type === \"checkpoint\"\n || type === \"subProcess\"\n || type === \"signal.wait\"\n || type === \"timer.boundary\"\n}\n\nfunction isPauseCapableNode(type: NodeType): boolean {\n // Nodes that either pause the run (checkpoint, subProcess,\n // signal.wait, timer.boundary) or consume an external event (agent).\n // A cycle is safe if it crosses at least one such node — otherwise the\n // walker would spin forever.\n return type === \"agent\"\n || type === \"owner.ask\"\n || type === \"person.wait\"\n || type === \"checkpoint\"\n || type === \"subProcess\"\n || type === \"signal.wait\"\n || type === \"timer.boundary\"\n}\n\nfunction detectCyclesWithoutPauseNodes(wf: Workflow): string[][] {\n const adj = new Map<string, string[]>()\n for (const e of wf.edges) {\n if (!adj.has(e.from)) adj.set(e.from, [])\n adj.get(e.from)!.push(e.to)\n }\n const byId = new Map(wf.nodes.map((n) => [n.id, n] as const))\n const results: string[][] = []\n const color = new Map<string, 0 | 1 | 2>() // 0=unvisited, 1=in-progress, 2=done\n const stack: string[] = []\n const dfs = (id: string) => {\n if (color.get(id) === 1) {\n // cycle: slice from first occurrence\n const i = stack.indexOf(id)\n if (i >= 0) {\n const cycle = stack.slice(i).concat(id)\n const hasPause = cycle.some((nid) => {\n const t = byId.get(nid)?.type\n return t ? isPauseCapableNode(t) : false\n })\n if (!hasPause) results.push(cycle)\n }\n return\n }\n if (color.get(id) === 2) return\n color.set(id, 1)\n stack.push(id)\n for (const next of adj.get(id) ?? []) dfs(next)\n stack.pop()\n color.set(id, 2)\n }\n for (const n of wf.nodes) if (color.get(n.id) !== 2) dfs(n.id)\n return results\n}\n\n// --------------------------- Run model ------------------------------------\n\nexport const runStatusSchema = z.enum([\n \"running\",\n \"paused\",\n \"completed\",\n \"failed\",\n \"canceled\",\n])\nexport type RunStatus = z.infer<typeof runStatusSchema>\n\nexport const nodeExecutionStatusSchema = z.enum([\n \"ok\",\n \"failed\",\n \"timeout\",\n \"skipped\",\n \"paused\",\n \"resumed\",\n])\nexport type NodeExecutionStatus = z.infer<typeof nodeExecutionStatusSchema>\n\nexport const nodeExecutionEntrySchema = z.object({\n at: z.string(), // ISO-8601\n nodeId: z.string(),\n /** Which upstream node outputs fed this execution. Usually one id, or\n * empty for the trigger. */\n inputKeys: z.array(z.string()).default([]),\n status: nodeExecutionStatusSchema,\n /** The node's output bundle, if successful. Stored here so the full run\n * history can be replayed without the live context. */\n output: z.record(z.unknown()).optional(),\n /** Per-(runId, nodeId, eventId) key used to drop duplicate webhook\n * deliveries when a run is resumed from a checkpoint. */\n idempotencyKey: z.string(),\n note: z.string().optional(),\n /** When the step began (#858). `at` is when it was recorded, so the\n * step took `durationMs` from here. Absent on entries written before. */\n startedAt: z.string().optional(),\n /** How long the step ran, in milliseconds (#858). */\n durationMs: z.number().int().min(0).optional(),\n})\nexport type NodeExecutionEntry = z.infer<typeof nodeExecutionEntrySchema>\n\nexport const entityRefSchema = z.object({\n backend: z.string(),\n /** Stable identifier for the entity this run is scoped to. Used to key\n * the entity index so webhook re-entry can find the active run. */\n id: z.string(),\n})\nexport type EntityRef = z.infer<typeof entityRefSchema>\n\n// Discriminated `pausedAt` — each kind has its own resume path in the\n// dispatcher. The original checkpoint shape is the `checkpoint` variant;\n// new BPM nodes add subProcess / signalWait / timerWait.\nexport const pausedAtSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"checkpoint\"),\n nodeId: z.string(),\n checkpointName: z.string(),\n /** Filter applied to incoming events for resume. Same shape as the\n * matching trigger.channel filter. */\n resumeMatch: z.record(z.unknown()).default({}),\n }),\n z.object({\n kind: z.literal(\"subProcess\"),\n nodeId: z.string(),\n childRunId: z.string(),\n childWorkflowId: z.string(),\n }),\n z.object({\n kind: z.literal(\"signalWait\"),\n nodeId: z.string(),\n signalName: z.string(),\n /** Optional field-match filter against the emitted signal payload. */\n match: z.record(z.unknown()).default({}),\n scope: z.enum([\"workflow\", \"global\"]).default(\"workflow\"),\n }),\n z.object({\n kind: z.literal(\"timerWait\"),\n nodeId: z.string(),\n /** ISO-8601 instant when the timer should fire. */\n fireAt: z.string(),\n }),\n // Follow-up (#788): waiting on the owner's answer to a decision card.\n // `ask` is an owner.ask step, `send` a person.message waiting for its\n // approval (the message is kept here), `start` the whole run waiting\n // for its approval before the first step.\n z.object({\n kind: z.literal(\"ownerDecision\"),\n nodeId: z.string(),\n cardId: z.string(),\n purpose: z.enum([\"ask\", \"send\", \"start\"]),\n message: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n text: z.string(),\n to: z.string().optional(),\n }).optional(),\n }),\n // Waiting for a person to write back on a channel, with a deadline and\n // reminders.\n z.object({\n kind: z.literal(\"replyWait\"),\n nodeId: z.string(),\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n /** In a group: only this sender's message counts. */\n from: z.string().optional(),\n deadline: z.string(),\n nextRemindAt: z.string().optional(),\n remindEveryMs: z.number().optional(),\n reminders: z.number().int().default(0),\n maxReminders: z.number().int().default(0),\n /** A person.message step whose sent text the reminder repeats. Unset:\n * the reminder goes to the owner instead. */\n reminds: z.string().optional(),\n }),\n // An agent step whose turn ended without saying it is done: the agent\n // is nudged when it stalls, and the step is blocked after the last nudge.\n z.object({\n kind: z.literal(\"agentStep\"),\n nodeId: z.string(),\n agentId: z.string(),\n nudges: z.number().int().default(0),\n maxNudges: z.number().int().default(0),\n stallMs: z.number(),\n nextNudgeAt: z.string().optional(),\n /** Set once the step is blocked: no more nudges, the owner is told. */\n blocked: z.string().optional(),\n }),\n])\nexport type PausedAt = z.infer<typeof pausedAtSchema>\n\n/** A plan an agent writes for a task (#858). Steps done keep their place;\n * a revision replaces only the steps not done yet, and is recorded. */\nexport const planStepSchema = z.object({\n id: z.string(),\n title: z.string(),\n})\nexport const planRevisionSchema = z.object({\n at: z.string(),\n /** `plan`: the first plan. `revise`: a change to the steps left. */\n kind: z.enum([\"plan\", \"revise\"]),\n reason: z.string().optional(),\n /** The step ids still to do after this change. */\n steps: z.array(z.string()),\n /** The step ids the change dropped. */\n dropped: z.array(z.string()).default([]),\n})\nexport const planSchema = z.object({\n steps: z.array(planStepSchema),\n /** The step the agent said it started, and when. */\n current: z.object({ id: z.string(), startedAt: z.string() }).optional(),\n revisions: z.array(planRevisionSchema).default([]),\n})\nexport type PlanStep = z.infer<typeof planStepSchema>\nexport type Plan = z.infer<typeof planSchema>\n\n/** What a follow-up run is about and who started it (#788). Runs that\n * triggers start have none. */\nexport const runMetaSchema = z.object({\n /** One line: what this run is doing, in the owner's words. */\n title: z.string().optional(),\n /** What the run concerns, as \"kind:name\" (client:acme, employee:sam,\n * project:site, idea:newsletter). The progress view groups by these. */\n tags: z.array(z.string()).default([]),\n /** The agent that started it. */\n startedBy: z.string().optional(),\n /** The open request (src/requests) this run serves. */\n requestId: z.string().optional(),\n /** Followed: nudges, reminders, a blocked notice and one summary at\n * the end. */\n followUp: z.boolean().default(false),\n /** The owner approved this run's messages when it started. */\n approvedAtStart: z.boolean().default(false),\n /** The messages that approval covers, by step id: recipient and text as\n * the owner saw them (messageKey). Any other message is asked again. */\n approved: z.record(z.string()).optional(),\n /** Why the run cannot go on without the owner, and when they were told. */\n blocked: z.object({\n nodeId: z.string(),\n reason: z.string(),\n at: z.string(),\n }).optional(),\n /** The owner got the end summary. Sent once. */\n endNotifiedAt: z.string().optional(),\n /** A task run inside a workflow because `workflows.required` is on\n * (#858): the agent turn it wraps. `mode` is `linear` (the one-step\n * template) until the agent writes a plan. */\n wrap: z.object({\n agentId: z.string(),\n channel: z.string(),\n mode: z.enum([\"linear\", \"plan\"]),\n taskId: z.string().optional(),\n }).optional(),\n /** The plan the agent wrote for a wrapped task, and each change to it. */\n plan: planSchema.optional(),\n})\nexport type RunMeta = z.infer<typeof runMetaSchema>\n\nexport const workflowRunSchema = z.object({\n id: z.string(),\n workflowId: z.string(),\n workflowVersion: z.literal(2),\n homeNode: z.string(),\n status: runStatusSchema,\n pausedAt: pausedAtSchema.optional(),\n /** Output bundles keyed by producing node id. Downstream nodes read via\n * `{{<nodeId>.<path>}}` in templates. */\n context: z.record(z.record(z.unknown())).default({}),\n /** Node ids queued for execution next. The walk driver pops from this\n * until it's empty or a checkpoint pauses the run. */\n pending: z.array(z.string()).default([]),\n /** Per-`gateway.parallel(mode=join)` arrival tracker. Keys are join node\n * ids; values are the upstream node ids that have already delivered.\n * The join fires once when arrived.length === #incoming edges. Entries\n * are deleted from the map on fire so repeated passes (rare, but\n * possible via crash + retry) stay idempotent. */\n joinCounters: z.record(z.array(z.string())).default({}),\n entityRef: entityRefSchema,\n history: z.array(nodeExecutionEntrySchema).default([]),\n /** Parent run id if this run is a sub-process child. Root runs have no\n * parent. */\n parentRunId: z.string().nullable().default(null),\n /** The parent node in the parent run that spawned this child. Null on\n * root runs. */\n parentNodeId: z.string().nullable().default(null),\n /** Root ancestor. Self-referential on root runs. Lets queries fetch the\n * entire composition tree in one shot. */\n rootRunId: z.string().nullable().default(null),\n /** Depth from root. 0 on root runs, N on Nth-level descendants. */\n depth: z.number().int().min(0).default(0),\n /** Event-bus root the run was started under (src/events/envelope.ts).\n * Resumes after a pause or restart publish under it again. */\n eventRootId: z.string().optional(),\n /** Follow-up runs only (#788). */\n meta: runMetaSchema.optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\nexport type WorkflowRun = z.infer<typeof workflowRunSchema>\n","import yaml from \"js-yaml\"\n\n// --- Move C — YAML workflow authoring ---\n//\n// Workflows are still defined by `workflowSchema` in types.ts; this\n// module only provides:\n// 1) parseYamlWorkflow(text) — js-yaml + a guard against multi-doc\n// streams (we only accept a single workflow per file in v1).\n// 2) desugarFlow(raw) — expand the optional top-level `flow: [a,b,c]`\n// sugar into explicit edges, then strip the `flow` key so Zod\n// validation sees the canonical shape.\n//\n// Why a `flow:` shorthand? Linear sequences are the common case for\n// authors and they're verbose to express via parallel `nodes:` +\n// `edges:` lists. `flow:` lets you read a workflow top-to-bottom.\n// Branches, parallel gateways, signals, and loops still require\n// explicit edges — those have semantics that no array can imply.\n//\n// Sugar limits (enforced at parse time so authors get a clear error\n// rather than a strange runtime behaviour):\n// - flow: cannot include node ids whose type is one of:\n// branch, gateway.parallel, rule, signal.wait,\n// subProcess, timer.boundary, checkpoint.\n// - flow: ids must reference real nodes in the same file.\n// - flow: and edges: may coexist; the result is union + dedup.\n// - Multi-document YAML (`---`) is rejected — one workflow per file.\n\n/** Node types that have multi-port semantics or human/timed\n * suspension and therefore must use explicit `edges:` rather than\n * `flow:`. Mirroring this list in the doc keeps authors honest. */\nconst FLOW_FORBIDDEN_TYPES = new Set([\n \"branch\",\n \"gateway.parallel\",\n \"rule\",\n \"signal.wait\",\n \"subProcess\",\n \"timer.boundary\",\n \"checkpoint\",\n])\n\nexport interface ParseYamlOptions {\n /** Optional file path used only to enrich error messages. */\n filePath?: string\n}\n\n/** Thrown when the YAML structure is invalid before Zod ever sees it.\n * Distinct class so the CLI can render parser errors with line/col\n * hints without conflating them with schema errors. */\nexport class WorkflowYamlError extends Error {\n constructor(message: string, public readonly mark?: yaml.Mark) {\n super(message)\n this.name = \"WorkflowYamlError\"\n }\n}\n\n/** Parse and desugar — returns the JSON-shaped workflow object ready\n * to feed `workflowSchema.safeParse()`. Does NOT validate the schema;\n * callers (CLI, store) run Zod and surface its issues separately. */\nexport function parseYamlWorkflow(text: string, opts: ParseYamlOptions = {}): unknown {\n if (!text || text.trim() === \"\") {\n throw new WorkflowYamlError(\n `${prefix(opts)}empty file — workflow YAML must contain at least an id, version, title, and one node.`,\n )\n }\n\n // js-yaml's loadAll detects multi-doc streams. We support exactly\n // one document per file in v1 — multi-doc encourages copy-paste-edit\n // patterns that hide later docs from the validator.\n let docs: unknown[]\n try {\n docs = yaml.loadAll(text)\n } catch (e: any) {\n const mark = e?.mark as yaml.Mark | undefined\n throw new WorkflowYamlError(\n `${prefix(opts)}YAML parse error: ${e?.reason ?? e?.message ?? e}` +\n (mark ? ` (line ${mark.line + 1}, column ${mark.column + 1})` : \"\"),\n mark,\n )\n }\n if (docs.length === 0 || (docs.length === 1 && docs[0] == null)) {\n throw new WorkflowYamlError(`${prefix(opts)}YAML produced no document.`)\n }\n if (docs.length > 1) {\n throw new WorkflowYamlError(\n `${prefix(opts)}multi-document YAML is not supported in v1; one workflow per file.`,\n )\n }\n const raw = docs[0]\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n const got = Array.isArray(raw) ? \"array\" : typeof raw\n throw new WorkflowYamlError(`${prefix(opts)}top-level YAML must be a mapping (got ${got}).`)\n }\n\n return desugarFlow(raw as Record<string, unknown>, opts)\n}\n\n/** Expand `flow: [a,b,c]` into edges and strip the `flow` key. Also\n * validates the rules listed at the top of this file. Pure function\n * on the parsed-but-not-yet-validated object. */\nexport function desugarFlow(\n raw: Record<string, unknown>,\n opts: ParseYamlOptions = {},\n): unknown {\n if (!(\"flow\" in raw)) return raw\n\n const flow = raw.flow\n if (flow === undefined || flow === null) {\n // Treat null/undefined as if `flow:` weren't present — drop the key\n // and pass through.\n const out: Record<string, unknown> = { ...raw }\n delete out.flow\n return out\n }\n if (!Array.isArray(flow)) {\n throw new WorkflowYamlError(`${prefix(opts)}flow must be an array of node ids (got ${typeof flow}).`)\n }\n for (const item of flow) {\n if (typeof item !== \"string\") {\n throw new WorkflowYamlError(`${prefix(opts)}flow entries must be strings (got ${typeof item}).`)\n }\n }\n const flowIds = flow as string[]\n\n // Cross-check against the nodes[] in the same file. We only need id +\n // type for the flow-rule check; any malformed node entry is left for\n // Zod to surface with a precise path.\n const nodes = Array.isArray(raw.nodes) ? (raw.nodes as Array<Record<string, unknown>>) : []\n const idToType = new Map<string, string>()\n for (const n of nodes) {\n if (n && typeof n === \"object\" && typeof n.id === \"string\" && typeof n.type === \"string\") {\n idToType.set(n.id, n.type)\n }\n }\n\n for (const id of flowIds) {\n if (!idToType.has(id)) {\n throw new WorkflowYamlError(`${prefix(opts)}flow references unknown node \"${id}\".`)\n }\n const type = idToType.get(id)!\n if (FLOW_FORBIDDEN_TYPES.has(type)) {\n throw new WorkflowYamlError(\n `${prefix(opts)}flow cannot include ${type} node \"${id}\"; use explicit edges for ${type} nodes.`,\n )\n }\n }\n\n // Synthesize linear edges. Single-id flows produce nothing — their\n // \"edges\" are degenerate. Use Map keyed by from→fromPort→to so we can\n // dedup against any pre-existing entries in raw.edges.\n const synthesized: Array<{ from: string; to: string }> = []\n for (let i = 0; i < flowIds.length - 1; i++) {\n synthesized.push({ from: flowIds[i], to: flowIds[i + 1] })\n }\n\n // Union with explicit edges, deduping on (from, fromPort, to). Explicit\n // edges win when a synthesized edge would shadow them — the explicit\n // form may carry a label or a port the sugar doesn't know about.\n const explicit: Array<Record<string, unknown>> = Array.isArray(raw.edges)\n ? (raw.edges as Array<Record<string, unknown>>)\n : []\n const seen = new Set<string>()\n const unioned: Array<Record<string, unknown>> = []\n\n for (const e of explicit) {\n if (!e || typeof e !== \"object\") continue\n const k = edgeKey(e.from, e.fromPort, e.to)\n if (k && !seen.has(k)) {\n seen.add(k)\n unioned.push(e)\n } else if (!k) {\n // Pass through ill-formed edges so Zod can surface them with a\n // precise path. Don't drop silently.\n unioned.push(e)\n }\n }\n for (const e of synthesized) {\n const k = edgeKey(e.from, undefined, e.to)\n if (k && !seen.has(k)) {\n seen.add(k)\n unioned.push(e)\n }\n }\n\n const out: Record<string, unknown> = { ...raw, edges: unioned }\n delete out.flow\n return out\n}\n\nfunction edgeKey(from: unknown, fromPort: unknown, to: unknown): string | null {\n if (typeof from !== \"string\" || typeof to !== \"string\") return null\n return `${from}::${typeof fromPort === \"string\" ? fromPort : \"\"}::${to}`\n}\n\nfunction prefix(opts: ParseYamlOptions): string {\n return opts.filePath ? `${opts.filePath}: ` : \"\"\n}\n\n/** Render a JSON workflow back to YAML. One-way (not lossless against\n * `flow:` sugar — round-tripped output is always the canonical\n * nodes/edges form). Used by `agentx workflow show --format yaml`. */\nexport function renderWorkflowYaml(workflow: unknown): string {\n return yaml.dump(workflow, { noRefs: true, lineWidth: 100, sortKeys: false })\n}\n","import { Document, isMap, isSeq, isScalar, parseDocument, YAMLMap, YAMLSeq, type Node, type Pair } from \"yaml\"\nimport type { Workflow } from \"./types\"\n\n// --- YAML round-trip save (Move D) ---\n//\n// Preserves comments and key-ordering when the editor writes back to a\n// YAML-authored workflow file. Without this, every editor save would\n// either (a) refuse to overwrite the YAML (the old behaviour) or (b)\n// regenerate canonical YAML from JSON, dropping every `# ...` comment\n// the operator wrote in the file.\n//\n// Strategy: parse the existing file as a yaml@2 Document (which retains\n// per-key + per-item commentBefore/commentAfter metadata), then merge\n// the new workflow object into it in-place:\n//\n// * top-level keys: preserve order for keys present in both old and\n// new; append new keys; drop removed keys (e.g. `flow:` becomes\n// `edges:` after a structural edit)\n// * arrays-of-objects with an `id` field (nodes, agents, …): merge by\n// id, keeping the original Pair (and its commentBefore) when ids\n// match. Re-order to match the new array. Append entries for new\n// ids; drop entries whose id was removed.\n// * arrays-of-objects without `id` (edges): replace wholesale —\n// edges don't carry author comments in practice.\n// * scalars + other shapes: replace.\n//\n// Comments tied to deleted keys / removed nodes are lost (there's no\n// honest place to relocate them). The document-level header comment\n// (`doc.commentBefore`) is always kept.\n\nexport function renderWorkflowYamlPreservingComments(originalText: string, workflow: Workflow): string {\n const doc = parseDocument(originalText)\n if (!doc.contents || !isMap(doc.contents)) {\n // Source file isn't a mapping — fall back to a clean dump of the new\n // workflow. The header comment (if any) carries forward via\n // commentBefore on the new contents.\n const fresh = new Document(workflow as unknown)\n if (doc.commentBefore) fresh.commentBefore = doc.commentBefore\n return String(fresh)\n }\n mergeMapInPlace(doc, doc.contents, workflow as unknown as Record<string, unknown>)\n return String(doc)\n}\n\nfunction mergeMapInPlace(doc: Document, map: YAMLMap, next: Record<string, unknown>): void {\n const newKeys = Object.keys(next)\n const newKeySet = new Set(newKeys)\n const oldPairs = new Map<string, Pair>()\n for (const pair of map.items) {\n const k = scalarKey(pair)\n if (k !== null) oldPairs.set(k, pair)\n }\n\n const merged: Pair[] = []\n for (const key of newKeys) {\n const value = next[key]\n const existing = oldPairs.get(key)\n if (existing) {\n mergeValueInPlace(doc, existing, value)\n merged.push(existing)\n } else {\n merged.push(doc.createPair(key, value))\n }\n }\n // Keep dropped pairs out of the mapping. Their commentBefore is lost\n // by design — orphaned comments would render in the wrong place.\n void newKeySet\n map.items = merged\n}\n\nfunction mergeValueInPlace(doc: Document, pair: Pair, nextValue: unknown): void {\n const oldVal = pair.value as Node | null\n\n // null/undefined → blank scalar\n if (nextValue === null || nextValue === undefined) {\n pair.value = doc.createNode(null) as Node\n return\n }\n\n // primitive → scalar replacement (preserves the key's commentBefore\n // because we mutate `pair.value`, not the pair itself).\n if (typeof nextValue !== \"object\") {\n pair.value = doc.createNode(nextValue) as Node\n return\n }\n\n // array\n if (Array.isArray(nextValue)) {\n if (isSeq(oldVal)) {\n mergeSeqInPlace(doc, oldVal as YAMLSeq, nextValue)\n } else {\n pair.value = doc.createNode(nextValue) as Node\n }\n return\n }\n\n // object/mapping\n if (isMap(oldVal)) {\n mergeMapInPlace(doc, oldVal as YAMLMap, nextValue as Record<string, unknown>)\n } else {\n pair.value = doc.createNode(nextValue) as Node\n }\n}\n\nfunction mergeSeqInPlace(doc: Document, seq: YAMLSeq, nextItems: unknown[]): void {\n // If every item is an object with a stable string `id`, merge by id so\n // per-item commentBefore (e.g. the `# Capture start time …` block above\n // a workflow node) sticks with that node even if it moves.\n const allHaveId =\n nextItems.length > 0 &&\n nextItems.every((it) => it && typeof it === \"object\" && typeof (it as { id?: unknown }).id === \"string\")\n\n if (!allHaveId) {\n // Replace wholesale. Edge lists, envAllow strings, etc.\n const replacement = doc.createNode(nextItems) as YAMLSeq\n seq.items = replacement.items\n return\n }\n\n const oldById = new Map<string, Node>()\n for (const item of seq.items) {\n if (isMap(item)) {\n const idNode = item.get(\"id\", true)\n if (isScalar(idNode) && typeof idNode.value === \"string\") {\n oldById.set(idNode.value, item)\n }\n }\n }\n\n const merged: Node[] = []\n for (const next of nextItems) {\n const id = (next as { id: string }).id\n const existing = oldById.get(id)\n if (existing && isMap(existing)) {\n mergeMapInPlace(doc, existing as YAMLMap, next as Record<string, unknown>)\n merged.push(existing)\n } else {\n merged.push(doc.createNode(next) as Node)\n }\n }\n seq.items = merged\n}\n\nfunction scalarKey(pair: Pair): string | null {\n const k = pair.key\n if (isScalar(k) && typeof k.value === \"string\") return k.value\n if (typeof k === \"string\") return k\n return null\n}\n","import { createHash, randomUUID } from \"crypto\"\nimport { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { currentRoot } from \"@/events/envelope\"\nimport {\n type EntityRef,\n type NodeExecutionEntry,\n type PausedAt,\n type RunMeta,\n type RunStatus,\n type WorkflowRun,\n} from \"./types\"\n\n// --- RunStore (V2) ---\n//\n// Home-node-only persistence for dataflow runs. The node that processes the\n// triggering event becomes the home node and owns:\n// _runs/<runId>.jsonl — append-only run events\n// _index/<backend>__<entityId>.json — entity -> active runId lookup\n//\n// Event lines come in two shapes:\n// { v: 2, kind: \"snapshot\", run } — full run state at a moment\n// { v: 2, kind: \"exec\", entry, context? } — one node execution\n//\n// Append a snapshot on create + on status changes (completed/failed/paused);\n// append an `exec` on every node completion. The authoritative run state is\n// reconstructed by walking the lines in order: start from the last snapshot,\n// fold subsequent `exec` entries into `history` + `context` + `pending`.\n//\n// This replaces the V1 transition-centric record. The entity-index +\n// home-node ownership semantics are unchanged.\n\nexport interface RunStoreOptions {\n baseDir?: string\n /** This daemon's mesh peer id. Stamped as `homeNode` on run creation so\n * later code can verify ownership before mutating. */\n nodeId: string\n}\n\ntype RunEventLine =\n | { v: 2; kind: \"snapshot\"; run: WorkflowRun }\n | {\n v: 2; kind: \"exec\"; runId: string; entry: NodeExecutionEntry;\n pending: string[];\n status?: RunStatus;\n pausedAt?: PausedAt | null;\n context?: Record<string, Record<string, unknown>>;\n joinCounters?: Record<string, string[]>;\n }\n\nfunction sanitize(s: string): string {\n // Keep the set narrow so the result is always a safe single filename\n // component — `/` is replaced because otherwise it creates unintended\n // subdirectories under _index/ on write.\n return s.replace(/[^a-zA-Z0-9._:#@-]/g, \"_\").slice(0, 200)\n}\n\n/** Stable idempotency key for a node execution given its triggering event.\n * Same event delivered twice -> same key -> engine drops the duplicate. */\nexport function idempotencyKey(runId: string, nodeId: string, eventId: string): string {\n return createHash(\"sha1\").update(`${runId}|${nodeId}|${eventId}`).digest(\"hex\").slice(0, 16)\n}\n\nexport class RunStore {\n readonly baseDir: string\n readonly runsDir: string\n readonly indexDir: string\n readonly nodeId: string\n\n constructor(opts: RunStoreOptions) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n this.runsDir = resolve(this.baseDir, \"_runs\")\n this.indexDir = resolve(this.baseDir, \"_index\")\n this.nodeId = opts.nodeId\n mkdirSync(this.runsDir, { recursive: true })\n mkdirSync(this.indexDir, { recursive: true })\n }\n\n private runPath(runId: string): string {\n return resolve(this.runsDir, `${runId}.jsonl`)\n }\n\n private indexPath(entity: EntityRef): string {\n return resolve(this.indexDir, `${entity.backend}__${sanitize(entity.id)}.json`)\n }\n\n /** Create a new run. Seeds the initial pending queue (typically the\n * trigger node's immediate successors) and writes an entity-index entry\n * so future events for the same entity land on this home node. */\n create(args: {\n workflowId: string\n initialPending: string[]\n entityRef: EntityRef\n initialContext?: Record<string, Record<string, unknown>>\n /** Optional parent-run linkage for sub-process children. */\n parentRunId?: string | null\n parentNodeId?: string | null\n rootRunId?: string | null\n depth?: number\n /** Defaults to the current event root, if any. */\n eventRootId?: string\n /** Follow-up runs (#788): title, tags, who started it. */\n meta?: RunMeta\n /** A run id chosen before the run exists (#858: the agent is told it\n * before its turn starts). Default: a fresh UUID. */\n id?: string\n }): WorkflowRun {\n const now = new Date().toISOString()\n const id = args.id ?? randomUUID()\n const run: WorkflowRun = {\n id,\n workflowId: args.workflowId,\n workflowVersion: 2,\n homeNode: this.nodeId,\n status: \"running\",\n pausedAt: undefined,\n context: args.initialContext ?? {},\n pending: args.initialPending,\n entityRef: args.entityRef,\n history: [],\n parentRunId: args.parentRunId ?? null,\n parentNodeId: args.parentNodeId ?? null,\n rootRunId: args.rootRunId ?? id,\n depth: args.depth ?? 0,\n eventRootId: args.eventRootId ?? currentRoot()?.rootId,\n joinCounters: {},\n ...(args.meta ? { meta: args.meta } : {}),\n createdAt: now,\n updatedAt: now,\n }\n this.appendSnapshot(run)\n this.writeIndex(args.entityRef, run.id)\n return run\n }\n\n /** Read the latest snapshot of a run by replaying the jsonl log. */\n get(runId: string): WorkflowRun | null {\n const p = this.runPath(runId)\n if (!existsSync(p)) return null\n const raw = readFileSync(p, \"utf-8\").trim()\n if (!raw) return null\n let run: WorkflowRun | null = null\n for (const line of raw.split(\"\\n\")) {\n let evt: RunEventLine\n try { evt = JSON.parse(line) as RunEventLine } catch { continue }\n if (evt.v !== 2) continue\n if (evt.kind === \"snapshot\") run = evt.run\n else if (evt.kind === \"exec\" && run && run.id === evt.runId) {\n const current: WorkflowRun = run\n run = {\n ...current,\n history: [...current.history, evt.entry],\n pending: evt.pending,\n status: evt.status ?? current.status,\n pausedAt: evt.pausedAt === null ? undefined : (evt.pausedAt ?? current.pausedAt),\n context: evt.context ?? current.context,\n joinCounters: evt.joinCounters ?? current.joinCounters,\n updatedAt: evt.entry.at,\n }\n }\n }\n return run\n }\n\n /** Look up the active run id for an entity, if any. */\n getActiveByEntity(entity: EntityRef): string | null {\n const p = this.indexPath(entity)\n if (!existsSync(p)) return null\n try {\n const parsed = JSON.parse(readFileSync(p, \"utf-8\")) as { runId: string; homeNode: string; updatedAt: string }\n return parsed.runId\n } catch {\n return null\n }\n }\n\n /** Which node owns a given entity (null if unclaimed). Used by the\n * dispatcher to decide whether to process locally or forward via mesh. */\n getHomeNodeByEntity(entity: EntityRef): string | null {\n const p = this.indexPath(entity)\n if (!existsSync(p)) return null\n try {\n const parsed = JSON.parse(readFileSync(p, \"utf-8\")) as { runId: string; homeNode: string; updatedAt: string }\n return parsed.homeNode\n } catch {\n return null\n }\n }\n\n /** Record a single node's execution. Appends an `exec` line and returns\n * the updated run snapshot. Idempotent: if an entry with the same\n * idempotencyKey already exists, the call is a no-op. */\n recordExecution(args: {\n runId: string\n entry: NodeExecutionEntry\n nextPending: string[]\n status?: RunStatus\n pausedAt?: PausedAt | null // null = clear, undefined = unchanged\n /** Optional updated context snapshot. When provided, replaces the run's\n * context wholesale (used when a node's output bundle lands). When\n * omitted, context is carried forward unchanged. */\n context?: Record<string, Record<string, unknown>>\n /** Optional updated join-counter map. Undefined = carry forward. */\n joinCounters?: Record<string, string[]>\n }): WorkflowRun | null {\n const run = this.get(args.runId)\n if (!run) return null\n if (run.history.some((h) => h.idempotencyKey === args.entry.idempotencyKey)) return run\n\n const line: RunEventLine = {\n v: 2,\n kind: \"exec\",\n runId: args.runId,\n entry: args.entry,\n pending: args.nextPending,\n status: args.status,\n pausedAt: args.pausedAt === null ? null : args.pausedAt,\n context: args.context,\n joinCounters: args.joinCounters,\n }\n appendFileSync(this.runPath(args.runId), JSON.stringify(line) + \"\\n\")\n\n // Refresh the index: completed/failed/canceled runs clear it; running +\n // paused runs keep it so webhook re-entry can still find the home node.\n const nextStatus = args.status ?? run.status\n if (nextStatus === \"completed\" || nextStatus === \"failed\" || nextStatus === \"canceled\") {\n this.clearIndex(run.entityRef)\n } else {\n this.writeIndex(run.entityRef, run.id)\n }\n\n return this.get(args.runId)\n }\n\n /** Mutate status without adding a history entry — used for\n * pause / resume / cancel commands from the CLI or dashboard. */\n setStatus(runId: string, status: RunStatus): WorkflowRun | null {\n const run = this.get(runId)\n if (!run) return null\n const updated: WorkflowRun = { ...run, status, updatedAt: new Date().toISOString() }\n this.appendSnapshot(updated)\n if (status !== \"running\" && status !== \"paused\") this.clearIndex(updated.entityRef)\n return updated\n }\n\n /** Merge fields into a run's meta (#788). Appends a snapshot, like\n * setStatus; history and context are carried over as they are. */\n setMeta(runId: string, patch: Partial<RunMeta>): WorkflowRun | null {\n const run = this.get(runId)\n if (!run) return null\n const meta: RunMeta = { tags: [], followUp: false, approvedAtStart: false, ...run.meta, ...patch }\n const updated: WorkflowRun = { ...run, meta, updatedAt: new Date().toISOString() }\n this.appendSnapshot(updated)\n return updated\n }\n\n /** Merge into a run's meta and replace its pending queue (#858: a plan\n * written or changed mid-run). Appends a snapshot; history is kept. */\n patch(runId: string, patch: { meta?: Partial<RunMeta>; pending?: string[] }): WorkflowRun | null {\n const run = this.get(runId)\n if (!run) return null\n const updated: WorkflowRun = {\n ...run,\n ...(patch.meta ? { meta: { tags: [], followUp: false, approvedAtStart: false, ...run.meta, ...patch.meta } } : {}),\n ...(patch.pending ? { pending: patch.pending } : {}),\n updatedAt: new Date().toISOString(),\n }\n this.appendSnapshot(updated)\n return updated\n }\n\n /** Remove a run and its entity index entry, as if it never ran (#858: a\n * plain question exempted from workflows.required). */\n discard(runId: string): boolean {\n const run = this.get(runId)\n if (!run) return false\n this.clearIndex(run.entityRef)\n try { unlinkSync(this.runPath(runId)); return true } catch { return false }\n }\n\n /** List runs, newest first. */\n list(opts: { workflowId?: string; limit?: number } = {}): WorkflowRun[] {\n if (!existsSync(this.runsDir)) return []\n const files = readdirSync(this.runsDir)\n .filter((f) => f.endsWith(\".jsonl\"))\n .map((f) => ({ name: f, mtime: statSync(resolve(this.runsDir, f)).mtimeMs }))\n .sort((a, b) => b.mtime - a.mtime)\n const out: WorkflowRun[] = []\n for (const f of files) {\n const run = this.get(f.name.replace(/\\.jsonl$/, \"\"))\n if (!run) continue\n if (opts.workflowId && run.workflowId !== opts.workflowId) continue\n out.push(run)\n if (opts.limit && out.length >= opts.limit) break\n }\n return out\n }\n\n /** Retention: keep at most `maxRuns` completed/failed/canceled runs\n * younger than `maxDays`. Running/paused runs are never pruned. */\n prune(policy: { maxRuns: number; maxDays: number }): number {\n if (!existsSync(this.runsDir)) return 0\n const cutoff = Date.now() - policy.maxDays * 24 * 60 * 60 * 1000\n const candidates: Array<{ file: string; run: WorkflowRun; mtime: number }> = []\n for (const name of readdirSync(this.runsDir)) {\n if (!name.endsWith(\".jsonl\")) continue\n const path = resolve(this.runsDir, name)\n const mtime = statSync(path).mtimeMs\n const run = this.get(name.replace(/\\.jsonl$/, \"\"))\n if (!run) continue\n if (run.status === \"running\" || run.status === \"paused\") continue\n candidates.push({ file: path, run, mtime })\n }\n candidates.sort((a, b) => b.mtime - a.mtime)\n let pruned = 0\n for (let i = 0; i < candidates.length; i++) {\n const c = candidates[i]\n const tooOld = c.mtime < cutoff\n const tooMany = i >= policy.maxRuns\n if (tooOld || tooMany) {\n try { unlinkSync(c.file); pruned++ } catch { /* ignore */ }\n }\n }\n return pruned\n }\n\n private appendSnapshot(run: WorkflowRun): void {\n const line: RunEventLine = { v: 2, kind: \"snapshot\", run }\n appendFileSync(this.runPath(run.id), JSON.stringify(line) + \"\\n\")\n }\n\n private writeIndex(entity: EntityRef, runId: string): void {\n writeFileSync(this.indexPath(entity), JSON.stringify({\n runId,\n homeNode: this.nodeId,\n updatedAt: new Date().toISOString(),\n }, null, 2) + \"\\n\")\n }\n\n private clearIndex(entity: EntityRef): void {\n const p = this.indexPath(entity)\n if (existsSync(p)) {\n try { unlinkSync(p) } catch { /* ignore */ }\n }\n }\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { z } from \"zod\"\n\n// --- LayoutStore ---\n//\n// Persists node (state) positions for the visual editor alongside workflow\n// definitions, but in a sibling file so the Workflow schema stays pure\n// behavior. A missing layout just means \"no positions yet\" — the editor\n// falls back to auto-layout on load.\n//\n// Layout here is only x/y for state nodes. Edges derive their path from the\n// endpoint nodes at render time, so they don't need persisted coordinates.\n//\n// Shape on disk:\n// .agentx/workflows/_layouts/<id>.json\n// { \"version\": 1, \"nodes\": { \"<stateName>\": { \"x\": 120, \"y\": 80 } } }\n\nconst layoutSchema = z.object({\n version: z.literal(1),\n nodes: z.record(z.object({\n x: z.number(),\n y: z.number(),\n })),\n})\nexport type WorkflowLayout = z.infer<typeof layoutSchema>\n\nexport interface LayoutStoreOptions {\n baseDir?: string\n}\n\nexport class LayoutStore {\n readonly baseDir: string\n readonly layoutsDir: string\n\n constructor(opts: LayoutStoreOptions = {}) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n this.layoutsDir = resolve(this.baseDir, \"_layouts\")\n mkdirSync(this.layoutsDir, { recursive: true })\n }\n\n private pathFor(workflowId: string): string {\n // The workflow id grammar already excludes path-hostile chars, but guard\n // anyway in case a caller passes something unvalidated.\n const safe = workflowId.replace(/[^a-zA-Z0-9._-]/g, \"_\")\n return resolve(this.layoutsDir, `${safe}.json`)\n }\n\n get(workflowId: string): WorkflowLayout | null {\n const p = this.pathFor(workflowId)\n if (!existsSync(p)) return null\n try {\n const parsed = layoutSchema.safeParse(JSON.parse(readFileSync(p, \"utf-8\")))\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n /** Save node positions. Callers should pass only coordinates for states\n * that currently exist in the workflow — stale entries are kept until\n * the caller explicitly prunes them via `sync()`. */\n save(workflowId: string, layout: WorkflowLayout): WorkflowLayout {\n const parsed = layoutSchema.parse(layout)\n writeFileSync(this.pathFor(workflowId), JSON.stringify(parsed, null, 2) + \"\\n\")\n return parsed\n }\n\n /** Drop nodes from the layout whose ids aren't in `validIds`. Prevents\n * stale positions from lingering after a state is removed in the editor. */\n sync(workflowId: string, validIds: string[]): WorkflowLayout | null {\n const existing = this.get(workflowId)\n if (!existing) return null\n const keep: WorkflowLayout[\"nodes\"] = {}\n for (const id of validIds) if (existing.nodes[id]) keep[id] = existing.nodes[id]\n const next: WorkflowLayout = { version: 1, nodes: keep }\n return this.save(workflowId, next)\n }\n\n delete(workflowId: string): boolean {\n const p = this.pathFor(workflowId)\n if (!existsSync(p)) return false\n unlinkSync(p)\n return true\n }\n}\n","import type { Workflow } from \"./types\"\n\nexport interface WorkflowMatchInput {\n agentId: string\n channel?: string\n message: string\n intentPath?: string[]\n}\n\nexport interface WorkflowMatch {\n workflow: Workflow\n confidence: number\n reasons: string[]\n}\n\nfunction words(text: string): Set<string> {\n return new Set(\n text.toLowerCase()\n .replace(/[^a-z0-9\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 3),\n )\n}\n\nfunction overlap(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 || b.size === 0) return 0\n let n = 0\n for (const w of a) if (b.has(w)) n++\n return n / Math.max(a.size, b.size)\n}\n\nexport function matchWorkflow(input: WorkflowMatchInput, workflows: Workflow[]): WorkflowMatch | null {\n const messageWords = words(input.message)\n const active = workflows.filter((wf) => wf.state === \"active\" && (wf.status ?? \"active\") === \"active\")\n let best: WorkflowMatch | null = null\n\n for (const wf of active) {\n let score = 0\n const reasons: string[] = []\n if (wf.ownerAgent && wf.ownerAgent === input.agentId) { score += 0.25; reasons.push(\"owner-agent\") }\n if (input.channel && wf.tags.includes(input.channel)) { score += 0.15; reasons.push(\"channel-tag\") }\n if (input.intentPath?.length && wf.intentPath.length) {\n const same = input.intentPath.filter((p, i) => wf.intentPath[i] === p).length\n if (same > 0) { score += Math.min(0.3, same * 0.1); reasons.push(\"intent-path\") }\n }\n const text = [wf.id, wf.title, wf.description ?? \"\", ...wf.tags].join(\" \")\n const wordScore = overlap(messageWords, words(text))\n if (wordScore > 0) { score += Math.min(0.35, wordScore); reasons.push(\"text-similarity\") }\n if ((wf.matchCount ?? 0) > 0) { score += Math.min(0.1, wf.matchCount * 0.01); reasons.push(\"prior-matches\") }\n const confidence = Math.max(0, Math.min(1, score))\n if (!best || confidence > best.confidence) best = { workflow: wf, confidence, reasons }\n }\n\n return best && best.confidence > 0 ? best : null\n}\n","import { FOLLOW_UP_PORTS, type Condition, type Workflow, type WorkflowEdge, type WorkflowNode } from \"./types\"\n\n// --- Engine (V2 — dataflow walk driver) ---\n//\n// Pure functions that decide what happens next given a workflow DAG + a\n// completed node's output. No side effects — the dispatcher owns run-store\n// writes, node handler execution, and mesh forwarding.\n//\n// The walk algorithm is simple: after a node completes, find its outgoing\n// edges; for each edge, decide if it fires (always, for linear nodes; based\n// on the selected port, for branch nodes); collect the target nodeIds into\n// `nextPending`. The dispatcher picks them up on the next turn.\n\nexport interface WalkInput {\n workflow: Workflow\n /** The node that just completed. */\n fromNodeId: string\n /** For `branch` nodes, the port that was selected (e.g. \"status\", \"new\",\n * \"fallback\"). For other node types, ignored. */\n selectedPort?: string\n}\n\nexport interface WalkResult {\n /** Node ids the run should execute next. Usually 0 or 1 entries; can be\n * >1 if the workflow deliberately fans out. */\n nextPending: string[]\n}\n\n/** Pure: given the workflow graph and the node that just completed, figure\n * out what should run next. */\nexport function nextNodes(input: WalkInput): WalkResult {\n const { workflow, fromNodeId, selectedPort } = input\n const fromNode = workflow.nodes.find((n) => n.id === fromNodeId)\n if (!fromNode) return { nextPending: [] }\n const outgoing = workflow.edges.filter((e) => e.from === fromNodeId)\n\n // Branch nodes: only the edge whose fromPort matches the selectedPort fires.\n // Linear nodes: all outgoing edges fire (rare in v1; defaults to one).\n // Follow-up steps: an edge without a port leaves on the main port.\n const ports = FOLLOW_UP_PORTS[fromNode.type]\n const firing = fromNode.type === \"branch\"\n ? outgoing.filter((e) => (e.fromPort ?? \"\") === (selectedPort ?? \"\"))\n : ports\n ? outgoing.filter((e) => (e.fromPort ?? ports.main) === (selectedPort ?? ports.main))\n : outgoing\n\n return { nextPending: firing.map((e) => e.to) }\n}\n\n// --- Branch condition evaluation ---\n//\n// A `branch` node's config is:\n// { cases: [ { when: Condition, to: portName } ], default: portName }\n// Given the run's accumulated context, pick the first matching case or the\n// default. Returns the port name the edge should leave from.\n\nexport function evaluateBranch(node: WorkflowNode, context: Record<string, unknown>): string {\n const cases = (node.config.cases as Array<{ when: Condition; to: string }> | undefined) ?? []\n const defaultPort = (node.config.default as string | undefined) ?? \"\"\n for (const c of cases) {\n if (conditionMatches(c.when, context)) return c.to\n }\n return defaultPort\n}\n\n/** One condition against a context. Also the `trigger.poll` item filter. */\nexport function conditionMatches(cond: Condition, context: Record<string, unknown>): boolean {\n const p = (cond.params ?? {}) as Record<string, unknown>\n const path = String(p.path ?? \"\")\n const value = getByPath(context, path)\n switch (cond.kind) {\n case \"equals\":\n return value === p.value\n case \"contains\": {\n if (typeof value === \"string\") return value.includes(String(p.value ?? \"\"))\n if (Array.isArray(value)) return value.some((v) => v === p.value)\n return false\n }\n case \"matches\": {\n if (typeof value !== \"string\") return false\n try { return new RegExp(String(p.regex ?? \"\")).test(value) } catch { return false }\n }\n case \"exists\":\n return value !== undefined && value !== null && value !== \"\"\n default:\n return false\n }\n}\n\n/** Tiny dotted-path lookup that stops at non-objects. */\nexport function getByPath(ctx: unknown, path: string): unknown {\n if (!path) return undefined\n const parts = path.split(\".\")\n let cur: unknown = ctx\n for (const p of parts) {\n if (cur === null || cur === undefined) return undefined\n if (typeof cur !== \"object\") return undefined\n cur = (cur as Record<string, unknown>)[p]\n }\n return cur\n}\n\n/** The initial pending queue for a new run: the trigger node's successors.\n * Called by the dispatcher when seeding a run's state. */\nexport function initialPendingFromTrigger(workflow: Workflow): { triggerId: string; pending: string[] } | null {\n const trigger = workflow.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger) return null\n const successors = workflow.edges.filter((e) => e.from === trigger.id).map((e) => e.to)\n return { triggerId: trigger.id, pending: successors }\n}\n\n/** Return a node by id. Convenience used by the dispatcher + tests. */\nexport function findNode(workflow: Workflow, id: string): WorkflowNode | undefined {\n return workflow.nodes.find((n) => n.id === id)\n}\n\n/** Edges leaving a node — used for debugging and the editor overlay. */\nexport function outgoingEdges(workflow: Workflow, fromId: string): WorkflowEdge[] {\n return workflow.edges.filter((e) => e.from === fromId)\n}\n","import { randomUUID } from \"crypto\"\nimport { evaluateBranch, findNode, initialPendingFromTrigger, nextNodes } from \"./engine\"\nimport { parseResultToken, resolveHandler } from \"./nodes/handlers\"\nimport { deliver, messageKey, nextWakeAt, nudgeText, renderPersonMessage, stepVerdict } from \"./nodes/follow-up\"\nimport { DEFAULT_FOLLOW_UP, type AgentExecuteRequest, type AgentExecuteResponse, type FollowUpDefaults, type NodeResult, type OwnerPort } from \"./nodes/types\"\nimport { RunStore, idempotencyKey } from \"./run-store\"\nimport type { WorkflowStore } from \"./store\"\nimport { TimerService, type TimerRecord } from \"./timers\"\nimport { SignalBus, matchesSignal, type SignalEmission } from \"./signals\"\nimport type { EventBus } from \"../daemon/event-bus\"\nimport { FOLLOW_UP_PORTS, type EntityRef, type NodeExecutionEntry, type PausedAt, type RunMeta, type Workflow, type WorkflowRun } from \"./types\"\nimport { getLedgerMode } from \"@/intent/mode\"\nimport { getDefaultLedger } from \"@/intent/instance\"\nimport { recordWorkflowDispatch } from \"@/intent/sources/workflow\"\nimport { openDb } from \"@/storage/sqlite\"\nimport { recordTraceStart, recordTraceEnd } from \"@/storage/traces\"\nimport { withRoot } from \"@/events/envelope\"\n\n// --- Dispatcher (V2) ---\n//\n// Entry point called by the hook layer. Given a triggering event:\n//\n// 1. Find workflows whose trigger filter matches.\n// 2. For each match, resolve or create a run keyed by the event's entityRef.\n// 3. If the run's home node is a remote peer, forward via mesh.\n// 4. Locally: seed the trigger node's output into run.context, then\n// walk the DAG — execute each pending node's handler, fold the output\n// into run.context, enqueue successors, loop until pending is empty\n// or a handler paused / failed the run.\n//\n// All side effects (adapter calls, agent dispatch) happen inside node\n// handlers. This module only orchestrates.\n\nexport interface MeshForwarder {\n forwardTransition(peer: string, payload: { workflowId: string; event: TriggerEvent; entityRef: EntityRef }): Promise<void>\n /** Broadcast a fresh trigger event to all healthy mesh peers that may host\n * remote-allowed workflows for this trigger's source. Implementations\n * should be best-effort: a failing peer must not block delivery to others\n * or surface back to the local dispatch path. Optional — when absent,\n * workflows are local-only as before. */\n broadcastTrigger?(payload: {\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] }\n entityRef: EntityRef\n event: TriggerEvent\n }): Promise<void>\n /** Forward an outbound channel send to whichever peer hosts the channel.\n * Returns the message id from the remote adapter (or null when not\n * available). Throws when no healthy peer hosts the channel — callers\n * should treat that as a hard error and surface it as the action's\n * failure. Optional — when absent, workflow `action.send` to a non-local\n * channel just errors out, preserving today's behaviour. */\n forwardChannelSend?(payload: {\n channel: string\n chatId: string\n text: string\n accountId?: string\n parseMode?: string\n replyTo?: string\n }): Promise<{ messageId: string | null }>\n}\n\nexport interface TriggerEvent {\n /** Stable event id — used by idempotency keys so retried webhooks\n * collapse into the same node execution. */\n id: string\n /** Output bundle produced by the trigger node. Hooks build this from\n * the raw channel payload. Goes directly into `run.context[triggerId]`. */\n payload: Record<string, unknown>\n}\n\nexport interface DispatcherOptions {\n store: WorkflowStore\n runs: RunStore\n nodeId: string\n forwarder?: MeshForwarder\n channels: Record<string, unknown>\n agents: { execute(req: AgentExecuteRequest): Promise<AgentExecuteResponse> }\n log?: (msg: string) => void\n /** Optional timer service. When provided, `timer.boundary` nodes\n * schedule against it and resume on fire. When omitted, a default is\n * constructed but its loop is NOT started — callers can start it via\n * `dispatcher.timers.start()` once (typically at daemon boot). */\n timers?: TimerService\n /** Optional signal bus. Workflow-scoped default if omitted. */\n signals?: SignalBus\n /** Optional observability bus. When present, the dispatcher emits\n * run-phase events (created / ok / failed / paused / resumed /\n * completed) so operators can watch live via SSE or CLI. No-op when\n * absent — the dispatcher runs exactly as before. */\n events?: EventBus\n /** Follow-up (#788): how the engine tells and asks the owner. */\n owner?: OwnerPort\n /** Follow-up defaults, read at each use so a settings change applies. */\n followUp?: () => FollowUpDefaults\n /** A follow-up run ended (completed, failed, canceled). Called once. */\n onRunEnded?: (run: WorkflowRun, workflow: Workflow | null) => void | Promise<void>\n /** A follow-up step is blocked and needs the owner. Called once per block. */\n onBlocked?: (run: WorkflowRun, workflow: Workflow, nodeId: string, reason: string) => void | Promise<void>\n}\n\n/** An inbound message a `person.wait` step may be waiting for. */\nexport interface InboundReply {\n channel: string\n chatId: string\n accountId?: string\n /** Who wrote it, in a group. */\n senderId?: string\n senderName?: string\n text: string\n media?: unknown\n messageId?: string\n}\n\nconst TERMINAL = new Set([\"completed\", \"failed\", \"canceled\"])\n\n/** Subset of channel adapter API used for the auto-acknowledge lifecycle on\n * channel-triggered runs. Duck-typed so any adapter implementing react +\n * sendTyping (telegram today, future whatsapp/discord) gets the UX for free.\n * The accountId arg is forwarded so multi-account adapters reply on the\n * originating bot, not whichever bot the chat was last seen on. */\ninterface AckCapableAdapter {\n react?: (chatId: string, messageId: string, emoji?: string, accountId?: string) => Promise<void> | void\n sendTyping?: (chatId: string, accountId?: string) => Promise<void> | void\n}\n\nconst ACK_TYPING_INTERVAL_MS = 4000\n\n/** Max parallel node handlers per run. Caps fan-out storms (e.g.,\n * gateway.parallel(fanOut) → 100 branches) from exhausting agent\n * slots. Chosen to be meaningful for the common case (3–8 parallel\n * approvers / implementations) while never going wild. */\nconst MAX_PARALLEL_PER_RUN = 8\n\nexport class WorkflowDispatcher {\n private readonly store: WorkflowStore\n private readonly runs: RunStore\n private readonly nodeId: string\n private readonly forwarder?: MeshForwarder\n private readonly channels: Record<string, unknown>\n private readonly agents: { execute(req: AgentExecuteRequest): Promise<AgentExecuteResponse> }\n private readonly log: (msg: string) => void\n readonly timers: TimerService\n readonly signals: SignalBus\n readonly events?: EventBus\n owner?: OwnerPort\n private readonly followUpDefaults: () => FollowUpDefaults\n private readonly onRunEnded?: DispatcherOptions[\"onRunEnded\"]\n private readonly onBlocked?: DispatcherOptions[\"onBlocked\"]\n /** Runs paused on person.wait, by channel. Read on every inbound\n * message, so it is kept in memory: built from disk once, then kept up\n * to date as runs pause. A stale entry costs one read; the run's own\n * pause is checked before it takes a message. */\n private replyWaiters: Map<string, Set<string>> | null = null\n /** \"done\" an agent reported from inside its own step's turn, before\n * the turn ended (runId:nodeId). Applied when the turn ends. */\n private readonly earlyDone = new Map<string, Record<string, unknown>>()\n /** Per-run typing timer. Started when a channel-triggered run is created or\n * resumed; stopped when the run terminates (completed / failed / canceled\n * / paused). Keyed by runId so concurrent channel runs don't stomp on each\n * other's lifecycles. */\n private readonly ackTypingTimers: Map<string, ReturnType<typeof setInterval>> = new Map()\n /** Per-run commit chain. The parallel walk loop fires node handlers\n * concurrently via Promise.allSettled, but every state-mutating\n * operation (recordExecution, joinCounter update, pause transition,\n * resume) must land atomically — runs are a single log and the\n * read-update-write on joinCounters + dedup check on idempotencyKey\n * need to see a consistent snapshot. Serialising commits through a\n * per-run promise chain gives us that guarantee without a broader\n * global lock; different runs still progress independently. */\n private readonly commitChains: Map<string, Promise<unknown>> = new Map()\n\n constructor(opts: DispatcherOptions) {\n this.store = opts.store\n this.runs = opts.runs\n this.nodeId = opts.nodeId\n this.forwarder = opts.forwarder\n this.channels = opts.channels\n this.agents = opts.agents\n this.log = opts.log ?? (() => {})\n this.timers = opts.timers ?? new TimerService({ log: (m) => this.log(m) })\n this.signals = opts.signals ?? new SignalBus()\n this.events = opts.events\n this.owner = opts.owner\n this.followUpDefaults = opts.followUp ?? (() => DEFAULT_FOLLOW_UP)\n this.onRunEnded = opts.onRunEnded\n this.onBlocked = opts.onBlocked\n\n // Register the timer-fire callback once. TimerService is a per-node\n // singleton; re-registration would clobber prior instances, but the\n // dispatcher also is, so this is safe.\n this.timers.onFire((t) => this.resumeFromTimer(t))\n // Resume any paused signalWait runs whose filter matches a published\n // signal. Running a full scan of paused runs per emission is cheap in\n // v1 (runs are fs-backed and typically numbered in the hundreds).\n this.signals.subscribe((emission) => this.resumeFromSignal(emission))\n }\n\n /** Publish a run-phase event if an EventBus is configured. No-op\n * otherwise. Used by the walk loop + resume paths so operators can\n * Monitor runs live. */\n private emitRunEvent(args: {\n runId: string; workflowId: string; nodeId?: string; phase: string; status?: string; note?: string; homeNode?: string\n /** The run's event root; defaults to the current root context. */\n rootId?: string\n }): void {\n if (!this.events) return\n try {\n this.events.publish({\n kind: \"run\",\n runId: args.runId, workflowId: args.workflowId,\n nodeId: args.nodeId, phase: args.phase,\n status: args.status, note: args.note, homeNode: args.homeNode,\n rootId: args.rootId,\n })\n } catch { /* defensive — a bus failure never breaks the engine */ }\n }\n\n /** Serialise a state-mutating operation against all other commits for\n * the same run. Parallel branches execute their handlers concurrently,\n * but their commits land one after another — keeping joinCounters +\n * pending + pausedAt + recordExecution dedup consistent. */\n private async commit<T>(runId: string, fn: () => Promise<T> | T): Promise<T> {\n const prev = this.commitChains.get(runId) ?? Promise.resolve()\n const next = prev.then(() => fn(), () => fn())\n // Keep the chain alive until the caller's fn settles. We don't care\n // about fn's result type for the chain tail — `unknown` is enough.\n this.commitChains.set(runId, next.then(() => undefined, () => undefined))\n return next as Promise<T>\n }\n\n /** Public helper for the HTTP API to emit a signal programmatically. */\n emitSignal(args: { name: string; scope?: \"workflow\" | \"global\"; workflowId?: string; payload?: Record<string, unknown> }): SignalEmission {\n const emission: SignalEmission = {\n name: args.name,\n scope: args.scope ?? \"global\",\n workflowId: args.workflowId ?? \"\",\n payload: args.payload ?? {},\n emittedAt: new Date().toISOString(),\n }\n this.signals.emit(emission)\n if (this.events) {\n try {\n this.events.publish({\n kind: \"signal\",\n name: emission.name,\n scope: emission.scope,\n workflowId: emission.workflowId || undefined,\n payload: emission.payload,\n })\n } catch { /* ignore */ }\n }\n return emission\n }\n\n /** Callback when a signal is published. Finds paused `signalWait` runs\n * whose filter matches and resumes them. */\n private async resumeFromSignal(emission: SignalEmission): Promise<void> {\n const all = this.runs.list({ limit: 500 })\n for (const run of all) {\n if (run.status !== \"paused\" || !run.pausedAt || run.pausedAt.kind !== \"signalWait\") continue\n const waiter = {\n name: run.pausedAt.signalName,\n scope: run.pausedAt.scope,\n workflowId: run.workflowId,\n match: run.pausedAt.match ?? {},\n }\n if (!matchesSignal(waiter, emission)) continue\n const wf = this.store.list().find((w) => w.id === run.workflowId)\n if (!wf) continue\n // Resume under the per-run mutex so a concurrent walk batch (or\n // another signal firing at the same instant) can't see a half-\n // transitioned pausedAt/pending/status set.\n await this.commit(run.id, () => {\n const fresh = this.runs.get(run.id)\n if (!fresh || fresh.status !== \"paused\" || !fresh.pausedAt || fresh.pausedAt.kind !== \"signalWait\") return\n if (!matchesSignal({ name: fresh.pausedAt.signalName, scope: fresh.pausedAt.scope, workflowId: fresh.workflowId, match: fresh.pausedAt.match ?? {} }, emission)) return\n const node = findNode(wf, fresh.pausedAt.nodeId)\n const successors = node ? wf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n const output = {\n receivedAt: new Date().toISOString(),\n name: emission.name,\n payload: emission.payload,\n }\n this.runs.recordExecution({\n runId: fresh.id,\n entry: {\n at: output.receivedAt,\n nodeId: fresh.pausedAt.nodeId,\n inputKeys: [],\n status: \"resumed\",\n output,\n idempotencyKey: idempotencyKey(fresh.id, fresh.pausedAt.nodeId, `signal:${emission.name}:${emission.emittedAt}`),\n },\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: { ...fresh.context, [fresh.pausedAt.nodeId]: output },\n })\n this.log(`[workflow:${wf.id}] run ${fresh.id} resumed from signal \"${emission.name}\"`)\n this.emitRunEvent({ runId: fresh.id, workflowId: wf.id, nodeId: fresh.pausedAt.nodeId, phase: \"resumed\", status: \"running\", note: `signal:${emission.name}`, rootId: fresh.eventRootId })\n })\n void this.walk(wf, run.id, `signal:${emission.name}`)\n .catch((e: any) => this.log(`[workflow:${wf.id}] walk-after-signal failed: ${e.message}`))\n }\n }\n\n /** Callback from the TimerService when a scheduled timer elapses. Reads\n * the paused run, verifies the pause is still on this timer's node, and\n * resumes by seeding the timer node's output with { firedAt } and\n * enqueueing successors. */\n private async resumeFromTimer(t: TimerRecord): Promise<void> {\n const wf = this.store.list().find((w) => w.id === (this.runs.get(t.runId)?.workflowId ?? \"\"))\n if (!wf) return\n const kind = this.runs.get(t.runId)?.pausedAt?.kind\n if (kind === \"replyWait\" || kind === \"agentStep\") { await this.followUpTimer(wf, t); return }\n await this.commit(t.runId, () => {\n const fresh = this.runs.get(t.runId)\n if (!fresh || fresh.status !== \"paused\") return\n if (!fresh.pausedAt || fresh.pausedAt.kind !== \"timerWait\" || fresh.pausedAt.nodeId !== t.nodeId) return\n const node = findNode(wf, t.nodeId)\n const successors = node ? wf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n const firedAt = new Date().toISOString()\n const output = { firedAt, scheduledFor: t.fireAt }\n this.runs.recordExecution({\n runId: fresh.id,\n entry: {\n at: firedAt, nodeId: t.nodeId, inputKeys: [], status: \"resumed\", output,\n idempotencyKey: idempotencyKey(fresh.id, t.nodeId, `timer:${t.id}`),\n },\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: { ...fresh.context, [t.nodeId]: output },\n })\n this.log(`[workflow:${wf.id}] run ${fresh.id} resumed from timer \"${t.nodeId}\" (fired ${firedAt})`)\n this.emitRunEvent({ runId: fresh.id, workflowId: wf.id, nodeId: t.nodeId, phase: \"resumed\", status: \"running\", note: `timer:${t.id}`, rootId: fresh.eventRootId })\n })\n void this.walk(wf, t.runId, `timer:${t.id}`)\n .catch((e: any) => this.log(`[workflow:${wf.id}] walk-after-timer failed: ${e.message}`))\n }\n\n /** Fire a specific workflow by id, bypassing matchByTrigger. Used by\n * `trigger.hook` and `trigger.cron` subscribers — they already KNOW\n * which workflow they're firing, and the workflow's trigger config\n * may not carry a `source` field to match against the caller's event.\n * `trigger` is synthesized so the reused dispatchOne path still has\n * something to feed matchesResume/paused-run resume logic. */\n async dispatchWorkflow(args: {\n workflowId: string\n entityRef: EntityRef\n event: TriggerEvent\n trigger?: { source?: string; project?: string; repo?: string; chat?: string; labels?: string[] }\n }): Promise<{ claimed: boolean; run: WorkflowRun | null }> {\n const wf = this.store.list().find((w) => w.id === args.workflowId)\n if (!wf) { this.log(`[workflows] dispatchWorkflow: workflow \"${args.workflowId}\" not found`); return { claimed: false, run: null } }\n // Runs are indexed by entity only. A run of another workflow on this\n // entity must not be resumed, or walked with this workflow's graph.\n const activeRunId = this.runs.getActiveByEntity(args.entityRef)\n const active = activeRunId ? this.runs.get(activeRunId) : null\n if (active && active.workflowId !== wf.id) {\n this.log(`[workflow:${wf.id}] entity ${args.entityRef.id} has run ${active.id} of workflow \"${active.workflowId}\" — not dispatching`)\n return { claimed: false, run: null }\n }\n const triggerNode = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n const cfg = (triggerNode?.config ?? {}) as { source?: string; filter?: Record<string, unknown> }\n const effective = {\n source: args.trigger?.source ?? cfg.source ?? \"hook\",\n project: args.trigger?.project,\n repo: args.trigger?.repo,\n chat: args.trigger?.chat,\n labels: args.trigger?.labels,\n }\n return this.dispatchOne(wf, args.entityRef, args.event, effective)\n }\n\n /** Fire 👀 + start the typing loop for a channel-triggered run, mirroring\n * what MessageRouter does when it owns the conversation. Without this,\n * workflow-claimed messages lose the \"I see you, I'm working on it\" UX\n * that users learn to expect from the agent path.\n *\n * Best-effort throughout: a failing adapter call must not break the walk.\n * Idempotent: repeated calls for the same runId no-op (typing loop stays). */\n private startChannelAck(runId: string, payload: Record<string, unknown>): void {\n const channel = String(payload.channel ?? \"\")\n const chatId = String(payload.chatId ?? \"\")\n if (!channel || !chatId) return\n const adapter = this.channels[channel] as AckCapableAdapter | undefined\n if (!adapter) return\n const accountId = typeof payload.accountId === \"string\" ? payload.accountId : undefined\n const event = payload.event as { id?: unknown } | undefined\n const messageId = typeof event?.id === \"string\" ? event.id : (event?.id != null ? String(event.id) : undefined)\n\n if (messageId && adapter.react) {\n try { void Promise.resolve(adapter.react(chatId, messageId, \"👀\", accountId)).catch(() => {}) }\n catch { /* swallow — ack is decorative */ }\n }\n if (this.ackTypingTimers.has(runId)) return\n if (!adapter.sendTyping) return\n const tick = () => {\n try { void Promise.resolve(adapter.sendTyping!(chatId, accountId)).catch(() => {}) }\n catch { /* swallow */ }\n }\n tick()\n const timer = setInterval(tick, ACK_TYPING_INTERVAL_MS)\n this.ackTypingTimers.set(runId, timer)\n }\n\n /** Stop the typing loop for a run. Called when a run leaves the running\n * state (terminal status OR pause — paused runs are waiting on an external\n * event and should not appear \"still typing\"). */\n private stopChannelAck(runId: string): void {\n const timer = this.ackTypingTimers.get(runId)\n if (!timer) return\n clearInterval(timer)\n this.ackTypingTimers.delete(runId)\n }\n\n /** Main entry. The hook subscribers map channel events into this shape.\n * `claimed` lists workflows that are actively handling this event for the\n * entity — newly created, resumed, forwarded to a remote home, or dropped\n * as a concurrent duplicate of a running run. The pre:channel-message hook\n * uses this to suppress the router's default reply so the workflow owns\n * the conversation (no double-send). A paused run whose resumeMatch didn't\n * accept the event is deliberately NOT claimed, so unrelated chatter flows\n * through the default agent while the workflow waits for the signal it\n * cares about. */\n async dispatch(args: {\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] }\n entityRef: EntityRef\n event: TriggerEvent\n /** When true, this dispatch was initiated by a peer's broadcast (received\n * via /workflow/event with kind=trigger). Only workflows that have\n * explicitly opted in via `mesh.allowRemote: true` (and pass the\n * `mesh.peers` allowlist when set) match in that mode — local workflows\n * without the opt-in stay isolated. The flag also short-circuits the\n * outbound fan-out below so a remote-origin dispatch never re-broadcasts\n * back through the mesh (no echo loops). */\n fromRemote?: { peer: string }\n }): Promise<{ claimed: Workflow[]; runs: WorkflowRun[] }> {\n const matches = this.matchByTrigger(args.trigger, args.fromRemote)\n const willFan = matches.some((m) => m.fanOut)\n const toRun = willFan ? matches : matches.slice(0, 1)\n const claimed: Workflow[] = []\n const runs: WorkflowRun[] = []\n for (const wf of toRun) {\n const r = await this.dispatchOne(wf, args.entityRef, args.event, args.trigger)\n if (r.claimed) claimed.push(wf)\n if (r.run) runs.push(r.run)\n }\n\n // Mesh trigger fan-out. Only emit on local-origin dispatches; broadcasts\n // from peers (`fromRemote`) are never re-broadcast or we'd echo. The\n // forwarder decides per-peer whether to actually deliver — typically based\n // on cached peer agent-cards advertising allowRemote workflows for this\n // trigger source.\n if (!args.fromRemote && this.forwarder?.broadcastTrigger) {\n // Fire-and-forget: a slow / unhealthy peer must not delay the local\n // hook return path that the router awaits. Failures are logged inside\n // the forwarder.\n void Promise.resolve(this.forwarder.broadcastTrigger({\n trigger: args.trigger,\n entityRef: args.entityRef,\n event: args.event,\n })).catch((e: any) => this.log(`[mesh-broadcast] ${e?.message ?? e}`))\n }\n\n return { claimed, runs }\n }\n\n private matchByTrigger(\n t: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n fromRemote?: { peer: string },\n ): Workflow[] {\n const all = this.store.list()\n const out: Workflow[] = []\n for (const wf of all) {\n // Lifecycle gate: only `active` workflows match new triggers.\n // disabled (operator kill switch) and quarantined (set by the\n // conflict detector) both opt out of fresh dispatches; in-flight\n // runs created while active continue to advance.\n if (wf.state && wf.state !== \"active\") continue\n // Mesh isolation: remote-origin events only see workflows that opted in.\n if (fromRemote) {\n if (!wf.mesh?.allowRemote) continue\n const allowed = wf.mesh.peers\n if (allowed && allowed.length > 0 && !allowed.includes(fromRemote.peer)) continue\n }\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger) continue\n const cfg = trigger.config as {\n source?: string\n filter?: { project?: string; repo?: string; chat?: string; labels?: string[]; fromJid?: string }\n }\n if (cfg.source !== t.source) continue\n // Project-scope gate. A workflow's top-level `project:` field is\n // a hard scope: events from a different project never match. This\n // is what stops cross-tenant fan-out — e.g. an `on:gitlab-issue`\n // event from `initech/int.initech.example.com` reaching `globex-pm-triage` (which is\n // tagged `project: globex/globex-system-v2`). Workflows with no\n // `project` field are global and match across projects (rare —\n // typically cross-project chores or templates). Events with no\n // `t.project` (manual / cron / 1:1 chat) bypass this check; the\n // project field only constrains project-scoped event sources.\n if (wf.project && t.project && wf.project !== t.project) continue\n const f = cfg.filter\n if (f) {\n if (f.project && f.project !== \"*\" && f.project !== t.project) continue\n if (f.repo && f.repo !== \"*\" && f.repo !== t.repo) continue\n if (f.chat && f.chat !== \"*\" && normalizeChat(t.source, f.chat) !== normalizeChat(t.source, t.chat)) continue\n if (f.labels?.length) {\n const have = new Set(t.labels ?? [])\n const hit = f.labels.some((l) => have.has(l))\n if (!hit) continue\n }\n }\n out.push(wf)\n }\n return out.sort((a, b) => b.priority - a.priority || a.id.localeCompare(b.id))\n }\n\n /**\n * Phase 1 commit 6.c — thin wrapper around the legacy dispatch. Calls\n * `dispatchOneLegacy` then, when `getLedgerMode(\"workflow\") !== \"off\"`,\n * records the dispatch to the intent ledger and reports any divergence\n * between ledger and legacy. Wrapped in try/catch so a ledger failure\n * never breaks workflow dispatch — legacy stays authoritative until\n * the 1c per-source promotion lands.\n */\n private async dispatchOne(\n workflow: Workflow,\n entityRef: EntityRef,\n event: TriggerEvent,\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n ): Promise<{ claimed: boolean; run: WorkflowRun | null }> {\n const result = await this.dispatchOneLegacy(workflow, entityRef, event, trigger)\n if (getLedgerMode(\"workflow\") !== \"off\") {\n try {\n recordWorkflowDispatch(\n getDefaultLedger(),\n {\n workflowId: workflow.id,\n eventId: event.id,\n triggerSource: trigger.source,\n project: trigger.project ?? null,\n entityRef,\n },\n JSON.stringify({ event, trigger, entityRef }),\n {\n claimed: result.claimed,\n runId: result.run?.id ?? null,\n },\n )\n } catch (e: any) {\n this.log(`[ledger] workflow ${workflow.id} run ${result.run?.id ?? \"?\"} record failed: ${e?.message ?? e}`)\n }\n }\n return result\n }\n\n private async dispatchOneLegacy(\n workflow: Workflow,\n entityRef: EntityRef,\n event: TriggerEvent,\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n ): Promise<{ claimed: boolean; run: WorkflowRun | null }> {\n const activeRunId = this.runs.getActiveByEntity(entityRef)\n const homeNode = activeRunId ? this.runs.getHomeNodeByEntity(entityRef) : null\n\n // Remote home node — forward via mesh and bail. The remote owns the\n // conversation, so we still claim the event locally (no default reply).\n if (activeRunId && homeNode && homeNode !== this.nodeId) {\n if (!this.forwarder) {\n this.log(`[workflow:${workflow.id}] run ${activeRunId} home'd on \"${homeNode}\" but no forwarder — dropping`)\n return { claimed: false, run: null }\n }\n try {\n await this.forwarder.forwardTransition(homeNode, { workflowId: workflow.id, event, entityRef })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] forward to \"${homeNode}\" failed: ${e.message}`)\n }\n return { claimed: true, run: null }\n }\n\n let run = activeRunId ? this.runs.get(activeRunId) : null\n if (!run) {\n // New entity → create a run, seed trigger context, enqueue trigger's successors.\n const init = initialPendingFromTrigger(workflow)\n if (!init) { this.log(`[workflow:${workflow.id}] no trigger node found`); return { claimed: false, run: null } }\n run = this.runs.create({\n workflowId: workflow.id,\n initialPending: init.pending,\n entityRef,\n initialContext: { [init.triggerId]: event.payload },\n })\n this.log(`[workflow:${workflow.id}] run ${run.id} created from trigger \"${init.triggerId}\" for ${entityRef.id}`)\n this.emitRunEvent({ runId: run.id, workflowId: workflow.id, phase: \"created\", status: run.status, homeNode: run.homeNode, rootId: run.eventRootId })\n // Channel-triggered: kick off the react+typing lifecycle so users see\n // the same \"I'm on it\" affordances they get from the router's path.\n if (trigger.source.endsWith(\"-message\")) this.startChannelAck(run.id, event.payload)\n } else if (run.status === \"paused\" && run.pausedAt) {\n // Resume path: a matching event arrived for a paused run. Only\n // `checkpoint` pauses resume on channel events. userTask pauses on\n // form submit; subProcess on child end; signalWait on signal.emit;\n // timerWait on timer fire. Each of those has its own resume entry\n // point elsewhere — here we just drop the channel event and decline\n // to claim (so default routing handles it).\n if (run.pausedAt.kind !== \"checkpoint\") {\n this.log(`[workflow:${workflow.id}] run ${run.id} paused on ${run.pausedAt.kind} — channel event bypasses workflow`)\n return { claimed: false, run: null }\n }\n if (!matchesResume(run.pausedAt.resumeMatch, trigger, event)) {\n this.log(`[workflow:${workflow.id}] run ${run.id} paused but event doesn't match resumeMatch — dropping`)\n return { claimed: false, run: null }\n }\n const checkpointNode = findNode(workflow, run.pausedAt.nodeId)\n const successors = checkpointNode\n ? workflow.edges.filter((e) => e.from === checkpointNode.id).map((e) => e.to)\n : []\n const resumeEntry = {\n at: new Date().toISOString(),\n nodeId: run.pausedAt.nodeId,\n inputKeys: [],\n status: \"resumed\" as const,\n output: { event: event.payload },\n idempotencyKey: idempotencyKey(run.id, run.pausedAt.nodeId, `resume:${event.id}`),\n }\n const newContext: Record<string, Record<string, unknown>> = {\n ...run.context,\n [run.pausedAt.nodeId]: { event: event.payload },\n }\n const pausedCheckpointName = run.pausedAt.checkpointName\n const updated = this.runs.recordExecution({\n runId: run.id,\n entry: resumeEntry,\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: newContext,\n })\n if (updated) run = updated\n this.log(`[workflow:${workflow.id}] run ${run.id} resumed from checkpoint \"${pausedCheckpointName}\"`)\n if (trigger.source.endsWith(\"-message\")) this.startChannelAck(run.id, event.payload)\n } else if (run.status === \"running\") {\n // Concurrent message on an already-running run. v1 policy: drop —\n // but still claim the event so the router doesn't ALSO reply.\n this.log(`[workflow:${workflow.id}] run ${run.id} already running; dropping concurrent event ${event.id}`)\n return { claimed: true, run: null }\n } else {\n // completed / failed / canceled — dead run. The filter matched but\n // this conversation's workflow is over, so let default routing handle.\n this.log(`[workflow:${workflow.id}] run ${run.id} is ${run.status}; dropping event ${event.id}`)\n return { claimed: false, run: null }\n }\n\n // Background walk — the dispatch call already returned 200 to the\n // webhook source. Agent calls inside the walk can take minutes.\n void this.walk(workflow, run.id, event.id)\n return { claimed: true, run }\n }\n\n /** Execute the pending queue until empty, paused, or failed. Wraps the\n * inner walk in try/finally so the channel-ack typing loop is always\n * cleared on exit — regardless of which branch (failed / paused / end /\n * drained) returned. Without this wrapper, an early return inside the\n * loop would leave the typing indicator running forever. */\n private async walk(workflow: Workflow, runId: string, triggeringEventId: string): Promise<void> {\n // Everything the walk does (agent tasks, mesh forwards, run events)\n // belongs to the run's root, whatever resumed it.\n const rootId = this.runs.get(runId)?.eventRootId\n const inner = () => this.walkInner(workflow, runId, triggeringEventId)\n try { await (rootId ? withRoot({ rootId }, inner) : inner()) }\n finally {\n this.stopChannelAck(runId)\n await this.settle(runId)\n }\n }\n\n private async walkInner(workflow: Workflow, runId: string, triggeringEventId: string): Promise<void> {\n let run = this.runs.get(runId)\n if (!run) return\n\n // Batch-parallel walk: dispatch up to MAX_PARALLEL_PER_RUN pending\n // node handlers concurrently via Promise.allSettled. Each node's\n // handler runs unguarded (parallel); only the commit (recordExecution\n // + joinCounters + pause transitions) runs under the per-run mutex.\n // For linear workflows (single pending) this collapses to the old\n // serial path — identical behaviour, identical history.\n while (run.status === \"running\" && run.pending.length > 0) {\n const batch = run.pending.slice(0, MAX_PARALLEL_PER_RUN)\n await Promise.allSettled(\n batch.map((nodeId) => this.executeNodeAndCommit(workflow, runId, nodeId, triggeringEventId)),\n )\n const fresh = this.runs.get(runId)\n if (!fresh) return\n run = fresh\n if (run.status !== \"running\") return\n }\n }\n\n /** Execute one node + commit its result. Handler runs outside the\n * mutex (so sibling branches run concurrently). Dedup + recordExecution\n * + joinCounters run inside commit() so state transitions never\n * interleave. */\n private async executeNodeAndCommit(\n workflow: Workflow,\n runId: string,\n nodeId: string,\n triggeringEventId: string,\n ): Promise<void> {\n const key = idempotencyKey(runId, nodeId, triggeringEventId)\n\n // --- phase 1 (guarded): dedup check + resolve handler ---\n const prelude = await this.commit(runId, () => {\n const run = this.runs.get(runId)\n if (!run) return { kind: \"done\" as const }\n if (run.status !== \"running\") return { kind: \"done\" as const }\n const node = findNode(workflow, nodeId)\n if (!node) {\n this.log(`[workflow:${workflow.id}] missing node \"${nodeId}\" — marking run failed`)\n this.runs.recordExecution({\n runId, entry: {\n at: new Date().toISOString(), nodeId, inputKeys: [], status: \"failed\",\n idempotencyKey: key, note: \"node definition missing from workflow\",\n },\n nextPending: run.pending.filter((x) => x !== nodeId),\n status: \"failed\",\n })\n return { kind: \"done\" as const }\n }\n const handler = resolveHandler(node.type)\n if (!handler) {\n this.log(`[workflow:${workflow.id}] no handler for node type \"${node.type}\"`)\n this.runs.recordExecution({\n runId, entry: {\n at: new Date().toISOString(), nodeId, inputKeys: [], status: \"failed\",\n idempotencyKey: key, note: `no handler for node type \"${node.type}\"`,\n },\n nextPending: run.pending.filter((x) => x !== nodeId),\n status: \"failed\",\n })\n return { kind: \"done\" as const }\n }\n if (run.history.some((h) => h.idempotencyKey === key)) {\n this.runs.recordExecution({\n runId, entry: {\n at: new Date().toISOString(), nodeId, inputKeys: [], status: \"skipped\",\n idempotencyKey: key + \"_dedup\", note: \"duplicate execution dropped\",\n },\n nextPending: run.pending.filter((x) => x !== nodeId),\n })\n return { kind: \"done\" as const }\n }\n return { kind: \"go\" as const, run, node, handler }\n })\n if (prelude.kind === \"done\") return\n\n // --- phase 2 (unguarded): actually run the handler, parallel-safe ---\n const { run, node, handler } = prelude\n const inputKeys = workflow.edges.filter((e) => e.to === nodeId).map((e) => e.from)\n\n // Improvement plan #9c — workflow steps populate task_traces so a\n // single `agentx trace list --workflow <runId>` shows every step,\n // joined by workflow_run_id. Agent nodes generate their own trace\n // via registry.execute (with workflow context already plumbed via\n // task.workflowRunId) so wrapping them here would double-count;\n // every other node type gets a synthetic trace row keyed by node\n // type so operators can answer \"which transform / branch / signal\n // / action.builtin step ran when, and how long?\"\n let workflowTraceTaskId: string | undefined\n if (node.type !== \"agent\") {\n const db = openDb()\n if (db) {\n try {\n workflowTraceTaskId = recordTraceStart(db, {\n agentId: `workflow:${node.type}`,\n channel: \"workflow\",\n chatId: `${workflow.id}:${nodeId}`,\n workflowRunId: runId,\n workflowId: workflow.id,\n workflowNodeId: nodeId,\n messagePreview: `${node.type} ${nodeId}`,\n })\n } catch { /* observability best-effort */ }\n }\n }\n\n // Improvement plan #9b — per-node retry on hard errors. Pause\n // results (userTask, signalWait, timerWait, subProcess) are\n // intentionally NOT retried — pausing is a normal lifecycle\n // transition. Only `{error}` results (handler returned an error\n // or threw) consume a retry budget.\n const retry = (node as { retry?: { maxAttempts: number; backoffMs: number } }).retry\n ?? { maxAttempts: 1, backoffMs: 1000 }\n let result: NodeResult = { error: \"retry-loop did not run\" }\n let attempt = 0\n // How long the step took, retries included (#858: run records).\n const stepStartedMs = Date.now()\n const timing = () => ({ startedAt: new Date(stepStartedMs).toISOString(), durationMs: Math.max(0, Date.now() - stepStartedMs) })\n while (attempt < retry.maxAttempts) {\n attempt++\n try {\n result = await handler({\n workflow, run, node,\n channels: this.channels,\n agents: this.agents,\n forwardChannelSend: this.forwarder?.forwardChannelSend?.bind(this.forwarder),\n log: this.log,\n owner: this.owner,\n followUp: this.followUpDefaults(),\n })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] handler \"${node.type}\" threw: ${e.message}`)\n result = { error: e.message }\n }\n // Stop on success or pause; keep going only on hard error.\n if (!result.error || result.paused) break\n if (attempt < retry.maxAttempts) {\n const wait = retry.backoffMs * Math.pow(2, attempt - 1)\n this.log(`[workflow:${workflow.id}] node \"${nodeId}\" failed (attempt ${attempt}/${retry.maxAttempts}): ${result.error.slice(0, 120)} — retrying in ${wait}ms`)\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n\n // The agent said \"done\" with agentx_workflow while its turn ran.\n const early = this.earlyDone.get(`${runId}:${nodeId}`)\n if (early) {\n this.earlyDone.delete(`${runId}:${nodeId}`)\n if (result.paused && result.pausedAt?.kind === \"agentStep\" && !result.blocked) {\n result = { output: { ...(result.output ?? {}), ...early } }\n }\n }\n\n // Finalize the workflow-step trace row, regardless of result.\n // Status mirrors the result shape: error → \"error\", paused → \"ok\"\n // (the node ran successfully and is waiting on external input;\n // pause is a normal lifecycle transition, not a failure), else \"ok\".\n if (workflowTraceTaskId) {\n const db = openDb()\n if (db) {\n try {\n recordTraceEnd(db, workflowTraceTaskId, {\n status: result.error ? \"error\" : \"ok\",\n error: result.error ?? null,\n })\n } catch { /* */ }\n }\n }\n\n // --- phase 3 (guarded): persist result + side effects ---\n await this.commit(runId, async () => {\n const fresh = this.runs.get(runId)\n if (!fresh) return\n const now = new Date().toISOString()\n const remainingFromPending = fresh.pending.filter((x) => x !== nodeId)\n\n if (result.error) {\n // If run is already paused/terminal from a sibling, just append\n // the exec entry without flipping status.\n const statusUpdate = fresh.status === \"running\" ? (\"failed\" as const) : undefined\n this.runs.recordExecution({\n runId, entry: {\n at: now, nodeId, inputKeys, status: \"failed\",\n idempotencyKey: key, note: result.error.slice(0, 200), ...timing(),\n },\n nextPending: remainingFromPending,\n status: statusUpdate,\n })\n this.emitRunEvent({ runId, workflowId: workflow.id, nodeId, phase: \"failed\", status: statusUpdate ?? fresh.status, note: result.error.slice(0, 200) })\n return\n }\n if (result.paused && result.pausedAt && TERMINAL.has(fresh.status)) {\n // Ended meanwhile (canceled while the step ran): note it, don't revive it.\n this.runs.recordExecution({\n runId, entry: { at: now, nodeId, inputKeys, status: \"skipped\", idempotencyKey: key, note: `step ended after the run was ${fresh.status}` },\n nextPending: remainingFromPending,\n })\n return\n }\n if (result.paused && result.pausedAt) {\n let pausedAt = result.pausedAt\n let spawnedChild: WorkflowRun | null = null\n if (result.spawnChild && pausedAt.kind === \"subProcess\") {\n spawnedChild = await this.spawnChild({\n parent: fresh, parentNodeId: nodeId,\n childWorkflowId: result.spawnChild.workflowId,\n input: result.spawnChild.input,\n })\n if (!spawnedChild) {\n this.runs.recordExecution({\n runId, entry: {\n at: now, nodeId, inputKeys, status: \"failed\",\n idempotencyKey: key,\n note: `subProcess: child workflow \"${result.spawnChild.workflowId}\" not found`,\n },\n nextPending: remainingFromPending, status: \"failed\",\n })\n return\n }\n pausedAt = { ...pausedAt, childRunId: spawnedChild.id }\n }\n this.runs.recordExecution({\n runId, entry: {\n at: now, nodeId, inputKeys, status: \"paused\", idempotencyKey: key, ...timing(),\n ...(result.blocked ? { note: `blocked: ${result.blocked}`.slice(0, 200) } : {}),\n },\n nextPending: remainingFromPending,\n status: \"paused\",\n pausedAt,\n // A followed agent step keeps what its turn said.\n ...(result.output ? { context: { ...fresh.context, [nodeId]: result.output } } : {}),\n })\n this.emitRunEvent({ runId, workflowId: workflow.id, nodeId, phase: \"paused\", status: \"paused\", note: pausedAt.kind })\n this.scheduleWake(runId, workflow.id, pausedAt)\n if (result.blocked) this.markBlocked(runId, workflow, nodeId, result.blocked)\n if (spawnedChild) this.kickChildWalk(spawnedChild)\n if (pausedAt.kind === \"timerWait\") {\n try {\n this.timers.schedule({\n runId, workflowId: workflow.id, nodeId: pausedAt.nodeId,\n fireAt: pausedAt.fireAt,\n cancelKey: `${runId}:${pausedAt.nodeId}`,\n })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] timer schedule failed: ${e.message}`)\n }\n }\n return\n }\n\n // Success path — fold output, join-gate, enqueue successors.\n const output: Record<string, unknown> = result.output ?? {}\n const newContext: Record<string, Record<string, unknown>> = { ...fresh.context, [nodeId]: output }\n const { nextPending } = nextNodes({ workflow, fromNodeId: nodeId, selectedPort: result.port })\n\n const readyNext: string[] = []\n const joinCounters: Record<string, string[]> = { ...(fresh.joinCounters ?? {}) }\n for (const succId of nextPending) {\n const succ = findNode(workflow, succId)\n const isJoin = succ?.type === \"gateway.parallel\" && String((succ.config as { mode?: unknown } | undefined)?.mode ?? \"fanOut\") === \"join\"\n if (!isJoin) { readyNext.push(succId); continue }\n const arrived = joinCounters[succId] ?? []\n if (!arrived.includes(nodeId)) arrived.push(nodeId)\n joinCounters[succId] = arrived\n const expected = workflow.edges.filter((e) => e.to === succId).length\n if (arrived.length >= expected) {\n readyNext.push(succId)\n delete joinCounters[succId]\n }\n }\n const merged = [...remainingFromPending, ...readyNext]\n const terminal = node.type === \"end\"\n\n // Respect a sibling branch's pause: if the run is already non-running,\n // don't flip status back to running. Still record the exec entry +\n // merged pending so the resume path can see the siblings' successors.\n const statusUpdate = fresh.status === \"running\"\n ? (terminal ? (String(node.config.status ?? \"completed\") as WorkflowRun[\"status\"]) : (\"running\" as const))\n : undefined\n\n const updated = this.runs.recordExecution({\n runId,\n entry: { at: now, nodeId, inputKeys, status: \"ok\", output, idempotencyKey: key, ...timing() },\n nextPending: terminal ? [] : merged,\n status: statusUpdate,\n context: newContext,\n joinCounters,\n })\n\n this.emitRunEvent({\n runId, workflowId: workflow.id, nodeId,\n phase: terminal ? \"completed\" : \"ok\",\n status: updated?.status ?? fresh.status,\n })\n\n if (result.emitSignal) {\n this.emitSignal({\n name: result.emitSignal.name,\n scope: result.emitSignal.scope,\n workflowId: workflow.id,\n payload: result.emitSignal.payload,\n })\n }\n\n if (terminal && updated && updated.parentRunId && updated.parentNodeId) {\n void this.resumeParent({\n parentRunId: updated.parentRunId,\n parentNodeId: updated.parentNodeId,\n childRun: updated,\n }).catch((e: any) => this.log(`[workflow:${workflow.id}] resumeParent failed: ${e.message}`))\n }\n })\n }\n\n // ---------------- Follow-up (#788) ----------------\n //\n // A follow-up run is one an agent, the CLI or the dashboard started for\n // a request (startRun). The run itself is the follow-up: each pause has\n // a resume path here (a card answer, a person's reply, a step reported\n // done, a timer), so nothing needs its own watcher.\n\n /** Start a run of `workflowId` with these inputs as the trigger's\n * output. With approval at start (and messages to people in it), the\n * run waits for the owner's yes before its first step. */\n async startRun(args: {\n workflowId: string\n inputs?: Record<string, unknown>\n meta?: Partial<RunMeta>\n entityRef?: EntityRef\n }): Promise<{ run: WorkflowRun | null; error?: string; awaitingApproval?: boolean }> {\n const wf = this.store.get(args.workflowId)\n if (!wf) return { run: null, error: `workflow \"${args.workflowId}\" not found` }\n const init = initialPendingFromTrigger(wf)\n if (!init) return { run: null, error: `workflow \"${wf.id}\" has no trigger node` }\n const entityRef = args.entityRef ?? { backend: \"follow-up\", id: `${wf.id}:${randomUUID()}` }\n const meta: RunMeta = { tags: [], approvedAtStart: false, ...args.meta, followUp: args.meta?.followUp ?? true }\n const run = this.runs.create({\n workflowId: wf.id,\n initialPending: init.pending,\n entityRef,\n initialContext: { [init.triggerId]: args.inputs ?? {} },\n meta,\n })\n this.log(`[workflow:${wf.id}] follow-up run ${run.id} started${meta.startedBy ? ` by ${meta.startedBy}` : \"\"}`)\n this.emitRunEvent({ runId: run.id, workflowId: wf.id, phase: \"created\", status: run.status, homeNode: run.homeNode, rootId: run.eventRootId })\n\n const messages = this.startMessages(wf, run)\n const approval = wf.approval ?? this.followUpDefaults().approval\n // Only messages known in full at the start can be approved then; one\n // that uses what a later step produces is asked again before it goes.\n if (approval === \"start\" && messages.some((m) => m.msg) && !meta.approvedAtStart) {\n const fail = async (error: string) => {\n this.runs.setStatus(run.id, \"failed\")\n await this.settle(run.id)\n return { run: this.runs.get(run.id), error }\n }\n if (!this.owner) return fail(\"this workflow needs the owner's approval at start, and there is no owner to ask on this node\")\n const lines = messages.map(({ nodeId, msg }) => msg\n ? `- ${nodeId} to ${msg.to ? `${msg.to} ` : \"\"}(${msg.channel} ${msg.chatId}): ${msg.text.replace(/\\s+/g, \" \").slice(0, 160)}`\n : `- ${nodeId}: asked again before it is sent (it uses what an earlier step produces)`)\n try {\n const { cardId } = await this.owner.ask({\n title: (meta.title ?? wf.title).slice(0, 120),\n ask: `Start \"${wf.title}\" and let it send the message(s) below without asking again?`.slice(0, 300),\n recommend: \"Yes if the recipients and the text below are right.\",\n context: lines.join(\"\\n\").slice(0, 600),\n }, run, init.triggerId)\n this.runs.recordExecution({\n runId: run.id,\n entry: { at: new Date().toISOString(), nodeId: init.triggerId, inputKeys: [], status: \"paused\", idempotencyKey: idempotencyKey(run.id, init.triggerId, \"start-approval\"), note: \"waiting for the owner's approval at start\" },\n nextPending: init.pending,\n status: \"paused\",\n pausedAt: { kind: \"ownerDecision\", nodeId: init.triggerId, cardId, purpose: \"start\" },\n })\n return { run: this.runs.get(run.id), awaitingApproval: true }\n } catch (e: any) {\n return fail(`could not ask the owner: ${e?.message ?? e}`)\n }\n }\n void this.walk(wf, run.id, `start:${run.id}`)\n .catch((e: any) => this.log(`[workflow:${wf.id}] walk after start failed: ${e.message}`))\n return { run }\n }\n\n /** The run's person.message steps as they read at the start: in full\n * when they use only the run's inputs, else null (asked later). */\n private startMessages(wf: Workflow, run: WorkflowRun): Array<{ nodeId: string; msg: ReturnType<typeof renderPersonMessage> | null }> {\n const triggerId = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))?.id ?? \"\"\n return wf.nodes.filter((n) => n.type === \"person.message\").map((node) => {\n const c = node.config as Record<string, unknown>\n const refs = [c.channel, c.chatId, c.text, c.to, c.accountId].flatMap((v) => typeof v === \"string\" ? [...v.matchAll(/\\{\\{\\s*([A-Za-z_][\\w-]*)/g)].map((m) => m[1]) : [])\n if (refs.some((r) => r !== triggerId && r !== \"env\")) return { nodeId: node.id, msg: null }\n const msg = renderPersonMessage({ node, run, workflow: wf })\n return { nodeId: node.id, msg: msg.channel && msg.chatId && msg.text ? msg : null }\n })\n }\n\n /** A decision card raised for a run was answered or expired. Returns\n * true when it belonged to a run waiting on it. */\n async resumeFromCard(card: { id: string; status: string; verdict?: string; text?: string; choice?: string; note?: string; origin?: { kind: string; runId?: string; nodeId?: string } }): Promise<boolean> {\n const runId = card.origin?.kind === \"workflow\" ? card.origin.runId : undefined\n if (!runId || card.status === \"pending\") return false\n const run0 = this.runs.get(runId)\n const wf = run0 ? this.store.get(run0.workflowId) : null\n if (!run0 || !wf) return false\n const port = card.status === \"decided\" ? (card.verdict === \"yes\" ? \"yes\" : \"no\") : \"expired\"\n let handled = false\n let walk = false\n let sendFailure: string | undefined\n // Sending happens outside the commit: a slow channel must not hold it.\n const p0 = run0.pausedAt\n let sent: { messageId: string | null } | { error: string } | null = null\n if (p0?.kind === \"ownerDecision\" && p0.cardId === card.id && p0.purpose === \"send\" && port === \"yes\" && p0.message) {\n sent = await deliver({ channels: this.channels, forwardChannelSend: this.forwarder?.forwardChannelSend?.bind(this.forwarder) }, { ...p0.message, text: card.text?.trim() || p0.message.text })\n }\n await this.commit(runId, () => {\n const fresh = this.runs.get(runId)\n const p = fresh?.pausedAt\n if (!fresh || fresh.status !== \"paused\" || p?.kind !== \"ownerDecision\" || p.cardId !== card.id) return\n handled = true\n const at = new Date().toISOString()\n const key = idempotencyKey(fresh.id, p.nodeId, `card:${card.id}`)\n if (p.purpose === \"start\") {\n if (port === \"yes\") {\n const approved: Record<string, string> = {}\n for (const { nodeId, msg } of this.startMessages(wf, fresh)) if (msg) approved[nodeId] = messageKey(msg)\n this.runs.setMeta(fresh.id, { approvedAtStart: true, approved })\n this.runs.recordExecution({ runId: fresh.id, entry: { at, nodeId: p.nodeId, inputKeys: [], status: \"resumed\", idempotencyKey: key, note: \"approved at start\" }, nextPending: fresh.pending, status: \"running\", pausedAt: null })\n walk = true\n } else {\n this.runs.recordExecution({ runId: fresh.id, entry: { at, nodeId: p.nodeId, inputKeys: [], status: \"failed\", idempotencyKey: key, note: port === \"no\" ? \"the owner said no at start\" : \"the owner did not answer before the start card expired\" }, nextPending: [], status: \"canceled\", pausedAt: null })\n }\n return\n }\n const decision = { cardId: card.id, verdict: card.verdict ?? null, status: card.status, choice: card.choice ?? null, text: card.text ?? null, note: card.note ?? null }\n if (p.purpose === \"send\") {\n if (port === \"yes\" && sent && \"error\" in sent) {\n sendFailure = sent.error\n this.runs.recordExecution({ runId: fresh.id, entry: { at, nodeId: p.nodeId, inputKeys: [], status: \"failed\", idempotencyKey: key, note: sent.error.slice(0, 200) }, nextPending: [], status: \"failed\", pausedAt: null })\n return\n }\n const output = port === \"yes\"\n ? { ...decision, ...p.message, text: card.text?.trim() || p.message?.text, messageId: sent && \"messageId\" in sent ? sent.messageId : null, approved: \"step\" }\n : { ...decision, ...p.message, declined: true }\n walk = this.resumeOn(wf, fresh, p.nodeId, output, port === \"yes\" ? \"sent\" : \"declined\", key, port === \"yes\" ? \"sent after approval\" : `not sent: ${port === \"no\" ? \"the owner said no\" : \"the card expired\"}`)\n return\n }\n walk = this.resumeOn(wf, fresh, p.nodeId, decision, port, key, `owner answered: ${port}`)\n })\n if (sendFailure) this.log(`[workflow:${wf.id}] run ${runId}: approved message not sent: ${sendFailure}`)\n if (walk) void this.walk(wf, runId, `card:${card.id}`).catch((e: any) => this.log(`[workflow:${wf.id}] walk after card failed: ${e.message}`))\n else if (handled) await this.settle(runId)\n return handled\n }\n\n /** An inbound message: the oldest run waiting for a reply in this chat\n * (from this sender, in a group) takes it. Returns that run's id, or\n * null when no run was waiting for it. */\n async resumeFromReply(msg: InboundReply): Promise<string | null> {\n const source = `${msg.channel}-message`\n if (!this.replyWaiters) {\n this.replyWaiters = new Map()\n for (const r of this.runs.list({ limit: 500 })) {\n if (r.status === \"paused\" && r.pausedAt?.kind === \"replyWait\") this.noteReplyWaiter(r.id, r.pausedAt.channel)\n }\n }\n const ids = this.replyWaiters.get(msg.channel)\n if (!ids?.size) return null\n const waiting = [...ids]\n .map((id) => this.runs.get(id))\n .filter((r): r is WorkflowRun => {\n if (r?.status === \"paused\" && r.pausedAt?.kind === \"replyWait\") return true\n if (r) ids.delete(r.id)\n return false\n })\n .filter((r) => {\n const p = r.pausedAt as Extract<PausedAt, { kind: \"replyWait\" }>\n if (p.channel !== msg.channel) return false\n if (normalizeChat(source, p.chatId) !== normalizeChat(source, msg.chatId)) return false\n if (p.accountId && msg.accountId && p.accountId !== msg.accountId) return false\n if (p.from && normalizeChat(source, p.from) !== normalizeChat(source, msg.senderId ?? \"\")) return false\n return true\n })\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n for (const candidate of waiting) {\n const wf = this.store.get(candidate.workflowId)\n if (!wf) continue\n let taken = false\n await this.commit(candidate.id, () => {\n const fresh = this.runs.get(candidate.id)\n const p = fresh?.pausedAt\n if (!fresh || fresh.status !== \"paused\" || p?.kind !== \"replyWait\" || p.nodeId !== (candidate.pausedAt as { nodeId: string }).nodeId) return\n taken = true\n const output = { text: msg.text, from: msg.senderId ?? null, fromName: msg.senderName ?? null, media: msg.media ?? null, channel: msg.channel, chatId: msg.chatId, at: new Date().toISOString(), reminders: p.reminders }\n this.timers.cancel({ cancelKey: `${fresh.id}:${p.nodeId}` })\n this.resumeOn(wf, fresh, p.nodeId, output, \"reply\", idempotencyKey(fresh.id, p.nodeId, `reply:${msg.messageId ?? output.at}`), \"reply received\")\n })\n if (!taken) continue\n ids.delete(candidate.id)\n void this.walk(wf, candidate.id, `reply:${msg.messageId ?? Date.now()}`).catch((e: any) => this.log(`[workflow:${wf.id}] walk after reply failed: ${e.message}`))\n return candidate.id\n }\n return null\n }\n\n /** An agent says its step is done (agentx_workflow done), or that\n * it is blocked. `nodeId` may be left out: the step the run waits on. */\n async stepDone(args: { runId: string; nodeId?: string; agentId?: string; output?: Record<string, unknown>; blocked?: string }): Promise<{ ok: boolean; error?: string }> {\n const run0 = this.runs.get(args.runId)\n if (!run0) return { ok: false, error: `no run \"${args.runId}\"` }\n const wf = this.store.get(run0.workflowId)\n if (!wf) return { ok: false, error: `workflow \"${run0.workflowId}\" is gone` }\n let error: string | undefined\n let walk = false\n await this.commit(args.runId, () => {\n const fresh = this.runs.get(args.runId)\n const p = fresh?.pausedAt\n // Reported from inside the step's own turn: kept until the turn ends.\n if (fresh?.status === \"running\" && !args.blocked) {\n const nodeId = args.nodeId ?? fresh.pending.find((id) => findNode(wf, id)?.type === \"agent\")\n const node = nodeId ? findNode(wf, nodeId) : undefined\n const owner = node ? String((node.config as { agentId?: unknown }).agentId ?? \"\") : \"\"\n if (node?.type === \"agent\" && fresh.pending.includes(node.id) && (!args.agentId || args.agentId === owner || args.agentId === fresh.meta?.startedBy)) {\n this.earlyDone.set(`${fresh.id}:${node.id}`, { ...(args.output ?? {}), result: (args.output?.result as string | undefined) ?? \"done\", doneAt: new Date().toISOString() })\n return\n }\n }\n if (!fresh || fresh.status !== \"paused\" || p?.kind !== \"agentStep\") { error = `run ${args.runId} is not waiting on an agent step (it is ${fresh?.status ?? \"gone\"}${p ? `, on ${p.kind} ${p.nodeId}` : \"\"})`; return }\n if (args.nodeId && args.nodeId !== p.nodeId) { error = `run ${args.runId} waits on step \"${p.nodeId}\", not \"${args.nodeId}\"`; return }\n if (args.agentId && args.agentId !== p.agentId && args.agentId !== fresh.meta?.startedBy) { error = `step \"${p.nodeId}\" belongs to ${p.agentId}`; return }\n if (args.blocked) {\n // Already blocked: the owner was told once.\n if (!p.blocked) this.blockStep(wf, fresh, p, args.blocked)\n return\n }\n this.timers.cancel({ cancelKey: `${fresh.id}:${p.nodeId}` })\n if (fresh.meta?.blocked) this.runs.setMeta(fresh.id, { blocked: undefined })\n const output = { ...(fresh.context[p.nodeId] ?? {}), ...(args.output ?? {}), result: (args.output?.result as string | undefined) ?? \"done\", doneAt: new Date().toISOString() }\n walk = this.resumeOn(wf, this.runs.get(fresh.id)!, p.nodeId, output, undefined, idempotencyKey(fresh.id, p.nodeId, `done:${output.doneAt}`), \"reported done\")\n })\n if (error) return { ok: false, error }\n if (walk) void this.walk(wf, args.runId, `done:${args.runId}`).catch((e: any) => this.log(`[workflow:${wf.id}] walk after step done failed: ${e.message}`))\n else await this.settle(args.runId)\n return { ok: true }\n }\n\n /** The owner stops a run (CLI, dashboard). */\n async cancelRun(runId: string, reason = \"canceled by the owner\"): Promise<boolean> {\n let done = false\n await this.commit(runId, () => {\n const fresh = this.runs.get(runId)\n if (!fresh || TERMINAL.has(fresh.status)) return\n const nodeId = fresh.pausedAt?.nodeId ?? fresh.pending[0] ?? \"run\"\n this.timers.cancel({ runId })\n this.runs.recordExecution({ runId, entry: { at: new Date().toISOString(), nodeId, inputKeys: [], status: \"skipped\", idempotencyKey: idempotencyKey(runId, nodeId, `cancel:${Date.now()}`), note: reason.slice(0, 200) }, nextPending: [], status: \"canceled\", pausedAt: null })\n done = true\n })\n if (done) await this.settle(runId)\n return done\n }\n\n /** Resume a paused follow-up step on `port`. Inside a commit. Returns\n * true when there is a next step to walk. A step that ended on a port\n * with nowhere to go stops the run (the summary says why). */\n private resumeOn(wf: Workflow, run: WorkflowRun, nodeId: string, output: Record<string, unknown>, port: string | undefined, key: string, note: string): boolean {\n const node = findNode(wf, nodeId)\n const { nextPending } = node ? nextNodes({ workflow: wf, fromNodeId: nodeId, selectedPort: port }) : { nextPending: [] as string[] }\n const ports = node ? FOLLOW_UP_PORTS[node.type] : undefined\n const dead = !!ports && port !== undefined && port !== ports.main && nextPending.length === 0\n const pending = [...run.pending.filter((x) => x !== nodeId), ...nextPending]\n this.runs.recordExecution({\n runId: run.id,\n entry: { at: new Date().toISOString(), nodeId, inputKeys: [], status: dead ? \"failed\" : \"resumed\", output: port ? { ...output, port } : output, idempotencyKey: key, note: dead ? `stopped: ${note}` : note },\n nextPending: dead ? [] : pending,\n status: dead ? \"failed\" : \"running\",\n pausedAt: null,\n context: { ...run.context, [nodeId]: port ? { ...output, port } : output },\n })\n this.emitRunEvent({ runId: run.id, workflowId: wf.id, nodeId, phase: dead ? \"failed\" : \"resumed\", status: dead ? \"failed\" : \"running\", note, rootId: run.eventRootId })\n return !dead\n }\n\n /** Reminders, deadlines and nudges. */\n private async followUpTimer(wf: Workflow, t: TimerRecord): Promise<void> {\n // Filled inside the commit, acted on after it.\n const after: {\n nudge?: { agentId: string; text: string }\n reminder?: { to: \"person\"; msg: { channel: string; chatId: string; accountId?: string; text: string } } | { to: \"owner\"; text: string }\n walk?: boolean\n } = {}\n await this.commit(t.runId, () => {\n const fresh = this.runs.get(t.runId)\n const p = fresh?.pausedAt\n if (!fresh || fresh.status !== \"paused\" || !p || p.nodeId !== t.nodeId) return\n const now = Date.now()\n const at = new Date(now).toISOString()\n if (p.kind === \"replyWait\") {\n if (now >= Date.parse(p.deadline)) {\n after.walk = this.resumeOn(wf, fresh, p.nodeId, { timedOut: true, reminders: p.reminders, deadline: p.deadline }, \"timeout\", idempotencyKey(fresh.id, p.nodeId, `deadline:${p.deadline}`), \"no reply before the deadline\")\n return\n }\n if (!p.nextRemindAt || now < Date.parse(p.nextRemindAt) || p.reminders >= p.maxReminders) { this.scheduleWake(fresh.id, wf.id, p); return }\n const n = p.reminders + 1\n const next: PausedAt = { ...p, reminders: n, ...(n < p.maxReminders && p.remindEveryMs ? { nextRemindAt: new Date(now + p.remindEveryMs).toISOString() } : { nextRemindAt: undefined }) }\n const sent = p.reminds ? fresh.context[p.reminds] as { text?: unknown } | undefined : undefined\n const reminder: NonNullable<typeof after.reminder> = sent && typeof sent.text === \"string\" && sent.text\n ? { to: \"person\", msg: { channel: p.channel, chatId: p.chatId, ...(p.accountId ? { accountId: p.accountId } : {}), text: `Reminder: ${sent.text}` } }\n : { to: \"owner\", text: `Still waiting for a reply on ${p.channel} (${p.chatId}) for step \"${p.nodeId}\" of ${fresh.meta?.title ? `\"${fresh.meta.title}\"` : `workflow ${wf.id}`}. Deadline: ${p.deadline.slice(0, 16).replace(\"T\", \" \")} UTC.` }\n this.runs.recordExecution({ runId: fresh.id, entry: { at, nodeId: p.nodeId, inputKeys: [], status: \"paused\", idempotencyKey: idempotencyKey(fresh.id, p.nodeId, `remind:${n}`), note: `reminder ${n}/${p.maxReminders} to ${reminder.to}` }, nextPending: fresh.pending, status: \"paused\", pausedAt: next })\n this.scheduleWake(fresh.id, wf.id, next)\n after.reminder = reminder\n return\n }\n if (p.kind === \"agentStep\") {\n if (p.blocked || !p.nextNudgeAt) return\n if (now < Date.parse(p.nextNudgeAt)) { this.scheduleWake(fresh.id, wf.id, p); return }\n if (p.nudges >= p.maxNudges) {\n this.blockStep(wf, fresh, p, p.maxNudges ? `no progress after ${p.maxNudges} reminder(s) to ${p.agentId}` : `no progress from ${p.agentId} for ${Math.round(p.stallMs / 60_000)} minute(s)`)\n return\n }\n const n = p.nudges + 1\n const next: PausedAt = { ...p, nudges: n, nextNudgeAt: new Date(now + p.stallMs).toISOString() }\n this.runs.recordExecution({ runId: fresh.id, entry: { at, nodeId: p.nodeId, inputKeys: [], status: \"paused\", idempotencyKey: idempotencyKey(fresh.id, p.nodeId, `nudge:${n}`), note: `nudge ${n}/${p.maxNudges} to ${p.agentId}` }, nextPending: fresh.pending, status: \"paused\", pausedAt: next })\n this.scheduleWake(fresh.id, wf.id, next)\n after.nudge = { agentId: p.agentId, text: nudgeText(fresh, p.nodeId, n, p.maxNudges, p.stallMs * n) }\n this.log(`[workflow:${wf.id}] run ${fresh.id}: nudge ${n}/${p.maxNudges} to ${p.agentId} on step \"${p.nodeId}\"`)\n }\n })\n if (after.walk) { void this.walk(wf, t.runId, `timer:${t.id}`).catch((e: any) => this.log(`[workflow:${wf.id}] walk after deadline failed: ${e.message}`)); return }\n await this.settle(t.runId)\n const r = after.reminder\n if (r?.to === \"person\") {\n const res = await deliver({ channels: this.channels, forwardChannelSend: this.forwarder?.forwardChannelSend?.bind(this.forwarder) }, r.msg)\n if (\"error\" in res) this.log(`[workflow:${wf.id}] run ${t.runId}: reminder not sent: ${res.error}`)\n } else if (r?.to === \"owner\") {\n const run = this.runs.get(t.runId)\n if (run) await this.owner?.notify(r.text, run).catch((e: any) => this.log(`[workflow:${wf.id}] run ${t.runId}: reminder to the owner failed: ${e?.message ?? e}`))\n }\n const nd = after.nudge\n if (nd) {\n // Fire and forget: the turn may take minutes. What it says decides.\n void this.agents.execute({ agentId: nd.agentId, message: nd.text, workflowRunId: t.runId })\n .then(async (resp) => {\n if (resp.error) { this.log(`[workflow:${wf.id}] run ${t.runId}: nudge turn on ${nd.agentId} failed: ${resp.error}`); return }\n const token = parseResultToken(resp.content).result\n const verdict = stepVerdict(token)\n if (verdict === \"blocked\") await this.stepDone({ runId: t.runId, nodeId: t.nodeId, blocked: lastLine(resp.content) || `${nd.agentId} says the step is ${token}` })\n else if (verdict === \"done\") await this.stepDone({ runId: t.runId, nodeId: t.nodeId, output: { reply: resp.content, result: token, via: \"nudge\" } })\n })\n .catch((e: any) => this.log(`[workflow:${wf.id}] run ${t.runId}: nudge turn failed: ${e?.message ?? e}`))\n }\n }\n\n /** Inside a commit: the agent step can't go on without the owner. */\n private blockStep(wf: Workflow, run: WorkflowRun, p: Extract<PausedAt, { kind: \"agentStep\" }>, reason: string): void {\n const next: PausedAt = { ...p, blocked: reason.slice(0, 300), nextNudgeAt: undefined }\n this.timers.cancel({ cancelKey: `${run.id}:${p.nodeId}` })\n this.runs.recordExecution({ runId: run.id, entry: { at: new Date().toISOString(), nodeId: p.nodeId, inputKeys: [], status: \"paused\", idempotencyKey: idempotencyKey(run.id, p.nodeId, `blocked:${Date.now()}`), note: `blocked: ${reason}`.slice(0, 200) }, nextPending: run.pending, status: \"paused\", pausedAt: next })\n this.markBlocked(run.id, wf, p.nodeId, reason)\n }\n\n /** Record the block and tell the owner once. */\n private markBlocked(runId: string, wf: Workflow, nodeId: string, reason: string): void {\n const run = this.runs.setMeta(runId, { blocked: { nodeId, reason: reason.slice(0, 300), at: new Date().toISOString() } })\n this.log(`[workflow:${wf.id}] run ${runId} blocked on \"${nodeId}\": ${reason}`)\n if (!run || !this.onBlocked) return\n void Promise.resolve(this.onBlocked(run, wf, nodeId, reason)).catch((e: any) => this.log(`[workflow:${wf.id}] blocked notice failed: ${e?.message ?? e}`))\n }\n\n private noteReplyWaiter(runId: string, channel: string): void {\n if (!this.replyWaiters) return\n if (!this.replyWaiters.has(channel)) this.replyWaiters.set(channel, new Set())\n this.replyWaiters.get(channel)!.add(runId)\n }\n\n /** Schedule the timer for a follow-up pause's next moment. */\n private scheduleWake(runId: string, workflowId: string, p: PausedAt): void {\n if (p.kind === \"replyWait\") this.noteReplyWaiter(runId, p.channel)\n const at = nextWakeAt(p)\n if (!at) return\n try {\n this.timers.cancel({ cancelKey: `${runId}:${p.nodeId}` })\n this.timers.schedule({ runId, workflowId, nodeId: p.nodeId, fireAt: at, cancelKey: `${runId}:${p.nodeId}` })\n } catch (e: any) {\n this.log(`[workflow:${workflowId}] timer schedule failed: ${e.message}`)\n }\n }\n\n /** After a walk or a resume: a follow-up run that ran out of steps is\n * complete, and a run that ended gets its one summary. */\n private async settle(runId: string): Promise<void> {\n let run = this.runs.get(runId)\n if (!run?.meta?.followUp) return\n if (run.status === \"running\" && run.pending.length === 0) run = this.runs.setStatus(runId, \"completed\") ?? run\n if (!TERMINAL.has(run.status) || run.meta?.endNotifiedAt) return\n run = this.runs.setMeta(runId, { endNotifiedAt: new Date().toISOString() }) ?? run\n this.timers.cancel({ runId })\n this.emitRunEvent({ runId, workflowId: run.workflowId, phase: \"ended\", status: run.status, rootId: run.eventRootId })\n if (!this.onRunEnded) return\n try { await this.onRunEnded(run, this.store.get(run.workflowId)) }\n catch (e: any) { this.log(`[workflow:${run.workflowId}] end notice for ${runId} failed: ${e?.message ?? e}`) }\n }\n\n // ---------------- Sub-process helpers ----------------\n\n /** Create a child run for a sub-process node. Returns null if the child\n * workflow id doesn't exist. Depth cap is enforced at the handler layer\n * before we get here. */\n private async spawnChild(args: {\n parent: WorkflowRun\n parentNodeId: string\n childWorkflowId: string\n input: Record<string, unknown>\n }): Promise<WorkflowRun | null> {\n const childWf = this.store.list().find((w) => w.id === args.childWorkflowId)\n if (!childWf) return null\n\n const init = initialPendingFromTrigger(childWf)\n if (!init) { this.log(`[workflow:${childWf.id}] no trigger node — cannot spawn as child`); return null }\n\n // Child gets a fresh context, seeded by the parent's inputMap. Top-level\n // keys are child-context keys; values are bundles. Parent linkage is\n // stamped onto the trigger node's bundle so downstream templates can\n // read {{trigger.parentRunId}} if useful.\n const childContext: Record<string, Record<string, unknown>> = {\n ...(args.input as Record<string, Record<string, unknown>>),\n }\n childContext[init.triggerId] = {\n ...(childContext[init.triggerId] ?? {}),\n parentRunId: args.parent.id,\n parentNodeId: args.parentNodeId,\n }\n\n const entityRef: EntityRef = {\n backend: \"agentx-internal\",\n id: `subprocess:${args.parent.id}:${args.parentNodeId}`,\n }\n\n const child = this.runs.create({\n workflowId: childWf.id,\n initialPending: init.pending,\n entityRef,\n initialContext: childContext,\n parentRunId: args.parent.id,\n parentNodeId: args.parentNodeId,\n rootRunId: args.parent.rootRunId ?? args.parent.id,\n depth: (args.parent.depth ?? 0) + 1,\n })\n this.log(`[workflow:${childWf.id}] child run ${child.id} spawned from ${args.parent.id}/${args.parentNodeId} (depth ${child.depth})`)\n // The caller (walk loop) kicks off the child walk AFTER persisting the\n // parent's paused state, so a fast child reaching end doesn't try to\n // resume a parent that hasn't been saved yet.\n return child\n }\n\n /** Kick a child run's walk loop — used by the walk loop after persisting\n * the parent's pause. Fire-and-forget. */\n private kickChildWalk(childRun: WorkflowRun): void {\n const childWf = this.store.list().find((w) => w.id === childRun.workflowId)\n if (!childWf) return\n void this.walk(childWf, childRun.id, `spawn:${childRun.parentRunId}:${childRun.parentNodeId}`)\n .catch((e: any) => this.log(`[workflow:${childWf.id}] child walk failed: ${e.message}`))\n }\n\n /** Resume a parent run whose subProcess node was awaiting this child. */\n async resumeParent(args: {\n parentRunId: string\n parentNodeId: string\n childRun: WorkflowRun\n }): Promise<void> {\n const preliminaryParent = this.runs.get(args.parentRunId)\n if (!preliminaryParent) return\n const parentWf = this.store.list().find((w) => w.id === preliminaryParent.workflowId)\n if (!parentWf) return\n\n await this.commit(args.parentRunId, () => {\n const parent = this.runs.get(args.parentRunId)\n if (!parent || parent.status !== \"paused\") return\n if (!parent.pausedAt || parent.pausedAt.kind !== \"subProcess\") return\n if (parent.pausedAt.nodeId !== args.parentNodeId) return\n if (parent.pausedAt.childRunId !== args.childRun.id) return\n\n const node = findNode(parentWf, args.parentNodeId)\n const successors = node ? parentWf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n const childOutput: Record<string, unknown> = {\n childRunId: args.childRun.id,\n status: args.childRun.status,\n output: pickChildOutput(args.childRun),\n }\n this.runs.recordExecution({\n runId: parent.id,\n entry: {\n at: new Date().toISOString(),\n nodeId: args.parentNodeId,\n inputKeys: [],\n status: \"resumed\",\n output: childOutput,\n idempotencyKey: idempotencyKey(parent.id, args.parentNodeId, `child:${args.childRun.id}`),\n },\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: { ...parent.context, [args.parentNodeId]: childOutput },\n })\n this.log(`[workflow:${parentWf.id}] parent run ${parent.id} resumed from subProcess child ${args.childRun.id}`)\n this.emitRunEvent({ runId: parent.id, workflowId: parentWf.id, nodeId: args.parentNodeId, phase: \"resumed\", status: \"running\", note: `child:${args.childRun.id}`, rootId: parent.eventRootId })\n })\n void this.walk(parentWf, args.parentRunId, `child:${args.childRun.id}`)\n }\n\n}\n\n/** Pick the most informative output bundle from a child run's history as\n * the `output` field exposed to the parent's subProcess node. We prefer\n * the last non-empty `ok` entry, which is typically the last meaningful\n * step before `end`. */\nfunction pickChildOutput(child: WorkflowRun): Record<string, unknown> {\n for (let i = child.history.length - 1; i >= 0; i--) {\n const h = child.history[i]\n if (h.status === \"ok\" && h.output && Object.keys(h.output).length > 0) {\n return h.output\n }\n }\n return {}\n}\n\n/** Does the incoming event's trigger fields match the checkpoint's\n * resumeMatch filter? Empty/missing fields are wildcards (match anything),\n * so the default `resumeMatch: {}` accepts any event on the entity. */\nfunction matchesResume(\n resumeMatch: Record<string, unknown>,\n trigger: { source?: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n event: TriggerEvent,\n): boolean {\n const match = (k: keyof typeof trigger): boolean => {\n const want = resumeMatch[k]\n if (want === undefined || want === \"\" || want === \"*\") return true\n if (Array.isArray(want)) {\n const have = new Set(trigger.labels ?? [])\n return (want as string[]).some((l) => have.has(String(l)))\n }\n if (k === \"chat\") {\n return normalizeChat(trigger.source, String(want)) === normalizeChat(trigger.source, trigger.chat)\n }\n return trigger[k] === want\n }\n // Fields supported in v1: source, project, repo, chat, labels. Additional\n // keys in resumeMatch are ignored (won't accidentally block resume).\n if (!match(\"source\")) return false\n if (!match(\"project\")) return false\n if (!match(\"repo\")) return false\n if (!match(\"chat\")) return false\n // `eventIdLike`: optional substring match on the event id, useful for\n // resuming only on events that carry a specific marker.\n const eventIdLike = resumeMatch.eventIdLike\n if (typeof eventIdLike === \"string\" && eventIdLike && !event.id.includes(eventIdLike)) return false\n return true\n}\n\n/** Normalize a chat identifier for filter comparison. Channel-aware: WhatsApp\n * ids drift between formats (raw digits \"10000000000\", JID\n * \"10000000000@s.whatsapp.net\", group JID \"...-...@g.us\", and human-formatted\n * \"+216 00 000 000\") depending on whether they came from the adapter's\n * payload, a copy-paste from the WA UI, or an editor field. We collapse all\n * of these to the canonical bare-id form before equality so authors can write\n * filters in whichever form is convenient. Other channels (Telegram, GitLab,\n * ...) use stable id formats from their APIs and don't need normalization. */\nfunction normalizeChat(source: string | undefined, value: string | undefined): string {\n if (value == null) return \"\"\n if (source === \"whatsapp-message\") {\n return value.replace(/@s\\.whatsapp\\.net$|@g\\.us$/i, \"\").replace(/[\\s+()]/g, \"\")\n }\n return value\n}\n\n/** The last line of a reply that is not the RESULT token. */\nfunction lastLine(text: string | undefined): string {\n if (!text) return \"\"\n const lines = text.split(\"\\n\").map((l) => l.trim()).filter((l) => l && !/^RESULT:/i.test(l))\n return (lines.at(-1) ?? \"\").slice(0, 300)\n}\n\nexport { idempotencyKey } from \"./run-store\"\n","import { choice } from \"@/decisions/questions\"\nimport { askSeat } from \"@/decisions/seat\"\nimport { render } from \"../template\"\nimport type { StateValue } from \"@/decisions/types\"\nimport type { NodeHandler } from \"./types\"\n\n// A classification node that classifies.\n//\n// The pattern the editor has taught until now is an `agent` node whose\n// prompt ends \"reply on one line: RESULT: a|b|c\", parsed back out with\n// a regex. That runs a full agent turn — on this fleet, Opus 5 — to\n// produce one token, and it has three problems beyond the cost.\n//\n// It returns no probabilities, so a `switch` downstream can only ask\n// which label came back, never how sure the model was. It cannot say \"I\n// don't know\": every input is forced into a label. And when the token\n// is missing or malformed the parser yields nothing and the run takes\n// the default port silently, which is the worst possible failure for a\n// branch — indistinguishable from a real decision.\n//\n// This asks the decision seat instead. One call, a distribution back,\n// and the node is its own branch: it fires the port named after the\n// winning label, or `unsure` when nothing clears the threshold. That\n// last port is the whole point. A workflow can now escalate the 10% it\n// should not have guessed at, which is the same confidence-gated\n// routing classifier.dev built on top of Jev to gain 2.5 points on AG\n// News — except here the uncertain branch is a first-class edge an\n// author can wire to a human.\n\nexport const CLASSIFY_SEAT = \"workflow-classify\"\n\n/** Ports a classify node can fire beyond its labels. */\nexport const UNSURE_PORT = \"unsure\"\n\nexport const classifyHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const vars = ctx.run.context as unknown as Record<string, unknown>\n const opts = { envAllow: ctx.workflow.envAllow }\n\n const input = render(String(cfg.input ?? \"\"), vars, opts).trim()\n if (!input) return { error: `classify node \"${ctx.node.id}\" missing config.input` }\n\n // Labels may be given as a map (label → description, which is what the\n // model actually needs) or as a bare list for quick authoring.\n const raw = cfg.labels\n const labels: Record<string, string | null> = Array.isArray(raw)\n ? Object.fromEntries(raw.map((l) => [String(l), null]))\n : typeof raw === \"object\" && raw\n ? Object.fromEntries(Object.entries(raw as Record<string, unknown>)\n .map(([k, v]) => [k, v == null ? null : String(v)]))\n : {}\n\n const names = Object.keys(labels)\n if (names.length < 2) {\n return { error: `classify node \"${ctx.node.id}\" needs at least two labels` }\n }\n if (names.includes(UNSURE_PORT)) {\n // Otherwise a low-confidence answer and a confident \"unsure\" fire the\n // same port and the distinction the node exists for is erased.\n return { error: `classify node \"${ctx.node.id}\": \"${UNSURE_PORT}\" is reserved for the low-confidence port` }\n }\n\n const minConfidence = typeof cfg.minConfidence === \"number\" ? cfg.minConfidence : 0.7\n const instructions = cfg.instructions ? render(String(cfg.instructions), vars, opts) : undefined\n\n const state: Record<string, StateValue> = { input }\n if (cfg.state && typeof cfg.state === \"object\") {\n for (const [k, v] of Object.entries(cfg.state as Record<string, unknown>)) {\n // Anything a template can produce is JSON; anything else is the\n // author's mistake and is better dropped than stringified into\n // \"[object Object]\" for the model to puzzle over.\n const rendered = typeof v === \"string\" ? render(v, vars, opts) : v\n if (rendered === null || [\"string\", \"number\", \"boolean\"].includes(typeof rendered)) {\n state[k] = rendered as StateValue\n }\n }\n }\n\n const res = await askSeat(\n CLASSIFY_SEAT,\n state,\n { label: choice(labels, instructions) },\n { features: { workflow: ctx.workflow.id, node: ctx.node.id } },\n )\n\n // The seat is off, or the backend failed. Fail the node rather than\n // guessing a port: a branch that silently picks a direction when its\n // decision procedure is unavailable is how a workflow does the wrong\n // thing confidently.\n if (!res) {\n return { error: `classify node \"${ctx.node.id}\": decision seat unavailable (set decisions.seats.${CLASSIFY_SEAT}.mode)` }\n }\n\n const answer = res.answers.label as { choice: string; confidence: number; probabilities: Record<string, number> }\n const confident = answer.confidence >= minConfidence\n const port = confident ? answer.choice : UNSURE_PORT\n\n ctx.log(`[node:${ctx.node.id}] classify → ${answer.choice} (${answer.confidence.toFixed(2)})${confident ? \"\" : ` — below ${minConfidence}, routing to ${UNSURE_PORT}`}`)\n\n return {\n output: {\n // `result` keeps the name the RESULT-token pattern used, so an\n // existing workflow can swap the node kind without rewriting the\n // {{node.result}} references downstream.\n result: answer.choice,\n label: answer.choice,\n confidence: answer.confidence,\n probabilities: answer.probabilities,\n unsure: !confident,\n port,\n },\n port,\n }\n}\n","// --- Template rendering ---\n//\n// Named-helper substitution: {{a.b.c}} resolves dotted paths in a context\n// object, returning the string form of the leaf value. Unknown paths render\n// as \"\" (not left as literal {{...}}) so agents don't see template syntax.\n// No arbitrary expressions in v1 — see types.ts for the rationale.\n//\n// `envAllow` is an allowlist of env-var names that {{env.X}} can reach; any\n// other env access renders as \"\". Prevents leaking secrets through prompt\n// text when a workflow author writes {{env.SUPER_SECRET}} by mistake.\n\nexport interface RenderOptions {\n envAllow?: string[]\n}\n\nfunction lookup(path: string, ctx: Record<string, unknown>, envAllow: Set<string>): string {\n const parts = path.split(\".\")\n // env.* is special: only allow-listed names are readable.\n if (parts[0] === \"env\") {\n if (parts.length !== 2) return \"\"\n const name = parts[1]\n if (!envAllow.has(name)) return \"\"\n const v = process.env[name]\n return typeof v === \"string\" ? v : \"\"\n }\n let cur: unknown = ctx\n for (const p of parts) {\n if (cur === null || cur === undefined) return \"\"\n if (typeof cur !== \"object\") return \"\"\n cur = (cur as Record<string, unknown>)[p]\n }\n if (cur === null || cur === undefined) return \"\"\n if (typeof cur === \"object\") return JSON.stringify(cur)\n return String(cur)\n}\n\nexport function render(template: string, ctx: Record<string, unknown>, opts: RenderOptions = {}): string {\n const envAllow = new Set(opts.envAllow || [])\n // Character class matches the workflow node-id regex (letters, digits,\n // underscore, hyphen, plus the dotted-path separator). Without the\n // hyphen, auto-generated ids like \"n-fcjwrx\" silently fail to\n // resolve — the {{…}} literal leaks into rendered output.\n return template.replace(/\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\\s*\\}\\}/g, (_, path: string) => lookup(path, ctx, envAllow))\n}\n\n/** Walks a params object and renders every string-valued leaf. Used by\n * actions so params like `{ body: \"Issue #{{issue.iid}} done\" }` work\n * without per-action special-casing. */\nexport function renderParams(params: Record<string, unknown>, ctx: Record<string, unknown>, opts: RenderOptions = {}): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(params)) {\n if (typeof v === \"string\") out[k] = render(v, ctx, opts)\n else if (Array.isArray(v)) out[k] = v.map((x) => typeof x === \"string\" ? render(x, ctx, opts) : x)\n else if (v && typeof v === \"object\") out[k] = renderParams(v as Record<string, unknown>, ctx, opts)\n else out[k] = v\n }\n return out\n}\n","// --- Per-workflow-node concurrency gate ---\n//\n// Caps how many simultaneous executions a single node ID can have across\n// concurrent workflow runs. Without this, N runs of the same workflow can\n// each fan a hot node (e.g. agent invocations) at once, multiplying token\n// spend and overwhelming downstream agents.\n//\n// Scope is `${workflowId}:${nodeId}` — different nodes don't share the\n// budget; different workflows that happen to use the same node id stay\n// independent.\n//\n// Used by `agentHandler` in nodes/handlers.ts. Activated only when the\n// node config sets `maxConcurrent`. Without that field the gate no-ops.\n\ninterface Waiter {\n resolve: () => void\n}\n\nclass NodeConcurrencyGate {\n private active: Map<string, number> = new Map()\n private waiters: Map<string, Waiter[]> = new Map()\n\n /** Acquire a slot. Resolves immediately if under cap; otherwise queues\n * in FIFO order behind earlier waiters. The caller must invoke\n * `release(key)` exactly once per acquire (use a try/finally). */\n async acquire(key: string, max: number): Promise<void> {\n const cur = this.active.get(key) ?? 0\n if (cur < max) {\n this.active.set(key, cur + 1)\n return\n }\n return new Promise<void>((resolve) => {\n const queue = this.waiters.get(key) ?? []\n queue.push({\n resolve: () => {\n this.active.set(key, (this.active.get(key) ?? 0) + 1)\n resolve()\n },\n })\n this.waiters.set(key, queue)\n })\n }\n\n release(key: string): void {\n const cur = this.active.get(key) ?? 0\n this.active.set(key, Math.max(0, cur - 1))\n const queue = this.waiters.get(key)\n if (queue && queue.length > 0) {\n const next = queue.shift()!\n next.resolve()\n }\n }\n\n /** For tests / observability. */\n stats(key: string): { active: number; waiting: number } {\n return {\n active: this.active.get(key) ?? 0,\n waiting: this.waiters.get(key)?.length ?? 0,\n }\n }\n}\n\n/** Process-singleton — workflow nodes share the same gate. */\nexport const nodeConcurrencyGate = new NodeConcurrencyGate()\n\nexport function nodeKey(workflowId: string, nodeId: string): string {\n return `${workflowId}:${nodeId}`\n}\n","import type { PausedAt, Workflow, WorkflowNode, WorkflowRun } from \"../types\"\n\n// --- Node handler interface ---\n//\n// Each node type registers a handler that executes the node given the live\n// run context. Handlers are awaited inside the dispatcher's walk loop; they\n// are NOT on the webhook-response critical path, so taking tens of seconds\n// (e.g. for an agent call) is fine.\n//\n// Handlers return one of three shapes:\n// - ok: { output } — node produced data, carry on\n// - ok with port: { output, port } — branch nodes: which outgoing port fired\n// - paused: { paused: true, pausedAt } — checkpoint nodes (Phase 2)\n// - failed: { error } — log + fail the run\n\nexport interface AgentExecuteRequest {\n agentId: string\n message: string\n workflowRunId?: string\n timeoutMinutes?: number\n /** Step autonomy (report | propose | act). Unset = act. */\n autonomy?: \"report\" | \"propose\" | \"act\"\n}\n\nexport interface AgentExecuteResponse {\n content: string\n error?: string\n /** Typed discriminator for `error`. Common values: `out_of_credits`,\n * `rate_limit`, `auth`, `timeout`, `overage_disabled`, `unknown`. Lets a\n * downstream `branch` node route on the failure cause without parsing\n * the friendly message. The full taxonomy lives in\n * src/agents/error-map.ts → FriendlyError.kind. */\n errorKind?: string\n taskId?: string\n durationMs?: number\n /** Tool calls blocked by the step's autonomy level. */\n autonomyBlocks?: Array<{ tool: string; target: string | null; ruleId: string; reason: string }>\n}\n\nexport interface NodeContext {\n workflow: Workflow\n run: WorkflowRun\n node: WorkflowNode\n /** Live channel adapter instances keyed by name (gitlab/github/telegram/...).\n * Handlers narrow to the method they need. */\n channels: Record<string, unknown>\n /** Thin agent-execute shim around AgentRegistry.execute(). */\n agents: { execute(req: AgentExecuteRequest): Promise<AgentExecuteResponse> }\n /** Outbound mesh forwarder — used by `action.send` to deliver to a channel\n * hosted on a peer node (e.g. workflow on macbook, whatsapp on\n * peer-server). Optional; absent in single-node setups and tests. */\n forwardChannelSend?: (payload: {\n channel: string\n chatId: string\n text: string\n accountId?: string\n parseMode?: string\n replyTo?: string\n }) => Promise<{ messageId: string | null }>\n /** Structured log sink. */\n log: (msg: string) => void\n /** The owner of this node: told things, asked things (#788). Absent\n * in tests and setups without a daemon; owner.* steps then fail. */\n owner?: OwnerPort\n /** Stall time, nudges and approval mode for follow-up steps. */\n followUp?: FollowUpDefaults\n}\n\n/** How the engine reaches the owner. The daemon implements it with its\n * notify path and decision cards (src/approvals). */\nexport interface OwnerPort {\n /** Tell the owner. Resolves once it was handed to a channel. */\n notify(text: string, run: { id: string; workflowId: string; meta?: { title?: string; startedBy?: string } }): Promise<void>\n /** Raise a decision card about this run's step. The card's answer comes\n * back through WorkflowDispatcher.resumeFromCard. */\n ask(card: OwnerCardInput, run: { id: string; workflowId: string; meta?: { startedBy?: string } }, nodeId: string): Promise<{ cardId: string }>\n}\n\nexport interface OwnerCardInput {\n title: string\n ask: string\n recommend: string\n /** Text the owner may edit before saying yes (a message to a person). */\n draft?: string\n choices?: string[]\n /** A few lines of background shown above the question. */\n context?: string\n /** ISO time, or relative like \"2d\". Default: the card setting. */\n expires?: string\n}\n\nexport interface FollowUpDefaults {\n stallMinutes: number\n maxNudges: number\n approval: \"start\" | \"step\"\n}\n\nexport const DEFAULT_FOLLOW_UP: FollowUpDefaults = { stallMinutes: 30, maxNudges: 2, approval: \"step\" }\n\nexport interface NodeResult {\n /** Output bundle keyed under ctx.run.context[node.id] by the dispatcher. */\n output?: Record<string, unknown>\n /** For branch nodes: which outgoing port fired (case's `to` field or\n * `default` port). Undefined for non-branch nodes. */\n port?: string\n /** True when the node paused the run (checkpoint, userTask, subProcess,\n * signalWait, timerWait). Dispatcher stops the walk and parks the run\n * until a matching event arrives. */\n paused?: boolean\n pausedAt?: PausedAt\n /** Optional actor ids who should see this task right now (userTask only). */\n assignedTo?: string[]\n /** Child workflow id + run id (subProcess only). The dispatcher spawns\n * the child before parking the parent. */\n spawnChild?: { workflowId: string; input: Record<string, unknown> }\n /** Signal emission produced by a signal.emit node, posted to the bus\n * before walk continues. */\n emitSignal?: { name: string; scope: \"workflow\" | \"global\"; payload: Record<string, unknown> }\n /** On failure, a short message. Dispatcher logs it + marks the run failed. */\n error?: string\n /** Follow-up (#788): the step is blocked and needs the owner. The run\n * pauses (pausedAt) and the owner is told this reason once. */\n blocked?: string\n}\n\nexport type NodeHandler = (ctx: NodeContext) => Promise<NodeResult>\n","import { render, renderParams } from \"../template\"\nimport type { WorkflowRun } from \"../types\"\nimport { DEFAULT_FOLLOW_UP, type FollowUpDefaults, type NodeContext, type NodeHandler, type NodeResult } from \"./types\"\n\n// --- Follow-up steps (#788) ---\n//\n// The steps a request with several stages needs beside agent work:\n// owner.notify tell the owner something, and go on\n// owner.ask ask the owner on a decision card; ports yes / no / expired\n// person.message send a message to a person (an employee, a client);\n// the owner approves it first, at the start of the run or\n// on its own card; ports sent / declined\n// person.wait wait for a person to write back on a channel, with a\n// deadline and reminders; ports reply / timeout\n// and the supervised agent step (supervisedResult): an agent step whose\n// turn ends without saying it is done is nudged when it stalls, and\n// blocked after the last nudge.\n//\n// Every pause here is resumed by the dispatcher: resumeFromCard,\n// resumeFromReply, stepDone, and the timer for reminders, deadlines and\n// nudges. No step needs its own watcher.\n\nconst ctxOf = (ctx: NodeContext) => ctx.run.context as unknown as Record<string, unknown>\n\n/** Minutes as a number, \"30m\" / \"4h\" / \"2d\", or ISO \"PT4H\" / \"P2D\". */\nexport function durationMs(v: unknown): number | null {\n if (typeof v === \"number\") return Number.isFinite(v) && v > 0 ? v * 60_000 : null\n if (typeof v !== \"string\" || !v.trim()) return null\n const s = v.trim()\n const short = /^(\\d+(?:\\.\\d+)?)\\s*(m|min|h|d)$/i.exec(s)\n if (short) {\n const n = Number(short[1])\n const unit = short[2].toLowerCase()\n const ms = n * (unit === \"d\" ? 86_400_000 : unit === \"h\" ? 3_600_000 : 60_000)\n return ms > 0 ? ms : null\n }\n const iso = /^P(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?$/i.exec(s)\n if (iso) {\n const ms = (((Number(iso[1] ?? 0) * 24 + Number(iso[2] ?? 0)) * 60 + Number(iso[3] ?? 0)) * 60 + Number(iso[4] ?? 0)) * 1000\n return ms > 0 ? ms : null\n }\n if (/^\\d+$/.test(s)) return Number(s) * 60_000 || null\n return null\n}\n\n/** The follow-up settings for this step: node config, then the workflow's\n * own, then the node's defaults. */\nexport function followUpFor(ctx: Pick<NodeContext, \"workflow\" | \"node\" | \"followUp\">): FollowUpDefaults {\n const base = ctx.followUp ?? DEFAULT_FOLLOW_UP\n const cfg = ctx.node.config as { stallMinutes?: unknown; maxNudges?: unknown }\n const wf = ctx.workflow.followUp ?? {}\n const num = (v: unknown) => (typeof v === \"number\" && Number.isFinite(v) && v >= 0 ? v : undefined)\n return {\n stallMinutes: num(cfg.stallMinutes) || wf.stallMinutes || base.stallMinutes,\n maxNudges: num(cfg.maxNudges) ?? wf.maxNudges ?? base.maxNudges,\n approval: ctx.workflow.approval ?? base.approval,\n }\n}\n\n/** Is this agent step followed? On a follow-up run unless the step says\n * `supervise: false`; elsewhere only with `supervise: true`. */\nexport function isSupervised(ctx: Pick<NodeContext, \"node\" | \"run\">): boolean {\n const flag = (ctx.node.config as { supervise?: unknown }).supervise\n if (typeof flag === \"boolean\") return flag\n return ctx.run.meta?.followUp === true\n}\n\n/** What a supervised step's prompt ends with: how to say it is done. */\nexport function supervisedSuffix(run: Pick<WorkflowRun, \"id\">, nodeId: string): string {\n return [\n \"\",\n `[agentx:workflow-step run=${run.id} step=${nodeId}]`,\n \"This is one step of a workflow AgentX is following. When the step is finished, end your reply with a line `RESULT: done` and say what proves it (a link, an id, the text you checked).\",\n `If it will finish later (a deploy still running, an answer still due), say so and report it when it is done with agentx_workflow: {action:\"done\", runId:\"${run.id}\", step:\"${nodeId}\", evidence:\"<proof>\"}. You will be reminded if nothing happens.`,\n \"If you cannot do it without the owner, end with `RESULT: blocked` and the reason in one line.\",\n ].join(\"\\n\")\n}\n\n/** What a RESULT word means for a followed step: finished, blocked (the\n * owner must act), or no word yet (wait, and remind when it stalls). A\n * failure is not a finish: the owner hears about it. */\nexport function stepVerdict(word: string | undefined): \"done\" | \"blocked\" | \"wait\" {\n if (!word) return \"wait\"\n if ([\"done\", \"approved\", \"skipped\", \"ok\", \"complete\", \"completed\"].includes(word)) return \"done\"\n if ([\"blocked\", \"failed\", \"rejected\", \"changes-requested\"].includes(word)) return \"blocked\"\n return \"wait\"\n}\n\n/** Decide what a supervised agent step's turn means. `parsed.result` is\n * the RESULT token, if the reply had one. */\nexport function supervisedResult(\n ctx: Pick<NodeContext, \"workflow\" | \"node\" | \"run\" | \"followUp\">,\n agentId: string,\n turn: { output?: Record<string, unknown>; error?: string; result?: string; reply?: string },\n): NodeResult {\n const { stallMinutes, maxNudges } = followUpFor(ctx)\n const stallMs = stallMinutes * 60_000\n const pausedAt = (blocked?: string) => ({\n kind: \"agentStep\" as const,\n nodeId: ctx.node.id,\n agentId,\n nudges: 0,\n maxNudges,\n stallMs,\n ...(blocked ? { blocked } : { nextNudgeAt: new Date(Date.now() + stallMs).toISOString() }),\n })\n const verdict = turn.error ? \"wait\" : stepVerdict(turn.result)\n if (verdict === \"blocked\") {\n const reason = lastLine(turn.reply) || `${agentId} says the step is ${turn.result}`\n return { paused: true, pausedAt: pausedAt(reason), blocked: reason, output: turn.output }\n }\n if (verdict === \"done\") return { output: turn.output }\n // No word that it is done, or the turn failed: wait, and nudge when it stalls.\n return { paused: true, pausedAt: pausedAt(), output: turn.output }\n}\n\nfunction lastLine(text: string | undefined): string {\n if (!text) return \"\"\n const lines = text.split(\"\\n\").map((l) => l.trim()).filter((l) => l && !/^RESULT:/i.test(l))\n return (lines.at(-1) ?? \"\").slice(0, 300)\n}\n\n/** The prompt of a nudge. */\nexport function nudgeText(run: Pick<WorkflowRun, \"id\" | \"meta\">, nodeId: string, n: number, max: number, sinceMs: number): string {\n const mins = Math.max(1, Math.round(sinceMs / 60_000))\n return [\n `[agentx:workflow-nudge run=${run.id} step=${nodeId} nudge=${n}/${max}]`,\n `Reminder ${n} of ${max}: the workflow step \"${nodeId}\"${run.meta?.title ? ` of \"${run.meta.title}\"` : \"\"} has shown no progress for ${mins} minute(s).`,\n \"Check where it stands and finish it now if you can.\",\n `When it is done, end your reply with \\`RESULT: done\\` and the proof, or call agentx_workflow {action:\"done\", runId:\"${run.id}\", step:\"${nodeId}\", evidence:\"<proof>\"}.`,\n \"If you cannot finish it without the owner, end with `RESULT: blocked` and the reason.\",\n n >= max ? \"This is the last reminder: after it the owner is told the step is blocked.\" : \"\",\n ].filter(Boolean).join(\"\\n\")\n}\n\n// --- owner.notify ---\n\nconst ownerNotifyHandler: NodeHandler = async (ctx) => {\n const text = render(String(ctx.node.config.text ?? \"\"), ctxOf(ctx), { envAllow: ctx.workflow.envAllow }).trim()\n if (!text) return { error: `owner.notify \"${ctx.node.id}\" needs config.text` }\n if (!ctx.owner) return { error: `owner.notify \"${ctx.node.id}\": no owner to tell on this node` }\n try {\n await ctx.owner.notify(text, ctx.run)\n } catch (e: any) {\n return { error: `owner.notify \"${ctx.node.id}\" failed: ${e?.message ?? e}` }\n }\n return { output: { text, sentAt: new Date().toISOString() } }\n}\n\n// --- owner.ask ---\n\nconst ownerAskHandler: NodeHandler = async (ctx) => {\n const cfg = renderParams(ctx.node.config, ctxOf(ctx), { envAllow: ctx.workflow.envAllow })\n const ask = String(cfg.ask ?? cfg.question ?? \"\").trim()\n if (!ask) return { error: `owner.ask \"${ctx.node.id}\" needs config.ask (the yes/no question)` }\n if (!ctx.owner) return { error: `owner.ask \"${ctx.node.id}\": no owner to ask on this node` }\n const choices = Array.isArray(cfg.choices) ? (cfg.choices as unknown[]).map(String).filter(Boolean) : undefined\n try {\n const { cardId } = await ctx.owner.ask({\n title: clip(String(cfg.title ?? ctx.run.meta?.title ?? ctx.workflow.title), 120),\n ask: clip(ask, 300),\n recommend: clip(String(cfg.recommend ?? \"Your call.\"), 300),\n ...(typeof cfg.draft === \"string\" && cfg.draft.trim() ? { draft: cfg.draft } : {}),\n ...(choices?.length ? { choices } : {}),\n ...(typeof cfg.context === \"string\" && cfg.context.trim() ? { context: clip(cfg.context, 600) } : {}),\n ...(typeof cfg.expires === \"string\" && cfg.expires.trim() ? { expires: cfg.expires } : {}),\n }, ctx.run, ctx.node.id)\n return { paused: true, pausedAt: { kind: \"ownerDecision\", nodeId: ctx.node.id, cardId, purpose: \"ask\" } }\n } catch (e: any) {\n return { error: `owner.ask \"${ctx.node.id}\": could not raise the card: ${e?.message ?? e}` }\n }\n}\n\n// --- person.message ---\n\n/** The message a person.message step sends, rendered against the run. */\nexport function renderPersonMessage(ctx: Pick<NodeContext, \"node\" | \"run\" | \"workflow\">): {\n channel: string; chatId: string; accountId?: string; text: string; to?: string\n} {\n const cfg = renderParams(ctx.node.config, ctxOf(ctx as NodeContext), { envAllow: ctx.workflow.envAllow })\n return {\n channel: String(cfg.channel ?? \"\").trim(),\n chatId: String(cfg.chatId ?? \"\").trim(),\n ...(typeof cfg.accountId === \"string\" && cfg.accountId ? { accountId: cfg.accountId } : {}),\n text: String(cfg.text ?? \"\").trim(),\n ...(typeof cfg.to === \"string\" && cfg.to.trim() ? { to: cfg.to.trim() } : {}),\n }\n}\n\nconst personMessageHandler: NodeHandler = async (ctx) => {\n const msg = renderPersonMessage(ctx)\n if (!msg.channel || !msg.chatId || !msg.text) {\n return { error: `person.message \"${ctx.node.id}\" needs channel, chatId and text` }\n }\n // Approved with the whole run, exactly as it reads now: send it.\n if (ctx.run.meta?.approved?.[ctx.node.id] === messageKey(msg)) {\n const sent = await deliver(ctx, msg)\n if (\"error\" in sent) return { error: sent.error }\n return { output: { ...msg, messageId: sent.messageId, approved: \"start\" }, port: \"sent\" }\n }\n if (!ctx.owner) return { error: `person.message \"${ctx.node.id}\": no owner to approve it on this node` }\n // The card always names the real chat: `to` alone could say anything.\n const who = msg.to ? `${msg.to} (${msg.channel} ${msg.chatId})` : `${msg.channel} ${msg.chatId}`\n try {\n const { cardId } = await ctx.owner.ask({\n title: clip(`Send to ${who}`, 120),\n ask: clip(`Send this message to ${who}?`, 300),\n recommend: clip(`It is step \"${ctx.node.id}\" of ${ctx.run.meta?.title ? `\"${ctx.run.meta.title}\"` : `workflow ${ctx.workflow.id}`}. Edit it if needed, then say yes.`, 300),\n draft: msg.text,\n }, ctx.run, ctx.node.id)\n return { paused: true, pausedAt: { kind: \"ownerDecision\", nodeId: ctx.node.id, cardId, purpose: \"send\", message: msg } }\n } catch (e: any) {\n return { error: `person.message \"${ctx.node.id}\": could not ask the owner: ${e?.message ?? e}` }\n }\n}\n\n/** What an approval at start covers: this recipient and this text. A\n * message that reads otherwise when its turn comes is asked again. */\nexport function messageKey(msg: { channel: string; chatId: string; text: string }): string {\n return `${msg.channel}\\n${msg.chatId}\\n${msg.text}`\n}\n\n/** Send through the local adapter, or a mesh peer that hosts the channel. */\nexport async function deliver(\n ctx: Pick<NodeContext, \"channels\" | \"forwardChannelSend\">,\n msg: { channel: string; chatId: string; accountId?: string; text: string },\n): Promise<{ messageId: string | null } | { error: string }> {\n const adapter = ctx.channels[msg.channel] as { send?: (m: { channel: string; chatId: string; text: string; accountId?: string }) => Promise<string | void> } | undefined\n const out = { channel: msg.channel, chatId: msg.chatId, text: msg.text, ...(msg.accountId ? { accountId: msg.accountId } : {}) }\n if (adapter?.send) {\n try {\n return { messageId: (await adapter.send(out)) ?? null }\n } catch (e: any) {\n return { error: `send on ${msg.channel} failed: ${e?.message ?? e}` }\n }\n }\n if (ctx.forwardChannelSend) {\n try {\n return { messageId: (await ctx.forwardChannelSend(out)).messageId }\n } catch (e: any) {\n return { error: `send on ${msg.channel} via the mesh failed: ${e?.message ?? e}` }\n }\n }\n return { error: `channel \"${msg.channel}\" is not available here or on a peer` }\n}\n\n// --- person.wait ---\n\nconst personWaitHandler: NodeHandler = async (ctx) => {\n const cfg = renderParams(ctx.node.config, ctxOf(ctx), { envAllow: ctx.workflow.envAllow })\n // Default: wait where the step named in `reminds` wrote.\n const reminds = typeof cfg.reminds === \"string\" && cfg.reminds ? cfg.reminds : undefined\n const sent = reminds ? ctx.run.context[reminds] as { channel?: unknown; chatId?: unknown; accountId?: unknown } | undefined : undefined\n const channel = String(cfg.channel ?? sent?.channel ?? \"\").trim()\n const chatId = String(cfg.chatId ?? sent?.chatId ?? \"\").trim()\n if (!channel || !chatId) return { error: `person.wait \"${ctx.node.id}\" needs channel and chatId (or reminds: a person.message step)` }\n const timeout = durationMs(cfg.timeout ?? \"P1D\")\n if (!timeout) return { error: `person.wait \"${ctx.node.id}\": timeout must be minutes, \"4h\", \"2d\" or \"P2D\"` }\n const every = cfg.remindAfter === undefined ? null : durationMs(cfg.remindAfter)\n if (cfg.remindAfter !== undefined && !every) return { error: `person.wait \"${ctx.node.id}\": remindAfter must be minutes, \"4h\", \"2d\" or \"P2D\"` }\n const maxReminders = typeof cfg.maxReminders === \"number\" ? Math.max(0, Math.floor(cfg.maxReminders)) : every ? 1 : 0\n const now = Date.now()\n const accountId = typeof cfg.accountId === \"string\" && cfg.accountId ? cfg.accountId\n : typeof sent?.accountId === \"string\" && sent.accountId ? sent.accountId : undefined\n return {\n paused: true,\n pausedAt: {\n kind: \"replyWait\",\n nodeId: ctx.node.id,\n channel,\n chatId,\n ...(accountId ? { accountId } : {}),\n ...(typeof cfg.from === \"string\" && cfg.from ? { from: cfg.from } : {}),\n deadline: new Date(now + timeout).toISOString(),\n ...(every && maxReminders > 0 ? { nextRemindAt: new Date(now + every).toISOString(), remindEveryMs: every } : {}),\n reminders: 0,\n maxReminders,\n ...(reminds ? { reminds } : {}),\n },\n }\n}\n\n/** The pause's next moment: a reminder, a nudge or the deadline. */\nexport function nextWakeAt(p: NonNullable<WorkflowRun[\"pausedAt\"]>): string | null {\n if (p.kind === \"replyWait\") {\n const remind = p.nextRemindAt && p.reminders < p.maxReminders ? p.nextRemindAt : null\n return remind && Date.parse(remind) < Date.parse(p.deadline) ? remind : p.deadline\n }\n if (p.kind === \"agentStep\") return p.blocked ? null : p.nextNudgeAt ?? null\n return null\n}\n\nconst clip = (s: string, n: number) => {\n const flat = s.replace(/\\s+/g, \" \").trim()\n return flat.length > n ? `${flat.slice(0, n - 1)}…` : flat\n}\n\nexport const FOLLOW_UP_HANDLERS: Record<string, NodeHandler> = {\n \"owner.notify\": ownerNotifyHandler,\n \"owner.ask\": ownerAskHandler,\n \"person.message\": personMessageHandler,\n \"person.wait\": personWaitHandler,\n}\n","import { evaluateBranch, getByPath } from \"../engine\"\nimport { classifyHandler } from \"./classify\"\nimport { renderParams, render } from \"../template\"\nimport { nodeConcurrencyGate, nodeKey } from \"../node-concurrency\"\nimport type { NodeContext, NodeHandler, NodeResult } from \"./types\"\nimport { FOLLOW_UP_HANDLERS, isSupervised, supervisedResult, supervisedSuffix } from \"./follow-up\"\n\n// --- Node handlers (Phase 1 set) ---\n//\n// Phase 1 ships: trigger.* (passthrough), agent, branch, action.send,\n// action.createIssue, end. Phase 2 adds checkpoint + label verbs.\n// Phase 3 adds react / editMessage / logTime / callHTTP / createCard /\n// trigger.cron / trigger.hook / trigger.manual specialisations.\n//\n// Each handler validates the shape of `node.config` locally and narrows the\n// channel adapter it needs to just the method it calls. Failures log and\n// return { error }; the dispatcher converts that into a failed-run state.\n\n/** Trigger nodes' output is pre-seeded into `run.context[triggerId]` by the\n * dispatcher when the run is created — they never actually \"execute\" here.\n * This handler is just a passthrough for completeness. */\nconst triggerHandler: NodeHandler = async (ctx) => {\n const output = ctx.run.context[ctx.node.id] ?? {}\n return { output }\n}\n\nconst agentHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const agentId = String(cfg.agentId ?? \"\")\n const promptTemplate = String(cfg.prompt ?? \"\")\n if (!agentId) return { error: `agent node \"${ctx.node.id}\" missing config.agentId` }\n\n // A followed step (#788) tells the agent how to say it is done.\n const supervised = isSupervised(ctx)\n const prompt = render(promptTemplate, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n + (supervised ? supervisedSuffix(ctx.run, ctx.node.id) : \"\")\n const timeoutMinutes = typeof cfg.timeoutMinutes === \"number\" ? cfg.timeoutMinutes : undefined\n // Step autonomy. An unrecognised value fails the step rather than\n // silently running it at full power.\n const autonomy = cfg.autonomy === undefined || cfg.autonomy === null ? undefined : String(cfg.autonomy)\n if (autonomy !== undefined && autonomy !== \"report\" && autonomy !== \"propose\" && autonomy !== \"act\") {\n return { error: `agent node \"${ctx.node.id}\" has invalid autonomy \"${autonomy}\" (expected report, propose or act)` }\n }\n\n // Optional per-node concurrency cap. When N runs of this workflow all\n // arrive at the same hot node, the gate serializes execution past N\n // active. Prevents the scenario where a burst of triggers fans out\n // every concurrent run to the same agent and multiplies token spend.\n const maxConcurrent = typeof cfg.maxConcurrent === \"number\" && cfg.maxConcurrent > 0\n ? cfg.maxConcurrent\n : null\n const gateKey = maxConcurrent ? nodeKey(ctx.workflow.id, ctx.node.id) : null\n if (gateKey && maxConcurrent) {\n const before = nodeConcurrencyGate.stats(gateKey)\n if (before.active >= maxConcurrent) {\n ctx.log(`[node:${ctx.node.id}] gated: ${before.active} active (cap ${maxConcurrent}), ${before.waiting + 1} waiting`)\n }\n await nodeConcurrencyGate.acquire(gateKey, maxConcurrent)\n }\n\n const start = Date.now()\n try {\n const resp = await ctx.agents.execute({\n agentId,\n message: prompt,\n workflowRunId: ctx.run.id,\n timeoutMinutes,\n ...(autonomy ? { autonomy: autonomy as \"report\" | \"propose\" | \"act\" } : {}),\n })\n const durationMs = Date.now() - start\n if (resp.error) {\n const kind = resp.errorKind ?? \"unknown\"\n ctx.log(`[node:${ctx.node.id}] agent \"${agentId}\" failed (${kind}): ${resp.error}`)\n // Followed: a failed turn is a stall, nudged like any other.\n if (supervised) return supervisedResult(ctx, agentId, { error: `[${kind}] ${resp.error}`, output: { error: `[${kind}] ${resp.error}` } })\n // Prefix the kind onto the run-level error string so the workflow\n // execution log and dashboard surface a stable, parseable token.\n // The friendly message follows verbatim for operators reading the log.\n return { error: `[${kind}] ${resp.error}` }\n }\n const parser = String(cfg.resultParser ?? \"acme-result-token\")\n const parsed = parser === \"json\" ? extractJsonBlock(resp.content) : parseResultToken(resp.content)\n const output = {\n reply: resp.content,\n result: parsed.result,\n json: parsed.json,\n taskId: resp.taskId,\n durationMs,\n ...(resp.autonomyBlocks?.length ? { autonomyBlocks: resp.autonomyBlocks } : {}),\n }\n if (supervised) return supervisedResult(ctx, agentId, { output, result: parsed.result, reply: resp.content })\n return { output }\n } catch (e: any) {\n ctx.log(`[node:${ctx.node.id}] agent \"${agentId}\" threw: ${e.message}`)\n return { error: e.message }\n } finally {\n if (gateKey) nodeConcurrencyGate.release(gateKey)\n }\n}\n\nconst branchHandler: NodeHandler = async (ctx) => {\n const port = evaluateBranch(ctx.node, ctx.run.context as unknown as Record<string, unknown>)\n if (!port) {\n ctx.log(`[node:${ctx.node.id}] branch matched no case and no default port`)\n return { output: { port: null } }\n }\n return { output: { port }, port }\n}\n\n/** Send a message via any channel adapter with a `send()` method.\n *\n * Account inheritance: when `accountId` isn't set on this node, fall back to\n * the trigger's `accountId` so workflow replies go out on the SAME bot the\n * message arrived on. Without this, multi-account adapters (Telegram) would\n * resolve the token via `chatAccountMap` — last bot to chat with the user —\n * and the same agent's reply could leak through any of N bots depending on\n * who messaged first. Authors can still override by setting `accountId`\n * explicitly in the node config. */\nconst sendHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"\")\n const chatId = String(rendered.chatId ?? \"\")\n const text = String(rendered.text ?? \"\")\n if (!channel || !chatId) {\n // Surface which template source was empty — usually the culprit is an\n // unallowed env var or a typo in a {{node.path}} reference. Without\n // this the author sees only \"needs channel + chatId\" and has to dig.\n const rawChat = String((ctx.node.config as Record<string, unknown>).chatId ?? \"\")\n const rawChan = String((ctx.node.config as Record<string, unknown>).channel ?? \"\")\n const hints: string[] = []\n if (!channel) hints.push(`channel config=\"${rawChan}\" rendered=\"\"`)\n if (!chatId) hints.push(`chatId config=\"${rawChat}\" rendered=\"\"`)\n if (/\\{\\{\\s*env\\./.test(rawChat) || /\\{\\{\\s*env\\./.test(rawChan)) {\n hints.push(`(env.* templates only resolve for names listed in workflow.envAllow and actually set in process.env)`)\n }\n return { error: `action.send \"${ctx.node.id}\" needs channel + chatId — ${hints.join(\"; \")}` }\n }\n\n const accountId = resolveAccountId(ctx, rendered.accountId)\n\n const adapter = ctx.channels[channel] as { send?: (m: { channel: string; chatId: string; text: string; accountId?: string }) => Promise<string | void> } | undefined\n\n // Local-first: if this node hosts the channel, send through the live\n // adapter. Otherwise fall back to the mesh forwarder (the channel may live\n // on a peer — workflow on macbook, whatsapp on peer-server, etc.). Without\n // either, we hard-error rather than silently dropping.\n if (adapter?.send) {\n try {\n const messageId = await adapter.send({ channel, chatId, text, ...(accountId ? { accountId } : {}) })\n return { output: { messageId: messageId ?? null } }\n } catch (e: any) {\n return { error: `action.send failed: ${e.message}` }\n }\n }\n\n if (ctx.forwardChannelSend) {\n try {\n const r = await ctx.forwardChannelSend({ channel, chatId, text, accountId })\n return { output: { messageId: r.messageId, viaMesh: true } }\n } catch (e: any) {\n ctx.log(`[node:${ctx.node.id}] mesh-forward of \"${channel}\" send failed: ${e.message}`)\n return { error: `action.send via mesh failed: ${e.message}` }\n }\n }\n\n ctx.log(`[node:${ctx.node.id}] adapter \"${channel}\" not available locally and no mesh forwarder`)\n return { error: `channel \"${channel}\" not available (no local adapter, no mesh route)` }\n}\n\n/** Create an issue via the GitLab adapter's createIssue method. */\nconst createIssueHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const project = String(rendered.project ?? \"\")\n const title = String(rendered.title ?? \"\")\n const description = String(rendered.description ?? \"\")\n const labels = Array.isArray(rendered.labels) ? (rendered.labels as string[]).map(String) : []\n const assignees = Array.isArray(rendered.assignees) ? (rendered.assignees as string[]).map(String) : []\n if (!project || !title) return { error: `action.createIssue \"${ctx.node.id}\" needs project + title` }\n\n // Resolve the originating agent id (if any) from the most recent upstream\n // agent-node execution, so per-agent token identity is preserved.\n const agentId = findUpstreamAgentId(ctx)\n\n const adapter = ctx.channels[channel] as {\n createIssue?: (args: {\n project: string; title: string; description?: string;\n labels?: string[]; assignees?: string[]; agentId?: string\n }) => Promise<{ iid: number; url: string } | null>\n } | undefined\n if (!adapter?.createIssue) {\n return { error: `channel \"${channel}\" does not support createIssue` }\n }\n try {\n const result = await adapter.createIssue({ project, title, description, labels, assignees, agentId })\n if (!result) return { error: \"createIssue returned null (see adapter log)\" }\n return {\n output: {\n issue: {\n iid: result.iid,\n url: result.url,\n webUrl: result.url,\n },\n },\n }\n } catch (e: any) {\n return { error: `action.createIssue failed: ${e.message}` }\n }\n}\n\nconst endHandler: NodeHandler = async () => ({ output: {} })\n\n/** Checkpoint: pauses the run with a named state + a resume filter. When an\n * event matching the filter arrives later (webhook, manual), the dispatcher\n * resumes the run by enqueueing the checkpoint's successors and seeding the\n * incoming event as this node's output. */\nconst checkpointHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const name = String(cfg.name ?? ctx.node.id)\n const waitFor = (cfg.waitFor ?? {}) as Record<string, unknown>\n const resumeMatch = (cfg.resumeMatch ?? {}) as Record<string, unknown>\n return {\n paused: true,\n pausedAt: {\n kind: \"checkpoint\",\n nodeId: ctx.node.id,\n checkpointName: name,\n // Consolidate waitFor + resumeMatch: when a future event arrives on\n // this run's entity, it's compared to both. waitFor is the canonical\n // shape (it mirrors a trigger.channel filter); resumeMatch is an\n // escape hatch for custom matches on event payload fields.\n resumeMatch: { ...waitFor, ...resumeMatch },\n },\n }\n}\n\n/** Action.setLabel: add / remove labels on a GitLab issue or MR. Looks up\n * the project + iid from the config; defaults to `{{trigger.project}}` +\n * `{{trigger.issue.iid}}` when those exist in the run context. */\nconst setLabelHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const project = String(rendered.project ?? \"\")\n const iid = String(rendered.iid ?? \"\")\n const add = Array.isArray(rendered.add) ? (rendered.add as string[]).map(String) : []\n const remove = Array.isArray(rendered.remove) ? (rendered.remove as string[]).map(String) : []\n if (!project || !iid) return { error: `action.setLabel \"${ctx.node.id}\" needs project + iid (try {{trigger.project}} / {{trigger.issue.iid}})` }\n if (add.length === 0 && remove.length === 0) return { output: { skipped: true, reason: \"no labels to add or remove\" } }\n\n const adapter = ctx.channels[channel] as {\n setLabels?: (a: { project: string; kind?: \"issue\" | \"merge_request\"; iid: string; add?: string[]; remove?: string[]; agentId?: string }) => Promise<string[] | null>\n } | undefined\n if (!adapter?.setLabels) return { error: `channel \"${channel}\" does not support setLabels` }\n\n const agentId = findUpstreamAgentId(ctx)\n const kind = (rendered.kind === \"merge_request\" ? \"merge_request\" : \"issue\") as \"issue\" | \"merge_request\"\n const labels = await adapter.setLabels({ project, kind, iid, add, remove, agentId })\n return { output: { labels: labels ?? [], add, remove } }\n}\n\n/** Action.readLabel: fetch the current labels on an issue/MR so downstream\n * nodes can branch on them. Emits `{ labels: string[] }` to the run context. */\nconst readLabelHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const project = String(rendered.project ?? \"\")\n const iid = String(rendered.iid ?? \"\")\n if (!project || !iid) return { error: `action.readLabel \"${ctx.node.id}\" needs project + iid` }\n\n const adapter = ctx.channels[channel] as {\n getLabels?: (a: { project: string; kind?: \"issue\" | \"merge_request\"; iid: string }) => Promise<string[] | null>\n } | undefined\n if (!adapter?.getLabels) return { error: `channel \"${channel}\" does not support getLabels` }\n\n const kind = (rendered.kind === \"merge_request\" ? \"merge_request\" : \"issue\") as \"issue\" | \"merge_request\"\n const labels = await adapter.getLabels({ project, kind, iid })\n return { output: { labels: labels ?? [] } }\n}\n\n/** Action.react: add an emoji reaction via the adapter's `react()` method.\n * Telegram + WhatsApp implement it; others return \"not supported\". */\nconst reactHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"\")\n const chatId = String(rendered.chatId ?? \"\")\n const messageId = String(rendered.messageId ?? \"\")\n const emoji = String(rendered.emoji ?? \"👀\")\n if (!channel || !chatId || !messageId) return { error: `action.react \"${ctx.node.id}\" needs channel + chatId + messageId` }\n\n const accountId = resolveAccountId(ctx, rendered.accountId)\n const adapter = ctx.channels[channel] as { react?: (chatId: string, messageId: string, emoji?: string, accountId?: string) => Promise<void> } | undefined\n if (!adapter?.react) return { error: `channel \"${channel}\" does not support react` }\n try { await adapter.react(chatId, messageId, emoji, accountId); return { output: { emoji } } }\n catch (e: any) { return { error: `action.react failed: ${e.message}` } }\n}\n\n/** Action.editMessage: edit a previously-sent message in place. */\nconst editMessageHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"\")\n const chatId = String(rendered.chatId ?? \"\")\n const messageId = String(rendered.messageId ?? \"\")\n const text = String(rendered.text ?? \"\")\n const parseMode = rendered.parseMode ? String(rendered.parseMode) : undefined\n if (!channel || !chatId || !messageId || !text) return { error: `action.editMessage \"${ctx.node.id}\" needs channel + chatId + messageId + text` }\n\n const accountId = resolveAccountId(ctx, rendered.accountId)\n const adapter = ctx.channels[channel] as { editMessage?: (chatId: string, messageId: string, text: string, parseMode?: string, accountId?: string) => Promise<boolean> } | undefined\n if (!adapter?.editMessage) return { error: `channel \"${channel}\" does not support editMessage` }\n try {\n const ok = await adapter.editMessage(chatId, messageId, text, parseMode, accountId)\n return { output: { edited: ok } }\n } catch (e: any) { return { error: `action.editMessage failed: ${e.message}` } }\n}\n\n/** Action.logTime: record time spent. Currently GitLab only — wraps the\n * adapter's logTimeSpent(chatId, durationMs, agentId?). The chatId\n * convention is \"project:issue:iid\" or \"project:merge_request:iid\". */\nconst logTimeHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const chatId = String(rendered.chatId ?? \"\")\n const durationMs = Number(rendered.durationMs ?? 0)\n if (!chatId || !durationMs) return { error: `action.logTime \"${ctx.node.id}\" needs chatId + durationMs` }\n\n const adapter = ctx.channels[channel] as { logTimeSpent?: (chatId: string, durationMs: number, agentId?: string) => Promise<void> } | undefined\n if (!adapter?.logTimeSpent) return { error: `channel \"${channel}\" does not support logTimeSpent` }\n const agentId = findUpstreamAgentId(ctx)\n try {\n await adapter.logTimeSpent(chatId, durationMs, agentId)\n return { output: { durationMs } }\n } catch (e: any) { return { error: `action.logTime failed: ${e.message}` } }\n}\n\n/** Action.callHTTP: generic outbound request with templated params. Body is\n * rendered as a string (authors write JSON templates) or passed straight\n * if already an object. Response body is captured as `{ status, body }`\n * so downstream nodes can branch on it. */\nconst callHTTPHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const url = String(rendered.url ?? \"\")\n if (!url) return { error: `action.callHTTP \"${ctx.node.id}\" needs url` }\n const method = String(rendered.method ?? \"POST\").toUpperCase()\n const headers = (rendered.headers && typeof rendered.headers === \"object\")\n ? rendered.headers as Record<string, string>\n : { \"Content-Type\": \"application/json\" }\n let body: string | undefined\n if (rendered.body !== undefined) {\n body = typeof rendered.body === \"string\" ? rendered.body : JSON.stringify(rendered.body)\n }\n\n try {\n const res = await fetch(url, { method, headers, body })\n // Prefer parsed JSON, fall back to text.\n const rawText = await res.text()\n let parsed: unknown = rawText\n try { parsed = JSON.parse(rawText) } catch { /* leave as text */ }\n return {\n output: {\n ok: res.ok,\n status: res.status,\n body: parsed,\n },\n }\n } catch (e: any) {\n return { error: `action.callHTTP failed: ${e.message}` }\n }\n}\n\n/** Action.run: invoke a registered action from .agentx/actions/<id>.json\n * with templated inputs. Output is the full ActionRunResult so downstream\n * nodes can branch on `.ok`, `.status`, or read `.output` text. */\nconst actionRunHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const actionId = String(rendered.actionId ?? rendered.id ?? \"\")\n if (!actionId) return { error: `action.run \"${ctx.node.id}\" needs actionId` }\n const inputs = (rendered.inputs && typeof rendered.inputs === \"object\")\n ? rendered.inputs as Record<string, unknown>\n : {}\n const { ActionStore } = await import(\"../../actions/store\")\n const { runAction } = await import(\"../../actions/runner\")\n const action = new ActionStore().get(actionId)\n if (!action) return { error: `action.run \"${ctx.node.id}\": no action \"${actionId}\" in registry` }\n try {\n const result = await runAction(action, inputs)\n const output: Record<string, unknown> = {\n ok: result.ok,\n output: result.output,\n status: result.status,\n durationMs: result.durationMs,\n }\n if (result.errors !== undefined) output.errors = result.errors\n return result.ok\n ? { output }\n : { output, error: `action \"${actionId}\" failed (status=${result.status})` }\n } catch (e: any) {\n return { error: `action.run failed: ${e.message}` }\n }\n}\n\n/** Action.builtin: call a daemon-shipped typed built-in action by name\n * (improvement plan #6 + #9). Closes the loop on the typed-workflow-DSL\n * story — workflow YAMLs can compose http.fetch / extract.structured /\n * file.read_lines / mesh.delegate / etc. without operator-defined action\n * records. Input is templated against the run context the same way\n * action.run handles its `inputs` field.\n *\n * Config shape:\n * {\n * kind: \"action.builtin\",\n * config: {\n * name: \"http.fetch\", // built-in name (dotted)\n * input: { url: \"{{previousNodeId.url}}\" } // any templatable object\n * }\n * }\n *\n * Output: the built-in's typed response, exposed under the run context\n * as `<nodeId>` so downstream nodes can reference it via templates.\n * On error (Zod validation, timeout, handler throw) returns\n * `{ error: ... }` so branch / rule nodes can react.\n */\nconst actionBuiltinHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const name = String(rendered.name ?? \"\")\n if (!name) return { error: `action.builtin \"${ctx.node.id}\" needs a \"name\" (e.g. \"http.fetch\")` }\n const input = (rendered.input && typeof rendered.input === \"object\")\n ? rendered.input as Record<string, unknown>\n : {}\n const { runBuiltin, getBuiltin } = await import(\"../../actions/builtin\")\n if (!getBuiltin(name)) {\n return { error: `action.builtin \"${ctx.node.id}\": unknown built-in \"${name}\"` }\n }\n try {\n const output = await runBuiltin(name, input)\n return { output: output as Record<string, unknown> }\n } catch (e: any) {\n // ZodError surfaces as a validation failure with a path-aware\n // message. Other errors (network, timeout, handler throw) come\n // through with their own message. Either way, return as a node\n // error so the engine can branch / surface in the run log.\n const msg = e?.name === \"ZodError\" ? `validation: ${e.message}` : (e?.message || String(e))\n return { error: `action.builtin \"${name}\" failed: ${msg}` }\n }\n}\n\n/** Transform: derive a bundle from upstream context. V2 scope is tight — we\n * support two modes:\n * 1. `path`: pick a value from context by dotted path, expose as {value}\n * 2. `template`: render each key via the template engine so authors can\n * reshape + combine upstream outputs for the next node. */\nconst transformHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const ctxObj = ctx.run.context as unknown as Record<string, unknown>\n if (typeof cfg.path === \"string\" && cfg.path) {\n return { output: { value: getByPath(ctxObj, cfg.path) } }\n }\n if (cfg.template && typeof cfg.template === \"object\") {\n const rendered = renderParams(cfg.template as Record<string, unknown>, ctxObj, { envAllow: ctx.workflow.envAllow })\n return { output: rendered }\n }\n return { output: {} }\n}\n\n/** Convert an ISO-8601 duration (\"PT2H\", \"P1D\") or a plain number-of-minutes\n * into an absolute dueAt timestamp. Accepts { minutes: N } too for convenience. */\nfunction computeDueAt(dueIn: unknown): string | undefined {\n if (dueIn === undefined || dueIn === null) return undefined\n if (typeof dueIn === \"number\") return new Date(Date.now() + dueIn * 60_000).toISOString()\n if (typeof dueIn === \"object\" && dueIn && \"minutes\" in dueIn) {\n const m = Number((dueIn as { minutes: unknown }).minutes)\n if (Number.isFinite(m)) return new Date(Date.now() + m * 60_000).toISOString()\n }\n if (typeof dueIn === \"string\") {\n const match = /^P(?:([0-9]+)D)?(?:T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)S)?)?$/.exec(dueIn)\n if (match) {\n const days = Number(match[1] ?? 0)\n const hours = Number(match[2] ?? 0)\n const minutes = Number(match[3] ?? 0)\n const seconds = Number(match[4] ?? 0)\n const ms = ((days * 24 + hours) * 60 + minutes) * 60_000 + seconds * 1000\n if (ms > 0) return new Date(Date.now() + ms).toISOString()\n }\n }\n return undefined\n}\n\n/** subProcess: spawn a child workflow run, pause the parent until the child\n * reaches `end`. Enforces workflow.maxChildDepth safety rail at spawn time.\n * Actual spawn is performed by the dispatcher (walk loop observes\n * `spawnChild` on the result and creates + kicks the child run). */\nconst subProcessHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n\n const childWorkflowId = String(rendered.workflowId ?? \"\")\n if (!childWorkflowId) return { error: `subProcess \"${ctx.node.id}\" missing config.workflowId` }\n\n // Depth safety rail: parent at depth D may spawn children up to\n // maxChildDepth - 1. I.e. a cap of 5 allows depths 0..4 to spawn 1..5.\n const parentDepth = ctx.run.depth ?? 0\n const cap = ctx.workflow.maxChildDepth ?? 5\n if (parentDepth + 1 >= cap) {\n return { error: `max child workflow depth ${cap} exceeded at ${ctx.workflow.id} → ${childWorkflowId} (parent depth ${parentDepth})` }\n }\n\n // Compute the child's initial context from inputMap. Map top-level keys\n // are child-context keys (typically the child's trigger node id); values\n // are objects whose leaves are rendered via the template engine.\n const rawInputMap = cfg.inputMap as unknown\n let inputBundle: Record<string, Record<string, unknown>> = {}\n if (rawInputMap === \"*\") {\n inputBundle = { ...(ctx.run.context as Record<string, Record<string, unknown>>) }\n } else if (rawInputMap && typeof rawInputMap === \"object\") {\n for (const [k, v] of Object.entries(rawInputMap as Record<string, unknown>)) {\n if (v && typeof v === \"object\" && !Array.isArray(v)) {\n inputBundle[k] = renderParams(v as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n } else if (typeof v === \"string\") {\n inputBundle[k] = { value: render(v, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow }) }\n }\n }\n }\n\n return {\n paused: true,\n pausedAt: {\n kind: \"subProcess\",\n nodeId: ctx.node.id,\n childRunId: \"\", // populated by dispatcher after spawn\n childWorkflowId,\n },\n spawnChild: { workflowId: childWorkflowId, input: inputBundle },\n }\n}\n\n/** trigger.form: same passthrough semantics as other triggers — the\n * dispatcher seeds the trigger node's output from the incoming form\n * submission when a run is created. */\nconst triggerFormHandler: NodeHandler = triggerHandler\n\n/** signal.emit: side-effect node — post a signal to the bus. Walk loop\n * reads `emitSignal` from the result and dispatches it before continuing. */\nconst signalEmitHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const name = String(rendered.name ?? \"\")\n if (!name) return { error: `signal.emit \"${ctx.node.id}\" missing config.name` }\n const scope = rendered.scope === \"global\" ? \"global\" : \"workflow\"\n const payload = (rendered.payload && typeof rendered.payload === \"object\") ? rendered.payload as Record<string, unknown> : {}\n return {\n output: { emittedAt: new Date().toISOString(), name, scope, payload },\n emitSignal: { name, scope, payload },\n }\n}\n\n/** signal.wait: pause the run until a matching signal arrives. */\nconst signalWaitHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const name = String(rendered.name ?? \"\")\n if (!name) return { error: `signal.wait \"${ctx.node.id}\" missing config.name` }\n const scope = rendered.scope === \"global\" ? \"global\" : \"workflow\"\n const match = (cfg.match && typeof cfg.match === \"object\") ? cfg.match as Record<string, unknown> : {}\n return {\n paused: true,\n pausedAt: {\n kind: \"signalWait\",\n nodeId: ctx.node.id,\n signalName: name,\n match,\n scope,\n },\n }\n}\n\n/** timer.boundary: pause the run until `after` elapses. The timer\n * subsystem fires a resume event on fireAt. Used both for SLA escalation\n * (attached to a prior node) and for intermediate waits. */\nconst timerBoundaryHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const due = computeDueAt(rendered.after ?? cfg.after)\n if (!due) return { error: `timer.boundary \"${ctx.node.id}\" needs config.after (duration or minutes)` }\n return {\n paused: true,\n pausedAt: {\n kind: \"timerWait\",\n nodeId: ctx.node.id,\n fireAt: due,\n },\n }\n}\n\n/** gateway.parallel: v1 is a passthrough. Multi-branch join semantics\n * land in Phase 2 when the walk driver upgrades to concurrent-pending. */\nconst gatewayParallelHandler: NodeHandler = async () => ({ output: {} })\n\n/** DMN-style decision-table rule node.\n *\n * Config shape:\n * {\n * inputs: [\"{{path.a}}\", \"{{path.b}}\", ...] // N templated values\n * rules: [\n * { when: [\"*\", \"gold\", \">100\"], to: \"vip\", output: { tier: \"gold\" } },\n * { when: [\"nike\", \"*\", \"*\"], to: \"oem\", output: { brand: \"nike\" } },\n * ...\n * ],\n * default: { to: \"fallback\", output: {} }\n * }\n *\n * Each `when` cell matches the corresponding input by one of:\n * - \"*\" → wildcard (matches anything)\n * - \"value\" → exact string equality\n * - \">N\" / \"<N\" → numeric comparison\n * - \">=N\" / \"<=N\" → numeric comparison\n * - \"!=value\" → inequality\n * - \"/regex/\" → regex match\n *\n * First matching row wins. Falls through to `default` when no rule matches. */\nconst ruleHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config as {\n inputs?: unknown\n rules?: Array<{ when?: unknown; to?: string; output?: Record<string, unknown> }>\n default?: { to?: string; output?: Record<string, unknown> }\n }\n const inputTemplates = Array.isArray(cfg.inputs) ? cfg.inputs as unknown[] : []\n const inputs = inputTemplates.map((raw) =>\n typeof raw === \"string\"\n ? render(raw, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n : raw\n )\n const rules = Array.isArray(cfg.rules) ? cfg.rules : []\n for (const rule of rules) {\n const when = Array.isArray(rule.when) ? rule.when as unknown[] : []\n if (when.length !== inputs.length) continue\n let all = true\n for (let i = 0; i < when.length; i++) {\n if (!matchCell(when[i], inputs[i])) { all = false; break }\n }\n if (all) {\n return {\n output: { ...(rule.output ?? {}), matchedPort: rule.to ?? \"\" },\n port: rule.to,\n }\n }\n }\n const def = cfg.default\n return {\n output: { ...(def?.output ?? {}), matchedPort: def?.to ?? \"\" },\n port: def?.to,\n }\n}\n\nfunction matchCell(when: unknown, input: unknown): boolean {\n if (when === undefined || when === null || when === \"*\") return true\n const w = String(when)\n const v = typeof input === \"number\" ? input : String(input ?? \"\")\n // Numeric comparators\n const numMatch = /^(>=|<=|>|<)\\s*(-?\\d+(?:\\.\\d+)?)$/.exec(w)\n if (numMatch) {\n const op = numMatch[1], n = Number(numMatch[2])\n const vNum = typeof v === \"number\" ? v : Number(v)\n if (Number.isNaN(vNum)) return false\n switch (op) {\n case \">\": return vNum > n\n case \"<\": return vNum < n\n case \">=\": return vNum >= n\n case \"<=\": return vNum <= n\n }\n }\n if (w.startsWith(\"!=\")) return String(v) !== w.slice(2)\n if (w.startsWith(\"/\") && w.endsWith(\"/\") && w.length > 2) {\n try { return new RegExp(w.slice(1, -1)).test(String(v)) } catch { return false }\n }\n return String(v) === w\n}\n\n// --- Registry ---\n\nexport const NODE_HANDLERS: Record<string, NodeHandler> = {\n \"trigger.channel\": triggerHandler,\n \"trigger.manual\": triggerHandler,\n \"trigger.cron\": triggerHandler,\n \"trigger.hook\": triggerHandler,\n \"trigger.form\": triggerFormHandler,\n \"trigger.poll\": triggerHandler,\n \"agent\": agentHandler,\n // A branch in its own right: it fires the port named after the label\n // it picked, or `unsure`. No RESULT token, no separate branch node.\n \"classify\": classifyHandler,\n \"branch\": branchHandler,\n \"transform\": transformHandler,\n \"gateway.parallel\": gatewayParallelHandler,\n \"rule\": ruleHandler,\n \"action.send\": sendHandler,\n \"action.createIssue\": createIssueHandler,\n \"action.setLabel\": setLabelHandler,\n \"action.readLabel\": readLabelHandler,\n \"action.react\": reactHandler,\n \"action.editMessage\": editMessageHandler,\n \"action.logTime\": logTimeHandler,\n \"action.callHTTP\": callHTTPHandler,\n \"action.run\": actionRunHandler,\n \"action.builtin\": actionBuiltinHandler,\n \"subProcess\": subProcessHandler,\n \"signal.emit\": signalEmitHandler,\n \"signal.wait\": signalWaitHandler,\n \"timer.boundary\": timerBoundaryHandler,\n \"checkpoint\": checkpointHandler,\n \"end\": endHandler,\n ...FOLLOW_UP_HANDLERS,\n}\n\nexport function resolveHandler(type: string): NodeHandler | undefined {\n return NODE_HANDLERS[type]\n}\n\n// --- Helpers ---\n\n/** Resolve the accountId for a channel-action node. Explicit config wins; if\n * unset, inherit from the trigger payload so workflow replies stay on the\n * same bot/account that received the inbound message. Returns undefined when\n * neither is available — adapters that don't care about accountId (single-\n * account channels) handle it transparently. */\nfunction resolveAccountId(ctx: NodeContext, explicit: unknown): string | undefined {\n if (typeof explicit === \"string\" && explicit) return explicit\n const triggerNode = ctx.workflow.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!triggerNode) return undefined\n const trigger = ctx.run.context[triggerNode.id] as { accountId?: unknown } | undefined\n const fromTrigger = trigger?.accountId\n return typeof fromTrigger === \"string\" && fromTrigger ? fromTrigger : undefined\n}\n\n/** Look back through the run's history for the most recently-executed\n * `agent` node and return its agentId. Used to attribute downstream\n * side effects (issue creation, labels, time logs) to the same agent. */\nfunction findUpstreamAgentId(ctx: NodeContext): string | undefined {\n for (let i = ctx.run.history.length - 1; i >= 0; i--) {\n const entry = ctx.run.history[i]\n const node = ctx.workflow.nodes.find((n) => n.id === entry.nodeId)\n if (node?.type === \"agent\") {\n return (node.config.agentId as string | undefined) ?? undefined\n }\n }\n return undefined\n}\n\n/** Agent contract: the reply MAY include a line like `RESULT: approved` or\n * `[APPROVED]` to signal a workflow result. Same parser as the V1 engine\n * (src/daemon/index.ts::parseAgentResult), lifted here so node handlers\n * don't depend on daemon code. */\nexport function parseResultToken(content: string | undefined): { result?: string; json?: unknown } {\n if (!content) return {}\n const explicit = content.match(/\\bRESULT:\\s*([a-z][a-z0-9_-]*)/i)\n if (explicit) return { result: explicit[1].toLowerCase() }\n const bracket = content.match(/\\[(APPROVED|REJECTED|CHANGES[-_]REQUESTED|DONE|FAILED|SKIPPED)\\]/i)\n if (bracket) return { result: bracket[1].toLowerCase().replace(\"_\", \"-\") }\n return {}\n}\n\n/** Extract the first fenced ```json ... ``` block and the RESULT: token.\n * Used when a workflow asks the agent for structured output (e.g. draft\n * issue title + body alongside a classification). */\nfunction extractJsonBlock(content: string | undefined): { result?: string; json?: unknown } {\n const { result } = parseResultToken(content)\n if (!content) return { result }\n const m = content.match(/```(?:json)?\\s*([\\s\\S]*?)```/)\n if (!m) return { result }\n try { return { result, json: JSON.parse(m[1]) } } catch { return { result } }\n}\n","// --- Signal bus (in-memory) ---\n//\n// Process-to-process eventing for workflows. Any running daemon instance\n// has one bus; `signal.emit` nodes post, `signal.wait` pauses subscribe\n// via the dispatcher's resume-on-signal callback. Two scopes:\n//\n// - workflow: a signal is only delivered to waiters in the same workflow\n// definition (workflowId). Default.\n// - global: delivered to every waiter matching name + match filter,\n// regardless of workflow.\n//\n// The bus is intentionally in-memory: signals fire now or never. Cross-\n// node delivery (mesh) is Phase 3. Durable \"signal log\" is out of scope.\n\nexport interface SignalEmission {\n name: string\n scope: \"workflow\" | \"global\"\n workflowId: string\n payload: Record<string, unknown>\n emittedAt: string\n}\n\nexport type SignalHandler = (emission: SignalEmission) => void | Promise<void>\n\nexport class SignalBus {\n private handlers: Set<SignalHandler> = new Set()\n\n subscribe(h: SignalHandler): () => void {\n this.handlers.add(h)\n return () => this.handlers.delete(h)\n }\n\n emit(emission: SignalEmission): void {\n for (const h of this.handlers) {\n try { void Promise.resolve(h(emission)).catch(() => {}) }\n catch { /* swallow — listeners must be robust */ }\n }\n }\n}\n\n/** Does a signal emission match a waiter's filter?\n * - Name must match exactly.\n * - If waiter scope is \"workflow\", the emission's workflowId must match\n * the waiter's workflowId.\n * - The match-record is shallow equality: every key in match must equal\n * the emission's payload at that key. Empty match accepts anything. */\nexport function matchesSignal(\n waiter: { name: string; scope: \"workflow\" | \"global\"; workflowId: string; match: Record<string, unknown> },\n emission: SignalEmission,\n): boolean {\n if (waiter.name !== emission.name) return false\n if (waiter.scope === \"workflow\" && waiter.workflowId !== emission.workflowId) return false\n for (const [k, v] of Object.entries(waiter.match ?? {})) {\n if (v === undefined || v === \"\" || v === \"*\") continue\n if ((emission.payload as Record<string, unknown>)[k] !== v) return false\n }\n return true\n}\n","import type { IntentLedger } from \"../ledger\"\nimport { decideAndCommit, type DispatchPolicy } from \"../decide\"\nimport { reportDivergence, type LegacyOutcome } from \"../divergence\"\nimport type { IntentDecision, IntentEventInput } from \"../types\"\n\n// Source adapter — workflow dispatcher. Phase 1 commit 6.c.\n//\n// One trigger event → at most one workflow run firing. The legacy\n// dispatchOne returns `{ claimed: boolean, run: WorkflowRun | null }`.\n// We project that into a LegacyOutcome and pass-through to the ledger:\n//\n// claimed=true, run!=null → \"dispatched\" (new or resumed run)\n// claimed=true, run==null → \"halted\" (forwarded or concurrent-drop)\n// claimed=false → \"halted\" (declined: no trigger, dead, etc.)\n//\n// The \"claimed but no run\" case is a legitimate dispatcher state (the\n// event was claimed by the workflow even though no node fired here —\n// either the home node is remote or a concurrent run owns the event).\n// Mapping to \"halted\" for the ledger means: from the ledger's POV no\n// agent was dispatched. The legacy reason captures the nuance for audit.\n\nexport interface WorkflowEventProjection {\n workflowId: string\n /** TriggerEvent.id — stable across retries. */\n eventId: string\n triggerSource: string\n /** Optional project axis from the trigger metadata (gitlab project,\n * etc.). When null, Inv-ActiveTaskSafety doesn't engage — same as the\n * router. */\n project: string | null\n entityRef: { backend: string; id: string }\n}\n\nexport interface WorkflowLegacyResult {\n claimed: boolean\n /** Set when the legacy path created or resumed a run. */\n runId: string | null\n /** Optional human-readable reason — preserved verbatim in the divergence\n * row's legacy_reason column. Helps triage during the soak. */\n reason?: string\n}\n\n/** Build the IntentEventInput for one workflow trigger. The\n * sourceEventId is per-(workflow, trigger event) so re-deliveries\n * collapse to one event. Subject scopes active-task safety to one\n * entity per workflow. */\nexport function buildWorkflowEventInput(\n proj: WorkflowEventProjection,\n rawJson: string,\n now: () => number = Date.now,\n): IntentEventInput {\n return {\n ts: now(),\n source: \"workflow\",\n sourceEventId: `${proj.workflowId}:${proj.eventId}`,\n project: proj.project,\n subject: `workflow:${proj.workflowId}:entity:${proj.entityRef.backend}:${proj.entityRef.id}`,\n intent: `workflow.${proj.triggerSource}`,\n rawJson,\n }\n}\n\n/** Project the workflow's `{ claimed, run }` shape into the LegacyOutcome\n * the divergence reporter consumes. */\nexport function workflowResultToLegacyOutcome(result: WorkflowLegacyResult): LegacyOutcome {\n if (result.claimed && result.runId) {\n return {\n agentId: `workflow-run:${result.runId}`,\n outcome: \"dispatched\",\n reason: result.reason ?? \"claimed\",\n }\n }\n if (result.claimed && !result.runId) {\n // Forwarded to remote or concurrent-drop — claimed but no local run.\n return {\n agentId: null,\n outcome: \"halted\",\n reason: result.reason ?? \"claimed-but-no-run\",\n }\n }\n return {\n agentId: null,\n outcome: \"halted\",\n reason: result.reason ?? \"not-claimed\",\n }\n}\n\n/** Pass-through policy: ledger decision mirrors legacy outcome.\n * Divergences come from ledger MECHANICS (idempotency on (source,\n * sourceEventId), active-task safety on (project, subject)). */\nexport function buildWorkflowPolicyFromLegacy(legacy: LegacyOutcome): DispatchPolicy {\n return {\n decidedBy: \"workflow-dispatcher\",\n decide: () => ({\n agentId: legacy.agentId,\n outcome: legacy.outcome === \"deduped\" ? \"halted\" : legacy.outcome,\n reason:\n legacy.outcome === \"deduped\"\n ? `legacy-dedup: ${legacy.reason ?? \"\"}`.trim()\n : legacy.reason ?? null,\n }),\n }\n}\n\n/** Record + report for one (trigger event, legacy result). Caller catches\n * exceptions — workflow dispatch must continue even if the ledger errors. */\nexport function recordWorkflowDispatch(\n ledger: IntentLedger,\n proj: WorkflowEventProjection,\n rawJson: string,\n result: WorkflowLegacyResult,\n now: () => number = Date.now,\n): IntentDecision {\n const eventInput = buildWorkflowEventInput(proj, rawJson, now)\n const legacyOutcome = workflowResultToLegacyOutcome(result)\n const policy = buildWorkflowPolicyFromLegacy(legacyOutcome)\n const ledgerDecision = decideAndCommit(ledger, eventInput, policy, now)\n reportDivergence(ledger, \"workflow\", ledgerDecision, legacyOutcome, now)\n return ledgerDecision\n}\n","import type { HookContext, HookEvent, HookResult } from \"@/hooks/types\"\nimport type { IncomingMessage } from \"@/channels/types\"\nimport type { WorkflowDispatcher } from \"./dispatcher\"\nimport type { EntityRef } from \"./types\"\n\n// --- Workflow hook subscribers (V2) ---\n//\n// Thin mappers: take whatever raw payload a hook fires with, produce a\n// typed trigger bundle, and call the dispatcher. No routing decisions\n// here — the dispatcher picks matching workflows, the engine walks the\n// DAG, node handlers do the work.\n//\n// For channel-triggered workflows, the subscriber returns\n// { blocked: true } when the event is claimed by a workflow so the router\n// skips its default agent reply — otherwise BOTH the workflow's action.send\n// AND the router's own reply would fire, producing duplicate messages.\n// GitLab/pipeline subscribers stay non-blocking because those events run\n// side-by-side with agent routing by design.\n//\n// A workflow can opt out per-trigger with `trigger.config.passthrough = true`\n// to let the default router reply alongside the workflow (useful for\n// observability-only workflows that just log or tag).\n\nexport function createWorkflowHookHandlers(dispatcher: WorkflowDispatcher): Partial<Record<HookEvent, (ctx: HookContext) => Promise<HookResult>>> {\n return {\n // Generic inbound channel message — WhatsApp, Telegram, Discord, Slack.\n // The router fires this before resolving an agent, so workflows see the\n // message with full structured detail (sender, group, media, etc.).\n \"pre:channel-message\": async (ctx) => {\n const msg = ctx.msg as IncomingMessage | undefined\n if (!msg) return {}\n const chatId = msg.group?.id ?? msg.sender.id\n\n // A run waiting for this person's reply (person.wait, #788) takes\n // it first, on any channel: the workflow owns that answer.\n const waiting = await dispatcher.resumeFromReply({\n channel: msg.channel,\n chatId,\n accountId: msg.accountId,\n senderId: msg.sender.id,\n senderName: msg.sender.name,\n text: msg.text,\n media: msg.media,\n messageId: msg.id,\n })\n if (waiting) return { blocked: true, message: `reply taken by workflow run ${waiting}` }\n\n const source = channelToTriggerSource(msg.channel)\n if (!source) return {}\n\n const entityRef: EntityRef = { backend: msg.channel, id: `${msg.channel}:${chatId}` }\n\n const { claimed } = await dispatcher.dispatch({\n trigger: {\n source,\n chat: chatId,\n },\n entityRef,\n event: {\n id: `${msg.channel}:${msg.accountId}:${msg.id}`,\n payload: buildChannelTriggerPayload(msg),\n },\n })\n\n if (claimed.length === 0) return {}\n\n // Opt-out: if every matched workflow declared `passthrough: true` on its\n // trigger node, let the router's default reply run alongside. Otherwise\n // the workflow owns the conversation — block the router.\n const allPassthrough = claimed.every((wf) => {\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n return (trigger?.config as { passthrough?: boolean } | undefined)?.passthrough === true\n })\n if (allPassthrough) return {}\n\n return {\n blocked: true,\n message: `handled by workflow${claimed.length > 1 ? \"s\" : \"\"}: ${claimed.map((w) => w.id).join(\", \")}`,\n }\n },\n\n // GitLab issue events — open, update, close. Existing subscriber shape\n // from V1 is preserved so the GitLab adapter doesn't need changes.\n \"on:gitlab-issue\": async (ctx) => {\n const project = ctx.project as string | undefined\n const iid = ctx.iid as string | number | undefined\n const issueEvent = ctx.issueEvent as any\n if (!project || iid === undefined) return {}\n\n const labels: string[] = Array.isArray(issueEvent?.object_attributes?.labels)\n ? issueEvent.object_attributes.labels.map((l: any) => typeof l === \"string\" ? l : l?.title).filter(Boolean)\n : (issueEvent?.labels?.map((l: any) => l?.title) ?? [])\n const assignees = issueEvent?.assignees ?? issueEvent?.object_attributes?.assignee_ids ?? []\n\n const entityRef: EntityRef = { backend: \"gitlab\", id: `${project}#${iid}` }\n await dispatcher.dispatch({\n trigger: { source: \"gitlab-issue\", project, labels },\n entityRef,\n event: {\n id: `gitlab-issue:${project}:${iid}:${issueEvent?.object_attributes?.updated_at || issueEvent?.object_attributes?.action || \"evt\"}`,\n payload: {\n issue: {\n iid,\n title: issueEvent?.object_attributes?.title,\n description: issueEvent?.object_attributes?.description,\n url: issueEvent?.object_attributes?.url,\n action: issueEvent?.object_attributes?.action,\n labels,\n assignees,\n },\n project,\n channel: \"gitlab\",\n chatId: `${project}:issue:${iid}`,\n },\n },\n })\n return {}\n },\n\n // GitLab MR events — open, reopen, update, approved, merge, close.\n // Mirrors on:gitlab-issue; the gitlab adapter fires this in handleMR\n // after the project rule's merge_request clause has approved the\n // event. Workflow subscribers see the typed mr payload and can route\n // off attrs.action (e.g. fire a review on `open`, fire deploy on\n // `merge`).\n \"on:gitlab-mr\": async (ctx) => {\n const project = ctx.project as string | undefined\n const iid = ctx.iid as string | number | undefined\n const mrEvent = ctx.mrEvent as any\n if (!project || iid === undefined) return {}\n\n const labels: string[] = Array.isArray(ctx.labels)\n ? (ctx.labels as string[])\n : (Array.isArray(mrEvent?.object_attributes?.labels)\n ? mrEvent.object_attributes.labels.map((l: any) => typeof l === \"string\" ? l : l?.title).filter(Boolean)\n : [])\n const assignees = mrEvent?.assignees ?? mrEvent?.object_attributes?.assignee_ids ?? []\n const reviewers = mrEvent?.reviewers ?? []\n\n const entityRef: EntityRef = { backend: \"gitlab\", id: `${project}!${iid}` }\n await dispatcher.dispatch({\n trigger: { source: \"gitlab-mr\", project, labels },\n entityRef,\n event: {\n id: `gitlab-mr:${project}:${iid}:${mrEvent?.object_attributes?.updated_at || mrEvent?.object_attributes?.action || \"evt\"}`,\n payload: {\n mr: {\n iid,\n title: ctx.title,\n description: ctx.description,\n url: ctx.url,\n action: ctx.action,\n state: ctx.state,\n source_branch: ctx.source_branch,\n target_branch: ctx.target_branch,\n labels,\n assignees,\n reviewers,\n },\n project,\n channel: \"gitlab\",\n chatId: `${project}:merge_request:${iid}`,\n iid,\n title: ctx.title,\n description: ctx.description,\n url: ctx.url,\n action: ctx.action,\n state: ctx.state,\n source_branch: ctx.source_branch,\n target_branch: ctx.target_branch,\n labels,\n },\n },\n })\n return {}\n },\n\n // GitLab note events — comments on issues / MRs / commits. Mirrors\n // on:gitlab-mr; the gitlab adapter fires this in handleNote AFTER the\n // project rule's note clause has approved the event AND cascade-\n // prevention checks (AgentX marker, sentNoteIds, isBotUser) have\n // passed. Workflow subscribers can branch off mentions / noteableType\n // (e.g. mr-fix-loop fires on noteableType=merge_request when a\n // reviewer's comment matches a request-changes pattern).\n \"on:gitlab-note\": async (ctx) => {\n const project = ctx.project as string | undefined\n const noteId = ctx.noteId as string | undefined\n const noteableType = ctx.noteableType as string | undefined\n const noteableIid = ctx.noteableIid as string | undefined\n const text = ctx.text as string | undefined\n const author = ctx.authorUsername as string | undefined\n const mentions = (ctx.mentions ?? []) as string[]\n if (!project || !noteId || !noteableType || !noteableIid) return {}\n\n const entityRef: EntityRef =\n noteableType === \"merge_request\"\n ? { backend: \"gitlab\", id: `${project}!${noteableIid}` }\n : { backend: \"gitlab\", id: `${project}#${noteableIid}` }\n\n await dispatcher.dispatch({\n // The trigger field carries only structural matchers; per-event\n // payload (mentions, etc.) lives in `event.payload`. Workflows\n // that need to filter on mentions look at `{{trigger.mentions}}`\n // resolved from the payload, not from this trigger object.\n trigger: { source: \"gitlab-note\", project },\n entityRef,\n event: {\n id: `gitlab-note:${project}:${noteableType}:${noteableIid}:${noteId}`,\n payload: {\n note: {\n id: noteId,\n text,\n author,\n mentions,\n noteableType,\n noteableIid,\n noteableTitle: ctx.noteableTitle,\n },\n project,\n channel: \"gitlab\",\n chatId: `${project}:${noteableType}:${noteableIid}`,\n noteId,\n noteableType,\n noteableIid,\n text,\n author,\n mentions,\n },\n },\n })\n return {}\n },\n\n // GitLab pipeline events (success / failed / canceled). Scoped to\n // MR-linked pipelines — pushes to branches without an MR don't enter\n // the workflow dispatcher.\n \"on:gitlab-pipeline\": async (ctx) => {\n const project = ctx.project as string | undefined\n const pipelineId = ctx.pipelineId as string | number | undefined\n const status = ctx.status as string | undefined\n const ref = ctx.ref as string | undefined\n if (!project || !status) return {}\n\n const raw = ctx.raw as any\n const mrIid = raw?.merge_request?.iid\n if (!mrIid) return {}\n\n const entityRef: EntityRef = { backend: \"gitlab\", id: `${project}!${mrIid}` }\n await dispatcher.dispatch({\n trigger: { source: \"gitlab-pipeline\", project },\n entityRef,\n event: {\n id: `gitlab-pipeline:${project}:${pipelineId}:${status}`,\n payload: {\n pipeline: { id: pipelineId, status, ref },\n project,\n channel: \"gitlab\",\n chatId: `${project}:merge_request:${mrIid}`,\n },\n },\n })\n return {}\n },\n }\n}\n\nfunction channelToTriggerSource(channel: string): string | null {\n switch (channel) {\n case \"telegram\": return \"telegram-message\"\n case \"whatsapp\": return \"whatsapp-message\"\n case \"discord\": return \"discord-message\"\n case \"slack\": return \"slack-message\"\n default: return null\n }\n}\n\nfunction buildChannelTriggerPayload(msg: IncomingMessage): Record<string, unknown> {\n const chatId = msg.group?.id ?? msg.sender.id\n return {\n channel: msg.channel,\n chatId,\n accountId: msg.accountId,\n text: msg.text,\n fromJid: msg.sender.id,\n sender: {\n id: msg.sender.id,\n name: msg.sender.name,\n username: msg.sender.username,\n },\n contact: msg.channelMeta?.facts ? { facts: msg.channelMeta.facts } : undefined,\n group: msg.group ? { id: msg.group.id, name: msg.group.name } : undefined,\n channelMeta: msg.channelMeta,\n replyTo: msg.replyTo,\n replyToText: msg.replyToText,\n media: msg.media,\n event: { id: msg.id, timestamp: msg.timestamp?.toISOString?.() ?? new Date().toISOString() },\n }\n}\n","// --- Loop guard for event-triggered workflows (trigger.hook) ---\n//\n// Generator/critic routine pairs can loop: a routine posts as its bot\n// identity, the forge emits an event for that post, and the same routine\n// (or its partner) wakes up again. Three gates, evaluated after the\n// regular trigger filters in triggers.ts:\n//\n// 1. Self-authored skip (opt-in via `filter.skipSelfAuthored: true`).\n// Off by default because label-driven lifecycle workflows react to\n// their own agent's transitions on purpose. The event author is the forge\n// identity of an agent the workflow itself runs. Identity comes from\n// `ctx.authorAgent` (stamped by the GitLab adapter from its\n// token-resolved username map or the agentx comment signature) or from\n// the configured forge usernames of the workflow's agents.\n// 2. `filter.ignoreAuthors` — explicit usernames to skip.\n// 3. `filter.maxFiresPerTarget: { count, windowMinutes }` — at most\n// `count` fires of this workflow for the same issue/MR/PR inside a\n// sliding window. Stops two-routine ping-pong that (1) can't see\n// because each routine is woken by the *other* routine's identity.\n\nimport type { Workflow } from \"./types\"\n\nexport interface LoopGuardFilter {\n ignoreAuthors?: string[]\n skipSelfAuthored?: boolean\n maxFiresPerTarget?: { count?: number; windowMinutes?: number }\n}\n\nexport type LoopGuardVerdict =\n | { skip: false }\n | { skip: true; reason: \"self-authored\" | \"ignored-author\" | \"chain-limit\"; detail: string }\n\nconst norm = (u: string): string => u.trim().toLowerCase().replace(/^@/, \"\")\n\n/** Event author username, normalized. Adapters stamp `author`; older\n * gitlab-note contexts carry `authorUsername`; GitHub contexts fall back to\n * the raw webhook `sender.login`. */\nexport function eventAuthor(ctx: Record<string, unknown>): string | undefined {\n const direct = typeof ctx.author === \"string\" ? ctx.author\n : typeof ctx.authorUsername === \"string\" ? ctx.authorUsername\n : undefined\n if (direct) return norm(direct)\n const payload = ctx.payload as { sender?: { login?: unknown } } | undefined\n const login = payload?.sender?.login\n return typeof login === \"string\" && login ? norm(login) : undefined\n}\n\n/** Stable key for the thing the event is about, shared across event types\n * so a note on MR !5 and an update of MR !5 count against the same target.\n * Undefined when the event has no issue/MR/PR (pushes, pipelines, n8n). */\nexport function eventTargetKey(event: string, ctx: Record<string, unknown>): string | undefined {\n const project = typeof ctx.project === \"string\" ? ctx.project : \"\"\n if (event === \"on:gitlab-issue\" || event === \"on:gitlab-mr\") {\n if (!project || ctx.iid == null) return undefined\n return `gitlab:${project}${event === \"on:gitlab-issue\" ? \"#\" : \"!\"}${String(ctx.iid)}`\n }\n if (event === \"on:gitlab-note\") {\n const iid = ctx.noteableIid != null ? String(ctx.noteableIid) : \"\"\n const type = typeof ctx.noteableType === \"string\" ? ctx.noteableType : \"\"\n if (!project || !iid || !type) return undefined\n return `gitlab:${project}${type === \"issue\" ? \"#\" : \"!\"}${iid}`\n }\n if (event === \"on:github-issue\" || event === \"on:github-pr\") {\n const payload = (ctx.payload ?? {}) as {\n repository?: { full_name?: unknown }\n issue?: { number?: unknown }\n pull_request?: { number?: unknown }\n }\n const repo = typeof ctx.repo === \"string\" ? ctx.repo : payload.repository?.full_name\n const num = ctx.number ?? payload.issue?.number ?? payload.pull_request?.number\n if (typeof repo !== \"string\" || !repo || num == null) return undefined\n // Issues and PRs share one number space on GitHub, so one separator.\n return `github:${repo}#${String(num)}`\n }\n return undefined\n}\n\n/** Agent ids a workflow acts as — every `agent` node's config.agentId. */\nexport function workflowAgentIds(wf: Pick<Workflow, \"nodes\">): string[] {\n const out = new Set<string>()\n for (const n of wf.nodes) {\n const id = n.type === \"agent\" ? (n.config as { agentId?: unknown }).agentId : undefined\n if (typeof id === \"string\" && id) out.add(id)\n }\n return [...out]\n}\n\n/** Sliding-window fire counter keyed by `<workflowId>|<target>`.\n * In-memory on purpose: a daemon restart resets the windows, which at worst\n * lets a looping pair run one more window before it is capped again. The\n * map is bounded — oldest keys are evicted past `maxKeys`. */\nexport class FireWindow {\n private fires = new Map<string, number[]>()\n constructor(private readonly maxKeys = 2000) {}\n\n /** Record a fire unless `count` fires already landed inside the window.\n * Returns false (and records nothing) when the limit is hit. */\n tryFire(key: string, count: number, windowMs: number, now = Date.now()): boolean {\n const recent = (this.fires.get(key) ?? []).filter((t) => now - t < windowMs)\n if (recent.length >= count) {\n this.fires.set(key, recent)\n return false\n }\n recent.push(now)\n // Re-insert so Map iteration order approximates least-recently-fired.\n this.fires.delete(key)\n this.fires.set(key, recent)\n while (this.fires.size > this.maxKeys) {\n const oldest = this.fires.keys().next().value\n if (oldest === undefined) break\n this.fires.delete(oldest)\n }\n return true\n }\n\n get size(): number { return this.fires.size }\n}\n\nexport interface LoopGuardDeps {\n /** Configured forge usernames for an agent (GitLab/GitHub agentMappings). */\n forgeUsernames?: (agentId: string) => string[]\n fireWindow: FireWindow\n /** Log sink; `once` keys de-duplicate repeated notices per process. */\n log: (msg: string) => void\n logged?: Set<string>\n now?: () => number\n}\n\nfunction logOnce(deps: LoopGuardDeps, key: string, msg: string): void {\n const seen = deps.logged ?? (deps.logged = new Set())\n if (seen.has(key)) return\n seen.add(key)\n deps.log(msg)\n}\n\n/** Decide whether a trigger.hook fire should be skipped as a loop.\n * The chain-limit counter is only advanced when the verdict is \"fire\". */\nexport function checkLoopGuard(\n wf: Pick<Workflow, \"id\" | \"nodes\">,\n event: string,\n ctx: Record<string, unknown>,\n filter: LoopGuardFilter | undefined,\n deps: LoopGuardDeps,\n): LoopGuardVerdict {\n const author = eventAuthor(ctx)\n\n if (author && Array.isArray(filter?.ignoreAuthors) && filter!.ignoreAuthors.length > 0) {\n if (filter!.ignoreAuthors.map(norm).includes(author)) {\n return { skip: true, reason: \"ignored-author\", detail: `author @${author} is in filter.ignoreAuthors` }\n }\n }\n\n if (filter?.skipSelfAuthored === true) {\n const agents = workflowAgentIds(wf)\n const authorAgent = typeof ctx.authorAgent === \"string\" ? ctx.authorAgent : undefined\n if (authorAgent && agents.includes(authorAgent)) {\n return { skip: true, reason: \"self-authored\", detail: `author @${author ?? \"?\"} is agent \"${authorAgent}\" which this workflow runs` }\n }\n if (author) {\n const own = new Set(agents.flatMap((a) => (deps.forgeUsernames?.(a) ?? []).map(norm)))\n if (own.has(author)) {\n return { skip: true, reason: \"self-authored\", detail: `author @${author} is this workflow's own bot identity` }\n }\n if (agents.length === 0) {\n logOnce(deps, `noagents:${wf.id}`, `[workflows] ${wf.id} has no agent node — self-authored skip can't resolve a bot identity; only filter.ignoreAuthors applies`)\n } else if (own.size === 0 && !(\"authorAgent\" in ctx)) {\n logOnce(deps, `noident:${wf.id}:${event}`, `[workflows] ${wf.id} no forge username known for agent(s) ${agents.join(\", \")} on ${event} — self-authored skip inactive; only filter.ignoreAuthors applies`)\n }\n }\n }\n\n const limit = filter?.maxFiresPerTarget\n if (limit && typeof limit.count === \"number\" && limit.count > 0) {\n const target = eventTargetKey(event, ctx)\n if (target) {\n const windowMinutes = typeof limit.windowMinutes === \"number\" && limit.windowMinutes > 0 ? limit.windowMinutes : 60\n const now = deps.now?.() ?? Date.now()\n if (!deps.fireWindow.tryFire(`${wf.id}|${target}`, limit.count, windowMinutes * 60_000, now)) {\n return {\n skip: true,\n reason: \"chain-limit\",\n detail: `${target} already fired ${limit.count}x within ${windowMinutes}m (filter.maxFiresPerTarget)`,\n }\n }\n } else {\n logOnce(deps, `notarget:${wf.id}:${event}`, `[workflows] ${wf.id} filter.maxFiresPerTarget has no target key for ${event} — limit not applied`)\n }\n }\n\n return { skip: false }\n}\n","import { getNextCronDate } from \"@/crons/scheduler\"\nimport type { WorkflowDispatcher } from \"./dispatcher\"\nimport type { WorkflowStore } from \"./store\"\nimport type { HookEvent } from \"@/hooks/types\"\nimport type { HookRegistry } from \"@/hooks\"\nimport { checkLoopGuard, FireWindow, type LoopGuardFilter } from \"./loop-guard\"\n\n// --- Trigger wiring for Phase 3 ---\n//\n// Workflows with `trigger.cron` or `trigger.hook` nodes don't map to\n// channel events, so the regular hook subscribers in hooks.ts don't fire\n// them. This module wires those two sources:\n//\n// - trigger.cron: compute next fire time via getNextCronDate + chain\n// setTimeout. On fire, call dispatcher with a synthetic trigger event.\n// Each workflow gets its own timer loop; rescheduled on every fire.\n//\n// - trigger.hook: register a HookRegistry handler on whichever on:* event\n// the trigger node configures. On fire, map the hook context payload\n// directly into a trigger event.\n//\n// Called once from daemon boot after the dispatcher is constructed. Teardown\n// isn't wired for v1 — daemon restart clears timers.\n\nexport interface CronTriggerOptions {\n workflowId: string\n triggerNodeId: string\n spec: string\n timezone: string\n}\n\nexport function startWorkflowTriggers(args: {\n store: WorkflowStore\n dispatcher: WorkflowDispatcher\n hooks: HookRegistry\n log: (msg: string) => void\n /** Configured forge usernames (GitLab/GitHub agentMappings) for an agent.\n * Feeds the loop guard's self-authored skip; see loop-guard.ts. */\n forgeUsernames?: (agentId: string) => string[]\n}): { cronTimers: number; hookSubscribers: number } {\n const workflows = args.store.list()\n // Loop-guard state shared by every trigger.hook subscriber registered in\n // this call. In-memory; resets on daemon restart (see FireWindow).\n const loopGuard = { fireWindow: new FireWindow(), log: args.log, forgeUsernames: args.forgeUsernames, logged: new Set<string>() }\n let cronTimers = 0\n let hookSubscribers = 0\n\n for (const wf of workflows) {\n // Lifecycle gate: don't register triggers for non-active workflows. A\n // disabled or quarantined workflow's cron must not fire and its\n // trigger.hook subscribers must not run. State changes that flip a\n // workflow back to `active` re-run this registrar.\n if (wf.state && wf.state !== \"active\") {\n args.log(`[workflows] ${wf.id} state=${wf.state} — skipping trigger registration`)\n continue\n }\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger) continue\n\n if (trigger.type === \"trigger.cron\") {\n const cfg = trigger.config as { spec?: string; timezone?: string }\n if (!cfg.spec) { args.log(`[workflows] ${wf.id} trigger.cron missing config.spec — skipping`); continue }\n scheduleCron({\n workflowId: wf.id,\n triggerNodeId: trigger.id,\n spec: cfg.spec,\n timezone: cfg.timezone ?? \"UTC\",\n }, args.dispatcher, args.log)\n cronTimers++\n } else if (trigger.type === \"trigger.hook\") {\n const cfg = trigger.config as {\n event?: string\n passthrough?: boolean\n filter?: {\n /** Restrict on:n8n to specific topics — the last path segment of\n * POST /webhook/n8n/<topic>. Without it EVERY workflow listening\n * on on:n8n runs for every call, whatever the caller meant. */\n topic?: string[]\n /** Restrict to specific actions (gitlab-issue / gitlab-mr).\n * Example: [\"open\", \"reopen\"] keeps the workflow off update/close events. */\n action?: string[]\n /** Restrict on:gitlab-note to comments that @-mention at least one of\n * these usernames. Without this, every note on the project fires the\n * workflow — including ones that don't address any agent. Match is\n * case-insensitive on the bare username (no leading @). */\n mentions?: string[]\n /** Restrict on:gitlab-note to a noteable type — \"merge_request\" or\n * \"issue\". Without this, an mr-fix-loop whose only gate is\n * `mentions` also claims issue comments that @-mention the coder,\n * suppresses the legacy @-mention reply path, then no-ops (the\n * prompt bails on issues) — so the comment gets swallowed with no\n * reply. Gating here lets issue notes fall through to the legacy\n * resolver. Match is case-insensitive on `ctx.noteableType`. */\n noteableType?: string[]\n /** Fire only when one of these usernames was newly added as an\n * assignee. The adapter computes the diff from\n * `changes.assignees.{previous,current}` and exposes it as\n * `ctx.assigneesAdded`. Lets a \"coder-pickup\" workflow target the\n * moment a coder is assigned without the workflow needing to\n * re-derive the diff in its prompt. Username comparison is\n * case-insensitive, leading @ stripped. */\n assigneesAdded?: string[]\n /** Same shape, for MR reviewers. `ctx.reviewersAdded`. */\n reviewersAdded?: string[]\n /** Fire only when one of these labels was newly added on an\n * update event. The adapter computes the diff from\n * `changes.labels.{previous,current}` and exposes it as\n * `ctx.labelsAdded`. Labels are matched case-insensitive. */\n labelsAdded?: string[]\n } & LoopGuardFilter\n // LoopGuardFilter adds (see loop-guard.ts):\n // ignoreAuthors — skip events authored by these usernames\n // skipSelfAuthored — skip events authored by this workflow's own agents\n // maxFiresPerTarget — { count, windowMinutes } per issue/MR/PR\n }\n if (!cfg.event || !cfg.event.startsWith(\"on:\")) {\n args.log(`[workflows] ${wf.id} trigger.hook config.event must start with \"on:\" — skipping`)\n continue\n }\n args.hooks.registerHandler(cfg.event as HookEvent, `workflows:${wf.id}`, async (ctx) => {\n try {\n // Project-scope gate. The hook subscriber is registered per-workflow,\n // so we go direct (skipping matchByTrigger) — but the workflow's\n // top-level `project:` still has to be honored, otherwise an\n // on:gitlab-issue from project A reaches every workflow tagged\n // for project B. matchByTrigger has the same gate; this is the\n // companion check on the per-workflow hook path.\n const ctxProject = typeof ctx.project === \"string\" ? ctx.project : undefined\n if (wf.project && ctxProject && wf.project !== ctxProject) return {}\n // Trigger-level filters. These move the \"ONLY ACT when …; exit\n // cleanly otherwise\" prompt-level guard up to the dispatch gate,\n // which means the agent isn't woken up + billed for a turn just\n // to print \"exit cleanly\".\n if (cfg.filter) {\n // filter.topic — on:n8n topic gate. The topic is the path segment\n // n8n posted to, so this is what makes one URL address one\n // workflow instead of waking all of them.\n if (Array.isArray(cfg.filter.topic) && cfg.filter.topic.length > 0) {\n const topic = typeof ctx.topic === \"string\" ? ctx.topic.toLowerCase() : undefined\n if (!topic || !cfg.filter.topic.map((t) => t.toLowerCase()).includes(topic)) return {}\n }\n // filter.action — gitlab-issue / gitlab-mr action gate.\n if (Array.isArray(cfg.filter.action) && cfg.filter.action.length > 0) {\n const action = typeof ctx.action === \"string\" ? ctx.action : undefined\n if (!action || !cfg.filter.action.includes(action)) return {}\n }\n // filter.mentions — gitlab-note mention gate. Without this, the\n // workflow's hard-coded `agentId` runs no matter who the comment\n // @-mentioned, so e.g. initech-mr-fix-loop spawns initech-v2 even on a\n // comment addressed @pm-initech. The legacy @-mention resolver still\n // routes those to the right agent on the second pass when the\n // workflow doesn't claim the event.\n if (Array.isArray(cfg.filter.mentions) && cfg.filter.mentions.length > 0) {\n const ctxMentions = Array.isArray(ctx.mentions)\n ? (ctx.mentions as string[]).map((m) => m.toLowerCase().replace(/^@/, \"\"))\n : []\n const wanted = cfg.filter.mentions.map((m) => m.toLowerCase().replace(/^@/, \"\"))\n const hit = wanted.some((w) => ctxMentions.includes(w))\n if (!hit) return {}\n }\n // filter.noteableType — gitlab-note type gate. Keeps an\n // mr-fix-loop off issue comments so they fall through to the\n // legacy @-mention reply path instead of being claimed + no-op'd.\n if (Array.isArray(cfg.filter.noteableType) && cfg.filter.noteableType.length > 0) {\n const ctxType = typeof ctx.noteableType === \"string\" ? ctx.noteableType.toLowerCase() : undefined\n const wanted = cfg.filter.noteableType.map((t) => t.toLowerCase())\n if (!ctxType || !wanted.includes(ctxType)) return {}\n }\n // filter.assigneesAdded — coder-pickup-style trigger. Fires only\n // when one of the listed usernames appears in ctx.assigneesAdded\n // (computed by the gitlab adapter from changes.assignees diff).\n if (Array.isArray(cfg.filter.assigneesAdded) && cfg.filter.assigneesAdded.length > 0) {\n const ctxAdded = Array.isArray(ctx.assigneesAdded)\n ? (ctx.assigneesAdded as string[]).map((u) => u.toLowerCase().replace(/^@/, \"\"))\n : []\n const wanted = cfg.filter.assigneesAdded.map((u) => u.toLowerCase().replace(/^@/, \"\"))\n const hit = wanted.some((w) => ctxAdded.includes(w))\n if (!hit) return {}\n }\n // filter.reviewersAdded — MR review-on-assignment trigger.\n if (Array.isArray(cfg.filter.reviewersAdded) && cfg.filter.reviewersAdded.length > 0) {\n const ctxAdded = Array.isArray(ctx.reviewersAdded)\n ? (ctx.reviewersAdded as string[]).map((u) => u.toLowerCase().replace(/^@/, \"\"))\n : []\n const wanted = cfg.filter.reviewersAdded.map((u) => u.toLowerCase().replace(/^@/, \"\"))\n const hit = wanted.some((w) => ctxAdded.includes(w))\n if (!hit) return {}\n }\n // filter.labelsAdded — fires when a specific label was added on\n // an update event. Useful for \"Blocked\"-label workflows.\n if (Array.isArray(cfg.filter.labelsAdded) && cfg.filter.labelsAdded.length > 0) {\n const ctxAdded = Array.isArray(ctx.labelsAdded)\n ? (ctx.labelsAdded as string[]).map((l) => l.toLowerCase())\n : []\n const wanted = cfg.filter.labelsAdded.map((l) => l.toLowerCase())\n const hit = wanted.some((w) => ctxAdded.includes(w))\n if (!hit) return {}\n }\n }\n // Loop guard — runs after the filters above so the chain-limit\n // counter only advances for events this workflow would act on.\n // Every guard skip still claims the event (unless passthrough) so\n // the adapter's fallback — default-agent route or legacy @-mention\n // dispatch — doesn't spawn the agent behind the guard's back.\n const guard = checkLoopGuard(wf, cfg.event!, ctx, cfg.filter, loopGuard)\n if (guard.skip) {\n args.log(`[workflows] ${wf.id} skipping ${cfg.event} (${guard.reason}): ${guard.detail}`)\n if (cfg.passthrough) return {}\n const prev = (ctx as { __workflowClaimed?: unknown }).__workflowClaimed\n return { modified: { __workflowClaimed: [...(Array.isArray(prev) ? prev as string[] : []), wf.id] } }\n }\n // Go direct: this hook subscriber is registered per-workflow,\n // so we already know which workflow to fire. Going through\n // dispatch() + matchByTrigger() would drop the event unless the\n // workflow's trigger.hook node had a matching `source` field —\n // which users don't set (the event IS the filter).\n const entityId = entityKeyFromHookContext(wf.id, cfg.event!, ctx)\n await args.dispatcher.dispatchWorkflow({\n workflowId: wf.id,\n entityRef: { backend: \"hook\", id: entityId },\n event: {\n id: `hook:${cfg.event}:${Date.now().toString(36)}`,\n payload: { hookEvent: cfg.event, ...ctx },\n },\n })\n // Signal claim. When a workflow claims a project-scoped event,\n // the GitLab adapter (and other future adapters) must suppress\n // their legacy \"@-mention dispatch\" / \"default-route dispatch\"\n // path — otherwise the same agent gets spawned twice for one\n // event (once via the workflow run, once via the legacy\n // resolver). Read from `combinedModified.__workflowClaimed`\n // already accumulated by earlier per-workflow handlers; append\n // this workflow's id.\n // Opt-out: `trigger.config.passthrough = true` — the workflow is\n // observability-only and the legacy reply should still fire.\n if (cfg.passthrough) return {}\n const prevClaimed = (ctx as { __workflowClaimed?: unknown }).__workflowClaimed\n const claimedList = Array.isArray(prevClaimed) ? (prevClaimed as string[]) : []\n return { modified: { __workflowClaimed: [...claimedList, wf.id] } }\n } catch (e: any) {\n args.log(`[workflows] ${wf.id} hook dispatch failed: ${e.message}`)\n return {}\n }\n }, 60)\n hookSubscribers++\n }\n }\n\n return { cronTimers, hookSubscribers }\n}\n\n/** Derive a per-event entity id for a hook fire. Two unrelated issues\n * (or MRs, or messages) MUST resolve to different entities — otherwise\n * the second one collides with the first's paused/running run and\n * either gets dropped or hijacks the paused state. Falls back to a\n * per-fire unique id so unknown hook shapes never silently collide. */\nfunction entityKeyFromHookContext(workflowId: string, event: string, ctx: Record<string, unknown>): string {\n const project = typeof ctx.project === \"string\" ? ctx.project : \"\"\n const iid = ctx.iid != null ? String(ctx.iid) : \"\"\n if (event === \"on:gitlab-issue\" && project && iid) return `${workflowId}:${event}:${project}#${iid}`\n if (event === \"on:gitlab-mr\" && project && iid) return `${workflowId}:${event}:${project}!${iid}`\n if (event === \"on:gitlab-pipeline\" && project) {\n const pid = ctx.pipelineId != null ? String(ctx.pipelineId) : \"\"\n if (pid) return `${workflowId}:${event}:${project}@pipeline:${pid}`\n }\n // Unknown / shapeless hook payloads: per-fire entity so each fire spawns\n // its own run rather than colliding on a single shared key. Authors who\n // want stickiness can move to a custom matcher later.\n return `${workflowId}:${event}:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction scheduleCron(\n opts: CronTriggerOptions,\n dispatcher: WorkflowDispatcher,\n log: (msg: string) => void,\n): void {\n const tick = () => {\n let next: Date\n try { next = getNextCronDate(opts.spec, new Date(), opts.timezone) }\n catch (e: any) { log(`[workflows] ${opts.workflowId} cron spec invalid: ${e.message}`); return }\n\n const delay = Math.max(1000, next.getTime() - Date.now())\n setTimeout(() => {\n void (async () => {\n const eventId = `cron:${opts.workflowId}:${next.toISOString()}`\n try {\n await dispatcher.dispatchWorkflow({\n workflowId: opts.workflowId,\n trigger: { source: \"cron\" },\n // Each scheduled fire gets a distinct entity so multiple runs\n // can coexist. If authors want a single \"rolling\" entity keyed\n // by the workflow, they can override via a transform node.\n entityRef: { backend: \"cron\", id: `${opts.workflowId}@${next.toISOString()}` },\n event: {\n id: eventId,\n payload: { now: next.toISOString(), spec: opts.spec, workflowId: opts.workflowId },\n },\n })\n } catch (e: any) {\n log(`[workflows] ${opts.workflowId} cron dispatch failed: ${e.message}`)\n } finally {\n // Reschedule for the next fire window regardless of success.\n tick()\n }\n })()\n }, delay)\n }\n tick()\n}\n","import { createHash } from \"crypto\"\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { z } from \"zod\"\nimport { runAction } from \"@/actions/runner\"\nimport { ActionStore } from \"@/actions/store\"\nimport type { ActionRunResult } from \"@/actions/types\"\nimport type { WorkflowDispatcher } from \"./dispatcher\"\nimport { conditionMatches, getByPath } from \"./engine\"\nimport type { WorkflowStore } from \"./store\"\nimport { conditionSchema, type Workflow } from \"./types\"\n\n// --- trigger.poll ---\n//\n// Runs a registered action on an interval and starts one run per NEW item\n// it prints (one JSON object per line). The engine keeps the \"already\n// seen\" keys, so the command can be a plain \"list the latest N\" call.\n//\n// - Seen keys live in <workflows dir>/_poll/<workflowId>.json and survive\n// a restart. A key is written BEFORE its run is dispatched: an item\n// starts at most one run, even if the daemon dies mid-poll.\n// - The first poll (no state, or actionId/key changed) only records a\n// baseline; old items start nothing.\n// - A poll with nothing new dispatches nothing, so it leaves no run record.\n// - A failing or timed-out command is logged and retried on the next tick.\n// The action must exit non-zero when its source fails: an empty success\n// on the first poll records an empty baseline.\n//\n// Timers follow the workflow files: sync() starts a timer for every active\n// trigger.poll workflow and stops the ones that are gone, disabled or\n// quarantined. Each tick re-reads the workflow, so config edits apply on\n// the next poll.\n\nexport const pollConfigSchema = z.object({\n /** Registered action (.agentx/actions/<id>.json) that prints the items. */\n actionId: z.string().min(1),\n everySeconds: z.number().int().min(5).max(86_400).default(60),\n /** Dotted path of the field that identifies an item. */\n key: z.string().min(1).default(\"id\"),\n /** Same condition kinds as `branch`, paths relative to the item. All\n * must match. A filtered-out item is still recorded as seen. */\n filter: z.array(conditionSchema).default([]),\n /** Most runs one poll may start. The rest stay unseen for the next poll. */\n maxPerPoll: z.number().int().min(1).max(500).default(20),\n})\nexport type PollConfig = z.infer<typeof pollConfigSchema>\n\n/** Seen keys kept per workflow. Keys still in the command's output are\n * never dropped, whatever the count. */\nconst MAX_SEEN = 5000\n\ninterface PollState { actionId: string; key: string; seen: string[] }\n\nexport interface PollTriggersOptions {\n store: Pick<WorkflowStore, \"list\" | \"get\" | \"baseDir\">\n dispatcher: Pick<WorkflowDispatcher, \"dispatchWorkflow\">\n log: (msg: string) => void\n /** Runs the poll action. Default: look it up in the action registry. */\n runAction?: (actionId: string) => Promise<ActionRunResult>\n}\n\n/** How often start() re-reads the workflow files, so a workflow saved,\n * enabled or disabled by any process (editor, CLI, a copied file) is\n * picked up without a restart. */\nconst RESYNC_SECONDS = 30\n\nasync function runRegisteredAction(actionId: string): Promise<ActionRunResult> {\n const action = new ActionStore().get(actionId)\n if (!action) throw new Error(`no action \"${actionId}\" in registry`)\n return runAction(action)\n}\n\n/** Parse the command's stdout: one JSON object per line. Lines that are not\n * an object, or carry no string/number at `keyPath`, are skipped. */\nexport function parsePollItems(output: string, keyPath: string): { items: Array<{ key: string; value: Record<string, unknown> }>; skipped: number } {\n const items: Array<{ key: string; value: Record<string, unknown> }> = []\n let skipped = 0\n for (const line of output.split(\"\\n\")) {\n const text = line.trim()\n if (!text) continue\n let value: unknown\n try { value = JSON.parse(text) } catch { skipped++; continue }\n if (!value || typeof value !== \"object\" || Array.isArray(value)) { skipped++; continue }\n const key = getByPath(value, keyPath)\n if ((typeof key !== \"string\" || !key) && typeof key !== \"number\") { skipped++; continue }\n items.push({ key: String(key), value: value as Record<string, unknown> })\n }\n return { items, skipped }\n}\n\n/** Entity id of one item's run. The run index keeps only [a-zA-Z0-9._:#@-]\n * and the first 200 characters, so a key outside that is hashed: two keys\n * must never share an index entry. A plain key holds no \"#\", so it cannot\n * equal a hashed one. */\nexport function pollEntityId(workflowId: string, key: string): string {\n const plain = `${workflowId}:${key}`\n if (/^[a-zA-Z0-9._:@-]+$/.test(key) && plain.length <= 200) return plain\n return `${workflowId}:#${createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 32)}`\n}\n\nexport class PollTriggers {\n private readonly timers = new Map<string, ReturnType<typeof setTimeout>>()\n /** Last warning per workflow, so a standing problem logs once, not every tick. */\n private readonly warned = new Map<string, string>()\n private readonly stateDir: string\n private readonly runAction: (actionId: string) => Promise<ActionRunResult>\n private resync?: ReturnType<typeof setInterval>\n\n constructor(private readonly opts: PollTriggersOptions) {\n this.stateDir = resolve(opts.store.baseDir, \"_poll\")\n this.runAction = opts.runAction ?? runRegisteredAction\n }\n\n /** sync() now and every RESYNC_SECONDS. Returns the number polling. */\n start(): number {\n this.resync ??= setInterval(() => {\n try { this.sync() } catch (e: any) { this.opts.log(`[workflows] poll sync failed: ${e.message}`) }\n }, RESYNC_SECONDS * 1000)\n return this.sync()\n }\n\n /** Match timers to the workflows on disk. Returns the number polling. */\n sync(): number {\n const wanted = new Map<string, PollConfig>()\n for (const wf of this.opts.store.list()) {\n const cfg = this.configOf(wf)\n if (cfg) wanted.set(wf.id, cfg)\n }\n for (const [id, timer] of this.timers) {\n if (wanted.has(id)) continue\n clearTimeout(timer)\n this.timers.delete(id)\n this.opts.log(`[workflows] ${id} poll stopped`)\n }\n for (const [id, cfg] of wanted) {\n if (this.timers.has(id)) continue\n this.schedule(id, cfg.everySeconds)\n this.opts.log(`[workflows] ${id} polls action \"${cfg.actionId}\" every ${cfg.everySeconds}s`)\n }\n return this.timers.size\n }\n\n stop(): void {\n if (this.resync) clearInterval(this.resync)\n this.resync = undefined\n for (const timer of this.timers.values()) clearTimeout(timer)\n this.timers.clear()\n }\n\n /** One poll. Returns the number of runs started. */\n async pollOnce(workflowId: string): Promise<number> {\n const wf = this.opts.store.get(workflowId)\n const cfg = wf ? this.configOf(wf) : null\n if (!cfg) return 0\n\n let result: ActionRunResult\n try { result = await this.runAction(cfg.actionId) }\n catch (e: any) { this.warnOnce(workflowId, `poll failed: ${e.message}`); return 0 }\n if (!result.ok) {\n this.warnOnce(workflowId, `poll failed (status=${result.status ?? \"?\"}): ${(result.errors ?? \"\").slice(0, 200)}`)\n return 0\n }\n\n const { items, skipped } = parsePollItems(result.output, cfg.key)\n this.warnOnce(workflowId, skipped > 0 ? `poll skipped ${skipped} line(s) that are not a JSON object with \"${cfg.key}\"` : \"\")\n\n const current = new Set(items.map((i) => i.key))\n const state = this.readState(workflowId)\n if (!state || state.actionId !== cfg.actionId || state.key !== cfg.key) {\n this.writeState(workflowId, { actionId: cfg.actionId, key: cfg.key, seen: [...current] }, current)\n this.opts.log(`[workflows] ${workflowId} poll baseline: ${current.size} existing item(s) recorded, no runs started`)\n return 0\n }\n\n const seen = new Set(state.seen)\n let started = 0\n let dirty = false\n for (const item of items) {\n if (seen.has(item.key)) continue\n const matches = cfg.filter.every((c) => conditionMatches(c, item.value))\n if (matches && started >= cfg.maxPerPoll) continue\n seen.add(item.key)\n state.seen.push(item.key)\n if (!matches) { dirty = true; continue }\n // Record the key first: a crash from here on loses the item rather\n // than running it twice.\n this.writeState(workflowId, state, current)\n dirty = false\n try {\n const { run } = await this.opts.dispatcher.dispatchWorkflow({\n workflowId,\n trigger: { source: \"poll\" },\n entityRef: { backend: \"poll\", id: pollEntityId(workflowId, item.key) },\n event: { id: `poll:${workflowId}:${item.key}`, payload: item.value },\n })\n if (run) started++\n else this.opts.log(`[workflows] ${workflowId} poll item \"${item.key}\" started no run`)\n } catch (e: any) {\n this.opts.log(`[workflows] ${workflowId} poll dispatch failed for \"${item.key}\": ${e.message}`)\n }\n }\n if (dirty) this.writeState(workflowId, state, current)\n return started\n }\n\n private schedule(workflowId: string, everySeconds: number): void {\n const timer = setTimeout(() => {\n void (async () => {\n try { await this.pollOnce(workflowId) }\n catch (e: any) { this.opts.log(`[workflows] ${workflowId} poll failed: ${e.message}`) }\n // Stopped, or restarted by sync() while this poll ran: not our chain.\n if (this.timers.get(workflowId) !== timer) return\n const wf = this.opts.store.get(workflowId)\n const cfg = wf ? this.configOf(wf) : null\n if (cfg) this.schedule(workflowId, cfg.everySeconds)\n else this.timers.delete(workflowId)\n })()\n }, everySeconds * 1000)\n this.timers.set(workflowId, timer)\n }\n\n /** The poll config of an ACTIVE trigger.poll workflow, else null. */\n private configOf(wf: Workflow): PollConfig | null {\n if (wf.state && wf.state !== \"active\") return null\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger || trigger.type !== \"trigger.poll\") return null\n const parsed = pollConfigSchema.safeParse(trigger.config)\n if (!parsed.success) {\n const why = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"config\"}: ${i.message}`).join(\"; \")\n this.warnOnce(wf.id, `trigger.poll config invalid, not polling (${why})`)\n return null\n }\n return parsed.data\n }\n\n private warnOnce(workflowId: string, msg: string): void {\n if (this.warned.get(workflowId) === msg) return\n this.warned.set(workflowId, msg)\n if (msg) this.opts.log(`[workflows] ${workflowId} ${msg}`)\n }\n\n private statePath(workflowId: string): string {\n return resolve(this.stateDir, `${workflowId}.json`)\n }\n\n private readState(workflowId: string): PollState | null {\n const file = this.statePath(workflowId)\n if (!existsSync(file)) return null\n try {\n const raw = JSON.parse(readFileSync(file, \"utf-8\")) as Partial<PollState>\n if (typeof raw.actionId !== \"string\" || typeof raw.key !== \"string\" || !Array.isArray(raw.seen)) return null\n return { actionId: raw.actionId, key: raw.key, seen: raw.seen.map(String) }\n } catch { return null }\n }\n\n /** Atomic write (tmp + rename). Trims `seen` to MAX_SEEN, oldest first,\n * keeping every key the command still prints. */\n private writeState(workflowId: string, state: PollState, current: Set<string>): void {\n let drop = state.seen.length - MAX_SEEN\n if (drop > 0) state.seen = state.seen.filter((k) => current.has(k) || drop-- <= 0)\n mkdirSync(this.stateDir, { recursive: true })\n const file = this.statePath(workflowId)\n writeFileSync(`${file}.tmp`, JSON.stringify(state))\n renameSync(`${file}.tmp`, file)\n }\n}\n","import type { Workflow } from \"./types\"\n\n// --- Workflow input resolution for auto-match dispatch ---\n//\n// When workflows.matching.mode === \"auto\" and the matcher picks a workflow,\n// the workflow's trigger.config.inputSchema declares what payload it needs\n// (e.g. project, environment, host, ref). The matcher hands us a free-form\n// message + chatId + agent context; we have to bridge the two.\n//\n// v1 strategy: deterministic only.\n// 1. passthrough — copy any context fields the schema declares (message,\n// agentId, channel, chatId, senderId, senderUsername).\n// 2. chatId parse — known channel composites yield typed fields:\n// \"<group>/<project>:issue:<id>\" → { project, id }\n// \"<group>/<project>:merge_request:<id>\" → { project, id }\n// \"<group>/<project>:pull_request:<id>\" → { project, id }\n// \"<group>/<project>:push:refs/heads/<ref>\" → { project, ref }\n// 3. schema defaults — any remaining gaps fall back to the inputSchema's\n// `default` fields.\n// 4. required check — if any `required` field is still empty, signal to\n// caller (auto-runner falls back to suggest mode + agent execution).\n//\n// LLM-based extraction (passing the message + context through an agent\n// running extract.structured) is the natural follow-on but lives in a\n// separate module so this hot path stays cheap and predictable.\n\nexport interface InputSchemaProperty {\n type?: string\n description?: string\n default?: unknown\n enum?: unknown[]\n}\n\nexport interface InputSchemaShape {\n type?: string\n properties?: Record<string, InputSchemaProperty>\n required?: string[]\n}\n\nexport interface ResolveContext {\n chatId?: string\n message?: string\n agentId?: string\n channel?: string\n senderId?: string\n senderUsername?: string\n}\n\nexport interface ResolveResult {\n inputs: Record<string, unknown>\n missing: string[]\n /** Provenance per-field so log lines can explain what filled what. */\n filledFrom: { passthrough: string[]; chatId: string[]; defaults: string[] }\n}\n\n/** Pull the inputSchema off the workflow's trigger node. Returns null when\n * the workflow is unschematized — caller should pass the matcher bundle\n * through unchanged. */\nexport function getInputSchema(wf: Workflow): InputSchemaShape | null {\n const trig = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n const cfg = trig?.config as { inputSchema?: InputSchemaShape } | undefined\n return cfg?.inputSchema ?? null\n}\n\n/** Best-effort extraction from chatId composites. Only fields that exist\n * in `schema.properties` end up in the result — we don't invent inputs\n * the workflow didn't declare. */\nexport function fillFromChatId(\n chatId: string | undefined,\n schema: InputSchemaShape | null,\n): Record<string, unknown> {\n if (!chatId || !schema?.properties) return {}\n const m = chatId.match(/^([^:]+\\/[^:]+):([^:]+):(.+)$/)\n if (!m) return {}\n const [, project, kind, rest] = m\n const out: Record<string, unknown> = {}\n if (schema.properties.project) out.project = project\n if (kind === \"push\") {\n const refMatch = rest.match(/^refs\\/heads\\/(.+)$/)\n if (refMatch && schema.properties.ref) out.ref = refMatch[1]\n } else {\n // issue / merge_request / pull_request → rest is the numeric id.\n // Architects emit the id under several names depending on platform —\n // populate whichever one(s) the workflow declared.\n const idNum = Number(rest)\n if (Number.isFinite(idNum)) {\n if (schema.properties.id) out.id = idNum\n if (schema.properties.iid) out.iid = idNum\n if (schema.properties.mrId) out.mrId = idNum\n if (schema.properties.issueId) out.issueId = idNum\n if (schema.properties.prId) out.prId = idNum\n }\n }\n return out\n}\n\n/** Fill any not-yet-set field with its inputSchema `default`. */\nexport function applyDefaults(\n schema: InputSchemaShape | null,\n current: Record<string, unknown>,\n): Record<string, unknown> {\n if (!schema?.properties) return current\n const out = { ...current }\n for (const [k, v] of Object.entries(schema.properties)) {\n if (out[k] === undefined && v && \"default\" in v) out[k] = v.default\n }\n return out\n}\n\n/** Names of required fields still missing or empty after merging. */\nexport function missingRequired(\n schema: InputSchemaShape | null,\n populated: Record<string, unknown>,\n): string[] {\n if (!schema?.required || !Array.isArray(schema.required)) return []\n return schema.required.filter((k) => {\n const v = populated[k]\n return v === undefined || v === \"\" || v === null\n })\n}\n\n/** Main entry point. Merges passthrough + chatId-parse + defaults and\n * returns the result plus any required fields still missing. */\nexport function resolveAutoRunInputs(wf: Workflow, ctx: ResolveContext): ResolveResult {\n const schema = getInputSchema(wf)\n if (!schema?.properties) {\n // Unschematized workflow — hand the matcher bundle through. No\n // required check applies.\n const all: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(ctx)) if (v !== undefined) all[k] = v\n return { inputs: all, missing: [], filledFrom: { passthrough: Object.keys(all), chatId: [], defaults: [] } }\n }\n\n const passthrough: Record<string, unknown> = {}\n for (const k of [\"message\", \"agentId\", \"channel\", \"chatId\", \"senderId\", \"senderUsername\"] as const) {\n if (schema.properties[k] && ctx[k] !== undefined) passthrough[k] = ctx[k]\n }\n\n const fromChat = fillFromChatId(ctx.chatId, schema)\n // chatId-derived fields are higher fidelity than passthrough message.\n let merged = { ...passthrough, ...fromChat }\n const beforeDefaults = { ...merged }\n merged = applyDefaults(schema, merged)\n const filledFromDefaults = Object.keys(merged).filter(\n (k) => beforeDefaults[k] === undefined && merged[k] !== undefined,\n )\n\n return {\n inputs: merged,\n missing: missingRequired(schema, merged),\n filledFrom: {\n passthrough: Object.keys(passthrough),\n chatId: Object.keys(fromChat),\n defaults: filledFromDefaults,\n },\n }\n}\n","import { lintWorkflow, workflowSchema, type NodeType, type Workflow, type WorkflowRun } from \"./types\"\n\n// --- Follow-up workflows: what agents, the CLI and the dashboard share (#788) ---\n//\n// workflowHintText the line that tells an agent a saved workflow fits\n// buildWorkflow a workflow from an agent's step list (built on demand)\n// runSummary the owner's one message when a run ends\n// blockedText the owner's message when a step needs them\n// progressGroups running follow-ups grouped by what they concern\n//\n// Pure functions: no I/O, so the daemon, the CLI and tests use them alike.\n\n/** Step types an agent may put in a workflow it builds or proposes. No\n * raw channel sends (`action.send` skips the owner's approval), no\n * shell, HTTP or registered actions: those are the owner's to add from\n * the editor or the CLI. */\nexport const AGENT_STEP_TYPES: ReadonlySet<NodeType> = new Set<NodeType>([\n \"agent\", \"owner.notify\", \"owner.ask\", \"person.message\", \"person.wait\",\n \"branch\", \"transform\", \"timer.boundary\", \"end\",\n])\n\nexport function workflowHintText(wf: Pick<Workflow, \"id\" | \"title\" | \"autoStart\">): string {\n return [\n `[Saved workflow] This request looks like the saved workflow \"${wf.title}\" (${wf.id}).`,\n `If it is, start it with agentx_workflow {action:\"start\", workflowId:\"${wf.id}\", title:\"<the request in a few words>\", tags:[\"client:<name>\"]} instead of doing the steps by hand: AgentX then follows each step, reminds whoever is slow and tells the owner when it is done.`,\n wf.autoStart ? \"It is marked to start without asking.\" : \"AgentX tells the owner which workflow you chose when you start it.\",\n ].join(\" \")\n}\n\nexport interface StepInput {\n id?: unknown\n type?: unknown\n config?: unknown\n}\n\nexport interface BuildInput {\n id: string\n title: string\n description?: string\n steps: unknown\n edges?: unknown\n approval?: unknown\n autoStart?: unknown\n ownerAgent?: string\n tags?: string[]\n}\n\n/** A workflow from a list of steps: a manual trigger first, the steps in\n * order (or joined by `edges` when given), an end last. Every step must\n * be one an agent may use (AGENT_STEP_TYPES). */\nexport function buildWorkflow(input: BuildInput): { ok: true; workflow: Workflow } | { ok: false; error: string } {\n if (!Array.isArray(input.steps) || input.steps.length === 0) return { ok: false, error: \"steps must be a non-empty list of {id, type, config}\" }\n if (input.steps.length > 40) return { ok: false, error: \"a workflow has at most 40 steps\" }\n const nodes: Array<{ id: string; type: string; config: Record<string, unknown> }> = [{ id: \"start\", type: \"trigger.manual\", config: {} }]\n for (const [i, raw] of (input.steps as StepInput[]).entries()) {\n const id = typeof raw?.id === \"string\" && raw.id ? raw.id : `step${i + 1}`\n const type = typeof raw?.type === \"string\" ? raw.type : \"\"\n if (!AGENT_STEP_TYPES.has(type as NodeType)) {\n return { ok: false, error: `step \"${id}\": type \"${type}\" is not one an agent may use (${[...AGENT_STEP_TYPES].join(\", \")})` }\n }\n if (id === \"start\" || nodes.some((n) => n.id === id)) return { ok: false, error: `step id \"${id}\" is used twice (or is \"start\")` }\n nodes.push({ id, type, config: raw.config && typeof raw.config === \"object\" && !Array.isArray(raw.config) ? raw.config as Record<string, unknown> : {} })\n }\n const addEnd = !nodes.some((n) => n.type === \"end\")\n if (addEnd) nodes.push({ id: \"done\", type: \"end\", config: {} })\n let edges: unknown = input.edges\n if (edges === undefined) {\n // In order, and the start to the first step.\n edges = nodes.slice(0, -1).flatMap((n, i) => (n.type === \"end\" ? [] : [{ from: n.id, to: nodes[i + 1].id }]))\n } else if (Array.isArray(edges)) {\n const given = edges as Array<{ from?: unknown }>\n // A step nothing leads on from ends the run.\n const loose = addEnd ? nodes.slice(1, -1).filter((n) => n.type !== \"end\" && !given.some((e) => e?.from === n.id)) : []\n edges = [{ from: \"start\", to: nodes[1].id }, ...given, ...loose.map((n) => ({ from: n.id, to: \"done\" }))]\n }\n const parsed = workflowSchema.safeParse({\n id: input.id,\n title: input.title,\n ...(input.description ? { description: input.description } : {}),\n nodes,\n edges,\n tags: input.tags ?? [],\n ...(input.ownerAgent ? { ownerAgent: input.ownerAgent } : {}),\n ...(input.approval === \"start\" || input.approval === \"step\" ? { approval: input.approval } : {}),\n autoStart: input.autoStart === true,\n })\n if (!parsed.success) {\n return { ok: false, error: parsed.error.issues.map((i) => `${i.path.join(\".\") || \"workflow\"}: ${i.message}`).join(\"; \").slice(0, 600) }\n }\n const issues = lintWorkflow(parsed.data)\n if (issues.length) return { ok: false, error: issues.join(\"; \").slice(0, 600) }\n return { ok: true, workflow: parsed.data }\n}\n\n/** A workflow id from a title: lower-kebab, with a suffix that keeps it new. */\nexport function workflowIdFor(title: string, prefix = \"wf\"): string {\n const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\").slice(0, 40)\n return `${prefix}-${slug || \"run\"}-${Date.now().toString(36)}`\n}\n\n/** Tags as \"kind:name\", lower case, without spaces at the ends. */\nexport function cleanTags(raw: unknown): string[] {\n const list = Array.isArray(raw) ? raw : typeof raw === \"string\" ? raw.split(\",\") : []\n const out: string[] = []\n for (const t of list) {\n if (typeof t !== \"string\") continue\n const tag = t.trim().toLowerCase().replace(/\\s+/g, \"-\").slice(0, 60)\n if (tag && !out.includes(tag)) out.push(tag)\n }\n return out.slice(0, 10)\n}\n\n/** A pause as list APIs show it: a message waiting for approval keeps\n * its recipient, never its text (CLAUDE.md: bounded summaries). */\nexport function slimPausedAt(p: WorkflowRun[\"pausedAt\"]): WorkflowRun[\"pausedAt\"] {\n if (p?.kind !== \"ownerDecision\" || !p.message) return p\n const { text: _text, ...recipient } = p.message\n return { ...p, message: { ...recipient, text: \"\" } }\n}\n\n/** What a paused or running run is waiting on, in words. */\nexport function waitingOn(run: Pick<WorkflowRun, \"status\" | \"pausedAt\" | \"pending\" | \"meta\">): string {\n const p = run.pausedAt\n if (run.status === \"running\") return run.pending.length ? `running ${run.pending.join(\", \")}` : \"finishing\"\n if (run.status !== \"paused\" || !p) return run.status\n switch (p.kind) {\n case \"ownerDecision\":\n return p.purpose === \"start\" ? \"your approval to start\" : p.purpose === \"send\" ? `your approval to send to ${p.message?.to ?? p.message?.chatId ?? \"a person\"}` : \"your answer\"\n case \"replyWait\":\n return `a reply on ${p.channel} (${p.chatId}) until ${p.deadline.slice(0, 16).replace(\"T\", \" \")} UTC${p.reminders ? `, ${p.reminders} reminder(s) sent` : \"\"}`\n case \"agentStep\":\n return p.blocked ? `blocked: ${p.blocked}` : `${p.agentId} to finish${p.nudges ? ` (${p.nudges}/${p.maxNudges} reminder(s) sent)` : \"\"}`\n case \"timerWait\":\n return `a timer until ${p.fireAt.slice(0, 16).replace(\"T\", \" \")} UTC`\n case \"signalWait\":\n return `the signal \"${p.signalName}\"`\n case \"subProcess\":\n return `the child workflow ${p.childWorkflowId}`\n case \"checkpoint\":\n return `an event at checkpoint ${p.checkpointName}`\n }\n}\n\n/** The step a run is on. */\nexport function currentStep(run: Pick<WorkflowRun, \"pausedAt\" | \"pending\" | \"history\">): string {\n return run.pausedAt?.nodeId ?? run.pending[0] ?? run.history.at(-1)?.nodeId ?? \"-\"\n}\n\n/** The owner's one message when a follow-up run ends. */\nexport function runSummary(run: WorkflowRun, wf: Pick<Workflow, \"id\" | \"title\"> | null): { title: string; message: string } {\n const name = run.meta?.title ?? wf?.title ?? run.workflowId\n const word = run.status === \"completed\" ? \"done\" : run.status === \"canceled\" ? \"stopped\" : \"did not finish\"\n const steps: string[] = []\n const seen = new Set<string>()\n for (const h of run.history) {\n if (h.status !== \"ok\" && h.status !== \"resumed\" && h.status !== \"failed\") continue\n if (seen.has(h.nodeId) && h.status !== \"failed\") continue\n seen.add(h.nodeId)\n const proof = h.output && typeof h.output.evidence === \"string\" ? ` (${h.output.evidence})` : \"\"\n steps.push(`${h.status === \"failed\" ? \"✗\" : \"✓\"} ${h.nodeId}${proof}${h.status === \"failed\" && h.note ? `: ${h.note}` : \"\"}`)\n }\n const last = run.history.at(-1)\n return {\n title: `Workflow ${word}: ${name}`.slice(0, 120),\n message: [\n `\"${name}\" is ${word}${run.meta?.startedBy ? ` (started by ${run.meta.startedBy})` : \"\"}.`,\n ...(run.meta?.tags?.length ? [`About: ${run.meta.tags.join(\", \")}`] : []),\n ...steps.slice(-12),\n ...(run.status !== \"completed\" && last?.note ? [`Why: ${last.note}`] : []),\n `Run ${run.id}`,\n ].join(\"\\n\"),\n }\n}\n\n/** The evidence a finished run leaves on its request: the steps' proofs. */\nexport function runEvidence(run: WorkflowRun): string {\n const proofs = run.history\n .map((h) => (h.output && typeof h.output.evidence === \"string\" ? h.output.evidence : null))\n .filter((x): x is string => !!x)\n return proofs.length ? proofs.join(\" \") : `workflow run ${run.id} (${run.workflowId}) completed`\n}\n\nexport function blockedText(run: WorkflowRun, wf: Pick<Workflow, \"title\"> | null, nodeId: string, reason: string): { title: string; message: string } {\n const name = run.meta?.title ?? wf?.title ?? run.workflowId\n return {\n title: `Workflow blocked: ${name}`.slice(0, 120),\n message: [\n `Step \"${nodeId}\" of \"${name}\" cannot go on without you.`,\n `Why: ${reason}`,\n `When it is sorted, the agent can report it done, or you can stop it: agentx workflow cancel ${run.id}`,\n ].join(\"\\n\"),\n }\n}\n\nexport interface ProgressRow {\n runId: string\n workflowId: string\n title: string\n status: string\n step: string\n waitingOn: string\n since: string\n startedBy?: string\n blocked: boolean\n tags: string[]\n}\n\nexport interface ProgressGroup {\n tag: string\n rows: ProgressRow[]\n}\n\n/** Follow-up runs still going, grouped by tag (a run with two tags shows\n * in both groups; one with none under \"untagged\"). Oldest first. */\nexport function progressGroups(runs: WorkflowRun[], titles: (workflowId: string) => string | undefined = () => undefined): ProgressGroup[] {\n const active = runs\n .filter((r) => r.meta?.followUp && (r.status === \"running\" || r.status === \"paused\") && !r.parentRunId)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt))\n const groups = new Map<string, ProgressRow[]>()\n for (const r of active) {\n const row: ProgressRow = {\n runId: r.id,\n workflowId: r.workflowId,\n title: r.meta?.title ?? titles(r.workflowId) ?? r.workflowId,\n status: r.status,\n step: currentStep(r),\n waitingOn: waitingOn(r),\n since: r.updatedAt,\n ...(r.meta?.startedBy ? { startedBy: r.meta.startedBy } : {}),\n blocked: !!r.meta?.blocked,\n tags: r.meta?.tags ?? [],\n }\n const tags = row.tags.length ? row.tags : [\"untagged\"]\n for (const tag of tags) {\n if (!groups.has(tag)) groups.set(tag, [])\n groups.get(tag)!.push(row)\n }\n }\n return [...groups.entries()]\n .sort(([a], [b]) => (a === \"untagged\" ? 1 : b === \"untagged\" ? -1 : a.localeCompare(b)))\n .map(([tag, rows]) => ({ tag, rows }))\n}\n","import { idempotencyKey, type RunStore } from \"./run-store\"\nimport type { NodeExecutionEntry, Plan, PlanStep, WorkflowRun } from \"./types\"\n\n// --- Run every task through a workflow (#858) ---\n//\n// With `workflows.required` on, every task that reaches an agent runs\n// inside a workflow run:\n// 1. a saved workflow that fits the request, started and followed by the\n// engine (the registry's auto-runner, human-facing turns only);\n// 2. otherwise a plan the agent writes first (agentx_workflow plan), whose\n// steps it reports as it goes and may change mid-run;\n// 3. otherwise the one-step `linear` template: start → reply → done, the\n// reply being the agent's turn.\n// Cases 2 and 3 wrap the agent's own turn: the run is created when the turn\n// starts and ends with it, so nothing extra runs and no step is replayed.\n//\n// The run records each step with when it started, how long it took and\n// where it failed; runRecord() turns a run into one line a later analysis\n// reads across runs.\n//\n// A plain question (no plan, and only tools that change nothing) can be\n// exempted: its run is discarded when the turn ends (exemptQuestions).\n\n/** The workflow id wrapped tasks run under. No file: the steps are the\n * `linear` template's, or the agent's plan. */\nexport const TASK_WORKFLOW_ID = \"task\"\n/** Node ids of the `linear` template (templates/linear.yaml). */\nexport const LINEAR_STEPS = { start: \"start\", reply: \"reply\", done: \"done\" } as const\n\nexport interface RequiredSettings {\n enabled: boolean\n /** Per agent, by id: true or false wins over `enabled`. */\n agents: Record<string, boolean>\n /** Plain questions answered in one turn with nothing changed leave no run. */\n exemptQuestions: boolean\n}\n\nexport const REQUIRED_DEFAULTS: RequiredSettings = { enabled: false, agents: {}, exemptQuestions: true }\n\n/** Is a workflow required for this agent's tasks? Needs the engine on. */\nexport function requiredFor(\n workflows: { enabled?: boolean; required?: Partial<RequiredSettings> } | undefined,\n agentId: string,\n): boolean {\n if (!workflows?.enabled) return false\n const r = workflows.required\n return r?.agents?.[agentId] ?? r?.enabled ?? false\n}\n\n/** Should this task be wrapped? Not a step of a workflow run already: an\n * agent node's turn is that run's step, and wrapping it would nest runs. */\nexport function shouldWrap(task: { workflowRunId?: string; context?: { channel?: string } }): boolean {\n return !task.workflowRunId && task.context?.channel !== \"workflow\"\n}\n\n/** The one context block the agent gets when its task is wrapped. Kept\n * short: it is sent on every wrapped turn (the overhead, in tokens). */\nexport function wrapHintText(runId: string): string {\n return [\n `[Workflow run ${runId}] This task runs inside a workflow run (workflows.required).`,\n `If it takes more than one step, write your plan first: agentx_workflow {action:\"plan\", runId:\"${runId}\", steps:[\"…\",\"…\"]}.`,\n `Report each step: {action:\"step\", runId:\"${runId}\", step:\"<id>\", status:\"started\"|\"done\"|\"failed\", note}.`,\n `If the plan changes, call plan again with the steps left and a reason; steps done stay.`,\n `A one-step task needs none of this: your reply is the run's one step.`,\n ].join(\" \")\n}\n\n/** What the agentx_workflow tool says about plan and step: sent with the\n * tool list, so it counts toward the overhead too. */\nexport const PLAN_TOOL_TEXT =\n \"plan / step: when your task names a [Workflow run <id>], write your plan first (runId, steps: short titles) and report each step (runId, step, status started|done|failed, note); call plan again with the steps left and a reason to change it.\"\n\n// ---------------------------------------------------------------------------\n// Tools that change nothing (the plain-question exemption)\n\nconst READ_ONLY_TOOLS = new Set([\n \"Read\", \"Grep\", \"Glob\", \"LS\", \"NotebookRead\", \"WebSearch\", \"WebFetch\",\n \"TodoWrite\", \"TodoRead\", \"ToolSearch\", \"ListMcpResourcesTool\", \"ReadMcpResourceTool\",\n])\nconst READ_ONLY_VERB = /^(get|list|search|read|query|find|show|status|describe|fetch|view|lookup|recall|whoami|check)(_|-|$|[A-Z])/\nconst READ_ONLY_WORKFLOW_ACTIONS = new Set([\"list\", \"match\", \"status\"])\n/** A first word that changes something: \"set_status\" is a write, even\n * though its last word reads like a lookup. */\nconst WRITE_VERB = /^(set|update|delete|clear|create|add|remove|send|post|put|patch|write|run|start|stop|cancel|edit|move|rename|reset|save|upload|merge|close|open|approve|reject|assign|mark)$/\n\n/** Does this tool call change nothing? Unknown tools count as changing. */\nexport function isReadOnlyToolUse(name: string, input?: Record<string, unknown>): boolean {\n if (READ_ONLY_TOOLS.has(name)) return true\n const bare = name.startsWith(\"mcp__\") ? name.split(\"__\").slice(2).join(\"__\") : name\n if (bare === \"agentx_workflow\") return READ_ONLY_WORKFLOW_ACTIONS.has(String(input?.action ?? \"list\").toLowerCase())\n const verb = bare.replace(/^agentx_/, \"\")\n // \"get_issue\", or \"wiki_query\": the verb leads, or the noun comes first.\n // A name that leads with a write verb (\"set_status\") is never read-only.\n const words = verb.split(/[_-]/)\n if (WRITE_VERB.test(words[0].toLowerCase())) return false\n return READ_ONLY_VERB.test(verb) || (words.length > 1 && READ_ONLY_VERB.test(words.at(-1) ?? \"\"))\n}\n\n/** Tool calls in one stream event (assistant tool_use blocks). */\nexport function toolUsesOf(event: any): Array<{ name: string; input?: Record<string, unknown> }> {\n if (!event || typeof event !== \"object\" || event.type !== \"assistant\") return []\n const blocks = event.message?.content\n if (!Array.isArray(blocks)) return []\n return blocks\n .filter((b: any) => b?.type === \"tool_use\" && typeof b.name === \"string\" && b.name)\n .map((b: any) => ({ name: b.name, input: b.input && typeof b.input === \"object\" ? b.input : undefined }))\n}\n\n// ---------------------------------------------------------------------------\n// The wrap: create, plan, step, finish\n\nexport interface WrapStart {\n runId: string\n agentId: string\n channel: string\n chatId?: string\n message: string\n taskId?: string\n}\n\n/** Create the run a task is wrapped in, on the `linear` template's steps. */\nexport function startWrap(runs: RunStore, args: WrapStart): WorkflowRun {\n const title = args.message.replace(/\\s+/g, \" \").trim().slice(0, 80) || `${args.agentId} task`\n return runs.create({\n id: args.runId,\n workflowId: TASK_WORKFLOW_ID,\n initialPending: [LINEAR_STEPS.reply],\n entityRef: { backend: \"task\", id: args.runId },\n // A bounded preview, never the whole request (run files are listed).\n initialContext: { [LINEAR_STEPS.start]: { agentId: args.agentId, channel: args.channel, ...(args.chatId ? { chatId: args.chatId } : {}), preview: args.message.slice(0, 200) } },\n meta: {\n title,\n tags: [`agent:${args.agentId}`],\n startedBy: args.agentId,\n followUp: false,\n approvedAtStart: false,\n wrap: { agentId: args.agentId, channel: args.channel, mode: \"linear\", ...(args.taskId ? { taskId: args.taskId } : {}) },\n },\n })\n}\n\n/** Step ids the run has finished (ok or failed). */\nfunction finishedSteps(run: WorkflowRun): Set<string> {\n return new Set(run.history.filter((h) => h.status === \"ok\" || h.status === \"failed\").map((h) => h.nodeId))\n}\n\nfunction stepTitle(raw: unknown): string {\n if (typeof raw === \"string\") return raw.trim()\n if (raw && typeof raw === \"object\") {\n const r = raw as Record<string, unknown>\n for (const k of [\"title\", \"name\", \"step\", \"id\"]) if (typeof r[k] === \"string\" && (r[k] as string).trim()) return (r[k] as string).trim()\n }\n return \"\"\n}\n\nexport type WrapResult = { ok: true; run: WorkflowRun } | { ok: false; error: string }\n\n/** The agent writes its plan, or changes the steps it has left. Steps done\n * keep their place; the change is recorded on the run's plan. */\nexport function writePlan(runs: RunStore, runId: string, input: { steps: unknown; reason?: string }): WrapResult {\n const run = runs.get(runId)\n if (!run?.meta?.wrap) return { ok: false, error: `no wrapped task run \"${runId}\"` }\n if (run.status !== \"running\") return { ok: false, error: `run ${runId} is ${run.status}` }\n if (!Array.isArray(input.steps) || input.steps.length === 0) return { ok: false, error: \"plan needs steps: a list of short step titles\" }\n if (input.steps.length > 30) return { ok: false, error: \"a plan has at most 30 steps\" }\n const titles = input.steps.map(stepTitle)\n if (titles.some((t) => !t)) return { ok: false, error: \"every step needs a title\" }\n\n const old = run.meta.plan\n const done = finishedSteps(run)\n const kept: PlanStep[] = (old?.steps ?? []).filter((s) => done.has(s.id))\n let n = (old?.steps ?? []).reduce((max, s) => Math.max(max, Number(s.id.replace(/^step/, \"\")) || 0), 0)\n const fresh: PlanStep[] = titles.map((title) => ({ id: `step${++n}`, title: title.slice(0, 160) }))\n const dropped = (old?.steps ?? []).filter((s) => !done.has(s.id)).map((s) => s.id)\n const plan: Plan = {\n steps: [...kept, ...fresh],\n revisions: [\n ...(old?.revisions ?? []),\n {\n at: new Date().toISOString(),\n kind: old ? \"revise\" : \"plan\",\n ...(input.reason ? { reason: String(input.reason).slice(0, 300) } : {}),\n steps: fresh.map((s) => s.id),\n dropped,\n },\n ],\n }\n const updated = runs.patch(runId, {\n meta: { plan, wrap: { ...run.meta.wrap, mode: \"plan\" } },\n pending: fresh.map((s) => s.id),\n })\n return updated ? { ok: true, run: updated } : { ok: false, error: `run ${runId} vanished` }\n}\n\n/** Find a plan step by id, by title, or by its number (1-based). */\nfunction findStep(plan: Plan, ref: string): PlanStep | undefined {\n const r = ref.trim()\n return plan.steps.find((s) => s.id === r)\n ?? plan.steps.find((s) => s.title.toLowerCase() === r.toLowerCase())\n ?? (/^\\d+$/.test(r) ? plan.steps[Number(r) - 1] : undefined)\n}\n\n/** When the step began: when the agent said it started it, else when the\n * last step ended, else when the run began. */\nfunction stepStart(run: WorkflowRun, stepId: string): string {\n const cur = run.meta?.plan?.current\n if (cur?.id === stepId) return cur.startedAt\n return run.history.at(-1)?.at ?? run.createdAt\n}\n\nfunction entry(run: WorkflowRun, nodeId: string, status: NodeExecutionEntry[\"status\"], startedAt: string, extra: Partial<NodeExecutionEntry> = {}): NodeExecutionEntry {\n const at = new Date().toISOString()\n return {\n at, nodeId, inputKeys: [], status, startedAt,\n durationMs: Math.max(0, Date.parse(at) - Date.parse(startedAt)),\n idempotencyKey: idempotencyKey(run.id, nodeId, `wrap:${status}:${run.history.length}`),\n ...extra,\n }\n}\n\n/** The agent reports a plan step: started, done or failed. A failed step\n * marks where the run failed; the run itself ends with the turn. */\nexport function reportStep(runs: RunStore, runId: string, input: { step: unknown; status: unknown; note?: unknown }): WrapResult {\n const run = runs.get(runId)\n if (!run?.meta?.wrap) return { ok: false, error: `no wrapped task run \"${runId}\"` }\n if (run.status !== \"running\") return { ok: false, error: `run ${runId} is ${run.status}` }\n const plan = run.meta.plan\n if (!plan) return { ok: false, error: \"write a plan first: {action:\\\"plan\\\", runId, steps:[…]}\" }\n const status = String(input.status ?? \"done\").toLowerCase()\n if (![\"started\", \"done\", \"failed\"].includes(status)) return { ok: false, error: \"status is started, done or failed\" }\n const ref = typeof input.step === \"string\" || typeof input.step === \"number\" ? String(input.step) : \"\"\n const step = ref ? findStep(plan, ref) : plan.steps.find((s) => !finishedSteps(run).has(s.id))\n if (!step) return { ok: false, error: `no step \"${ref}\" in the plan (${plan.steps.map((s) => `${s.id} ${s.title}`).join(\"; \")})` }\n const note = typeof input.note === \"string\" && input.note.trim() ? input.note.trim().slice(0, 300) : undefined\n\n if (status === \"started\") {\n const pending = [step.id, ...run.pending.filter((p) => p !== step.id)]\n const updated = runs.patch(runId, { meta: { plan: { ...plan, current: { id: step.id, startedAt: new Date().toISOString() } } }, pending })\n return updated ? { ok: true, run: updated } : { ok: false, error: `run ${runId} vanished` }\n }\n const e = entry(run, step.id, status === \"done\" ? \"ok\" : \"failed\", stepStart(run, step.id), note ? { note } : {})\n const recorded = runs.recordExecution({ runId, entry: e, nextPending: run.pending.filter((p) => p !== step.id) })\n if (!recorded) return { ok: false, error: `run ${runId} vanished` }\n const updated = plan.current?.id === step.id ? runs.patch(runId, { meta: { plan: { ...plan, current: undefined } } }) : recorded\n return updated ? { ok: true, run: updated } : { ok: false, error: `run ${runId} vanished` }\n}\n\nexport interface WrapOutcome {\n /** The turn's error, if it failed or was stopped. */\n error?: string\n /** Why the turn ended without finishing, when it was not a failure: a\n * stop signal (#857), an operator cancel, a daemon stop. The run is\n * then closed as `canceled`, not `failed`, so analysis across runs\n * never counts a pause as a failure. */\n canceled?: boolean\n /** Turn wall time, ms. */\n durationMs: number\n inputTokens?: number\n outputTokens?: number\n /** Every tool the turn used changed nothing. */\n readOnly: boolean\n /** Discard the run of a plain question (exemptQuestions on). */\n exemptQuestions: boolean\n}\n\n/** The turn ended: close the run. Returns \"discarded\" for an exempted\n * plain question, else the final run. */\nexport function finishWrap(runs: RunStore, runId: string, out: WrapOutcome): WorkflowRun | \"discarded\" | null {\n const run = runs.get(runId)\n if (!run?.meta?.wrap) return null\n if (run.status !== \"running\") return run\n const plan = run.meta.plan\n if (!plan && !out.error && out.readOnly && out.exemptQuestions) {\n runs.discard(runId)\n return \"discarded\"\n }\n const usage = {\n turnMs: out.durationMs,\n ...(out.inputTokens !== undefined ? { inputTokens: out.inputTokens } : {}),\n ...(out.outputTokens !== undefined ? { outputTokens: out.outputTokens } : {}),\n }\n const error = out.error ? out.error.slice(0, 300) : undefined\n let current = run\n\n if (error && out.canceled) {\n // Stopped, not failed: the step it was on and the ones after it are\n // skipped with the reason; nothing is marked failed.\n const done = finishedSteps(current)\n const left = plan ? plan.steps.filter((s) => !done.has(s.id)).map((s) => s.id) : [LINEAR_STEPS.reply]\n const at = plan?.current && left.includes(plan.current.id) ? plan.current.id : left[0]\n for (const id of left) {\n const note = id === at ? `stopped: ${error}` : \"not reached: the turn was stopped\"\n const e = entry(current, id, \"skipped\", id === at ? stepStart(current, id) : (current.history.at(-1)?.at ?? current.createdAt), { note, ...(id === at && !plan ? { output: usage } : {}) })\n current = runs.recordExecution({ runId, entry: e, nextPending: [] }) ?? current\n }\n return runs.setStatus(runId, \"canceled\") ?? current\n }\n\n if (!plan) {\n // linear: the reply step is the turn.\n const e = entry(current, LINEAR_STEPS.reply, error ? \"failed\" : \"ok\", current.createdAt, { output: usage, ...(error ? { note: error } : {}) })\n current = runs.recordExecution({ runId, entry: e, nextPending: error ? [] : [LINEAR_STEPS.done], ...(error ? { status: \"failed\" as const } : {}) }) ?? current\n } else {\n const done = finishedSteps(current)\n const left = plan.steps.filter((s) => !done.has(s.id))\n if (error) {\n // Failed where it was: the step it started, else the next one left.\n const at = (plan.current && left.find((s) => s.id === plan.current!.id)) ?? left[0]\n if (at) {\n const e = entry(current, at.id, \"failed\", stepStart(current, at.id), { note: error })\n current = runs.recordExecution({ runId, entry: e, nextPending: [] }) ?? current\n }\n }\n for (const s of left) {\n if (error && finishedSteps(current).has(s.id)) continue\n const e = entry(current, s.id, \"skipped\", current.history.at(-1)?.at ?? current.createdAt, { note: error ? \"not reached: the turn failed\" : \"not reported before the turn ended\" })\n current = runs.recordExecution({ runId, entry: e, nextPending: [] }) ?? current\n }\n if (error) current = runs.setStatus(runId, \"failed\") ?? current\n }\n if (!error) {\n const e = entry(current, LINEAR_STEPS.done, \"ok\", current.history.at(-1)?.at ?? current.createdAt, { output: usage })\n current = runs.recordExecution({ runId, entry: e, nextPending: [], status: \"completed\" }) ?? current\n } else if (current.status === \"running\") {\n current = runs.setStatus(runId, \"failed\") ?? current\n }\n return runs.get(runId) ?? current\n}\n\n/** Error kinds that end a turn without it failing: a stop signal (#857),\n * an operator cancel, a daemon shutdown. Compared as strings so a kind\n * added later needs no type change here. */\nconst NOT_A_FAILURE = new Set([\"stopped\", \"cancelled\", \"interrupted\"])\nexport function endedWithoutFailing(errorKind: unknown): boolean {\n return typeof errorKind === \"string\" && NOT_A_FAILURE.has(errorKind)\n}\n\n/** At boot: a wrapped task's run still \"running\" lost its turn when the\n * daemon stopped (a hard kill skips the turn's own close). Close it as\n * canceled where it was, so records and the Live page do not show it as\n * going on. The task itself is resumed (or reported) by the restart\n * path as before: wrapping never sets the task's workflowRunId. */\nexport function closeStaleWraps(runs: RunStore, note = \"interrupted: the daemon stopped during the turn\"): number {\n let closed = 0\n for (const run of runs.list()) {\n if (run.status !== \"running\" || !run.meta?.wrap) continue\n // Cut off, not failed: the task itself is resumed after the restart.\n const r = finishWrap(runs, run.id, { error: note, canceled: true, durationMs: Math.max(0, Date.now() - Date.parse(run.createdAt)), readOnly: false, exemptQuestions: false })\n if (r && r !== \"discarded\") closed++\n }\n return closed\n}\n\n// ---------------------------------------------------------------------------\n// One line per run, for analysis across runs\n\nexport interface RunRecordStep {\n id: string\n title?: string\n status: string\n startedAt?: string\n durationMs?: number\n note?: string\n}\n\nexport interface RunRecord {\n v: 1\n runId: string\n workflowId: string\n title: string\n /** How the work ran: a saved workflow, the agent's plan, or `linear`. */\n mode: \"workflow\" | \"plan\" | \"linear\"\n agentId?: string\n status: string\n startedAt: string\n endedAt?: string\n durationMs?: number\n steps: RunRecordStep[]\n /** The first step that failed, if any. */\n failedAt: string | null\n /** Changes made to the plan after it was first written. */\n revisions: number\n tags: string[]\n /** Tokens the agent turn used, when the run wrapped one. */\n tokens?: { input?: number; output?: number }\n}\n\nconst TERMINAL = new Set([\"completed\", \"failed\", \"canceled\"])\n\n/** A run as one record: which steps were taken, how long each took, where\n * it failed. Step durations missing from older runs are taken from the\n * gap to the entry before. Bounded: no step outputs, no prompts. */\nexport function runRecord(run: WorkflowRun): RunRecord {\n const plan = run.meta?.plan\n const titles = new Map((plan?.steps ?? []).map((s) => [s.id, s.title] as const))\n const steps: RunRecordStep[] = []\n let prevAt = run.createdAt\n for (const h of run.history) {\n if (h.status === \"paused\" || h.status === \"resumed\") { prevAt = h.at; continue }\n const durationMs = h.durationMs ?? Math.max(0, Date.parse(h.at) - Date.parse(prevAt))\n steps.push({\n id: h.nodeId,\n ...(titles.has(h.nodeId) ? { title: titles.get(h.nodeId) } : {}),\n status: h.status,\n startedAt: h.startedAt ?? prevAt,\n durationMs,\n ...(h.note ? { note: h.note.slice(0, 200) } : {}),\n })\n prevAt = h.at\n }\n const ended = TERMINAL.has(run.status) ? run.updatedAt : undefined\n const usage = [...run.history].reverse().find((h) => h.output && typeof h.output.turnMs === \"number\")?.output as Record<string, number> | undefined\n return {\n v: 1,\n runId: run.id,\n workflowId: run.workflowId,\n title: run.meta?.title ?? run.workflowId,\n mode: run.meta?.wrap ? run.meta.wrap.mode : \"workflow\",\n ...(run.meta?.wrap?.agentId ?? run.meta?.startedBy ? { agentId: run.meta?.wrap?.agentId ?? run.meta?.startedBy } : {}),\n status: run.status,\n startedAt: run.createdAt,\n ...(ended ? { endedAt: ended, durationMs: Math.max(0, Date.parse(ended) - Date.parse(run.createdAt)) } : {}),\n steps,\n failedAt: steps.find((s) => s.status === \"failed\" || s.status === \"timeout\")?.id ?? null,\n revisions: Math.max(0, (plan?.revisions.length ?? 0) - 1),\n tags: run.meta?.tags ?? [],\n ...(usage && (usage.inputTokens !== undefined || usage.outputTokens !== undefined)\n ? { tokens: { ...(usage.inputTokens !== undefined ? { input: usage.inputTokens } : {}), ...(usage.outputTokens !== undefined ? { output: usage.outputTokens } : {}) } }\n : {}),\n }\n}\n\n/** The step a wrapped or engine run is on, with plan progress when there\n * is one (\"step2 Check the logs (2/4)\"). For the live page. */\nexport function liveStep(run: WorkflowRun): string {\n const plan = run.meta?.plan\n const id = run.meta?.plan?.current?.id ?? run.pausedAt?.nodeId ?? run.pending[0] ?? run.history.at(-1)?.nodeId ?? \"-\"\n if (!plan) return id\n const i = plan.steps.findIndex((s) => s.id === id)\n return i >= 0 ? `${plan.steps[i].title} (${i + 1}/${plan.steps.length})` : id\n}\n","import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, appendFileSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\nimport { randomBytes } from \"crypto\"\n\n// --- Members: a teammate's machines, and what happened with them (#385) ---\n//\n// A member is a person from the people list (people.ts) who reaches one\n// page of this node, /member, from their own machine. Each machine is one\n// token with the scope `member:<person>` in the token store; this file\n// keeps what the token store does not: whose machine it is, what it is\n// called, where it paired from, what the private network said about who\n// was connecting, and whether the owner has said yes to it.\n//\n// Every sign-in, refusal and removal is appended to a log, one line per\n// event, per person and per machine.\n\nconst DEVICES_FILE = \".agentx/members.json\"\nconst LOG_FILE = \".agentx/members-log.jsonl\"\n\nexport type DeviceState = \"pending\" | \"active\" | \"removed\"\n\nexport interface MemberDevice {\n /** The token record's id (token-store.ts). */\n tokenId: string\n personId: string\n /** What the person called the machine when pairing. */\n name: string\n state: DeviceState\n /** The decision card the owner answers before the machine works. */\n cardId?: string\n /** Where the pairing came from. */\n address?: string\n /** Who the private network said was connecting, when it said. */\n network?: string\n createdAt: string\n approvedAt?: string\n removedAt?: string\n /** Why it was removed: the owner said no, the owner removed it, the key expired. */\n removedReason?: string\n lastSeenAt?: string\n lastAddress?: string\n}\n\nexport type MemberEventKind =\n | \"invited\" | \"paired\" | \"approved\" | \"refused\" | \"signed-in\" | \"removed\" | \"expired\"\n /** A message to an agent the person may not reach (people[].agents, #379);\n * `detail` is the agent. */\n | \"agent-refused\"\n /** A tool or skill call the person's turn may not make (people[].deny,\n * #379); `detail` is the tool, or \"skill:<name>\". */\n | \"tool-refused\"\n\nexport interface MemberEvent {\n at: string\n person: string\n device?: string\n event: MemberEventKind\n address?: string\n detail?: string\n}\n\ninterface DevicesFile { devices: MemberDevice[] }\n\nexport class MemberStore {\n private file: string\n private logFile: string\n\n private prunedAt = 0\n\n constructor(\n baseDir: string = process.cwd(),\n private now: () => number = Date.now,\n /** Days a log line is kept (members.logRetentionDays). */\n private retentionDays = 90,\n ) {\n this.file = resolve(baseDir, DEVICES_FILE)\n this.logFile = resolve(baseDir, LOG_FILE)\n }\n\n devices(personId?: string): MemberDevice[] {\n const all = this.load().devices\n return personId ? all.filter((d) => d.personId === personId) : all\n }\n\n byToken(tokenId: string): MemberDevice | null {\n return this.load().devices.find((d) => d.tokenId === tokenId) ?? null\n }\n\n add(device: MemberDevice): void {\n const data = this.load()\n data.devices = data.devices.filter((d) => d.tokenId !== device.tokenId)\n data.devices.push(device)\n this.save(data)\n }\n\n update(tokenId: string, patch: Partial<MemberDevice>): MemberDevice | null {\n const data = this.load()\n const device = data.devices.find((d) => d.tokenId === tokenId)\n if (!device) return null\n Object.assign(device, patch)\n this.save(data)\n return device\n }\n\n /** Append one event. Never throws: the log must not break a sign-in. */\n log(event: Omit<MemberEvent, \"at\">): void {\n // On its own: a failed prune must not cost the new line.\n try { this.prune() } catch { /* next write tries again */ }\n try {\n mkdirSync(dirname(this.logFile), { recursive: true })\n appendFileSync(this.logFile, JSON.stringify({ at: new Date(this.now()).toISOString(), ...event }) + \"\\n\", { mode: 0o600 })\n } catch { /* the device record is the source of truth; the log is a trail */ }\n }\n\n /** Drops lines older than the retention, at most once an hour. The daemon,\n * the dashboard and the CLI all append here with no lock: a line another\n * process writes between the read and the rename is lost. */\n prune(force = false): number {\n const now = this.now()\n if (!force && now - this.prunedAt < 3_600_000) return 0\n this.prunedAt = now\n if (!existsSync(this.logFile)) return 0\n const cutoff = now - this.retentionDays * 86_400_000\n const lines = readFileSync(this.logFile, \"utf-8\").split(\"\\n\").filter(Boolean)\n const kept = lines.filter((line) => {\n try { return Date.parse(JSON.parse(line).at) >= cutoff } catch { return false }\n })\n if (kept.length === lines.length) return 0\n const tmp = `${this.logFile}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`\n writeFileSync(tmp, kept.length ? kept.join(\"\\n\") + \"\\n\" : \"\", { encoding: \"utf-8\", mode: 0o600 })\n renameSync(tmp, this.logFile)\n return lines.length - kept.length\n }\n\n /** The latest events, newest first; `personId` narrows them to one person. */\n events(personId?: string, limit = 50): MemberEvent[] {\n if (!existsSync(this.logFile)) return []\n const lines = readFileSync(this.logFile, \"utf-8\").split(\"\\n\").filter(Boolean)\n const out: MemberEvent[] = []\n for (let i = lines.length - 1; i >= 0 && out.length < limit; i--) {\n try {\n const ev = JSON.parse(lines[i]) as MemberEvent\n if (!personId || ev.person === personId) out.push(ev)\n } catch { /* a torn line */ }\n }\n return out\n }\n\n private load(): DevicesFile {\n if (existsSync(this.file)) {\n try {\n const parsed = JSON.parse(readFileSync(this.file, \"utf-8\"))\n if (parsed && Array.isArray(parsed.devices)) return parsed\n } catch { /* rewritten on the next save */ }\n }\n return { devices: [] }\n }\n\n private save(data: DevicesFile): void {\n mkdirSync(dirname(this.file), { recursive: true })\n const tmp = `${this.file}.${process.pid}.${randomBytes(4).toString(\"hex\")}.tmp`\n writeFileSync(tmp, JSON.stringify(data, null, 2) + \"\\n\", { encoding: \"utf-8\", mode: 0o600 })\n renameSync(tmp, this.file)\n }\n}\n","import { extname } from \"path\"\n\n// --- Files an agent declares in its answer: <agentx-artifact> ---\n//\n// One mechanism for every surface that shows files an agent created. The\n// agent saves the file in its workspace and ends its answer with one\n// sentinel per file:\n//\n// <agentx-artifact>{\"filename\":\"charts/sales.png\",\"mime\":\"image/png\"}</agentx-artifact>\n//\n// The daemon's web chat (POST /chat) returns them as `artifacts`; the phone\n// app registers them per conversation and serves them through\n// /api/app/files/:id (app-files.ts). Closed sentinels are always stripped\n// from the text people read or hear, including malformed ones. An unclosed\n// tag is only cut when the answer itself was cut off: a finished answer may\n// mention the tag in prose.\n\nexport interface DeclaredArtifact {\n /** As the agent wrote it: a path relative to its workspace. */\n filename: string\n /** As the agent declared it. Serving goes by the file extension instead. */\n mime: string\n /** The web chat's coarse kind, kept for its callers. */\n type: \"image\" | \"pdf\" | \"text\" | \"file\"\n}\n\nconst SENTINEL = /<agentx-artifact>([\\s\\S]*?)<\\/agentx-artifact>/gi\n/** A sentinel cut off mid-way (a stopped or broken answer). */\nconst DANGLING = /<agentx-artifact>[\\s\\S]*$/i\n\n/** The text without its sentinels, and the files they declare, in order.\n * Files past `max` are stripped but not returned. `cutOff` marks a stopped\n * or failed answer, whose half-written sentinel is stripped as well. */\nexport function extractArtifacts(text: string, max = Infinity, cutOff = false): { text: string; artifacts: DeclaredArtifact[] } {\n const artifacts: DeclaredArtifact[] = []\n const raw = String(text ?? \"\")\n // Text without a sentinel comes back exactly as it was (a partial answer\n // keeps its trailing space).\n if (!/<agentx-artifact>/i.test(raw)) return { text: raw, artifacts }\n const clean = raw.replace(SENTINEL, (_full, inner: string) => {\n try {\n const parsed = JSON.parse(inner.trim())\n if (parsed && typeof parsed.filename === \"string\" && typeof parsed.mime === \"string\" && artifacts.length < max) {\n const mime: string = parsed.mime\n const type = mime.startsWith(\"image/\") ? \"image\"\n : mime === \"application/pdf\" ? \"pdf\"\n : mime.startsWith(\"text/\") ? \"text\"\n : \"file\"\n artifacts.push({ type, filename: parsed.filename, mime })\n }\n } catch { /* malformed: dropped from the text all the same */ }\n return \"\"\n })\n return { text: (cutOff ? clean.replace(DANGLING, \"\") : clean).trim(), artifacts }\n}\n\n/** An answer from an agent with rich messages off (#259): each sentinel\n * becomes its file's name as text, so nothing is silently lost, and a web\n * picture becomes an ordinary link. Code is left as written. `cutOff`\n * drops an unclosed tag, as in extractArtifacts. */\nexport function plainAnswer(text: string, cutOff = false): string {\n const raw = String(text ?? \"\")\n let named = raw\n if (/<agentx-artifact>/i.test(raw)) {\n const replaced = raw.replace(SENTINEL, (_full, inner: string) => {\n try {\n const parsed = JSON.parse(inner.trim())\n return parsed && typeof parsed.filename === \"string\" ? parsed.filename : \"\"\n } catch { return \"\" }\n })\n named = (cutOff ? replaced.replace(DANGLING, \"\") : replaced).trim()\n }\n return named.replace(/```[\\s\\S]*?```|`[^`\\n]*`|!\\[([^\\]\\n]*)\\]\\(([^)\\s]+)\\)/g, (m: string, alt?: string, src?: string) =>\n alt === undefined || src === undefined ? m : `[${alt || src}](${src})`)\n}\n\n/** What may be served, by extension. SVG is an image but can carry script,\n * so it is only ever served as a download. */\nexport const ARTIFACT_TYPES: Readonly<Record<string, { mime: string; kind: \"image\" | \"audio\" | \"video\" | \"file\" }>> = {\n \".png\": { mime: \"image/png\", kind: \"image\" },\n \".jpg\": { mime: \"image/jpeg\", kind: \"image\" },\n \".jpeg\": { mime: \"image/jpeg\", kind: \"image\" },\n \".gif\": { mime: \"image/gif\", kind: \"image\" },\n \".webp\": { mime: \"image/webp\", kind: \"image\" },\n \".svg\": { mime: \"image/svg+xml\", kind: \"file\" },\n \".pdf\": { mime: \"application/pdf\", kind: \"file\" },\n \".txt\": { mime: \"text/plain; charset=utf-8\", kind: \"file\" },\n \".md\": { mime: \"text/markdown; charset=utf-8\", kind: \"file\" },\n \".csv\": { mime: \"text/csv; charset=utf-8\", kind: \"file\" },\n \".json\": { mime: \"application/json\", kind: \"file\" },\n \".mp3\": { mime: \"audio/mpeg\", kind: \"audio\" },\n \".m4a\": { mime: \"audio/mp4\", kind: \"audio\" },\n \".wav\": { mime: \"audio/wav\", kind: \"audio\" },\n \".mp4\": { mime: \"video/mp4\", kind: \"video\" },\n \".webm\": { mime: \"video/webm\", kind: \"video\" },\n}\n\nexport const ARTIFACT_LIMITS = {\n /** Largest file served. */\n bytes: 20 * 1024 * 1024,\n /** Files kept from one answer. */\n perMessage: 20,\n /** Longest declared path. */\n pathChars: 512,\n} as const\n\n/** The served type of a declared path, or null when it may not be served. */\nexport function artifactType(filename: string): { mime: string; kind: \"image\" | \"audio\" | \"video\" | \"file\" } | null {\n return ARTIFACT_TYPES[extname(filename).toLowerCase()] ?? null\n}\n\n/** What an agent on the phone app is told, once per fresh session. */\nexport const APP_ATTACH_HINT = [\n \"[Phone app: showing files]\",\n \"To show the owner a file you created (a chart, a screenshot, a PDF, a recording), save or copy it into .agentx/outbox/ in your workspace and end your reply with one line per file:\",\n '<agentx-artifact>{\"filename\":\".agentx/outbox/<name.ext>\",\"mime\":\"<mime/type>\"}</agentx-artifact>',\n \"Files outside your workspace (/tmp, your home folder) are refused. Outbox files are removed after 7 days.\",\n \"The line is removed from the text. Images show in the reply, audio and video get a player, other files an Open link. Allowed: png, jpg, gif, webp, svg, pdf, txt, md, csv, json, mp3, m4a, wav, mp4, webm; up to 20 MB and 20 files per reply.\",\n \"A picture already on the web can go inline as .\",\n].join(\"\\n\")\n\n/** The attach note for one turn: on the phone app, when a session starts,\n * unless the agent has rich messages off (#259). */\nexport function appAttachHint(channel: string, sessionStarts: boolean, richMessages: boolean | undefined): string | undefined {\n return channel === \"app\" && sessionStarts && richMessages !== false ? APP_ATTACH_HINT : undefined\n}\n","import { lstatSync, mkdirSync, readdirSync, realpathSync, unlinkSync } from \"fs\"\nimport { join, resolve } from \"path\"\n\n// --- The phone app outbox: <workspace>/.agentx/outbox/ ---\n//\n// Files an agent shows on the phone must be inside its workspace\n// (app-files-api.ts). A file made elsewhere (/tmp) is copied here first and\n// attached from here. The daemon makes the folder when a phone chat starts\n// and removes what is older than a week, so the copies don't pile up.\n//\n// Only a real folder at exactly this place is ever cleaned: if `.agentx` or\n// `outbox` is a link, or the workspace is missing, nothing is touched.\n\nexport const OUTBOX_DIR = \".agentx/outbox\"\nexport const OUTBOX_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\n/** The outbox's real path, or null when it is missing or not where it should be. */\nfunction outboxPath(workspace: string, cwd: string): string | null {\n try {\n const root = realpathSync(resolve(cwd, workspace))\n const dir = join(root, OUTBOX_DIR)\n return realpathSync(dir) === dir && lstatSync(dir).isDirectory() ? dir : null\n } catch {\n return null\n }\n}\n\n/** Creates the outbox if needed, then removes files older than `maxAgeMs`.\n * Returns how many were removed. */\nexport function prepareOutbox(workspace: string, opts: { cwd?: string; now?: number; maxAgeMs?: number; create?: boolean } = {}): number {\n const cwd = opts.cwd ?? process.cwd()\n if (opts.create !== false) {\n try {\n const root = realpathSync(resolve(cwd, workspace))\n // Never make the folder through a linked `.agentx`.\n if (!lstatSync(join(root, \".agentx\"), { throwIfNoEntry: false })?.isSymbolicLink()) {\n mkdirSync(join(root, OUTBOX_DIR), { recursive: true })\n }\n } catch { return 0 }\n }\n const dir = outboxPath(workspace, cwd)\n if (!dir) return 0\n return prune(dir, (opts.now ?? Date.now()) - (opts.maxAgeMs ?? OUTBOX_MAX_AGE_MS))\n}\n\n/** Old files and links go, links never followed. Folders stay.\n * A file's age counts from when it entered the outbox: `mv`, `cp -p` or\n * `tar x` keep an old mtime, but ctime is set when the file lands here. */\nfunction prune(dir: string, before: number): number {\n let removed = 0\n for (const name of readdirSync(dir)) {\n const path = join(dir, name)\n try {\n const st = lstatSync(path)\n if (st.isDirectory()) removed += prune(path, before)\n else if (Math.max(st.mtimeMs, st.ctimeMs) < before) {\n unlinkSync(path)\n removed++\n }\n } catch { /* gone already or not ours to remove: the next pass retries */ }\n }\n return removed\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readFileSync","writeFileSync","renameSync","resolve","dirname","HandoverStore","opts","base","channel","chatId","accountId","raw","parsed","file","tmp","o","rec","k","summary","now","agentId","all","readFileSync","writeFileSync","appendFileSync","mkdirSync","existsSync","statSync","resolve","MEMORY_HEADER","BUDGET","flag","unverified","age","wikiRefLine","f","now","isStaleFact","tag","daysSince","memoryLine","m","scope","needsRecheck","ageDays","memoryContextLines","memories","wikiFacts","kept","referenced","chars","ruleCounted","wf","matchFact","rendered","wikiRef","newerNews","extra","VERIFY_OR_ASK_RULE","renderMemoryBlock","rule","l","MAX_MEMORIES","TASK_STATE_TTL_DAYS","MemoryStore","baseDir","resolve","FactLedger","existsSync","mkdirSync","agentId","fact","containsSecret","trust","trustForChannel","review","initialReview","entry","appendFileSync","file","mtimeMs","size","statSync","hit","facts","readFileSync","l","m","message","limit","memories","isInjectable","docs","cachePath","index","buildIndexCached","bm25Scores","scoreAll","scoreMap","r","matched","i","s","selected","a","b","renderMemoryBlock","wikiFacts","memoryContextLines","memoryIds","idSet","changed","currentInterval","writeFileSync","now","due","lines","age","factTrust","id","decision","by","f","apply","keep","removed","out","key","unreadable","content","contentWords","w","mLower","ttlMs","unwrapMeshError","raw","out","i","before","QUEUED_MARKER","queuedMarker","mode","pending","QUEUED_RE","parseQueued","text","m","unwrapMeshError","isQueued","existsSync","mkdirSync","readdirSync","readFileSync","statSync","unlinkSync","writeFileSync","resolve","relative","z","nodeTypeSchema","conditionKindSchema","conditionSchema","retryPolicySchema","workflowNodeSchema","workflowEdgeSchema","isoDateString","d","workflowSchema","ns","FOLLOW_UP_PORTS","lintWorkflow","wf","issues","byId","n","triggers","e","trigger","adj","seen","stack","id","next","isTerminalOrPauseNode","cases","r","defaultPort","declared","c","outgoing","edge","port","onCycle","detectCyclesWithoutPauseNodes","nodeIds","type","isPauseCapableNode","results","color","dfs","i","cycle","nid","t","runStatusSchema","nodeExecutionStatusSchema","nodeExecutionEntrySchema","entityRefSchema","pausedAtSchema","planStepSchema","planRevisionSchema","planSchema","runMetaSchema","workflowRunSchema","yaml","FLOW_FORBIDDEN_TYPES","WorkflowYamlError","message","mark","parseYamlWorkflow","text","opts","prefix","docs","e","raw","got","desugarFlow","flow","out","item","flowIds","nodes","idToType","n","id","type","synthesized","i","explicit","seen","unioned","k","edgeKey","from","fromPort","to","renderWorkflowYaml","workflow","Document","isMap","isSeq","isScalar","parseDocument","renderWorkflowYamlPreservingComments","originalText","workflow","doc","fresh","mergeMapInPlace","map","next","newKeys","newKeySet","oldPairs","pair","k","scalarKey","merged","key","value","existing","mergeValueInPlace","nextValue","oldVal","mergeSeqInPlace","seq","nextItems","it","replacement","oldById","item","idNode","id","listCache","WorkflowStore","opts","resolve","mkdirSync","id","name","full","text","readFileSync","parseYamlWorkflow","existsSync","byId","sig","entry","readdirSync","list","st","statSync","signature","cached","out","files","raw","parsed","workflowSchema","workflows","a","b","results","sorted","relative","rel","e","reason","WorkflowYamlError","issues","i","lintIssues","lintWorkflow","jsonPath","yamlPath","ymlPath","haveJson","haveYaml","yPath","workflow","now","normalized","existingYaml","unlinkSync","writeFileSync","originalText","renderWorkflowYamlPreservingComments","err","removed","createHash","randomUUID","appendFileSync","existsSync","mkdirSync","readdirSync","readFileSync","statSync","unlinkSync","writeFileSync","resolve","sanitize","s","idempotencyKey","runId","nodeId","eventId","createHash","RunStore","opts","resolve","mkdirSync","entity","args","now","id","randomUUID","run","currentRoot","p","existsSync","raw","readFileSync","line","evt","current","h","appendFileSync","nextStatus","status","updated","patch","meta","unlinkSync","files","readdirSync","f","statSync","a","b","out","policy","cutoff","candidates","name","path","mtime","pruned","i","c","tooOld","tooMany","writeFileSync","existsSync","mkdirSync","readFileSync","unlinkSync","writeFileSync","resolve","z","layoutSchema","LayoutStore","opts","workflowId","safe","p","parsed","layout","validIds","existing","keep","id","next","words","text","w","overlap","a","b","n","matchWorkflow","input","workflows","messageWords","active","wf","best","score","reasons","same","p","i","wordScore","confidence","nextNodes","input","workflow","fromNodeId","selectedPort","fromNode","n","outgoing","e","ports","FOLLOW_UP_PORTS","evaluateBranch","node","context","cases","defaultPort","c","conditionMatches","cond","p","path","value","getByPath","v","ctx","parts","cur","initialPendingFromTrigger","trigger","successors","findNode","id","randomUUID","init_questions","init_seat","lookup","path","ctx","envAllow","parts","name","v","cur","p","render","template","opts","_","renderParams","params","out","k","x","CLASSIFY_SEAT","UNSURE_PORT","classifyHandler","ctx","cfg","vars","opts","input","render","raw","labels","l","k","v","names","minConfidence","instructions","state","rendered","res","askSeat","choice","answer","confident","port","NodeConcurrencyGate","key","max","cur","resolve","queue","nodeConcurrencyGate","nodeKey","workflowId","nodeId","DEFAULT_FOLLOW_UP","ctxOf","ctx","durationMs","v","s","short","n","unit","ms","iso","followUpFor","base","DEFAULT_FOLLOW_UP","cfg","wf","num","isSupervised","flag","supervisedSuffix","run","nodeId","stepVerdict","word","supervisedResult","agentId","turn","stallMinutes","maxNudges","stallMs","pausedAt","blocked","verdict","reason","lastLine","text","l","nudgeText","max","sinceMs","mins","ownerNotifyHandler","render","e","ownerAskHandler","renderParams","ask","choices","cardId","clip","renderPersonMessage","personMessageHandler","msg","messageKey","sent","deliver","who","adapter","out","personWaitHandler","reminds","channel","chatId","timeout","every","maxReminders","now","accountId","nextWakeAt","p","remind","flat","FOLLOW_UP_HANDLERS","triggerHandler","ctx","agentHandler","cfg","agentId","promptTemplate","supervised","isSupervised","prompt","render","supervisedSuffix","timeoutMinutes","autonomy","maxConcurrent","gateKey","nodeKey","before","nodeConcurrencyGate","start","resp","durationMs","kind","supervisedResult","parsed","extractJsonBlock","parseResultToken","output","e","branchHandler","port","evaluateBranch","sendHandler","rendered","renderParams","channel","chatId","text","rawChat","rawChan","hints","accountId","resolveAccountId","adapter","createIssueHandler","project","title","description","labels","assignees","findUpstreamAgentId","result","endHandler","checkpointHandler","name","waitFor","resumeMatch","setLabelHandler","iid","add","remove","readLabelHandler","reactHandler","messageId","emoji","editMessageHandler","parseMode","logTimeHandler","callHTTPHandler","url","method","headers","body","res","rawText","actionRunHandler","actionId","inputs","ActionStore","runAction","action","actionBuiltinHandler","input","runBuiltin","getBuiltin","msg","transformHandler","ctxObj","getByPath","computeDueAt","dueIn","m","match","days","hours","minutes","seconds","ms","subProcessHandler","childWorkflowId","parentDepth","cap","rawInputMap","inputBundle","k","v","triggerFormHandler","signalEmitHandler","scope","payload","signalWaitHandler","timerBoundaryHandler","due","gatewayParallelHandler","ruleHandler","raw","rules","rule","when","all","i","matchCell","def","w","numMatch","op","n","vNum","NODE_HANDLERS","classifyHandler","FOLLOW_UP_HANDLERS","resolveHandler","type","explicit","triggerNode","fromTrigger","entry","node","content","bracket","SignalBus","h","emission","matchesSignal","waiter","k","v","buildWorkflowEventInput","proj","rawJson","now","workflowResultToLegacyOutcome","result","buildWorkflowPolicyFromLegacy","legacy","recordWorkflowDispatch","ledger","eventInput","legacyOutcome","policy","ledgerDecision","decideAndCommit","reportDivergence","init_sqlite","TERMINAL","ACK_TYPING_INTERVAL_MS","MAX_PARALLEL_PER_RUN","WorkflowDispatcher","opts","TimerService","m","SignalBus","DEFAULT_FOLLOW_UP","emission","args","runId","fn","next","all","run","waiter","matchesSignal","wf","w","fresh","node","findNode","successors","e","output","idempotencyKey","t","kind","firedAt","activeRunId","active","cfg","n","effective","payload","channel","chatId","adapter","accountId","event","messageId","tick","timer","matches","toRun","claimed","runs","r","fromRemote","out","allowed","trigger","f","normalizeChat","have","l","a","b","workflow","entityRef","result","getLedgerMode","recordWorkflowDispatch","getDefaultLedger","homeNode","matchesResume","checkpointNode","resumeEntry","newContext","pausedCheckpointName","updated","init","initialPendingFromTrigger","triggeringEventId","rootId","inner","withRoot","batch","nodeId","key","prelude","x","handler","resolveHandler","h","inputKeys","workflowTraceTaskId","db","openDb","recordTraceStart","retry","attempt","stepStartedMs","timing","wait","early","recordTraceEnd","now","remainingFromPending","statusUpdate","pausedAt","spawnedChild","nextPending","nextNodes","readyNext","joinCounters","succId","succ","arrived","expected","merged","terminal","randomUUID","meta","messages","fail","error","lines","msg","cardId","triggerId","c","v","renderPersonMessage","card","run0","port","handled","walk","sendFailure","p0","sent","deliver","p","at","approved","messageKey","decision","source","ids","waiting","id","candidate","taken","owner","reason","done","note","ports","FOLLOW_UP_PORTS","dead","pending","after","reminder","nudgeText","res","nd","resp","token","parseResultToken","verdict","stepVerdict","lastLine","workflowId","nextWakeAt","childWf","childContext","child","childRun","preliminaryParent","parentWf","parent","childOutput","pickChildOutput","i","resumeMatch","match","k","want","eventIdLike","value","text","createWorkflowHookHandlers","dispatcher","ctx","msg","chatId","waiting","source","channelToTriggerSource","entityRef","claimed","buildChannelTriggerPayload","wf","n","w","project","iid","issueEvent","labels","l","assignees","mrEvent","reviewers","noteId","noteableType","noteableIid","text","author","mentions","pipelineId","status","ref","mrIid","channel","norm","u","eventAuthor","ctx","direct","login","eventTargetKey","event","project","iid","type","payload","repo","num","workflowAgentIds","wf","out","n","id","FireWindow","maxKeys","key","count","windowMs","now","recent","t","oldest","logOnce","deps","msg","seen","checkLoopGuard","filter","author","agents","authorAgent","own","a","limit","target","windowMinutes","startWorkflowTriggers","args","workflows","loopGuard","FireWindow","cronTimers","hookSubscribers","wf","trigger","n","cfg","scheduleCron","ctx","ctxProject","topic","t","action","ctxMentions","m","ctxType","wanted","ctxAdded","u","l","guard","checkLoopGuard","prev","entityId","entityKeyFromHookContext","prevClaimed","e","workflowId","event","project","iid","pid","opts","dispatcher","log","tick","next","getNextCronDate","delay","eventId","createHash","existsSync","mkdirSync","readFileSync","renameSync","writeFileSync","resolve","z","pollConfigSchema","z","conditionSchema","MAX_SEEN","RESYNC_SECONDS","runRegisteredAction","actionId","action","ActionStore","runAction","parsePollItems","output","keyPath","items","skipped","line","text","value","key","getByPath","pollEntityId","workflowId","plain","createHash","PollTriggers","opts","resolve","wanted","wf","cfg","id","timer","result","e","current","i","state","seen","started","dirty","item","matches","c","conditionMatches","run","everySeconds","trigger","n","parsed","why","msg","file","existsSync","raw","readFileSync","drop","k","mkdirSync","writeFileSync","renameSync","getInputSchema","wf","n","fillFromChatId","chatId","schema","m","project","kind","rest","out","refMatch","idNum","applyDefaults","current","k","v","missingRequired","populated","resolveAutoRunInputs","ctx","all","passthrough","fromChat","merged","beforeDefaults","filledFromDefaults","AGENT_STEP_TYPES","workflowHintText","wf","buildWorkflow","input","nodes","i","raw","id","type","n","addEnd","edges","given","loose","e","parsed","workflowSchema","issues","lintWorkflow","workflowIdFor","title","prefix","slug","cleanTags","list","out","t","tag","slimPausedAt","p","_text","recipient","waitingOn","run","currentStep","runSummary","name","word","steps","seen","h","proof","last","runEvidence","proofs","x","blockedText","nodeId","reason","progressGroups","runs","titles","active","r","a","b","groups","row","tags","rows","TASK_WORKFLOW_ID","LINEAR_STEPS","REQUIRED_DEFAULTS","requiredFor","workflows","agentId","r","shouldWrap","task","wrapHintText","runId","PLAN_TOOL_TEXT","READ_ONLY_TOOLS","READ_ONLY_VERB","READ_ONLY_WORKFLOW_ACTIONS","WRITE_VERB","isReadOnlyToolUse","name","input","bare","verb","words","toolUsesOf","event","blocks","b","startWrap","runs","args","title","finishedSteps","run","h","stepTitle","raw","k","writePlan","titles","t","old","done","kept","s","n","max","fresh","dropped","plan","updated","findStep","ref","stepStart","stepId","cur","entry","nodeId","status","startedAt","extra","at","idempotencyKey","reportStep","step","note","pending","p","e","recorded","finishWrap","out","usage","error","current","left","id","NOT_A_FAILURE","endedWithoutFailing","errorKind","closeStaleWraps","closed","TERMINAL","runRecord","steps","prevAt","durationMs","ended","liveStep","i","existsSync","mkdirSync","readFileSync","renameSync","writeFileSync","appendFileSync","dirname","resolve","randomBytes","DEVICES_FILE","LOG_FILE","MemberStore","baseDir","now","retentionDays","personId","all","d","tokenId","device","data","patch","event","force","cutoff","lines","kept","line","tmp","limit","out","i","ev","parsed","extname","SENTINEL","DANGLING","extractArtifacts","text","max","cutOff","artifacts","raw","clean","_full","inner","parsed","mime","type","plainAnswer","named","replaced","m","alt","src","ARTIFACT_TYPES","ARTIFACT_LIMITS","artifactType","filename","APP_ATTACH_HINT","appAttachHint","channel","sessionStarts","richMessages","lstatSync","mkdirSync","readdirSync","realpathSync","unlinkSync","join","resolve","OUTBOX_DIR","OUTBOX_MAX_AGE_MS","outboxPath","workspace","cwd","root","dir","prepareOutbox","opts","prune","before","removed","name","path","st"]}
|