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.
Files changed (270) hide show
  1. package/README.md +4 -0
  2. package/builtin-skills/browser/SKILL.md +42 -0
  3. package/dist/agent/claudeAdapter.js +2 -2
  4. package/dist/agent/cursorProvider.d.ts +10 -0
  5. package/dist/agent/cursorProvider.js +83 -8
  6. package/dist/agent/fakeAgent.d.ts +1 -0
  7. package/dist/agent/fakeAgent.js +13 -0
  8. package/dist/agent/piAdapter.d.ts +3 -24
  9. package/dist/agent/piAdapter.js +49 -47
  10. package/dist/agent/piSessionFactory.d.ts +38 -0
  11. package/dist/agent/piSessionFactory.js +60 -0
  12. package/dist/agent/port.d.ts +2 -0
  13. package/dist/agent/providerAuth.d.ts +7 -3
  14. package/dist/agent/providerAuth.js +5 -3
  15. package/dist/agent/streamLogRedact.d.ts +6 -0
  16. package/dist/agent/streamLogRedact.js +23 -1
  17. package/dist/browser/anchor.d.ts +27 -0
  18. package/dist/browser/anchor.js +127 -0
  19. package/dist/browser/auditSink.d.ts +38 -0
  20. package/dist/browser/auditSink.js +34 -0
  21. package/dist/browser/browserHost.d.ts +86 -0
  22. package/dist/browser/browserHost.js +7 -0
  23. package/dist/browser/browserSweep.d.ts +14 -0
  24. package/dist/browser/browserSweep.js +54 -0
  25. package/dist/browser/browserTeardown.d.ts +56 -0
  26. package/dist/browser/browserTeardown.js +235 -0
  27. package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
  28. package/dist/browser/fakeRemoteBrowserHost.js +34 -0
  29. package/dist/browser/gateHandoff.d.ts +25 -0
  30. package/dist/browser/gateHandoff.js +83 -0
  31. package/dist/browser/humanLogin.d.ts +18 -0
  32. package/dist/browser/humanLogin.js +34 -0
  33. package/dist/browser/keyProvider.d.ts +10 -0
  34. package/dist/browser/keyProvider.js +39 -0
  35. package/dist/browser/localBrowserHost.d.ts +10 -0
  36. package/dist/browser/localBrowserHost.js +152 -0
  37. package/dist/browser/localProfileLock.d.ts +2 -0
  38. package/dist/browser/localProfileLock.js +218 -0
  39. package/dist/browser/localProfileStore.d.ts +5 -0
  40. package/dist/browser/localProfileStore.js +91 -0
  41. package/dist/browser/loginCheck.d.ts +53 -0
  42. package/dist/browser/loginCheck.js +147 -0
  43. package/dist/browser/memoryProfileLock.d.ts +2 -0
  44. package/dist/browser/memoryProfileLock.js +53 -0
  45. package/dist/browser/memoryProfileStore.d.ts +2 -0
  46. package/dist/browser/memoryProfileStore.js +31 -0
  47. package/dist/browser/navigationAudit.d.ts +16 -0
  48. package/dist/browser/navigationAudit.js +53 -0
  49. package/dist/browser/persistedEnv.d.ts +3 -0
  50. package/dist/browser/persistedEnv.js +15 -0
  51. package/dist/browser/profileLock.d.ts +40 -0
  52. package/dist/browser/profileLock.js +10 -0
  53. package/dist/browser/profileStore.d.ts +23 -0
  54. package/dist/browser/profileStore.js +39 -0
  55. package/dist/browser/runLiveness.d.ts +13 -0
  56. package/dist/browser/runLiveness.js +39 -0
  57. package/dist/browser/sitePolicy.d.ts +10 -0
  58. package/dist/browser/sitePolicy.js +61 -0
  59. package/dist/browser/stageBrowserEnv.d.ts +22 -0
  60. package/dist/browser/stageBrowserEnv.js +147 -0
  61. package/dist/browser/stageProfileLock.d.ts +17 -0
  62. package/dist/browser/stageProfileLock.js +39 -0
  63. package/dist/cli/browserCommand.d.ts +44 -0
  64. package/dist/cli/browserCommand.js +472 -0
  65. package/dist/cli/doctorCommand.js +7 -0
  66. package/dist/cli/hostClient.d.ts +20 -0
  67. package/dist/cli/hostClient.js +36 -2
  68. package/dist/cli/initCommand.d.ts +1 -0
  69. package/dist/cli/initCommand.js +23 -7
  70. package/dist/cli/initTemplates.d.ts +5 -2
  71. package/dist/cli/initTemplates.js +25 -2
  72. package/dist/cli/providersCommand.d.ts +1 -1
  73. package/dist/cli/providersCommand.js +4 -4
  74. package/dist/cli/runsCommand.js +6 -2
  75. package/dist/cli/triggerCommand.d.ts +12 -0
  76. package/dist/cli/triggerCommand.js +183 -0
  77. package/dist/cli/validateCommand.d.ts +1 -1
  78. package/dist/cli/validateCommand.js +23 -6
  79. package/dist/cli.js +32 -3
  80. package/dist/config/browseCatalog.d.ts +1 -0
  81. package/dist/config/browseCatalog.js +5 -4
  82. package/dist/config/builtinSkills.d.ts +4 -0
  83. package/dist/config/builtinSkills.js +23 -0
  84. package/dist/config/catalogRelativePath.d.ts +1 -0
  85. package/dist/config/catalogRelativePath.js +10 -2
  86. package/dist/config/createTrigger.d.ts +27 -0
  87. package/dist/config/createTrigger.js +232 -0
  88. package/dist/config/draftPackage.d.ts +117 -0
  89. package/dist/config/draftPackage.js +550 -0
  90. package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
  91. package/dist/config/ensureCatalogScanRoot.js +73 -0
  92. package/dist/config/hostConfig.d.ts +2 -0
  93. package/dist/config/hostConfig.js +27 -0
  94. package/dist/config/listSkills.d.ts +2 -2
  95. package/dist/config/listSkills.js +17 -3
  96. package/dist/config/loadOutcome.d.ts +6 -0
  97. package/dist/config/loadPipeline.js +16 -0
  98. package/dist/config/loadStage.js +7 -0
  99. package/dist/config/loadStageflowManifest.js +25 -0
  100. package/dist/config/loadTrigger.d.ts +9 -0
  101. package/dist/config/loadTrigger.js +120 -0
  102. package/dist/config/multiProjectCatalog.d.ts +2 -0
  103. package/dist/config/multiProjectCatalog.js +26 -18
  104. package/dist/config/normalizePipelineStageEntry.js +8 -1
  105. package/dist/config/parseCompletionContract.js +6 -0
  106. package/dist/config/pipelineStageKeys.js +1 -0
  107. package/dist/config/publishDraftPackage.d.ts +7 -0
  108. package/dist/config/publishDraftPackage.js +25 -0
  109. package/dist/config/resolveCatalogContext.js +1 -1
  110. package/dist/config/resolvePipelineDag.js +1 -1
  111. package/dist/config/scanCatalogPaths.d.ts +1 -1
  112. package/dist/config/scanCatalogPaths.js +6 -2
  113. package/dist/config/stageBrowser.d.ts +12 -0
  114. package/dist/config/stageBrowser.js +118 -0
  115. package/dist/config/triggerCatalog.d.ts +10 -0
  116. package/dist/config/triggerCatalog.js +37 -0
  117. package/dist/config/validateCatalog.d.ts +13 -3
  118. package/dist/config/validateCatalog.js +83 -2
  119. package/dist/config/validateCompletionContract.js +8 -0
  120. package/dist/envelope/preEmitChecks.d.ts +1 -1
  121. package/dist/envelope/preEmitChecks.js +8 -0
  122. package/dist/logging/redact.d.ts +4 -0
  123. package/dist/logging/redact.js +17 -0
  124. package/dist/mcp/catalogTools.js +43 -8
  125. package/dist/mcp/controlTools.js +3 -1
  126. package/dist/mcp/deps.d.ts +2 -0
  127. package/dist/mcp/server.js +1 -0
  128. package/dist/mcp/tools.js +2 -0
  129. package/dist/mcp/triggerTools.d.ts +3 -0
  130. package/dist/mcp/triggerTools.js +176 -0
  131. package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
  132. package/dist/operatorAgent/docsRetrieval.js +269 -0
  133. package/dist/operatorAgent/draftContext.d.ts +16 -0
  134. package/dist/operatorAgent/draftContext.js +68 -0
  135. package/dist/operatorAgent/fakeHost.d.ts +39 -0
  136. package/dist/operatorAgent/fakeHost.js +92 -0
  137. package/dist/operatorAgent/host.d.ts +40 -0
  138. package/dist/operatorAgent/host.js +179 -0
  139. package/dist/operatorAgent/index.d.ts +9 -0
  140. package/dist/operatorAgent/index.js +8 -0
  141. package/dist/operatorAgent/piModel.d.ts +80 -0
  142. package/dist/operatorAgent/piModel.js +528 -0
  143. package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
  144. package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
  145. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
  146. package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
  147. package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
  148. package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
  149. package/dist/operatorAgent/proposals.d.ts +32 -0
  150. package/dist/operatorAgent/proposals.js +144 -0
  151. package/dist/operatorAgent/types.d.ts +128 -0
  152. package/dist/operatorAgent/types.js +1 -0
  153. package/dist/package-meta.d.ts +1 -1
  154. package/dist/package-meta.js +1 -1
  155. package/dist/project/findManifestRoot.d.ts +3 -0
  156. package/dist/project/findManifestRoot.js +35 -0
  157. package/dist/project/resolveStageflowContext.d.ts +7 -0
  158. package/dist/project/resolveStageflowContext.js +44 -28
  159. package/dist/runstore/assertTmpdir.js +6 -2
  160. package/dist/runstore/diskUsage.d.ts +1 -1
  161. package/dist/runstore/diskUsage.js +2 -7
  162. package/dist/runstore/port.d.ts +28 -0
  163. package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -1
  164. package/dist/runstore/sqlite/SqliteRunStore.js +90 -0
  165. package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
  166. package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
  167. package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
  168. package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
  169. package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
  170. package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
  171. package/dist/runstore/sqlite/migrations/index.js +6 -0
  172. package/dist/runtime/answerResume.d.ts +1 -0
  173. package/dist/runtime/answerResume.js +3 -1
  174. package/dist/runtime/completionCheckRunner.d.ts +12 -1
  175. package/dist/runtime/completionCheckRunner.js +22 -0
  176. package/dist/runtime/credentialBinding.d.ts +7 -2
  177. package/dist/runtime/credentialBinding.js +20 -28
  178. package/dist/runtime/emailSource.d.ts +72 -0
  179. package/dist/runtime/emailSource.js +304 -0
  180. package/dist/runtime/githubPollSource.d.ts +86 -0
  181. package/dist/runtime/githubPollSource.js +284 -0
  182. package/dist/runtime/pipelineRunner.d.ts +4 -0
  183. package/dist/runtime/pipelineRunner.js +3 -0
  184. package/dist/runtime/pipelineScheduler.d.ts +2 -0
  185. package/dist/runtime/pipelineScheduler.js +86 -1
  186. package/dist/runtime/repositoryMaterialize.d.ts +1 -1
  187. package/dist/runtime/runManager.d.ts +11 -0
  188. package/dist/runtime/runManager.js +161 -3
  189. package/dist/runtime/runRetryCoordinator.d.ts +2 -0
  190. package/dist/runtime/runRetryCoordinator.js +1 -0
  191. package/dist/runtime/scheduleSource.d.ts +38 -0
  192. package/dist/runtime/scheduleSource.js +119 -0
  193. package/dist/runtime/settingsFile.d.ts +7 -2
  194. package/dist/runtime/settingsFile.js +66 -56
  195. package/dist/runtime/stageAttemptBootstrap.js +55 -3
  196. package/dist/runtime/stageEnvironment.js +1 -0
  197. package/dist/runtime/stageHitl.d.ts +1 -0
  198. package/dist/runtime/stageHitl.js +5 -1
  199. package/dist/runtime/stageProcessLauncher.d.ts +3 -0
  200. package/dist/runtime/stageProcessLauncher.js +52 -5
  201. package/dist/runtime/stageRunner.d.ts +5 -0
  202. package/dist/runtime/stageRunner.js +30 -6
  203. package/dist/runtime/stageStreamLog.d.ts +1 -0
  204. package/dist/runtime/stageStreamLog.js +2 -1
  205. package/dist/runtime/triggerCatalog.d.ts +6 -0
  206. package/dist/runtime/triggerCatalog.js +24 -0
  207. package/dist/runtime/triggerEventMatch.d.ts +2 -0
  208. package/dist/runtime/triggerEventMatch.js +6 -0
  209. package/dist/runtime/triggerPort.d.ts +14 -0
  210. package/dist/runtime/triggerPort.js +1 -0
  211. package/dist/runtime/triggerRunner.d.ts +17 -0
  212. package/dist/runtime/triggerRunner.js +89 -0
  213. package/dist/runtime/verifiedStageExecution.d.ts +7 -0
  214. package/dist/runtime/verifiedStageExecution.js +14 -0
  215. package/dist/runtime/webhookSignature.d.ts +1 -0
  216. package/dist/runtime/webhookSignature.js +12 -0
  217. package/dist/server/bootstrap.d.ts +6 -0
  218. package/dist/server/bootstrap.js +70 -0
  219. package/dist/server/controlToken.js +13 -0
  220. package/dist/server/createHttpHost.js +3 -0
  221. package/dist/server/healthSurfaces.js +31 -6
  222. package/dist/server/http.d.ts +15 -0
  223. package/dist/server/http.js +1251 -74
  224. package/dist/server/requestAuthContext.d.ts +1 -1
  225. package/dist/server/triggerRoutes.d.ts +15 -0
  226. package/dist/server/triggerRoutes.js +268 -0
  227. package/dist/tools/askOperator.d.ts +3 -2
  228. package/dist/types/completion.d.ts +3 -0
  229. package/dist/types/pipeline.d.ts +3 -1
  230. package/dist/types/preEmitCheck.d.ts +7 -0
  231. package/dist/types/stage.d.ts +14 -0
  232. package/dist/types/stageflowManifest.d.ts +3 -0
  233. package/dist/types/trigger.d.ts +18 -0
  234. package/dist/types/trigger.js +1 -0
  235. package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
  236. package/dist/ui/assets/index-p8Iou3zk.js +133 -0
  237. package/dist/ui/index.html +2 -2
  238. package/dist/workshop/autosave.d.ts +58 -0
  239. package/dist/workshop/autosave.js +222 -0
  240. package/dist/workshop/buildStore.d.ts +79 -0
  241. package/dist/workshop/buildStore.js +326 -0
  242. package/dist/workshop/chatTurn.d.ts +232 -0
  243. package/dist/workshop/chatTurn.js +475 -0
  244. package/dist/workshop/modelSettings.d.ts +18 -0
  245. package/dist/workshop/modelSettings.js +44 -0
  246. package/dist/workshop/sessionStore.d.ts +61 -0
  247. package/dist/workshop/sessionStore.js +254 -0
  248. package/dist/workshop/workshopPin.d.ts +72 -0
  249. package/dist/workshop/workshopPin.js +193 -0
  250. package/examples/README.md +2 -1
  251. package/examples/browser-session/README.md +77 -0
  252. package/examples/browser-session/browser-session.pipeline.yaml +50 -0
  253. package/examples/browser-session/browser-session.task.yaml +6 -0
  254. package/examples/browser-session/check-login.yaml +22 -0
  255. package/examples/browser-session/fixture-server.mjs +64 -0
  256. package/examples/browser-session/human-login.yaml +31 -0
  257. package/examples/browser-session/merge.yaml +36 -0
  258. package/examples/browser-session/work-a.yaml +41 -0
  259. package/examples/browser-session/work-b.yaml +41 -0
  260. package/examples/conditional-fork/decide.yaml +1 -1
  261. package/examples/github-release/README.md +17 -3
  262. package/examples/stage-mcp/README.md +3 -2
  263. package/examples/stageflow.yaml +51 -0
  264. package/package.json +9 -4
  265. package/skills/stageflow/references/control-surface.md +3 -1
  266. package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
  267. package/skills/stageflow-run/SKILL.md +2 -0
  268. package/skills/stageflow-run/references/task-and-pipeline-selection.md +3 -1
  269. package/skills/stageflow-setup/SKILL.md +11 -0
  270. package/dist/ui/assets/index-B6hlSXXn.js +0 -120
@@ -0,0 +1,118 @@
1
+ import { loadFailure, loadSuccess } from "./loadOutcome.js";
2
+ import { mergeToolRequires } from "./toolRequires.js";
3
+ export const BROWSER_TOOL_NAME = "agent-browser";
4
+ const ALLOWED_KEYS = new Set(["profile", "headed", "allow_domains", "login_url", "check"]);
5
+ const CHECK_KEYS = new Set(["url", "logged_in_url", "logged_out_url"]);
6
+ const REJECTED_KEYS = new Set(["path", "scope", "secret"]);
7
+ const PROFILE_NAME = /^[A-Za-z0-9_-]{1,64}$/;
8
+ const DOMAIN = /^(\*\.)?[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
9
+ function isPlainObject(value) {
10
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11
+ }
12
+ function fail(label, stageId, message) {
13
+ return loadFailure([
14
+ {
15
+ code: "stage.invalid_browser",
16
+ message: `Invalid stage ${label}: browser ${message}`,
17
+ category: "stage",
18
+ ...(stageId !== undefined ? { stageId } : {}),
19
+ },
20
+ ]);
21
+ }
22
+ function nonEmptyString(value) {
23
+ return typeof value === "string" && value.trim() !== "";
24
+ }
25
+ function parseCheck(raw, label, stageId) {
26
+ if (!isPlainObject(raw)) {
27
+ return fail(label, stageId, "check must be an object");
28
+ }
29
+ for (const key of Object.keys(raw)) {
30
+ if (!CHECK_KEYS.has(key)) {
31
+ return fail(label, stageId, `check unknown key "${key}" (allowed: url, logged_in_url, logged_out_url)`);
32
+ }
33
+ }
34
+ if (!nonEmptyString(raw.url)) {
35
+ return fail(label, stageId, "check.url must be a non-empty string");
36
+ }
37
+ const check = {
38
+ url: raw.url.trim(),
39
+ };
40
+ if (raw.logged_in_url !== undefined) {
41
+ if (!nonEmptyString(raw.logged_in_url)) {
42
+ return fail(label, stageId, "check.logged_in_url must be a non-empty string");
43
+ }
44
+ check.logged_in_url = raw.logged_in_url.trim();
45
+ }
46
+ if (raw.logged_out_url !== undefined) {
47
+ const list = Array.isArray(raw.logged_out_url)
48
+ ? raw.logged_out_url
49
+ : [raw.logged_out_url];
50
+ if (list.length === 0 || !list.every(nonEmptyString)) {
51
+ return fail(label, stageId, "check.logged_out_url must be a non-empty string or a non-empty list of strings");
52
+ }
53
+ const trimmed = list.map((item) => item.trim());
54
+ check.logged_out_url = Array.isArray(raw.logged_out_url)
55
+ ? trimmed
56
+ : trimmed[0];
57
+ }
58
+ return loadSuccess(check);
59
+ }
60
+ export function parseStageBrowser(raw, label, stageId) {
61
+ if (raw === undefined)
62
+ return loadSuccess(undefined);
63
+ if (!isPlainObject(raw)) {
64
+ return fail(label, stageId, "must be an object");
65
+ }
66
+ for (const key of Object.keys(raw)) {
67
+ if (REJECTED_KEYS.has(key)) {
68
+ return fail(label, stageId, `"${key}" is not allowed (the Host chooses profile location and scope)`);
69
+ }
70
+ if (!ALLOWED_KEYS.has(key)) {
71
+ return fail(label, stageId, `unknown key "${key}" (allowed: profile, headed, allow_domains, login_url, check)`);
72
+ }
73
+ }
74
+ const browser = {};
75
+ if (raw.profile !== undefined) {
76
+ if (typeof raw.profile !== "string" || !PROFILE_NAME.test(raw.profile)) {
77
+ return fail(label, stageId, "profile must be 1-64 characters of letters, digits, dash, or underscore");
78
+ }
79
+ browser.profile = raw.profile;
80
+ }
81
+ if (raw.headed !== undefined) {
82
+ if (typeof raw.headed !== "boolean") {
83
+ return fail(label, stageId, "headed must be a boolean");
84
+ }
85
+ browser.headed = raw.headed;
86
+ }
87
+ if (raw.allow_domains !== undefined) {
88
+ if (!Array.isArray(raw.allow_domains) || raw.allow_domains.length === 0) {
89
+ return fail(label, stageId, "allow_domains must be a non-empty array of domain names");
90
+ }
91
+ const domains = [];
92
+ for (const item of raw.allow_domains) {
93
+ if (typeof item !== "string" || !DOMAIN.test(item.trim())) {
94
+ return fail(label, stageId, `allow_domains entry ${JSON.stringify(item)} must be a bare domain such as "example.com" or "*.example.com" (no scheme, port, or path)`);
95
+ }
96
+ domains.push(item.trim());
97
+ }
98
+ browser.allow_domains = domains;
99
+ }
100
+ if (raw.login_url !== undefined) {
101
+ if (!nonEmptyString(raw.login_url)) {
102
+ return fail(label, stageId, "login_url must be a non-empty string");
103
+ }
104
+ browser.login_url = raw.login_url.trim();
105
+ }
106
+ if (raw.check !== undefined) {
107
+ const check = parseCheck(raw.check, label, stageId);
108
+ if (!check.ok)
109
+ return check;
110
+ browser.check = check.value;
111
+ }
112
+ return loadSuccess(browser);
113
+ }
114
+ export function withBrowserRequires(stage, ctx) {
115
+ if (stage.browser === undefined)
116
+ return loadSuccess(stage.requires);
117
+ return mergeToolRequires([stage.requires, [{ tool: BROWSER_TOOL_NAME }]], { pipelineId: ctx.pipelineId, label: `stage "${ctx.stageId}"` });
118
+ }
@@ -0,0 +1,10 @@
1
+ import type { RunStore } from "../runstore/port.js";
2
+ import type { TriggerFile } from "../types/trigger.js";
3
+ export type TriggerListItem = TriggerFile & {
4
+ definition_ref: string;
5
+ last_fired_at?: string;
6
+ last_run_id?: string;
7
+ next_run_at?: string;
8
+ };
9
+ /** Catalog-authoritative trigger list, overlaid with each trigger's store-recorded fire state (if any). */
10
+ export declare function loadCatalogTriggers(cwd: string, store: RunStore): Promise<TriggerListItem[] | undefined>;
@@ -0,0 +1,37 @@
1
+ import path from "node:path";
2
+ import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
3
+ import { getCatalogScanPaths } from "./browseCatalog.js";
4
+ import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
5
+ import { loadTriggerOutcome } from "./loadTrigger.js";
6
+ /** Catalog-authoritative trigger list, overlaid with each trigger's store-recorded fire state (if any). */
7
+ export async function loadCatalogTriggers(cwd, store) {
8
+ const ctx = catalogContextFromStageflow(await resolveStageflowContext(cwd));
9
+ const scanPaths = await getCatalogScanPaths(ctx);
10
+ if (!scanPaths)
11
+ return undefined;
12
+ const projectRoot = ctx.projectRoot ?? undefined;
13
+ const items = [];
14
+ for (const filePath of scanPaths.triggerPaths) {
15
+ const outcome = await loadTriggerOutcome(filePath);
16
+ if (!outcome.ok)
17
+ continue;
18
+ const definitionRef = projectRoot !== undefined
19
+ ? path.relative(projectRoot, filePath).replace(/\\/g, "/")
20
+ : filePath;
21
+ const record = await store.getTrigger(outcome.value.id);
22
+ items.push({
23
+ ...outcome.value,
24
+ definition_ref: definitionRef,
25
+ ...(record?.last_fired_at !== undefined
26
+ ? { last_fired_at: record.last_fired_at }
27
+ : {}),
28
+ ...(record?.last_run_id !== undefined
29
+ ? { last_run_id: record.last_run_id }
30
+ : {}),
31
+ ...(record?.next_run_at !== undefined
32
+ ? { next_run_at: record.next_run_at }
33
+ : {}),
34
+ });
35
+ }
36
+ return items.sort((a, b) => a.id.localeCompare(b.id));
37
+ }
@@ -1,8 +1,9 @@
1
1
  import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
2
+ import type { TriggerFile } from "../types/trigger.js";
2
3
  import type { LoadIssue } from "./loadOutcome.js";
3
4
  export type ValidationSeverity = "error" | "warning";
4
- export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task";
5
- export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
5
+ export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task" | "trigger";
6
+ export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
6
7
  export type ValidationFinding = {
7
8
  severity: ValidationSeverity;
8
9
  code: ValidationFindingCode | string;
@@ -12,13 +13,14 @@ export type ValidationFinding = {
12
13
  pipelineId?: string;
13
14
  stageId?: string;
14
15
  };
15
- export type ValidationScope = "full" | "pipeline" | "task";
16
+ export type ValidationScope = "full" | "pipeline" | "task" | "trigger";
16
17
  export type ValidateCatalogOptions = {
17
18
  cwd?: string;
18
19
  projectRoot?: string;
19
20
  scope: ValidationScope;
20
21
  pipeline?: string;
21
22
  task?: string;
23
+ trigger?: string;
22
24
  strict?: boolean;
23
25
  };
24
26
  export type ValidationResult = {
@@ -59,4 +61,12 @@ export declare function loadPipelineValidated(nameOrPath: string | InlinePipelin
59
61
  projectRoot?: string;
60
62
  validateStages?: boolean;
61
63
  }): Promise<LoadPipelineValidatedResult>;
64
+ export declare function collectPipelineIdsFromPaths(pipelinePaths: string[]): Promise<Map<string, string[]>>;
65
+ export declare function collectTaskIdsFromPaths(taskPaths: string[]): Promise<Set<string>>;
66
+ export type TriggerCatalogRefs = {
67
+ pipelineIds: ReadonlySet<string>;
68
+ taskIds: ReadonlySet<string>;
69
+ };
70
+ /** Confirms a trigger's `pipeline`/`task` refs resolve against known catalog ids. */
71
+ export declare function findingsForTriggerRefs(cwd: string, absPath: string, trigger: TriggerFile, refs: TriggerCatalogRefs): ValidationFinding[];
62
72
  export declare function validateCatalog(options: ValidateCatalogOptions): Promise<ValidationResult>;
@@ -3,6 +3,7 @@ import { globalStageflowHome } from "../project/globalHome.js";
3
3
  import { INLINE_PIPELINE_PATH, loadPipelineFromObjectOutcome, loadPipelineOutcome, resolvePipelinePath, } from "./loadPipeline.js";
4
4
  import { loadStageOutcome } from "./loadStage.js";
5
5
  import { loadTaskOutcome } from "./loadTask.js";
6
+ import { loadTriggerOutcome } from "./loadTrigger.js";
6
7
  import { readYamlObject } from "./readYamlObject.js";
7
8
  import { resolveCatalogContext } from "./resolveCatalogContext.js";
8
9
  import { getCatalogScanPaths } from "./browseCatalog.js";
@@ -103,6 +104,15 @@ function findingTaskError(cwd, absPath, message, code) {
103
104
  category: "task",
104
105
  }, "error");
105
106
  }
107
+ function findingTriggerError(cwd, absPath, message, code) {
108
+ return baseFinding({
109
+ cwd,
110
+ absPath,
111
+ message,
112
+ code,
113
+ category: "trigger",
114
+ }, "error");
115
+ }
106
116
  function findingCatalog(cwd, absPath, message, code, severity) {
107
117
  return baseFinding({
108
118
  cwd,
@@ -142,6 +152,11 @@ export function findingsFromLoadIssues(cwd, absPath, issues) {
142
152
  findingTaskError(cwd, absPath, issue.message, issue.code),
143
153
  ];
144
154
  }
155
+ if (issue.category === "trigger") {
156
+ return [
157
+ findingTriggerError(cwd, absPath, issue.message, issue.code),
158
+ ];
159
+ }
145
160
  if (issue.category === "catalog") {
146
161
  const code = issue.code;
147
162
  const severity = code === "catalog.manifest_missing" ||
@@ -469,7 +484,7 @@ function findingManifestMissing(cwd, projectRoot) {
469
484
  function findingEmptyCatalog(cwd, manifestPath, kind) {
470
485
  return findingCatalog(cwd, manifestPath, `Manifest catalog.${kind} is empty`, "catalog.empty_catalog", "warning");
471
486
  }
472
- async function collectPipelineIdsFromPaths(pipelinePaths) {
487
+ export async function collectPipelineIdsFromPaths(pipelinePaths) {
473
488
  const idToPaths = new Map();
474
489
  for (const filePath of pipelinePaths) {
475
490
  try {
@@ -486,6 +501,20 @@ async function collectPipelineIdsFromPaths(pipelinePaths) {
486
501
  }
487
502
  return idToPaths;
488
503
  }
504
+ export async function collectTaskIdsFromPaths(taskPaths) {
505
+ const ids = new Set();
506
+ for (const filePath of taskPaths) {
507
+ try {
508
+ const raw = await readYamlObject(filePath);
509
+ if (typeof raw?.id === "string")
510
+ ids.add(raw.id);
511
+ }
512
+ catch {
513
+ // unreadable files handled by task validation loop
514
+ }
515
+ }
516
+ return ids;
517
+ }
489
518
  function findingsForDuplicatePipelineIds(cwd, idToPaths) {
490
519
  const findings = [];
491
520
  for (const [pipelineId, paths] of idToPaths) {
@@ -530,7 +559,7 @@ async function validateManifestAll(invocationCwd) {
530
559
  if (!scanPaths) {
531
560
  return findings;
532
561
  }
533
- const { pipelinePaths, taskPaths } = scanPaths;
562
+ const { pipelinePaths, taskPaths, triggerPaths } = scanPaths;
534
563
  for (const pipelinePath of pipelinePaths) {
535
564
  const core = await runPipelineValidation(pipelinePath, {
536
565
  cwd: projectRoot,
@@ -548,6 +577,32 @@ async function validateManifestAll(invocationCwd) {
548
577
  }
549
578
  const idToPaths = await collectPipelineIdsFromPaths(pipelinePaths);
550
579
  findings.push(...findingsForDuplicatePipelineIds(projectRoot, idToPaths));
580
+ if (triggerPaths.length > 0) {
581
+ const pipelineIds = new Set(idToPaths.keys());
582
+ const taskIds = await collectTaskIdsFromPaths(taskPaths);
583
+ for (const triggerPath of triggerPaths) {
584
+ const outcome = await loadTriggerOutcome(triggerPath);
585
+ if (!outcome.ok) {
586
+ findings.push(...findingsFromLoadIssues(projectRoot, triggerPath, outcome.issues));
587
+ continue;
588
+ }
589
+ findings.push(...findingsForTriggerRefs(projectRoot, triggerPath, outcome.value, {
590
+ pipelineIds,
591
+ taskIds,
592
+ }));
593
+ }
594
+ }
595
+ return findings;
596
+ }
597
+ /** Confirms a trigger's `pipeline`/`task` refs resolve against known catalog ids. */
598
+ export function findingsForTriggerRefs(cwd, absPath, trigger, refs) {
599
+ const findings = [];
600
+ if (!refs.pipelineIds.has(trigger.pipeline)) {
601
+ findings.push(findingTriggerError(cwd, absPath, `Trigger "${trigger.id}" references unknown pipeline "${trigger.pipeline}"`, "trigger.unknown_pipeline"));
602
+ }
603
+ if (trigger.task !== undefined && !refs.taskIds.has(trigger.task)) {
604
+ findings.push(findingTriggerError(cwd, absPath, `Trigger "${trigger.id}" references unknown task "${trigger.task}"`, "trigger.unknown_task"));
605
+ }
551
606
  return findings;
552
607
  }
553
608
  async function validateSingleTask(invocationCwd, taskArg) {
@@ -560,6 +615,26 @@ async function validateSingleTask(invocationCwd, taskArg) {
560
615
  }
561
616
  return [];
562
617
  }
618
+ async function validateSingleTrigger(invocationCwd, triggerArg) {
619
+ const ctx = await resolveCatalogContext(invocationCwd);
620
+ const relCwd = ctx.projectRoot ?? invocationCwd;
621
+ const triggerPath = path.resolve(invocationCwd, triggerArg);
622
+ const outcome = await loadTriggerOutcome(triggerPath);
623
+ if (!outcome.ok) {
624
+ return findingsFromLoadIssues(relCwd, triggerPath, outcome.issues);
625
+ }
626
+ const scanPaths = await getCatalogScanPaths(ctx);
627
+ const pipelineIds = scanPaths
628
+ ? new Set((await collectPipelineIdsFromPaths(scanPaths.pipelinePaths)).keys())
629
+ : new Set();
630
+ const taskIds = scanPaths
631
+ ? await collectTaskIdsFromPaths(scanPaths.taskPaths)
632
+ : new Set();
633
+ return findingsForTriggerRefs(relCwd, triggerPath, outcome.value, {
634
+ pipelineIds,
635
+ taskIds,
636
+ });
637
+ }
563
638
  export async function validateCatalog(options) {
564
639
  const cwd = options.cwd ?? process.cwd();
565
640
  const strict = options.strict ?? false;
@@ -569,6 +644,9 @@ export async function validateCatalog(options) {
569
644
  if (options.scope === "task" && !options.task) {
570
645
  throw new Error("validateCatalog: task is required when scope is \"task\"");
571
646
  }
647
+ if (options.scope === "trigger" && !options.trigger) {
648
+ throw new Error("validateCatalog: trigger is required when scope is \"trigger\"");
649
+ }
572
650
  const allFindings = [];
573
651
  if (options.scope === "pipeline") {
574
652
  return validatePipeline(options.pipeline, {
@@ -581,6 +659,9 @@ export async function validateCatalog(options) {
581
659
  else if (options.scope === "task") {
582
660
  allFindings.push(...(await validateSingleTask(cwd, options.task)));
583
661
  }
662
+ else if (options.scope === "trigger") {
663
+ allFindings.push(...(await validateSingleTrigger(cwd, options.trigger)));
664
+ }
584
665
  else {
585
666
  allFindings.push(...(await validateManifestAll(cwd)));
586
667
  }
@@ -45,6 +45,14 @@ export function validateCompletionContractForStage(stage, completion) {
45
45
  !stage.gate_kinds?.includes(check.kind)) {
46
46
  return failure(stage.id, `gate check "${check.id}" requires gate_kinds to include "${check.kind}"`);
47
47
  }
48
+ if (check.type === "browser_login") {
49
+ if (stage.browser?.check === undefined) {
50
+ return failure(stage.id, `check "${check.id}" requires the stage to declare browser.check`);
51
+ }
52
+ if (stage.browser.headed === false) {
53
+ return failure(stage.id, `check "${check.id}" is a human login check and needs a visible browser (browser.headed must not be false)`);
54
+ }
55
+ }
48
56
  if (check.type === "checkout_changes") {
49
57
  for (const field of check.path_fields ?? []) {
50
58
  if (!isRequiredStringArrayField(stage.payload_schema, field)) {
@@ -26,4 +26,4 @@ export declare function declaredArtifactIssue(artifacts: readonly string[], chec
26
26
  * `gate` check with no `readQaTrail` fails closed (cannot be verified). No
27
27
  * `checks` (or an empty list) is a no-op.
28
28
  */
29
- export declare function assertPreEmitChecks(envelope: Pick<StageEnvelope, "artifacts">, options: PreEmitCheckOptions | undefined): Promise<void>;
29
+ export declare function assertPreEmitChecks(envelope: Pick<StageEnvelope, "artifacts" | "payload">, options: PreEmitCheckOptions | undefined): Promise<void>;
@@ -10,6 +10,7 @@
10
10
  * why this stays separate from `CompletionCheck`'s `artifact`/`gate` runners.
11
11
  */
12
12
  import { preEmitGateIssue } from "../hitl/qaTrail.js";
13
+ import { loginCheckIssue } from "../browser/loginCheck.js";
13
14
  import { EnvelopeError } from "../types/envelope.js";
14
15
  function artifactListsBasename(artifact, basename) {
15
16
  return artifact === basename || artifact.endsWith(`/${basename}`);
@@ -42,6 +43,13 @@ export async function assertPreEmitChecks(envelope, options) {
42
43
  }
43
44
  continue;
44
45
  }
46
+ if (check.type === "browser_login_check") {
47
+ const issue = loginCheckIssue(envelope.payload, check);
48
+ if (issue !== undefined) {
49
+ throw new EnvelopeError(issue);
50
+ }
51
+ continue;
52
+ }
45
53
  // check.type === "gate"
46
54
  if (options?.readQaTrail === undefined) {
47
55
  throw new EnvelopeError("declared gate kinds cannot be verified without a QA trail reader");
@@ -1,3 +1,7 @@
1
+ /** Cookie/storage/header/token shapes from browser commands; applied only to browser stages. */
2
+ export declare const BROWSER_SECRET_PATTERNS: RegExp[];
3
+ /** Default patterns plus browser patterns, for `RedactOptions.patterns` on browser stages. */
4
+ export declare const BROWSER_STAGE_PATTERNS: RegExp[];
1
5
  export type NamedSecret = {
2
6
  name: string;
3
7
  value: string;
@@ -6,6 +6,23 @@ const SECRET_PATTERNS = [
6
6
  /\bBearer\s+[A-Za-z0-9\-._~+/]+=*/gi,
7
7
  /\b[A-Za-z_]*KEY[A-Za-z_]*\s*[:=]\s*['"]?[A-Za-z0-9+/_-]{16,}['"]?/gi,
8
8
  ];
9
+ /** Cookie/storage/header/token shapes from browser commands; applied only to browser stages. */
10
+ export const BROWSER_SECRET_PATTERNS = [
11
+ /\b(?:Set-Cookie|Cookie)["']?\s*:\s*["']?[^\r\n"']+/gi,
12
+ /\b(?:Proxy-)?Authorization["']?\s*[:=]\s*["']?[^\r\n"']+/gi,
13
+ /\b(?:X-Api-Key|X-Auth-Token|X-CSRF-Token|X-Xsrf-Token)["']?\s*[:=]\s*["']?[^\s"']+/gi,
14
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,
15
+ /"name"\s*:\s*"[^"]*"\s*,\s*"value"\s*:\s*"[^"]*"/g,
16
+ /"value"\s*:\s*"[^"]*"(?=\s*,\s*"domain")/g,
17
+ /\b(?:li_at|li_rm|JSESSIONID|sessionid|session_id|sessid|csrftoken|auth_token|access_token|refresh_token|id_token)\b["']?\s*[:=]\s*["']?[A-Za-z0-9%+/_.=-]{8,}/gi,
18
+ /\b(?:li_at|li_rm|JSESSIONID|sessionid)\b[ \t|]+[A-Za-z0-9%+/_.=-]{16,}/g,
19
+ /"[^"\s]*(?:token|secret|authorization|jwt|session|password|passwd|api_?key|credential|csrf)[^"\s]*"\s*:\s*"[^"]{8,}"/gi,
20
+ ];
21
+ /** Default patterns plus browser patterns, for `RedactOptions.patterns` on browser stages. */
22
+ export const BROWSER_STAGE_PATTERNS = [
23
+ ...SECRET_PATTERNS,
24
+ ...BROWSER_SECRET_PATTERNS,
25
+ ];
9
26
  const REDACTED = "[redacted]";
10
27
  export function shouldRegisterValue(value) {
11
28
  if (value.length < 8)
@@ -9,6 +9,8 @@ import { validateCatalog } from "../config/validateCatalog.js";
9
9
  import { writeAudit } from "../logging/audit.js";
10
10
  import { logger as rootLogger } from "../logging/logger.js";
11
11
  import { PACKAGE_VERSION, BUILD_SHA } from "../package-meta.js";
12
+ import { globalStageflowHome } from "../project/globalHome.js";
13
+ import { getAuthStatus, } from "../agent/providerAuth.js";
12
14
  import { PipelineValidationError } from "../runtime/pipelineValidationError.js";
13
15
  import { PipelinePreflightError } from "../runtime/pipelineRunner.js";
14
16
  import { EnvelopeRefError, resolveEnvelopeRefsToTask, } from "../runtime/resolveEnvelopeRef.js";
@@ -144,7 +146,7 @@ const runStatusSchema = z.enum([
144
146
  "cancelled",
145
147
  ]);
146
148
  export function registerCatalogTools(server, deps) {
147
- const { manager, store, cwd } = deps;
149
+ const { manager, store, cwd, providerAuthContext, providerBoot } = deps;
148
150
  const agentDir = deps.agentDir ?? getAgentDir();
149
151
  server.registerTool("list_skills", {
150
152
  description: "List skills with name, description, and origin (run|checkout|host). Optional runId scopes to that run's resolution view (run-tier skills first, then checkout .pi/skills, then host).",
@@ -201,6 +203,7 @@ export function registerCatalogTools(server, deps) {
201
203
  return textResult({
202
204
  pipelines: result.items,
203
205
  root_errors: result.root_errors,
206
+ ...(result.tip !== undefined ? { tip: result.tip } : {}),
204
207
  });
205
208
  });
206
209
  server.registerTool("list_tasks", {
@@ -225,6 +228,7 @@ export function registerCatalogTools(server, deps) {
225
228
  return textResult({
226
229
  tasks: result.items,
227
230
  root_errors: result.root_errors,
231
+ ...(result.tip !== undefined ? { tip: result.tip } : {}),
228
232
  });
229
233
  });
230
234
  server.registerTool("list_models", {
@@ -278,14 +282,45 @@ export function registerCatalogTools(server, deps) {
278
282
  return textResult({ runs });
279
283
  });
280
284
  server.registerTool("get_health", {
281
- description: "Server health and soft-max run capacity: activeRunIds, activeCount, maxConcurrent, slotsAvailable, activeStageProcesses, version, plus disk breakdown (disk.runs_bytes, worktrees_bytes, repos_bytes, state_db_bytes, a2a_artifacts_bytes, free_bytes) for the durable root. Start until slotsAvailable is 0; then wait for a run to finish or raise STAGEFLOW_MAX_CONCURRENT_RUNS.",
285
+ description: "Server health and soft-max run capacity: activeRunIds, activeCount, maxConcurrent, slotsAvailable, activeStageProcesses, version, stageflow_home, boot_providers (Host boot snapshot), providers_live (or pointer to list_providers), plus disk breakdown for the durable root. Start until slotsAvailable is 0; then wait for a run to finish or raise STAGEFLOW_MAX_CONCURRENT_RUNS.",
282
286
  inputSchema: z.object({}),
283
- }, async () => textResult({
284
- ...(await manager.getHealthWithDisk()),
285
- version: PACKAGE_VERSION,
286
- build_sha: BUILD_SHA,
287
- toolchain: toolchainHealthMap(),
288
- }));
287
+ }, async () => {
288
+ const boot_providers = providerBoot
289
+ ? {
290
+ configured: providerBoot.configured,
291
+ failures: providerBoot.failures,
292
+ }
293
+ : { configured: [], failures: [] };
294
+ let providers_live = {
295
+ configured: [],
296
+ note: "Live auth inspect — prefer list_providers / sf providers status. boot_providers is Host boot env only and may be empty when CLI credentials are configured.",
297
+ };
298
+ if (providerAuthContext !== undefined) {
299
+ try {
300
+ const status = await getAuthStatus(cwd, undefined, providerAuthContext);
301
+ const rows = Array.isArray(status) ? status : [status];
302
+ providers_live = {
303
+ configured: rows
304
+ .filter((r) => r.configured)
305
+ .map((r) => r.providerId),
306
+ note: providers_live.note,
307
+ };
308
+ }
309
+ catch {
310
+ /* keep empty configured + note */
311
+ }
312
+ }
313
+ return textResult({
314
+ ...(await manager.getHealthWithDisk()),
315
+ version: PACKAGE_VERSION,
316
+ build_sha: BUILD_SHA,
317
+ toolchain: toolchainHealthMap(),
318
+ stageflow_home: globalStageflowHome(),
319
+ boot_providers,
320
+ providers_live,
321
+ providers: boot_providers,
322
+ });
323
+ });
289
324
  server.registerTool("preflight", {
290
325
  description: "Check pipeline requires:/secrets:/mcp against the Host toolchain manifest and curated stage env before start_run. Accepts pipeline path or inline definition (same union as start_run). Returns { ok, checks } with status ok|missing_tool|tool_version_mismatch|unknown_version|secret_unavailable|unresolved_var. Does not create a Run. unknown_version is ok unless strict:true.",
291
326
  inputSchema: z.object({
@@ -82,7 +82,9 @@ export function registerControlTools(server, deps) {
82
82
  target_run_id: runId,
83
83
  outcome: "ok",
84
84
  });
85
- return textResult({ ok: true });
85
+ return textResult(result.verification !== undefined
86
+ ? { ok: true, verification: result.verification }
87
+ : { ok: true });
86
88
  });
87
89
  server.registerTool("decide_feedback_loop", {
88
90
  description: "Resolve a feedback-loop wait_for_human decision (extend, continue, or abandon). Same semantics as POST /api/runs/:id/stages/:stageId/feedback-decision.",
@@ -1,3 +1,4 @@
1
+ import type { BootProviderResult } from "../agent/bootProviderConfig.js";
1
2
  import type { ProviderAuthContext } from "../agent/providerAuth.js";
2
3
  import type { RunStore } from "../runstore/port.js";
3
4
  import type { RunChangeBus } from "../runtime/runChangeBus.js";
@@ -9,6 +10,7 @@ export type McpToolDeps = {
9
10
  agentDir?: string;
10
11
  providerAuthContext?: ProviderAuthContext;
11
12
  projectRoot?: string;
13
+ providerBoot?: BootProviderResult;
12
14
  };
13
15
  export type McpHttpDeps = McpToolDeps & {
14
16
  runChangeBus: RunChangeBus;
@@ -43,6 +43,7 @@ function toolDepsFrom(deps) {
43
43
  ? { providerAuthContext: deps.providerAuthContext }
44
44
  : {}),
45
45
  ...(deps.projectRoot !== undefined ? { projectRoot: deps.projectRoot } : {}),
46
+ ...(deps.providerBoot !== undefined ? { providerBoot: deps.providerBoot } : {}),
46
47
  };
47
48
  }
48
49
  function attachBusNotifications(server, bus) {
package/dist/mcp/tools.js CHANGED
@@ -4,6 +4,7 @@ import { registerCheckoutTools } from "./checkoutTools.js";
4
4
  import { registerControlTools } from "./controlTools.js";
5
5
  import { registerProjectMcpTools } from "./projectMcpTools.js";
6
6
  import { registerProviderTools } from "./providerTools.js";
7
+ import { registerTriggerTools } from "./triggerTools.js";
7
8
  import { textResult } from "./toolResults.js";
8
9
  import { DEFAULT_TIMEOUT_MS, waitRun } from "./waitRun.js";
9
10
  import { registerGetStartedTool } from "./getStartedTool.js";
@@ -15,6 +16,7 @@ export function registerMcpTools(server, deps) {
15
16
  registerCheckoutTools(server, deps);
16
17
  registerProviderTools(server, deps);
17
18
  registerProjectMcpTools(server, deps);
19
+ registerTriggerTools(server, deps);
18
20
  server.registerTool("wait_run", {
19
21
  description: "Long-poll until a run is waiting for HITL input and/or terminal (succeeded/failed), or until timeout_ms. Default until=any wakes on waiting OR terminal. Abort cancels only this wait (not the run). Optional notifications/progress when the client supplies _meta.progressToken. Compose: wait_run → answer_gate → wait_run.",
20
22
  inputSchema: z.object({
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ import type { McpToolDeps } from "./deps.js";
3
+ export declare function registerTriggerTools(server: McpServer, deps: McpToolDeps): void;