stageflow 0.42.0 → 0.46.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 (315) 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/evalGate.js +1 -0
  27. package/dist/agents/hostContext.d.ts +1 -1
  28. package/dist/agents/runtime/piRuntime.d.ts +5 -1
  29. package/dist/agents/runtime/piRuntime.js +46 -12
  30. package/dist/agents/service.js +3 -2
  31. package/dist/agents/sessions.js +3 -1
  32. package/dist/cli/evalCommand.d.ts +1 -1
  33. package/dist/cli/evalCommand.js +39 -2
  34. package/dist/cli/hostClient.d.ts +6 -0
  35. package/dist/cli/hostClient.js +4 -0
  36. package/dist/compaction/directives.d.ts +18 -0
  37. package/dist/compaction/directives.js +119 -0
  38. package/dist/compaction/extension.d.ts +20 -0
  39. package/dist/compaction/extension.js +78 -0
  40. package/dist/compaction/mask.d.ts +18 -0
  41. package/dist/compaction/mask.js +155 -0
  42. package/dist/compaction/pin.d.ts +21 -0
  43. package/dist/compaction/pin.js +270 -0
  44. package/dist/compaction/scrub.d.ts +7 -0
  45. package/dist/compaction/scrub.js +26 -0
  46. package/dist/compaction/sessionWiring.d.ts +22 -0
  47. package/dist/compaction/sessionWiring.js +26 -0
  48. package/dist/compaction/summarize.d.ts +79 -0
  49. package/dist/compaction/summarize.js +171 -0
  50. package/dist/compaction/toolPolicy.d.ts +3 -0
  51. package/dist/compaction/toolPolicy.js +33 -0
  52. package/dist/compaction/types.d.ts +45 -0
  53. package/dist/compaction/types.js +7 -0
  54. package/dist/compaction/workingState.d.ts +56 -0
  55. package/dist/compaction/workingState.js +198 -0
  56. package/dist/config/createPipeline.js +2 -0
  57. package/dist/config/createTrigger.js +7 -1
  58. package/dist/config/ensureCatalogScanRoot.d.ts +2 -0
  59. package/dist/config/ensureCatalogScanRoot.js +6 -0
  60. package/dist/config/evalCatalog.js +30 -2
  61. package/dist/config/githubTriggerAction.d.ts +8 -0
  62. package/dist/config/githubTriggerAction.js +29 -0
  63. package/dist/config/hostConfig.js +2 -0
  64. package/dist/config/loadEval.d.ts +3 -1
  65. package/dist/config/loadEval.js +96 -10
  66. package/dist/config/pipelineNeeds.d.ts +2 -0
  67. package/dist/config/pipelineNeeds.js +16 -3
  68. package/dist/config/pipelineRoute.js +5 -4
  69. package/dist/config/validateCatalog.d.ts +1 -1
  70. package/dist/config/validateCatalog.js +12 -0
  71. package/dist/config/validateEval.js +60 -3
  72. package/dist/config/writeEval.js +2 -2
  73. package/dist/envelope/assertEmittedArtifacts.d.ts +12 -0
  74. package/dist/envelope/assertEmittedArtifacts.js +43 -0
  75. package/dist/envelope/preEmitChecks.d.ts +3 -0
  76. package/dist/evals/calibration.d.ts +21 -0
  77. package/dist/evals/calibration.js +69 -0
  78. package/dist/evals/calibrationSuggest.d.ts +29 -0
  79. package/dist/evals/calibrationSuggest.js +72 -0
  80. package/dist/evals/calibrator.d.ts +46 -0
  81. package/dist/evals/calibrator.js +148 -0
  82. package/dist/evals/compare.d.ts +1 -1
  83. package/dist/evals/compare.js +3 -0
  84. package/dist/evals/constants.d.ts +7 -3
  85. package/dist/evals/constants.js +7 -1
  86. package/dist/evals/engine/FakeEngine.d.ts +2 -1
  87. package/dist/evals/engine/FakeEngine.js +9 -3
  88. package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +1 -1
  89. package/dist/evals/engine/promptfoo/PromptfooEngine.js +4 -2
  90. package/dist/evals/engine/promptfoo/generate.d.ts +3 -1
  91. package/dist/evals/engine/promptfoo/generate.js +43 -23
  92. package/dist/evals/engine/promptfoo/judgeProviders.js +2 -1
  93. package/dist/evals/engine/promptfoo/normalize.d.ts +4 -1
  94. package/dist/evals/engine/promptfoo/normalize.js +16 -3
  95. package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +3 -1
  96. package/dist/evals/engine/promptfoo/rubricPrompt.js +15 -5
  97. package/dist/evals/errors.d.ts +7 -0
  98. package/dist/evals/errors.js +10 -0
  99. package/dist/evals/judgeProviders.d.ts +6 -0
  100. package/dist/evals/judgeProviders.js +14 -0
  101. package/dist/evals/judged.d.ts +7 -0
  102. package/dist/evals/judged.js +7 -0
  103. package/dist/evals/manager.d.ts +27 -8
  104. package/dist/evals/manager.js +73 -12
  105. package/dist/evals/plan.d.ts +10 -1
  106. package/dist/evals/plan.js +33 -13
  107. package/dist/evals/planInternalAgent.d.ts +19 -0
  108. package/dist/evals/planInternalAgent.js +201 -0
  109. package/dist/evals/read/api.d.ts +18 -0
  110. package/dist/evals/read/api.js +10 -1
  111. package/dist/evals/read/labelQueue.d.ts +45 -0
  112. package/dist/evals/read/labelQueue.js +83 -0
  113. package/dist/evals/read/list.js +10 -1
  114. package/dist/evals/read/runs.d.ts +5 -0
  115. package/dist/evals/read/runs.js +7 -1
  116. package/dist/evals/runner/caseRunner.d.ts +2 -1
  117. package/dist/evals/runner/caseRunner.js +16 -5
  118. package/dist/evals/runner/internalAgentCaseRunner.d.ts +6 -0
  119. package/dist/evals/runner/internalAgentCaseRunner.js +250 -0
  120. package/dist/evals/snapshot.d.ts +10 -2
  121. package/dist/evals/snapshot.js +8 -3
  122. package/dist/evals/stats.d.ts +40 -0
  123. package/dist/evals/stats.js +69 -0
  124. package/dist/evals/storeTypes.d.ts +42 -0
  125. package/dist/evals/types.d.ts +25 -2
  126. package/dist/evals/types.js +4 -1
  127. package/dist/evals/votes.d.ts +10 -0
  128. package/dist/evals/votes.js +60 -0
  129. package/dist/guide/cardContract.d.ts +133 -0
  130. package/dist/guide/cardContract.js +156 -0
  131. package/dist/guide/chat.d.ts +45 -0
  132. package/dist/guide/chat.js +378 -0
  133. package/dist/guide/knowledge/index.d.ts +14 -0
  134. package/dist/guide/knowledge/index.js +109 -0
  135. package/dist/guide/knowledge/topics/agents.d.ts +2 -0
  136. package/dist/guide/knowledge/topics/agents.js +58 -0
  137. package/dist/guide/knowledge/topics/bestPractices.d.ts +2 -0
  138. package/dist/guide/knowledge/topics/bestPractices.js +67 -0
  139. package/dist/guide/knowledge/topics/connections.d.ts +2 -0
  140. package/dist/guide/knowledge/topics/connections.js +66 -0
  141. package/dist/guide/knowledge/topics/consoleMap.d.ts +2 -0
  142. package/dist/guide/knowledge/topics/consoleMap.js +55 -0
  143. package/dist/guide/knowledge/topics/evals.d.ts +2 -0
  144. package/dist/guide/knowledge/topics/evals.js +72 -0
  145. package/dist/guide/knowledge/topics/limitations.d.ts +2 -0
  146. package/dist/guide/knowledge/topics/limitations.js +84 -0
  147. package/dist/guide/knowledge/topics/mcpSkillsBrowser.d.ts +2 -0
  148. package/dist/guide/knowledge/topics/mcpSkillsBrowser.js +57 -0
  149. package/dist/guide/knowledge/topics/memories.d.ts +2 -0
  150. package/dist/guide/knowledge/topics/memories.js +63 -0
  151. package/dist/guide/knowledge/topics/modelsAndProviders.d.ts +2 -0
  152. package/dist/guide/knowledge/topics/modelsAndProviders.js +56 -0
  153. package/dist/guide/knowledge/topics/overview.d.ts +2 -0
  154. package/dist/guide/knowledge/topics/overview.js +56 -0
  155. package/dist/guide/knowledge/topics/pipelines.d.ts +2 -0
  156. package/dist/guide/knowledge/topics/pipelines.js +93 -0
  157. package/dist/guide/knowledge/topics/stages.d.ts +2 -0
  158. package/dist/guide/knowledge/topics/stages.js +54 -0
  159. package/dist/guide/knowledge/topics/tasks.d.ts +2 -0
  160. package/dist/guide/knowledge/topics/tasks.js +52 -0
  161. package/dist/guide/knowledge/topics/triggers.d.ts +2 -0
  162. package/dist/guide/knowledge/topics/triggers.js +68 -0
  163. package/dist/guide/knowledge/topics/troubleshooting.d.ts +2 -0
  164. package/dist/guide/knowledge/topics/troubleshooting.js +85 -0
  165. package/dist/guide/knowledgeTools.d.ts +2 -0
  166. package/dist/guide/knowledgeTools.js +56 -0
  167. package/dist/guide/showUi.d.ts +6 -0
  168. package/dist/guide/showUi.js +546 -0
  169. package/dist/guide/threadStore.d.ts +48 -0
  170. package/dist/guide/threadStore.js +189 -0
  171. package/dist/guide/writeRoot.d.ts +11 -0
  172. package/dist/guide/writeRoot.js +70 -0
  173. package/dist/hosted/translate.js +2 -0
  174. package/dist/mcp/agentTools.js +12 -0
  175. package/dist/mcp/catalogTools.js +18 -20
  176. package/dist/mcp/catalogWriteTools.d.ts +8 -0
  177. package/dist/mcp/catalogWriteTools.js +225 -0
  178. package/dist/mcp/connectionTools.d.ts +3 -0
  179. package/dist/mcp/connectionTools.js +85 -0
  180. package/dist/mcp/controlTools.js +34 -1
  181. package/dist/mcp/deps.d.ts +9 -0
  182. package/dist/mcp/evalTools.js +723 -21
  183. package/dist/mcp/memoryHostTools.d.ts +3 -0
  184. package/dist/mcp/memoryHostTools.js +72 -0
  185. package/dist/mcp/pipelineAuthorTools.js +14 -1
  186. package/dist/mcp/server.js +5 -0
  187. package/dist/mcp/tools.js +6 -0
  188. package/dist/mcp/triggerTools.js +77 -31
  189. package/dist/operatorAgent/evalGate.d.ts +22 -0
  190. package/dist/operatorAgent/evalGate.js +77 -0
  191. package/dist/operatorAgent/fakeHost.js +10 -2
  192. package/dist/operatorAgent/host.js +18 -2
  193. package/dist/operatorAgent/index.d.ts +1 -0
  194. package/dist/operatorAgent/index.js +1 -0
  195. package/dist/operatorAgent/internalAgents.d.ts +11 -0
  196. package/dist/operatorAgent/internalAgents.js +13 -0
  197. package/dist/operatorAgent/piModel.d.ts +5 -1
  198. package/dist/operatorAgent/piModel.js +15 -10
  199. package/dist/operatorAgent/profiles/agentAuthor.js +66 -22
  200. package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
  201. package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +1 -1
  202. package/dist/operatorAgent/profiles/guideConfirm.d.ts +21 -0
  203. package/dist/operatorAgent/profiles/guideConfirm.js +100 -0
  204. package/dist/operatorAgent/profiles/guideTools.d.ts +7 -0
  205. package/dist/operatorAgent/profiles/guideTools.js +123 -0
  206. package/dist/operatorAgent/profiles/stageflowGuide.d.ts +7 -0
  207. package/dist/operatorAgent/profiles/stageflowGuide.js +133 -0
  208. package/dist/operatorAgent/profiles/workshopAuthor.js +23 -0
  209. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
  210. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +9 -1
  211. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +2 -0
  212. package/dist/operatorAgent/profiles/workshopAuthorTools.js +197 -9
  213. package/dist/operatorAgent/types.d.ts +23 -0
  214. package/dist/package-meta.d.ts +1 -1
  215. package/dist/package-meta.js +1 -1
  216. package/dist/runstore/port.d.ts +13 -1
  217. package/dist/runstore/retention.d.ts +2 -1
  218. package/dist/runstore/retention.js +3 -7
  219. package/dist/runstore/runStatusClock.d.ts +17 -0
  220. package/dist/runstore/runStatusClock.js +19 -0
  221. package/dist/runstore/sqlite/SqliteRunStore.d.ts +11 -1
  222. package/dist/runstore/sqlite/SqliteRunStore.js +70 -45
  223. package/dist/runstore/sqlite/calibrationStore.d.ts +10 -0
  224. package/dist/runstore/sqlite/calibrationStore.js +106 -0
  225. package/dist/runstore/sqlite/evalStore.d.ts +5 -0
  226. package/dist/runstore/sqlite/evalStore.js +7 -0
  227. package/dist/runstore/sqlite/migrations/018-eval-grader-labels.d.ts +11 -0
  228. package/dist/runstore/sqlite/migrations/018-eval-grader-labels.js +43 -0
  229. package/dist/runstore/sqlite/migrations/index.js +2 -0
  230. package/dist/runstore/workspaceLayout.js +6 -1
  231. package/dist/runtime/artifactDiskInspect.d.ts +14 -0
  232. package/dist/runtime/artifactDiskInspect.js +52 -0
  233. package/dist/runtime/artifactPath.d.ts +24 -0
  234. package/dist/runtime/artifactPath.js +84 -0
  235. package/dist/runtime/completionCheckRunner.js +15 -43
  236. package/dist/runtime/githubPollSource.d.ts +22 -4
  237. package/dist/runtime/githubPollSource.js +93 -8
  238. package/dist/runtime/priorArtifactGrants.d.ts +8 -0
  239. package/dist/runtime/priorArtifactGrants.js +105 -0
  240. package/dist/runtime/runActionEligibility.d.ts +46 -0
  241. package/dist/runtime/runActionEligibility.js +123 -0
  242. package/dist/runtime/runManager.d.ts +4 -1
  243. package/dist/runtime/runManager.js +52 -10
  244. package/dist/runtime/runRetryCoordinator.d.ts +2 -14
  245. package/dist/runtime/runRetryCoordinator.js +3 -67
  246. package/dist/runtime/sandboxContainer.d.ts +7 -0
  247. package/dist/runtime/sandboxContainer.js +48 -6
  248. package/dist/runtime/sandboxDesk.d.ts +2 -1
  249. package/dist/runtime/sandboxDesk.js +11 -12
  250. package/dist/runtime/stageEnvironment.js +1 -0
  251. package/dist/runtime/stageRoots.d.ts +12 -0
  252. package/dist/runtime/stageSandbox.d.ts +2 -1
  253. package/dist/runtime/stageSandbox.js +18 -2
  254. package/dist/runtime/stageWorker.js +20 -0
  255. package/dist/server/agentAuthorRoutes.js +12 -3
  256. package/dist/server/bootstrap.d.ts +5 -0
  257. package/dist/server/bootstrap.js +14 -1
  258. package/dist/server/evalRoutes.js +251 -12
  259. package/dist/server/guideRoutes.d.ts +5 -0
  260. package/dist/server/guideRoutes.js +166 -0
  261. package/dist/server/http.d.ts +10 -0
  262. package/dist/server/http.js +35 -1
  263. package/dist/server/internalAgentRoutes.d.ts +6 -0
  264. package/dist/server/internalAgentRoutes.js +12 -0
  265. package/dist/server/mcpHost.js +2 -0
  266. package/dist/toolkit/meta.d.ts +1 -1
  267. package/dist/toolkit/meta.js +41 -1
  268. package/dist/toolkit/profiles.d.ts +5 -0
  269. package/dist/toolkit/profiles.js +12 -0
  270. package/dist/tools/askOperator.js +2 -1
  271. package/dist/tools/emitStageEnvelope.d.ts +17 -16
  272. package/dist/tools/emitStageEnvelope.js +46 -32
  273. package/dist/tools/stageToolNames.d.ts +4 -0
  274. package/dist/tools/stageToolNames.js +4 -0
  275. package/dist/tools/workingState.d.ts +40 -0
  276. package/dist/tools/workingState.js +108 -0
  277. package/dist/tools/writeStageArtifact.d.ts +0 -9
  278. package/dist/tools/writeStageArtifact.js +2 -1
  279. package/dist/ui/assets/{MarkdownText-BTrOqu5J.js → MarkdownText-BS2BBbCx.js} +2 -2
  280. package/dist/ui/assets/adapters-DPD-cAsy.js +17 -0
  281. package/dist/ui/assets/chat-BD1-VTT6.js +2 -0
  282. package/dist/ui/assets/chat-DXnQv9st.css +2 -0
  283. package/dist/ui/assets/main-AX5XNLtX.js +278 -0
  284. package/dist/ui/assets/{main-CHlmTNJe.css → main-CFX0_n8J.css} +1 -1
  285. package/dist/ui/chat.html +3 -4
  286. package/dist/ui/index.html +3 -4
  287. package/dist/workshop/chatTurn.js +1 -1
  288. package/dist/workshop/workshopPin.d.ts +3 -3
  289. package/dist/workshop/workshopPin.js +16 -15
  290. package/examples/compaction-stress/README.md +30 -0
  291. package/examples/compaction-stress/check-run.mjs +53 -0
  292. package/examples/compaction-stress/collect-evidence.yaml +60 -0
  293. package/examples/compaction-stress/compaction-stress.pipeline.yaml +141 -0
  294. package/examples/compaction-stress/compaction-stress.task.yaml +15 -0
  295. package/examples/compaction-stress/generate-evidence.mjs +110 -0
  296. package/examples/compaction-stress/root-cause.yaml +26 -0
  297. package/examples/compaction-stress/stageflow.yaml +9 -0
  298. package/examples/compaction-stress/write-postmortem.yaml +40 -0
  299. package/examples/evals/README.md +18 -0
  300. package/examples/evals/guide-behavior.dataset.jsonl +15 -0
  301. package/examples/evals/guide-behavior.eval.yaml +35 -0
  302. package/examples/evals/guide-depth.dataset.jsonl +35 -0
  303. package/examples/evals/guide-depth.eval.yaml +169 -0
  304. package/examples/memory/evals/guide-behavior.dataset.jsonl +15 -0
  305. package/examples/memory/evals/guide-behavior.eval.yaml +35 -0
  306. package/examples/memory/evals/guide-depth.dataset.jsonl +35 -0
  307. package/examples/memory/evals/guide-depth.eval.yaml +169 -0
  308. package/package.json +1 -1
  309. package/skills/stageflow/references/control-surface.md +1 -1
  310. package/skills/stageflow/references/evals.md +1 -0
  311. package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +0 -9
  312. package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +0 -6
  313. package/dist/ui/assets/chat-Df_wCpak.js +0 -5
  314. package/dist/ui/assets/chat-P7KQznHm.css +0 -2
  315. package/dist/ui/assets/main-CvVfxmMd.js +0 -271
@@ -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",
@@ -1,4 +1,4 @@
1
- import type { EvalConversation, EvalFile, ResolvedConversation, ResolvedGrader } from "../evals/types.js";
1
+ import type { EvalConversation, JudgeSettings, EvalFile, ResolvedConversation, ResolvedGrader } from "../evals/types.js";
2
2
  import { type LoadOutcome } from "./loadOutcome.js";
3
3
  type UnhashedGrader = ResolvedGrader extends infer G ? (G extends unknown ? Omit<G, "hash"> : never) : never;
4
4
  export type LoadedEval = {
@@ -25,6 +25,8 @@ export type EvalPaths = {
25
25
  };
26
26
  /** Paths in an eval file resolve against the eval file's directory (P6). */
27
27
  export declare function resolveEvalPaths(file: EvalFile, evalAbsPath: string): EvalPaths;
28
+ /** Grader settings over the eval defaults, key by key; undefined when neither sets anything. */
29
+ export declare function mergeJudgeSettings(base: JudgeSettings | undefined, over: JudgeSettings | undefined): JudgeSettings | undefined;
28
30
  export declare function graderHash(grader: UnhashedGrader): string;
29
31
  export declare function graderHashShort(grader: Pick<ResolvedGrader, "hash">): string;
30
32
  export declare function parseEvalOutcome(yamlText: string, filePath: string, projectRoot: string): LoadOutcome<LoadedEval>;
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { parse as parseYaml } from "yaml";
4
4
  import { AGENT_ID_PATTERN, isReservedAgentId } from "../agents/agentId.js";
5
5
  import { canonicalJson, hash6, sha256Hex } from "../evals/canonical.js";
6
+ import { EVAL_LIMITS } from "../evals/constants.js";
6
7
  import { loadFailure, loadSuccess } from "./loadOutcome.js";
7
8
  import { yamlParsePosition } from "./yamlParsePosition.js";
8
9
  export const EVAL_DEFAULT_REPEATS = 3;
@@ -74,6 +75,7 @@ const TOP_KEYS = [
74
75
  ];
75
76
  const TARGET_KEYS = ["kind", "pipeline", "stage", "file"];
76
77
  const AGENT_TARGET_KEYS = ["kind", "agent"];
78
+ const INTERNAL_AGENT_TARGET_KEYS = ["kind", "profile"];
77
79
  const CONVERSATION_KEYS = [
78
80
  "mode",
79
81
  "max_turns",
@@ -88,6 +90,7 @@ const TASK_KEYS = ["goal", "context", "constraints"];
88
90
  const DEFAULTS_KEYS = [
89
91
  "repeats",
90
92
  "judge",
93
+ "judge_settings",
91
94
  "pass_threshold",
92
95
  "max_cost_usd",
93
96
  "concurrency",
@@ -100,11 +103,11 @@ const GRADER_TYPE_KEYS = {
100
103
  schema: [],
101
104
  field: ["checks"],
102
105
  reference: ["mode", "path", "expected", "pass_at"],
103
- rubric: ["rubric", "scale", "pass_at", "judge", "examples"],
106
+ rubric: ["rubric", "scale", "pass_at", "judge", "judge_settings", "examples"],
104
107
  code: ["code"],
105
108
  tool_calls: ["must_call", "must_not_call", "must_ask_first", "max_calls", "args"],
106
109
  conversation_check: ["max_turns", "max_agent_questions_per_turn", "final_reply"],
107
- goal_reached: ["judge", "guidance"],
110
+ goal_reached: ["judge", "judge_settings", "guidance"],
108
111
  };
109
112
  const TOOL_ARG_KEYS = ["tool", "path", "op", "value"];
110
113
  const FINAL_REPLY_KEYS = ["op", "value"];
@@ -188,13 +191,15 @@ function parseTarget(c, raw) {
188
191
  return undefined;
189
192
  }
190
193
  if (raw.kind === undefined) {
191
- c.add("eval.invalid_field", `target "kind" is required ("stage" or "agent")`);
194
+ c.add("eval.invalid_field", `target "kind" is required ("stage", "agent" or "internal_agent")`);
192
195
  return undefined;
193
196
  }
194
197
  if (raw.kind === "agent")
195
198
  return parseAgentTarget(c, raw);
199
+ if (raw.kind === "internal_agent")
200
+ return parseInternalAgentTarget(c, raw);
196
201
  if (raw.kind !== "stage") {
197
- c.add("eval.target_kind_unsupported", `target kind ${JSON.stringify(raw.kind)} is not supported (only "stage" and "agent")`);
202
+ c.add("eval.target_kind_unsupported", `target kind ${JSON.stringify(raw.kind)} is not supported (only "stage", "agent" and "internal_agent")`);
198
203
  return undefined;
199
204
  }
200
205
  if (raw.agent !== undefined) {
@@ -251,6 +256,21 @@ function parseAgentTarget(c, raw) {
251
256
  }
252
257
  return { kind: "agent", agent };
253
258
  }
259
+ function parseInternalAgentTarget(c, raw) {
260
+ for (const key of ["agent", "pipeline", "stage", "file"]) {
261
+ if (raw[key] !== undefined) {
262
+ c.add("eval.target_invalid", `target "${key}" is not valid with kind: internal_agent; use "profile"`);
263
+ return undefined;
264
+ }
265
+ }
266
+ c.unknownKeys(raw, INTERNAL_AGENT_TARGET_KEYS, "target");
267
+ const profile = raw.profile;
268
+ if (typeof profile !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(profile)) {
269
+ c.add("eval.target_invalid", `target "profile" must be an internal agent id such as stageflow-guide`);
270
+ return undefined;
271
+ }
272
+ return { kind: "internal_agent", profile };
273
+ }
254
274
  function parseTask(c, raw) {
255
275
  if (raw === undefined)
256
276
  return undefined;
@@ -267,6 +287,51 @@ function parseTask(c, raw) {
267
287
  }
268
288
  return out;
269
289
  }
290
+ const JUDGE_SETTINGS_KEYS = ["temperature", "reasoning", "votes"];
291
+ /** A mapping of judge call settings, or undefined when absent or invalid (findings added). */
292
+ function parseJudgeSettings(c, raw, label) {
293
+ if (raw === undefined)
294
+ return undefined;
295
+ if (!isPlainObject(raw)) {
296
+ c.add("eval.invalid_field", `${label} "judge_settings" must be a mapping`);
297
+ return undefined;
298
+ }
299
+ c.unknownKeys(raw, JUDGE_SETTINGS_KEYS, `${label} judge_settings`);
300
+ const out = {};
301
+ let ok = true;
302
+ if (raw.temperature !== undefined) {
303
+ if (typeof raw.temperature === "number" && raw.temperature >= 0 && raw.temperature <= 1) {
304
+ out.temperature = raw.temperature;
305
+ }
306
+ else {
307
+ c.add("eval.invalid_field", `${label} judge_settings "temperature" must be a number from 0 to 1`);
308
+ ok = false;
309
+ }
310
+ }
311
+ if (raw.reasoning !== undefined) {
312
+ if (typeof raw.reasoning === "boolean")
313
+ out.reasoning = raw.reasoning;
314
+ else {
315
+ c.add("eval.invalid_field", `${label} judge_settings "reasoning" must be true or false`);
316
+ ok = false;
317
+ }
318
+ }
319
+ if (raw.votes !== undefined) {
320
+ if (typeof raw.votes === "number" && Number.isInteger(raw.votes) && raw.votes >= 1 && raw.votes <= 5 && raw.votes % 2 === 1) {
321
+ out.votes = raw.votes;
322
+ }
323
+ else {
324
+ c.add("eval.invalid_field", `${label} judge_settings "votes" must be 1, 3 or 5`);
325
+ ok = false;
326
+ }
327
+ }
328
+ return ok ? out : undefined;
329
+ }
330
+ /** Grader settings over the eval defaults, key by key; undefined when neither sets anything. */
331
+ export function mergeJudgeSettings(base, over) {
332
+ const merged = { ...(base ?? {}), ...(over ?? {}) };
333
+ return Object.keys(merged).length === 0 ? undefined : merged;
334
+ }
270
335
  function parseDefaults(c, raw, kind) {
271
336
  if (raw === undefined)
272
337
  return undefined;
@@ -282,6 +347,9 @@ function parseDefaults(c, raw, kind) {
282
347
  const judge = optionalString(c, raw, "judge", "defaults", { min: 1 });
283
348
  if (judge !== undefined)
284
349
  out.judge = judge;
350
+ const judgeSettings = parseJudgeSettings(c, raw.judge_settings, "defaults");
351
+ if (judgeSettings !== undefined)
352
+ out.judge_settings = judgeSettings;
285
353
  const passThreshold = optionalNumber(c, raw, "pass_threshold", "defaults", "a number greater than 0 and at most 1", (n) => n > 0 && n <= 1);
286
354
  if (passThreshold !== undefined)
287
355
  out.pass_threshold = passThreshold;
@@ -465,8 +533,8 @@ function parseChecks(c, raw, where) {
465
533
  return ok ? checks : undefined;
466
534
  }
467
535
  function parseExamples(c, raw, scale, where) {
468
- if (!Array.isArray(raw) || raw.length > 6) {
469
- c.add("eval.grader_invalid", `${where} "examples" must be an array of at most 6 items`);
536
+ if (!Array.isArray(raw) || raw.length > EVAL_LIMITS.rubricExamplesMax) {
537
+ c.add("eval.grader_invalid", `${where} "examples" must be an array of at most ${EVAL_LIMITS.rubricExamplesMax} items`);
470
538
  return undefined;
471
539
  }
472
540
  const out = [];
@@ -592,7 +660,7 @@ function parseFinalReply(c, raw, where) {
592
660
  });
593
661
  return ok ? out : undefined;
594
662
  }
595
- function parseGrader(c, raw, index, defaultJudge, kind) {
663
+ function parseGrader(c, raw, index, defaultJudge, kind, defaultJudgeSettings) {
596
664
  if (!isPlainObject(raw)) {
597
665
  c.add("eval.grader_invalid", `graders[${index}] must be a mapping`);
598
666
  return undefined;
@@ -669,7 +737,7 @@ function parseGrader(c, raw, index, defaultJudge, kind) {
669
737
  c.add("eval.grader_invalid", `${label} "mode" must be "each_mentioned" or "equals"`);
670
738
  ok = false;
671
739
  }
672
- let pathValue = kind === "agent" ? "final_reply" : "payload";
740
+ let pathValue = kind === "agent" || kind === "internal_agent" ? "final_reply" : "payload";
673
741
  if (raw.path !== undefined) {
674
742
  if (typeof raw.path === "string" && FIELD_PATH_PATTERN.test(raw.path))
675
743
  pathValue = raw.path;
@@ -751,6 +819,10 @@ function parseGrader(c, raw, index, defaultJudge, kind) {
751
819
  if (examples === undefined)
752
820
  ok = false;
753
821
  }
822
+ const ownSettings = parseJudgeSettings(c, raw.judge_settings, label);
823
+ if (raw.judge_settings !== undefined && ownSettings === undefined)
824
+ ok = false;
825
+ const judgeSettings = mergeJudgeSettings(defaultJudgeSettings, ownSettings);
754
826
  if (ok) {
755
827
  result = {
756
828
  config: {
@@ -760,6 +832,7 @@ function parseGrader(c, raw, index, defaultJudge, kind) {
760
832
  ...(raw.scale !== undefined ? { scale } : {}),
761
833
  ...(passAt !== undefined ? { pass_at: passAt } : {}),
762
834
  ...(judge !== undefined ? { judge } : {}),
835
+ ...(ownSettings !== undefined ? { judge_settings: ownSettings } : {}),
763
836
  ...(examples !== undefined ? { examples } : {}),
764
837
  },
765
838
  resolved: {
@@ -769,6 +842,7 @@ function parseGrader(c, raw, index, defaultJudge, kind) {
769
842
  scale,
770
843
  passAt: scale === "1_to_5" ? (passAt ?? 4) : 1,
771
844
  judge: judge ?? defaultJudge ?? "",
845
+ ...(judgeSettings !== undefined ? { judgeSettings } : {}),
772
846
  examples: examples ?? [],
773
847
  },
774
848
  };
@@ -864,18 +938,24 @@ function parseGrader(c, raw, index, defaultJudge, kind) {
864
938
  ok = false;
865
939
  }
866
940
  }
941
+ const goalSettings = parseJudgeSettings(c, raw.judge_settings, label);
942
+ if (raw.judge_settings !== undefined && goalSettings === undefined)
943
+ ok = false;
944
+ const goalMerged = mergeJudgeSettings(defaultJudgeSettings, goalSettings);
867
945
  if (ok) {
868
946
  result = {
869
947
  config: {
870
948
  ...common,
871
949
  type: "goal_reached",
872
950
  ...(judge !== undefined ? { judge } : {}),
951
+ ...(goalSettings !== undefined ? { judge_settings: goalSettings } : {}),
873
952
  ...(guidance !== undefined ? { guidance } : {}),
874
953
  },
875
954
  resolved: {
876
955
  ...resolvedCommon,
877
956
  type: "goal_reached",
878
957
  judge: judge ?? defaultJudge ?? "",
958
+ ...(goalMerged !== undefined ? { judgeSettings: goalMerged } : {}),
879
959
  ...(guidance !== undefined ? { guidance } : {}),
880
960
  },
881
961
  };
@@ -914,6 +994,7 @@ function graderHashObject(grader) {
914
994
  scale: grader.scale,
915
995
  ...(grader.scale === "1_to_5" ? { pass_at: grader.passAt } : {}),
916
996
  judge: grader.judge,
997
+ ...(grader.judgeSettings !== undefined ? { judge_settings: grader.judgeSettings } : {}),
917
998
  examples: grader.examples,
918
999
  };
919
1000
  case "code":
@@ -935,7 +1016,12 @@ function graderHashObject(grader) {
935
1016
  final_reply: grader.finalReply,
936
1017
  };
937
1018
  case "goal_reached":
938
- return { ...base, judge: grader.judge, guidance: grader.guidance };
1019
+ return {
1020
+ ...base,
1021
+ judge: grader.judge,
1022
+ ...(grader.judgeSettings !== undefined ? { judge_settings: grader.judgeSettings } : {}),
1023
+ guidance: grader.guidance,
1024
+ };
939
1025
  }
940
1026
  }
941
1027
  export function graderHash(grader) {
@@ -1029,7 +1115,7 @@ export function parseEvalOutcome(yamlText, filePath, projectRoot) {
1029
1115
  else {
1030
1116
  const seen = new Set();
1031
1117
  raw.graders.forEach((rawGrader, index) => {
1032
- const parsed = parseGrader(c, rawGrader, index, defaults?.judge, target?.kind);
1118
+ const parsed = parseGrader(c, rawGrader, index, defaults?.judge, target?.kind, defaults?.judge_settings);
1033
1119
  if (isPlainObject(rawGrader) && typeof rawGrader.id === "string") {
1034
1120
  if (seen.has(rawGrader.id)) {
1035
1121
  c.add("eval.grader_duplicate_id", `Duplicate grader id "${rawGrader.id}"`);
@@ -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) {
@@ -1,6 +1,8 @@
1
+ import { internalAgentsVisible } from "../operatorAgent/internalAgents.js";
2
+ import { PROFILE_GRANTS } from "../toolkit/profiles.js";
1
3
  import { realpath, stat } from "node:fs/promises";
2
4
  import path from "node:path";
3
- import { mapJudge } from "../evals/judgeProviders.js";
5
+ import { isFloatingModelId, mapJudge } from "../evals/judgeProviders.js";
4
6
  import { classifyTool } from "../agents/toolRisk.js";
5
7
  import { checkDatasetAgainstAgent } from "./datasetAgentCheck.js";
6
8
  import { checkDatasetAgainstStage } from "./datasetStageCheck.js";
@@ -69,16 +71,20 @@ export async function validateEval(loaded, ctx) {
69
71
  }
70
72
  const targetKind = file.target.kind;
71
73
  for (const grader of loaded.graders) {
72
- const wrongKind = targetKind === "agent"
74
+ const conversationTarget = targetKind === "agent" || targetKind === "internal_agent";
75
+ const wrongKind = conversationTarget
73
76
  ? grader.type === "schema"
74
77
  : CONVERSATION_GRADER_TYPES.includes(grader.type);
75
78
  if (wrongKind) {
76
- add("error", "eval.grader_not_applicable", `Grader "${grader.id}" (${grader.type}) cannot grade ${targetKind === "agent" ? "an agent" : "a stage"} target`);
79
+ add("error", "eval.grader_not_applicable", `Grader "${grader.id}" (${grader.type}) cannot grade ${conversationTarget ? "an agent" : "a stage"} target`);
77
80
  }
78
81
  }
79
82
  if (file.target.kind === "agent") {
80
83
  return validateAgentEval(loaded, ctx, findings);
81
84
  }
85
+ if (file.target.kind === "internal_agent") {
86
+ return validateInternalAgentEval(loaded, findings);
87
+ }
82
88
  const target = await resolveEvalTarget(file, loaded.path, ctx.projectRoot);
83
89
  let resolved;
84
90
  if (!target.ok) {
@@ -100,6 +106,7 @@ export async function validateEval(loaded, ctx) {
100
106
  }
101
107
  const stageModel = file.defaults?.model ?? resolved?.modelResolved;
102
108
  const unknownChecked = new Set();
109
+ const unpinnedWarned = new Set();
103
110
  for (const grader of loaded.graders) {
104
111
  if (grader.type !== "rubric")
105
112
  continue;
@@ -116,6 +123,13 @@ export async function validateEval(loaded, ctx) {
116
123
  if (stageModel !== undefined && grader.judge === stageModel) {
117
124
  add("warning", "eval.judge_same_as_stage", `Rubric grader "${grader.id}" uses judge "${grader.judge}", the same model as the stage (self-preference bias)`);
118
125
  }
126
+ if (isFloatingModelId(grader.judge) && !unpinnedWarned.has(grader.judge)) {
127
+ unpinnedWarned.add(grader.judge);
128
+ add("warning", "eval.judge_unpinned", `Judge model "${grader.judge}" is a floating id; its behavior can change under stored runs. Pin a dated version`);
129
+ }
130
+ if ((grader.judgeSettings?.votes ?? 1) > 1 && grader.judgeSettings?.temperature === 0) {
131
+ add("warning", "eval.judge_votes_no_variation", `Grader "${grader.id}" asks for ${grader.judgeSettings?.votes} votes at temperature 0, so every vote is the same call and the cost is multiplied for nothing`);
132
+ }
119
133
  }
120
134
  const paths = resolveEvalPaths(file, loaded.path);
121
135
  const datasetProblem = ctx.datasetInline
@@ -199,6 +213,41 @@ function hasExpectedTools(c) {
199
213
  const record = expected;
200
214
  return record.tools !== undefined || record.forbidden_tools !== undefined;
201
215
  }
216
+ function validateInternalAgentEval(loaded, findings) {
217
+ const add = (severity, code, message) => {
218
+ findings.push(evalFinding(loaded.relPath, severity, code, message));
219
+ };
220
+ const profile = loaded.file.target.kind === "internal_agent" ? loaded.file.target.profile : "";
221
+ if (!internalAgentsVisible()) {
222
+ add("error", "eval.internal_agent_hidden", "Internal agents are hidden. Set STAGEFLOW_SHOW_INTERNAL_AGENTS=1 on the Host to eval one.");
223
+ }
224
+ const grant = PROFILE_GRANTS[profile];
225
+ if (!grant) {
226
+ add("error", "eval.internal_agent_unknown", `No internal agent has id "${profile}"`);
227
+ }
228
+ const conversation = resolveConversation(loaded.file.conversation);
229
+ if (conversation.tools === "real") {
230
+ add("error", "eval.internal_agent_real_tools", "An internal agent eval cannot use real tools. They write the operator's project and do not pass the project-agent tool gate. Use read_only or mocked.");
231
+ }
232
+ if (grant) {
233
+ const names = new Set(grant.tools);
234
+ for (const grader of loaded.graders) {
235
+ if (grader.type !== "tool_calls")
236
+ continue;
237
+ const mentioned = [
238
+ ...(grader.mustCall ?? []),
239
+ ...(grader.mustNotCall ?? []),
240
+ ...(grader.mustAskFirst ?? []),
241
+ ...grader.args.map((arg) => arg.tool),
242
+ ];
243
+ const missing = [...new Set(mentioned)].filter((name) => !names.has(name));
244
+ if (missing.length > 0) {
245
+ add("warning", "eval.tool_not_granted", `Grader "${grader.id}" names tool(s) ${profile} does not have: ${missing.join(", ")}`);
246
+ }
247
+ }
248
+ }
249
+ return findings;
250
+ }
202
251
  async function validateAgentEval(loaded, ctx, findings) {
203
252
  const add = (severity, code, message) => {
204
253
  findings.push(evalFinding(loaded.relPath, severity, code, message));
@@ -226,6 +275,7 @@ async function validateAgentEval(loaded, ctx, findings) {
226
275
  }
227
276
  const agentModel = file.defaults?.model ?? resolved?.modelResolved;
228
277
  const unknownChecked = new Set();
278
+ const unpinnedWarned = new Set();
229
279
  for (const grader of loaded.graders) {
230
280
  if (grader.type !== "rubric" && grader.type !== "goal_reached")
231
281
  continue;
@@ -242,6 +292,13 @@ async function validateAgentEval(loaded, ctx, findings) {
242
292
  if (agentModel !== undefined && grader.judge === agentModel) {
243
293
  add("warning", "eval.judge_same_as_stage", `Grader "${grader.id}" uses judge "${grader.judge}", the same model as the agent (self-preference bias)`);
244
294
  }
295
+ if (isFloatingModelId(grader.judge) && !unpinnedWarned.has(grader.judge)) {
296
+ unpinnedWarned.add(grader.judge);
297
+ add("warning", "eval.judge_unpinned", `Judge model "${grader.judge}" is a floating id; its behavior can change under stored runs. Pin a dated version`);
298
+ }
299
+ if ((grader.judgeSettings?.votes ?? 1) > 1 && grader.judgeSettings?.temperature === 0) {
300
+ add("warning", "eval.judge_votes_no_variation", `Grader "${grader.id}" asks for ${grader.judgeSettings?.votes} votes at temperature 0, so every vote is the same call and the cost is multiplied for nothing`);
301
+ }
245
302
  }
246
303
  if (resolved) {
247
304
  const grantWarn = (names, where) => {
@@ -138,7 +138,7 @@ export function renderEvalYaml(definition) {
138
138
  return toText(doc);
139
139
  }
140
140
  function fillCreateDefaults(definition) {
141
- if (definition.target?.kind === "agent") {
141
+ if (definition.target?.kind === "agent" || definition.target?.kind === "internal_agent") {
142
142
  const conversation = { ...definition.conversation };
143
143
  const d = EVAL_CONVERSATION_DEFAULTS;
144
144
  const explicit = {
@@ -315,7 +315,7 @@ export function previewEvalUpdate(currentText, definition) {
315
315
  if (isDeepStrictEqual(doc.toJS(), JSON.parse(JSON.stringify(target))))
316
316
  return currentText;
317
317
  const targetKind = isPlain(target.target) ? target.target.kind : undefined;
318
- syncMap(doc, doc.contents, target, [], targetKind === "agent");
318
+ syncMap(doc, doc.contents, target, [], targetKind === "agent" || targetKind === "internal_agent");
319
319
  return toText(doc);
320
320
  }
321
321
  function relOf(projectRoot, abs) {
@@ -0,0 +1,12 @@
1
+ export type ArtifactPresenceContext = {
2
+ runWorkspaceDir: string;
3
+ stageId: string;
4
+ attempt: number;
5
+ };
6
+ /**
7
+ * When `artifacts` is non-empty and attempt context is provided, each listed
8
+ * path must resolve to an existing regular file under the current attempt
9
+ * artifacts directory. Empty `artifacts` is a no-op. Distinct from emit-phase
10
+ * `artifact_declared` (list-only) and after-phase `type: artifact` verify.
11
+ */
12
+ export declare function assertEmittedArtifactsOnDisk(artifacts: readonly string[], presence: ArtifactPresenceContext | undefined): Promise<void>;
@@ -0,0 +1,43 @@
1
+ import path from "node:path";
2
+ import { acceptArtifactListedPath } from "../runtime/artifactPath.js";
3
+ import { inspectArtifactUnderRoot } from "../runtime/artifactDiskInspect.js";
4
+ import { attemptArtifactsDir } from "../runstore/workspaceLayout.js";
5
+ import { EnvelopeError } from "../types/envelope.js";
6
+ function artifactBasename(listedPath) {
7
+ return path.posix.basename(listedPath.replace(/\\/g, "/"));
8
+ }
9
+ function missingArtifactMessage(listedPath) {
10
+ return `artifact "${artifactBasename(listedPath)}" is missing from this attempt's artifacts directory; write it with write_stage_artifact before emit`;
11
+ }
12
+ function notFileArtifactMessage(listedPath, fileType) {
13
+ return `artifact "${artifactBasename(listedPath)}" is ${fileType}, not a regular file under this attempt's artifacts directory; write it with write_stage_artifact before emit`;
14
+ }
15
+ /**
16
+ * When `artifacts` is non-empty and attempt context is provided, each listed
17
+ * path must resolve to an existing regular file under the current attempt
18
+ * artifacts directory. Empty `artifacts` is a no-op. Distinct from emit-phase
19
+ * `artifact_declared` (list-only) and after-phase `type: artifact` verify.
20
+ */
21
+ export async function assertEmittedArtifactsOnDisk(artifacts, presence) {
22
+ if (artifacts.length === 0 || presence === undefined) {
23
+ return;
24
+ }
25
+ const artifactsDir = attemptArtifactsDir(presence.runWorkspaceDir, presence.stageId, presence.attempt);
26
+ for (const listed of artifacts) {
27
+ const accepted = acceptArtifactListedPath(listed, {
28
+ kind: "current_attempt",
29
+ stageId: presence.stageId,
30
+ attempt: presence.attempt,
31
+ });
32
+ if (accepted === undefined) {
33
+ throw new EnvelopeError(missingArtifactMessage(listed));
34
+ }
35
+ const disk = await inspectArtifactUnderRoot(artifactsDir, accepted.relative);
36
+ if (disk.file_type === "missing") {
37
+ throw new EnvelopeError(missingArtifactMessage(listed));
38
+ }
39
+ if (disk.file_type !== "file") {
40
+ throw new EnvelopeError(notFileArtifactMessage(listed, disk.file_type));
41
+ }
42
+ }
43
+ }
@@ -12,9 +12,12 @@
12
12
  import { type QaExchange } from "../hitl/qaTrail.js";
13
13
  import { type StageEnvelope } from "../types/envelope.js";
14
14
  import type { PreEmitCheck } from "../types/preEmitCheck.js";
15
+ import type { ArtifactPresenceContext } from "./assertEmittedArtifacts.js";
15
16
  export type PreEmitCheckOptions = {
16
17
  checks?: PreEmitCheck[];
17
18
  readQaTrail?: () => QaExchange[] | Promise<QaExchange[]>;
19
+ /** Host attempt context for emit disk-truth of non-empty `envelope.artifacts`. */
20
+ artifactPresence?: ArtifactPresenceContext;
18
21
  };
19
22
  export declare function declaredArtifactIssue(artifacts: readonly string[], check: Extract<PreEmitCheck, {
20
23
  type: "artifact_declared";