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.
- package/README.md +12 -3
- package/dist/agent/activity.d.ts +18 -0
- package/dist/agent/claudeAdapter.js +7 -0
- package/dist/agent/claudeTools.d.ts +27 -18
- package/dist/agent/claudeTools.js +54 -15
- package/dist/agent/compactionEvents.d.ts +34 -0
- package/dist/agent/compactionEvents.js +37 -0
- package/dist/agent/openRouterRouting.d.ts +7 -0
- package/dist/agent/openRouterRouting.js +23 -0
- package/dist/agent/piAdapter.d.ts +6 -0
- package/dist/agent/piAdapter.js +128 -27
- package/dist/agent/piSettings.d.ts +7 -0
- package/dist/agent/piSettings.js +9 -0
- package/dist/agent/sandboxPiTools.d.ts +3 -1
- package/dist/agent/sandboxPiTools.js +8 -2
- package/dist/agent/stageTurnDriver.d.ts +11 -2
- package/dist/agent/stageTurnDriver.js +45 -4
- package/dist/agent/toolOutputBudget.d.ts +15 -1
- package/dist/agent/toolOutputBudget.js +18 -0
- package/dist/agent/toolOutputBudgetExtension.d.ts +2 -0
- package/dist/agent/toolOutputBudgetExtension.js +40 -3
- package/dist/agents/authorChat.d.ts +1 -0
- package/dist/agents/authorChat.js +7 -0
- package/dist/agents/authorSessionStore.d.ts +3 -0
- package/dist/agents/authorSessionStore.js +17 -2
- package/dist/agents/evalGate.js +1 -0
- package/dist/agents/hostContext.d.ts +1 -1
- package/dist/agents/runtime/piRuntime.d.ts +5 -1
- package/dist/agents/runtime/piRuntime.js +46 -12
- package/dist/agents/service.js +3 -2
- package/dist/agents/sessions.js +3 -1
- package/dist/cli/evalCommand.d.ts +1 -1
- package/dist/cli/evalCommand.js +39 -2
- package/dist/cli/hostClient.d.ts +6 -0
- package/dist/cli/hostClient.js +4 -0
- package/dist/compaction/directives.d.ts +18 -0
- package/dist/compaction/directives.js +119 -0
- package/dist/compaction/extension.d.ts +20 -0
- package/dist/compaction/extension.js +78 -0
- package/dist/compaction/mask.d.ts +18 -0
- package/dist/compaction/mask.js +155 -0
- package/dist/compaction/pin.d.ts +21 -0
- package/dist/compaction/pin.js +270 -0
- package/dist/compaction/scrub.d.ts +7 -0
- package/dist/compaction/scrub.js +26 -0
- package/dist/compaction/sessionWiring.d.ts +22 -0
- package/dist/compaction/sessionWiring.js +26 -0
- package/dist/compaction/summarize.d.ts +79 -0
- package/dist/compaction/summarize.js +171 -0
- package/dist/compaction/toolPolicy.d.ts +3 -0
- package/dist/compaction/toolPolicy.js +33 -0
- package/dist/compaction/types.d.ts +45 -0
- package/dist/compaction/types.js +7 -0
- package/dist/compaction/workingState.d.ts +56 -0
- package/dist/compaction/workingState.js +198 -0
- package/dist/config/createPipeline.js +2 -0
- package/dist/config/createTrigger.js +7 -1
- package/dist/config/ensureCatalogScanRoot.d.ts +2 -0
- package/dist/config/ensureCatalogScanRoot.js +6 -0
- package/dist/config/evalCatalog.js +30 -2
- package/dist/config/githubTriggerAction.d.ts +8 -0
- package/dist/config/githubTriggerAction.js +29 -0
- package/dist/config/hostConfig.js +2 -0
- package/dist/config/loadEval.d.ts +3 -1
- package/dist/config/loadEval.js +96 -10
- package/dist/config/pipelineNeeds.d.ts +2 -0
- package/dist/config/pipelineNeeds.js +16 -3
- package/dist/config/pipelineRoute.js +5 -4
- package/dist/config/validateCatalog.d.ts +1 -1
- package/dist/config/validateCatalog.js +12 -0
- package/dist/config/validateEval.js +60 -3
- package/dist/config/writeEval.js +2 -2
- package/dist/envelope/assertEmittedArtifacts.d.ts +12 -0
- package/dist/envelope/assertEmittedArtifacts.js +43 -0
- package/dist/envelope/preEmitChecks.d.ts +3 -0
- package/dist/evals/calibration.d.ts +21 -0
- package/dist/evals/calibration.js +69 -0
- package/dist/evals/calibrationSuggest.d.ts +29 -0
- package/dist/evals/calibrationSuggest.js +72 -0
- package/dist/evals/calibrator.d.ts +46 -0
- package/dist/evals/calibrator.js +148 -0
- package/dist/evals/compare.d.ts +1 -1
- package/dist/evals/compare.js +3 -0
- package/dist/evals/constants.d.ts +7 -3
- package/dist/evals/constants.js +7 -1
- package/dist/evals/engine/FakeEngine.d.ts +2 -1
- package/dist/evals/engine/FakeEngine.js +9 -3
- package/dist/evals/engine/promptfoo/PromptfooEngine.d.ts +1 -1
- package/dist/evals/engine/promptfoo/PromptfooEngine.js +4 -2
- package/dist/evals/engine/promptfoo/generate.d.ts +3 -1
- package/dist/evals/engine/promptfoo/generate.js +43 -23
- package/dist/evals/engine/promptfoo/judgeProviders.js +2 -1
- package/dist/evals/engine/promptfoo/normalize.d.ts +4 -1
- package/dist/evals/engine/promptfoo/normalize.js +16 -3
- package/dist/evals/engine/promptfoo/rubricPrompt.d.ts +3 -1
- package/dist/evals/engine/promptfoo/rubricPrompt.js +15 -5
- package/dist/evals/errors.d.ts +7 -0
- package/dist/evals/errors.js +10 -0
- package/dist/evals/judgeProviders.d.ts +6 -0
- package/dist/evals/judgeProviders.js +14 -0
- package/dist/evals/judged.d.ts +7 -0
- package/dist/evals/judged.js +7 -0
- package/dist/evals/manager.d.ts +27 -8
- package/dist/evals/manager.js +73 -12
- package/dist/evals/plan.d.ts +10 -1
- package/dist/evals/plan.js +33 -13
- package/dist/evals/planInternalAgent.d.ts +19 -0
- package/dist/evals/planInternalAgent.js +201 -0
- package/dist/evals/read/api.d.ts +18 -0
- package/dist/evals/read/api.js +10 -1
- package/dist/evals/read/labelQueue.d.ts +45 -0
- package/dist/evals/read/labelQueue.js +83 -0
- package/dist/evals/read/list.js +10 -1
- package/dist/evals/read/runs.d.ts +5 -0
- package/dist/evals/read/runs.js +7 -1
- package/dist/evals/runner/caseRunner.d.ts +2 -1
- package/dist/evals/runner/caseRunner.js +16 -5
- package/dist/evals/runner/internalAgentCaseRunner.d.ts +6 -0
- package/dist/evals/runner/internalAgentCaseRunner.js +250 -0
- package/dist/evals/snapshot.d.ts +10 -2
- package/dist/evals/snapshot.js +8 -3
- package/dist/evals/stats.d.ts +40 -0
- package/dist/evals/stats.js +69 -0
- package/dist/evals/storeTypes.d.ts +42 -0
- package/dist/evals/types.d.ts +25 -2
- package/dist/evals/types.js +4 -1
- package/dist/evals/votes.d.ts +10 -0
- package/dist/evals/votes.js +60 -0
- package/dist/guide/cardContract.d.ts +133 -0
- package/dist/guide/cardContract.js +156 -0
- package/dist/guide/chat.d.ts +45 -0
- package/dist/guide/chat.js +378 -0
- package/dist/guide/knowledge/index.d.ts +14 -0
- package/dist/guide/knowledge/index.js +109 -0
- package/dist/guide/knowledge/topics/agents.d.ts +2 -0
- package/dist/guide/knowledge/topics/agents.js +58 -0
- package/dist/guide/knowledge/topics/bestPractices.d.ts +2 -0
- package/dist/guide/knowledge/topics/bestPractices.js +67 -0
- package/dist/guide/knowledge/topics/connections.d.ts +2 -0
- package/dist/guide/knowledge/topics/connections.js +66 -0
- package/dist/guide/knowledge/topics/consoleMap.d.ts +2 -0
- package/dist/guide/knowledge/topics/consoleMap.js +55 -0
- package/dist/guide/knowledge/topics/evals.d.ts +2 -0
- package/dist/guide/knowledge/topics/evals.js +72 -0
- package/dist/guide/knowledge/topics/limitations.d.ts +2 -0
- package/dist/guide/knowledge/topics/limitations.js +84 -0
- package/dist/guide/knowledge/topics/mcpSkillsBrowser.d.ts +2 -0
- package/dist/guide/knowledge/topics/mcpSkillsBrowser.js +57 -0
- package/dist/guide/knowledge/topics/memories.d.ts +2 -0
- package/dist/guide/knowledge/topics/memories.js +63 -0
- package/dist/guide/knowledge/topics/modelsAndProviders.d.ts +2 -0
- package/dist/guide/knowledge/topics/modelsAndProviders.js +56 -0
- package/dist/guide/knowledge/topics/overview.d.ts +2 -0
- package/dist/guide/knowledge/topics/overview.js +56 -0
- package/dist/guide/knowledge/topics/pipelines.d.ts +2 -0
- package/dist/guide/knowledge/topics/pipelines.js +93 -0
- package/dist/guide/knowledge/topics/stages.d.ts +2 -0
- package/dist/guide/knowledge/topics/stages.js +54 -0
- package/dist/guide/knowledge/topics/tasks.d.ts +2 -0
- package/dist/guide/knowledge/topics/tasks.js +52 -0
- package/dist/guide/knowledge/topics/triggers.d.ts +2 -0
- package/dist/guide/knowledge/topics/triggers.js +68 -0
- package/dist/guide/knowledge/topics/troubleshooting.d.ts +2 -0
- package/dist/guide/knowledge/topics/troubleshooting.js +85 -0
- package/dist/guide/knowledgeTools.d.ts +2 -0
- package/dist/guide/knowledgeTools.js +56 -0
- package/dist/guide/showUi.d.ts +6 -0
- package/dist/guide/showUi.js +546 -0
- package/dist/guide/threadStore.d.ts +48 -0
- package/dist/guide/threadStore.js +189 -0
- package/dist/guide/writeRoot.d.ts +11 -0
- package/dist/guide/writeRoot.js +70 -0
- package/dist/hosted/translate.js +2 -0
- package/dist/mcp/agentTools.js +12 -0
- package/dist/mcp/catalogTools.js +18 -20
- package/dist/mcp/catalogWriteTools.d.ts +8 -0
- package/dist/mcp/catalogWriteTools.js +225 -0
- package/dist/mcp/connectionTools.d.ts +3 -0
- package/dist/mcp/connectionTools.js +85 -0
- package/dist/mcp/controlTools.js +34 -1
- package/dist/mcp/deps.d.ts +9 -0
- package/dist/mcp/evalTools.js +723 -21
- package/dist/mcp/memoryHostTools.d.ts +3 -0
- package/dist/mcp/memoryHostTools.js +72 -0
- package/dist/mcp/pipelineAuthorTools.js +14 -1
- package/dist/mcp/server.js +5 -0
- package/dist/mcp/tools.js +6 -0
- package/dist/mcp/triggerTools.js +77 -31
- package/dist/operatorAgent/evalGate.d.ts +22 -0
- package/dist/operatorAgent/evalGate.js +77 -0
- package/dist/operatorAgent/fakeHost.js +10 -2
- package/dist/operatorAgent/host.js +18 -2
- package/dist/operatorAgent/index.d.ts +1 -0
- package/dist/operatorAgent/index.js +1 -0
- package/dist/operatorAgent/internalAgents.d.ts +11 -0
- package/dist/operatorAgent/internalAgents.js +13 -0
- package/dist/operatorAgent/piModel.d.ts +5 -1
- package/dist/operatorAgent/piModel.js +15 -10
- package/dist/operatorAgent/profiles/agentAuthor.js +66 -22
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.d.ts +1 -1
- package/dist/operatorAgent/profiles/agentAuthorPlaybook.js +1 -1
- package/dist/operatorAgent/profiles/guideConfirm.d.ts +21 -0
- package/dist/operatorAgent/profiles/guideConfirm.js +100 -0
- package/dist/operatorAgent/profiles/guideTools.d.ts +7 -0
- package/dist/operatorAgent/profiles/guideTools.js +123 -0
- package/dist/operatorAgent/profiles/stageflowGuide.d.ts +7 -0
- package/dist/operatorAgent/profiles/stageflowGuide.js +133 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +23 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +9 -1
- package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +2 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +197 -9
- package/dist/operatorAgent/types.d.ts +23 -0
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/runstore/port.d.ts +13 -1
- package/dist/runstore/retention.d.ts +2 -1
- package/dist/runstore/retention.js +3 -7
- package/dist/runstore/runStatusClock.d.ts +17 -0
- package/dist/runstore/runStatusClock.js +19 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +11 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +70 -45
- package/dist/runstore/sqlite/calibrationStore.d.ts +10 -0
- package/dist/runstore/sqlite/calibrationStore.js +106 -0
- package/dist/runstore/sqlite/evalStore.d.ts +5 -0
- package/dist/runstore/sqlite/evalStore.js +7 -0
- package/dist/runstore/sqlite/migrations/018-eval-grader-labels.d.ts +11 -0
- package/dist/runstore/sqlite/migrations/018-eval-grader-labels.js +43 -0
- package/dist/runstore/sqlite/migrations/index.js +2 -0
- package/dist/runstore/workspaceLayout.js +6 -1
- package/dist/runtime/artifactDiskInspect.d.ts +14 -0
- package/dist/runtime/artifactDiskInspect.js +52 -0
- package/dist/runtime/artifactPath.d.ts +24 -0
- package/dist/runtime/artifactPath.js +84 -0
- package/dist/runtime/completionCheckRunner.js +15 -43
- package/dist/runtime/githubPollSource.d.ts +22 -4
- package/dist/runtime/githubPollSource.js +93 -8
- package/dist/runtime/priorArtifactGrants.d.ts +8 -0
- package/dist/runtime/priorArtifactGrants.js +105 -0
- package/dist/runtime/runActionEligibility.d.ts +46 -0
- package/dist/runtime/runActionEligibility.js +123 -0
- package/dist/runtime/runManager.d.ts +4 -1
- package/dist/runtime/runManager.js +52 -10
- package/dist/runtime/runRetryCoordinator.d.ts +2 -14
- package/dist/runtime/runRetryCoordinator.js +3 -67
- package/dist/runtime/sandboxContainer.d.ts +7 -0
- package/dist/runtime/sandboxContainer.js +48 -6
- package/dist/runtime/sandboxDesk.d.ts +2 -1
- package/dist/runtime/sandboxDesk.js +11 -12
- package/dist/runtime/stageEnvironment.js +1 -0
- package/dist/runtime/stageRoots.d.ts +12 -0
- package/dist/runtime/stageSandbox.d.ts +2 -1
- package/dist/runtime/stageSandbox.js +18 -2
- package/dist/runtime/stageWorker.js +20 -0
- package/dist/server/agentAuthorRoutes.js +12 -3
- package/dist/server/bootstrap.d.ts +5 -0
- package/dist/server/bootstrap.js +14 -1
- package/dist/server/evalRoutes.js +251 -12
- package/dist/server/guideRoutes.d.ts +5 -0
- package/dist/server/guideRoutes.js +166 -0
- package/dist/server/http.d.ts +10 -0
- package/dist/server/http.js +35 -1
- package/dist/server/internalAgentRoutes.d.ts +6 -0
- package/dist/server/internalAgentRoutes.js +12 -0
- package/dist/server/mcpHost.js +2 -0
- package/dist/toolkit/meta.d.ts +1 -1
- package/dist/toolkit/meta.js +41 -1
- package/dist/toolkit/profiles.d.ts +5 -0
- package/dist/toolkit/profiles.js +12 -0
- package/dist/tools/askOperator.js +2 -1
- package/dist/tools/emitStageEnvelope.d.ts +17 -16
- package/dist/tools/emitStageEnvelope.js +46 -32
- package/dist/tools/stageToolNames.d.ts +4 -0
- package/dist/tools/stageToolNames.js +4 -0
- package/dist/tools/workingState.d.ts +40 -0
- package/dist/tools/workingState.js +108 -0
- package/dist/tools/writeStageArtifact.d.ts +0 -9
- package/dist/tools/writeStageArtifact.js +2 -1
- package/dist/ui/assets/{MarkdownText-BTrOqu5J.js → MarkdownText-BS2BBbCx.js} +2 -2
- package/dist/ui/assets/adapters-DPD-cAsy.js +17 -0
- package/dist/ui/assets/chat-BD1-VTT6.js +2 -0
- package/dist/ui/assets/chat-DXnQv9st.css +2 -0
- package/dist/ui/assets/main-AX5XNLtX.js +278 -0
- package/dist/ui/assets/{main-CHlmTNJe.css → main-CFX0_n8J.css} +1 -1
- package/dist/ui/chat.html +3 -4
- package/dist/ui/index.html +3 -4
- package/dist/workshop/chatTurn.js +1 -1
- package/dist/workshop/workshopPin.d.ts +3 -3
- package/dist/workshop/workshopPin.js +16 -15
- package/examples/compaction-stress/README.md +30 -0
- package/examples/compaction-stress/check-run.mjs +53 -0
- package/examples/compaction-stress/collect-evidence.yaml +60 -0
- package/examples/compaction-stress/compaction-stress.pipeline.yaml +141 -0
- package/examples/compaction-stress/compaction-stress.task.yaml +15 -0
- package/examples/compaction-stress/generate-evidence.mjs +110 -0
- package/examples/compaction-stress/root-cause.yaml +26 -0
- package/examples/compaction-stress/stageflow.yaml +9 -0
- package/examples/compaction-stress/write-postmortem.yaml +40 -0
- package/examples/evals/README.md +18 -0
- package/examples/evals/guide-behavior.dataset.jsonl +15 -0
- package/examples/evals/guide-behavior.eval.yaml +35 -0
- package/examples/evals/guide-depth.dataset.jsonl +35 -0
- package/examples/evals/guide-depth.eval.yaml +169 -0
- package/examples/memory/evals/guide-behavior.dataset.jsonl +15 -0
- package/examples/memory/evals/guide-behavior.eval.yaml +35 -0
- package/examples/memory/evals/guide-depth.dataset.jsonl +35 -0
- package/examples/memory/evals/guide-depth.eval.yaml +169 -0
- package/package.json +1 -1
- package/skills/stageflow/references/control-surface.md +1 -1
- package/skills/stageflow/references/evals.md +1 -0
- package/dist/ui/assets/AssistantRuntimeProvider-qIX1xNoH.js +0 -9
- package/dist/ui/assets/ReadonlyStore-BfXqHykm.js +0 -6
- package/dist/ui/assets/chat-Df_wCpak.js +0 -5
- package/dist/ui/assets/chat-P7KQznHm.css +0 -2
- 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>;
|
package/dist/config/loadEval.js
CHANGED
|
@@ -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 "
|
|
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 "
|
|
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 >
|
|
469
|
-
c.add("eval.grader_invalid", `${where} "examples" must be an array of at most
|
|
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 {
|
|
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
|
-
|
|
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(
|
|
31
|
-
on.push(
|
|
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 {
|
|
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
|
-
|
|
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(
|
|
91
|
-
on.push(
|
|
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
|
|
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 ${
|
|
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) => {
|
package/dist/config/writeEval.js
CHANGED
|
@@ -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";
|