stageflow 0.27.0 → 0.30.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 +2 -0
- package/dist/agent/cursorProvider.js +30 -5
- 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/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/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/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 +31 -2
- 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/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 +2 -0
- package/dist/runtime/credentialBinding.js +3 -0
- 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 +85 -1
- package/dist/runtime/repositoryMaterialize.d.ts +1 -1
- package/dist/runtime/runManager.d.ts +11 -0
- package/dist/runtime/runManager.js +149 -1
- 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 +5 -0
- package/dist/runtime/settingsFile.js +61 -54
- 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 +2 -0
- package/dist/runtime/stageProcessLauncher.js +44 -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 +64 -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-CntB4bZR.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/stage-mcp/README.md +3 -2
- package/examples/stageflow.yaml +51 -0
- package/package.json +5 -1
- 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
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { Octokit } from "octokit";
|
|
3
|
+
import { getCatalogScanPaths } from "../config/browseCatalog.js";
|
|
4
|
+
import { loadTriggerOutcome } from "../config/loadTrigger.js";
|
|
5
|
+
import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
|
|
6
|
+
import { readSecretFromEnvOrFile } from "../config/secretFromEnvOrFile.js";
|
|
7
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
8
|
+
import { matchesEventFilter } from "./triggerEventMatch.js";
|
|
9
|
+
export const DEFAULT_GITHUB_POLL_INTERVAL_MS = 60_000;
|
|
10
|
+
export const DEFAULT_GITHUB_TOKEN_SECRET = "GITHUB_TOKEN";
|
|
11
|
+
const GITHUB_REPO_RE = /^[^\s/]+\/[^\s/]+$/;
|
|
12
|
+
/** Parse `STAGEFLOW_GITHUB_POLL_INTERVAL_MS`; default 60s; `0` disables. */
|
|
13
|
+
export function githubPollIntervalMsFromEnv(env = process.env) {
|
|
14
|
+
const raw = env.STAGEFLOW_GITHUB_POLL_INTERVAL_MS;
|
|
15
|
+
if (raw === undefined || raw.trim() === "")
|
|
16
|
+
return DEFAULT_GITHUB_POLL_INTERVAL_MS;
|
|
17
|
+
const parsed = Number(raw);
|
|
18
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
19
|
+
return DEFAULT_GITHUB_POLL_INTERVAL_MS;
|
|
20
|
+
return parsed;
|
|
21
|
+
}
|
|
22
|
+
export function defaultCreateGithubClient(token) {
|
|
23
|
+
const octokit = new Octokit({ auth: token });
|
|
24
|
+
return {
|
|
25
|
+
async listPulls({ owner, repo, etag }) {
|
|
26
|
+
let response;
|
|
27
|
+
try {
|
|
28
|
+
response = await octokit.rest.pulls.list({
|
|
29
|
+
owner,
|
|
30
|
+
repo,
|
|
31
|
+
state: "all",
|
|
32
|
+
sort: "updated",
|
|
33
|
+
direction: "desc",
|
|
34
|
+
per_page: 30,
|
|
35
|
+
...(etag !== undefined ? { headers: { "If-None-Match": etag } } : {}),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
if (err !== null && typeof err === "object" && "status" in err && err.status === 304) {
|
|
40
|
+
return { status: 304, items: [] };
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
const responseEtag = response.headers.etag;
|
|
45
|
+
return {
|
|
46
|
+
status: response.status,
|
|
47
|
+
...(typeof responseEtag === "string" ? { etag: responseEtag } : {}),
|
|
48
|
+
items: response.data.map((pr) => ({
|
|
49
|
+
number: pr.number,
|
|
50
|
+
title: pr.title,
|
|
51
|
+
html_url: pr.html_url,
|
|
52
|
+
state: pr.state,
|
|
53
|
+
merged_at: pr.merged_at,
|
|
54
|
+
created_at: pr.created_at,
|
|
55
|
+
updated_at: pr.updated_at,
|
|
56
|
+
user: pr.user ? { login: pr.user.login } : null,
|
|
57
|
+
})),
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function deriveAction(item) {
|
|
63
|
+
if (item.merged_at)
|
|
64
|
+
return "merged";
|
|
65
|
+
if (item.state === "closed")
|
|
66
|
+
return "closed";
|
|
67
|
+
if (item.created_at === item.updated_at)
|
|
68
|
+
return "opened";
|
|
69
|
+
return "updated";
|
|
70
|
+
}
|
|
71
|
+
function normalizeGithubItem(item, repo) {
|
|
72
|
+
return {
|
|
73
|
+
action: deriveAction(item),
|
|
74
|
+
number: item.number,
|
|
75
|
+
title: item.title,
|
|
76
|
+
url: item.html_url,
|
|
77
|
+
author: item.user?.login ?? "unknown",
|
|
78
|
+
repo,
|
|
79
|
+
updatedAt: item.updated_at,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function parseCursor(raw) {
|
|
83
|
+
if (raw === null)
|
|
84
|
+
return null;
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(raw);
|
|
87
|
+
if (typeof parsed.updatedAt === "string" && typeof parsed.number === "number") {
|
|
88
|
+
return { updatedAt: parsed.updatedAt, number: parsed.number };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Corrupted/foreign value; treat as unset.
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
function isNewerThanCursor(item, cursor) {
|
|
97
|
+
const itemMs = new Date(item.updated_at).getTime();
|
|
98
|
+
const cursorMs = new Date(cursor.updatedAt).getTime();
|
|
99
|
+
if (itemMs !== cursorMs)
|
|
100
|
+
return itemMs > cursorMs;
|
|
101
|
+
return item.number > cursor.number;
|
|
102
|
+
}
|
|
103
|
+
function firstSecretRef(matches) {
|
|
104
|
+
for (const { definition } of matches) {
|
|
105
|
+
const secretRef = definition.event?.config?.secretRef;
|
|
106
|
+
if (typeof secretRef === "string" && secretRef.length > 0)
|
|
107
|
+
return secretRef;
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `TriggerSourcePort` adapter for `event`-kind triggers whose `event.source`
|
|
113
|
+
* starts with `github.`. Ticks on an interval; each tick re-scans the catalog,
|
|
114
|
+
* groups triggers by their distinct `event.config.repo` (one API call per repo
|
|
115
|
+
* per tick regardless of how many triggers watch it), and diffs each repo's
|
|
116
|
+
* PR list against a persisted ETag + cursor to find genuinely new activity.
|
|
117
|
+
*
|
|
118
|
+
* The first tick for a never-before-seen repo only seeds the cursor — it
|
|
119
|
+
* never fires for a repo's pre-existing PR history, mirroring how
|
|
120
|
+
* `ScheduleSource` seeds `next_run_at` without firing on first sight.
|
|
121
|
+
*/
|
|
122
|
+
export class GithubPollSource {
|
|
123
|
+
store;
|
|
124
|
+
cwd;
|
|
125
|
+
intervalMs;
|
|
126
|
+
logError;
|
|
127
|
+
env;
|
|
128
|
+
createGithubClient;
|
|
129
|
+
interval;
|
|
130
|
+
inFlight = false;
|
|
131
|
+
constructor(options) {
|
|
132
|
+
this.store = options.store;
|
|
133
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
134
|
+
this.intervalMs = options.intervalMs ?? DEFAULT_GITHUB_POLL_INTERVAL_MS;
|
|
135
|
+
this.logError =
|
|
136
|
+
options.logError ??
|
|
137
|
+
((message) => {
|
|
138
|
+
console.error(message);
|
|
139
|
+
});
|
|
140
|
+
this.env = options.env ?? process.env;
|
|
141
|
+
this.createGithubClient = options.createGithubClient ?? defaultCreateGithubClient;
|
|
142
|
+
}
|
|
143
|
+
async start(onFire) {
|
|
144
|
+
if (this.intervalMs <= 0)
|
|
145
|
+
return;
|
|
146
|
+
this.interval = setInterval(() => {
|
|
147
|
+
if (this.inFlight)
|
|
148
|
+
return;
|
|
149
|
+
this.inFlight = true;
|
|
150
|
+
void this.tick(onFire)
|
|
151
|
+
.catch((err) => {
|
|
152
|
+
this.logError(`github poll tick failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
153
|
+
})
|
|
154
|
+
.finally(() => {
|
|
155
|
+
this.inFlight = false;
|
|
156
|
+
});
|
|
157
|
+
}, this.intervalMs).unref();
|
|
158
|
+
}
|
|
159
|
+
async stop() {
|
|
160
|
+
if (this.interval !== undefined)
|
|
161
|
+
clearInterval(this.interval);
|
|
162
|
+
this.interval = undefined;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* One discovery + poll + fire pass. Public so tests can drive it directly
|
|
166
|
+
* instead of waiting on real interval timers.
|
|
167
|
+
*/
|
|
168
|
+
async tick(onFire) {
|
|
169
|
+
const ctx = catalogContextFromStageflow(await resolveStageflowContext(this.cwd));
|
|
170
|
+
const scanPaths = await getCatalogScanPaths(ctx);
|
|
171
|
+
if (!scanPaths)
|
|
172
|
+
return;
|
|
173
|
+
const projectRoot = ctx.projectRoot ?? undefined;
|
|
174
|
+
const groups = await this.discover(scanPaths.triggerPaths);
|
|
175
|
+
for (const [repo, matches] of groups) {
|
|
176
|
+
for (const match of matches) {
|
|
177
|
+
const definitionRef = projectRoot !== undefined
|
|
178
|
+
? path.relative(projectRoot, match.path).replace(/\\/g, "/")
|
|
179
|
+
: match.path;
|
|
180
|
+
await this.store.upsertTrigger({
|
|
181
|
+
id: match.definition.id,
|
|
182
|
+
definitionRef,
|
|
183
|
+
enabled: true,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
await this.pollRepo(repo, matches, onFire);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async discover(triggerPaths) {
|
|
190
|
+
const groups = new Map();
|
|
191
|
+
for (const triggerPath of triggerPaths) {
|
|
192
|
+
const outcome = await loadTriggerOutcome(triggerPath);
|
|
193
|
+
if (!outcome.ok)
|
|
194
|
+
continue;
|
|
195
|
+
const definition = outcome.value;
|
|
196
|
+
if (definition.kind !== "event" || !definition.enabled)
|
|
197
|
+
continue;
|
|
198
|
+
const source = definition.event?.source;
|
|
199
|
+
if (typeof source !== "string" || !source.startsWith("github."))
|
|
200
|
+
continue;
|
|
201
|
+
const repo = definition.event?.config?.repo;
|
|
202
|
+
if (typeof repo !== "string" || !GITHUB_REPO_RE.test(repo)) {
|
|
203
|
+
this.logError(`github poll: trigger "${definition.id}" has an invalid event.config.repo ` +
|
|
204
|
+
`(expected "owner/repo"); skipping`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const list = groups.get(repo) ?? [];
|
|
208
|
+
list.push({ path: triggerPath, definition });
|
|
209
|
+
groups.set(repo, list);
|
|
210
|
+
}
|
|
211
|
+
return groups;
|
|
212
|
+
}
|
|
213
|
+
async pollRepo(repo, matches, onFire) {
|
|
214
|
+
const secretRef = firstSecretRef(matches) ?? DEFAULT_GITHUB_TOKEN_SECRET;
|
|
215
|
+
let token;
|
|
216
|
+
try {
|
|
217
|
+
token = readSecretFromEnvOrFile(this.env, secretRef);
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
this.logError(`github poll: failed to read secret "${secretRef}" for repo "${repo}": ${err instanceof Error ? err.message : String(err)}`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (token === undefined) {
|
|
224
|
+
this.logError(`github poll: secret "${secretRef}" is not set for repo "${repo}"; skipping this tick`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const adapterId = `github:${repo}`;
|
|
228
|
+
const etag = await this.store.getTriggerAdapterState(adapterId, "etag");
|
|
229
|
+
const cursor = parseCursor(await this.store.getTriggerAdapterState(adapterId, "cursor"));
|
|
230
|
+
const [owner, repoName] = repo.split("/");
|
|
231
|
+
let response;
|
|
232
|
+
try {
|
|
233
|
+
const client = this.createGithubClient(token);
|
|
234
|
+
response = await client.listPulls({
|
|
235
|
+
owner,
|
|
236
|
+
repo: repoName,
|
|
237
|
+
...(etag !== null ? { etag } : {}),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
this.logError(`github poll: request failed for repo "${repo}": ${err instanceof Error ? err.message : String(err)}`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (response.status === 304)
|
|
245
|
+
return;
|
|
246
|
+
if (response.etag !== undefined) {
|
|
247
|
+
await this.store.setTriggerAdapterState(adapterId, "etag", response.etag);
|
|
248
|
+
}
|
|
249
|
+
if (response.items.length === 0)
|
|
250
|
+
return;
|
|
251
|
+
const newest = response.items[0];
|
|
252
|
+
if (cursor === null) {
|
|
253
|
+
await this.store.setTriggerAdapterState(adapterId, "cursor", JSON.stringify({ updatedAt: newest.updated_at, number: newest.number }));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const newItems = response.items.filter((item) => isNewerThanCursor(item, cursor)).reverse();
|
|
257
|
+
for (const item of newItems) {
|
|
258
|
+
const summary = normalizeGithubItem(item, repo);
|
|
259
|
+
for (const { definition } of matches) {
|
|
260
|
+
if (!matchesEventFilter(definition.event?.match, summary))
|
|
261
|
+
continue;
|
|
262
|
+
await this.fireSafely(onFire, definition, summary);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
await this.store.setTriggerAdapterState(adapterId, "cursor", JSON.stringify({ updatedAt: newest.updated_at, number: newest.number }));
|
|
266
|
+
}
|
|
267
|
+
async fireSafely(onFire, definition, summary) {
|
|
268
|
+
try {
|
|
269
|
+
if (definition.task !== undefined) {
|
|
270
|
+
await onFire({ triggerId: definition.id });
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const task = {
|
|
274
|
+
id: `${definition.id}-pr-${summary.number}`,
|
|
275
|
+
goal: `Handle PR #${summary.number}: ${summary.title}`,
|
|
276
|
+
input: summary,
|
|
277
|
+
};
|
|
278
|
+
await onFire({ triggerId: definition.id, task });
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
this.logError(`github poll: firing trigger "${definition.id}" failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -7,6 +7,7 @@ import type { StageHitlController } from "./stageHitl.js";
|
|
|
7
7
|
import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
|
|
8
8
|
import type { TaskFile } from "../types/task.js";
|
|
9
9
|
import { type StageExecutionMode } from "./stageConcurrency.js";
|
|
10
|
+
import type { StageBrowserSupport } from "../browser/browserHost.js";
|
|
10
11
|
import { StageProcessLauncher } from "./stageProcessLauncher.js";
|
|
11
12
|
import type { OperatorCatalog } from "./stageAttemptBootstrap.js";
|
|
12
13
|
import { type SkillsPayload } from "./runSkills.js";
|
|
@@ -59,6 +60,7 @@ export type PreparedPipeline = {
|
|
|
59
60
|
hitl?: StageHitlController;
|
|
60
61
|
executionMode?: StageExecutionMode;
|
|
61
62
|
stageProcessLauncher?: StageProcessLauncher;
|
|
63
|
+
browser?: StageBrowserSupport;
|
|
62
64
|
operatorCatalog?: OperatorCatalog;
|
|
63
65
|
skipGates?: boolean;
|
|
64
66
|
findings?: ValidationFinding[];
|
|
@@ -89,6 +91,7 @@ export declare function runPipeline(options: {
|
|
|
89
91
|
maxActiveStagesPerRun?: number;
|
|
90
92
|
executionMode?: StageExecutionMode;
|
|
91
93
|
stageProcessLauncher?: StageProcessLauncher;
|
|
94
|
+
browser?: StageBrowserSupport;
|
|
92
95
|
operatorCatalog?: OperatorCatalog;
|
|
93
96
|
skipGates?: boolean;
|
|
94
97
|
}): Promise<PipelineRunResult>;
|
|
@@ -117,6 +120,7 @@ export declare function startPipeline(options: {
|
|
|
117
120
|
maxActiveStagesPerRun?: number;
|
|
118
121
|
executionMode?: StageExecutionMode;
|
|
119
122
|
stageProcessLauncher?: StageProcessLauncher;
|
|
123
|
+
browser?: StageBrowserSupport;
|
|
120
124
|
operatorCatalog?: OperatorCatalog;
|
|
121
125
|
skipGates?: boolean;
|
|
122
126
|
schedulingHalt?: {
|
|
@@ -242,6 +242,7 @@ async function preparePipeline(options) {
|
|
|
242
242
|
hitl: options.hitl,
|
|
243
243
|
executionMode,
|
|
244
244
|
stageProcessLauncher,
|
|
245
|
+
browser: options.browser,
|
|
245
246
|
operatorCatalog: options.operatorCatalog,
|
|
246
247
|
skipGates: options.skipGates,
|
|
247
248
|
...(warningFindings.length > 0 ? { findings: warningFindings } : {}),
|
|
@@ -283,6 +284,7 @@ export async function runPipeline(options) {
|
|
|
283
284
|
hitl: options.hitl,
|
|
284
285
|
executionMode: options.executionMode,
|
|
285
286
|
stageProcessLauncher: options.stageProcessLauncher,
|
|
287
|
+
browser: options.browser,
|
|
286
288
|
operatorCatalog: options.operatorCatalog,
|
|
287
289
|
skipGates: options.skipGates,
|
|
288
290
|
});
|
|
@@ -319,6 +321,7 @@ export async function startPipeline(options) {
|
|
|
319
321
|
hitl: options.hitl,
|
|
320
322
|
executionMode: options.executionMode,
|
|
321
323
|
stageProcessLauncher: options.stageProcessLauncher,
|
|
324
|
+
browser: options.browser,
|
|
322
325
|
operatorCatalog: options.operatorCatalog,
|
|
323
326
|
skipGates: options.skipGates,
|
|
324
327
|
callerId: options.callerId,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentPort } from "../agent/port.js";
|
|
2
2
|
import type { RunPipelineDagSnapshot, RunStore, StageSnapshot } from "../runstore/port.js";
|
|
3
|
+
import type { StageBrowserSupport } from "../browser/browserHost.js";
|
|
3
4
|
import type { StageEnvelope } from "../types/envelope.js";
|
|
4
5
|
import type { LoadedPipeline, ResolvedPipelineDag } from "../types/pipeline.js";
|
|
5
6
|
import type { TaskFile } from "../types/task.js";
|
|
@@ -24,6 +25,7 @@ type SchedulerPreparedPipeline = {
|
|
|
24
25
|
hitl?: StageHitlController;
|
|
25
26
|
operatorCatalog?: OperatorCatalog;
|
|
26
27
|
skipGates?: boolean;
|
|
28
|
+
browser?: StageBrowserSupport;
|
|
27
29
|
};
|
|
28
30
|
export type { SchedulerPreparedPipeline };
|
|
29
31
|
type RetryContext = {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { normalizeForkChoice } from "../envelope/forkChoice.js";
|
|
2
2
|
import { refreshRunDiskUsage } from "../runstore/diskUsage.js";
|
|
3
3
|
import { buildPipelineDagSnapshotFromLoaded } from "../runstore/pipelineDagSnapshot.js";
|
|
4
|
+
import { defaultStageBrowserSupport, resolveStageBrowserEnv, } from "../browser/stageBrowserEnv.js";
|
|
5
|
+
import { teardownRunBrowsers, teardownStageBrowser, } from "../browser/browserTeardown.js";
|
|
6
|
+
import { isHumanLoginStage } from "../browser/humanLogin.js";
|
|
7
|
+
import { createRunLiveness } from "../browser/runLiveness.js";
|
|
8
|
+
import { acquireStageProfile, profileWaitingMessage, } from "../browser/stageProfileLock.js";
|
|
4
9
|
import { definitionIdForInstance } from "../runstore/stageInstanceId.js";
|
|
5
10
|
import { cloneFailureContinuesSchedule, cloneRetryDownstream, cloneFailFastSkipIds, cloneScheduleAllowsRun, failureIsAccepted, isCloneInstance, mint, protectedClonableChildIds, sequentialLaterCloneIds, skipRejectedNeedDependents, } from "./cloneSchedule.js";
|
|
6
11
|
import { buildCompletedEnvelopesFromRun, buildStageConfigById, } from "./envelopeRouting.js";
|
|
@@ -386,6 +391,7 @@ export async function runPipelineDag(options) {
|
|
|
386
391
|
hostEnv: process.env,
|
|
387
392
|
});
|
|
388
393
|
const stageById = buildStageConfigById(loaded);
|
|
394
|
+
const runUsesBrowser = [...stageById.values()].some((s) => s.browser !== undefined);
|
|
389
395
|
const retryContext = options.retryContext;
|
|
390
396
|
const feedbackSchedule = createReplaySchedule();
|
|
391
397
|
await hydrateFromStore(feedbackSchedule, store, run.runId, dag);
|
|
@@ -842,6 +848,40 @@ export async function runPipelineDag(options) {
|
|
|
842
848
|
return;
|
|
843
849
|
}
|
|
844
850
|
const attempt = await resolveLaunchAttempt(stageId);
|
|
851
|
+
if (stage.browser?.profile !== undefined) {
|
|
852
|
+
const profile = stage.browser.profile;
|
|
853
|
+
// A stage queued behind another run's lease must not hold one of this
|
|
854
|
+
// run's active slots; it takes the slot back once it joins the lease.
|
|
855
|
+
let slotReleased = false;
|
|
856
|
+
let acquired;
|
|
857
|
+
try {
|
|
858
|
+
acquired = await acquireStageProfile(prepared.browser ?? defaultStageBrowserSupport(), {
|
|
859
|
+
profile,
|
|
860
|
+
owner: { runId: run.runId, stageId },
|
|
861
|
+
isRunLive: createRunLiveness(store),
|
|
862
|
+
halted: () => options.schedulingHalt?.halted === true,
|
|
863
|
+
onWaiting: async (holder) => {
|
|
864
|
+
if (!slotReleased) {
|
|
865
|
+
slotReleased = true;
|
|
866
|
+
activeCount -= 1;
|
|
867
|
+
}
|
|
868
|
+
await store.appendStageEvent(run.runId, stageId, {
|
|
869
|
+
event: "message",
|
|
870
|
+
role: "host",
|
|
871
|
+
text: profileWaitingMessage(profile, holder),
|
|
872
|
+
}, { attempt });
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
finally {
|
|
877
|
+
if (slotReleased)
|
|
878
|
+
activeCount += 1;
|
|
879
|
+
}
|
|
880
|
+
if (acquired === "halted") {
|
|
881
|
+
states.set(stageId, "skipped");
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
845
885
|
const attemptCtx = attemptContext(attempt);
|
|
846
886
|
const prep = launchFor(feedbackSchedule, stageId);
|
|
847
887
|
const feedbackLoopContext = prep?.feedbackLoopContext;
|
|
@@ -899,6 +939,22 @@ export async function runPipelineDag(options) {
|
|
|
899
939
|
// best-effort Host cleanup
|
|
900
940
|
}
|
|
901
941
|
};
|
|
942
|
+
let browserEnv;
|
|
943
|
+
try {
|
|
944
|
+
browserEnv = await resolveStageBrowserEnv(prepared.browser ?? defaultStageBrowserSupport(), {
|
|
945
|
+
runId: run.runId,
|
|
946
|
+
stageId,
|
|
947
|
+
runDir: run.workspaceDir,
|
|
948
|
+
browser: stage.browser,
|
|
949
|
+
attempt,
|
|
950
|
+
humanLogin: isHumanLoginStage(dag, definitionId, stage.browser),
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
catch (err) {
|
|
954
|
+
cleanupAttemptCredentials();
|
|
955
|
+
await onStageFailure(stageId, err instanceof Error ? err.message : String(err));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
902
958
|
if (executionMode === "process") {
|
|
903
959
|
const launcher = options.stageProcessLauncher;
|
|
904
960
|
if (!launcher) {
|
|
@@ -917,6 +973,7 @@ export async function runPipelineDag(options) {
|
|
|
917
973
|
bindingKind: stageBinding.kind,
|
|
918
974
|
grants,
|
|
919
975
|
attemptHome,
|
|
976
|
+
...(browserEnv !== undefined ? { browserEnv } : {}),
|
|
920
977
|
...(sessionMode !== undefined
|
|
921
978
|
? {
|
|
922
979
|
mode: sessionMode === "feedback_resume"
|
|
@@ -955,6 +1012,10 @@ export async function runPipelineDag(options) {
|
|
|
955
1012
|
states.set(stageId, "waiting");
|
|
956
1013
|
return;
|
|
957
1014
|
}
|
|
1015
|
+
const recorded = await store.listStageEvents(run.runId, stageId, attempt);
|
|
1016
|
+
if (!recorded.some((event) => event.event === "failed")) {
|
|
1017
|
+
await store.appendStageEvent(run.runId, stageId, { event: "failed", reason: launchResult.reason }, { attempt });
|
|
1018
|
+
}
|
|
958
1019
|
await onStageFailure(stageId, launchResult.reason);
|
|
959
1020
|
return;
|
|
960
1021
|
}
|
|
@@ -974,9 +1035,11 @@ export async function runPipelineDag(options) {
|
|
|
974
1035
|
operatorCatalog: prepared.operatorCatalog,
|
|
975
1036
|
completedEnvelopes,
|
|
976
1037
|
skipGates: prepared.skipGates,
|
|
1038
|
+
...(prepared.browser !== undefined ? { browser: prepared.browser } : {}),
|
|
977
1039
|
stageEnv: {
|
|
978
1040
|
...stageBinding.env,
|
|
979
1041
|
...grants.env,
|
|
1042
|
+
...browserEnv,
|
|
980
1043
|
HOME: attemptHome,
|
|
981
1044
|
},
|
|
982
1045
|
...(sessionMode !== undefined ? { sessionMode } : {}),
|
|
@@ -1016,7 +1079,19 @@ export async function runPipelineDag(options) {
|
|
|
1016
1079
|
states.set(stageId, "active");
|
|
1017
1080
|
launchedIds.add(stageId);
|
|
1018
1081
|
activeCount += 1;
|
|
1019
|
-
const taskPromise = launchStage(stageId)
|
|
1082
|
+
const taskPromise = launchStage(stageId)
|
|
1083
|
+
.then(async () => {
|
|
1084
|
+
const state = states.get(stageId);
|
|
1085
|
+
if (runUsesBrowser && (state === "succeeded" || state === "failed")) {
|
|
1086
|
+
await teardownStageBrowser(prepared.browser ?? defaultStageBrowserSupport(), {
|
|
1087
|
+
runId: run.runId,
|
|
1088
|
+
runDir: run.workspaceDir,
|
|
1089
|
+
stageId,
|
|
1090
|
+
events: () => store.listStageEvents(run.runId, stageId),
|
|
1091
|
+
}).catch(() => undefined);
|
|
1092
|
+
}
|
|
1093
|
+
})
|
|
1094
|
+
.finally(() => {
|
|
1020
1095
|
activeCount -= 1;
|
|
1021
1096
|
inFlight.delete(taskPromise);
|
|
1022
1097
|
});
|
|
@@ -1084,6 +1159,15 @@ export async function runPipelineDag(options) {
|
|
|
1084
1159
|
await Promise.all(inFlight);
|
|
1085
1160
|
}
|
|
1086
1161
|
const hasWaiting = [...states.values()].some((s) => s === "waiting");
|
|
1162
|
+
if (runUsesBrowser &&
|
|
1163
|
+
!(hasWaiting && !schedulingHalted) &&
|
|
1164
|
+
!options.schedulingHalt?.hostShutdown) {
|
|
1165
|
+
await teardownRunBrowsers(prepared.browser ?? defaultStageBrowserSupport(), {
|
|
1166
|
+
runId: run.runId,
|
|
1167
|
+
runDir: run.workspaceDir,
|
|
1168
|
+
events: (stageId) => store.listStageEvents(run.runId, stageId),
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1087
1171
|
if (hasWaiting && !schedulingHalted) {
|
|
1088
1172
|
return {
|
|
1089
1173
|
ok: false,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RunMeta, RunStore } from "../runstore/port.js";
|
|
2
2
|
import type { TaskFile } from "../types/task.js";
|
|
3
3
|
import type { WorkspaceBinding } from "./workspaceBinding.js";
|
|
4
|
-
export type StartFailureCode = "busy_capacity" | "busy_checkout" | "busy_caller_quota" | "repository_auth_failed" | "repository_not_found" | "ref_not_found" | "repository_fetch_failed" | "worktree_create_failed" | "git_missing" | "insufficient_disk" | "disk_check_failed" | "pinned_sha_unavailable" | "invalid_run_branch" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "task.invalid_shape" | "task.load_error" | "start.token_rejected" | "inline_pipeline_too_large" | "skills_invalid_name" | "skills_invalid_path" | "skills_missing_skill_md" | "skills_payload_too_large" | "missing_tool" | "tool_version_mismatch" | "unknown_version" | "secret_unavailable" | "unresolved_var" | "shutting_down" | "autostart_disabled" | "ensure_project_not_allowed" | "claude_as_root";
|
|
4
|
+
export type StartFailureCode = "busy_capacity" | "busy_checkout" | "busy_caller_quota" | "repository_auth_failed" | "repository_not_found" | "ref_not_found" | "repository_fetch_failed" | "worktree_create_failed" | "git_missing" | "insufficient_disk" | "disk_check_failed" | "pinned_sha_unavailable" | "invalid_run_branch" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "task.invalid_shape" | "task.load_error" | "trigger.task_override_not_allowed" | "trigger.task_required" | "start.token_rejected" | "inline_pipeline_too_large" | "skills_invalid_name" | "skills_invalid_path" | "skills_missing_skill_md" | "skills_payload_too_large" | "missing_tool" | "tool_version_mismatch" | "unknown_version" | "secret_unavailable" | "unresolved_var" | "shutting_down" | "autostart_disabled" | "ensure_project_not_allowed" | "claude_as_root";
|
|
5
5
|
export type MaterializedBinding = {
|
|
6
6
|
runId: string;
|
|
7
7
|
checkoutRoot?: string;
|
|
@@ -9,6 +9,7 @@ import { type SkillsPayload } from "./runSkills.js";
|
|
|
9
9
|
import { type FeedbackLoopDecisionKind } from "./feedbackLoopDecision.js";
|
|
10
10
|
import { type RetryStageResult } from "./runRetryCoordinator.js";
|
|
11
11
|
import { type StageExecutionMode } from "./stageConcurrency.js";
|
|
12
|
+
import type { StageBrowserSupport } from "../browser/browserHost.js";
|
|
12
13
|
import { StageProcessLauncher } from "./stageProcessLauncher.js";
|
|
13
14
|
import { StageHitlController, type DeliverAnswerResult, type HitlSeams } from "./stageHitl.js";
|
|
14
15
|
import { type StartTaskInput } from "./taskInput.js";
|
|
@@ -190,6 +191,7 @@ export declare class RunManager {
|
|
|
190
191
|
maxActiveStagesPerRun?: number;
|
|
191
192
|
executionMode?: StageExecutionMode;
|
|
192
193
|
stageProcessLauncher?: StageProcessLauncher;
|
|
194
|
+
browser?: StageBrowserSupport;
|
|
193
195
|
a2aStore?: A2aStore;
|
|
194
196
|
knownWritableProjectRoots?: () => Iterable<string> | Promise<Iterable<string>>;
|
|
195
197
|
freeSpaceReader?: FreeSpaceReader;
|
|
@@ -235,6 +237,9 @@ export declare class RunManager {
|
|
|
235
237
|
stageId: string;
|
|
236
238
|
}>>;
|
|
237
239
|
hasActiveWorker(runId: string, stageId: string): boolean;
|
|
240
|
+
sweepBrowserSessions(): Promise<{
|
|
241
|
+
closed: string[];
|
|
242
|
+
}>;
|
|
238
243
|
reconcileOrphanedStages(): Promise<{
|
|
239
244
|
reconciled: Array<{
|
|
240
245
|
runId: string;
|
|
@@ -344,6 +349,12 @@ export declare class RunManager {
|
|
|
344
349
|
reason?: string;
|
|
345
350
|
}): Promise<DecideFeedbackLoopResult>;
|
|
346
351
|
private resumeViaSubprocess;
|
|
352
|
+
/**
|
|
353
|
+
* A resumed stage whose after-phase verification failed is repaired by the
|
|
354
|
+
* same policy the scheduler applies to a first-launch failure. Returns the
|
|
355
|
+
* freshly created attempt, or undefined when the failure is final.
|
|
356
|
+
*/
|
|
357
|
+
private scheduleRepairAfterVerifyFailure;
|
|
347
358
|
private reconstructAndContinue;
|
|
348
359
|
private reserveAndStartPipeline;
|
|
349
360
|
private busyFailure;
|