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,171 @@
|
|
|
1
|
+
import { applyClearing, planClearing, renderClearedIndex } from "./mask.js";
|
|
2
|
+
import { extractDirectives, mergeDirectives, renderDirectives } from "./directives.js";
|
|
3
|
+
import { extractPinnedFacts, renderPinned } from "./pin.js";
|
|
4
|
+
import { scrubSecrets } from "./scrub.js";
|
|
5
|
+
import { COMPACTION_PROMPT_VERSION } from "./types.js";
|
|
6
|
+
import { WorkingStateStore } from "./workingState.js";
|
|
7
|
+
export const COMPACTION_FOCUS = "This is an autonomous agent run, not a chat. Make the summary a working record the agent can continue from without the earlier messages. " +
|
|
8
|
+
"Copy exact identifiers, numbers, counts, timestamps, versions, paths, URLs and quoted error text verbatim; never round, reword or estimate them. " +
|
|
9
|
+
"Keep every failure, rejection and dead end explicit, with why it failed, so it is not retried. " +
|
|
10
|
+
"State what is in flight, which sources or items are already handled and which remain, and the exact next step. " +
|
|
11
|
+
"Keep decisions made by the operator or user and the deliverable the run must produce, including its required format. " +
|
|
12
|
+
"Leave out routine output that carries no decision or value.";
|
|
13
|
+
export const SECTIONS_MARKER = "<!-- stageflow:compaction-sections -->";
|
|
14
|
+
export const PINNED_RENDER_CHARS = 6000;
|
|
15
|
+
export const CLEARED_RENDER_CHARS = 3000;
|
|
16
|
+
export const STATE_RENDER_CHARS = 6000;
|
|
17
|
+
export const DIRECTIVES_RENDER_CHARS = 5000;
|
|
18
|
+
export const PINNED_CARRY_MAX = 400;
|
|
19
|
+
export const CLEARED_CARRY_MAX = 200;
|
|
20
|
+
/** The sections are always appended last, so the last marker at the start of a line is the real one. */
|
|
21
|
+
export function splitSummary(summary) {
|
|
22
|
+
const afterNewline = summary.lastIndexOf(`\n${SECTIONS_MARKER}`);
|
|
23
|
+
const at = afterNewline >= 0 ? afterNewline + 1 : summary.startsWith(SECTIONS_MARKER) ? 0 : -1;
|
|
24
|
+
if (at < 0)
|
|
25
|
+
return { core: summary, sections: "" };
|
|
26
|
+
return { core: summary.slice(0, at).trimEnd(), sections: summary.slice(at) };
|
|
27
|
+
}
|
|
28
|
+
const FACT_KINDS = new Set(["id", "number", "timestamp", "url", "path", "error", "other"]);
|
|
29
|
+
const DIRECTIVE_SOURCES = new Set(["operator", "user", "rejection"]);
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function strings(value) {
|
|
34
|
+
return Array.isArray(value) ? value.filter((x) => typeof x === "string") : undefined;
|
|
35
|
+
}
|
|
36
|
+
function validPinned(x) {
|
|
37
|
+
return isRecord(x) && typeof x.kind === "string" && FACT_KINDS.has(x.kind) && typeof x.value === "string" && typeof x.context === "string" && isRecord(x.source) && typeof x.source.messageIndex === "number";
|
|
38
|
+
}
|
|
39
|
+
function validCleared(x) {
|
|
40
|
+
return isRecord(x) && typeof x.toolName === "string" && typeof x.toolCallId === "string" && typeof x.recoverHint === "string" && typeof x.bytes === "number" && typeof x.descriptor === "string";
|
|
41
|
+
}
|
|
42
|
+
function validDirective(x) {
|
|
43
|
+
return isRecord(x) && typeof x.source === "string" && DIRECTIVE_SOURCES.has(x.source) && typeof x.text === "string";
|
|
44
|
+
}
|
|
45
|
+
/** Details come from the session file, so anything not shaped like ours is dropped rather than trusted. */
|
|
46
|
+
export function readStageflowDetails(value) {
|
|
47
|
+
if (!isRecord(value))
|
|
48
|
+
return undefined;
|
|
49
|
+
const readFiles = strings(value.readFiles);
|
|
50
|
+
const modifiedFiles = strings(value.modifiedFiles);
|
|
51
|
+
return {
|
|
52
|
+
...(readFiles ? { readFiles } : {}),
|
|
53
|
+
...(modifiedFiles ? { modifiedFiles } : {}),
|
|
54
|
+
...(Array.isArray(value.stageflowPinned) ? { stageflowPinned: value.stageflowPinned.filter(validPinned) } : {}),
|
|
55
|
+
...(Array.isArray(value.stageflowCleared) ? { stageflowCleared: value.stageflowCleared.filter(validCleared) } : {}),
|
|
56
|
+
...(Array.isArray(value.stageflowDirectives) ? { stageflowDirectives: value.stageflowDirectives.filter(validDirective) } : {}),
|
|
57
|
+
...(typeof value.stageflowPromptVersion === "number" ? { stageflowPromptVersion: value.stageflowPromptVersion } : {}),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function factKey(f) {
|
|
61
|
+
return `${f.kind}\u0000${f.value}`;
|
|
62
|
+
}
|
|
63
|
+
/** Newest facts first win a tie; capped so repeated compactions cannot grow without bound. */
|
|
64
|
+
export function mergePinned(previous, fresh, max = PINNED_CARRY_MAX) {
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const f of [...fresh, ...previous]) {
|
|
68
|
+
const key = factKey(f);
|
|
69
|
+
if (seen.has(key))
|
|
70
|
+
continue;
|
|
71
|
+
seen.add(key);
|
|
72
|
+
out.push(f);
|
|
73
|
+
if (out.length >= max)
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
export function mergeCleared(previous, fresh, max = CLEARED_CARRY_MAX) {
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
const out = [];
|
|
81
|
+
for (const c of [...fresh, ...previous]) {
|
|
82
|
+
if (seen.has(c.toolCallId))
|
|
83
|
+
continue;
|
|
84
|
+
seen.add(c.toolCallId);
|
|
85
|
+
out.push(c);
|
|
86
|
+
if (out.length >= max)
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
export function renderSections(input) {
|
|
92
|
+
const parts = [];
|
|
93
|
+
if (input.state.length > 0) {
|
|
94
|
+
const store = new WorkingStateStore();
|
|
95
|
+
for (const e of input.state)
|
|
96
|
+
store.set({ key: e.key, kind: e.kind, value: e.value, updatedAt: e.updatedAt });
|
|
97
|
+
const rendered = store.render(STATE_RENDER_CHARS);
|
|
98
|
+
if (rendered)
|
|
99
|
+
parts.push(rendered);
|
|
100
|
+
}
|
|
101
|
+
const directives = renderDirectives(input.directives ?? [], DIRECTIVES_RENDER_CHARS);
|
|
102
|
+
if (directives)
|
|
103
|
+
parts.push(directives);
|
|
104
|
+
if (input.pinned.length > 0)
|
|
105
|
+
parts.push(renderPinned(input.pinned, PINNED_RENDER_CHARS));
|
|
106
|
+
if (input.cleared.length > 0)
|
|
107
|
+
parts.push(renderClearedIndex(input.cleared, CLEARED_RENDER_CHARS));
|
|
108
|
+
if (parts.length === 0)
|
|
109
|
+
return "";
|
|
110
|
+
return `${SECTIONS_MARKER}\n${parts.join("\n\n")}`;
|
|
111
|
+
}
|
|
112
|
+
function scrubValue(value) {
|
|
113
|
+
if (typeof value === "string")
|
|
114
|
+
return scrubSecrets(value);
|
|
115
|
+
if (Array.isArray(value))
|
|
116
|
+
return value.map(scrubValue);
|
|
117
|
+
if (value !== null && typeof value === "object") {
|
|
118
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, scrubValue(v)]));
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
/** What the summarizer reads: message text, thinking and tool-call arguments with secrets removed. */
|
|
123
|
+
export function redactMessages(messages) {
|
|
124
|
+
return messages.map((m) => (Array.isArray(m.content) || typeof m.content === "string" ? { ...m, content: scrubValue(m.content) } : m));
|
|
125
|
+
}
|
|
126
|
+
/** Everything deterministic that happens before the model-written summary is requested. */
|
|
127
|
+
export function prepareSummaryInputs(input) {
|
|
128
|
+
const extracted = extractPinnedFacts(input.messages);
|
|
129
|
+
const pinned = mergePinned(input.previousDetails?.stageflowPinned ?? [], extracted);
|
|
130
|
+
const directives = mergeDirectives(input.previousDetails?.stageflowDirectives ?? [], extractDirectives(input.messages));
|
|
131
|
+
if (!input.clearInSummary) {
|
|
132
|
+
return { messagesForSummarizer: redactMessages(input.messages), pinned, directives, cleared: [...(input.previousDetails?.stageflowCleared ?? [])] };
|
|
133
|
+
}
|
|
134
|
+
const plan = planClearing(input.messages, input.clearInSummary);
|
|
135
|
+
return {
|
|
136
|
+
messagesForSummarizer: redactMessages(applyClearing(input.messages, plan)),
|
|
137
|
+
pinned,
|
|
138
|
+
directives,
|
|
139
|
+
cleared: mergeCleared(input.previousDetails?.stageflowCleared ?? [], plan.cleared),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function stageflowDetails(piDetails, previous, pinned, cleared, directives = []) {
|
|
143
|
+
const union = (a, b) => [...new Set([...(b ?? []), ...(a ?? [])])];
|
|
144
|
+
return {
|
|
145
|
+
readFiles: union(piDetails?.readFiles, previous?.readFiles),
|
|
146
|
+
modifiedFiles: union(piDetails?.modifiedFiles, previous?.modifiedFiles),
|
|
147
|
+
stageflowPinned: [...pinned],
|
|
148
|
+
stageflowCleared: [...cleared],
|
|
149
|
+
stageflowDirectives: [...directives],
|
|
150
|
+
stageflowPromptVersion: COMPACTION_PROMPT_VERSION,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The whole summary pipeline in one place: pin and redact the input, ask the model for the written part,
|
|
155
|
+
* scrub it, then append the deterministic sections. The Pi hook and the evaluation harness both call this,
|
|
156
|
+
* so what the harness measures is what ships.
|
|
157
|
+
*/
|
|
158
|
+
export async function composeCompactionSummary(input) {
|
|
159
|
+
const prepared = prepareSummaryInputs({
|
|
160
|
+
messages: input.messages,
|
|
161
|
+
...(input.previousDetails ? { previousDetails: input.previousDetails } : {}),
|
|
162
|
+
...(input.clearInSummary ? { clearInSummary: input.clearInSummary } : {}),
|
|
163
|
+
});
|
|
164
|
+
const result = await input.summarize({
|
|
165
|
+
messages: prepared.messagesForSummarizer,
|
|
166
|
+
previousSummary: input.previousSummary !== undefined ? splitSummary(input.previousSummary).core : undefined,
|
|
167
|
+
});
|
|
168
|
+
const core = scrubSecrets(result.summary).split(SECTIONS_MARKER).join("").trimEnd();
|
|
169
|
+
const sections = scrubSecrets(renderSections({ pinned: prepared.pinned, cleared: prepared.cleared, state: input.state, directives: prepared.directives }));
|
|
170
|
+
return { summary: sections ? `${core}\n\n${sections}` : core, result, prepared };
|
|
171
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type ClearingClass = "clearable" | "keep";
|
|
2
|
+
/** Fail closed: only provably re-fetchable read results are clearable; everything else (bash included, it can have side effects) is kept. */
|
|
3
|
+
export declare function classifyForClearing(toolName: string, isError: boolean, _details?: unknown): ClearingClass;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { classifyTool } from "../agents/toolRisk.js";
|
|
2
|
+
import { ASK_OPERATOR_TOOL_NAME, EMIT_STAGE_ENVELOPE_TOOL_NAME, WRITE_STAGE_ARTIFACT_TOOL_NAME } from "../tools/stageToolNames.js";
|
|
3
|
+
import { WORKING_STATE_TOOL_NAME } from "./types.js";
|
|
4
|
+
const ALWAYS_KEEP = new Set([
|
|
5
|
+
ASK_OPERATOR_TOOL_NAME,
|
|
6
|
+
EMIT_STAGE_ENVELOPE_TOOL_NAME,
|
|
7
|
+
WRITE_STAGE_ARTIFACT_TOOL_NAME,
|
|
8
|
+
"write",
|
|
9
|
+
"edit",
|
|
10
|
+
WORKING_STATE_TOOL_NAME,
|
|
11
|
+
]);
|
|
12
|
+
const CLEARABLE_BUILTINS = new Set(["read", "grep", "find", "ls"]);
|
|
13
|
+
/** Fail closed: only provably re-fetchable read results are clearable; everything else (bash included, it can have side effects) is kept. */
|
|
14
|
+
export function classifyForClearing(toolName, isError, _details) {
|
|
15
|
+
if (isError)
|
|
16
|
+
return "keep";
|
|
17
|
+
if (typeof toolName !== "string" || toolName.length === 0)
|
|
18
|
+
return "keep";
|
|
19
|
+
if (ALWAYS_KEEP.has(toolName))
|
|
20
|
+
return "keep";
|
|
21
|
+
if (CLEARABLE_BUILTINS.has(toolName))
|
|
22
|
+
return "clearable";
|
|
23
|
+
if (toolName.startsWith("stageflow_")) {
|
|
24
|
+
const name = toolName.slice("stageflow_".length);
|
|
25
|
+
return ALWAYS_KEEP.has(name) ? "keep" : classifyTool(undefined, `stageflow.${name}`).readOnly ? "clearable" : "keep";
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return classifyTool(undefined, toolName).readOnly ? "clearable" : "keep";
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return "keep";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contracts for Stageflow's built-in compaction (docs/plans/compaction-strategy.md).
|
|
3
|
+
* Pure data types only: the layers (mask, pin, state, summary, guards) import from here.
|
|
4
|
+
*/
|
|
5
|
+
export type PinnedFactKind = "id" | "number" | "timestamp" | "url" | "path" | "error" | "other";
|
|
6
|
+
/** A value copied verbatim out of a message so a summary cannot lose or reword it. */
|
|
7
|
+
export type PinnedFact = {
|
|
8
|
+
kind: PinnedFactKind;
|
|
9
|
+
/** The exact text, e.g. "INC-4471", "48211", "2026-03-14T05:52:03Z". */
|
|
10
|
+
value: string;
|
|
11
|
+
/** A short verbatim snippet around the value (single line, capped), for orientation. */
|
|
12
|
+
context: string;
|
|
13
|
+
/** Position in the message list the facts were extracted from, and the tool when from a tool result. */
|
|
14
|
+
source: {
|
|
15
|
+
messageIndex: number;
|
|
16
|
+
toolName?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/** A tool result replaced by a placeholder in the model's context. */
|
|
20
|
+
export type ClearedResult = {
|
|
21
|
+
toolName: string;
|
|
22
|
+
toolCallId: string;
|
|
23
|
+
/** Where the full output can be read again (a file path or a tool + args hint). */
|
|
24
|
+
recoverHint: string;
|
|
25
|
+
bytes: number;
|
|
26
|
+
/** One line describing what the result was, for the recovery index. */
|
|
27
|
+
descriptor: string;
|
|
28
|
+
};
|
|
29
|
+
export type WorkingStateKind = "fact" | "decision" | "progress" | "todo" | "blocker";
|
|
30
|
+
/** One keyed entry in the model's working state (the `working_state` tool). */
|
|
31
|
+
export type WorkingStateEntry = {
|
|
32
|
+
key: string;
|
|
33
|
+
kind: WorkingStateKind;
|
|
34
|
+
value: string;
|
|
35
|
+
updatedAt: string;
|
|
36
|
+
};
|
|
37
|
+
export declare const WORKING_STATE_TOOL_NAME = "working_state";
|
|
38
|
+
/** Everything the compaction summary is built from, besides the model-written part. */
|
|
39
|
+
export type CompactionSections = {
|
|
40
|
+
pinned: readonly PinnedFact[];
|
|
41
|
+
cleared: readonly ClearedResult[];
|
|
42
|
+
state: readonly WorkingStateEntry[];
|
|
43
|
+
};
|
|
44
|
+
/** Bumped whenever the focus prompt or section layout changes (like RUBRIC_PROMPT_VERSION). */
|
|
45
|
+
export declare const COMPACTION_PROMPT_VERSION = 1;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contracts for Stageflow's built-in compaction (docs/plans/compaction-strategy.md).
|
|
3
|
+
* Pure data types only: the layers (mask, pin, state, summary, guards) import from here.
|
|
4
|
+
*/
|
|
5
|
+
export const WORKING_STATE_TOOL_NAME = "working_state";
|
|
6
|
+
/** Bumped whenever the focus prompt or section layout changes (like RUBRIC_PROMPT_VERSION). */
|
|
7
|
+
export const COMPACTION_PROMPT_VERSION = 1;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { WorkingStateEntry, WorkingStateKind } from "./types.js";
|
|
2
|
+
export declare const WORKING_STATE_CUSTOM_TYPE = "stageflow.working_state";
|
|
3
|
+
export declare const WORKING_STATE_KINDS: readonly WorkingStateKind[];
|
|
4
|
+
export declare const WORKING_STATE_MAX_KEY = 80;
|
|
5
|
+
export declare const WORKING_STATE_MAX_VALUE = 600;
|
|
6
|
+
export declare const WORKING_STATE_MAX_ENTRIES = 60;
|
|
7
|
+
export declare const WORKING_STATE_DEFAULT_RENDER_CHARS = 6000;
|
|
8
|
+
export declare const WORKING_STATE_GUIDANCE = "Older messages and tool results are trimmed automatically as the conversation grows, so keep your own working state with the working_state tool. As soon as you learn or decide something you will need later, record it: exact values and identifiers (fact), choices and their reasons (decision), what is done (progress), what remains (todo) and what is stuck (blocker). Keep each entry to one or two short lines, update an existing key instead of adding a duplicate, and delete entries that no longer matter. Never store secrets or credentials in it.";
|
|
9
|
+
export type WorkingStateEvent = {
|
|
10
|
+
op: "set";
|
|
11
|
+
key: string;
|
|
12
|
+
kind: WorkingStateKind;
|
|
13
|
+
value: string;
|
|
14
|
+
at: string;
|
|
15
|
+
} | {
|
|
16
|
+
op: "delete";
|
|
17
|
+
key: string;
|
|
18
|
+
at: string;
|
|
19
|
+
};
|
|
20
|
+
export type WorkingStateSetResult = {
|
|
21
|
+
ok: true;
|
|
22
|
+
dropped: string[];
|
|
23
|
+
replaced: boolean;
|
|
24
|
+
redacted: boolean;
|
|
25
|
+
entry: WorkingStateEntry;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
export type WorkingStateInput = {
|
|
31
|
+
key: string;
|
|
32
|
+
kind: WorkingStateKind;
|
|
33
|
+
value: string;
|
|
34
|
+
updatedAt?: string;
|
|
35
|
+
};
|
|
36
|
+
export declare class WorkingStateStore {
|
|
37
|
+
private readonly entries;
|
|
38
|
+
get size(): number;
|
|
39
|
+
set(input: WorkingStateInput): WorkingStateSetResult;
|
|
40
|
+
delete(key: string): boolean;
|
|
41
|
+
list(): WorkingStateEntry[];
|
|
42
|
+
apply(event: WorkingStateEvent): void;
|
|
43
|
+
load(events: readonly WorkingStateEvent[]): void;
|
|
44
|
+
render(maxChars?: number): string;
|
|
45
|
+
private oldestOfKind;
|
|
46
|
+
}
|
|
47
|
+
export declare function reduceWorkingState(events: readonly WorkingStateEvent[]): WorkingStateStore;
|
|
48
|
+
type SessionEntryLike = {
|
|
49
|
+
type?: unknown;
|
|
50
|
+
customType?: unknown;
|
|
51
|
+
data?: unknown;
|
|
52
|
+
};
|
|
53
|
+
export declare function workingStateEventsFromEntries(entries: readonly SessionEntryLike[]): WorkingStateEvent[];
|
|
54
|
+
export declare function restoreWorkingState(entries: readonly SessionEntryLike[]): WorkingStateStore;
|
|
55
|
+
export declare function getWorkingStateSections(entries: readonly SessionEntryLike[]): WorkingStateEntry[];
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { scrubSecrets } from "./scrub.js";
|
|
2
|
+
export const WORKING_STATE_CUSTOM_TYPE = "stageflow.working_state";
|
|
3
|
+
export const WORKING_STATE_KINDS = [
|
|
4
|
+
"fact",
|
|
5
|
+
"decision",
|
|
6
|
+
"progress",
|
|
7
|
+
"todo",
|
|
8
|
+
"blocker",
|
|
9
|
+
];
|
|
10
|
+
export const WORKING_STATE_MAX_KEY = 80;
|
|
11
|
+
export const WORKING_STATE_MAX_VALUE = 600;
|
|
12
|
+
export const WORKING_STATE_MAX_ENTRIES = 60;
|
|
13
|
+
export const WORKING_STATE_DEFAULT_RENDER_CHARS = 6000;
|
|
14
|
+
export const WORKING_STATE_GUIDANCE = "Older messages and tool results are trimmed automatically as the conversation grows, so keep your own working state with the working_state tool. As soon as you learn or decide something you will need later, record it: exact values and identifiers (fact), choices and their reasons (decision), what is done (progress), what remains (todo) and what is stuck (blocker). Keep each entry to one or two short lines, update an existing key instead of adding a duplicate, and delete entries that no longer matter. Never store secrets or credentials in it.";
|
|
15
|
+
const RENDER_ORDER = [
|
|
16
|
+
"blocker",
|
|
17
|
+
"todo",
|
|
18
|
+
"decision",
|
|
19
|
+
"fact",
|
|
20
|
+
"progress",
|
|
21
|
+
];
|
|
22
|
+
const EVICTION_ORDER = [
|
|
23
|
+
"progress",
|
|
24
|
+
"fact",
|
|
25
|
+
"decision",
|
|
26
|
+
"todo",
|
|
27
|
+
"blocker",
|
|
28
|
+
];
|
|
29
|
+
const HEADINGS = {
|
|
30
|
+
blocker: "Blockers",
|
|
31
|
+
todo: "Todo",
|
|
32
|
+
decision: "Decisions",
|
|
33
|
+
fact: "Facts",
|
|
34
|
+
progress: "Progress",
|
|
35
|
+
};
|
|
36
|
+
function isKind(value) {
|
|
37
|
+
return (typeof value === "string" &&
|
|
38
|
+
WORKING_STATE_KINDS.includes(value));
|
|
39
|
+
}
|
|
40
|
+
function oneLine(text) {
|
|
41
|
+
return text.replace(/\s+/g, " ").trim();
|
|
42
|
+
}
|
|
43
|
+
export class WorkingStateStore {
|
|
44
|
+
entries = new Map();
|
|
45
|
+
get size() {
|
|
46
|
+
return this.entries.size;
|
|
47
|
+
}
|
|
48
|
+
set(input) {
|
|
49
|
+
if (!isKind(input.kind)) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
error: `kind must be one of ${WORKING_STATE_KINDS.join(", ")}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const rawKey = oneLine(String(input.key ?? ""));
|
|
56
|
+
const rawValue = oneLine(String(input.value ?? ""));
|
|
57
|
+
const key = oneLine(scrubSecrets(rawKey));
|
|
58
|
+
const value = oneLine(scrubSecrets(rawValue));
|
|
59
|
+
const redacted = key !== rawKey || value !== rawValue;
|
|
60
|
+
if (key.length === 0)
|
|
61
|
+
return { ok: false, error: "key is required" };
|
|
62
|
+
if (key.length > WORKING_STATE_MAX_KEY) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: `key is ${key.length} chars; max ${WORKING_STATE_MAX_KEY}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (value.length === 0)
|
|
69
|
+
return { ok: false, error: "value is required" };
|
|
70
|
+
if (value.length > WORKING_STATE_MAX_VALUE) {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
error: `value is ${value.length} chars; max ${WORKING_STATE_MAX_VALUE}. Shorten it.`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const replaced = this.entries.delete(key);
|
|
77
|
+
const dropped = [];
|
|
78
|
+
while (this.entries.size >= WORKING_STATE_MAX_ENTRIES) {
|
|
79
|
+
const victim = this.oldestOfKind("progress");
|
|
80
|
+
if (victim === undefined) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: `working state is full (${WORKING_STATE_MAX_ENTRIES} entries, none are progress). Delete entries first.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
this.entries.delete(victim);
|
|
87
|
+
dropped.push(victim);
|
|
88
|
+
}
|
|
89
|
+
const entry = {
|
|
90
|
+
key,
|
|
91
|
+
kind: input.kind,
|
|
92
|
+
value,
|
|
93
|
+
updatedAt: input.updatedAt ?? new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
this.entries.set(key, entry);
|
|
96
|
+
return { ok: true, dropped, replaced, redacted, entry };
|
|
97
|
+
}
|
|
98
|
+
delete(key) {
|
|
99
|
+
const raw = oneLine(String(key ?? ""));
|
|
100
|
+
return this.entries.delete(raw) || this.entries.delete(oneLine(scrubSecrets(raw)));
|
|
101
|
+
}
|
|
102
|
+
list() {
|
|
103
|
+
return [...this.entries.values()];
|
|
104
|
+
}
|
|
105
|
+
apply(event) {
|
|
106
|
+
if (event.op === "set") {
|
|
107
|
+
this.set({
|
|
108
|
+
key: event.key,
|
|
109
|
+
kind: event.kind,
|
|
110
|
+
value: event.value,
|
|
111
|
+
updatedAt: event.at,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (event.op === "delete") {
|
|
115
|
+
this.delete(event.key);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
load(events) {
|
|
119
|
+
this.entries.clear();
|
|
120
|
+
for (const event of events)
|
|
121
|
+
this.apply(event);
|
|
122
|
+
}
|
|
123
|
+
render(maxChars = WORKING_STATE_DEFAULT_RENDER_CHARS) {
|
|
124
|
+
const all = this.list();
|
|
125
|
+
if (all.length === 0)
|
|
126
|
+
return "";
|
|
127
|
+
const keep = new Set(all);
|
|
128
|
+
let omitted = 0;
|
|
129
|
+
const build = () => {
|
|
130
|
+
const lines = ["## Working state"];
|
|
131
|
+
for (const kind of RENDER_ORDER) {
|
|
132
|
+
const group = all.filter((e) => keep.has(e) && e.kind === kind);
|
|
133
|
+
if (group.length === 0)
|
|
134
|
+
continue;
|
|
135
|
+
lines.push("", `### ${HEADINGS[kind]}`);
|
|
136
|
+
for (const e of group)
|
|
137
|
+
lines.push(`- ${e.key}: ${e.value}`);
|
|
138
|
+
}
|
|
139
|
+
if (omitted > 0) {
|
|
140
|
+
lines.push("", `(${omitted} older entries omitted for space)`);
|
|
141
|
+
}
|
|
142
|
+
return lines.join("\n");
|
|
143
|
+
};
|
|
144
|
+
let text = build();
|
|
145
|
+
for (const kind of EVICTION_ORDER) {
|
|
146
|
+
for (const e of all) {
|
|
147
|
+
if (text.length <= maxChars)
|
|
148
|
+
return text;
|
|
149
|
+
if (e.kind !== kind)
|
|
150
|
+
continue;
|
|
151
|
+
keep.delete(e);
|
|
152
|
+
omitted += 1;
|
|
153
|
+
text = build();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return text.length <= maxChars ? text : text.slice(0, Math.max(0, maxChars));
|
|
157
|
+
}
|
|
158
|
+
oldestOfKind(kind) {
|
|
159
|
+
for (const e of this.entries.values()) {
|
|
160
|
+
if (e.kind === kind)
|
|
161
|
+
return e.key;
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
export function reduceWorkingState(events) {
|
|
167
|
+
const store = new WorkingStateStore();
|
|
168
|
+
for (const event of events)
|
|
169
|
+
store.apply(event);
|
|
170
|
+
return store;
|
|
171
|
+
}
|
|
172
|
+
function isEvent(data) {
|
|
173
|
+
if (data === null || typeof data !== "object")
|
|
174
|
+
return false;
|
|
175
|
+
const d = data;
|
|
176
|
+
if (typeof d.key !== "string")
|
|
177
|
+
return false;
|
|
178
|
+
if (d.op === "delete")
|
|
179
|
+
return true;
|
|
180
|
+
return d.op === "set" && isKind(d.kind) && typeof d.value === "string";
|
|
181
|
+
}
|
|
182
|
+
export function workingStateEventsFromEntries(entries) {
|
|
183
|
+
const events = [];
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
if (entry.type === "custom" &&
|
|
186
|
+
entry.customType === WORKING_STATE_CUSTOM_TYPE &&
|
|
187
|
+
isEvent(entry.data)) {
|
|
188
|
+
events.push(entry.data);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return events;
|
|
192
|
+
}
|
|
193
|
+
export function restoreWorkingState(entries) {
|
|
194
|
+
return reduceWorkingState(workingStateEventsFromEntries(entries));
|
|
195
|
+
}
|
|
196
|
+
export function getWorkingStateSections(entries) {
|
|
197
|
+
return restoreWorkingState(entries).list();
|
|
198
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, mkdir, readdir, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { ensureCatalogScanRoot } from "./ensureCatalogScanRoot.js";
|
|
3
4
|
import { invertPredecessorEdgesToRoute, invertRouteToPredecessorEdges, parsePipelineNeeds, toPipelineNeeds, } from "./pipelineNeeds.js";
|
|
4
5
|
import { STAGE_ID_PATTERN } from "./createStage.js";
|
|
5
6
|
import { inferIdFromUsesPath, normalizePipelineStageEntries, toWiringRefs, } from "./normalizePipelineStageEntry.js";
|
|
@@ -548,6 +549,7 @@ export async function createPipeline(projectRoot, input) {
|
|
|
548
549
|
error: errorFinding?.message ?? "Pipeline validation failed",
|
|
549
550
|
};
|
|
550
551
|
}
|
|
552
|
+
await ensureCatalogScanRoot(projectRoot, path.relative(projectRoot, pipelineDirectory));
|
|
551
553
|
const loadOutcome = await loadPipelineOutcome(relPath, { cwd: projectRoot });
|
|
552
554
|
if (!loadOutcome.ok) {
|
|
553
555
|
await unlink(filePath).catch(() => { });
|
|
@@ -6,6 +6,7 @@ import { STAGE_ID_PATTERN } from "./createStage.js";
|
|
|
6
6
|
import { getCatalogScanPaths } from "./browseCatalog.js";
|
|
7
7
|
import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
|
|
8
8
|
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
9
|
+
import { githubMatchActionError } from "./githubTriggerAction.js";
|
|
9
10
|
import { loadTriggerOutcome } from "./loadTrigger.js";
|
|
10
11
|
import { collectPipelineIdsFromPaths, collectTaskIdsFromPaths } from "./validateCatalog.js";
|
|
11
12
|
import { findCatalogPathById } from "../runtime/triggerCatalog.js";
|
|
@@ -56,11 +57,16 @@ export function parseEventField(raw) {
|
|
|
56
57
|
if (raw.config !== undefined && !isPlainObject(raw.config)) {
|
|
57
58
|
return { ok: false, message: "event.config must be an object" };
|
|
58
59
|
}
|
|
60
|
+
const match = isPlainObject(raw.match) ? raw.match : undefined;
|
|
61
|
+
const actionError = githubMatchActionError(raw.source, match);
|
|
62
|
+
if (actionError !== undefined) {
|
|
63
|
+
return { ok: false, message: actionError };
|
|
64
|
+
}
|
|
59
65
|
return {
|
|
60
66
|
ok: true,
|
|
61
67
|
value: {
|
|
62
68
|
source: raw.source,
|
|
63
|
-
...(
|
|
69
|
+
...(match !== undefined ? { match } : {}),
|
|
64
70
|
...(isPlainObject(raw.config) ? { config: raw.config } : {}),
|
|
65
71
|
},
|
|
66
72
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CatalogRootKind } from "./resolveCatalogRoots.js";
|
|
2
2
|
/** Directory scanned for workshop-authored pipelines and tasks. */
|
|
3
3
|
export declare const WORKSHOP_PACKAGE_ROOT = "workshop";
|
|
4
|
+
/** Directory the Guide always uses for pipeline packages and their stage files. */
|
|
5
|
+
export declare const GUIDE_PIPELINES_ROOT = "pipelines";
|
|
4
6
|
export declare function normalizeCatalogDir(directory: string): string;
|
|
5
7
|
/** Untitled workshop saves land here so Run can see them without a new catalog edit. */
|
|
6
8
|
export declare function defaultWorkshopPackageDirectory(pipelineId: string | undefined, catalogRootKind?: CatalogRootKind): string;
|
|
@@ -3,6 +3,8 @@ import { isSeq, parseDocument } from "yaml";
|
|
|
3
3
|
import { manifestPathForProject } from "./loadStageflowManifest.js";
|
|
4
4
|
/** Directory scanned for workshop-authored pipelines and tasks. */
|
|
5
5
|
export const WORKSHOP_PACKAGE_ROOT = "workshop";
|
|
6
|
+
/** Directory the Guide always uses for pipeline packages and their stage files. */
|
|
7
|
+
export const GUIDE_PIPELINES_ROOT = "pipelines";
|
|
6
8
|
export function normalizeCatalogDir(directory) {
|
|
7
9
|
return directory.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
8
10
|
}
|
|
@@ -30,6 +32,10 @@ export function catalogScanRootForDirectory(directory) {
|
|
|
30
32
|
normalized.startsWith(`${WORKSHOP_PACKAGE_ROOT}/`)) {
|
|
31
33
|
return WORKSHOP_PACKAGE_ROOT;
|
|
32
34
|
}
|
|
35
|
+
if (normalized === GUIDE_PIPELINES_ROOT ||
|
|
36
|
+
normalized.startsWith(`${GUIDE_PIPELINES_ROOT}/`)) {
|
|
37
|
+
return GUIDE_PIPELINES_ROOT;
|
|
38
|
+
}
|
|
33
39
|
return normalized;
|
|
34
40
|
}
|
|
35
41
|
function isCovered(entries, root) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { isInsideProjectRoot } from "./catalogRelativePath.js";
|
|
3
4
|
import { getModelCatalog } from "../models/catalog.js";
|
|
4
5
|
import { loadDatasetFile } from "./loadDataset.js";
|
|
5
6
|
import { loadEvalOutcome, resolveEvalPaths } from "./loadEval.js";
|
|
@@ -15,8 +16,35 @@ async function listFiles(manifest, kind) {
|
|
|
15
16
|
export function listEvalFiles(manifest) {
|
|
16
17
|
return listFiles(manifest, "eval");
|
|
17
18
|
}
|
|
18
|
-
export function listDatasetFiles(manifest) {
|
|
19
|
-
|
|
19
|
+
export async function listDatasetFiles(manifest) {
|
|
20
|
+
const scanned = await listFiles(manifest, "dataset");
|
|
21
|
+
const referenced = await datasetsReferencedByEvals(manifest, scanned);
|
|
22
|
+
return [...scanned, ...referenced].sort((a, b) => (a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0));
|
|
23
|
+
}
|
|
24
|
+
async function datasetsReferencedByEvals(manifest, scanned) {
|
|
25
|
+
const seen = new Set(scanned.map((ref) => path.resolve(ref.absPath)));
|
|
26
|
+
const extra = [];
|
|
27
|
+
for (const ref of await listFiles(manifest, "eval")) {
|
|
28
|
+
const outcome = await loadEvalOutcome(ref.absPath, manifest.projectRoot);
|
|
29
|
+
if (!outcome.ok)
|
|
30
|
+
continue;
|
|
31
|
+
const abs = path.resolve(resolveEvalPaths(outcome.value.file, outcome.value.path).datasetPath);
|
|
32
|
+
if (seen.has(abs) || !isInsideProjectRoot(manifest.projectRoot, abs))
|
|
33
|
+
continue;
|
|
34
|
+
try {
|
|
35
|
+
if (!(await stat(abs)).isFile())
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
seen.add(abs);
|
|
42
|
+
extra.push({
|
|
43
|
+
absPath: abs,
|
|
44
|
+
relPath: path.relative(manifest.projectRoot, abs).split(path.sep).join("/"),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return extra;
|
|
20
48
|
}
|
|
21
49
|
export async function loadEvalCatalog(manifest, options = {}) {
|
|
22
50
|
const refs = await listEvalFiles(manifest);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const GITHUB_TRIGGER_ACTIONS: readonly ["opened", "synchronize", "updated", "merged", "closed"];
|
|
2
|
+
export type GithubTriggerAction = (typeof GITHUB_TRIGGER_ACTIONS)[number];
|
|
3
|
+
export declare function isGithubTriggerAction(value: unknown): value is GithubTriggerAction;
|
|
4
|
+
/**
|
|
5
|
+
* For `event.source` values starting with `github.`, `match.action` must be set
|
|
6
|
+
* to one of the five PR poll actions. Returns an error message or undefined.
|
|
7
|
+
*/
|
|
8
|
+
export declare function githubMatchActionError(source: string, match: Record<string, unknown> | undefined): string | undefined;
|