@oisincoveney/pipeline 1.26.1 → 1.27.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.
@@ -23,7 +23,7 @@ Orchestrate package-owned pipeline config.
23
23
  Run workflow `inspect` for the user task.
24
24
  These nodes are not Codex native routes.
25
25
  CLI routes:
26
- - inspect: opencode CLI profile=pipeline-inspector command=`opencode run --agent pipeline-inspector --format json --dir <repo-root> <node prompt>` needs=none
26
+ - inspect: opencode CLI profile=moka-inspector command=`opencode run --agent "MoKa Inspector" --format json --dir <repo-root> <node prompt>` needs=none
27
27
 
28
28
  For each CLI node prompt include:
29
29
  - user task
package/README.md CHANGED
@@ -164,10 +164,17 @@ validation, contract-version checks, and JSON Schema generation.
164
164
 
165
165
  Pipeline Console submits hosted runs through `@oisincoveney/pipeline/moka-submit`.
166
166
  The subpath exports `mokaSubmitOptionsSchema`, `mokaSubmitResultSchema`,
167
- `MokaSubmitOptionsInput`, `MokaSubmitOptionsOutput`, `MokaSubmitInput`,
167
+ `mokaSubmitDirectHooksSchema`, `mokaSubmitHookPolicySchema`,
168
+ `MokaSubmitOptionsInput`, `MokaSubmitOptionsOutput`,
169
+ `MokaSubmitDirectHooksInput`, `MokaSubmitHookPolicyInput`, `MokaSubmitInput`,
168
170
  `MokaSubmitOutput`, and `submitMoka` so callers can validate prompt or ticket
169
- tasks with explicit repository, run, delivery, event sink, and runner settings
170
- without importing Argo, Kubernetes, or runner-command internals.
171
+ tasks with explicit repository, run, delivery, event sink, lifecycle hooks, hook
172
+ policy, and runner settings without importing Argo, Kubernetes, or
173
+ runner-command internals. `eventSink` is runner event transport; direct `hooks`
174
+ configure runner-side lifecycle behavior and are normalized into internal
175
+ runtime hook config by the package. Direct hook function ids are generated as
176
+ `moka-submit-<event-name-with-dashes>`; existing package hooks are preserved,
177
+ and a generated id that already exists in the supplied config is rejected.
171
178
 
172
179
  ## Configuration Model
173
180
 
@@ -148,50 +148,50 @@ skills:
148
148
  used_by: [orchestrator]
149
149
  - id: inspect
150
150
  path: .agents/skills/inspect/SKILL.md
151
- used_by: [pipeline-inspector]
151
+ used_by: [moka-inspector]
152
152
  - id: epic
153
153
  path: .agents/skills/epic/SKILL.md
154
- used_by: [pipeline-epic-router]
154
+ used_by: [moka-epic-router]
155
155
  - id: execute
156
156
  path: .agents/skills/execute/SKILL.md
157
- used_by: [pipeline-code-writer]
157
+ used_by: [moka-code-writer]
158
158
  - id: research
159
159
  path: .agents/skills/research/SKILL.md
160
- used_by: [pipeline-researcher]
160
+ used_by: [moka-researcher]
161
161
  - id: library-first-development
162
162
  path: .agents/skills/library-first-development/SKILL.md
163
- used_by: [pipeline-researcher, pipeline-code-writer]
163
+ used_by: [moka-researcher, moka-code-writer]
164
164
  - id: critique
165
165
  path: .agents/skills/critique/SKILL.md
166
- used_by: [pipeline-thermo-nuclear-reviewer]
166
+ used_by: [moka-thermo-nuclear-reviewer]
167
167
  - id: verify
168
168
  path: .agents/skills/verify/SKILL.md
169
- used_by: [pipeline-verifier]
169
+ used_by: [moka-verifier]
170
170
  prompts:
171
171
  - id: orchestrator
172
172
  source: package-default-profile
173
173
  used_by: [orchestrator]
174
174
  - id: researcher
175
175
  source: package-default-profile
176
- used_by: [pipeline-researcher]
176
+ used_by: [moka-researcher]
177
177
  - id: inspector
178
178
  source: package-default-profile
179
- used_by: [pipeline-inspector]
179
+ used_by: [moka-inspector]
180
180
  - id: schedule-planner
181
181
  source: package-default-profile
182
- used_by: [pipeline-schedule-planner]
182
+ used_by: [moka-schedule-planner]
183
183
  - id: test-writer
184
184
  source: package-default-profile
185
- used_by: [pipeline-test-writer]
185
+ used_by: [moka-test-writer]
186
186
  - id: code-writer
187
187
  source: package-default-profile
188
- used_by: [pipeline-code-writer]
188
+ used_by: [moka-code-writer]
189
189
  - id: acceptance-reviewer
190
190
  source: package-default-profile
191
- used_by: [pipeline-acceptance-reviewer]
191
+ used_by: [moka-acceptance-reviewer]
192
192
  - id: verifier
193
193
  source: package-default-profile
194
- used_by: [pipeline-verifier]
194
+ used_by: [moka-verifier]
195
195
  host_capabilities:
196
196
  agents: true
197
197
  subagents: true
package/dist/config.d.ts CHANGED
@@ -452,6 +452,7 @@ declare const configSchema: z.ZodObject<{
452
452
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
453
453
  timeout_ms: z.ZodOptional<z.ZodNumber>;
454
454
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
455
+ task: "task";
455
456
  read: "read";
456
457
  list: "list";
457
458
  grep: "grep";
@@ -459,7 +460,6 @@ declare const configSchema: z.ZodObject<{
459
460
  bash: "bash";
460
461
  edit: "edit";
461
462
  write: "write";
462
- task: "task";
463
463
  }>>>;
464
464
  }, z.core.$strict>>>;
465
465
  runner_command: z.ZodDefault<z.ZodObject<{
@@ -485,8 +485,8 @@ declare const configSchema: z.ZodObject<{
485
485
  rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
486
486
  path: z.ZodString;
487
487
  source_root: z.ZodDefault<z.ZodEnum<{
488
- package: "package";
489
488
  project: "project";
489
+ package: "package";
490
490
  }>>;
491
491
  }, z.core.$strict>>>;
492
492
  runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -511,6 +511,7 @@ declare const configSchema: z.ZodObject<{
511
511
  rules: z.ZodOptional<z.ZodBoolean>;
512
512
  skills: z.ZodOptional<z.ZodBoolean>;
513
513
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
514
+ task: "task";
514
515
  read: "read";
515
516
  list: "list";
516
517
  grep: "grep";
@@ -518,15 +519,14 @@ declare const configSchema: z.ZodObject<{
518
519
  bash: "bash";
519
520
  edit: "edit";
520
521
  write: "write";
521
- task: "task";
522
522
  }>>>;
523
523
  }, z.core.$strict>;
524
524
  command: z.ZodOptional<z.ZodString>;
525
525
  host_models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
526
526
  model: z.ZodOptional<z.ZodString>;
527
527
  type: z.ZodEnum<{
528
- opencode: "opencode";
529
528
  command: "command";
529
+ opencode: "opencode";
530
530
  }>;
531
531
  }, z.core.$strict>>>;
532
532
  scheduler: z.ZodDefault<z.ZodObject<{
@@ -613,8 +613,8 @@ declare const configSchema: z.ZodObject<{
613
613
  schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
614
614
  description: z.ZodOptional<z.ZodString>;
615
615
  baseline: z.ZodEnum<{
616
- execute: "execute";
617
616
  quick: "quick";
617
+ execute: "execute";
618
618
  }>;
619
619
  max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
620
620
  node_catalog: z.ZodOptional<z.ZodString>;
@@ -626,8 +626,8 @@ declare const configSchema: z.ZodObject<{
626
626
  skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
627
627
  path: z.ZodString;
628
628
  source_root: z.ZodDefault<z.ZodEnum<{
629
- package: "package";
630
629
  project: "project";
630
+ package: "package";
631
631
  }>>;
632
632
  }, z.core.$strict>>>;
633
633
  task_context: z.ZodOptional<z.ZodObject<{
package/dist/config.js CHANGED
@@ -124,6 +124,15 @@ mcp_gateway:
124
124
  workspace_path_source: PIPELINE_TARGET_PATH
125
125
  tool_prefixes: [backlog]
126
126
  skills:
127
+ execute:
128
+ path: .agents/skills/execute/SKILL.md
129
+ source_root: package
130
+ inspect:
131
+ path: .agents/skills/inspect/SKILL.md
132
+ source_root: package
133
+ quick:
134
+ path: .agents/skills/quick/SKILL.md
135
+ source_root: package
127
136
  critique:
128
137
  path: .agents/skills/critique/SKILL.md
129
138
  source_root: package
@@ -167,7 +176,16 @@ skills:
167
176
  path: .agents/skills/verify/SKILL.md
168
177
  source_root: package
169
178
  profiles:
170
- pipeline-researcher:
179
+ moka-orchestrator:
180
+ runner: opencode
181
+ description: Orchestrate the configured pipeline and enforce gates.
182
+ instructions: { inline: "Orchestrate the configured pipeline through package-defined entrypoints, native agents, and gates. Do not bypass configured runner subprocesses or package-configured gates." }
183
+ skills: [execute, quick, inspect]
184
+ mcp_servers: [pipeline-gateway]
185
+ tools: [read, list, grep, glob, bash]
186
+ filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
187
+ network: { mode: inherit }
188
+ moka-researcher:
171
189
  runner: opencode
172
190
  description: Research the requested task and produce structured findings.
173
191
  instructions: { inline: "Inspect first-party source, tests, docs, and task context for the current task only. Produce concise findings with file references and stop; do not perform open-ended repository exploration." }
@@ -181,7 +199,7 @@ profiles:
181
199
  format: json_schema
182
200
  schema_path: .pipeline/schemas/research.schema.json
183
201
  repair: { enabled: true, max_attempts: 1 }
184
- pipeline-inspector:
202
+ moka-inspector:
185
203
  runner: opencode
186
204
  description: Inspect the repository without modifying files.
187
205
  instructions: { inline: "Inspect the repository without modifying files." }
@@ -190,7 +208,7 @@ profiles:
190
208
  tools: [read, list, grep, glob, bash]
191
209
  filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
192
210
  network: { mode: inherit }
193
- pipeline-schedule-planner:
211
+ moka-schedule-planner:
194
212
  runner: opencode
195
213
  description: Refine a baseline schedule into a specialized approved-plan artifact.
196
214
  instructions: { inline: "Generate exactly one workflow named root as an explicit schedule graph. Return YAML only." }
@@ -200,7 +218,7 @@ profiles:
200
218
  tools: [read, list, grep, glob, bash]
201
219
  filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
202
220
  network: { mode: inherit }
203
- pipeline-test-writer:
221
+ moka-test-writer:
204
222
  runner: opencode
205
223
  scheduling_roles: [implementation]
206
224
  description: Add focused failing tests for the requested behavior.
@@ -214,7 +232,7 @@ profiles:
214
232
  format: json_schema
215
233
  schema_path: .pipeline/schemas/implementation.schema.json
216
234
  repair: { enabled: true, max_attempts: 1 }
217
- pipeline-code-writer:
235
+ moka-code-writer:
218
236
  runner: opencode
219
237
  scheduling_roles: [implementation]
220
238
  description: Implement production code until the failing tests pass.
@@ -228,7 +246,7 @@ profiles:
228
246
  format: json_schema
229
247
  schema_path: .pipeline/schemas/implementation.schema.json
230
248
  repair: { enabled: true, max_attempts: 1 }
231
- pipeline-acceptance-reviewer:
249
+ moka-acceptance-reviewer:
232
250
  runner: opencode
233
251
  scheduling_roles: [coverage]
234
252
  description: Audit the finished change against every acceptance criterion.
@@ -242,7 +260,7 @@ profiles:
242
260
  format: json_schema
243
261
  schema_path: .pipeline/schemas/acceptance.schema.json
244
262
  repair: { enabled: true, max_attempts: 1 }
245
- pipeline-thermo-nuclear-reviewer:
263
+ moka-thermo-nuclear-reviewer:
246
264
  runner: opencode
247
265
  scheduling_roles: [coverage]
248
266
  description: Perform the final thermo-nuclear code quality review of the integration branch.
@@ -256,7 +274,7 @@ profiles:
256
274
  format: json_schema
257
275
  schema_path: .pipeline/schemas/review.schema.json
258
276
  repair: { enabled: true, max_attempts: 1 }
259
- pipeline-verifier:
277
+ moka-verifier:
260
278
  runner: opencode
261
279
  scheduling_roles: [coverage]
262
280
  description: Verify checks, implementation fit, and final evidence.
@@ -270,7 +288,7 @@ profiles:
270
288
  format: json_schema
271
289
  schema_path: .pipeline/schemas/verify.schema.json
272
290
  repair: { enabled: true, max_attempts: 1 }
273
- pipeline-learner:
291
+ moka-learner:
274
292
  runner: opencode
275
293
  description: Store durable lessons from the completed run.
276
294
  instructions: { inline: "Store durable lessons from the completed run when useful." }
@@ -286,6 +304,8 @@ profiles:
286
304
  `;
287
305
  const PACKAGE_DEFAULT_PIPELINE_YAML = `version: 1
288
306
  default_workflow: inspect
307
+ orchestrator:
308
+ profile: moka-orchestrator
289
309
  entrypoints:
290
310
  quick:
291
311
  schedule: quick-schedule
@@ -323,63 +343,63 @@ scheduler:
323
343
  nodes:
324
344
  backlog-intake:
325
345
  category: intake
326
- profile: pipeline-researcher
346
+ profile: moka-researcher
327
347
  models: [zai-coding-plan/glm-5-turbo, openai/gpt-5.5-fast]
328
348
  red-tests:
329
349
  category: red
330
- profile: pipeline-test-writer
350
+ profile: moka-test-writer
331
351
  models: [openai/gpt-5.5, zai-coding-plan/glm-5.1, kimi-for-coding/kimi-k2-thinking]
332
352
  green-implementation:
333
353
  category: green
334
- profile: pipeline-code-writer
354
+ profile: moka-code-writer
335
355
  models: [opencode-go/qwen3.7-max, kimi-for-coding/k2p6, opencode-go/deepseek-v4-pro]
336
356
  verification:
337
357
  category: verification
338
- profile: pipeline-verifier
358
+ profile: moka-verifier
339
359
  models: [openai/gpt-5.5, zai-coding-plan/glm-5.1]
340
360
  execute:
341
361
  required_categories: [intake, research, red, green, mechanical, acceptance, verification, learn]
342
362
  nodes:
343
363
  backlog-intake:
344
364
  category: intake
345
- profile: pipeline-researcher
365
+ profile: moka-researcher
346
366
  models: [zai-coding-plan/glm-5-turbo, openai/gpt-5.5-fast]
347
367
  research:
348
368
  category: research
349
- profile: pipeline-researcher
369
+ profile: moka-researcher
350
370
  models: [openai/gpt-5.5-fast, zai-coding-plan/glm-5.1, kimi-for-coding/k2p6]
351
371
  red-tests:
352
372
  category: red
353
- profile: pipeline-test-writer
373
+ profile: moka-test-writer
354
374
  models: [openai/gpt-5.5, zai-coding-plan/glm-5.1, kimi-for-coding/kimi-k2-thinking]
355
375
  green-backend:
356
376
  category: green
357
- profile: pipeline-code-writer
377
+ profile: moka-code-writer
358
378
  models: [opencode-go/qwen3.7-max, kimi-for-coding/k2p6, opencode-go/deepseek-v4-pro]
359
379
  green-frontend:
360
380
  category: green
361
- profile: pipeline-code-writer
381
+ profile: moka-code-writer
362
382
  models: [opencode-go/qwen3.7-max, kimi-for-coding/k2p6, opencode-go/deepseek-v4-pro]
363
383
  acceptance-review:
364
384
  category: acceptance
365
- profile: pipeline-acceptance-reviewer
385
+ profile: moka-acceptance-reviewer
366
386
  models: [openai/gpt-5.5, zai-coding-plan/glm-5.1]
367
387
  verification:
368
388
  category: verification
369
- profile: pipeline-verifier
389
+ profile: moka-verifier
370
390
  models: [openai/gpt-5.5, zai-coding-plan/glm-5.1]
371
391
  learn:
372
392
  category: learn
373
- profile: pipeline-learner
393
+ profile: moka-learner
374
394
  models: [zai-coding-plan/glm-5-turbo, openai/gpt-5.5-fast]
375
395
  schedules:
376
396
  quick-schedule:
377
397
  baseline: quick
378
- planner_profile: pipeline-schedule-planner
398
+ planner_profile: moka-schedule-planner
379
399
  node_catalog: quick
380
400
  execute-schedule:
381
401
  baseline: execute
382
- planner_profile: pipeline-schedule-planner
402
+ planner_profile: moka-schedule-planner
383
403
  node_catalog: execute
384
404
  workflows:
385
405
  inspect:
@@ -387,7 +407,7 @@ workflows:
387
407
  nodes:
388
408
  - id: inspect
389
409
  kind: agent
390
- profile: pipeline-inspector
410
+ profile: moka-inspector
391
411
  `;
392
412
  const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST_URL = new URL(`../${OPENCODE_ECOSYSTEM_MANIFEST_PATH}`, import.meta.url);
393
413
  const PACKAGE_ASSET_ROOT = new URL("..", import.meta.url);
@@ -21,6 +21,8 @@ const SINGLE_OPENCODE_PLUGIN_ARRAY_RE = /\n {2}"plugin": \[\n {4}("[^"]+")\n {2}
21
21
  const OPENCODE_PROJECT_CONFIG_PATH = ".opencode/opencode.json";
22
22
  const ENTRYPOINT_PATH_PATTERNS = { opencode: [/^\.opencode\/commands\/([^/]+)\.md$/] };
23
23
  const COMMAND_HOSTS = ["opencode"];
24
+ const OPENCODE_ORCHESTRATOR_AGENT_ID = "MoKa Orchestrator";
25
+ const MOKA_PROFILE_PREFIX = "moka-";
24
26
  function header(host) {
25
27
  return [
26
28
  GENERATED_MARKER,
@@ -32,9 +34,7 @@ function markdown(data, body) {
32
34
  return `${matter.stringify(body.trimEnd(), data).trimEnd()}\n`;
33
35
  }
34
36
  function profileEntries(config) {
35
- const profileIds = /* @__PURE__ */ new Set();
36
- for (const workflow of Object.values(config.workflows)) for (const node of workflow.nodes) if (node.kind === "agent" && node.profile) profileIds.add(node.profile);
37
- return [...profileIds].sort((a, b) => a.localeCompare(b)).map((id) => [id, config.profiles[id]]);
37
+ return Object.entries(config.profiles).sort(([a], [b]) => a.localeCompare(b));
38
38
  }
39
39
  function entrypointEntries(config) {
40
40
  const entries = Object.entries(config.entrypoints);
@@ -50,7 +50,7 @@ function entrypointCommandDefinitions(_host, config, makeDefinition) {
50
50
  return entrypointEntries(config).map(([id, entrypoint]) => makeDefinition(id, entrypoint));
51
51
  }
52
52
  function nativeProfileEntries(host, config) {
53
- return profileEntries(config).filter(([_, profile]) => canRunNatively(host, profile));
53
+ return profileEntries(config).filter(([id, profile]) => id !== config.orchestrator?.profile && canRunNatively(host, profile));
54
54
  }
55
55
  function orchestratorProfile(config) {
56
56
  if (!config.orchestrator) return;
@@ -94,7 +94,7 @@ function dispatchRouteForAgent(host, config, route) {
94
94
  ...route,
95
95
  kind: "native-named-agent",
96
96
  ...model ? { model } : {},
97
- nativeAgentId: route.profileId,
97
+ nativeAgentId: nativeAgentIdForHost(host, route.profileId),
98
98
  runnerId
99
99
  };
100
100
  }
@@ -104,7 +104,7 @@ function dispatchRouteForAgent(host, config, route) {
104
104
  ...route,
105
105
  kind: "native-model-agent",
106
106
  ...model ? { model } : {},
107
- nativeAgentId: route.profileId,
107
+ nativeAgentId: nativeAgentIdForHost(host, route.profileId),
108
108
  runnerId
109
109
  };
110
110
  }
@@ -114,6 +114,17 @@ function dispatchRouteForAgent(host, config, route) {
114
114
  runnerId
115
115
  };
116
116
  }
117
+ function nativeAgentIdForHost(host, profileId) {
118
+ return host === "opencode" ? opencodeAgentName(profileId) : profileId;
119
+ }
120
+ function opencodeAgentName(profileId) {
121
+ if (!profileId.startsWith(MOKA_PROFILE_PREFIX)) return profileId;
122
+ return `MoKa ${profileId.slice(5).split("-").map(opencodeAgentNamePart).join(" ")}`;
123
+ }
124
+ function opencodeAgentNamePart(part) {
125
+ if (part === "opencode") return "OpenCode";
126
+ return `${part.charAt(0).toUpperCase()}${part.slice(1)}`;
127
+ }
117
128
  function grants(actor) {
118
129
  return [
119
130
  `model: ${actor.model ?? "default"}`,
@@ -201,7 +212,7 @@ function cliDispatchLine(route) {
201
212
  return `- ${route.nodeId}: ${route.runnerId} CLI profile=${route.profileId} command=\`${runnerCliCommand(route)}\` needs=${needsSummary(route.needs)}`;
202
213
  }
203
214
  function runnerCliCommand(route) {
204
- if (route.runnerId === "opencode") return `opencode run --agent ${route.profileId} --format json --dir <repo-root> <node prompt>`;
215
+ if (route.runnerId === "opencode") return `opencode run --agent "${opencodeAgentName(route.profileId)}" --format json --dir <repo-root> <node prompt>`;
205
216
  throw new Error(`runner '${route.runnerId}' cannot be represented as a supported native or CLI route`);
206
217
  }
207
218
  function nodePromptContract(workflowId, routes) {
@@ -352,7 +363,7 @@ function opencodeDefinitions(config, cwd) {
352
363
  return [
353
364
  ...entrypointCommandDefinitions("opencode", config, (id, entrypoint) => ({
354
365
  content: markdown({
355
- ...orchestrator ? { agent: "pipeline-orchestrator" } : {},
366
+ ...orchestrator ? { agent: OPENCODE_ORCHESTRATOR_AGENT_ID } : {},
356
367
  description: entrypointDescription(id, entrypoint)
357
368
  }, compactLines([
358
369
  header("opencode").trimEnd(),
@@ -379,6 +390,7 @@ function opencodeDefinitions(config, cwd) {
379
390
  content: markdown({
380
391
  description: "Orchestrate the configured pipeline and enforce gates.",
381
392
  mode: "primary",
393
+ name: OPENCODE_ORCHESTRATOR_AGENT_ID,
382
394
  permission: opencodePermission(orchestrator, { allowedTaskAgents: agentDispatchRoutes("opencode", config).filter((route) => route.kind !== "cli").map((route) => route.nativeAgentId).filter((id) => Boolean(id)) })
383
395
  }, compactLines([
384
396
  header("opencode").trimEnd(),
@@ -389,10 +401,11 @@ function opencodeDefinitions(config, cwd) {
389
401
  ]).join("\n")),
390
402
  host: "opencode",
391
403
  invocation: invocationForHost("opencode"),
392
- path: ".opencode/agents/pipeline-orchestrator.md"
404
+ path: `.opencode/agents/${OPENCODE_ORCHESTRATOR_AGENT_ID}.md`
393
405
  }] : [],
394
406
  ...nativeProfileEntries("opencode", config).map(([id, profile]) => ({
395
407
  content: markdown({
408
+ name: nativeAgentIdForHost("opencode", id),
396
409
  description: profile.description ?? id,
397
410
  hidden: false,
398
411
  mode: "subagent",
@@ -410,7 +423,7 @@ function opencodeDefinitions(config, cwd) {
410
423
  ].join("\n")),
411
424
  host: "opencode",
412
425
  invocation: invocationForHost("opencode"),
413
- path: `.opencode/agents/${id}.md`
426
+ path: `.opencode/agents/${nativeAgentIdForHost("opencode", id)}.md`
414
427
  })),
415
428
  ...opencodeSkillProjections(config, cwd).map(opencodeSkillDefinition),
416
429
  ...localPluginDefinitions(),
@@ -3,6 +3,59 @@ import { generateScheduleArtifact } from "./schedule-planner.js";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/moka-submit.d.ts
6
+ declare const mokaSubmitDirectHooksSchema: z.ZodRecord<z.ZodEnum<{
7
+ "workflow.start": "workflow.start";
8
+ "workflow.success": "workflow.success";
9
+ "workflow.failure": "workflow.failure";
10
+ "workflow.complete": "workflow.complete";
11
+ "node.start": "node.start";
12
+ "node.success": "node.success";
13
+ "node.error": "node.error";
14
+ "node.finish": "node.finish";
15
+ "gate.failure": "gate.failure";
16
+ }> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
17
+ failure: z.ZodDefault<z.ZodEnum<{
18
+ fail: "fail";
19
+ ignore: "ignore";
20
+ }>>;
21
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
22
+ publishResult: z.ZodOptional<z.ZodBoolean>;
23
+ saveResultAs: z.ZodOptional<z.ZodString>;
24
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
25
+ where: z.ZodOptional<z.ZodObject<{
26
+ gate: z.ZodOptional<z.ZodString>;
27
+ node: z.ZodOptional<z.ZodString>;
28
+ workflow: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strict>>;
30
+ command: z.ZodArray<z.ZodString>;
31
+ kind: z.ZodLiteral<"command">;
32
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
33
+ trusted: z.ZodOptional<z.ZodBoolean>;
34
+ }, z.core.$strict>, z.ZodObject<{
35
+ failure: z.ZodDefault<z.ZodEnum<{
36
+ fail: "fail";
37
+ ignore: "ignore";
38
+ }>>;
39
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
40
+ publishResult: z.ZodOptional<z.ZodBoolean>;
41
+ saveResultAs: z.ZodOptional<z.ZodString>;
42
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
43
+ where: z.ZodOptional<z.ZodObject<{
44
+ gate: z.ZodOptional<z.ZodString>;
45
+ node: z.ZodOptional<z.ZodString>;
46
+ workflow: z.ZodOptional<z.ZodString>;
47
+ }, z.core.$strict>>;
48
+ kind: z.ZodLiteral<"module">;
49
+ module: z.ZodString;
50
+ }, z.core.$strict>], "kind">>;
51
+ declare const mokaSubmitHookPolicySchema: z.ZodObject<{
52
+ allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
53
+ allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
54
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
55
+ envPassthrough: z.ZodOptional<z.ZodArray<z.ZodString>>;
56
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
57
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
58
+ }, z.core.$strict>;
6
59
  declare const mokaSubmitResultSchema: z.ZodObject<{
7
60
  namespace: z.ZodString;
8
61
  payloadConfigMapName: z.ZodString;
@@ -15,6 +68,11 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
15
68
  delivery: z.ZodDefault<z.ZodObject<{
16
69
  pullRequest: z.ZodDefault<z.ZodBoolean>;
17
70
  }, z.core.$strict>>;
71
+ eventSink: z.ZodOptional<z.ZodObject<{
72
+ authHeader: z.ZodDefault<z.ZodString>;
73
+ authTokenFile: z.ZodOptional<z.ZodString>;
74
+ url: z.ZodString;
75
+ }, z.core.$strict>>;
18
76
  eventAuthSecretKey: z.ZodDefault<z.ZodString>;
19
77
  eventAuthSecretName: z.ZodDefault<z.ZodString>;
20
78
  eventUrl: z.ZodDefault<z.ZodString>;
@@ -25,6 +83,59 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
25
83
  }, z.core.$strict>>;
26
84
  generateName: z.ZodOptional<z.ZodString>;
27
85
  githubAuthSecretName: z.ZodDefault<z.ZodString>;
86
+ hookPolicy: z.ZodOptional<z.ZodObject<{
87
+ allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
88
+ allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
89
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
90
+ envPassthrough: z.ZodOptional<z.ZodArray<z.ZodString>>;
91
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
92
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
93
+ }, z.core.$strict>>;
94
+ hooks: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
95
+ "workflow.start": "workflow.start";
96
+ "workflow.success": "workflow.success";
97
+ "workflow.failure": "workflow.failure";
98
+ "workflow.complete": "workflow.complete";
99
+ "node.start": "node.start";
100
+ "node.success": "node.success";
101
+ "node.error": "node.error";
102
+ "node.finish": "node.finish";
103
+ "gate.failure": "gate.failure";
104
+ }> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
105
+ failure: z.ZodDefault<z.ZodEnum<{
106
+ fail: "fail";
107
+ ignore: "ignore";
108
+ }>>;
109
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
110
+ publishResult: z.ZodOptional<z.ZodBoolean>;
111
+ saveResultAs: z.ZodOptional<z.ZodString>;
112
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
113
+ where: z.ZodOptional<z.ZodObject<{
114
+ gate: z.ZodOptional<z.ZodString>;
115
+ node: z.ZodOptional<z.ZodString>;
116
+ workflow: z.ZodOptional<z.ZodString>;
117
+ }, z.core.$strict>>;
118
+ command: z.ZodArray<z.ZodString>;
119
+ kind: z.ZodLiteral<"command">;
120
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
121
+ trusted: z.ZodOptional<z.ZodBoolean>;
122
+ }, z.core.$strict>, z.ZodObject<{
123
+ failure: z.ZodDefault<z.ZodEnum<{
124
+ fail: "fail";
125
+ ignore: "ignore";
126
+ }>>;
127
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
128
+ publishResult: z.ZodOptional<z.ZodBoolean>;
129
+ saveResultAs: z.ZodOptional<z.ZodString>;
130
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
131
+ where: z.ZodOptional<z.ZodObject<{
132
+ gate: z.ZodOptional<z.ZodString>;
133
+ node: z.ZodOptional<z.ZodString>;
134
+ workflow: z.ZodOptional<z.ZodString>;
135
+ }, z.core.$strict>>;
136
+ kind: z.ZodLiteral<"module">;
137
+ module: z.ZodString;
138
+ }, z.core.$strict>], "kind">>>;
28
139
  image: z.ZodOptional<z.ZodString>;
29
140
  imagePullPolicy: z.ZodDefault<z.ZodEnum<{
30
141
  Always: "Always";
@@ -49,8 +160,8 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
49
160
  }, z.core.$strict>>;
50
161
  serviceAccountName: z.ZodDefault<z.ZodString>;
51
162
  mode: z.ZodEnum<{
52
- quick: "quick";
53
163
  full: "full";
164
+ quick: "quick";
54
165
  }>;
55
166
  schedulePath: z.ZodOptional<z.ZodString>;
56
167
  scheduleYaml: z.ZodOptional<z.ZodString>;
@@ -69,6 +180,11 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
69
180
  delivery: z.ZodDefault<z.ZodObject<{
70
181
  pullRequest: z.ZodDefault<z.ZodBoolean>;
71
182
  }, z.core.$strict>>;
183
+ eventSink: z.ZodOptional<z.ZodObject<{
184
+ authHeader: z.ZodDefault<z.ZodString>;
185
+ authTokenFile: z.ZodOptional<z.ZodString>;
186
+ url: z.ZodString;
187
+ }, z.core.$strict>>;
72
188
  eventAuthSecretKey: z.ZodDefault<z.ZodString>;
73
189
  eventAuthSecretName: z.ZodDefault<z.ZodString>;
74
190
  eventUrl: z.ZodDefault<z.ZodString>;
@@ -79,6 +195,59 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
79
195
  }, z.core.$strict>>;
80
196
  generateName: z.ZodOptional<z.ZodString>;
81
197
  githubAuthSecretName: z.ZodDefault<z.ZodString>;
198
+ hookPolicy: z.ZodOptional<z.ZodObject<{
199
+ allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
200
+ allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
201
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
202
+ envPassthrough: z.ZodOptional<z.ZodArray<z.ZodString>>;
203
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
204
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
205
+ }, z.core.$strict>>;
206
+ hooks: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
207
+ "workflow.start": "workflow.start";
208
+ "workflow.success": "workflow.success";
209
+ "workflow.failure": "workflow.failure";
210
+ "workflow.complete": "workflow.complete";
211
+ "node.start": "node.start";
212
+ "node.success": "node.success";
213
+ "node.error": "node.error";
214
+ "node.finish": "node.finish";
215
+ "gate.failure": "gate.failure";
216
+ }> & z.core.$partial, z.ZodDiscriminatedUnion<[z.ZodObject<{
217
+ failure: z.ZodDefault<z.ZodEnum<{
218
+ fail: "fail";
219
+ ignore: "ignore";
220
+ }>>;
221
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
222
+ publishResult: z.ZodOptional<z.ZodBoolean>;
223
+ saveResultAs: z.ZodOptional<z.ZodString>;
224
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
225
+ where: z.ZodOptional<z.ZodObject<{
226
+ gate: z.ZodOptional<z.ZodString>;
227
+ node: z.ZodOptional<z.ZodString>;
228
+ workflow: z.ZodOptional<z.ZodString>;
229
+ }, z.core.$strict>>;
230
+ command: z.ZodArray<z.ZodString>;
231
+ kind: z.ZodLiteral<"command">;
232
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
233
+ trusted: z.ZodOptional<z.ZodBoolean>;
234
+ }, z.core.$strict>, z.ZodObject<{
235
+ failure: z.ZodDefault<z.ZodEnum<{
236
+ fail: "fail";
237
+ ignore: "ignore";
238
+ }>>;
239
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
240
+ publishResult: z.ZodOptional<z.ZodBoolean>;
241
+ saveResultAs: z.ZodOptional<z.ZodString>;
242
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
243
+ where: z.ZodOptional<z.ZodObject<{
244
+ gate: z.ZodOptional<z.ZodString>;
245
+ node: z.ZodOptional<z.ZodString>;
246
+ workflow: z.ZodOptional<z.ZodString>;
247
+ }, z.core.$strict>>;
248
+ kind: z.ZodLiteral<"module">;
249
+ module: z.ZodString;
250
+ }, z.core.$strict>], "kind">>>;
82
251
  image: z.ZodOptional<z.ZodString>;
83
252
  imagePullPolicy: z.ZodDefault<z.ZodEnum<{
84
253
  Always: "Always";
@@ -117,6 +286,10 @@ declare const mokaSubmitOptionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
117
286
  }, z.core.$strict>], "type">;
118
287
  type MokaSubmitOptionsInput = z.input<typeof mokaSubmitOptionsSchema>;
119
288
  type MokaSubmitOptionsOutput = z.output<typeof mokaSubmitOptionsSchema>;
289
+ type MokaSubmitDirectHooksInput = z.input<typeof mokaSubmitDirectHooksSchema>;
290
+ type MokaSubmitDirectHooksOutput = z.output<typeof mokaSubmitDirectHooksSchema>;
291
+ type MokaSubmitHookPolicyInput = z.input<typeof mokaSubmitHookPolicySchema>;
292
+ type MokaSubmitHookPolicyOutput = z.output<typeof mokaSubmitHookPolicySchema>;
120
293
  type MokaSubmitInput = MokaSubmitOptionsInput & {
121
294
  config: PipelineConfig;
122
295
  worktreePath?: string;
@@ -157,4 +330,4 @@ interface MokaWorkflowSubmitOptions {
157
330
  }
158
331
  declare function submitMoka(rawOptions: MokaSubmitInput, dependencies?: SubmitMokaDependencies): Promise<MokaSubmitOutput>;
159
332
  //#endregion
160
- export { MokaSubmitInput, MokaSubmitOptions, MokaSubmitOptionsInput, MokaSubmitOptionsOutput, MokaSubmitOutput, MokaSubmitResult, mokaSubmitOptionsSchema, mokaSubmitResultSchema, submitMoka };
333
+ export { MokaSubmitDirectHooksInput, MokaSubmitDirectHooksOutput, MokaSubmitHookPolicyInput, MokaSubmitHookPolicyOutput, MokaSubmitInput, MokaSubmitOptions, MokaSubmitOptionsInput, MokaSubmitOptionsOutput, MokaSubmitOutput, MokaSubmitResult, mokaSubmitDirectHooksSchema, mokaSubmitHookPolicySchema, mokaSubmitOptionsSchema, mokaSubmitResultSchema, submitMoka };
@@ -1,4 +1,4 @@
1
- import { buildRunnerCommandPayload, runnerDeliverySchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskSchema } from "./runner-command-contract.js";
1
+ import { buildRunnerCommandPayload, runnerDeliverySchema, runnerHookPolicySchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskSchema } from "./runner-command-contract.js";
2
2
  import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "./schedule-planner.js";
3
3
  import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
4
4
  import { buildCommandScheduleYaml, submitRunnerArgoWorkflow } from "./argo-submit.js";
@@ -28,15 +28,55 @@ const mokaSubmitEventsSchema = z.object({
28
28
  authTokenFile: z.string().min(1).optional(),
29
29
  url: z.string().url()
30
30
  }).strict();
31
+ const MOKA_SUBMIT_HOOK_EVENTS = [
32
+ "workflow.start",
33
+ "workflow.success",
34
+ "workflow.failure",
35
+ "workflow.complete",
36
+ "node.start",
37
+ "node.success",
38
+ "node.error",
39
+ "node.finish",
40
+ "gate.failure"
41
+ ];
42
+ const mokaSubmitHookWhereSchema = z.object({
43
+ gate: z.string().min(1).optional(),
44
+ node: z.string().min(1).optional(),
45
+ workflow: z.string().min(1).optional()
46
+ }).strict();
47
+ const mokaSubmitHookBaseSchema = z.object({
48
+ failure: z.enum(["fail", "ignore"]).default("ignore"),
49
+ input: z.record(z.string(), z.unknown()).optional(),
50
+ publishResult: z.boolean().optional(),
51
+ saveResultAs: z.string().min(1).optional(),
52
+ timeoutMs: z.number().int().positive().optional(),
53
+ where: mokaSubmitHookWhereSchema.optional()
54
+ }).strict();
55
+ const mokaSubmitCommandHookSchema = mokaSubmitHookBaseSchema.extend({
56
+ command: z.array(z.string().min(1)).min(1),
57
+ kind: z.literal("command"),
58
+ outputLimitBytes: z.number().int().positive().optional(),
59
+ trusted: z.boolean().optional()
60
+ }).strict();
61
+ const mokaSubmitModuleHookSchema = mokaSubmitHookBaseSchema.extend({
62
+ kind: z.literal("module"),
63
+ module: z.string().min(1)
64
+ }).strict();
65
+ const mokaSubmitDirectHookSchema = z.discriminatedUnion("kind", [mokaSubmitCommandHookSchema, mokaSubmitModuleHookSchema]);
66
+ const mokaSubmitDirectHooksSchema = z.partialRecord(z.enum(MOKA_SUBMIT_HOOK_EVENTS), mokaSubmitDirectHookSchema);
67
+ const mokaSubmitHookPolicySchema = runnerHookPolicySchema;
31
68
  const mokaSubmitResultSchema = workflowSubmitResultSchema;
32
69
  const mokaSubmitBaseOptionsSchema = z.object({
33
70
  delivery: runnerDeliverySchema.default({ pullRequest: false }),
71
+ eventSink: mokaSubmitEventsSchema.optional(),
34
72
  eventAuthSecretKey: z.string().min(1).default(MOMOKAYA_EVENT_AUTH_SECRET_KEY),
35
73
  eventAuthSecretName: z.string().min(1).default(MOMOKAYA_EVENT_AUTH_SECRET_NAME),
36
74
  eventUrl: z.string().url().default(MOMOKAYA_EVENT_URL),
37
75
  events: mokaSubmitEventsSchema.optional(),
38
76
  generateName: z.string().min(1).optional(),
39
77
  githubAuthSecretName: z.string().min(1).default(MOMOKAYA_GITHUB_AUTH_SECRET_NAME),
78
+ hookPolicy: mokaSubmitHookPolicySchema.optional(),
79
+ hooks: mokaSubmitDirectHooksSchema.optional(),
40
80
  image: z.string().min(1).optional(),
41
81
  imagePullPolicy: imagePullPolicySchema,
42
82
  imagePullSecretName: z.string().min(1).default(MOMOKAYA_IMAGE_PULL_SECRET_NAME),
@@ -61,12 +101,88 @@ const mokaCommandSubmitOptionsSchema = mokaSubmitBaseOptionsSchema.extend({
61
101
  task: mokaSubmitTaskInputSchema.optional(),
62
102
  type: z.literal("command")
63
103
  }).strict();
64
- const mokaSubmitOptionsSchema = z.discriminatedUnion("type", [mokaGraphSubmitOptionsSchema, mokaCommandSubmitOptionsSchema]);
104
+ const mokaSubmitOptionsSchema = z.discriminatedUnion("type", [mokaGraphSubmitOptionsSchema, mokaCommandSubmitOptionsSchema]).superRefine((data, ctx) => {
105
+ if (data.eventSink !== void 0 && data.events !== void 0) ctx.addIssue({
106
+ code: "custom",
107
+ message: "Choose either eventSink or events, not both",
108
+ path: ["eventSink"]
109
+ });
110
+ });
111
+ const submitHookId = (event) => `moka-submit-${event.replaceAll(".", "-")}`;
112
+ function objectWithoutUndefined(value) {
113
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
114
+ }
115
+ function hookFunctionForSubmitHook(hook) {
116
+ if (hook.kind === "module") return objectWithoutUndefined({
117
+ kind: "module",
118
+ module: hook.module,
119
+ timeout_ms: hook.timeoutMs
120
+ });
121
+ return objectWithoutUndefined({
122
+ command: hook.command,
123
+ kind: "command",
124
+ output_limit_bytes: hook.outputLimitBytes,
125
+ protocol: {
126
+ input: "file",
127
+ result: "file"
128
+ },
129
+ timeout_ms: hook.timeoutMs,
130
+ trusted: hook.trusted
131
+ });
132
+ }
133
+ function hookBindingForSubmitHook(event, hook) {
134
+ const id = submitHookId(event);
135
+ const result = hook.publishResult === void 0 && hook.saveResultAs === void 0 ? void 0 : objectWithoutUndefined({
136
+ publish: hook.publishResult,
137
+ save_as: hook.saveResultAs
138
+ });
139
+ return objectWithoutUndefined({
140
+ failure: hook.failure,
141
+ function: id,
142
+ id,
143
+ result,
144
+ where: hook.where,
145
+ with: hook.input
146
+ });
147
+ }
148
+ function submitHookEntries(hooks) {
149
+ return Object.entries(hooks ?? {}).filter((entry) => entry[1] !== void 0).map(([event, hook]) => ({
150
+ event,
151
+ hook
152
+ }));
153
+ }
154
+ function cloneHookBindings(on) {
155
+ return Object.fromEntries(Object.entries(on).map(([event, bindings]) => [event, [...bindings]]));
156
+ }
157
+ function appendSubmitHook(event, hook, target) {
158
+ const id = submitHookId(event);
159
+ if (target.functions[id] !== void 0) throw new Error(`Moka submit hook id already exists in config: ${id}`);
160
+ target.functions[id] = hookFunctionForSubmitHook(hook);
161
+ target.on[event] = [...target.on[event] ?? [], hookBindingForSubmitHook(event, hook)];
162
+ }
163
+ function configWithSubmitHooks(config, hooks) {
164
+ const entries = submitHookEntries(hooks);
165
+ if (entries.length === 0) return config;
166
+ const target = {
167
+ functions: { ...config.hooks.functions },
168
+ on: cloneHookBindings(config.hooks.on)
169
+ };
170
+ for (const { event, hook } of entries) appendSubmitHook(event, hook, target);
171
+ return {
172
+ ...config,
173
+ hooks: {
174
+ ...config.hooks,
175
+ functions: target.functions,
176
+ on: target.on
177
+ }
178
+ };
179
+ }
65
180
  function submitMoka(rawOptions, dependencies = {}) {
66
181
  const { config, worktreePath, ...schemaOptions } = rawOptions;
182
+ const options = mokaSubmitOptionsSchema.parse(schemaOptions);
67
183
  const parsedOptions = {
68
- ...mokaSubmitOptionsSchema.parse(schemaOptions),
69
- config,
184
+ ...options,
185
+ config: configWithSubmitHooks(config, options.hooks),
70
186
  worktreePath
71
187
  };
72
188
  return parsedOptions.type === "command" ? submitMokaCommand(parsedOptions, dependencies) : submitMokaGraph(parsedOptions, dependencies);
@@ -184,6 +300,7 @@ function runnerPayloadJson(input) {
184
300
  return JSON.stringify(buildRunnerCommandPayload({
185
301
  delivery: input.options.delivery,
186
302
  events: runnerEvents(input.options),
303
+ hookPolicy: input.options.hookPolicy,
187
304
  repository: {
188
305
  baseBranch: input.context.repository.baseBranch,
189
306
  sha: input.context.repository.sha,
@@ -200,10 +317,11 @@ function runnerPayloadJson(input) {
200
317
  }));
201
318
  }
202
319
  function runnerEvents(options) {
203
- if (options.events) return {
204
- authHeader: options.events.authHeader,
205
- authTokenFile: options.events.authTokenFile ?? eventAuthTokenFile(options),
206
- url: options.events.url
320
+ const eventSink = options.eventSink ?? options.events;
321
+ if (eventSink) return {
322
+ authHeader: eventSink.authHeader,
323
+ authTokenFile: eventSink.authTokenFile ?? eventAuthTokenFile(options),
324
+ url: eventSink.url
207
325
  };
208
326
  return {
209
327
  authHeader: "Authorization",
@@ -279,4 +397,4 @@ function generateRunId(dependencies) {
279
397
  return dependencies.generateRunId?.() ?? `run-${randomBytes(8).toString("hex")}`;
280
398
  }
281
399
  //#endregion
282
- export { mokaSubmitOptionsSchema, mokaSubmitResultSchema, submitMoka };
400
+ export { mokaSubmitDirectHooksSchema, mokaSubmitHookPolicySchema, mokaSubmitOptionsSchema, mokaSubmitResultSchema, submitMoka };
@@ -64,7 +64,7 @@ async function runRunnerCommand(rawOptions = {}) {
64
64
  });
65
65
  const result = await runScheduledWorkflowTask({
66
66
  config: compiled.config,
67
- hookPolicy: { allowCommandHooks: true },
67
+ hookPolicy: payload.hookPolicy,
68
68
  nodeId: descriptor.nodeId,
69
69
  reporter: (event) => sink.recordRuntimeEvent(event),
70
70
  runId: payload.run.id,
@@ -1,4 +1,4 @@
1
- import { PipelineRuntimeEvent } from "./runtime/contracts/contracts.js";
1
+ import { HookRuntimePolicy, PipelineRuntimeEvent } from "./runtime/contracts/contracts.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/runner-command-contract.d.ts
@@ -43,8 +43,8 @@ declare const runnerDeliverySchema: z.ZodObject<{
43
43
  declare const mokaSubmissionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
44
44
  kind: z.ZodLiteral<"graph">;
45
45
  mode: z.ZodEnum<{
46
- quick: "quick";
47
46
  full: "full";
47
+ quick: "quick";
48
48
  }>;
49
49
  }, z.core.$strict>, z.ZodObject<{
50
50
  argv: z.ZodArray<z.ZodString>;
@@ -55,6 +55,14 @@ declare const runnerEventsSchema: z.ZodObject<{
55
55
  authTokenFile: z.ZodString;
56
56
  url: z.ZodString;
57
57
  }, z.core.$strict>;
58
+ declare const runnerHookPolicySchema: z.ZodObject<{
59
+ allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
60
+ allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
61
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
62
+ envPassthrough: z.ZodOptional<z.ZodArray<z.ZodString>>;
63
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
64
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
65
+ }, z.core.$strict>;
58
66
  declare const runnerMomokayaContextSchema: z.ZodObject<{
59
67
  automationNamespace: z.ZodOptional<z.ZodString>;
60
68
  previewEnabled: z.ZodBoolean;
@@ -70,6 +78,14 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
70
78
  authTokenFile: z.ZodString;
71
79
  url: z.ZodString;
72
80
  }, z.core.$strict>;
81
+ hookPolicy: z.ZodOptional<z.ZodObject<{
82
+ allowCommandHooks: z.ZodOptional<z.ZodBoolean>;
83
+ allowUntrustedCommandHooks: z.ZodOptional<z.ZodBoolean>;
84
+ env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
85
+ envPassthrough: z.ZodOptional<z.ZodArray<z.ZodString>>;
86
+ outputLimitBytes: z.ZodOptional<z.ZodNumber>;
87
+ timeoutMs: z.ZodOptional<z.ZodNumber>;
88
+ }, z.core.$strict>>;
73
89
  momokaya: z.ZodOptional<z.ZodObject<{
74
90
  automationNamespace: z.ZodOptional<z.ZodString>;
75
91
  previewEnabled: z.ZodBoolean;
@@ -88,8 +104,8 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
88
104
  submission: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
89
105
  kind: z.ZodLiteral<"graph">;
90
106
  mode: z.ZodEnum<{
91
- quick: "quick";
92
107
  full: "full";
108
+ quick: "quick";
93
109
  }>;
94
110
  }, z.core.$strict>, z.ZodObject<{
95
111
  argv: z.ZodArray<z.ZodString>;
@@ -111,6 +127,7 @@ declare const runnerCommandPayloadSchema: z.ZodObject<{
111
127
  }, z.core.$strict>;
112
128
  type RunnerDelivery = z.infer<typeof runnerDeliverySchema>;
113
129
  type RunnerEvents = z.infer<typeof runnerEventsSchema>;
130
+ type RunnerHookPolicy = z.infer<typeof runnerHookPolicySchema>;
114
131
  type MokaSubmission = z.infer<typeof mokaSubmissionSchema>;
115
132
  type RunnerCommandPayload = z.infer<typeof runnerCommandPayloadSchema>;
116
133
  type RunnerMomokayaContext = z.infer<typeof runnerMomokayaContextSchema>;
@@ -130,6 +147,7 @@ declare class RunnerCommandPayloadValidationError extends Error {
130
147
  interface BuildRunnerCommandPayloadOptions {
131
148
  delivery?: RunnerDelivery;
132
149
  events: RunnerEvents;
150
+ hookPolicy?: HookRuntimePolicy;
133
151
  momokaya?: RunnerMomokayaContext;
134
152
  repository: RunnerRepositoryContext;
135
153
  run: RunnerRunIdentity;
@@ -271,4 +289,4 @@ declare function buildRunnerCommandPayload(options: BuildRunnerCommandPayloadOpt
271
289
  declare function parseRunnerCommandPayload(rawPayload: string): RunnerCommandPayload;
272
290
  declare function mapRuntimeEventToRunnerEventRecords(event: PipelineRuntimeEvent, context: RunnerEventMappingContext): RunnerEventRecord[];
273
291
  //#endregion
274
- export { BuildRunnerCommandPayloadOptions, MokaSubmission, ResolveRunnerEventSinkAuthTokenOptions, RunnerArtifactDetails, RunnerArtifactStatus, RunnerCommandPayload, RunnerCommandPayloadValidationError, RunnerCommandPayloadValidationIssue, RunnerDelivery, RunnerEventMappingContext, RunnerEventRecord, RunnerEvents, RunnerFinalResultDetails, RunnerGateDetails, RunnerGateStatus, RunnerHookResultDetails, RunnerLogDetails, RunnerLogLevel, RunnerMomokayaContext, RunnerNodeDetails, RunnerNodeStatus, RunnerRepositoryContext, RunnerRunIdentity, RunnerTask, RunnerWorkflowEdgeDetails, RunnerWorkflowEdgeRecordDetails, RunnerWorkflowIdentity, RunnerWorkflowNodeDetails, RunnerWorkflowPlanDetails, buildRunnerCommandPayload, gitRemoteUrlSchema, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema, runnerDeliverySchema, runnerEventsSchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskPromptSchema, runnerTaskSchema, runnerTaskTicketSchema, runnerWorkflowIdentitySchema };
292
+ export { BuildRunnerCommandPayloadOptions, MokaSubmission, ResolveRunnerEventSinkAuthTokenOptions, RunnerArtifactDetails, RunnerArtifactStatus, RunnerCommandPayload, RunnerCommandPayloadValidationError, RunnerCommandPayloadValidationIssue, RunnerDelivery, RunnerEventMappingContext, RunnerEventRecord, RunnerEvents, RunnerFinalResultDetails, RunnerGateDetails, RunnerGateStatus, RunnerHookPolicy, RunnerHookResultDetails, RunnerLogDetails, RunnerLogLevel, RunnerMomokayaContext, RunnerNodeDetails, RunnerNodeStatus, RunnerRepositoryContext, RunnerRunIdentity, RunnerTask, RunnerWorkflowEdgeDetails, RunnerWorkflowEdgeRecordDetails, RunnerWorkflowIdentity, RunnerWorkflowNodeDetails, RunnerWorkflowPlanDetails, buildRunnerCommandPayload, gitRemoteUrlSchema, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema, runnerDeliverySchema, runnerEventsSchema, runnerHookPolicySchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskPromptSchema, runnerTaskSchema, runnerTaskTicketSchema, runnerWorkflowIdentitySchema };
@@ -43,6 +43,14 @@ const runnerEventsSchema = z.object({
43
43
  authTokenFile: z.string().min(1),
44
44
  url: z.string().url()
45
45
  }).strict();
46
+ const runnerHookPolicySchema = z.object({
47
+ allowCommandHooks: z.boolean().optional(),
48
+ allowUntrustedCommandHooks: z.boolean().optional(),
49
+ env: z.record(z.string(), z.string()).optional(),
50
+ envPassthrough: z.array(z.string().min(1)).optional(),
51
+ outputLimitBytes: z.number().int().positive().optional(),
52
+ timeoutMs: z.number().int().positive().optional()
53
+ }).strict();
46
54
  const runnerMomokayaContextSchema = z.object({
47
55
  automationNamespace: z.string().min(1).optional(),
48
56
  previewEnabled: z.boolean(),
@@ -52,6 +60,7 @@ const runnerCommandPayloadSchema = z.object({
52
60
  contractVersion: z.literal(RUNNER_COMMAND_CONTRACT_VERSION, { error: "runner command payload contract version must be 1" }).default(RUNNER_COMMAND_CONTRACT_VERSION),
53
61
  delivery: runnerDeliverySchema.default({ pullRequest: false }),
54
62
  events: runnerEventsSchema,
63
+ hookPolicy: runnerHookPolicySchema.optional(),
55
64
  momokaya: runnerMomokayaContextSchema.optional(),
56
65
  repository: runnerRepositoryContextSchema,
57
66
  run: runnerRunIdentitySchema,
@@ -79,6 +88,7 @@ function buildRunnerCommandPayload(options) {
79
88
  contractVersion: RUNNER_COMMAND_CONTRACT_VERSION,
80
89
  delivery: options.delivery,
81
90
  events: options.events,
91
+ hookPolicy: options.hookPolicy,
82
92
  ...options.momokaya ? { momokaya: options.momokaya } : {},
83
93
  repository: options.repository,
84
94
  run: options.run,
@@ -433,4 +443,4 @@ function throwUnhandledRuntimeEvent(value) {
433
443
  throw new Error(`Unhandled runtime event: ${value.type}`);
434
444
  }
435
445
  //#endregion
436
- export { RunnerCommandPayloadValidationError, buildRunnerCommandPayload, gitRemoteUrlSchema, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema, runnerDeliverySchema, runnerEventsSchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskPromptSchema, runnerTaskSchema, runnerTaskTicketSchema, runnerWorkflowIdentitySchema };
446
+ export { RunnerCommandPayloadValidationError, buildRunnerCommandPayload, gitRemoteUrlSchema, mapRuntimeEventToRunnerEventRecords, parseRunnerCommandPayload, resolveRunnerEventSinkAuthToken, runnerCommandPayloadSchema, runnerDeliverySchema, runnerEventsSchema, runnerHookPolicySchema, runnerRepositoryContextSchema, runnerRunIdentitySchema, runnerTaskPromptSchema, runnerTaskSchema, runnerTaskTicketSchema, runnerWorkflowIdentitySchema };
@@ -67,7 +67,7 @@ function noProgressResult(state, prompts) {
67
67
  function createGoalContinuationLaunchPlan(input) {
68
68
  return createRunnerLaunchPlan(input.config, {
69
69
  nodeId: input.nodeId ?? "goal-continuation",
70
- profileId: input.profileId ?? "pipeline-code-writer",
70
+ profileId: input.profileId ?? "moka-code-writer",
71
71
  prompt: input.prompt,
72
72
  worktreePath: input.worktreePath
73
73
  });
@@ -30,9 +30,9 @@ const SCHEDULE_BUILTINS = [
30
30
  "typecheck"
31
31
  ];
32
32
  const DEFAULT_GENERATED_COVERAGE_PROFILE_PREFERENCE = [
33
- "pipeline-verifier",
34
- "pipeline-acceptance-reviewer",
35
- "pipeline-thermo-nuclear-reviewer"
33
+ "moka-verifier",
34
+ "moka-acceptance-reviewer",
35
+ "moka-thermo-nuclear-reviewer"
36
36
  ];
37
37
  const scheduleArtifactSchema = z.object({
38
38
  generated_at: z.string().datetime(),
@@ -273,19 +273,19 @@ function quickBaselineWorkflow() {
273
273
  {
274
274
  id: "backlog-intake",
275
275
  kind: "agent",
276
- profile: "pipeline-researcher"
276
+ profile: "moka-researcher"
277
277
  },
278
278
  {
279
279
  id: "red-tests",
280
280
  kind: "agent",
281
281
  needs: ["backlog-intake"],
282
- profile: "pipeline-test-writer"
282
+ profile: "moka-test-writer"
283
283
  },
284
284
  {
285
285
  id: "implement",
286
286
  kind: "agent",
287
287
  needs: ["red-tests"],
288
- profile: "pipeline-code-writer"
288
+ profile: "moka-code-writer"
289
289
  },
290
290
  {
291
291
  builtin: "test",
@@ -330,7 +330,7 @@ function quickBaselineWorkflow() {
330
330
  id: "verify",
331
331
  kind: "agent",
332
332
  needs: ["mechanical-tests", "mechanical-typecheck"],
333
- profile: "pipeline-verifier"
333
+ profile: "moka-verifier"
334
334
  }
335
335
  ]
336
336
  } };
@@ -342,13 +342,13 @@ function executeBaselineWorkflow() {
342
342
  {
343
343
  id: "backlog-intake",
344
344
  kind: "agent",
345
- profile: "pipeline-researcher"
345
+ profile: "moka-researcher"
346
346
  },
347
347
  {
348
348
  id: "research",
349
349
  kind: "agent",
350
350
  needs: ["backlog-intake"],
351
- profile: "pipeline-researcher"
351
+ profile: "moka-researcher"
352
352
  },
353
353
  {
354
354
  gates: [{
@@ -377,13 +377,13 @@ function executeBaselineWorkflow() {
377
377
  id: "red-tests",
378
378
  kind: "agent",
379
379
  needs: ["research"],
380
- profile: "pipeline-test-writer"
380
+ profile: "moka-test-writer"
381
381
  },
382
382
  {
383
383
  id: "green-implementation",
384
384
  kind: "agent",
385
385
  needs: ["red-tests"],
386
- profile: "pipeline-code-writer"
386
+ profile: "moka-code-writer"
387
387
  },
388
388
  {
389
389
  builtin: "test",
@@ -428,7 +428,7 @@ function executeBaselineWorkflow() {
428
428
  "mechanical-green-lint",
429
429
  "mechanical-green-fallow"
430
430
  ],
431
- profile: "pipeline-acceptance-reviewer"
431
+ profile: "moka-acceptance-reviewer"
432
432
  },
433
433
  {
434
434
  gates: [
@@ -471,13 +471,13 @@ function executeBaselineWorkflow() {
471
471
  id: "verification",
472
472
  kind: "agent",
473
473
  needs: ["acceptance-review"],
474
- profile: "pipeline-verifier"
474
+ profile: "moka-verifier"
475
475
  },
476
476
  {
477
477
  id: "learn",
478
478
  kind: "agent",
479
479
  needs: ["verification"],
480
- profile: "pipeline-learner"
480
+ profile: "moka-learner"
481
481
  }
482
482
  ]
483
483
  } };
@@ -55,7 +55,7 @@ profiles:
55
55
  tools: [read, list, grep, glob, bash]
56
56
  filesystem:
57
57
  mode: read-only
58
- pipeline-researcher:
58
+ moka-researcher:
59
59
  runner: opencode
60
60
  instructions:
61
61
  path: .pipeline/prompts/researcher.md
@@ -106,12 +106,12 @@ workflows:
106
106
  nodes:
107
107
  - id: inspect
108
108
  kind: agent
109
- profile: pipeline-inspector
109
+ profile: moka-inspector
110
110
  default:
111
111
  nodes:
112
112
  - id: research
113
113
  kind: agent
114
- profile: pipeline-researcher
114
+ profile: moka-researcher
115
115
  - id: verify
116
116
  kind: builtin
117
117
  builtin: test
@@ -73,9 +73,9 @@ mcp_gateway:
73
73
  tool_prefixes: [backlog]
74
74
 
75
75
  profiles:
76
- pipeline-researcher:
76
+ moka-researcher:
77
77
  mcp_servers: [pipeline-gateway]
78
- pipeline-test-writer:
78
+ moka-test-writer:
79
79
  mcp_servers: []
80
80
  ```
81
81
 
@@ -148,6 +148,16 @@ moka submit "fix the login bug" --quick --kubeconfig ~/.kube/config --namespace
148
148
  There is no separate workstation-local `submit` path; local submission means
149
149
  submitting to a local Kubernetes cluster.
150
150
 
151
+ Pipeline Console and other TypeScript control planes should use
152
+ `@oisincoveney/pipeline/moka-submit` instead of shelling out or importing Argo
153
+ internals. The public API accepts `eventSink` for runner event delivery,
154
+ `hooks` for direct lifecycle hook declarations, and `hookPolicy` for per-run
155
+ hook execution policy. `eventSink` is not a hook mechanism; it is the transport
156
+ used by runner pods to POST durable events back to the control plane. Direct
157
+ submit hooks are normalized into deterministic internal function ids like
158
+ `moka-submit-node-finish`; non-conflicting package hooks remain intact, and a
159
+ generated id collision is rejected before submission.
160
+
151
161
  ### Runner Workflow Shape
152
162
 
153
163
  The runner container entrypoint is `moka runner-command`. Validation errors exit
@@ -371,7 +381,7 @@ skills:
371
381
  path: .agents/skills/accessibility-review/SKILL.md
372
382
 
373
383
  profiles:
374
- pipeline-frontend-reviewer:
384
+ moka-frontend-reviewer:
375
385
  runner: opencode
376
386
  instructions:
377
387
  path: .pipeline/prompts/frontend-reviewer.md
@@ -390,7 +400,7 @@ mcp_gateway:
390
400
  authorization_env: PIPELINE_MCP_GATEWAY_AUTHORIZATION
391
401
 
392
402
  profiles:
393
- pipeline-router:
403
+ moka-router:
394
404
  runner: opencode
395
405
  instructions:
396
406
  path: .pipeline/prompts/router.md
package/package.json CHANGED
@@ -120,7 +120,7 @@
120
120
  "prepack": "bun run build:cli"
121
121
  },
122
122
  "type": "module",
123
- "version": "1.26.1",
123
+ "version": "1.27.0",
124
124
  "description": "Config-driven multi-agent pipeline runner for repository work",
125
125
  "main": "./dist/index.js",
126
126
  "types": "./dist/index.d.ts",