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,144 @@
1
+ import { readDraftFromContext } from "./draftContext.js";
2
+ const UNDO_CONFLICT_NOTICE = "Undo blocked: the draft changed after this mutation. Ask the agent to reverse the change instead of rejecting.";
3
+ export function draftFingerprint(draft) {
4
+ return JSON.stringify(draft);
5
+ }
6
+ export function formatArtifact(value) {
7
+ return JSON.stringify(value, null, 2);
8
+ }
9
+ function stageIds(draft) {
10
+ return draft.pipeline.stages.map((stage, index) => typeof stage.id === "string" && stage.id ? stage.id : `stage-${index}`);
11
+ }
12
+ export function diffDraftPackages(before, after) {
13
+ const diffs = [];
14
+ const pipelinePath = `${after.pipeline.id || before.pipeline.id || "pipeline"}.pipeline.yaml`;
15
+ if (JSON.stringify(before.pipeline) !== JSON.stringify(after.pipeline)) {
16
+ diffs.push({
17
+ path: pipelinePath,
18
+ kind: "modified",
19
+ before: formatArtifact(before.pipeline),
20
+ after: formatArtifact(after.pipeline),
21
+ });
22
+ }
23
+ const beforeStages = new Map((before.stages ?? []).map((s) => [s.path, s]));
24
+ const afterStages = new Map((after.stages ?? []).map((s) => [s.path, s]));
25
+ for (const [path, art] of afterStages) {
26
+ const prev = beforeStages.get(path);
27
+ if (!prev) {
28
+ diffs.push({
29
+ path,
30
+ kind: "added",
31
+ after: formatArtifact(art.body),
32
+ });
33
+ }
34
+ else if (JSON.stringify(prev.body) !== JSON.stringify(art.body)) {
35
+ diffs.push({
36
+ path,
37
+ kind: "modified",
38
+ before: formatArtifact(prev.body),
39
+ after: formatArtifact(art.body),
40
+ });
41
+ }
42
+ }
43
+ for (const [path, art] of beforeStages) {
44
+ if (!afterStages.has(path)) {
45
+ diffs.push({
46
+ path,
47
+ kind: "removed",
48
+ before: formatArtifact(art.body),
49
+ });
50
+ }
51
+ }
52
+ const beforeTask = before.task
53
+ ? formatArtifact({ filename: before.task.filename, body: before.task.body })
54
+ : null;
55
+ const afterTask = after.task
56
+ ? formatArtifact({ filename: after.task.filename, body: after.task.body })
57
+ : null;
58
+ if (beforeTask !== afterTask) {
59
+ const path = after.task?.filename ?? before.task?.filename ?? "task.task.yaml";
60
+ if (!beforeTask && afterTask) {
61
+ diffs.push({ path, kind: "added", after: afterTask });
62
+ }
63
+ else if (beforeTask && !afterTask) {
64
+ diffs.push({ path, kind: "removed", before: beforeTask });
65
+ }
66
+ else {
67
+ diffs.push({
68
+ path,
69
+ kind: "modified",
70
+ before: beforeTask ?? undefined,
71
+ after: afterTask ?? undefined,
72
+ });
73
+ }
74
+ }
75
+ return diffs;
76
+ }
77
+ export function affectedStageIds(before, after) {
78
+ const beforeSet = new Set(stageIds(before));
79
+ const afterSet = new Set(stageIds(after));
80
+ const ids = new Set();
81
+ for (const id of afterSet) {
82
+ if (!beforeSet.has(id))
83
+ ids.add(id);
84
+ }
85
+ for (const id of beforeSet) {
86
+ if (!afterSet.has(id))
87
+ ids.add(id);
88
+ }
89
+ const beforeByPath = new Map((before.stages ?? []).map((s) => [s.path, s]));
90
+ for (const art of after.stages ?? []) {
91
+ const prev = beforeByPath.get(art.path);
92
+ if (!prev || JSON.stringify(prev.body) !== JSON.stringify(art.body)) {
93
+ const id = typeof art.body.id === "string" && art.body.id
94
+ ? art.body.id
95
+ : undefined;
96
+ if (id)
97
+ ids.add(id);
98
+ }
99
+ }
100
+ for (const art of before.stages ?? []) {
101
+ if (!(after.stages ?? []).some((s) => s.path === art.path)) {
102
+ const id = typeof art.body.id === "string" && art.body.id
103
+ ? art.body.id
104
+ : undefined;
105
+ if (id)
106
+ ids.add(id);
107
+ }
108
+ }
109
+ for (let i = 0; i < after.pipeline.stages.length; i += 1) {
110
+ const a = after.pipeline.stages[i];
111
+ const b = before.pipeline.stages[i];
112
+ const id = typeof a.id === "string" && a.id ? a.id : `stage-${i}`;
113
+ if (!b || JSON.stringify(a) !== JSON.stringify(b)) {
114
+ ids.add(id);
115
+ }
116
+ }
117
+ return [...ids];
118
+ }
119
+ export function enrichProposal(proposal, currentContext) {
120
+ const baseDraft = readDraftFromContext(proposal.baseContext ?? currentContext);
121
+ const nextDraft = readDraftFromContext(proposal.nextContext);
122
+ const baseContext = proposal.baseContext ??
123
+ { draft: baseDraft };
124
+ return {
125
+ ...proposal,
126
+ baseContext,
127
+ baseFingerprint: proposal.baseFingerprint ?? draftFingerprint(baseDraft),
128
+ appliedFingerprint: proposal.appliedFingerprint ?? draftFingerprint(nextDraft),
129
+ artifacts: proposal.artifacts ?? diffDraftPackages(baseDraft, nextDraft),
130
+ affectedStageIds: proposal.affectedStageIds ?? affectedStageIds(baseDraft, nextDraft),
131
+ };
132
+ }
133
+ /** True when the live draft no longer matches the post-apply fingerprint. */
134
+ export function isMutationConflict(mutation, currentContext) {
135
+ const current = draftFingerprint(readDraftFromContext(currentContext));
136
+ if (mutation.appliedFingerprint) {
137
+ return mutation.appliedFingerprint !== current;
138
+ }
139
+ const next = draftFingerprint(readDraftFromContext(mutation.nextContext));
140
+ return next !== current;
141
+ }
142
+ export const UNDO_MUTATION_CONFLICT_NOTICE = UNDO_CONFLICT_NOTICE;
143
+ /** @deprecated Prefer UNDO_MUTATION_CONFLICT_NOTICE — kept for callers mid-migration. */
144
+ export const STALE_PROPOSAL_NOTICE = UNDO_CONFLICT_NOTICE;
@@ -0,0 +1,128 @@
1
+ import type { DraftPackage } from "../config/draftPackage.js";
2
+ import type { ValidationResult } from "../config/validateCatalog.js";
3
+ import type { AcceptProposalResult, ProposalArtifactDiff, UndoMutationResult } from "./proposals.js";
4
+ export type OperatorAgentToolResult = {
5
+ ok: boolean;
6
+ content: unknown;
7
+ error?: string;
8
+ };
9
+ export type OperatorAgentToolHandler = (args: Record<string, unknown>, ctx: OperatorAgentToolContext) => Promise<OperatorAgentToolResult> | OperatorAgentToolResult;
10
+ export type OperatorAgentTool = {
11
+ name: string;
12
+ description: string;
13
+ handler: OperatorAgentToolHandler;
14
+ };
15
+ export type OperatorAgentToolContext = {
16
+ getContext(): unknown;
17
+ setContext(next: unknown): void;
18
+ /** Apply mutation immediately and record an undo receipt. */
19
+ emitProposal(proposal: OperatorAgentProposal): void;
20
+ };
21
+ export type OperatorAgentProposal = {
22
+ /** Mutation id (Accept confirms; Reject / undoMutation soft-undos). */
23
+ id: string;
24
+ summary: string;
25
+ /** Context after the mutation (already applied to the session). */
26
+ nextContext: unknown;
27
+ /** Context snapshot before the mutation (restore target for undo). */
28
+ baseContext?: unknown;
29
+ /** Fingerprint of the draft before apply. */
30
+ baseFingerprint?: string;
31
+ /** Fingerprint of the draft immediately after apply — undo requires a match. */
32
+ appliedFingerprint?: string;
33
+ artifacts?: ProposalArtifactDiff[];
34
+ affectedStageIds?: string[];
35
+ };
36
+ export type OperatorAgentContextAdapter = {
37
+ serialize(context: unknown): unknown;
38
+ applyProposal(context: unknown, proposal: OperatorAgentProposal): unknown;
39
+ };
40
+ export type OperatorAgentProfile = {
41
+ id: string;
42
+ title: string;
43
+ playbook: string;
44
+ tools: OperatorAgentTool[];
45
+ contextAdapter: OperatorAgentContextAdapter;
46
+ greeting?: string;
47
+ };
48
+ export type OperatorAgentSessionEvent = {
49
+ type: "message";
50
+ role: "assistant" | "user" | "system";
51
+ text: string;
52
+ } | {
53
+ type: "proposal";
54
+ proposal: OperatorAgentProposal;
55
+ } | {
56
+ type: "tool_result";
57
+ name: string;
58
+ result: OperatorAgentToolResult;
59
+ } | {
60
+ type: "validation";
61
+ result: ValidationResult;
62
+ } | {
63
+ type: "error";
64
+ message: string;
65
+ };
66
+ export type WorkshopToolActivityUpdate = {
67
+ id: string;
68
+ name: string;
69
+ status: "running" | "complete" | "error";
70
+ target?: string;
71
+ errorMessage?: string;
72
+ draft?: DraftPackage;
73
+ /** Build the turn is editing. Absent when the chat is unlinked. */
74
+ buildId?: string;
75
+ };
76
+ export type OperatorAgentSession = {
77
+ readonly profileId: string;
78
+ readonly profileTitle: string;
79
+ getContext(): unknown;
80
+ setContext(next: unknown): void;
81
+ /** Latest undoable mutation (Accept-card payload), or null. */
82
+ getPendingProposal(): OperatorAgentProposal | null;
83
+ getMutation(mutationId: string): OperatorAgentProposal | null;
84
+ /**
85
+ * Seed backend from prior transcript after process restart (KTD7).
86
+ * No-op for fake / models without prepareRestart.
87
+ */
88
+ prepareRestart?(transcript: readonly {
89
+ role: string;
90
+ text: string;
91
+ }[]): Promise<void>;
92
+ send(message: string, options?: {
93
+ onDelta?: (text: string) => void;
94
+ onActivity?: (update: WorkshopToolActivityUpdate) => void;
95
+ modelId?: string;
96
+ }): Promise<OperatorAgentSessionEvent[]>;
97
+ /** Interrupt the in-flight prompt. Resolves when the backend is idle. */
98
+ abort(): Promise<void>;
99
+ /** Confirm mutation (soft UX); draft already applied — does not re-apply. */
100
+ acceptProposal(proposalId?: string): AcceptProposalResult;
101
+ /** Soft-undo the mutation when the applied fingerprint still matches. */
102
+ rejectProposal(proposalId?: string): UndoMutationResult;
103
+ undoMutation(mutationId?: string): UndoMutationResult;
104
+ close(): void;
105
+ };
106
+ export type OperatorAgentHost = {
107
+ registerProfile(profile: OperatorAgentProfile): void;
108
+ getProfile(id: string): OperatorAgentProfile | undefined;
109
+ listProfiles(): OperatorAgentProfile[];
110
+ openSession(input: {
111
+ profileId: string;
112
+ context: unknown;
113
+ }): OperatorAgentSession;
114
+ };
115
+ export type WorkshopDraftContext = {
116
+ draft: DraftPackage;
117
+ destination?: {
118
+ directory: string;
119
+ pipelineFilename?: string;
120
+ };
121
+ /** Catalog write root for the save tool (usually the operator project). */
122
+ projectRoot?: string;
123
+ /** Workshop chat session that owns this host context. */
124
+ chatSessionId?: string;
125
+ /** Build this turn is editing. Absent when that chat is unlinked. */
126
+ buildId?: string;
127
+ };
128
+ export type { AcceptProposalResult, ProposalArtifactDiff, UndoMutationResult };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,3 @@
1
1
  export declare const PACKAGE_NAME = "stageflow";
2
- export declare const PACKAGE_VERSION = "0.27.0";
2
+ export declare const PACKAGE_VERSION = "0.31.0";
3
3
  export declare const BUILD_SHA: string;
@@ -1,3 +1,3 @@
1
1
  export const PACKAGE_NAME = "stageflow";
2
- export const PACKAGE_VERSION = "0.27.0";
2
+ export const PACKAGE_VERSION = "0.31.0";
3
3
  export const BUILD_SHA = process.env.STAGEFLOW_BUILD_SHA ?? "unknown";
@@ -0,0 +1,3 @@
1
+ /** Walk up from `startDir` for the nearest `stageflow.yaml`, independent of git. */
2
+ export declare function findManifestRoot(startDir: string): string | null;
3
+ export declare function clearFindManifestRootCacheForTests(): void;
@@ -0,0 +1,35 @@
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import path from "node:path";
3
+ const cache = new Map();
4
+ function normalizeRoot(dir) {
5
+ try {
6
+ return realpathSync(path.resolve(dir));
7
+ }
8
+ catch {
9
+ return path.resolve(dir);
10
+ }
11
+ }
12
+ /** Walk up from `startDir` for the nearest `stageflow.yaml`, independent of git. */
13
+ export function findManifestRoot(startDir) {
14
+ const normalized = path.resolve(startDir);
15
+ if (cache.has(normalized)) {
16
+ return cache.get(normalized) ?? null;
17
+ }
18
+ let dir = normalized;
19
+ let result = null;
20
+ for (;;) {
21
+ if (existsSync(path.join(dir, "stageflow.yaml"))) {
22
+ result = normalizeRoot(dir);
23
+ break;
24
+ }
25
+ const parent = path.dirname(dir);
26
+ if (parent === dir)
27
+ break;
28
+ dir = parent;
29
+ }
30
+ cache.set(normalized, result);
31
+ return result;
32
+ }
33
+ export function clearFindManifestRootCacheForTests() {
34
+ cache.clear();
35
+ }
@@ -8,4 +8,11 @@ export type StageflowContext = ProjectContext & {
8
8
  manifestIssues: LoadIssue[];
9
9
  };
10
10
  export declare function projectContextFromStageflow(ctx: StageflowContext): ProjectContext;
11
+ /**
12
+ * Project root resolution is keyed off the nearest `stageflow.yaml`, not git:
13
+ * a manifest defines a project on its own, the same way a `package.json`
14
+ * does for npm. Git is checked independently (`isGitProject`) for callers
15
+ * that need it for execution (checkout/worktrees), not as a precondition
16
+ * for finding or reading the catalog.
17
+ */
11
18
  export declare function resolveStageflowContext(invocationCwd: string): Promise<StageflowContext>;
@@ -2,6 +2,7 @@ import { access } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { loadStageflowManifestOutcome, manifestPathForProject, } from "../config/loadStageflowManifest.js";
5
+ import { findManifestRoot } from "./findManifestRoot.js";
5
6
  import { findProjectRoot } from "./findProjectRoot.js";
6
7
  import { globalStageflowHome } from "./globalHome.js";
7
8
  export function projectContextFromStageflow(ctx) {
@@ -12,55 +13,70 @@ export function projectContextFromStageflow(ctx) {
12
13
  isGitProject: ctx.isGitProject,
13
14
  };
14
15
  }
16
+ /**
17
+ * Project root resolution is keyed off the nearest `stageflow.yaml`, not git:
18
+ * a manifest defines a project on its own, the same way a `package.json`
19
+ * does for npm. Git is checked independently (`isGitProject`) for callers
20
+ * that need it for execution (checkout/worktrees), not as a precondition
21
+ * for finding or reading the catalog.
22
+ */
15
23
  export async function resolveStageflowContext(invocationCwd) {
16
24
  const resolvedInvocation = path.resolve(invocationCwd);
17
- const gitRoot = findProjectRoot(resolvedInvocation);
18
25
  const globalHome = globalStageflowHome();
19
- if (gitRoot === null) {
26
+ const manifestRoot = findManifestRoot(resolvedInvocation);
27
+ if (manifestRoot !== null) {
28
+ const isGitProject = findProjectRoot(manifestRoot) !== null;
29
+ const manifestPath = manifestPathForProject(manifestRoot);
30
+ try {
31
+ await access(manifestPath);
32
+ }
33
+ catch {
34
+ return resolveWithoutManifest(resolvedInvocation, globalHome);
35
+ }
36
+ const outcome = await loadStageflowManifestOutcome(manifestRoot);
37
+ if (!outcome.ok) {
38
+ return {
39
+ invocationCwd: resolvedInvocation,
40
+ projectRoot: manifestRoot,
41
+ globalHome,
42
+ isGitProject,
43
+ manifest: null,
44
+ manifestStatus: "invalid",
45
+ manifestIssues: outcome.issues,
46
+ };
47
+ }
20
48
  return {
21
49
  invocationCwd: resolvedInvocation,
22
- projectRoot: os.homedir(),
50
+ projectRoot: manifestRoot,
23
51
  globalHome,
24
- isGitProject: false,
25
- manifest: null,
26
- manifestStatus: "not_git",
52
+ isGitProject,
53
+ manifest: outcome.value,
54
+ manifestStatus: "ok",
27
55
  manifestIssues: [],
28
56
  };
29
57
  }
30
- const manifestPath = manifestPathForProject(gitRoot);
31
- try {
32
- await access(manifestPath);
33
- }
34
- catch {
58
+ return resolveWithoutManifest(resolvedInvocation, globalHome);
59
+ }
60
+ function resolveWithoutManifest(resolvedInvocation, globalHome) {
61
+ const gitRoot = findProjectRoot(resolvedInvocation);
62
+ if (gitRoot === null) {
35
63
  return {
36
64
  invocationCwd: resolvedInvocation,
37
- projectRoot: gitRoot,
65
+ projectRoot: os.homedir(),
38
66
  globalHome,
39
- isGitProject: true,
67
+ isGitProject: false,
40
68
  manifest: null,
41
- manifestStatus: "missing",
69
+ manifestStatus: "not_git",
42
70
  manifestIssues: [],
43
71
  };
44
72
  }
45
- const outcome = await loadStageflowManifestOutcome(gitRoot);
46
- if (!outcome.ok) {
47
- return {
48
- invocationCwd: resolvedInvocation,
49
- projectRoot: gitRoot,
50
- globalHome,
51
- isGitProject: true,
52
- manifest: null,
53
- manifestStatus: "invalid",
54
- manifestIssues: outcome.issues,
55
- };
56
- }
57
73
  return {
58
74
  invocationCwd: resolvedInvocation,
59
75
  projectRoot: gitRoot,
60
76
  globalHome,
61
77
  isGitProject: true,
62
- manifest: outcome.value,
63
- manifestStatus: "ok",
78
+ manifest: null,
79
+ manifestStatus: "missing",
64
80
  manifestIssues: [],
65
81
  };
66
82
  }
@@ -1,9 +1,13 @@
1
1
  import { accessSync, constants, mkdirSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { globalStageflowHome } from "../project/globalHome.js";
2
4
  import { StoreOpenError } from "./sqlite/storeOpenError.js";
3
5
  export function assertTmpdirUsable(env = process.env) {
4
- const raw = env.TMPDIR ?? env.TMP ?? env.TEMP;
6
+ let raw = env.TMPDIR ?? env.TMP ?? env.TEMP;
5
7
  if (raw === undefined || raw.trim() === "") {
6
- throw new StoreOpenError("tmpdir_unusable: TMPDIR is unset. Set TMPDIR to a writable path (supported writable set: $STAGEFLOW_HOME and TMPDIR).", "tmpdir_unusable");
8
+ const soft = path.join(globalStageflowHome(), "tmp");
9
+ process.env.TMPDIR = soft;
10
+ raw = soft;
7
11
  }
8
12
  const dir = raw.trim();
9
13
  try {
@@ -32,7 +32,7 @@ export declare function durableRootDiskBreakdown(durableRoot: string, options?:
32
32
  export declare function parseDiskBytesThreshold(raw: string | undefined, filesystemTotalBytes: number): number | undefined;
33
33
  /**
34
34
  * Resolve `STAGEFLOW_MIN_FREE_DISK_BYTES`: bytes or `%` of filesystem.
35
- * Unset / empty / invalid → `max(2 GiB, 10% of filesystemTotalBytes)`.
35
+ * Unset / empty / invalid → `0` (admission check disabled). Set explicitly to enable.
36
36
  */
37
37
  export declare function resolveMinFreeDiskFloor(raw: string | undefined, filesystemTotalBytes: number): number;
38
38
  /**
@@ -162,20 +162,15 @@ export function parseDiskBytesThreshold(raw, filesystemTotalBytes) {
162
162
  return undefined;
163
163
  return n;
164
164
  }
165
- const DEFAULT_MIN_FREE_DISK_BYTES = 2 * 1024 * 1024 * 1024;
166
165
  /**
167
166
  * Resolve `STAGEFLOW_MIN_FREE_DISK_BYTES`: bytes or `%` of filesystem.
168
- * Unset / empty / invalid → `max(2 GiB, 10% of filesystemTotalBytes)`.
167
+ * Unset / empty / invalid → `0` (admission check disabled). Set explicitly to enable.
169
168
  */
170
169
  export function resolveMinFreeDiskFloor(raw, filesystemTotalBytes) {
171
170
  const parsed = parseDiskBytesThreshold(raw, filesystemTotalBytes);
172
171
  if (parsed !== undefined)
173
172
  return parsed;
174
- const total = Number.isFinite(filesystemTotalBytes) && filesystemTotalBytes > 0
175
- ? filesystemTotalBytes
176
- : 0;
177
- const tenPercent = Math.floor(total * 0.1);
178
- return Math.max(DEFAULT_MIN_FREE_DISK_BYTES, tenPercent);
173
+ return 0;
179
174
  }
180
175
  /**
181
176
  * One named warning when free space is below `STAGEFLOW_DISK_WARN_BYTES`. Never throws.
@@ -248,6 +248,22 @@ export type CreateForkGenerationInput = Omit<ForkGenerationRecord, "run_id" | "s
248
248
  status?: ForkGenerationStatus;
249
249
  };
250
250
  export type ForkGenerationPatch = Partial<Pick<ForkGenerationRecord, "status">>;
251
+ /** Durable registration of a catalog-owned `*.trigger.yaml` definition. */
252
+ export type TriggerRecord = {
253
+ id: string;
254
+ definition_ref: string;
255
+ enabled: boolean;
256
+ last_fired_at?: string;
257
+ last_run_id?: string;
258
+ next_run_at?: string;
259
+ created_at: string;
260
+ updated_at: string;
261
+ };
262
+ export type UpsertTriggerInput = {
263
+ id: string;
264
+ definitionRef: string;
265
+ enabled: boolean;
266
+ };
251
267
  export type CompactStage = {
252
268
  id: string;
253
269
  status: StageSnapshot["status"];
@@ -446,6 +462,18 @@ export interface RunStore {
446
462
  ensureProject(absPath: string): Promise<string>;
447
463
  /** Absolute roots from the durable projects registry. */
448
464
  listRegisteredProjects(): Promise<string[]>;
465
+ /** Idempotent create-or-update of a trigger's catalog registration. */
466
+ upsertTrigger(input: UpsertTriggerInput): Promise<TriggerRecord>;
467
+ getTrigger(id: string): Promise<TriggerRecord | null>;
468
+ listTriggers(): Promise<TriggerRecord[]>;
469
+ /** Record a fire: stamps last_fired_at/last_run_id. Throws if the trigger is missing. */
470
+ recordTriggerFired(id: string, runId: string): Promise<void>;
471
+ /** Persist the next computed fire time for a schedule-kind trigger. Throws if the trigger is missing. */
472
+ setTriggerNextRun(id: string, nextRunAt: string): Promise<void>;
473
+ /** Read a single adapter-owned state value for a trigger, or null if unset. */
474
+ getTriggerAdapterState(triggerId: string, key: string): Promise<string | null>;
475
+ /** Upsert a single adapter-owned state value for a trigger. */
476
+ setTriggerAdapterState(triggerId: string, key: string, value: string): Promise<void>;
449
477
  readRun(runId: string): Promise<RunDetail>;
450
478
  updatePipelineDag(runId: string, dag: RunPipelineDagSnapshot): Promise<void>;
451
479
  createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
@@ -1,7 +1,7 @@
1
1
  import Database from "better-sqlite3";
2
2
  import type { StageEnvelope } from "../../types/envelope.js";
3
3
  import type { StageLogLine } from "../../agent/activity.js";
4
- import { type CreateFeedbackLoopInput, type CreateFeedbackReplayInput, type CreateFeedbackReplayStagePassInput, type CreateForkGenerationInput, type CreateRunInput, type CreatedRun, type FeedbackLoopPatch, type FeedbackLoopRecord, type FeedbackLoopState, type FeedbackReplayPatch, type FeedbackReplayRecord, type FeedbackReplayStagePassPatch, type FeedbackReplayStagePassRecord, type ForkGenerationPatch, type ForkGenerationRecord, type ListRunsFilter, type RunDetail, type RunMeta, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent, type VerificationCheckResult, type VerificationCheckResultPatch, type ConfigOriginRecord } from "../port.js";
4
+ import { type CreateFeedbackLoopInput, type CreateFeedbackReplayInput, type CreateFeedbackReplayStagePassInput, type CreateForkGenerationInput, type CreateRunInput, type CreatedRun, type FeedbackLoopPatch, type FeedbackLoopRecord, type FeedbackLoopState, type FeedbackReplayPatch, type FeedbackReplayRecord, type FeedbackReplayStagePassPatch, type FeedbackReplayStagePassRecord, type ForkGenerationPatch, type ForkGenerationRecord, type ListRunsFilter, type RunDetail, type RunMeta, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent, type TriggerRecord, type UpsertTriggerInput, type VerificationCheckResult, type VerificationCheckResultPatch, type ConfigOriginRecord } from "../port.js";
5
5
  import { type RunSubmissionRecord } from "../submission.js";
6
6
  type SqliteRunStoreOpenerMode = "assert" | "migrate";
7
7
  export declare class SqliteRunStore implements RunStore {
@@ -77,6 +77,13 @@ export declare class SqliteRunStore implements RunStore {
77
77
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
78
78
  ensureProject(absPath: string): Promise<string>;
79
79
  listRegisteredProjects(): Promise<string[]>;
80
+ upsertTrigger(input: UpsertTriggerInput): Promise<TriggerRecord>;
81
+ getTrigger(id: string): Promise<TriggerRecord | null>;
82
+ listTriggers(): Promise<TriggerRecord[]>;
83
+ recordTriggerFired(id: string, runId: string): Promise<void>;
84
+ setTriggerNextRun(id: string, nextRunAt: string): Promise<void>;
85
+ getTriggerAdapterState(triggerId: string, key: string): Promise<string | null>;
86
+ setTriggerAdapterState(triggerId: string, key: string, value: string): Promise<void>;
80
87
  readRun(runId: string): Promise<RunDetail>;
81
88
  createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
82
89
  getFeedbackLoop(runId: string, loopId: string): Promise<FeedbackLoopRecord>;
@@ -72,6 +72,18 @@ function feedbackLoopFromRow(row) {
72
72
  updated_at: row.updated_at,
73
73
  };
74
74
  }
75
+ function triggerFromRow(row) {
76
+ return {
77
+ id: row.id,
78
+ definition_ref: row.definition_ref,
79
+ enabled: row.enabled !== 0,
80
+ ...(row.last_fired_at != null ? { last_fired_at: row.last_fired_at } : {}),
81
+ ...(row.last_run_id != null ? { last_run_id: row.last_run_id } : {}),
82
+ ...(row.next_run_at != null ? { next_run_at: row.next_run_at } : {}),
83
+ created_at: row.created_at,
84
+ updated_at: row.updated_at,
85
+ };
86
+ }
75
87
  function feedbackReplayFromRow(row) {
76
88
  return {
77
89
  run_id: row.run_id,
@@ -870,6 +882,84 @@ export class SqliteRunStore {
870
882
  .all();
871
883
  return rows.map((row) => row.project_root);
872
884
  }
885
+ async upsertTrigger(input) {
886
+ await this.ready();
887
+ const now = new Date().toISOString();
888
+ this.db
889
+ .prepare(`INSERT INTO triggers (id, definition_ref, enabled, created_at, updated_at)
890
+ VALUES (@id, @definition_ref, @enabled, @created_at, @updated_at)
891
+ ON CONFLICT(id) DO UPDATE SET
892
+ definition_ref = @definition_ref,
893
+ enabled = @enabled,
894
+ updated_at = @updated_at`)
895
+ .run({
896
+ id: input.id,
897
+ definition_ref: input.definitionRef,
898
+ enabled: input.enabled ? 1 : 0,
899
+ created_at: now,
900
+ updated_at: now,
901
+ });
902
+ const record = await this.getTrigger(input.id);
903
+ if (!record)
904
+ throw new Error(`Trigger not found after upsert: ${input.id}`);
905
+ return record;
906
+ }
907
+ async getTrigger(id) {
908
+ await this.ready();
909
+ const row = this.db
910
+ .prepare(`SELECT id, definition_ref, enabled, last_fired_at, last_run_id, next_run_at, created_at, updated_at
911
+ FROM triggers WHERE id = ?`)
912
+ .get(id);
913
+ return row ? triggerFromRow(row) : null;
914
+ }
915
+ async listTriggers() {
916
+ await this.ready();
917
+ const rows = this.db
918
+ .prepare(`SELECT id, definition_ref, enabled, last_fired_at, last_run_id, next_run_at, created_at, updated_at
919
+ FROM triggers ORDER BY id`)
920
+ .all();
921
+ return rows.map(triggerFromRow);
922
+ }
923
+ async recordTriggerFired(id, runId) {
924
+ await this.ready();
925
+ const now = new Date().toISOString();
926
+ const result = this.db
927
+ .prepare(`UPDATE triggers SET last_fired_at = @last_fired_at, last_run_id = @last_run_id, updated_at = @updated_at
928
+ WHERE id = @id`)
929
+ .run({ id, last_fired_at: now, last_run_id: runId, updated_at: now });
930
+ if (result.changes === 0) {
931
+ throw new Error(`Trigger not found: ${id}`);
932
+ }
933
+ }
934
+ async setTriggerNextRun(id, nextRunAt) {
935
+ await this.ready();
936
+ const now = new Date().toISOString();
937
+ const result = this.db
938
+ .prepare(`UPDATE triggers SET next_run_at = @next_run_at, updated_at = @updated_at
939
+ WHERE id = @id`)
940
+ .run({ id, next_run_at: nextRunAt, updated_at: now });
941
+ if (result.changes === 0) {
942
+ throw new Error(`Trigger not found: ${id}`);
943
+ }
944
+ }
945
+ async getTriggerAdapterState(triggerId, key) {
946
+ await this.ready();
947
+ const row = this.db
948
+ .prepare(`SELECT value FROM trigger_adapter_state WHERE trigger_id = ? AND key = ?`)
949
+ .get(triggerId, key);
950
+ return row ? row.value : null;
951
+ }
952
+ async setTriggerAdapterState(triggerId, key, value) {
953
+ await this.ready();
954
+ const now = new Date().toISOString();
955
+ this.db
956
+ .prepare(`INSERT INTO trigger_adapter_state (trigger_id, key, value, updated_at)
957
+ VALUES (@trigger_id, @key, @value, @updated_at)
958
+ ON CONFLICT(trigger_id, key) DO UPDATE SET
959
+ value = @value,
960
+ updated_at = @updated_at`)
961
+ .run({ trigger_id: triggerId, key, value, updated_at: now });
962
+ }
873
963
  async readRun(runId) {
874
964
  await this.ready();
875
965
  const row = this.getRunRow(runId);