agentix-cli 0.18.0 → 0.19.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/README.md +75 -27
- package/dist/{agent-RDVGL2TW.js → agent-57MG4LBM.js} +2 -2
- package/dist/{audit-Z47AQFFD.js → audit-JU3HWISB.js} +2 -2
- package/dist/{board-dashboard-KX2CNOSY.js → board-dashboard-HXDOYGIG.js} +2237 -69
- package/dist/board-dashboard-HXDOYGIG.js.map +1 -0
- package/dist/chunk-23AUU4S2.js +102 -0
- package/dist/chunk-23AUU4S2.js.map +1 -0
- package/dist/chunk-4MREHZP5.js +111 -0
- package/dist/chunk-4MREHZP5.js.map +1 -0
- package/dist/chunk-4YCH6IZV.js +2 -0
- package/dist/chunk-6B74TO2J.js +743 -0
- package/dist/chunk-6B74TO2J.js.map +1 -0
- package/dist/chunk-7P5QRX5W.js +108 -0
- package/dist/chunk-7P5QRX5W.js.map +1 -0
- package/dist/chunk-A6BKSZQA.js +3 -0
- package/dist/chunk-A6BKSZQA.js.map +1 -0
- package/dist/{chunk-6PPIFDOQ.js → chunk-CNNLREHD.js} +67 -22
- package/dist/{chunk-6PPIFDOQ.js.map → chunk-CNNLREHD.js.map} +1 -1
- package/dist/{chunk-J4WKMKTP.js → chunk-DLVOHCTA.js} +2 -2
- package/dist/chunk-E4ZZILAK.js +15 -0
- package/dist/chunk-E4ZZILAK.js.map +1 -0
- package/dist/{chunk-TRQSP67X.js → chunk-IKBHHTIT.js} +2 -2
- package/dist/chunk-JTJIPNXY.js +2 -0
- package/dist/chunk-JTJIPNXY.js.map +1 -0
- package/dist/chunk-MBOLXKA2.js +6 -0
- package/dist/chunk-MBOLXKA2.js.map +1 -0
- package/dist/{inbox-O5UB3YQN.js → chunk-MTRWXRFF.js} +69 -4
- package/dist/chunk-MTRWXRFF.js.map +1 -0
- package/dist/{processes-7W27G3J3.js → chunk-NOF4HSGK.js} +3 -3
- package/dist/{processes-7W27G3J3.js.map → chunk-NOF4HSGK.js.map} +1 -1
- package/dist/chunk-QW2OYR3N.js +7 -0
- package/dist/chunk-QW2OYR3N.js.map +1 -0
- package/dist/chunk-SV3I4CI7.js +440 -0
- package/dist/chunk-SV3I4CI7.js.map +1 -0
- package/dist/{chunk-JXEPJ3YD.js → chunk-TC3BSUP7.js} +5 -5
- package/dist/chunk-VWTBHLMX.js +1 -0
- package/dist/chunk-WFHIIVLL.js +719 -0
- package/dist/chunk-WFHIIVLL.js.map +1 -0
- package/dist/chunk-WPPUOTY6.js +6 -0
- package/dist/chunk-WPPUOTY6.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-S5H6DTOR.js → compaction-BTUJJ3LJ.js} +4 -4
- package/dist/config-QXWKEQON.js +2 -0
- package/dist/config-mutator-53XGR2NT.js +2 -0
- package/dist/conflict-detector-D7VIYPDI.js +2 -0
- package/dist/conflict-detector-D7VIYPDI.js.map +1 -0
- package/dist/{context-planner-2RGHJHSO.js → context-planner-I7ID7XF3.js} +3 -3
- package/dist/debug-N3B5NVJU.js +2 -0
- package/dist/{discover-OATU7SHI.js → discover-XJXQXXIO.js} +2 -2
- package/dist/doctor-SEOUFHSL.js +2 -0
- package/dist/{editor-chat-FUBQLMAQ.js → editor-chat-RVLCKKB7.js} +2 -2
- package/dist/{heal-YP7QI7D3.js → heal-YEQYWX2L.js} +2 -2
- package/dist/inbox-BIN2S5XN.js +2 -0
- package/dist/index.d.ts +830 -100
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/{ingest-whatsapp-C3O5UPBX.js → ingest-whatsapp-LZE62INA.js} +2 -2
- package/dist/loader-5CA3O5GM.js +2 -0
- package/dist/loader-6IHUCPVO.js +2 -0
- package/dist/memory-extract-KRCHD3OP.js +2 -0
- package/dist/migrate-v2-QO7SLKIM.js +4 -0
- package/dist/migrate-v2-QO7SLKIM.js.map +1 -0
- package/dist/processes-RJ6RDP32.js +2 -0
- package/dist/program-SASDC2GV.js +2 -0
- package/dist/{providers-WCQSKTNP.js → providers-WVCMIW67.js} +2 -2
- package/dist/query-HZYYRCLU.js +2 -0
- package/dist/recipes-EPLE2SNT.js +2 -0
- package/dist/registry-HM4ELSXS.js +2 -0
- package/dist/rotation-memo-GMTZPMNE.js +13 -0
- package/dist/rotation-memo-GMTZPMNE.js.map +1 -0
- package/dist/scheduler-XVFIK4XH.js +2 -0
- package/dist/{slack-D27BE37Q.js → slack-6P7ZJ32Y.js} +2 -2
- package/dist/store-BAPDZHXE.js +2 -0
- package/dist/{subagent-EPOT3QDM.js → subagent-WV7QKQ3L.js} +2 -2
- package/dist/{task-store-3NUWLYRY.js → task-store-RHISEU6V.js} +2 -2
- package/dist/{telegram-UWV67KWG.js → telegram-PIYDRM46.js} +2 -2
- package/dist/timers-5LPZN55J.js +2 -0
- package/dist/types-VECTMYTO.js +2 -0
- package/dist/types-VECTMYTO.js.map +1 -0
- package/dist/types-ZRLHHCGB.js +2 -0
- package/dist/types-ZRLHHCGB.js.map +1 -0
- package/dist/web/activity-graph.global.js +654 -0
- package/dist/web/activity-graph.global.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1 -1
- package/dist/web/workflow-editor.global.js.map +1 -1
- package/dist/{whatsapp-KJ2NRKKQ.js → whatsapp-PFRDMUMT.js} +2 -2
- package/dist/{whatsapp-state-3ZVQ6MQD.js → whatsapp-state-XMTCSRXU.js} +2 -2
- package/dist/wiki-53W5NLZV.js +2 -0
- package/dist/wiki-53W5NLZV.js.map +1 -0
- package/dist/workspace-setup-FPGN5VNY.js +2 -0
- package/dist/workspace-setup-FPGN5VNY.js.map +1 -0
- package/package.json +3 -2
- package/dist/board-dashboard-KX2CNOSY.js.map +0 -1
- package/dist/chunk-34NG7UDX.js +0 -11
- package/dist/chunk-34NG7UDX.js.map +0 -1
- package/dist/chunk-ASSJ4Z6Z.js +0 -164
- package/dist/chunk-ASSJ4Z6Z.js.map +0 -1
- package/dist/chunk-BLUXL6HR.js +0 -854
- package/dist/chunk-BLUXL6HR.js.map +0 -1
- package/dist/chunk-BTAEYL3P.js +0 -72
- package/dist/chunk-BTAEYL3P.js.map +0 -1
- package/dist/chunk-DSCMCVTV.js +0 -7
- package/dist/chunk-DSCMCVTV.js.map +0 -1
- package/dist/chunk-HPDHNIA3.js +0 -2
- package/dist/chunk-IM376SD6.js +0 -13
- package/dist/chunk-IM376SD6.js.map +0 -1
- package/dist/chunk-LEIYC3BF.js +0 -104
- package/dist/chunk-LEIYC3BF.js.map +0 -1
- package/dist/chunk-LY7JGDV7.js +0 -6
- package/dist/chunk-LY7JGDV7.js.map +0 -1
- package/dist/chunk-U364GNQX.js +0 -719
- package/dist/chunk-U364GNQX.js.map +0 -1
- package/dist/chunk-WZEYII2S.js +0 -6
- package/dist/chunk-WZEYII2S.js.map +0 -1
- package/dist/config-UOHEG4BW.js +0 -2
- package/dist/config-mutator-KSEBWKJH.js +0 -2
- package/dist/debug-TGAP7E7W.js +0 -2
- package/dist/inbox-O5UB3YQN.js.map +0 -1
- package/dist/loader-BGWA7IRY.js +0 -2
- package/dist/loader-LBHKSXBK.js +0 -2
- package/dist/memory-extract-KKPKAOPH.js +0 -2
- package/dist/program-2MDELQLX.js +0 -2
- package/dist/query-FZAL7AQD.js +0 -2
- package/dist/recipes-LZ56TA3X.js +0 -2
- package/dist/registry-ONJSILH4.js +0 -2
- package/dist/scheduler-XRM2RJIV.js +0 -2
- package/dist/store-4UCO56K2.js +0 -2
- package/dist/timers-QB4KYNAE.js +0 -2
- package/dist/types-ECMEC5BZ.js +0 -2
- package/dist/types-TTB6DJUE.js +0 -2
- package/dist/usage-dashboard-FQGPPABD.js +0 -803
- package/dist/usage-dashboard-FQGPPABD.js.map +0 -1
- package/dist/wiki-OUYKM7MB.js +0 -2
- package/dist/workspace-setup-6GDBEORT.js +0 -2
- /package/dist/{agent-RDVGL2TW.js.map → agent-57MG4LBM.js.map} +0 -0
- /package/dist/{audit-Z47AQFFD.js.map → audit-JU3HWISB.js.map} +0 -0
- /package/dist/{chunk-HPDHNIA3.js.map → chunk-4YCH6IZV.js.map} +0 -0
- /package/dist/{chunk-J4WKMKTP.js.map → chunk-DLVOHCTA.js.map} +0 -0
- /package/dist/{chunk-TRQSP67X.js.map → chunk-IKBHHTIT.js.map} +0 -0
- /package/dist/{chunk-JXEPJ3YD.js.map → chunk-TC3BSUP7.js.map} +0 -0
- /package/dist/{config-UOHEG4BW.js.map → chunk-VWTBHLMX.js.map} +0 -0
- /package/dist/{compaction-S5H6DTOR.js.map → compaction-BTUJJ3LJ.js.map} +0 -0
- /package/dist/{config-mutator-KSEBWKJH.js.map → config-QXWKEQON.js.map} +0 -0
- /package/dist/{debug-TGAP7E7W.js.map → config-mutator-53XGR2NT.js.map} +0 -0
- /package/dist/{context-planner-2RGHJHSO.js.map → context-planner-I7ID7XF3.js.map} +0 -0
- /package/dist/{heal-YP7QI7D3.js.map → debug-N3B5NVJU.js.map} +0 -0
- /package/dist/{discover-OATU7SHI.js.map → discover-XJXQXXIO.js.map} +0 -0
- /package/dist/{loader-BGWA7IRY.js.map → doctor-SEOUFHSL.js.map} +0 -0
- /package/dist/{editor-chat-FUBQLMAQ.js.map → editor-chat-RVLCKKB7.js.map} +0 -0
- /package/dist/{loader-LBHKSXBK.js.map → heal-YEQYWX2L.js.map} +0 -0
- /package/dist/{memory-extract-KKPKAOPH.js.map → inbox-BIN2S5XN.js.map} +0 -0
- /package/dist/{ingest-whatsapp-C3O5UPBX.js.map → ingest-whatsapp-LZE62INA.js.map} +0 -0
- /package/dist/{program-2MDELQLX.js.map → loader-5CA3O5GM.js.map} +0 -0
- /package/dist/{providers-WCQSKTNP.js.map → loader-6IHUCPVO.js.map} +0 -0
- /package/dist/{query-FZAL7AQD.js.map → memory-extract-KRCHD3OP.js.map} +0 -0
- /package/dist/{recipes-LZ56TA3X.js.map → processes-RJ6RDP32.js.map} +0 -0
- /package/dist/{registry-ONJSILH4.js.map → program-SASDC2GV.js.map} +0 -0
- /package/dist/{scheduler-XRM2RJIV.js.map → providers-WVCMIW67.js.map} +0 -0
- /package/dist/{store-4UCO56K2.js.map → query-HZYYRCLU.js.map} +0 -0
- /package/dist/{task-store-3NUWLYRY.js.map → recipes-EPLE2SNT.js.map} +0 -0
- /package/dist/{timers-QB4KYNAE.js.map → registry-HM4ELSXS.js.map} +0 -0
- /package/dist/{types-ECMEC5BZ.js.map → scheduler-XVFIK4XH.js.map} +0 -0
- /package/dist/{slack-D27BE37Q.js.map → slack-6P7ZJ32Y.js.map} +0 -0
- /package/dist/{types-TTB6DJUE.js.map → store-BAPDZHXE.js.map} +0 -0
- /package/dist/{subagent-EPOT3QDM.js.map → subagent-WV7QKQ3L.js.map} +0 -0
- /package/dist/{wiki-OUYKM7MB.js.map → task-store-RHISEU6V.js.map} +0 -0
- /package/dist/{telegram-UWV67KWG.js.map → telegram-PIYDRM46.js.map} +0 -0
- /package/dist/{workspace-setup-6GDBEORT.js.map → timers-5LPZN55J.js.map} +0 -0
- /package/dist/{whatsapp-KJ2NRKKQ.js.map → whatsapp-PFRDMUMT.js.map} +0 -0
- /package/dist/{whatsapp-state-3ZVQ6MQD.js.map → whatsapp-state-XMTCSRXU.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workflows/store.ts","../src/workflows/types.ts","../src/workflows/run-store.ts","../src/workflows/layout-store.ts","../src/workflows/engine.ts","../src/workflows/template.ts","../src/workflows/node-concurrency.ts","../src/workflows/nodes/handlers.ts","../src/workflows/signals.ts","../src/forms/validator.ts","../src/workflows/dispatcher.ts","../src/workflows/hooks.ts","../src/workflows/triggers.ts","../src/business/work-pool.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve, relative } from \"path\"\nimport { lintWorkflow, workflowSchema, type Workflow } from \"./types\"\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// No YAML dep — authors can hand-write JSON or use the visual editor.\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 resolve(this.baseDir, `${id}.json`)\n }\n\n /** Returns every well-formed workflow. Malformed files are skipped; the\n * CLI `validate` subcommand is where authors get details. */\n list(): Workflow[] {\n if (!existsSync(this.baseDir)) return []\n const out: Workflow[] = []\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\") || entry.name.startsWith(\"_\")) continue\n const full = resolve(this.baseDir, entry.name)\n try {\n const raw = JSON.parse(readFileSync(full, \"utf-8\"))\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // intentional: skip malformed\n }\n }\n return out.sort((a, b) => a.id.localeCompare(b.id))\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 for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\") || entry.name.startsWith(\"_\")) continue\n const full = resolve(this.baseDir, entry.name)\n const rel = relative(this.baseDir, full)\n let rawJson: unknown\n try {\n rawJson = JSON.parse(readFileSync(full, \"utf-8\"))\n } catch (e: any) {\n results.push({ id: entry.name.replace(/\\.json$/, \"\"), path: rel, issues: [`parse error: ${e.message}`], isValid: false })\n continue\n }\n const parsed = workflowSchema.safeParse(rawJson)\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\n results.push({ id: String((rawJson as any)?.id ?? entry.name.replace(/\\.json$/, \"\")), 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 p = this.pathFor(id)\n if (!existsSync(p)) return null\n try {\n const raw = JSON.parse(readFileSync(p, \"utf-8\"))\n const parsed = workflowSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n save(workflow: Workflow): 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 writeFileSync(this.pathFor(normalized.id), JSON.stringify(normalized, null, 2) + \"\\n\")\n return normalized\n }\n\n delete(id: string): boolean {\n const p = this.pathFor(id)\n if (!existsSync(p)) return false\n unlinkSync(p)\n return true\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 // Compute\n \"agent\",\n \"transform\",\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 // BPM: human tasks + composition + signals + intermediate timer\n \"userTask\",\n \"subProcess\",\n \"signal.emit\",\n \"signal.wait\",\n \"timer.boundary\",\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\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 /** 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\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 /** 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 created: z.string().optional(),\n updated: z.string().optional(),\n})\nexport type Workflow = z.infer<typeof workflowSchema>\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`, `userTask`, `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 === \"userTask\"\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, userTask, 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 === \"checkpoint\"\n || type === \"userTask\"\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})\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 userTask / 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(\"userTask\"),\n nodeId: z.string(),\n /** Task identifier — matches the record under _tasks/<taskId>.json. */\n taskId: z.string(),\n /** Assignee ref encoded as \"actor:<id>\" or \"role:<id>\". */\n assignee: z.string(),\n /** Concrete actors who currently see the task (resolved via role strategy). */\n assignedTo: z.array(z.string()).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])\nexport type PausedAt = z.infer<typeof pausedAtSchema>\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 createdAt: z.string(),\n updatedAt: z.string(),\n})\nexport type WorkflowRun = z.infer<typeof workflowRunSchema>\n","import { createHash, randomUUID } from \"crypto\"\nimport { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport {\n type EntityRef,\n type NodeExecutionEntry,\n type PausedAt,\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 }): WorkflowRun {\n const now = new Date().toISOString()\n const 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 joinCounters: {},\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 /** 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 { Condition, Workflow, WorkflowEdge, 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 const firing = fromNode.type === \"branch\"\n ? outgoing.filter((e) => (e.fromPort ?? \"\") === (selectedPort ?? \"\"))\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\nfunction 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","// --- 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 { evaluateBranch, getByPath } from \"../engine\"\nimport { renderParams, render } from \"../template\"\nimport { formSchemaSchema, type FormSchema } from \"../../forms/types\"\nimport { parseAssigneeRef } from \"../../actors/types\"\nimport { nodeConcurrencyGate, nodeKey } from \"../node-concurrency\"\nimport type { NodeContext, NodeHandler, NodeResult } from \"./types\"\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 const prompt = render(promptTemplate, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const timeoutMinutes = typeof cfg.timeoutMinutes === \"number\" ? cfg.timeoutMinutes : undefined\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 })\n const durationMs = Date.now() - start\n if (resp.error) {\n ctx.log(`[node:${ctx.node.id}] agent \"${agentId}\" failed: ${resp.error}`)\n return { error: resp.error }\n }\n const parser = String(cfg.resultParser ?? \"noqta-result-token\")\n const parsed = parser === \"json\" ? extractJsonBlock(resp.content) : parseResultToken(resp.content)\n return {\n output: {\n reply: resp.content,\n result: parsed.result,\n json: parsed.json,\n taskId: resp.taskId,\n durationMs,\n },\n }\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 clawd-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/** 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/** userTask: assign work to an actor or role, render a form to them, pause\n * the run until POST /workflow/task/:taskId/submit. Task records live in\n * the TaskStore so the inbox + channel renderers can find them. */\nconst userTaskHandler: 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 assigneeRaw = String(rendered.assignTo ?? \"\")\n if (!assigneeRaw) return { error: `userTask \"${ctx.node.id}\" missing config.assignTo` }\n const ref = parseAssigneeRef(assigneeRaw)\n if (!ref) return { error: `userTask \"${ctx.node.id}\" assignTo must be \"actor:<id>\" or \"role:<id>\", got \"${assigneeRaw}\"` }\n\n const formRaw = (cfg.form ?? rendered.form) as unknown\n const form = coerceForm(formRaw)\n if (!form) return { error: `userTask \"${ctx.node.id}\" missing or invalid config.form` }\n\n const title = String(rendered.title ?? form.title ?? ctx.node.id)\n const description = typeof rendered.description === \"string\" ? rendered.description : undefined\n\n if (!ctx.actors) return { error: `userTask \"${ctx.node.id}\" requires ActorStore in context (engine misconfigured)` }\n if (!ctx.tasks) return { error: `userTask \"${ctx.node.id}\" requires TaskStore in context (engine misconfigured)` }\n\n const assignedTo = ctx.actors.pickAssignees(ref)\n if (assignedTo.length === 0) {\n return { error: `userTask \"${ctx.node.id}\" assignee \"${assigneeRaw}\" resolved to zero actors` }\n }\n\n const dueAt = computeDueAt(rendered.dueIn)\n\n const task = ctx.tasks.create({\n runId: ctx.run.id,\n workflowId: ctx.workflow.id,\n nodeId: ctx.node.id,\n title,\n description,\n assignee: assigneeRaw,\n assignedTo,\n form,\n dueAt,\n })\n\n ctx.log(`[node:${ctx.node.id}] userTask created ${task.id} for ${assigneeRaw} → actors [${assignedTo.join(\", \")}]`)\n\n return {\n paused: true,\n pausedAt: {\n kind: \"userTask\",\n nodeId: ctx.node.id,\n taskId: task.id,\n assignee: assigneeRaw,\n assignedTo,\n },\n }\n}\n\nfunction coerceForm(raw: unknown): FormSchema | null {\n if (!raw || typeof raw !== \"object\") return null\n const parsed = formSchemaSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\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 \"agent\": agentHandler,\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 \"userTask\": userTaskHandler,\n \"subProcess\": subProcessHandler,\n \"signal.emit\": signalEmitHandler,\n \"signal.wait\": signalWaitHandler,\n \"timer.boundary\": timerBoundaryHandler,\n \"checkpoint\": checkpointHandler,\n \"end\": endHandler,\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. */\nfunction 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 { FormField, FormSchema, FormSubmission } from \"./types\"\n\n// --- Engine-side form validation ---\n//\n// One validator shared by all renderers. Channel adapters capture raw\n// strings; the engine coerces to the declared type and enforces required /\n// pattern / min / max rules. Returns a list of human-readable errors — an\n// empty list means the submission is valid.\n\nexport interface ValidationError {\n field: string\n message: string\n}\n\nexport interface ValidationResult {\n ok: boolean\n errors: ValidationError[]\n /** Coerced values — numbers as numbers, booleans as booleans, etc. Only\n * populated when ok === true; partial on failure so callers can show\n * per-field state. */\n values: Record<string, unknown>\n}\n\nexport function validateSubmission(schema: FormSchema, submission: FormSubmission): ValidationResult {\n const errors: ValidationError[] = []\n const coerced: Record<string, unknown> = {}\n\n for (const field of schema.fields) {\n const raw = submission.values[field.key]\n const present = raw !== undefined && raw !== null && raw !== \"\"\n\n if (!present) {\n if (field.required) {\n errors.push({ field: field.key, message: `\"${field.label}\" is required` })\n } else if (field.defaultValue !== undefined) {\n coerced[field.key] = field.defaultValue\n }\n continue\n }\n\n const result = coerceField(field, raw)\n if (result.error) {\n errors.push({ field: field.key, message: result.error })\n continue\n }\n coerced[field.key] = result.value\n }\n\n return { ok: errors.length === 0, errors, values: coerced }\n}\n\nfunction coerceField(field: FormField, raw: unknown): { value?: unknown; error?: string } {\n const label = field.label\n switch (field.type) {\n case \"text\":\n case \"long-text\": {\n const str = String(raw)\n if (field.validate?.min !== undefined && str.length < field.validate.min) {\n return { error: `\"${label}\" must be at least ${field.validate.min} characters` }\n }\n if (field.validate?.max !== undefined && str.length > field.validate.max) {\n return { error: `\"${label}\" must be at most ${field.validate.max} characters` }\n }\n if (field.validate?.pattern) {\n const re = new RegExp(field.validate.pattern)\n if (!re.test(str)) return { error: `\"${label}\" does not match the expected format` }\n }\n return { value: str }\n }\n case \"number\": {\n const num = typeof raw === \"number\" ? raw : Number(String(raw))\n if (!Number.isFinite(num)) return { error: `\"${label}\" must be a number` }\n if (field.validate?.min !== undefined && num < field.validate.min) {\n return { error: `\"${label}\" must be at least ${field.validate.min}` }\n }\n if (field.validate?.max !== undefined && num > field.validate.max) {\n return { error: `\"${label}\" must be at most ${field.validate.max}` }\n }\n return { value: num }\n }\n case \"boolean\": {\n if (typeof raw === \"boolean\") return { value: raw }\n const str = String(raw).toLowerCase()\n if ([\"true\", \"yes\", \"y\", \"1\"].includes(str)) return { value: true }\n if ([\"false\", \"no\", \"n\", \"0\"].includes(str)) return { value: false }\n return { error: `\"${label}\" must be yes/no` }\n }\n case \"date\": {\n const d = typeof raw === \"string\" || typeof raw === \"number\" ? new Date(raw) : null\n if (!d || Number.isNaN(d.getTime())) return { error: `\"${label}\" is not a valid date` }\n return { value: d.toISOString() }\n }\n case \"select\": {\n const str = String(raw)\n const options = field.options ?? []\n if (options.length && !options.includes(str)) {\n return { error: `\"${label}\" must be one of: ${options.join(\", \")}` }\n }\n return { value: str }\n }\n case \"multi-select\": {\n const arr = Array.isArray(raw) ? raw.map(String) : String(raw).split(\",\").map((s) => s.trim()).filter(Boolean)\n const options = field.options ?? []\n if (options.length) {\n const bad = arr.filter((v) => !options.includes(v))\n if (bad.length) return { error: `\"${label}\" contains invalid values: ${bad.join(\", \")}` }\n }\n return { value: arr }\n }\n case \"file\": {\n // For MVP, \"file\" is a URL or path string; channel adapters upload and\n // substitute before submission. Deeper validation is deferred.\n return { value: String(raw) }\n }\n }\n return { error: `unknown field type: ${field.type}` }\n}\n","import { evaluateBranch, findNode, initialPendingFromTrigger, nextNodes } from \"./engine\"\nimport { resolveHandler } from \"./nodes/handlers\"\nimport type { AgentExecuteRequest, AgentExecuteResponse } from \"./nodes/types\"\nimport { RunStore, idempotencyKey } from \"./run-store\"\nimport type { WorkflowStore } from \"./store\"\nimport { TaskStore } from \"./task-store\"\nimport { TimerService, type TimerRecord } from \"./timers\"\nimport { SignalBus, matchesSignal, type SignalEmission } from \"./signals\"\nimport { ActorStore } from \"../actors/store\"\nimport { validateSubmission } from \"../forms/validator\"\nimport type { FormSubmission } from \"../forms/types\"\nimport type { EntityRef, NodeExecutionEntry, Workflow, WorkflowRun } from \"./types\"\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 Actor/Role store. If omitted, constructed with defaults so\n * userTask handlers still work out of the box (reads from .agentx/actors/\n * and .agentx/roles/). */\n actors?: ActorStore\n /** Optional user-task store. If omitted, constructed with defaults\n * (.agentx/workflows/_tasks/). */\n tasks?: TaskStore\n /** Optional form-renderer hook for user tasks. Called after a userTask\n * record is persisted — the hook delivers the form to the assignee's\n * preferred channel(s). Best-effort; failures are logged. */\n renderUserTask?: (task: import(\"./task-store\").UserTaskRecord) => Promise<void> | 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}\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\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 actors: ActorStore\n readonly tasks: TaskStore\n readonly timers: TimerService\n readonly signals: SignalBus\n private readonly renderUserTask?: (task: import(\"./task-store\").UserTaskRecord) => Promise<void> | void\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\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.actors = opts.actors ?? new ActorStore()\n this.tasks = opts.tasks ?? new TaskStore()\n this.timers = opts.timers ?? new TimerService({ log: (m) => this.log(m) })\n this.signals = opts.signals ?? new SignalBus()\n this.renderUserTask = opts.renderUserTask\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 /** 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 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 const node = findNode(wf, run.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 const entry: NodeExecutionEntry = {\n at: output.receivedAt,\n nodeId: run.pausedAt.nodeId,\n inputKeys: [],\n status: \"resumed\",\n output,\n idempotencyKey: idempotencyKey(run.id, run.pausedAt.nodeId, `signal:${emission.name}:${emission.emittedAt}`),\n }\n const newContext: Record<string, Record<string, unknown>> = { ...run.context, [run.pausedAt.nodeId]: output }\n this.runs.recordExecution({\n runId: run.id,\n entry,\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: newContext,\n })\n this.log(`[workflow:${wf.id}] run ${run.id} resumed from signal \"${emission.name}\"`)\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 run = this.runs.get(t.runId)\n if (!run || run.status !== \"paused\") return\n if (!run.pausedAt || run.pausedAt.kind !== \"timerWait\" || run.pausedAt.nodeId !== t.nodeId) return\n const wf = this.store.list().find((w) => w.id === run.workflowId)\n if (!wf) 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 const entry: NodeExecutionEntry = {\n at: firedAt,\n nodeId: t.nodeId,\n inputKeys: [],\n status: \"resumed\",\n output,\n idempotencyKey: idempotencyKey(run.id, t.nodeId, `timer:${t.id}`),\n }\n const newContext: Record<string, Record<string, unknown>> = { ...run.context, [t.nodeId]: output }\n this.runs.recordExecution({\n runId: run.id,\n entry,\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: newContext,\n })\n this.log(`[workflow:${wf.id}] run ${run.id} resumed from timer \"${t.nodeId}\" (fired ${firedAt})`)\n void this.walk(wf, run.id, `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 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 // 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 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 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 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 // 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 try { await this.walkInner(workflow, runId, triggeringEventId) }\n finally { this.stopChannelAck(runId) }\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 while (run.status === \"running\" && run.pending.length > 0) {\n const nodeId: string = run.pending[0]\n const remaining: string[] = run.pending.slice(1)\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: run.id,\n entry: {\n at: new Date().toISOString(),\n nodeId,\n inputKeys: [],\n status: \"failed\",\n idempotencyKey: idempotencyKey(run.id, nodeId, triggeringEventId),\n note: \"node definition missing from workflow\",\n },\n nextPending: remaining,\n status: \"failed\",\n })\n return\n }\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: run.id,\n entry: {\n at: new Date().toISOString(),\n nodeId,\n inputKeys: [],\n status: \"failed\",\n idempotencyKey: idempotencyKey(run.id, nodeId, triggeringEventId),\n note: `no handler for node type \"${node.type}\"`,\n },\n nextPending: remaining,\n status: \"failed\",\n })\n return\n }\n\n const key = idempotencyKey(run.id, nodeId, triggeringEventId)\n if (run.history.some((h) => h.idempotencyKey === key)) {\n // Already executed — skip. Dispatcher advances past without\n // re-running, to support idempotent webhook retries.\n run = this.runs.recordExecution({\n runId: run.id,\n entry: {\n at: new Date().toISOString(),\n nodeId,\n inputKeys: [],\n status: \"skipped\",\n idempotencyKey: key + \"_dedup\",\n note: \"duplicate execution dropped\",\n },\n nextPending: remaining,\n }) ?? run\n continue\n }\n\n const inputKeys = workflow.edges.filter((e) => e.to === nodeId).map((e) => e.from)\n let result\n try {\n result = await handler({\n workflow, run, node,\n channels: this.channels,\n agents: this.agents,\n actors: this.actors,\n tasks: this.tasks,\n forwardChannelSend: this.forwarder?.forwardChannelSend?.bind(this.forwarder),\n log: this.log,\n })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] handler \"${node.type}\" threw: ${e.message}`)\n result = { error: e.message }\n }\n\n const now = new Date().toISOString()\n if (result.error) {\n const entry: NodeExecutionEntry = {\n at: now, nodeId, inputKeys,\n status: \"failed\",\n idempotencyKey: key,\n note: result.error.slice(0, 200),\n }\n run = this.runs.recordExecution({ runId: run.id, entry, nextPending: remaining, status: \"failed\" }) ?? run\n return\n }\n if (result.paused && result.pausedAt) {\n // Sub-process pause: spawn the child run (no walk yet), patch the\n // child id into pausedAt, persist the parent's pause, THEN kick the\n // child walk. Decoupling the walk kick from spawn avoids a race\n // where a fast child finishes before the parent's pause is on disk.\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: run,\n parentNodeId: nodeId,\n childWorkflowId: result.spawnChild.workflowId,\n input: result.spawnChild.input,\n })\n if (!spawnedChild) {\n const entry: NodeExecutionEntry = {\n at: now, nodeId, inputKeys,\n status: \"failed\",\n idempotencyKey: key,\n note: `subProcess: child workflow \"${result.spawnChild.workflowId}\" not found`,\n }\n run = this.runs.recordExecution({ runId: run.id, entry, nextPending: remaining, status: \"failed\" }) ?? run\n return\n }\n pausedAt = { ...pausedAt, childRunId: spawnedChild.id }\n }\n const entry: NodeExecutionEntry = {\n at: now, nodeId, inputKeys,\n status: \"paused\",\n idempotencyKey: key,\n }\n run = this.runs.recordExecution({\n runId: run.id, entry,\n nextPending: remaining,\n status: \"paused\",\n pausedAt,\n }) ?? run\n\n // Parent pause is now on disk — safe to kick the child walk.\n if (spawnedChild) this.kickChildWalk(spawnedChild)\n\n // Schedule the timer for timerWait pauses. A fast fireAt (already\n // past) fires on the next tick.\n if (pausedAt.kind === \"timerWait\") {\n try {\n this.timers.schedule({\n runId: run.id,\n workflowId: workflow.id,\n nodeId: pausedAt.nodeId,\n fireAt: pausedAt.fireAt,\n cancelKey: `${run.id}:${pausedAt.nodeId}`,\n })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] timer schedule failed: ${e.message}`)\n }\n }\n\n // Render userTask AFTER persisting the pause, so a failing channel\n // renderer doesn't lose the task record (retry from inbox works).\n if (pausedAt.kind === \"userTask\" && this.renderUserTask) {\n const taskRecord = this.tasks.get(pausedAt.taskId)\n if (taskRecord) {\n try { await this.renderUserTask(taskRecord) }\n catch (e: any) { this.log(`[workflow:${workflow.id}] userTask render failed: ${e.message}`) }\n }\n }\n return\n }\n\n // Success — fold the node's output into context, enqueue successors.\n const output: Record<string, unknown> = result.output ?? {}\n const newContext: Record<string, Record<string, unknown>> = { ...run.context, [nodeId]: output }\n const { nextPending } = nextNodes({ workflow, fromNodeId: nodeId, selectedPort: result.port })\n\n // Join-gate filtering: for each successor that is a\n // `gateway.parallel(mode=join)`, bump the arrival counter. Only\n // enqueue the join once every incoming edge has delivered.\n const readyNext: string[] = []\n const joinCounters: Record<string, string[]> = { ...(run.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 = [...remaining, ...readyNext]\n const terminal = node.type === \"end\"\n\n const entry: NodeExecutionEntry = {\n at: now,\n nodeId,\n inputKeys,\n status: \"ok\",\n output,\n idempotencyKey: key,\n }\n run = this.runs.recordExecution({\n runId: run.id,\n entry,\n nextPending: terminal ? [] : merged,\n status: terminal ? (String(node.config.status ?? \"completed\") as WorkflowRun[\"status\"]) : \"running\",\n context: newContext,\n joinCounters,\n }) ?? run\n\n // Side-effect: emit signals produced by `signal.emit` nodes. Runs\n // AFTER the exec entry is persisted so listeners observing the run\n // see the complete history before reacting.\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) {\n // Sub-process child reached its end node → notify the parent so it\n // can resume at its subProcess node. Fire-and-forget; any failure\n // is logged but doesn't block the child's terminal state.\n if (run.parentRunId && run.parentNodeId) {\n void this.resumeParent({\n parentRunId: run.parentRunId,\n parentNodeId: run.parentNodeId,\n childRun: run,\n }).catch((e: any) => this.log(`[workflow:${workflow.id}] resumeParent failed: ${e.message}`))\n }\n return\n }\n }\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 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 parentWf = this.store.list().find((w) => w.id === parent.workflowId)\n if (!parentWf) 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\n // The child's output bundle is its end-node inputs — take the last\n // non-empty node output in the child's history as a simplification.\n const childOutput: Record<string, unknown> = {\n childRunId: args.childRun.id,\n status: args.childRun.status,\n output: pickChildOutput(args.childRun),\n }\n\n const entry: NodeExecutionEntry = {\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 const newContext: Record<string, Record<string, unknown>> = {\n ...parent.context,\n [args.parentNodeId]: childOutput,\n }\n this.runs.recordExecution({\n runId: parent.id,\n entry,\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: newContext,\n })\n this.log(`[workflow:${parentWf.id}] parent run ${parent.id} resumed from subProcess child ${args.childRun.id}`)\n void this.walk(parentWf, parent.id, `child:${args.childRun.id}`)\n }\n\n // ---------------- User-task submit / resume ----------------\n\n async submitTask(taskId: string, submission: FormSubmission, submittedBy: string): Promise<\n { ok: true; runId: string } | { ok: false; error: string; fieldErrors?: Array<{ field: string; message: string }> }\n > {\n const task = this.tasks.get(taskId)\n if (!task) return { ok: false, error: `task not found: ${taskId}` }\n if (task.status !== \"open\") return { ok: false, error: `task is ${task.status}` }\n\n const validated = validateSubmission(task.form, submission)\n if (!validated.ok) return { ok: false, error: \"form validation failed\", fieldErrors: validated.errors }\n\n const parent = this.runs.get(task.runId)\n if (!parent || parent.status !== \"paused\") return { ok: false, error: `run ${task.runId} is not paused` }\n if (!parent.pausedAt || parent.pausedAt.kind !== \"userTask\" || parent.pausedAt.taskId !== taskId) {\n return { ok: false, error: `run ${task.runId} is not waiting on task ${taskId}` }\n }\n\n const parentWf = this.store.list().find((w) => w.id === parent.workflowId)\n if (!parentWf) return { ok: false, error: `workflow ${parent.workflowId} not found` }\n\n const node = findNode(parentWf, task.nodeId)\n const successors = node ? parentWf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n\n const output: Record<string, unknown> = {\n submittedBy,\n submittedAt: new Date().toISOString(),\n values: validated.values,\n action: submission.action,\n }\n const entry: NodeExecutionEntry = {\n at: new Date().toISOString(),\n nodeId: task.nodeId,\n inputKeys: [],\n status: \"resumed\",\n output,\n idempotencyKey: idempotencyKey(parent.id, task.nodeId, `task:${taskId}:${submission.action}`),\n }\n const newContext = { ...parent.context, [task.nodeId]: output }\n this.runs.recordExecution({\n runId: parent.id,\n entry,\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: newContext,\n })\n this.tasks.save({\n ...task,\n status: \"completed\",\n submittedBy,\n submittedAt: output.submittedAt as string,\n submittedValues: validated.values,\n submittedAction: submission.action,\n })\n this.log(`[workflow:${parentWf.id}] run ${parent.id} resumed from userTask ${taskId}`)\n void this.walk(parentWf, parent.id, `task:${taskId}`)\n return { ok: true, runId: parent.id }\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 \"21624309128\", JID\n * \"21624309128@s.whatsapp.net\", group JID \"...-...@g.us\", and human-formatted\n * \"+216 24 309 128\") 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\nexport { idempotencyKey } from \"./run-store\"\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 source = channelToTriggerSource(msg.channel)\n if (!source) return {}\n\n const chatId = msg.group?.id ?? msg.sender.id\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 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","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\"\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}): { cronTimers: number; hookSubscribers: number } {\n const workflows = args.store.list()\n let cronTimers = 0\n let hookSubscribers = 0\n\n for (const wf of workflows) {\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 { event?: string }\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 // 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 } catch (e: any) {\n args.log(`[workflows] ${wf.id} hook dispatch failed: ${e.message}`)\n }\n return {}\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 { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from \"fs\"\nimport { resolve, join } from \"path\"\nimport type { BusinessConfig, BusinessWorkSource } from \"./config\"\nimport type { DaemonConfig } from \"@/daemon/config\"\n\n// --- Work pool: pluggable source of tasks for the business layer ---\n\nexport interface WorkItem {\n id: string // stable id for claim/report (e.g. \"backlog:line-12\", \"gitlab:proj:issue:42\")\n title: string\n description?: string\n assignee?: string // agentId\n estimatedSeconds?: number\n url?: string\n priority?: number // lower = higher priority\n /** Board stage derived from labels (kanban column id). */\n stage?: \"triage\" | \"todo\" | \"doing\" | \"onhold\" | \"review\" | \"done\"\n /** Raw labels from the source (GitLab). Empty array if source doesn't support labels. */\n labels?: string[]\n /** Label name+color when the source returns colored labels (GitLab with_labels_details). */\n labelDetails?: Array<{ name: string; color: string; text_color?: string }>\n /** ISO timestamp of last update on the source. */\n updatedAt?: string\n /** Open/closed state from the source. */\n state?: \"opened\" | \"closed\"\n /** Optional milestone title (GitLab: issue.milestone.title). */\n milestone?: string\n /** GitLab assignees (0..n). `assignee` above is the agentId alias for assignees[0]. */\n assignees?: Array<{ username: string; name?: string; avatarUrl?: string }>\n}\n\n/** Member of a source (GitLab project members) — used for assignee pickers. */\nexport interface WorkMember {\n username: string\n name?: string\n avatarUrl?: string\n}\n\n/** Milestone on a source — used for milestone pickers. */\nexport interface WorkMilestone {\n id: number | string\n title: string\n state?: \"active\" | \"closed\"\n dueDate?: string\n}\n\n/** Full issue detail — a superset of WorkItem for the detail view. */\nexport interface WorkDetail extends WorkItem {\n /** Raw body, may be markdown. */\n descriptionHtml?: string\n /** ISO timestamp of creation. */\n createdAt?: string\n /** Author (issue reporter). */\n author?: { username: string; name?: string; avatarUrl?: string }\n /** Full labels list (all of them). */\n allLabels?: string[]\n}\n\nexport interface WorkReport {\n status: \"in-progress\" | \"done\" | \"blocked\"\n note?: string\n timeSeconds?: number\n blocker?: string\n}\n\n/** What a WorkSource can do beyond the minimal listOpen/claim/report triple. */\nexport interface WorkSourceCapabilities {\n /** Enumerate items beyond assignee filter — required for a board view. */\n listAll: boolean\n /** Create new items (e.g. POST GitLab issue). */\n create: boolean\n /** Arbitrary column transitions (label add/remove pair). */\n transition: boolean\n /** Surfaces labels[] on returned items. */\n labels: boolean\n /** Server-side text search. */\n search: boolean\n}\n\nexport interface WorkCreateInput {\n title: string\n description?: string\n /** agentId */\n assignee?: string\n /** Raw GitLab usernames — bypass agentId resolution for UI-driven creates. */\n assigneeUsernames?: string[]\n labels?: string[]\n /** Milestone title (resolved to id). */\n milestoneTitle?: string\n /** For sources that span multiple projects (gitlab): \"group/project\". */\n projectHint?: string\n}\n\nexport interface ListAllOpts {\n sinceDays?: number\n labels?: string[]\n /** Exclude items carrying any of these labels (client-side filter). */\n notLabels?: string[]\n /** Exclude items whose labels carry this scoped prefix (e.g. \"Status\"). */\n withoutScopedPrefix?: string\n search?: string\n assignee?: string\n /** GitLab issue state. Defaults to \"opened\" (boards hide closed issues). */\n state?: \"opened\" | \"closed\" | \"all\"\n /** Milestone filter (GitLab \"milestone\" query param). */\n milestone?: string\n}\n\nexport interface WorkSource {\n type: string\n capabilities: WorkSourceCapabilities\n listOpen(agentId: string): Promise<WorkItem[]>\n claim(agentId: string, itemId: string): Promise<void>\n report(itemId: string, update: WorkReport): Promise<void>\n /** Optional — gated by capabilities.listAll. */\n listAll?(opts: ListAllOpts): Promise<WorkItem[]>\n /** Optional — gated by capabilities.create. */\n create?(input: WorkCreateInput): Promise<WorkItem>\n /**\n * Optional — gated by capabilities.transition. Add `toLabel` and remove\n * `fromLabel` (if provided) from the item. Used by the kanban board on drag.\n */\n transition?(itemId: string, toLabel: string, fromLabel?: string): Promise<void>\n}\n\n// ---------- BacklogWorkSource: GFM checklist file ----------\n// Format in .agentx/backlog.md (or configured path):\n//\n// - [ ] @alice Implement login form [time: 2h]\n// - [x] @alice Write unit tests (done)\n// - [ ] @bob Design pricing page [time: 1h30m]\n//\n// Line number is the stable id.\n\nconst CHECK_RE = /^(\\s*)-\\s*\\[( |x|X)\\]\\s*(.*)$/\nconst MENTION_RE = /@([a-z0-9_-]+)/i\nconst TIME_RE = /\\[time:\\s*([0-9hm\\s]+)\\]/i\n\nfunction parseDuration(s: string): number {\n // \"2h30m\" -> 9000 s. \"1h\" -> 3600. \"45m\" -> 2700.\n const hMatch = s.match(/(\\d+)\\s*h/i)\n const mMatch = s.match(/(\\d+)\\s*m/i)\n return (hMatch ? parseInt(hMatch[1], 10) * 3600 : 0) + (mMatch ? parseInt(mMatch[1], 10) * 60 : 0)\n}\n\nexport class BacklogWorkSource implements WorkSource {\n type = \"backlog\"\n capabilities: WorkSourceCapabilities = {\n listAll: false, create: false, transition: false, labels: false, search: false,\n }\n constructor(private path: string) {}\n\n private read(): string[] {\n const full = resolve(process.cwd(), this.path)\n if (!existsSync(full)) return []\n return readFileSync(full, \"utf-8\").split(\"\\n\")\n }\n\n private write(lines: string[]): void {\n const full = resolve(process.cwd(), this.path)\n writeFileSync(full, lines.join(\"\\n\"))\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const lines = this.read()\n const items: WorkItem[] = []\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(CHECK_RE)\n if (!m) continue\n const checked = m[2].toLowerCase() === \"x\"\n if (checked) continue\n const body = m[3]\n const mention = body.match(MENTION_RE)\n if (mention && mention[1].toLowerCase() !== agentId.toLowerCase()) continue\n const timeMatch = body.match(TIME_RE)\n const title = body.replace(MENTION_RE, \"\").replace(TIME_RE, \"\").trim()\n items.push({\n id: `backlog:${i}`,\n title: title || `Task at line ${i + 1}`,\n assignee: mention ? mention[1] : undefined,\n estimatedSeconds: timeMatch ? parseDuration(timeMatch[1]) : undefined,\n priority: i,\n })\n }\n return items\n }\n\n async claim(_agentId: string, _itemId: string): Promise<void> {\n // No-op: backlog file has no \"claimed\" state distinct from \"assigned\".\n }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n if (!itemId.startsWith(\"backlog:\")) throw new Error(`not a backlog id: ${itemId}`)\n const lineNo = parseInt(itemId.slice(\"backlog:\".length), 10)\n const lines = this.read()\n const line = lines[lineNo]\n if (!line) throw new Error(`backlog line ${lineNo} missing`)\n const m = line.match(CHECK_RE)\n if (!m) throw new Error(`backlog line ${lineNo} not a checkbox`)\n\n if (update.status === \"done\") {\n lines[lineNo] = line.replace(/- \\[ \\]/, \"- [x]\")\n const note = update.timeSeconds\n ? ` <!-- done by agent, ${Math.round(update.timeSeconds / 60)}m -->`\n : \" <!-- done by agent -->\"\n lines.splice(lineNo + 1, 0, note)\n } else if (update.status === \"blocked\") {\n lines.splice(lineNo + 1, 0, ` <!-- BLOCKED: ${update.blocker || update.note || \"?\"} -->`)\n } else if (update.note) {\n lines.splice(lineNo + 1, 0, ` <!-- ${update.note.replace(/-->/g, \"— >\")} -->`)\n }\n this.write(lines)\n }\n}\n\n// ---------- WikiWorkSource: scan markdown files for @mention checkboxes ----------\n\nexport class WikiWorkSource implements WorkSource {\n type = \"wiki\"\n capabilities: WorkSourceCapabilities = {\n listAll: false, create: false, transition: false, labels: false, search: false,\n }\n constructor(private root: string, private glob: string) {}\n\n private walk(dir: string, out: string[]): void {\n if (!existsSync(dir)) return\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const st = statSync(full)\n if (st.isDirectory()) {\n this.walk(full, out)\n } else if (entry.endsWith(\".md\")) {\n out.push(full)\n }\n }\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const files: string[] = []\n this.walk(resolve(process.cwd(), this.root), files)\n const items: WorkItem[] = []\n for (const file of files) {\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\")\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(CHECK_RE)\n if (!m || m[2].toLowerCase() === \"x\") continue\n const mention = m[3].match(MENTION_RE)\n if (!mention || mention[1].toLowerCase() !== agentId.toLowerCase()) continue\n items.push({\n id: `wiki:${file}:${i}`,\n title: m[3].replace(MENTION_RE, \"\").trim(),\n assignee: mention[1],\n priority: i,\n })\n }\n }\n return items\n }\n\n async claim(): Promise<void> { /* no-op */ }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n // wiki:<file>:<line>\n const parts = itemId.split(\":\")\n const lineNo = parseInt(parts[parts.length - 1], 10)\n const file = parts.slice(1, -1).join(\":\")\n if (!existsSync(file)) return\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\")\n if (update.status === \"done\" && lines[lineNo]) {\n lines[lineNo] = lines[lineNo].replace(/- \\[ \\]/, \"- [x]\")\n writeFileSync(file, lines.join(\"\\n\"))\n }\n }\n}\n\n// ---------- GitLabWorkSource: assignee-filtered issues via GitLab API ----------\n\nexport class GitLabWorkSource implements WorkSource {\n type = \"gitlab\"\n capabilities: WorkSourceCapabilities = {\n listAll: true, create: true, transition: true, labels: true, search: true,\n }\n\n constructor(\n private host: string,\n private token: string,\n private projects: string[],\n /** Map from agentId -> GitLab username(s) */\n private agentUsernames: Record<string, string[]>,\n private log: (...args: unknown[]) => void,\n ) {}\n\n /** Reverse lookup: GitLab username -> agentId. Used when resolving board items\n * back to an agent (for the reconciler and card display). */\n private usernameToAgent(username: string): string | undefined {\n for (const [agentId, names] of Object.entries(this.agentUsernames)) {\n if (names.some((n) => n.toLowerCase() === username.toLowerCase())) return agentId\n }\n return undefined\n }\n\n private mapIssue(issue: any, project: string): WorkItem {\n const ns = issue.references?.full?.split(\"#\")[0] || project\n // `labels` may be a string[] or, with with_labels_details=true, an object[].\n let labels: string[] = []\n let labelDetails: Array<{ name: string; color: string; text_color?: string }> | undefined\n if (Array.isArray(issue.labels)) {\n if (issue.labels.length && typeof issue.labels[0] === \"object\") {\n labelDetails = issue.labels.map((l: any) => ({\n name: l.name, color: l.color, text_color: l.text_color,\n }))\n labels = labelDetails!.map((l) => l.name)\n } else {\n labels = issue.labels\n }\n }\n const rawAssignees: any[] = Array.isArray(issue.assignees) && issue.assignees.length\n ? issue.assignees\n : (issue.assignee ? [issue.assignee] : [])\n const assignees = rawAssignees.map((a) => ({\n username: a.username, name: a.name, avatarUrl: a.avatar_url,\n }))\n const firstUsername = assignees[0]?.username\n return {\n id: `gitlab:${ns}:issue:${issue.iid}`,\n title: issue.title,\n description: issue.description || undefined,\n // Prefer configured agentId mapping; fall back to the raw GitLab username\n // so cards still render an assignee even without agentMappings configured.\n assignee: firstUsername ? (this.usernameToAgent(firstUsername) || firstUsername) : undefined,\n assignees,\n url: issue.web_url,\n priority: issue.weight ?? 99,\n estimatedSeconds: issue.time_stats?.time_estimate || undefined,\n labels,\n labelDetails,\n updatedAt: issue.updated_at,\n state: issue.state === \"closed\" ? \"closed\" : \"opened\",\n milestone: issue.milestone?.title || undefined,\n }\n }\n\n private async api(path: string, init: RequestInit = {}): Promise<any> {\n const res = await fetch(`${this.host}/api/v4${path}`, {\n ...init,\n headers: {\n \"PRIVATE-TOKEN\": this.token,\n \"Content-Type\": \"application/json\",\n ...(init.headers || {}),\n },\n })\n if (!res.ok) throw new Error(`GitLab ${res.status}: ${await res.text().catch(() => \"\")}`)\n return res.json()\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const usernames = this.agentUsernames[agentId] || []\n if (!usernames.length) return []\n\n const items: WorkItem[] = []\n const projectList = this.projects.length\n ? this.projects\n : [\"__ALL__\"] // falls back to a global issues query\n\n for (const username of usernames) {\n for (const project of projectList) {\n try {\n const q = `assignee_username=${encodeURIComponent(username)}&state=opened&scope=all&per_page=20`\n const path = project === \"__ALL__\"\n ? `/issues?${q}`\n : `/projects/${encodeURIComponent(project)}/issues?${q}`\n const issues = await this.api(path)\n for (const issue of issues) {\n const item = this.mapIssue(issue, project)\n // Force assignee to the agent we queried for (listOpen is per-agent).\n item.assignee = agentId\n items.push(item)\n }\n } catch (e: any) {\n this.log(`[business] GitLab listOpen failed for ${username} on ${project}: ${e.message}`)\n }\n }\n }\n return items\n }\n\n async claim(agentId: string, itemId: string): Promise<void> {\n // itemId: \"gitlab:<project>:issue:<iid>\"\n const { project, iid, type } = this.parseId(itemId)\n // Transition labels: add \"Doing\", remove \"To Do\"\n const path = `/projects/${encodeURIComponent(project)}/${type === \"issue\" ? \"issues\" : \"merge_requests\"}/${iid}?add_labels=Doing&remove_labels=To Do`\n try {\n await this.api(path, { method: \"PUT\" })\n } catch (e: any) {\n this.log(`[business] claim label update failed: ${e.message}`)\n }\n }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n\n // Time tracking\n if (update.timeSeconds && update.timeSeconds > 0) {\n const mins = Math.max(1, Math.round(update.timeSeconds / 60))\n const dur = mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? ` ${mins % 60}m` : \"\"}` : `${mins}m`\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}/add_spent_time?duration=${encodeURIComponent(dur)}`,\n { method: \"POST\" },\n )\n } catch (e: any) {\n this.log(`[business] add_spent_time failed: ${e.message}`)\n }\n }\n\n // Status → labels\n if (update.status === \"done\") {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?add_labels=Done&remove_labels=Doing`,\n { method: \"PUT\" },\n )\n } catch (e: any) {\n this.log(`[business] done label update failed: ${e.message}`)\n }\n } else if (update.status === \"blocked\") {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?add_labels=Blocked`,\n { method: \"PUT\" },\n )\n } catch (e: any) {\n this.log(`[business] blocked label update failed: ${e.message}`)\n }\n }\n\n // Note/comment\n if (update.note) {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}/notes`,\n { method: \"POST\", body: JSON.stringify({ body: update.note }) },\n )\n } catch (e: any) {\n this.log(`[business] report note failed: ${e.message}`)\n }\n }\n }\n\n async listAll(opts: ListAllOpts = {}): Promise<WorkItem[]> {\n const sinceDays = opts.sinceDays ?? 30\n const updatedAfter = new Date(Date.now() - sinceDays * 24 * 3600_000).toISOString()\n\n const params = new URLSearchParams()\n const state = opts.state ?? \"opened\"\n params.set(\"state\", state)\n // `updated_after` makes sense for the Closed column (bounded window) and for\n // keeping the open list recent; for \"opened\" we pass it too but GitLab treats\n // the window loosely (any issue updated since counts, regardless of open-age).\n params.set(\"updated_after\", updatedAfter)\n params.set(\"scope\", \"all\")\n params.set(\"per_page\", \"100\")\n params.set(\"with_labels_details\", \"true\")\n params.set(\"order_by\", \"updated_at\")\n params.set(\"sort\", \"desc\")\n if (opts.labels?.length) params.set(\"labels\", opts.labels.join(\",\"))\n if (opts.milestone) params.set(\"milestone\", opts.milestone)\n if (opts.search) params.set(\"search\", opts.search)\n\n const projectList = this.projects.length ? this.projects : [\"__ALL__\"]\n const items: WorkItem[] = []\n for (const project of projectList) {\n try {\n const path = project === \"__ALL__\"\n ? `/issues?${params.toString()}`\n : `/projects/${encodeURIComponent(project)}/issues?${params.toString()}`\n const issues = await this.api(path)\n for (const issue of issues) items.push(this.mapIssue(issue, project))\n } catch (e: any) {\n this.log(`[business] GitLab listAll failed on ${project}: ${e.message}`)\n }\n }\n\n // Client-side negative-label filter (GitLab API supports `not[labels]` only\n // via the `not[]` nested query form, which isn't convenient via URLSearchParams).\n let result = items\n if (opts.notLabels?.length) {\n const deny = new Set(opts.notLabels)\n result = result.filter((i) => !(i.labels || []).some((l) => deny.has(l)))\n }\n if (opts.withoutScopedPrefix) {\n const prefix = opts.withoutScopedPrefix + \"::\"\n result = result.filter((i) => !(i.labels || []).some((l) => l.startsWith(prefix)))\n }\n return result\n }\n\n /** Close or reopen an issue via state_event. */\n async setState(itemId: string, to: \"close\" | \"reopen\"): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?state_event=${to}`,\n { method: \"PUT\" },\n )\n }\n\n /** Fetch a single issue's full detail. */\n async getItem(itemId: string): Promise<WorkDetail | null> {\n const { project, iid } = this.parseId(itemId)\n try {\n const issue = await this.api(\n `/projects/${encodeURIComponent(project)}/issues/${iid}?with_labels_details=true`,\n )\n const base = this.mapIssue(issue, project)\n return {\n ...base,\n descriptionHtml: issue.description_html || undefined,\n createdAt: issue.created_at,\n author: issue.author ? {\n username: issue.author.username, name: issue.author.name, avatarUrl: issue.author.avatar_url,\n } : undefined,\n allLabels: Array.isArray(issue.labels)\n ? (typeof issue.labels[0] === \"object\" ? issue.labels.map((l: any) => l.name) : issue.labels)\n : [],\n }\n } catch (e: any) {\n this.log(`[board] GitLab getItem failed for ${itemId}: ${e.message}`)\n return null\n }\n }\n\n /** Update fields on an issue. Accepts string usernames for assignees. */\n async updateItem(\n itemId: string,\n patch: {\n title?: string\n description?: string\n labels?: string[]\n addLabels?: string[]\n removeLabels?: string[]\n assigneeUsernames?: string[]\n milestoneTitle?: string | null\n },\n ): Promise<WorkItem> {\n const { project, iid } = this.parseId(itemId)\n\n // Resolve assignee usernames → user ids (parallel).\n let assigneeIds: number[] | undefined\n if (patch.assigneeUsernames) {\n const results = await Promise.all(patch.assigneeUsernames.map(async (u) => {\n try {\n const users = await this.api(`/users?username=${encodeURIComponent(u)}`)\n return Array.isArray(users) && users[0]?.id ? users[0].id : null\n } catch { return null }\n }))\n assigneeIds = results.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id (null clears the milestone).\n let milestoneId: number | 0 | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneId = 0\n } else {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?title=${encodeURIComponent(patch.milestoneTitle)}`,\n )\n if (Array.isArray(ms) && ms[0]?.id) milestoneId = ms[0].id\n } catch (e: any) {\n this.log(`[board] milestone lookup failed for \"${patch.milestoneTitle}\": ${e.message}`)\n }\n }\n }\n\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.description = patch.description\n if (patch.labels !== undefined) body.labels = patch.labels.join(\",\")\n if (patch.addLabels?.length) body.add_labels = patch.addLabels.join(\",\")\n if (patch.removeLabels?.length) body.remove_labels = patch.removeLabels.join(\",\")\n if (assigneeIds !== undefined) body.assignee_ids = assigneeIds\n if (milestoneId !== undefined) body.milestone_id = milestoneId\n\n const issue = await this.api(`/projects/${encodeURIComponent(project)}/issues/${iid}`, {\n method: \"PUT\",\n body: JSON.stringify(body),\n })\n return this.mapIssue(issue, project)\n }\n\n /** List project members (direct + inherited). Deduped by username across projects. */\n async listMembers(): Promise<WorkMember[]> {\n const byUsername = new Map<string, WorkMember>()\n for (const project of this.projects) {\n try {\n const members = await this.api(\n `/projects/${encodeURIComponent(project)}/members/all?per_page=100`,\n )\n for (const m of members) {\n if (m.username && !byUsername.has(m.username)) {\n byUsername.set(m.username, {\n username: m.username, name: m.name, avatarUrl: m.avatar_url,\n })\n }\n }\n } catch (e: any) {\n this.log(`[board] listMembers failed for ${project}: ${e.message}`)\n }\n }\n return [...byUsername.values()].sort((a, b) => a.username.localeCompare(b.username))\n }\n\n /** List milestones (active first, then closed). Deduped by title across projects. */\n async listMilestones(): Promise<WorkMilestone[]> {\n const byTitle = new Map<string, WorkMilestone>()\n for (const project of this.projects) {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?per_page=100`,\n )\n for (const m of ms) {\n if (m.title && !byTitle.has(m.title)) {\n byTitle.set(m.title, {\n id: m.id, title: m.title, state: m.state, dueDate: m.due_date,\n })\n }\n }\n } catch (e: any) {\n this.log(`[board] listMilestones failed for ${project}: ${e.message}`)\n }\n }\n const all = [...byTitle.values()]\n all.sort((a, b) => {\n if (a.state !== b.state) return a.state === \"active\" ? -1 : 1\n return a.title.localeCompare(b.title)\n })\n return all\n }\n\n async create(input: WorkCreateInput): Promise<WorkItem> {\n const project = input.projectHint || this.projects[0]\n if (!project) throw new Error(\"GitLabWorkSource.create requires projectHint or configured projects\")\n\n // Resolve assignee usernames → user ids. Prefer explicit `assigneeUsernames`,\n // fall back to agentId → configured mapping for legacy callers.\n let assignee_ids: number[] | undefined\n const usernames = input.assigneeUsernames?.length\n ? input.assigneeUsernames\n : (input.assignee ? (this.agentUsernames[input.assignee] || []).slice(0, 1) : [])\n if (usernames.length) {\n const results = await Promise.all(usernames.map(async (u) => {\n try {\n const users = await this.api(`/users?username=${encodeURIComponent(u)}`)\n return Array.isArray(users) && users[0]?.id ? users[0].id : null\n } catch (e: any) {\n this.log(`[board] user lookup failed for ${u}: ${e.message}`)\n return null\n }\n }))\n assignee_ids = results.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id.\n let milestone_id: number | undefined\n if (input.milestoneTitle) {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?title=${encodeURIComponent(input.milestoneTitle)}`,\n )\n if (Array.isArray(ms) && ms[0]?.id) milestone_id = ms[0].id\n } catch (e: any) {\n this.log(`[board] milestone lookup failed for \"${input.milestoneTitle}\": ${e.message}`)\n }\n }\n\n const body: Record<string, unknown> = {\n title: input.title,\n description: input.description,\n labels: (input.labels || []).join(\",\"),\n }\n if (assignee_ids?.length) body.assignee_ids = assignee_ids\n if (milestone_id !== undefined) body.milestone_id = milestone_id\n\n const issue = await this.api(`/projects/${encodeURIComponent(project)}/issues`, {\n method: \"POST\",\n body: JSON.stringify(body),\n })\n return this.mapIssue(issue, project)\n }\n\n async transition(itemId: string, toLabel: string, fromLabel?: string): Promise<void> {\n // Legacy single add/remove — boards use transitionMany.\n await this.transitionMany(itemId, toLabel ? [toLabel] : [], fromLabel ? [fromLabel] : [])\n }\n\n /** Multi-label transition; used by board drag-drop (scoped labels produce pairs). */\n async transitionMany(itemId: string, add: string[], remove: string[]): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n const params = new URLSearchParams()\n if (add.length) params.set(\"add_labels\", add.join(\",\"))\n if (remove.length) params.set(\"remove_labels\", remove.join(\",\"))\n if (!params.toString()) return\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?${params.toString()}`,\n { method: \"PUT\" },\n )\n }\n\n private parseId(itemId: string): { project: string; type: \"issue\" | \"mr\"; iid: string } {\n // \"gitlab:<project>:issue:<iid>\" (project may contain a slash → it's joined)\n const body = itemId.replace(/^gitlab:/, \"\")\n const parts = body.split(\":\")\n const iid = parts.pop() as string\n const type = parts.pop() === \"mr\" ? \"mr\" : \"issue\"\n const project = parts.join(\":\")\n return { project, type, iid }\n }\n}\n\n// ---------- Factory ----------\n\nexport function createWorkSource(\n business: BusinessConfig,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): WorkSource {\n const ws: BusinessWorkSource = business.workSource\n switch (ws.type) {\n case \"backlog\":\n return new BacklogWorkSource(ws.path)\n\n case \"wiki\":\n return new WikiWorkSource(ws.path, ws.glob)\n\n case \"gitlab\": {\n const gl = daemon.channels.gitlab\n if (!gl?.token) {\n throw new Error(\"business.workSource.type=gitlab requires channels.gitlab.token\")\n }\n const agentUsernames: Record<string, string[]> = {}\n for (const mapping of gl.agentMappings) {\n if (mapping.gitlabUsernames.length) {\n agentUsernames[mapping.agentId] = mapping.gitlabUsernames\n }\n }\n const projects = ws.projects.length\n ? ws.projects\n : gl.routes.map((r) => r.project)\n return new GitLabWorkSource(gl.host, gl.token, projects, agentUsernames, log)\n }\n }\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","resolve","relative","z","nodeTypeSchema","conditionKindSchema","conditionSchema","workflowNodeSchema","workflowEdgeSchema","workflowSchema","ns","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","workflowRunSchema","WorkflowStore","opts","resolve","mkdirSync","id","existsSync","out","entry","readdirSync","full","raw","readFileSync","parsed","workflowSchema","a","b","results","rel","relative","rawJson","e","issues","i","lintIssues","lintWorkflow","p","workflow","now","normalized","writeFileSync","unlinkSync","createHash","randomUUID","appendFileSync","existsSync","mkdirSync","readdirSync","readFileSync","statSync","unlinkSync","writeFileSync","resolve","sanitize","idempotencyKey","runId","nodeId","eventId","RunStore","opts","entity","args","now","id","run","p","raw","line","evt","current","h","nextStatus","status","updated","files","f","a","b","out","policy","cutoff","candidates","name","path","mtime","pruned","c","tooOld","tooMany","existsSync","mkdirSync","readFileSync","unlinkSync","writeFileSync","resolve","z","layoutSchema","LayoutStore","opts","workflowId","safe","p","parsed","layout","validIds","existing","keep","id","next","nextNodes","input","workflow","fromNodeId","selectedPort","fromNode","n","outgoing","e","evaluateBranch","node","context","cases","defaultPort","c","conditionMatches","cond","p","path","value","getByPath","v","ctx","parts","cur","initialPendingFromTrigger","trigger","successors","findNode","id","lookup","path","ctx","envAllow","parts","name","v","cur","p","render","template","opts","_","renderParams","params","out","k","x","NodeConcurrencyGate","key","max","cur","resolve","queue","nodeConcurrencyGate","nodeKey","workflowId","nodeId","triggerHandler","ctx","agentHandler","cfg","agentId","promptTemplate","prompt","render","timeoutMinutes","maxConcurrent","gateKey","nodeKey","before","nodeConcurrencyGate","start","resp","durationMs","parsed","extractJsonBlock","parseResultToken","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","kind","readLabelHandler","reactHandler","messageId","emoji","editMessageHandler","parseMode","logTimeHandler","callHTTPHandler","url","method","headers","body","res","rawText","transformHandler","ctxObj","getByPath","userTaskHandler","assigneeRaw","ref","parseAssigneeRef","formRaw","form","coerceForm","assignedTo","dueAt","computeDueAt","task","raw","formSchemaSchema","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","inputs","rules","rule","when","all","i","matchCell","def","input","w","numMatch","op","n","vNum","NODE_HANDLERS","resolveHandler","type","explicit","triggerNode","fromTrigger","entry","node","content","bracket","SignalBus","h","emission","matchesSignal","waiter","k","v","validateSubmission","schema","submission","errors","coerced","field","raw","result","coerceField","label","str","num","d","options","arr","s","bad","v","ACK_TYPING_INTERVAL_MS","WorkflowDispatcher","opts","ActorStore","TaskStore","TimerService","m","SignalBus","emission","args","all","run","waiter","matchesSignal","wf","w","node","findNode","successors","e","output","entry","idempotencyKey","newContext","t","firedAt","cfg","n","effective","runId","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","activeRunId","homeNode","matchesResume","checkpointNode","resumeEntry","pausedCheckpointName","updated","init","initialPendingFromTrigger","triggeringEventId","nodeId","remaining","handler","resolveHandler","key","h","inputKeys","result","now","pausedAt","spawnedChild","taskRecord","nextPending","nextNodes","readyNext","joinCounters","succId","succ","arrived","expected","merged","terminal","childWf","childContext","child","childRun","parent","parentWf","childOutput","pickChildOutput","taskId","submission","submittedBy","task","validated","validateSubmission","i","resumeMatch","match","k","want","eventIdLike","source","value","createWorkflowHookHandlers","dispatcher","ctx","msg","source","channelToTriggerSource","chatId","entityRef","claimed","buildChannelTriggerPayload","wf","n","w","project","iid","issueEvent","labels","l","assignees","pipelineId","status","ref","mrIid","channel","startWorkflowTriggers","args","workflows","cronTimers","hookSubscribers","wf","trigger","n","cfg","scheduleCron","ctx","entityId","entityKeyFromHookContext","e","workflowId","event","project","iid","pid","opts","dispatcher","log","tick","next","getNextCronDate","delay","eventId","readFileSync","writeFileSync","existsSync","readdirSync","statSync","resolve","join","CHECK_RE","MENTION_RE","TIME_RE","parseDuration","hMatch","mMatch","BacklogWorkSource","path","full","lines","agentId","items","i","m","body","mention","timeMatch","title","_agentId","_itemId","itemId","update","lineNo","line","note","WikiWorkSource","root","glob","dir","out","entry","files","file","parts","GitLabWorkSource","host","token","projects","agentUsernames","log","username","names","issue","project","ns","labels","labelDetails","l","assignees","a","firstUsername","init","res","usernames","projectList","q","issues","item","e","iid","type","seg","mins","dur","opts","sinceDays","updatedAfter","params","state","result","deny","prefix","to","patch","assigneeIds","users","x","milestoneId","ms","byUsername","members","b","byTitle","all","input","assignee_ids","u","milestone_id","toLabel","fromLabel","add","remove","createWorkSource","business","daemon","ws","gl","mapping","r"]}
|