stageflow 0.42.0 → 0.44.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.
Files changed (275) hide show
  1. package/README.md +12 -3
  2. package/dist/agent/activity.d.ts +18 -0
  3. package/dist/agent/claudeAdapter.js +7 -0
  4. package/dist/agent/claudeTools.d.ts +27 -18
  5. package/dist/agent/claudeTools.js +54 -15
  6. package/dist/agent/compactionEvents.d.ts +34 -0
  7. package/dist/agent/compactionEvents.js +37 -0
  8. package/dist/agent/openRouterRouting.d.ts +7 -0
  9. package/dist/agent/openRouterRouting.js +23 -0
  10. package/dist/agent/piAdapter.d.ts +6 -0
  11. package/dist/agent/piAdapter.js +128 -27
  12. package/dist/agent/piSettings.d.ts +7 -0
  13. package/dist/agent/piSettings.js +9 -0
  14. package/dist/agent/sandboxPiTools.d.ts +3 -1
  15. package/dist/agent/sandboxPiTools.js +8 -2
  16. package/dist/agent/stageTurnDriver.d.ts +11 -2
  17. package/dist/agent/stageTurnDriver.js +45 -4
  18. package/dist/agent/toolOutputBudget.d.ts +15 -1
  19. package/dist/agent/toolOutputBudget.js +18 -0
  20. package/dist/agent/toolOutputBudgetExtension.d.ts +2 -0
  21. package/dist/agent/toolOutputBudgetExtension.js +40 -3
  22. package/dist/agents/authorChat.d.ts +1 -0
  23. package/dist/agents/authorChat.js +7 -0
  24. package/dist/agents/authorSessionStore.d.ts +3 -0
  25. package/dist/agents/authorSessionStore.js +17 -2
  26. package/dist/agents/hostContext.d.ts +1 -1
  27. package/dist/agents/runtime/piRuntime.d.ts +5 -1
  28. package/dist/agents/runtime/piRuntime.js +46 -12
  29. package/dist/agents/service.js +3 -2
  30. package/dist/agents/sessions.js +3 -1
  31. package/dist/compaction/directives.d.ts +18 -0
  32. package/dist/compaction/directives.js +119 -0
  33. package/dist/compaction/extension.d.ts +20 -0
  34. package/dist/compaction/extension.js +78 -0
  35. package/dist/compaction/mask.d.ts +18 -0
  36. package/dist/compaction/mask.js +155 -0
  37. package/dist/compaction/pin.d.ts +21 -0
  38. package/dist/compaction/pin.js +270 -0
  39. package/dist/compaction/scrub.d.ts +7 -0
  40. package/dist/compaction/scrub.js +26 -0
  41. package/dist/compaction/sessionWiring.d.ts +22 -0
  42. package/dist/compaction/sessionWiring.js +26 -0
  43. package/dist/compaction/summarize.d.ts +79 -0
  44. package/dist/compaction/summarize.js +171 -0
  45. package/dist/compaction/toolPolicy.d.ts +3 -0
  46. package/dist/compaction/toolPolicy.js +33 -0
  47. package/dist/compaction/types.d.ts +45 -0
  48. package/dist/compaction/types.js +7 -0
  49. package/dist/compaction/workingState.d.ts +56 -0
  50. package/dist/compaction/workingState.js +198 -0
  51. package/dist/config/createPipeline.js +2 -0
  52. package/dist/config/createTrigger.js +7 -1
  53. package/dist/config/ensureCatalogScanRoot.d.ts +2 -0
  54. package/dist/config/ensureCatalogScanRoot.js +6 -0
  55. package/dist/config/evalCatalog.js +30 -2
  56. package/dist/config/githubTriggerAction.d.ts +8 -0
  57. package/dist/config/githubTriggerAction.js +29 -0
  58. package/dist/config/hostConfig.js +2 -0
  59. package/dist/config/loadEval.js +21 -3
  60. package/dist/config/pipelineNeeds.d.ts +2 -0
  61. package/dist/config/pipelineNeeds.js +16 -3
  62. package/dist/config/pipelineRoute.js +5 -4
  63. package/dist/config/validateCatalog.d.ts +1 -1
  64. package/dist/config/validateCatalog.js +12 -0
  65. package/dist/config/validateEval.js +43 -2
  66. package/dist/config/writeEval.js +2 -2
  67. package/dist/envelope/assertEmittedArtifacts.d.ts +12 -0
  68. package/dist/envelope/assertEmittedArtifacts.js +43 -0
  69. package/dist/envelope/preEmitChecks.d.ts +3 -0
  70. package/dist/evals/constants.d.ts +3 -1
  71. package/dist/evals/constants.js +3 -1
  72. package/dist/evals/engine/FakeEngine.d.ts +1 -1
  73. package/dist/evals/engine/FakeEngine.js +2 -1
  74. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +1 -1
  75. package/dist/evals/engine/promptfoo/PromptfooEngine.js +2 -1
  76. package/dist/evals/engine/promptfoo/generate.d.ts +1 -1
  77. package/dist/evals/engine/promptfoo/generate.js +11 -8
  78. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +1 -1
  79. package/dist/evals/engine/promptfoo/rubricPrompt.js +5 -3
  80. package/dist/evals/manager.d.ts +13 -0
  81. package/dist/evals/manager.js +10 -2
  82. package/dist/evals/plan.d.ts +8 -1
  83. package/dist/evals/plan.js +28 -9
  84. package/dist/evals/planInternalAgent.d.ts +19 -0
  85. package/dist/evals/planInternalAgent.js +205 -0
  86. package/dist/evals/read/api.d.ts +12 -0
  87. package/dist/evals/read/api.js +5 -0
  88. package/dist/evals/read/list.js +10 -1
  89. package/dist/evals/read/runs.d.ts +5 -0
  90. package/dist/evals/read/runs.js +7 -1
  91. package/dist/evals/runner/caseRunner.d.ts +2 -1
  92. package/dist/evals/runner/caseRunner.js +16 -5
  93. package/dist/evals/runner/internalAgentCaseRunner.d.ts +6 -0
  94. package/dist/evals/runner/internalAgentCaseRunner.js +250 -0
  95. package/dist/evals/snapshot.d.ts +10 -2
  96. package/dist/evals/snapshot.js +8 -3
  97. package/dist/evals/types.d.ts +12 -2
  98. package/dist/evals/types.js +4 -1
  99. package/dist/guide/cardContract.d.ts +133 -0
  100. package/dist/guide/cardContract.js +156 -0
  101. package/dist/guide/chat.d.ts +45 -0
  102. package/dist/guide/chat.js +378 -0
  103. package/dist/guide/knowledge/index.d.ts +14 -0
  104. package/dist/guide/knowledge/index.js +109 -0
  105. package/dist/guide/knowledge/topics/agents.d.ts +2 -0
  106. package/dist/guide/knowledge/topics/agents.js +58 -0
  107. package/dist/guide/knowledge/topics/bestPractices.d.ts +2 -0
  108. package/dist/guide/knowledge/topics/bestPractices.js +67 -0
  109. package/dist/guide/knowledge/topics/connections.d.ts +2 -0
  110. package/dist/guide/knowledge/topics/connections.js +66 -0
  111. package/dist/guide/knowledge/topics/consoleMap.d.ts +2 -0
  112. package/dist/guide/knowledge/topics/consoleMap.js +55 -0
  113. package/dist/guide/knowledge/topics/evals.d.ts +2 -0
  114. package/dist/guide/knowledge/topics/evals.js +72 -0
  115. package/dist/guide/knowledge/topics/limitations.d.ts +2 -0
  116. package/dist/guide/knowledge/topics/limitations.js +84 -0
  117. package/dist/guide/knowledge/topics/mcpSkillsBrowser.d.ts +2 -0
  118. package/dist/guide/knowledge/topics/mcpSkillsBrowser.js +57 -0
  119. package/dist/guide/knowledge/topics/memories.d.ts +2 -0
  120. package/dist/guide/knowledge/topics/memories.js +63 -0
  121. package/dist/guide/knowledge/topics/modelsAndProviders.d.ts +2 -0
  122. package/dist/guide/knowledge/topics/modelsAndProviders.js +56 -0
  123. package/dist/guide/knowledge/topics/overview.d.ts +2 -0
  124. package/dist/guide/knowledge/topics/overview.js +56 -0
  125. package/dist/guide/knowledge/topics/pipelines.d.ts +2 -0
  126. package/dist/guide/knowledge/topics/pipelines.js +93 -0
  127. package/dist/guide/knowledge/topics/stages.d.ts +2 -0
  128. package/dist/guide/knowledge/topics/stages.js +54 -0
  129. package/dist/guide/knowledge/topics/tasks.d.ts +2 -0
  130. package/dist/guide/knowledge/topics/tasks.js +52 -0
  131. package/dist/guide/knowledge/topics/triggers.d.ts +2 -0
  132. package/dist/guide/knowledge/topics/triggers.js +68 -0
  133. package/dist/guide/knowledge/topics/troubleshooting.d.ts +2 -0
  134. package/dist/guide/knowledge/topics/troubleshooting.js +85 -0
  135. package/dist/guide/knowledgeTools.d.ts +2 -0
  136. package/dist/guide/knowledgeTools.js +56 -0
  137. package/dist/guide/showUi.d.ts +6 -0
  138. package/dist/guide/showUi.js +546 -0
  139. package/dist/guide/threadStore.d.ts +48 -0
  140. package/dist/guide/threadStore.js +189 -0
  141. package/dist/guide/writeRoot.d.ts +11 -0
  142. package/dist/guide/writeRoot.js +70 -0
  143. package/dist/hosted/translate.js +2 -0
  144. package/dist/mcp/agentTools.js +12 -0
  145. package/dist/mcp/catalogTools.js +18 -20
  146. package/dist/mcp/catalogWriteTools.d.ts +8 -0
  147. package/dist/mcp/catalogWriteTools.js +225 -0
  148. package/dist/mcp/connectionTools.d.ts +3 -0
  149. package/dist/mcp/connectionTools.js +85 -0
  150. package/dist/mcp/controlTools.js +34 -1
  151. package/dist/mcp/deps.d.ts +9 -0
  152. package/dist/mcp/evalTools.js +684 -21
  153. package/dist/mcp/memoryHostTools.d.ts +3 -0
  154. package/dist/mcp/memoryHostTools.js +72 -0
  155. package/dist/mcp/pipelineAuthorTools.js +14 -1
  156. package/dist/mcp/server.js +5 -0
  157. package/dist/mcp/tools.js +6 -0
  158. package/dist/mcp/triggerTools.js +77 -31
  159. package/dist/operatorAgent/evalGate.d.ts +22 -0
  160. package/dist/operatorAgent/evalGate.js +77 -0
  161. package/dist/operatorAgent/fakeHost.js +10 -2
  162. package/dist/operatorAgent/host.js +18 -2
  163. package/dist/operatorAgent/index.d.ts +1 -0
  164. package/dist/operatorAgent/index.js +1 -0
  165. package/dist/operatorAgent/internalAgents.d.ts +11 -0
  166. package/dist/operatorAgent/internalAgents.js +13 -0
  167. package/dist/operatorAgent/piModel.d.ts +5 -1
  168. package/dist/operatorAgent/piModel.js +15 -10
  169. package/dist/operatorAgent/profiles/agentAuthor.js +66 -22
  170. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  171. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +1 -1
  172. package/dist/operatorAgent/profiles/guideConfirm.d.ts +21 -0
  173. package/dist/operatorAgent/profiles/guideConfirm.js +100 -0
  174. package/dist/operatorAgent/profiles/guideTools.d.ts +7 -0
  175. package/dist/operatorAgent/profiles/guideTools.js +123 -0
  176. package/dist/operatorAgent/profiles/stageflowGuide.d.ts +7 -0
  177. package/dist/operatorAgent/profiles/stageflowGuide.js +132 -0
  178. package/dist/operatorAgent/profiles/workshopAuthor.js +23 -0
  179. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  180. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +9 -1
  181. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +2 -0
  182. package/dist/operatorAgent/profiles/workshopAuthorTools.js +197 -9
  183. package/dist/operatorAgent/types.d.ts +23 -0
  184. package/dist/package-meta.d.ts +1 -1
  185. package/dist/package-meta.js +1 -1
  186. package/dist/runstore/retention.d.ts +2 -1
  187. package/dist/runstore/retention.js +3 -7
  188. package/dist/runstore/runStatusClock.d.ts +17 -0
  189. package/dist/runstore/runStatusClock.js +19 -0
  190. package/dist/runstore/sqlite/SqliteRunStore.d.ts +1 -0
  191. package/dist/runstore/sqlite/SqliteRunStore.js +45 -45
  192. package/dist/runstore/workspaceLayout.js +6 -1
  193. package/dist/runtime/artifactDiskInspect.d.ts +14 -0
  194. package/dist/runtime/artifactDiskInspect.js +52 -0
  195. package/dist/runtime/artifactPath.d.ts +24 -0
  196. package/dist/runtime/artifactPath.js +84 -0
  197. package/dist/runtime/completionCheckRunner.js +15 -43
  198. package/dist/runtime/githubPollSource.d.ts +22 -4
  199. package/dist/runtime/githubPollSource.js +93 -8
  200. package/dist/runtime/priorArtifactGrants.d.ts +8 -0
  201. package/dist/runtime/priorArtifactGrants.js +105 -0
  202. package/dist/runtime/runActionEligibility.d.ts +46 -0
  203. package/dist/runtime/runActionEligibility.js +123 -0
  204. package/dist/runtime/runManager.d.ts +4 -1
  205. package/dist/runtime/runManager.js +52 -10
  206. package/dist/runtime/runRetryCoordinator.d.ts +2 -14
  207. package/dist/runtime/runRetryCoordinator.js +3 -67
  208. package/dist/runtime/sandboxContainer.d.ts +7 -0
  209. package/dist/runtime/sandboxContainer.js +48 -6
  210. package/dist/runtime/sandboxDesk.d.ts +2 -1
  211. package/dist/runtime/sandboxDesk.js +11 -12
  212. package/dist/runtime/stageEnvironment.js +1 -0
  213. package/dist/runtime/stageRoots.d.ts +12 -0
  214. package/dist/runtime/stageSandbox.d.ts +2 -1
  215. package/dist/runtime/stageSandbox.js +18 -2
  216. package/dist/runtime/stageWorker.js +20 -0
  217. package/dist/server/agentAuthorRoutes.js +12 -3
  218. package/dist/server/bootstrap.d.ts +5 -0
  219. package/dist/server/bootstrap.js +14 -1
  220. package/dist/server/evalRoutes.js +38 -9
  221. package/dist/server/guideRoutes.d.ts +5 -0
  222. package/dist/server/guideRoutes.js +166 -0
  223. package/dist/server/http.d.ts +10 -0
  224. package/dist/server/http.js +35 -1
  225. package/dist/server/internalAgentRoutes.d.ts +6 -0
  226. package/dist/server/internalAgentRoutes.js +12 -0
  227. package/dist/server/mcpHost.js +2 -0
  228. package/dist/toolkit/meta.d.ts +1 -1
  229. package/dist/toolkit/meta.js +39 -1
  230. package/dist/toolkit/profiles.d.ts +5 -0
  231. package/dist/toolkit/profiles.js +12 -0
  232. package/dist/tools/askOperator.js +2 -1
  233. package/dist/tools/emitStageEnvelope.d.ts +17 -16
  234. package/dist/tools/emitStageEnvelope.js +46 -32
  235. package/dist/tools/stageToolNames.d.ts +4 -0
  236. package/dist/tools/stageToolNames.js +4 -0
  237. package/dist/tools/workingState.d.ts +40 -0
  238. package/dist/tools/workingState.js +108 -0
  239. package/dist/tools/writeStageArtifact.d.ts +0 -9
  240. package/dist/tools/writeStageArtifact.js +2 -1
  241. package/dist/ui/assets/{MarkdownText-BTrOqu5J.js → MarkdownText-S5C_vemc.js} +2 -2
  242. package/dist/ui/assets/adapters-C0l4ruiR.js +17 -0
  243. package/dist/ui/assets/chat-CjnsmvqR.js +2 -0
  244. package/dist/ui/assets/chat-DFDd4UjS.css +2 -0
  245. package/dist/ui/assets/{main-CHlmTNJe.css → main-Del1GeLf.css} +1 -1
  246. package/dist/ui/assets/main-_yXVqcW9.js +277 -0
  247. package/dist/ui/chat.html +3 -4
  248. package/dist/ui/index.html +3 -4
  249. package/dist/workshop/chatTurn.js +1 -1
  250. package/dist/workshop/workshopPin.d.ts +3 -3
  251. package/dist/workshop/workshopPin.js +16 -15
  252. package/examples/compaction-stress/README.md +30 -0
  253. package/examples/compaction-stress/check-run.mjs +53 -0
  254. package/examples/compaction-stress/collect-evidence.yaml +60 -0
  255. package/examples/compaction-stress/compaction-stress.pipeline.yaml +141 -0
  256. package/examples/compaction-stress/compaction-stress.task.yaml +15 -0
  257. package/examples/compaction-stress/generate-evidence.mjs +110 -0
  258. package/examples/compaction-stress/root-cause.yaml +26 -0
  259. package/examples/compaction-stress/stageflow.yaml +9 -0
  260. package/examples/compaction-stress/write-postmortem.yaml +40 -0
  261. package/examples/evals/README.md +18 -0
  262. package/examples/evals/guide-behavior.dataset.jsonl +15 -0
  263. package/examples/evals/guide-behavior.eval.yaml +35 -0
  264. package/examples/evals/guide-depth.dataset.jsonl +35 -0
  265. package/examples/evals/guide-depth.eval.yaml +169 -0
  266. package/examples/memory/evals/guide-behavior.dataset.jsonl +15 -0
  267. package/examples/memory/evals/guide-behavior.eval.yaml +35 -0
  268. package/examples/memory/evals/guide-depth.dataset.jsonl +35 -0
  269. package/examples/memory/evals/guide-depth.eval.yaml +169 -0
  270. package/package.json +1 -1
  271. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +0 -9
  272. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +0 -6
  273. package/dist/ui/assets/chat-Df_wCpak.js +0 -5
  274. package/dist/ui/assets/chat-P7KQznHm.css +0 -2
  275. package/dist/ui/assets/main-CvVfxmMd.js +0 -271
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shared contracts for Stageflow's built-in compaction (docs/plans/compaction-strategy.md).
3
+ * Pure data types only: the layers (mask, pin, state, summary, guards) import from here.
4
+ */
5
+ export type PinnedFactKind = "id" | "number" | "timestamp" | "url" | "path" | "error" | "other";
6
+ /** A value copied verbatim out of a message so a summary cannot lose or reword it. */
7
+ export type PinnedFact = {
8
+ kind: PinnedFactKind;
9
+ /** The exact text, e.g. "INC-4471", "48211", "2026-03-14T05:52:03Z". */
10
+ value: string;
11
+ /** A short verbatim snippet around the value (single line, capped), for orientation. */
12
+ context: string;
13
+ /** Position in the message list the facts were extracted from, and the tool when from a tool result. */
14
+ source: {
15
+ messageIndex: number;
16
+ toolName?: string;
17
+ };
18
+ };
19
+ /** A tool result replaced by a placeholder in the model's context. */
20
+ export type ClearedResult = {
21
+ toolName: string;
22
+ toolCallId: string;
23
+ /** Where the full output can be read again (a file path or a tool + args hint). */
24
+ recoverHint: string;
25
+ bytes: number;
26
+ /** One line describing what the result was, for the recovery index. */
27
+ descriptor: string;
28
+ };
29
+ export type WorkingStateKind = "fact" | "decision" | "progress" | "todo" | "blocker";
30
+ /** One keyed entry in the model's working state (the `working_state` tool). */
31
+ export type WorkingStateEntry = {
32
+ key: string;
33
+ kind: WorkingStateKind;
34
+ value: string;
35
+ updatedAt: string;
36
+ };
37
+ export declare const WORKING_STATE_TOOL_NAME = "working_state";
38
+ /** Everything the compaction summary is built from, besides the model-written part. */
39
+ export type CompactionSections = {
40
+ pinned: readonly PinnedFact[];
41
+ cleared: readonly ClearedResult[];
42
+ state: readonly WorkingStateEntry[];
43
+ };
44
+ /** Bumped whenever the focus prompt or section layout changes (like RUBRIC_PROMPT_VERSION). */
45
+ export declare const COMPACTION_PROMPT_VERSION = 1;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared contracts for Stageflow's built-in compaction (docs/plans/compaction-strategy.md).
3
+ * Pure data types only: the layers (mask, pin, state, summary, guards) import from here.
4
+ */
5
+ export const WORKING_STATE_TOOL_NAME = "working_state";
6
+ /** Bumped whenever the focus prompt or section layout changes (like RUBRIC_PROMPT_VERSION). */
7
+ export const COMPACTION_PROMPT_VERSION = 1;
@@ -0,0 +1,56 @@
1
+ import type { WorkingStateEntry, WorkingStateKind } from "./types.js";
2
+ export declare const WORKING_STATE_CUSTOM_TYPE = "stageflow.working_state";
3
+ export declare const WORKING_STATE_KINDS: readonly WorkingStateKind[];
4
+ export declare const WORKING_STATE_MAX_KEY = 80;
5
+ export declare const WORKING_STATE_MAX_VALUE = 600;
6
+ export declare const WORKING_STATE_MAX_ENTRIES = 60;
7
+ export declare const WORKING_STATE_DEFAULT_RENDER_CHARS = 6000;
8
+ export declare const WORKING_STATE_GUIDANCE = "Older messages and tool results are trimmed automatically as the conversation grows, so keep your own working state with the working_state tool. As soon as you learn or decide something you will need later, record it: exact values and identifiers (fact), choices and their reasons (decision), what is done (progress), what remains (todo) and what is stuck (blocker). Keep each entry to one or two short lines, update an existing key instead of adding a duplicate, and delete entries that no longer matter. Never store secrets or credentials in it.";
9
+ export type WorkingStateEvent = {
10
+ op: "set";
11
+ key: string;
12
+ kind: WorkingStateKind;
13
+ value: string;
14
+ at: string;
15
+ } | {
16
+ op: "delete";
17
+ key: string;
18
+ at: string;
19
+ };
20
+ export type WorkingStateSetResult = {
21
+ ok: true;
22
+ dropped: string[];
23
+ replaced: boolean;
24
+ redacted: boolean;
25
+ entry: WorkingStateEntry;
26
+ } | {
27
+ ok: false;
28
+ error: string;
29
+ };
30
+ export type WorkingStateInput = {
31
+ key: string;
32
+ kind: WorkingStateKind;
33
+ value: string;
34
+ updatedAt?: string;
35
+ };
36
+ export declare class WorkingStateStore {
37
+ private readonly entries;
38
+ get size(): number;
39
+ set(input: WorkingStateInput): WorkingStateSetResult;
40
+ delete(key: string): boolean;
41
+ list(): WorkingStateEntry[];
42
+ apply(event: WorkingStateEvent): void;
43
+ load(events: readonly WorkingStateEvent[]): void;
44
+ render(maxChars?: number): string;
45
+ private oldestOfKind;
46
+ }
47
+ export declare function reduceWorkingState(events: readonly WorkingStateEvent[]): WorkingStateStore;
48
+ type SessionEntryLike = {
49
+ type?: unknown;
50
+ customType?: unknown;
51
+ data?: unknown;
52
+ };
53
+ export declare function workingStateEventsFromEntries(entries: readonly SessionEntryLike[]): WorkingStateEvent[];
54
+ export declare function restoreWorkingState(entries: readonly SessionEntryLike[]): WorkingStateStore;
55
+ export declare function getWorkingStateSections(entries: readonly SessionEntryLike[]): WorkingStateEntry[];
56
+ export {};
@@ -0,0 +1,198 @@
1
+ import { scrubSecrets } from "./scrub.js";
2
+ export const WORKING_STATE_CUSTOM_TYPE = "stageflow.working_state";
3
+ export const WORKING_STATE_KINDS = [
4
+ "fact",
5
+ "decision",
6
+ "progress",
7
+ "todo",
8
+ "blocker",
9
+ ];
10
+ export const WORKING_STATE_MAX_KEY = 80;
11
+ export const WORKING_STATE_MAX_VALUE = 600;
12
+ export const WORKING_STATE_MAX_ENTRIES = 60;
13
+ export const WORKING_STATE_DEFAULT_RENDER_CHARS = 6000;
14
+ export const WORKING_STATE_GUIDANCE = "Older messages and tool results are trimmed automatically as the conversation grows, so keep your own working state with the working_state tool. As soon as you learn or decide something you will need later, record it: exact values and identifiers (fact), choices and their reasons (decision), what is done (progress), what remains (todo) and what is stuck (blocker). Keep each entry to one or two short lines, update an existing key instead of adding a duplicate, and delete entries that no longer matter. Never store secrets or credentials in it.";
15
+ const RENDER_ORDER = [
16
+ "blocker",
17
+ "todo",
18
+ "decision",
19
+ "fact",
20
+ "progress",
21
+ ];
22
+ const EVICTION_ORDER = [
23
+ "progress",
24
+ "fact",
25
+ "decision",
26
+ "todo",
27
+ "blocker",
28
+ ];
29
+ const HEADINGS = {
30
+ blocker: "Blockers",
31
+ todo: "Todo",
32
+ decision: "Decisions",
33
+ fact: "Facts",
34
+ progress: "Progress",
35
+ };
36
+ function isKind(value) {
37
+ return (typeof value === "string" &&
38
+ WORKING_STATE_KINDS.includes(value));
39
+ }
40
+ function oneLine(text) {
41
+ return text.replace(/\s+/g, " ").trim();
42
+ }
43
+ export class WorkingStateStore {
44
+ entries = new Map();
45
+ get size() {
46
+ return this.entries.size;
47
+ }
48
+ set(input) {
49
+ if (!isKind(input.kind)) {
50
+ return {
51
+ ok: false,
52
+ error: `kind must be one of ${WORKING_STATE_KINDS.join(", ")}`,
53
+ };
54
+ }
55
+ const rawKey = oneLine(String(input.key ?? ""));
56
+ const rawValue = oneLine(String(input.value ?? ""));
57
+ const key = oneLine(scrubSecrets(rawKey));
58
+ const value = oneLine(scrubSecrets(rawValue));
59
+ const redacted = key !== rawKey || value !== rawValue;
60
+ if (key.length === 0)
61
+ return { ok: false, error: "key is required" };
62
+ if (key.length > WORKING_STATE_MAX_KEY) {
63
+ return {
64
+ ok: false,
65
+ error: `key is ${key.length} chars; max ${WORKING_STATE_MAX_KEY}`,
66
+ };
67
+ }
68
+ if (value.length === 0)
69
+ return { ok: false, error: "value is required" };
70
+ if (value.length > WORKING_STATE_MAX_VALUE) {
71
+ return {
72
+ ok: false,
73
+ error: `value is ${value.length} chars; max ${WORKING_STATE_MAX_VALUE}. Shorten it.`,
74
+ };
75
+ }
76
+ const replaced = this.entries.delete(key);
77
+ const dropped = [];
78
+ while (this.entries.size >= WORKING_STATE_MAX_ENTRIES) {
79
+ const victim = this.oldestOfKind("progress");
80
+ if (victim === undefined) {
81
+ return {
82
+ ok: false,
83
+ error: `working state is full (${WORKING_STATE_MAX_ENTRIES} entries, none are progress). Delete entries first.`,
84
+ };
85
+ }
86
+ this.entries.delete(victim);
87
+ dropped.push(victim);
88
+ }
89
+ const entry = {
90
+ key,
91
+ kind: input.kind,
92
+ value,
93
+ updatedAt: input.updatedAt ?? new Date().toISOString(),
94
+ };
95
+ this.entries.set(key, entry);
96
+ return { ok: true, dropped, replaced, redacted, entry };
97
+ }
98
+ delete(key) {
99
+ const raw = oneLine(String(key ?? ""));
100
+ return this.entries.delete(raw) || this.entries.delete(oneLine(scrubSecrets(raw)));
101
+ }
102
+ list() {
103
+ return [...this.entries.values()];
104
+ }
105
+ apply(event) {
106
+ if (event.op === "set") {
107
+ this.set({
108
+ key: event.key,
109
+ kind: event.kind,
110
+ value: event.value,
111
+ updatedAt: event.at,
112
+ });
113
+ }
114
+ else if (event.op === "delete") {
115
+ this.delete(event.key);
116
+ }
117
+ }
118
+ load(events) {
119
+ this.entries.clear();
120
+ for (const event of events)
121
+ this.apply(event);
122
+ }
123
+ render(maxChars = WORKING_STATE_DEFAULT_RENDER_CHARS) {
124
+ const all = this.list();
125
+ if (all.length === 0)
126
+ return "";
127
+ const keep = new Set(all);
128
+ let omitted = 0;
129
+ const build = () => {
130
+ const lines = ["## Working state"];
131
+ for (const kind of RENDER_ORDER) {
132
+ const group = all.filter((e) => keep.has(e) && e.kind === kind);
133
+ if (group.length === 0)
134
+ continue;
135
+ lines.push("", `### ${HEADINGS[kind]}`);
136
+ for (const e of group)
137
+ lines.push(`- ${e.key}: ${e.value}`);
138
+ }
139
+ if (omitted > 0) {
140
+ lines.push("", `(${omitted} older entries omitted for space)`);
141
+ }
142
+ return lines.join("\n");
143
+ };
144
+ let text = build();
145
+ for (const kind of EVICTION_ORDER) {
146
+ for (const e of all) {
147
+ if (text.length <= maxChars)
148
+ return text;
149
+ if (e.kind !== kind)
150
+ continue;
151
+ keep.delete(e);
152
+ omitted += 1;
153
+ text = build();
154
+ }
155
+ }
156
+ return text.length <= maxChars ? text : text.slice(0, Math.max(0, maxChars));
157
+ }
158
+ oldestOfKind(kind) {
159
+ for (const e of this.entries.values()) {
160
+ if (e.kind === kind)
161
+ return e.key;
162
+ }
163
+ return undefined;
164
+ }
165
+ }
166
+ export function reduceWorkingState(events) {
167
+ const store = new WorkingStateStore();
168
+ for (const event of events)
169
+ store.apply(event);
170
+ return store;
171
+ }
172
+ function isEvent(data) {
173
+ if (data === null || typeof data !== "object")
174
+ return false;
175
+ const d = data;
176
+ if (typeof d.key !== "string")
177
+ return false;
178
+ if (d.op === "delete")
179
+ return true;
180
+ return d.op === "set" && isKind(d.kind) && typeof d.value === "string";
181
+ }
182
+ export function workingStateEventsFromEntries(entries) {
183
+ const events = [];
184
+ for (const entry of entries) {
185
+ if (entry.type === "custom" &&
186
+ entry.customType === WORKING_STATE_CUSTOM_TYPE &&
187
+ isEvent(entry.data)) {
188
+ events.push(entry.data);
189
+ }
190
+ }
191
+ return events;
192
+ }
193
+ export function restoreWorkingState(entries) {
194
+ return reduceWorkingState(workingStateEventsFromEntries(entries));
195
+ }
196
+ export function getWorkingStateSections(entries) {
197
+ return restoreWorkingState(entries).list();
198
+ }
@@ -1,5 +1,6 @@
1
1
  import { access, mkdir, readdir, unlink, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { ensureCatalogScanRoot } from "./ensureCatalogScanRoot.js";
3
4
  import { invertPredecessorEdgesToRoute, invertRouteToPredecessorEdges, parsePipelineNeeds, toPipelineNeeds, } from "./pipelineNeeds.js";
4
5
  import { STAGE_ID_PATTERN } from "./createStage.js";
5
6
  import { inferIdFromUsesPath, normalizePipelineStageEntries, toWiringRefs, } from "./normalizePipelineStageEntry.js";
@@ -548,6 +549,7 @@ export async function createPipeline(projectRoot, input) {
548
549
  error: errorFinding?.message ?? "Pipeline validation failed",
549
550
  };
550
551
  }
552
+ await ensureCatalogScanRoot(projectRoot, path.relative(projectRoot, pipelineDirectory));
551
553
  const loadOutcome = await loadPipelineOutcome(relPath, { cwd: projectRoot });
552
554
  if (!loadOutcome.ok) {
553
555
  await unlink(filePath).catch(() => { });
@@ -6,6 +6,7 @@ import { STAGE_ID_PATTERN } from "./createStage.js";
6
6
  import { getCatalogScanPaths } from "./browseCatalog.js";
7
7
  import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
8
8
  import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
9
+ import { githubMatchActionError } from "./githubTriggerAction.js";
9
10
  import { loadTriggerOutcome } from "./loadTrigger.js";
10
11
  import { collectPipelineIdsFromPaths, collectTaskIdsFromPaths } from "./validateCatalog.js";
11
12
  import { findCatalogPathById } from "../runtime/triggerCatalog.js";
@@ -56,11 +57,16 @@ export function parseEventField(raw) {
56
57
  if (raw.config !== undefined && !isPlainObject(raw.config)) {
57
58
  return { ok: false, message: "event.config must be an object" };
58
59
  }
60
+ const match = isPlainObject(raw.match) ? raw.match : undefined;
61
+ const actionError = githubMatchActionError(raw.source, match);
62
+ if (actionError !== undefined) {
63
+ return { ok: false, message: actionError };
64
+ }
59
65
  return {
60
66
  ok: true,
61
67
  value: {
62
68
  source: raw.source,
63
- ...(isPlainObject(raw.match) ? { match: raw.match } : {}),
69
+ ...(match !== undefined ? { match } : {}),
64
70
  ...(isPlainObject(raw.config) ? { config: raw.config } : {}),
65
71
  },
66
72
  };
@@ -1,6 +1,8 @@
1
1
  import type { CatalogRootKind } from "./resolveCatalogRoots.js";
2
2
  /** Directory scanned for workshop-authored pipelines and tasks. */
3
3
  export declare const WORKSHOP_PACKAGE_ROOT = "workshop";
4
+ /** Directory the Guide always uses for pipeline packages and their stage files. */
5
+ export declare const GUIDE_PIPELINES_ROOT = "pipelines";
4
6
  export declare function normalizeCatalogDir(directory: string): string;
5
7
  /** Untitled workshop saves land here so Run can see them without a new catalog edit. */
6
8
  export declare function defaultWorkshopPackageDirectory(pipelineId: string | undefined, catalogRootKind?: CatalogRootKind): string;
@@ -3,6 +3,8 @@ import { isSeq, parseDocument } from "yaml";
3
3
  import { manifestPathForProject } from "./loadStageflowManifest.js";
4
4
  /** Directory scanned for workshop-authored pipelines and tasks. */
5
5
  export const WORKSHOP_PACKAGE_ROOT = "workshop";
6
+ /** Directory the Guide always uses for pipeline packages and their stage files. */
7
+ export const GUIDE_PIPELINES_ROOT = "pipelines";
6
8
  export function normalizeCatalogDir(directory) {
7
9
  return directory.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
8
10
  }
@@ -30,6 +32,10 @@ export function catalogScanRootForDirectory(directory) {
30
32
  normalized.startsWith(`${WORKSHOP_PACKAGE_ROOT}/`)) {
31
33
  return WORKSHOP_PACKAGE_ROOT;
32
34
  }
35
+ if (normalized === GUIDE_PIPELINES_ROOT ||
36
+ normalized.startsWith(`${GUIDE_PIPELINES_ROOT}/`)) {
37
+ return GUIDE_PIPELINES_ROOT;
38
+ }
33
39
  return normalized;
34
40
  }
35
41
  function isCovered(entries, root) {
@@ -1,5 +1,6 @@
1
1
  import { stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { isInsideProjectRoot } from "./catalogRelativePath.js";
3
4
  import { getModelCatalog } from "../models/catalog.js";
4
5
  import { loadDatasetFile } from "./loadDataset.js";
5
6
  import { loadEvalOutcome, resolveEvalPaths } from "./loadEval.js";
@@ -15,8 +16,35 @@ async function listFiles(manifest, kind) {
15
16
  export function listEvalFiles(manifest) {
16
17
  return listFiles(manifest, "eval");
17
18
  }
18
- export function listDatasetFiles(manifest) {
19
- return listFiles(manifest, "dataset");
19
+ export async function listDatasetFiles(manifest) {
20
+ const scanned = await listFiles(manifest, "dataset");
21
+ const referenced = await datasetsReferencedByEvals(manifest, scanned);
22
+ return [...scanned, ...referenced].sort((a, b) => (a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0));
23
+ }
24
+ async function datasetsReferencedByEvals(manifest, scanned) {
25
+ const seen = new Set(scanned.map((ref) => path.resolve(ref.absPath)));
26
+ const extra = [];
27
+ for (const ref of await listFiles(manifest, "eval")) {
28
+ const outcome = await loadEvalOutcome(ref.absPath, manifest.projectRoot);
29
+ if (!outcome.ok)
30
+ continue;
31
+ const abs = path.resolve(resolveEvalPaths(outcome.value.file, outcome.value.path).datasetPath);
32
+ if (seen.has(abs) || !isInsideProjectRoot(manifest.projectRoot, abs))
33
+ continue;
34
+ try {
35
+ if (!(await stat(abs)).isFile())
36
+ continue;
37
+ }
38
+ catch {
39
+ continue;
40
+ }
41
+ seen.add(abs);
42
+ extra.push({
43
+ absPath: abs,
44
+ relPath: path.relative(manifest.projectRoot, abs).split(path.sep).join("/"),
45
+ });
46
+ }
47
+ return extra;
20
48
  }
21
49
  export async function loadEvalCatalog(manifest, options = {}) {
22
50
  const refs = await listEvalFiles(manifest);
@@ -0,0 +1,8 @@
1
+ export declare const GITHUB_TRIGGER_ACTIONS: readonly ["opened", "synchronize", "updated", "merged", "closed"];
2
+ export type GithubTriggerAction = (typeof GITHUB_TRIGGER_ACTIONS)[number];
3
+ export declare function isGithubTriggerAction(value: unknown): value is GithubTriggerAction;
4
+ /**
5
+ * For `event.source` values starting with `github.`, `match.action` must be set
6
+ * to one of the five PR poll actions. Returns an error message or undefined.
7
+ */
8
+ export declare function githubMatchActionError(source: string, match: Record<string, unknown> | undefined): string | undefined;
@@ -0,0 +1,29 @@
1
+ export const GITHUB_TRIGGER_ACTIONS = [
2
+ "opened",
3
+ "synchronize",
4
+ "updated",
5
+ "merged",
6
+ "closed",
7
+ ];
8
+ const ACTION_SET = new Set(GITHUB_TRIGGER_ACTIONS);
9
+ export function isGithubTriggerAction(value) {
10
+ return typeof value === "string" && ACTION_SET.has(value);
11
+ }
12
+ /**
13
+ * For `event.source` values starting with `github.`, `match.action` must be set
14
+ * to one of the five PR poll actions. Returns an error message or undefined.
15
+ */
16
+ export function githubMatchActionError(source, match) {
17
+ if (!source.startsWith("github."))
18
+ return undefined;
19
+ const action = match?.action;
20
+ if (action === undefined) {
21
+ return ("event.match.action is required for github.* sources " +
22
+ `(one of: ${GITHUB_TRIGGER_ACTIONS.join(", ")})`);
23
+ }
24
+ if (!isGithubTriggerAction(action)) {
25
+ return (`event.match.action must be one of: ${GITHUB_TRIGGER_ACTIONS.join(", ")} ` +
26
+ `(got ${JSON.stringify(action)})`);
27
+ }
28
+ return undefined;
29
+ }
@@ -28,6 +28,7 @@ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
28
28
  "STAGEFLOW_READ_TOKEN_FILE",
29
29
  "STAGEFLOW_ALLOW_UNKNOWN_CONFIG",
30
30
  "STAGEFLOW_EVALS_KEEP_WORKDIR",
31
+ "STAGEFLOW_SHOW_INTERNAL_AGENTS",
31
32
  "STAGEFLOW_MAX_CONCURRENT_RUNS",
32
33
  "STAGEFLOW_MAX_CONCURRENT_RUNS_PER_PROJECT",
33
34
  "STAGEFLOW_MAX_QUEUED",
@@ -59,6 +60,7 @@ export const KNOWN_STAGEFLOW_ENV_KEYS = new Set([
59
60
  "STAGEFLOW_STAGE_EXECUTION",
60
61
  "STAGEFLOW_STAGE_CONTAINER_IMAGE",
61
62
  "STAGEFLOW_STAGE_CONTAINER_DOCKER_BIN",
63
+ "STAGEFLOW_STAGE_READONLY_DIR",
62
64
  "STAGEFLOW_STAGE_ENV_ALLOW",
63
65
  "STAGEFLOW_STAGE_ENV_PASSTHROUGH",
64
66
  "STAGEFLOW_GC_INTERVAL_MS",
@@ -74,6 +74,7 @@ const TOP_KEYS = [
74
74
  ];
75
75
  const TARGET_KEYS = ["kind", "pipeline", "stage", "file"];
76
76
  const AGENT_TARGET_KEYS = ["kind", "agent"];
77
+ const INTERNAL_AGENT_TARGET_KEYS = ["kind", "profile"];
77
78
  const CONVERSATION_KEYS = [
78
79
  "mode",
79
80
  "max_turns",
@@ -188,13 +189,15 @@ function parseTarget(c, raw) {
188
189
  return undefined;
189
190
  }
190
191
  if (raw.kind === undefined) {
191
- c.add("eval.invalid_field", `target "kind" is required ("stage" or "agent")`);
192
+ c.add("eval.invalid_field", `target "kind" is required ("stage", "agent" or "internal_agent")`);
192
193
  return undefined;
193
194
  }
194
195
  if (raw.kind === "agent")
195
196
  return parseAgentTarget(c, raw);
197
+ if (raw.kind === "internal_agent")
198
+ return parseInternalAgentTarget(c, raw);
196
199
  if (raw.kind !== "stage") {
197
- c.add("eval.target_kind_unsupported", `target kind ${JSON.stringify(raw.kind)} is not supported (only "stage" and "agent")`);
200
+ c.add("eval.target_kind_unsupported", `target kind ${JSON.stringify(raw.kind)} is not supported (only "stage", "agent" and "internal_agent")`);
198
201
  return undefined;
199
202
  }
200
203
  if (raw.agent !== undefined) {
@@ -251,6 +254,21 @@ function parseAgentTarget(c, raw) {
251
254
  }
252
255
  return { kind: "agent", agent };
253
256
  }
257
+ function parseInternalAgentTarget(c, raw) {
258
+ for (const key of ["agent", "pipeline", "stage", "file"]) {
259
+ if (raw[key] !== undefined) {
260
+ c.add("eval.target_invalid", `target "${key}" is not valid with kind: internal_agent; use "profile"`);
261
+ return undefined;
262
+ }
263
+ }
264
+ c.unknownKeys(raw, INTERNAL_AGENT_TARGET_KEYS, "target");
265
+ const profile = raw.profile;
266
+ if (typeof profile !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(profile)) {
267
+ c.add("eval.target_invalid", `target "profile" must be an internal agent id such as stageflow-guide`);
268
+ return undefined;
269
+ }
270
+ return { kind: "internal_agent", profile };
271
+ }
254
272
  function parseTask(c, raw) {
255
273
  if (raw === undefined)
256
274
  return undefined;
@@ -669,7 +687,7 @@ function parseGrader(c, raw, index, defaultJudge, kind) {
669
687
  c.add("eval.grader_invalid", `${label} "mode" must be "each_mentioned" or "equals"`);
670
688
  ok = false;
671
689
  }
672
- let pathValue = kind === "agent" ? "final_reply" : "payload";
690
+ let pathValue = kind === "agent" || kind === "internal_agent" ? "final_reply" : "payload";
673
691
  if (raw.path !== undefined) {
674
692
  if (typeof raw.path === "string" && FIELD_PATH_PATTERN.test(raw.path))
675
693
  pathValue = raw.path;
@@ -1,6 +1,8 @@
1
1
  import type { NeedTerminalState, PipelineNeedEdge, PipelineNeedItem, PipelineNeeds, PipelineRoute, PipelineRouteForwardEntry, ResolvedPipelineStageNode } from "../types/pipeline.js";
2
2
  export declare const NEED_TERMINAL_STATES: readonly NeedTerminalState[];
3
3
  export declare function isNeedTerminalState(value: unknown): value is NeedTerminalState;
4
+ /** Map known aliases; return undefined if not a terminal state or known alias. */
5
+ export declare function canonicalizeNeedTerminalState(value: unknown): NeedTerminalState | undefined;
4
6
  export declare function parsePipelineNeeds(raw: unknown, stageId: string): {
5
7
  ok: true;
6
8
  value: PipelineNeeds;
@@ -5,9 +5,21 @@ export const NEED_TERMINAL_STATES = [
5
5
  "skipped",
6
6
  ];
7
7
  const NEED_TERMINAL_STATE_SET = new Set(NEED_TERMINAL_STATES);
8
+ const NEED_TERMINAL_ALIASES = {
9
+ success: "succeeded",
10
+ failure: "failed",
11
+ };
8
12
  export function isNeedTerminalState(value) {
9
13
  return typeof value === "string" && NEED_TERMINAL_STATE_SET.has(value);
10
14
  }
15
+ /** Map known aliases; return undefined if not a terminal state or known alias. */
16
+ export function canonicalizeNeedTerminalState(value) {
17
+ if (typeof value !== "string")
18
+ return undefined;
19
+ if (isNeedTerminalState(value))
20
+ return value;
21
+ return NEED_TERMINAL_ALIASES[value];
22
+ }
11
23
  function isPlainObject(value) {
12
24
  return typeof value === "object" && value !== null && !Array.isArray(value);
13
25
  }
@@ -21,14 +33,15 @@ function parseNeedOn(raw, stageId, parentId) {
21
33
  const seen = new Set();
22
34
  const on = [];
23
35
  for (const item of raw) {
24
- if (typeof item !== "string" || !NEED_TERMINAL_STATE_SET.has(item) || seen.has(item)) {
36
+ const canonical = canonicalizeNeedTerminalState(item);
37
+ if (canonical === undefined || seen.has(canonical)) {
25
38
  return {
26
39
  ok: false,
27
40
  message: `stage "${stageId}": needs "${parentId}": on must be a non-empty unique subset of succeeded, failed, skipped`,
28
41
  };
29
42
  }
30
- seen.add(item);
31
- on.push(item);
43
+ seen.add(canonical);
44
+ on.push(canonical);
32
45
  }
33
46
  return { ok: true, value: on };
34
47
  }
@@ -1,4 +1,4 @@
1
- import { isNeedTerminalState } from "./pipelineNeeds.js";
1
+ import { canonicalizeNeedTerminalState } from "./pipelineNeeds.js";
2
2
  import { parseRouteIf } from "./routeIf.js";
3
3
  function isPlainObject(value) {
4
4
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -81,14 +81,15 @@ function parseRouteOn(raw, stageId, targetId) {
81
81
  const seen = new Set();
82
82
  const on = [];
83
83
  for (const item of items) {
84
- if (typeof item !== "string" || !isNeedTerminalState(item) || seen.has(item)) {
84
+ const canonical = canonicalizeNeedTerminalState(item);
85
+ if (canonical === undefined || seen.has(canonical)) {
85
86
  return {
86
87
  ok: false,
87
88
  message: `stage "${stageId}": route "${targetId}": on must be a non-empty unique subset of succeeded, failed, skipped`,
88
89
  };
89
90
  }
90
- seen.add(item);
91
- on.push(item);
91
+ seen.add(canonical);
92
+ on.push(canonical);
92
93
  }
93
94
  return { ok: true, value: on };
94
95
  }
@@ -4,7 +4,7 @@ import type { LoadIssue } from "./loadOutcome.js";
4
4
  import type { AnyChannelProvider } from "../channels/types.js";
5
5
  export type ValidationSeverity = "error" | "warning";
6
6
  export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task" | "trigger" | "eval" | "dataset";
7
- export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.stage_wrapper_skill_rejected" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "pipeline.invalid_channels" | "pipeline.invalid_memories" | "pipeline.memory_unavailable" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_channels" | "stage.channel_unavailable" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
7
+ export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.stage_wrapper_skill_rejected" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "pipeline.invalid_channels" | "pipeline.invalid_memories" | "pipeline.memory_unavailable" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_channels" | "stage.channel_unavailable" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "trigger.github_action_required" | "trigger.invalid_github_action" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
8
8
  export type ValidationFinding = {
9
9
  severity: ValidationSeverity;
10
10
  code: ValidationFindingCode | string;
@@ -6,6 +6,7 @@ import { INLINE_PIPELINE_PATH, loadPipelineFromObjectOutcome, loadPipelineOutcom
6
6
  import { loadStageOutcome } from "./loadStage.js";
7
7
  import { loadTaskOutcome } from "./loadTask.js";
8
8
  import { loadTriggerOutcome } from "./loadTrigger.js";
9
+ import { githubMatchActionError, isGithubTriggerAction } from "./githubTriggerAction.js";
9
10
  import { loadEvalCatalog } from "./evalCatalog.js";
10
11
  import { evalFindingsFromIssues } from "./validateEval.js";
11
12
  import { readYamlObject } from "./readYamlObject.js";
@@ -666,6 +667,17 @@ export function findingsForTriggerRefs(cwd, absPath, trigger, refs) {
666
667
  if (trigger.task !== undefined && !refs.taskIds.has(trigger.task)) {
667
668
  findings.push(findingTriggerError(cwd, absPath, `Trigger "${trigger.id}" references unknown task "${trigger.task}"`, "trigger.unknown_task"));
668
669
  }
670
+ const source = trigger.event?.source;
671
+ if (typeof source === "string" && source.startsWith("github.")) {
672
+ const action = trigger.event?.match?.action;
673
+ if (action === undefined) {
674
+ findings.push(findingTriggerError(cwd, absPath, `Trigger "${trigger.id}" requires event.match.action for github.* sources`, "trigger.github_action_required"));
675
+ }
676
+ else if (!isGithubTriggerAction(action)) {
677
+ findings.push(findingTriggerError(cwd, absPath, githubMatchActionError(source, trigger.event?.match) ??
678
+ `Trigger "${trigger.id}" has an invalid event.match.action`, "trigger.invalid_github_action"));
679
+ }
680
+ }
669
681
  return findings;
670
682
  }
671
683
  async function validateSingleTask(invocationCwd, taskArg) {