stageflow 0.27.0 → 0.31.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 +4 -0
- package/builtin-skills/browser/SKILL.md +42 -0
- package/dist/agent/claudeAdapter.js +2 -2
- package/dist/agent/cursorProvider.d.ts +10 -0
- package/dist/agent/cursorProvider.js +83 -8
- package/dist/agent/fakeAgent.d.ts +1 -0
- package/dist/agent/fakeAgent.js +13 -0
- package/dist/agent/piAdapter.d.ts +3 -24
- package/dist/agent/piAdapter.js +49 -47
- package/dist/agent/piSessionFactory.d.ts +38 -0
- package/dist/agent/piSessionFactory.js +60 -0
- package/dist/agent/port.d.ts +2 -0
- package/dist/agent/providerAuth.d.ts +7 -3
- package/dist/agent/providerAuth.js +5 -3
- package/dist/agent/streamLogRedact.d.ts +6 -0
- package/dist/agent/streamLogRedact.js +23 -1
- package/dist/browser/anchor.d.ts +27 -0
- package/dist/browser/anchor.js +127 -0
- package/dist/browser/auditSink.d.ts +38 -0
- package/dist/browser/auditSink.js +34 -0
- package/dist/browser/browserHost.d.ts +86 -0
- package/dist/browser/browserHost.js +7 -0
- package/dist/browser/browserSweep.d.ts +14 -0
- package/dist/browser/browserSweep.js +54 -0
- package/dist/browser/browserTeardown.d.ts +56 -0
- package/dist/browser/browserTeardown.js +235 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
- package/dist/browser/fakeRemoteBrowserHost.js +34 -0
- package/dist/browser/gateHandoff.d.ts +25 -0
- package/dist/browser/gateHandoff.js +83 -0
- package/dist/browser/humanLogin.d.ts +18 -0
- package/dist/browser/humanLogin.js +34 -0
- package/dist/browser/keyProvider.d.ts +10 -0
- package/dist/browser/keyProvider.js +39 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +152 -0
- package/dist/browser/localProfileLock.d.ts +2 -0
- package/dist/browser/localProfileLock.js +218 -0
- package/dist/browser/localProfileStore.d.ts +5 -0
- package/dist/browser/localProfileStore.js +91 -0
- package/dist/browser/loginCheck.d.ts +53 -0
- package/dist/browser/loginCheck.js +147 -0
- package/dist/browser/memoryProfileLock.d.ts +2 -0
- package/dist/browser/memoryProfileLock.js +53 -0
- package/dist/browser/memoryProfileStore.d.ts +2 -0
- package/dist/browser/memoryProfileStore.js +31 -0
- package/dist/browser/navigationAudit.d.ts +16 -0
- package/dist/browser/navigationAudit.js +53 -0
- package/dist/browser/persistedEnv.d.ts +3 -0
- package/dist/browser/persistedEnv.js +15 -0
- package/dist/browser/profileLock.d.ts +40 -0
- package/dist/browser/profileLock.js +10 -0
- package/dist/browser/profileStore.d.ts +23 -0
- package/dist/browser/profileStore.js +39 -0
- package/dist/browser/runLiveness.d.ts +13 -0
- package/dist/browser/runLiveness.js +39 -0
- package/dist/browser/sitePolicy.d.ts +10 -0
- package/dist/browser/sitePolicy.js +61 -0
- package/dist/browser/stageBrowserEnv.d.ts +22 -0
- package/dist/browser/stageBrowserEnv.js +147 -0
- package/dist/browser/stageProfileLock.d.ts +17 -0
- package/dist/browser/stageProfileLock.js +39 -0
- package/dist/cli/browserCommand.d.ts +44 -0
- package/dist/cli/browserCommand.js +472 -0
- package/dist/cli/doctorCommand.js +7 -0
- package/dist/cli/hostClient.d.ts +20 -0
- package/dist/cli/hostClient.js +36 -2
- package/dist/cli/initCommand.d.ts +1 -0
- package/dist/cli/initCommand.js +23 -7
- package/dist/cli/initTemplates.d.ts +5 -2
- package/dist/cli/initTemplates.js +25 -2
- package/dist/cli/providersCommand.d.ts +1 -1
- package/dist/cli/providersCommand.js +4 -4
- package/dist/cli/runsCommand.js +6 -2
- package/dist/cli/triggerCommand.d.ts +12 -0
- package/dist/cli/triggerCommand.js +183 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +23 -6
- package/dist/cli.js +32 -3
- package/dist/config/browseCatalog.d.ts +1 -0
- package/dist/config/browseCatalog.js +5 -4
- package/dist/config/builtinSkills.d.ts +4 -0
- package/dist/config/builtinSkills.js +23 -0
- package/dist/config/catalogRelativePath.d.ts +1 -0
- package/dist/config/catalogRelativePath.js +10 -2
- package/dist/config/createTrigger.d.ts +27 -0
- package/dist/config/createTrigger.js +232 -0
- package/dist/config/draftPackage.d.ts +117 -0
- package/dist/config/draftPackage.js +550 -0
- package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
- package/dist/config/ensureCatalogScanRoot.js +73 -0
- package/dist/config/hostConfig.d.ts +2 -0
- package/dist/config/hostConfig.js +27 -0
- package/dist/config/listSkills.d.ts +2 -2
- package/dist/config/listSkills.js +17 -3
- package/dist/config/loadOutcome.d.ts +6 -0
- package/dist/config/loadPipeline.js +16 -0
- package/dist/config/loadStage.js +7 -0
- package/dist/config/loadStageflowManifest.js +25 -0
- package/dist/config/loadTrigger.d.ts +9 -0
- package/dist/config/loadTrigger.js +120 -0
- package/dist/config/multiProjectCatalog.d.ts +2 -0
- package/dist/config/multiProjectCatalog.js +26 -18
- package/dist/config/normalizePipelineStageEntry.js +8 -1
- package/dist/config/parseCompletionContract.js +6 -0
- package/dist/config/pipelineStageKeys.js +1 -0
- package/dist/config/publishDraftPackage.d.ts +7 -0
- package/dist/config/publishDraftPackage.js +25 -0
- package/dist/config/resolveCatalogContext.js +1 -1
- package/dist/config/resolvePipelineDag.js +1 -1
- package/dist/config/scanCatalogPaths.d.ts +1 -1
- package/dist/config/scanCatalogPaths.js +6 -2
- package/dist/config/stageBrowser.d.ts +12 -0
- package/dist/config/stageBrowser.js +118 -0
- package/dist/config/triggerCatalog.d.ts +10 -0
- package/dist/config/triggerCatalog.js +37 -0
- package/dist/config/validateCatalog.d.ts +13 -3
- package/dist/config/validateCatalog.js +83 -2
- package/dist/config/validateCompletionContract.js +8 -0
- package/dist/envelope/preEmitChecks.d.ts +1 -1
- package/dist/envelope/preEmitChecks.js +8 -0
- package/dist/logging/redact.d.ts +4 -0
- package/dist/logging/redact.js +17 -0
- package/dist/mcp/catalogTools.js +43 -8
- package/dist/mcp/controlTools.js +3 -1
- package/dist/mcp/deps.d.ts +2 -0
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/tools.js +2 -0
- package/dist/mcp/triggerTools.d.ts +3 -0
- package/dist/mcp/triggerTools.js +176 -0
- package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
- package/dist/operatorAgent/docsRetrieval.js +269 -0
- package/dist/operatorAgent/draftContext.d.ts +16 -0
- package/dist/operatorAgent/draftContext.js +68 -0
- package/dist/operatorAgent/fakeHost.d.ts +39 -0
- package/dist/operatorAgent/fakeHost.js +92 -0
- package/dist/operatorAgent/host.d.ts +40 -0
- package/dist/operatorAgent/host.js +179 -0
- package/dist/operatorAgent/index.d.ts +9 -0
- package/dist/operatorAgent/index.js +8 -0
- package/dist/operatorAgent/piModel.d.ts +80 -0
- package/dist/operatorAgent/piModel.js +528 -0
- package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
- package/dist/operatorAgent/proposals.d.ts +32 -0
- package/dist/operatorAgent/proposals.js +144 -0
- package/dist/operatorAgent/types.d.ts +128 -0
- package/dist/operatorAgent/types.js +1 -0
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/project/findManifestRoot.d.ts +3 -0
- package/dist/project/findManifestRoot.js +35 -0
- package/dist/project/resolveStageflowContext.d.ts +7 -0
- package/dist/project/resolveStageflowContext.js +44 -28
- package/dist/runstore/assertTmpdir.js +6 -2
- package/dist/runstore/diskUsage.d.ts +1 -1
- package/dist/runstore/diskUsage.js +2 -7
- package/dist/runstore/port.d.ts +28 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +90 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
- package/dist/runstore/sqlite/migrations/index.js +6 -0
- package/dist/runtime/answerResume.d.ts +1 -0
- package/dist/runtime/answerResume.js +3 -1
- package/dist/runtime/completionCheckRunner.d.ts +12 -1
- package/dist/runtime/completionCheckRunner.js +22 -0
- package/dist/runtime/credentialBinding.d.ts +7 -2
- package/dist/runtime/credentialBinding.js +20 -28
- package/dist/runtime/emailSource.d.ts +72 -0
- package/dist/runtime/emailSource.js +304 -0
- package/dist/runtime/githubPollSource.d.ts +86 -0
- package/dist/runtime/githubPollSource.js +284 -0
- package/dist/runtime/pipelineRunner.d.ts +4 -0
- package/dist/runtime/pipelineRunner.js +3 -0
- package/dist/runtime/pipelineScheduler.d.ts +2 -0
- package/dist/runtime/pipelineScheduler.js +86 -1
- package/dist/runtime/repositoryMaterialize.d.ts +1 -1
- package/dist/runtime/runManager.d.ts +11 -0
- package/dist/runtime/runManager.js +161 -3
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +1 -0
- package/dist/runtime/scheduleSource.d.ts +38 -0
- package/dist/runtime/scheduleSource.js +119 -0
- package/dist/runtime/settingsFile.d.ts +7 -2
- package/dist/runtime/settingsFile.js +66 -56
- package/dist/runtime/stageAttemptBootstrap.js +55 -3
- package/dist/runtime/stageEnvironment.js +1 -0
- package/dist/runtime/stageHitl.d.ts +1 -0
- package/dist/runtime/stageHitl.js +5 -1
- package/dist/runtime/stageProcessLauncher.d.ts +3 -0
- package/dist/runtime/stageProcessLauncher.js +52 -5
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +30 -6
- package/dist/runtime/stageStreamLog.d.ts +1 -0
- package/dist/runtime/stageStreamLog.js +2 -1
- package/dist/runtime/triggerCatalog.d.ts +6 -0
- package/dist/runtime/triggerCatalog.js +24 -0
- package/dist/runtime/triggerEventMatch.d.ts +2 -0
- package/dist/runtime/triggerEventMatch.js +6 -0
- package/dist/runtime/triggerPort.d.ts +14 -0
- package/dist/runtime/triggerPort.js +1 -0
- package/dist/runtime/triggerRunner.d.ts +17 -0
- package/dist/runtime/triggerRunner.js +89 -0
- package/dist/runtime/verifiedStageExecution.d.ts +7 -0
- package/dist/runtime/verifiedStageExecution.js +14 -0
- package/dist/runtime/webhookSignature.d.ts +1 -0
- package/dist/runtime/webhookSignature.js +12 -0
- package/dist/server/bootstrap.d.ts +6 -0
- package/dist/server/bootstrap.js +70 -0
- package/dist/server/controlToken.js +13 -0
- package/dist/server/createHttpHost.js +3 -0
- package/dist/server/healthSurfaces.js +31 -6
- package/dist/server/http.d.ts +15 -0
- package/dist/server/http.js +1251 -74
- package/dist/server/requestAuthContext.d.ts +1 -1
- package/dist/server/triggerRoutes.d.ts +15 -0
- package/dist/server/triggerRoutes.js +268 -0
- package/dist/tools/askOperator.d.ts +3 -2
- package/dist/types/completion.d.ts +3 -0
- package/dist/types/pipeline.d.ts +3 -1
- package/dist/types/preEmitCheck.d.ts +7 -0
- package/dist/types/stage.d.ts +14 -0
- package/dist/types/stageflowManifest.d.ts +3 -0
- package/dist/types/trigger.d.ts +18 -0
- package/dist/types/trigger.js +1 -0
- package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
- package/dist/ui/assets/index-p8Iou3zk.js +133 -0
- package/dist/ui/index.html +2 -2
- package/dist/workshop/autosave.d.ts +58 -0
- package/dist/workshop/autosave.js +222 -0
- package/dist/workshop/buildStore.d.ts +79 -0
- package/dist/workshop/buildStore.js +326 -0
- package/dist/workshop/chatTurn.d.ts +232 -0
- package/dist/workshop/chatTurn.js +475 -0
- package/dist/workshop/modelSettings.d.ts +18 -0
- package/dist/workshop/modelSettings.js +44 -0
- package/dist/workshop/sessionStore.d.ts +61 -0
- package/dist/workshop/sessionStore.js +254 -0
- package/dist/workshop/workshopPin.d.ts +72 -0
- package/dist/workshop/workshopPin.js +193 -0
- package/examples/README.md +2 -1
- package/examples/browser-session/README.md +77 -0
- package/examples/browser-session/browser-session.pipeline.yaml +50 -0
- package/examples/browser-session/browser-session.task.yaml +6 -0
- package/examples/browser-session/check-login.yaml +22 -0
- package/examples/browser-session/fixture-server.mjs +64 -0
- package/examples/browser-session/human-login.yaml +31 -0
- package/examples/browser-session/merge.yaml +36 -0
- package/examples/browser-session/work-a.yaml +41 -0
- package/examples/browser-session/work-b.yaml +41 -0
- package/examples/conditional-fork/decide.yaml +1 -1
- package/examples/github-release/README.md +17 -3
- package/examples/stage-mcp/README.md +3 -2
- package/examples/stageflow.yaml +51 -0
- package/package.json +9 -4
- package/skills/stageflow/references/control-surface.md +3 -1
- package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
- package/skills/stageflow-run/SKILL.md +2 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +3 -1
- package/skills/stageflow-setup/SKILL.md +11 -0
- package/dist/ui/assets/index-B6hlSXXn.js +0 -120
|
@@ -5,7 +5,8 @@ import { globalStageflowHome } from "../project/globalHome.js";
|
|
|
5
5
|
import { storeRootFor } from "../runstore/paths.js";
|
|
6
6
|
export const SETTINGS_FILE_NAME = "settings.json";
|
|
7
7
|
export const INVALID_SLOT_COUNT_MESSAGE = "maxConcurrent must be an integer >= 1";
|
|
8
|
-
export const INVALID_CREDENTIAL_SOURCE_MESSAGE = 'credentialSource must be "
|
|
8
|
+
export const INVALID_CREDENTIAL_SOURCE_MESSAGE = 'credentialSource must be "sf_owned"';
|
|
9
|
+
export const INVALID_WORKSHOP_MODEL_MESSAGE = "workshopModel must be a non-empty string";
|
|
9
10
|
export function globalSettingsFilePath() {
|
|
10
11
|
return path.join(globalStageflowHome(), SETTINGS_FILE_NAME);
|
|
11
12
|
}
|
|
@@ -28,11 +29,20 @@ export function parseSlotCount(value) {
|
|
|
28
29
|
return value;
|
|
29
30
|
}
|
|
30
31
|
export function parseCredentialSource(value) {
|
|
31
|
-
if (value === "
|
|
32
|
+
if (value === "sf_owned") {
|
|
32
33
|
return value;
|
|
33
34
|
}
|
|
35
|
+
if (value === "pi_home") {
|
|
36
|
+
return "sf_owned";
|
|
37
|
+
}
|
|
34
38
|
return undefined;
|
|
35
39
|
}
|
|
40
|
+
export function parseWorkshopModelSetting(value) {
|
|
41
|
+
if (typeof value !== "string")
|
|
42
|
+
return undefined;
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
45
|
+
}
|
|
36
46
|
function readRawSettingsFromFile(filePath) {
|
|
37
47
|
if (!existsSync(filePath))
|
|
38
48
|
return {};
|
|
@@ -60,8 +70,50 @@ function parseFactorySettingsFromRaw(raw) {
|
|
|
60
70
|
if (credentialSource !== undefined) {
|
|
61
71
|
settings.credentialSource = credentialSource;
|
|
62
72
|
}
|
|
73
|
+
const workshopModel = parseWorkshopModelSetting(raw.workshopModel);
|
|
74
|
+
if (workshopModel !== undefined) {
|
|
75
|
+
settings.workshopModel = workshopModel;
|
|
76
|
+
}
|
|
63
77
|
return settings;
|
|
64
78
|
}
|
|
79
|
+
function serializeFactorySettings(next) {
|
|
80
|
+
const out = {};
|
|
81
|
+
if (next.maxConcurrent !== undefined) {
|
|
82
|
+
out.maxConcurrent = next.maxConcurrent;
|
|
83
|
+
}
|
|
84
|
+
if (next.credentialSource !== undefined) {
|
|
85
|
+
out.credentialSource = next.credentialSource;
|
|
86
|
+
}
|
|
87
|
+
if (next.workshopModel !== undefined) {
|
|
88
|
+
out.workshopModel = next.workshopModel;
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
function applyFactorySettingsPatch(current, patch) {
|
|
93
|
+
if (patch.credentialSource !== undefined &&
|
|
94
|
+
parseCredentialSource(patch.credentialSource) === undefined) {
|
|
95
|
+
throw new Error(INVALID_CREDENTIAL_SOURCE_MESSAGE);
|
|
96
|
+
}
|
|
97
|
+
if (patch.maxConcurrent !== undefined &&
|
|
98
|
+
parseSlotCount(patch.maxConcurrent) === undefined) {
|
|
99
|
+
throw new Error(INVALID_SLOT_COUNT_MESSAGE);
|
|
100
|
+
}
|
|
101
|
+
if (patch.workshopModel !== undefined &&
|
|
102
|
+
parseWorkshopModelSetting(patch.workshopModel) === undefined) {
|
|
103
|
+
throw new Error(INVALID_WORKSHOP_MODEL_MESSAGE);
|
|
104
|
+
}
|
|
105
|
+
const next = { ...current };
|
|
106
|
+
if (patch.maxConcurrent !== undefined) {
|
|
107
|
+
next.maxConcurrent = patch.maxConcurrent;
|
|
108
|
+
}
|
|
109
|
+
if (patch.credentialSource !== undefined) {
|
|
110
|
+
next.credentialSource = patch.credentialSource;
|
|
111
|
+
}
|
|
112
|
+
if (patch.workshopModel !== undefined) {
|
|
113
|
+
next.workshopModel = parseWorkshopModelSetting(patch.workshopModel);
|
|
114
|
+
}
|
|
115
|
+
return next;
|
|
116
|
+
}
|
|
65
117
|
export function readCredentialSourceFromContext(ctx) {
|
|
66
118
|
if (ctx.isGitProject) {
|
|
67
119
|
const fromProject = parseCredentialSource(readRawSettingsFromFile(settingsFilePathForContext(ctx)).credentialSource);
|
|
@@ -84,56 +136,14 @@ function ensureSettingsDirForContext(ctx) {
|
|
|
84
136
|
mkdirSync(path.dirname(settingsFilePathForContext(ctx)), { recursive: true });
|
|
85
137
|
}
|
|
86
138
|
export function writeFactorySettingsForContext(ctx, patch) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
throw new Error(INVALID_CREDENTIAL_SOURCE_MESSAGE);
|
|
90
|
-
}
|
|
91
|
-
if (patch.maxConcurrent !== undefined &&
|
|
92
|
-
parseSlotCount(patch.maxConcurrent) === undefined) {
|
|
93
|
-
throw new Error(INVALID_SLOT_COUNT_MESSAGE);
|
|
94
|
-
}
|
|
95
|
-
const current = readFactorySettingsForContext(ctx);
|
|
96
|
-
const next = { ...current };
|
|
97
|
-
if (patch.maxConcurrent !== undefined) {
|
|
98
|
-
next.maxConcurrent = patch.maxConcurrent;
|
|
99
|
-
}
|
|
100
|
-
if (patch.credentialSource !== undefined) {
|
|
101
|
-
next.credentialSource = patch.credentialSource;
|
|
102
|
-
}
|
|
103
|
-
const out = {};
|
|
104
|
-
if (next.maxConcurrent !== undefined) {
|
|
105
|
-
out.maxConcurrent = next.maxConcurrent;
|
|
106
|
-
}
|
|
107
|
-
if (next.credentialSource !== undefined) {
|
|
108
|
-
out.credentialSource = next.credentialSource;
|
|
109
|
-
}
|
|
139
|
+
const next = applyFactorySettingsPatch(readFactorySettingsForContext(ctx), patch);
|
|
140
|
+
const out = serializeFactorySettings(next);
|
|
110
141
|
ensureSettingsDirForContext(ctx);
|
|
111
142
|
writeFileSync(settingsFilePathForContext(ctx), `${JSON.stringify(out, null, 2)}\n`, "utf8");
|
|
112
143
|
}
|
|
113
144
|
export function writeFactorySettings(cwd, patch) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
throw new Error(INVALID_CREDENTIAL_SOURCE_MESSAGE);
|
|
117
|
-
}
|
|
118
|
-
if (patch.maxConcurrent !== undefined &&
|
|
119
|
-
parseSlotCount(patch.maxConcurrent) === undefined) {
|
|
120
|
-
throw new Error(INVALID_SLOT_COUNT_MESSAGE);
|
|
121
|
-
}
|
|
122
|
-
const current = readFactorySettings(cwd);
|
|
123
|
-
const next = { ...current };
|
|
124
|
-
if (patch.maxConcurrent !== undefined) {
|
|
125
|
-
next.maxConcurrent = patch.maxConcurrent;
|
|
126
|
-
}
|
|
127
|
-
if (patch.credentialSource !== undefined) {
|
|
128
|
-
next.credentialSource = patch.credentialSource;
|
|
129
|
-
}
|
|
130
|
-
const out = {};
|
|
131
|
-
if (next.maxConcurrent !== undefined) {
|
|
132
|
-
out.maxConcurrent = next.maxConcurrent;
|
|
133
|
-
}
|
|
134
|
-
if (next.credentialSource !== undefined) {
|
|
135
|
-
out.credentialSource = next.credentialSource;
|
|
136
|
-
}
|
|
145
|
+
const next = applyFactorySettingsPatch(readFactorySettings(cwd), patch);
|
|
146
|
+
const out = serializeFactorySettings(next);
|
|
137
147
|
const root = path.resolve(cwd);
|
|
138
148
|
mkdirSync(storeRootFor(root), { recursive: true });
|
|
139
149
|
writeFileSync(settingsFilePath(cwd), `${JSON.stringify(out, null, 2)}\n`, "utf8");
|
|
@@ -158,17 +168,17 @@ export function writeMaxConcurrentToGlobal(maxConcurrent) {
|
|
|
158
168
|
throw new Error(INVALID_SLOT_COUNT_MESSAGE);
|
|
159
169
|
}
|
|
160
170
|
const current = parseFactorySettingsFromRaw(readRawSettingsFromFile(globalSettingsFilePath()));
|
|
161
|
-
const next =
|
|
162
|
-
const out =
|
|
163
|
-
if (next.maxConcurrent !== undefined) {
|
|
164
|
-
out.maxConcurrent = next.maxConcurrent;
|
|
165
|
-
}
|
|
166
|
-
if (next.credentialSource !== undefined) {
|
|
167
|
-
out.credentialSource = next.credentialSource;
|
|
168
|
-
}
|
|
171
|
+
const next = applyFactorySettingsPatch(current, { maxConcurrent });
|
|
172
|
+
const out = serializeFactorySettings(next);
|
|
169
173
|
mkdirSync(path.dirname(globalSettingsFilePath()), { recursive: true });
|
|
170
174
|
writeFileSync(globalSettingsFilePath(), `${JSON.stringify(out, null, 2)}\n`, "utf8");
|
|
171
175
|
}
|
|
176
|
+
export function readWorkshopModelFromFile(cwd) {
|
|
177
|
+
return readFactorySettings(cwd).workshopModel;
|
|
178
|
+
}
|
|
179
|
+
export function writeWorkshopModelToFile(cwd, workshopModel) {
|
|
180
|
+
writeFactorySettings(cwd, { workshopModel });
|
|
181
|
+
}
|
|
172
182
|
export function readCredentialSourceFromFile(cwd) {
|
|
173
183
|
return readFactorySettings(cwd).credentialSource;
|
|
174
184
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { bindingKindForOrigin, catalogOrSeededOrigin, decideWorkspaceConfigTrust, } from "../config/configOrigin.js";
|
|
2
|
+
import { loginCheckPromptBlock, readStageLoginCheck, } from "../browser/loginCheck.js";
|
|
3
|
+
import { humanLoginPromptBlock, isHumanLoginStage } from "../browser/humanLogin.js";
|
|
4
|
+
import { BROWSER_SKILL_NAME, resolveBuiltinSkillFile, } from "../config/builtinSkills.js";
|
|
2
5
|
import { resolveSkillByName, } from "../config/listSkills.js";
|
|
3
6
|
import { loadHostConfig } from "../config/hostConfig.js";
|
|
4
7
|
import { defaultSeededRoots } from "../config/seededCatalog.js";
|
|
@@ -7,6 +10,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
7
10
|
import { runSkillsDir } from "./runSkills.js";
|
|
8
11
|
import { MCP_CATALOG_FILENAME, STAGEFLOW_STAGE_ARTIFACTS_DIR_ENV, StageMcpError, mcpCatalogExists, mcpCatalogPath, resolveStageMcpServers, stampStagePromptArtifactsDir, } from "../config/resolveStageMcpServers.js";
|
|
9
12
|
import { attemptArtifactsDir, attemptStreamLogPath } from "../runstore/workspaceLayout.js";
|
|
13
|
+
import { redactBrowserActivityEvent, redactBrowserSecrets, } from "../agent/streamLogRedact.js";
|
|
10
14
|
import { createStageStreamLogWriter, } from "./stageStreamLog.js";
|
|
11
15
|
import { resolveCloneEmitContext, resolveForkEmitContext } from "../config/resolveForkEmitContext.js";
|
|
12
16
|
import { createAttemptQaTrailReader } from "../hitl/qaTrail.js";
|
|
@@ -68,6 +72,17 @@ async function resolveStageSkillForRun(stage, catalog, options) {
|
|
|
68
72
|
skillOrigin: resolved.origin,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
75
|
+
/** Browser stages get the `browser` skill (run > checkout > host > built-in). A missing skill is not an error. */
|
|
76
|
+
async function resolveBrowserSkillFile(stage, catalog, options) {
|
|
77
|
+
if (stage.browser === undefined || stage.skill === BROWSER_SKILL_NAME) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const resolved = await resolveStageSkillForRun({ skill: BROWSER_SKILL_NAME }, catalog, options);
|
|
81
|
+
if (resolved.ok && resolved.skillFilePath !== undefined) {
|
|
82
|
+
return resolved.skillFilePath;
|
|
83
|
+
}
|
|
84
|
+
return resolveBuiltinSkillFile(BROWSER_SKILL_NAME);
|
|
85
|
+
}
|
|
71
86
|
async function resolveAttemptMcpServers(allowlist, factoryCwd, artifactsDir, options) {
|
|
72
87
|
const names = allowlist ?? [];
|
|
73
88
|
if (names.length === 0)
|
|
@@ -151,6 +166,13 @@ async function openStageWithOperatorCatalog(agent, input, catalog, factoryCwd, a
|
|
|
151
166
|
});
|
|
152
167
|
if (!skill.ok)
|
|
153
168
|
return skill;
|
|
169
|
+
const browserSkillFilePath = await resolveBrowserSkillFile(input.stage, catalog, {
|
|
170
|
+
bindingKind: resolveOptions.bindingKind,
|
|
171
|
+
checkoutRoot: resolveOptions.checkoutRoot,
|
|
172
|
+
factoryCwd,
|
|
173
|
+
trustWorkspaceConfig: resolveOptions.trustWorkspaceConfig,
|
|
174
|
+
workspaceDir: resolveOptions.workspaceDir,
|
|
175
|
+
});
|
|
154
176
|
let resolvedMcpServers;
|
|
155
177
|
const origins = [];
|
|
156
178
|
try {
|
|
@@ -207,16 +229,41 @@ async function openStageWithOperatorCatalog(agent, input, catalog, factoryCwd, a
|
|
|
207
229
|
})).catch(() => null);
|
|
208
230
|
}
|
|
209
231
|
}
|
|
232
|
+
let loginCheck;
|
|
233
|
+
const humanLogin = isHumanLoginStage(resolveOptions.dag, input.stage.id, input.stage.browser);
|
|
234
|
+
if (input.stage.browser?.check !== undefined && !humanLogin) {
|
|
235
|
+
loginCheck = await readStageLoginCheck(resolveOptions.workspaceDir, stageIdForManifest);
|
|
236
|
+
if (loginCheck === undefined) {
|
|
237
|
+
return { ok: false, reason: "browser login check result is missing" };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
210
240
|
try {
|
|
211
241
|
const handle = agent.openStage({
|
|
212
242
|
...input,
|
|
213
243
|
stage: {
|
|
214
244
|
...input.stage,
|
|
215
|
-
system_prompt: stampStagePromptArtifactsDir(input.stage.system_prompt, artifactsDir)
|
|
245
|
+
system_prompt: `${stampStagePromptArtifactsDir(input.stage.system_prompt, artifactsDir)}${loginCheck !== undefined ? `\n\n${loginCheckPromptBlock(loginCheck)}` : ""}${humanLogin ? `\n\n${humanLoginPromptBlock(input.stage.browser?.login_url ?? input.stage.browser?.check?.url)}` : ""}`,
|
|
246
|
+
...(loginCheck !== undefined
|
|
247
|
+
? {
|
|
248
|
+
pre_emit_checks: [
|
|
249
|
+
...(input.stage.pre_emit_checks ?? []),
|
|
250
|
+
{
|
|
251
|
+
id: "browser_login_check",
|
|
252
|
+
type: "browser_login_check",
|
|
253
|
+
state: loginCheck.state,
|
|
254
|
+
logged_in: loginCheck.logged_in,
|
|
255
|
+
url: loginCheck.url,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
}
|
|
259
|
+
: {}),
|
|
216
260
|
},
|
|
217
261
|
...(skill.skillFilePath !== undefined
|
|
218
262
|
? { skillFilePath: skill.skillFilePath }
|
|
219
263
|
: {}),
|
|
264
|
+
...(browserSkillFilePath !== undefined
|
|
265
|
+
? { browserSkillFilePath }
|
|
266
|
+
: {}),
|
|
220
267
|
...(resolvedMcpServers !== undefined ? { resolvedMcpServers } : {}),
|
|
221
268
|
onResolvedModel: async (info) => {
|
|
222
269
|
await patchRunManifest(resolveOptions.store, resolveOptions.runId, (m) => withStageResolvedModel(m, info.stageId, {
|
|
@@ -357,7 +404,10 @@ export async function openStageAttempt(input) {
|
|
|
357
404
|
return priorInput;
|
|
358
405
|
const roots = resolveAttemptRoots(input, stageId);
|
|
359
406
|
const attempt = input.attemptCtx?.attempt ?? 1;
|
|
360
|
-
const
|
|
407
|
+
const streamLogPath = attemptStreamLogPath(input.workspaceDir, stageId, attempt);
|
|
408
|
+
const streamWriter = input.streamLogWriterFactory
|
|
409
|
+
? input.streamLogWriterFactory(streamLogPath)
|
|
410
|
+
: createStageStreamLogWriter(streamLogPath, input.stage.browser !== undefined ? { redact: redactBrowserSecrets } : {});
|
|
361
411
|
const resumeToken = input.resumeToken ??
|
|
362
412
|
resumeSessionFilePath(input.workspaceDir, stageId, attempt);
|
|
363
413
|
const forkEmitContext = resolveForkEmitContext(input.dag, definitionId);
|
|
@@ -394,7 +444,9 @@ export async function openStageAttempt(input) {
|
|
|
394
444
|
resumeToken,
|
|
395
445
|
...(input.sessionMode !== undefined ? { sessionMode: input.sessionMode } : {}),
|
|
396
446
|
onActivity: (event) => {
|
|
397
|
-
input.onActivity?.(
|
|
447
|
+
input.onActivity?.(input.stage.browser !== undefined
|
|
448
|
+
? redactBrowserActivityEvent(event)
|
|
449
|
+
: event);
|
|
398
450
|
void streamWriter.flush();
|
|
399
451
|
},
|
|
400
452
|
onAssistantTextDelta: streamWriter.onDelta,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseHostGateContext } from "../browser/gateHandoff.js";
|
|
1
2
|
import { appendOperatorAnswer, appendOperatorPrompt, derivePendingPrompt, } from "../hitl/qaTrail.js";
|
|
2
3
|
import { deriveExecutionPatchFromEvent } from "../runstore/stageExecution.js";
|
|
3
4
|
import { attemptContext } from "./stageAttemptContext.js";
|
|
@@ -7,7 +8,10 @@ export function tryParsePendingPrompt(pendingRequest) {
|
|
|
7
8
|
return undefined;
|
|
8
9
|
}
|
|
9
10
|
try {
|
|
10
|
-
return
|
|
11
|
+
return {
|
|
12
|
+
...normalizePromptIds(parseAskOperatorParams(pendingRequest)),
|
|
13
|
+
...parseHostGateContext(pendingRequest),
|
|
14
|
+
};
|
|
11
15
|
}
|
|
12
16
|
catch {
|
|
13
17
|
return undefined;
|
|
@@ -17,6 +17,8 @@ export type StageLaunchInput = {
|
|
|
17
17
|
bindingKind?: DerivedBindingKind;
|
|
18
18
|
grants?: ResolvedStageGrants;
|
|
19
19
|
attemptHome?: string;
|
|
20
|
+
browserEnv?: Record<string, string>;
|
|
21
|
+
model?: string;
|
|
20
22
|
};
|
|
21
23
|
export type StageLaunchResult = {
|
|
22
24
|
type: "succeeded";
|
|
@@ -45,6 +47,7 @@ export declare class StageProcessLauncher {
|
|
|
45
47
|
private readonly env;
|
|
46
48
|
private readonly explicitChildExtras;
|
|
47
49
|
private readonly cliEntry;
|
|
50
|
+
private readonly workerExecArgv;
|
|
48
51
|
private readonly logger;
|
|
49
52
|
private readonly forkFn;
|
|
50
53
|
private readonly active;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import * as childProcess from "node:child_process";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { logger as rootLogger, resolveLogMaxLineBytes, } from "../logging/logger.js";
|
|
4
|
+
import { isCursorModelRef, readCursorApiKey } from "../agent/cursorProvider.js";
|
|
5
|
+
import { BROWSER_ENV_PREFIX } from "../browser/browserHost.js";
|
|
4
6
|
import { PACKAGE_VERSION } from "../package-meta.js";
|
|
7
|
+
import { PI_HOME_AUTH_PATH_ENV, stageflowAgentAuthPath } from "./credentialBinding.js";
|
|
5
8
|
import { redactString } from "../logging/redact.js";
|
|
6
9
|
import { getNamedSecrets } from "../logging/namedSecrets.js";
|
|
7
10
|
import { readMaxActiveStageProcesses, } from "./stageConcurrency.js";
|
|
@@ -10,6 +13,28 @@ import { buildStageEnvironment, isAmbientBlockedEnv, isForeverDeniedSecret, } fr
|
|
|
10
13
|
import { ensureStageCacheDirs } from "./stageCacheEnv.js";
|
|
11
14
|
import { SF_STAGE_WORKER, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol.js";
|
|
12
15
|
import { overlayStageBindingEnv, } from "./stageRoots.js";
|
|
16
|
+
function defaultStageCliEntry() {
|
|
17
|
+
const source = import.meta.url.split("?")[0]?.endsWith(".ts") === true;
|
|
18
|
+
return fileURLToPath(new URL(source ? "../cli.ts" : "../cli.js", import.meta.url));
|
|
19
|
+
}
|
|
20
|
+
function isTypeScriptEntry(entry) {
|
|
21
|
+
return /\.(?:m|c)?ts$/.test(entry);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A compiled `dist/cli.js` child is plain node. `npm run dev` is TypeScript,
|
|
25
|
+
* and this launcher sets execArgv explicitly so the parent's tsx loader is
|
|
26
|
+
* not inherited. Load that entry with the same tsx hooks the dev CLI uses.
|
|
27
|
+
*/
|
|
28
|
+
function tsxLoaderExecArgv() {
|
|
29
|
+
try {
|
|
30
|
+
const preflight = fileURLToPath(import.meta.resolve("tsx/preflight"));
|
|
31
|
+
const loader = import.meta.resolve("tsx");
|
|
32
|
+
return ["--require", preflight, "--import", loader];
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
13
38
|
function flushCappedPartial(log, event, text, maxLineBytes) {
|
|
14
39
|
let remaining = redactString(text, { namedSecrets: getNamedSecrets() });
|
|
15
40
|
while (Buffer.byteLength(remaining, "utf8") > maxLineBytes) {
|
|
@@ -109,6 +134,7 @@ export class StageProcessLauncher {
|
|
|
109
134
|
env;
|
|
110
135
|
explicitChildExtras;
|
|
111
136
|
cliEntry;
|
|
137
|
+
workerExecArgv;
|
|
112
138
|
logger;
|
|
113
139
|
forkFn;
|
|
114
140
|
active = new Map();
|
|
@@ -126,9 +152,11 @@ export class StageProcessLauncher {
|
|
|
126
152
|
: undefined;
|
|
127
153
|
this.maxActive = readMaxActiveStageProcesses(this.env, options.maxActiveStageProcesses);
|
|
128
154
|
this.heapMb = getContainerLimits().maxOldSpaceSizeMb;
|
|
129
|
-
this.cliEntry =
|
|
130
|
-
|
|
131
|
-
|
|
155
|
+
this.cliEntry = options.cliEntry ?? defaultStageCliEntry();
|
|
156
|
+
this.workerExecArgv = [
|
|
157
|
+
`--max-old-space-size=${this.heapMb}`,
|
|
158
|
+
...(isTypeScriptEntry(this.cliEntry) ? tsxLoaderExecArgv() : []),
|
|
159
|
+
];
|
|
132
160
|
this.logger =
|
|
133
161
|
options.logger ?? rootLogger.child({ component: "runtime" });
|
|
134
162
|
this.forkFn = options.forkFn ?? childProcess.fork;
|
|
@@ -290,6 +318,7 @@ export class StageProcessLauncher {
|
|
|
290
318
|
cacheVars: ensureStageCacheDirs(),
|
|
291
319
|
grants: input.grants,
|
|
292
320
|
attemptHome: input.attemptHome,
|
|
321
|
+
runVars: input.browserEnv,
|
|
293
322
|
packageVersion: PACKAGE_VERSION,
|
|
294
323
|
});
|
|
295
324
|
for (const warning of built.warnings) {
|
|
@@ -301,14 +330,32 @@ export class StageProcessLauncher {
|
|
|
301
330
|
const withExtras = this.explicitChildExtras !== undefined
|
|
302
331
|
? { ...built.env, ...this.explicitChildExtras }
|
|
303
332
|
: built.env;
|
|
304
|
-
const
|
|
333
|
+
const overlaid = overlayStageBindingEnv(withExtras, input.env ?? {}, input.bindingKind ?? "unbound");
|
|
334
|
+
const withBrowser = input.browserEnv !== undefined
|
|
335
|
+
? {
|
|
336
|
+
...Object.fromEntries(Object.entries(overlaid).filter(([name]) => !name.startsWith(BROWSER_ENV_PREFIX))),
|
|
337
|
+
...input.browserEnv,
|
|
338
|
+
}
|
|
339
|
+
: overlaid;
|
|
340
|
+
const childEnv = {
|
|
341
|
+
...withBrowser,
|
|
342
|
+
[PI_HOME_AUTH_PATH_ENV]: stageflowAgentAuthPath(),
|
|
343
|
+
};
|
|
344
|
+
const cursorApiKey = readCursorApiKey(hostEnv);
|
|
345
|
+
if (isCursorModelRef(input.model ?? "") &&
|
|
346
|
+
cursorApiKey !== undefined &&
|
|
347
|
+
cursorApiKey !== "" &&
|
|
348
|
+
childEnv.CURSOR_API_KEY === undefined) {
|
|
349
|
+
childEnv.CURSOR_API_KEY = cursorApiKey;
|
|
350
|
+
}
|
|
305
351
|
const child = this.forkFn(this.cliEntry, args, {
|
|
306
352
|
cwd: input.rootDir,
|
|
307
353
|
env: { ...childEnv, [SF_STAGE_WORKER]: "1" },
|
|
308
354
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
309
355
|
detached: true,
|
|
310
356
|
// Explicit: do not inherit Host execArgv; set heap from cgroup budget.
|
|
311
|
-
|
|
357
|
+
// TypeScript entries also get the tsx loader, since that inheritance is off.
|
|
358
|
+
execArgv: this.workerExecArgv,
|
|
312
359
|
});
|
|
313
360
|
const key = activeKey(input.runId, input.stageId);
|
|
314
361
|
const tracked = {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { AgentPort, FeedbackLoopContext, StageHandle, StageRunResult, StageSessionMode } from "../agent/port.js";
|
|
2
|
+
import { type HostGateContext } from "../browser/gateHandoff.js";
|
|
3
|
+
import type { StageBrowserSupport } from "../browser/browserHost.js";
|
|
2
4
|
import type { StageEnvelope } from "../types/envelope.js";
|
|
3
5
|
import type { LoadedStageConfig } from "../types/stage.js";
|
|
4
6
|
import type { TaskFile } from "../types/task.js";
|
|
@@ -49,6 +51,8 @@ export type RunStageOptions = {
|
|
|
49
51
|
feedbackLoopContext?: FeedbackLoopContext;
|
|
50
52
|
resumeToken?: string;
|
|
51
53
|
stageEnv?: Record<string, string>;
|
|
54
|
+
/** Test seam for the Host login check run by a `browser_login` verify item. */
|
|
55
|
+
browser?: Pick<StageBrowserSupport, "runner" | "loginCheck">;
|
|
52
56
|
};
|
|
53
57
|
/**
|
|
54
58
|
* Yield loop: next() until completed; on wait, coordinator enterWait (KTD6).
|
|
@@ -62,5 +66,6 @@ export declare function runStageYieldLoop(options: {
|
|
|
62
66
|
store?: RunStore;
|
|
63
67
|
attemptCtx?: StageAttemptContext;
|
|
64
68
|
skipGates?: boolean;
|
|
69
|
+
gateContext?: HostGateContext;
|
|
65
70
|
}): Promise<RunStageYieldLoopResult>;
|
|
66
71
|
export declare function runStage(options: RunStageOptions): Promise<RunStageOutcome>;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { hostGateContextFor, stampGateRequest, } from "../browser/gateHandoff.js";
|
|
1
2
|
import { deriveExecutionPatchFromEvent } from "../runstore/stageExecution.js";
|
|
2
3
|
import { buildStageRoots, withResolvedAuthPath, } from "./stageRoots.js";
|
|
3
4
|
import { createDefaultHitlQaHooks, recordWaitEnter, } from "./stageHitl.js";
|
|
4
5
|
import { openStageAttempt, } from "./stageAttemptBootstrap.js";
|
|
5
6
|
import { createVerifiedStageExecution } from "./verifiedStageExecution.js";
|
|
7
|
+
import { BROWSER_STAGE_PATTERNS, redact } from "../logging/redact.js";
|
|
8
|
+
import { getNamedSecrets } from "../logging/namedSecrets.js";
|
|
6
9
|
import { isStageTimeoutReason } from "../agent/stageTimeout.js";
|
|
7
10
|
export function isRunStageWaiting(outcome) {
|
|
8
11
|
return "waiting" in outcome && outcome.waiting === true;
|
|
@@ -44,11 +47,16 @@ async function appendLifecycleEventAndPatchExecution(options) {
|
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
async function finalizeStageResult(options) {
|
|
47
|
-
const { store, runId, stageId, result, activityChain, attemptCtx } = options;
|
|
50
|
+
const { store, runId, stageId, result, activityChain, attemptCtx, browser } = options;
|
|
48
51
|
const attemptOpt = attemptCtx?.eventOptions();
|
|
49
52
|
await activityChain;
|
|
50
53
|
if (result.envelope) {
|
|
51
|
-
await store.writeEnvelope(runId, stageId,
|
|
54
|
+
await store.writeEnvelope(runId, stageId, browser
|
|
55
|
+
? redact(result.envelope, {
|
|
56
|
+
patterns: BROWSER_STAGE_PATTERNS,
|
|
57
|
+
namedSecrets: getNamedSecrets(),
|
|
58
|
+
})
|
|
59
|
+
: result.envelope, attemptOpt);
|
|
52
60
|
}
|
|
53
61
|
if (!result.ok) {
|
|
54
62
|
await appendLifecycleEventAndPatchExecution({
|
|
@@ -75,10 +83,11 @@ async function finalizeStageResult(options) {
|
|
|
75
83
|
* Yield loop: next() until completed; on wait, coordinator enterWait (KTD6).
|
|
76
84
|
*/
|
|
77
85
|
export async function runStageYieldLoop(options) {
|
|
78
|
-
const { handle, runId, stageId, hitl, workerMode, store, attemptCtx, skipGates } = options;
|
|
86
|
+
const { handle, runId, stageId, hitl, workerMode, store, attemptCtx, skipGates, gateContext } = options;
|
|
79
87
|
while (true) {
|
|
80
88
|
const event = await handle.next();
|
|
81
89
|
if (event.status === "waiting_for_input") {
|
|
90
|
+
const request = stampGateRequest(event.request, gateContext);
|
|
82
91
|
if (skipGates) {
|
|
83
92
|
return {
|
|
84
93
|
ok: false,
|
|
@@ -96,7 +105,7 @@ export async function runStageYieldLoop(options) {
|
|
|
96
105
|
store,
|
|
97
106
|
runId,
|
|
98
107
|
stageId,
|
|
99
|
-
request
|
|
108
|
+
request,
|
|
100
109
|
attemptCtx,
|
|
101
110
|
qaHooks: createDefaultHitlQaHooks(store),
|
|
102
111
|
});
|
|
@@ -108,14 +117,14 @@ export async function runStageYieldLoop(options) {
|
|
|
108
117
|
reason: "stage requested wait but no HITL controller is configured",
|
|
109
118
|
};
|
|
110
119
|
}
|
|
111
|
-
await hitl.enterWait(runId, stageId, handle,
|
|
120
|
+
await hitl.enterWait(runId, stageId, handle, request, attemptCtx);
|
|
112
121
|
continue;
|
|
113
122
|
}
|
|
114
123
|
return event.result;
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
export async function runStage(options) {
|
|
118
|
-
const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, skipGates, sessionMode, feedbackLoopContext, resumeToken, stageEnv, } = options;
|
|
127
|
+
const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, skipGates, sessionMode, feedbackLoopContext, resumeToken, stageEnv, browser: browserSeams, } = options;
|
|
119
128
|
const stageId = options.stageId ?? stage.id;
|
|
120
129
|
const attemptOpt = attemptCtx?.eventOptions();
|
|
121
130
|
const baseRoots = rootsOverride ??
|
|
@@ -144,6 +153,19 @@ export async function runStage(options) {
|
|
|
144
153
|
dag,
|
|
145
154
|
roots,
|
|
146
155
|
commandEnv: stageEnv ?? process.env,
|
|
156
|
+
...(stage.browser?.check !== undefined
|
|
157
|
+
? {
|
|
158
|
+
browserLogin: {
|
|
159
|
+
check: stage.browser.check,
|
|
160
|
+
...(browserSeams?.runner !== undefined
|
|
161
|
+
? { runner: browserSeams.runner }
|
|
162
|
+
: {}),
|
|
163
|
+
...(browserSeams?.loginCheck !== undefined
|
|
164
|
+
? { options: browserSeams.loginCheck }
|
|
165
|
+
: {}),
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
: {}),
|
|
147
169
|
});
|
|
148
170
|
await verifiedExecution.prepare();
|
|
149
171
|
console.error(`Running stage ${stageId} (${stage.model})...`);
|
|
@@ -209,6 +231,7 @@ export async function runStage(options) {
|
|
|
209
231
|
store: workerMode ? store : undefined,
|
|
210
232
|
attemptCtx,
|
|
211
233
|
skipGates,
|
|
234
|
+
gateContext: hostGateContextFor(stage.browser),
|
|
212
235
|
});
|
|
213
236
|
}
|
|
214
237
|
catch (err) {
|
|
@@ -253,6 +276,7 @@ export async function runStage(options) {
|
|
|
253
276
|
result,
|
|
254
277
|
activityChain,
|
|
255
278
|
attemptCtx,
|
|
279
|
+
browser: stage.browser !== undefined,
|
|
256
280
|
});
|
|
257
281
|
}
|
|
258
282
|
finally {
|
|
@@ -54,6 +54,7 @@ async function appendChunk(streamLogPath, chunk, capBytes) {
|
|
|
54
54
|
export function createStageStreamLogWriter(streamLogPath, options = {}) {
|
|
55
55
|
const flushThrottleMs = options.flushThrottleMs ?? STREAM_FLUSH_THROTTLE_MS;
|
|
56
56
|
const capBytes = options.capBytes ?? DEFAULT_CAP_BYTES;
|
|
57
|
+
const scrub = options.redact ?? redactSecrets;
|
|
57
58
|
let buffer = "";
|
|
58
59
|
let timer = null;
|
|
59
60
|
// Serializes disk writes so the throttle timer and an eager flush() never race.
|
|
@@ -65,7 +66,7 @@ export function createStageStreamLogWriter(streamLogPath, options = {}) {
|
|
|
65
66
|
}
|
|
66
67
|
if (!buffer)
|
|
67
68
|
return writeChain;
|
|
68
|
-
const chunk =
|
|
69
|
+
const chunk = scrub(buffer);
|
|
69
70
|
buffer = "";
|
|
70
71
|
writeChain = writeChain.then(() => appendChunk(streamLogPath, chunk, capBytes));
|
|
71
72
|
return writeChain;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { TriggerFile } from "../types/trigger.js";
|
|
2
|
+
export declare function findCatalogPathById(paths: string[], id: string): Promise<string | undefined>;
|
|
3
|
+
export declare function findTriggerDefinition(triggerPaths: string[], triggerId: string): Promise<{
|
|
4
|
+
path: string;
|
|
5
|
+
definition: TriggerFile;
|
|
6
|
+
} | undefined>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readYamlObject } from "../config/readYamlObject.js";
|
|
2
|
+
import { loadTriggerOutcome } from "../config/loadTrigger.js";
|
|
3
|
+
export async function findCatalogPathById(paths, id) {
|
|
4
|
+
for (const filePath of paths) {
|
|
5
|
+
try {
|
|
6
|
+
const raw = await readYamlObject(filePath);
|
|
7
|
+
if (raw?.id === id)
|
|
8
|
+
return filePath;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// Unreadable/invalid catalog files are surfaced by `sf validate`; skip here.
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
export async function findTriggerDefinition(triggerPaths, triggerId) {
|
|
17
|
+
for (const filePath of triggerPaths) {
|
|
18
|
+
const outcome = await loadTriggerOutcome(filePath);
|
|
19
|
+
if (outcome.ok && outcome.value.id === triggerId) {
|
|
20
|
+
return { path: filePath, definition: outcome.value };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Subset-match: every key/value in `match` must equal-match `payload`. No `match` matches everything. */
|
|
2
|
+
export function matchesEventFilter(match, payload) {
|
|
3
|
+
if (!match)
|
|
4
|
+
return true;
|
|
5
|
+
return Object.entries(match).every(([key, value]) => payload[key] === value);
|
|
6
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TaskFile } from "../types/task.js";
|
|
2
|
+
export type TriggerFireEvent = {
|
|
3
|
+
triggerId: string;
|
|
4
|
+
task?: TaskFile;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Abstract seam for anything that decides "fire trigger X now" — a croner-backed
|
|
8
|
+
* schedule loop, a GitHub/email poller, a webhook receiver. No adapter implements
|
|
9
|
+
* this yet; it exists so those later adapters build against a locked contract.
|
|
10
|
+
*/
|
|
11
|
+
export interface TriggerSourcePort {
|
|
12
|
+
start(onFire: (event: TriggerFireEvent) => Promise<void>): Promise<void>;
|
|
13
|
+
stop(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|