stageflow 0.27.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/builtin-skills/browser/SKILL.md +42 -0
- package/dist/agent/claudeAdapter.js +2 -2
- package/dist/agent/cursorProvider.d.ts +10 -0
- package/dist/agent/cursorProvider.js +83 -8
- package/dist/agent/fakeAgent.d.ts +1 -0
- package/dist/agent/fakeAgent.js +13 -0
- package/dist/agent/piAdapter.d.ts +3 -24
- package/dist/agent/piAdapter.js +49 -47
- package/dist/agent/piSessionFactory.d.ts +38 -0
- package/dist/agent/piSessionFactory.js +60 -0
- package/dist/agent/port.d.ts +2 -0
- package/dist/agent/providerAuth.d.ts +7 -3
- package/dist/agent/providerAuth.js +5 -3
- package/dist/agent/streamLogRedact.d.ts +6 -0
- package/dist/agent/streamLogRedact.js +23 -1
- package/dist/browser/anchor.d.ts +27 -0
- package/dist/browser/anchor.js +127 -0
- package/dist/browser/auditSink.d.ts +38 -0
- package/dist/browser/auditSink.js +34 -0
- package/dist/browser/browserHost.d.ts +86 -0
- package/dist/browser/browserHost.js +7 -0
- package/dist/browser/browserSweep.d.ts +14 -0
- package/dist/browser/browserSweep.js +54 -0
- package/dist/browser/browserTeardown.d.ts +56 -0
- package/dist/browser/browserTeardown.js +235 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
- package/dist/browser/fakeRemoteBrowserHost.js +34 -0
- package/dist/browser/gateHandoff.d.ts +25 -0
- package/dist/browser/gateHandoff.js +83 -0
- package/dist/browser/humanLogin.d.ts +18 -0
- package/dist/browser/humanLogin.js +34 -0
- package/dist/browser/keyProvider.d.ts +10 -0
- package/dist/browser/keyProvider.js +39 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +152 -0
- package/dist/browser/localProfileLock.d.ts +2 -0
- package/dist/browser/localProfileLock.js +218 -0
- package/dist/browser/localProfileStore.d.ts +5 -0
- package/dist/browser/localProfileStore.js +91 -0
- package/dist/browser/loginCheck.d.ts +53 -0
- package/dist/browser/loginCheck.js +147 -0
- package/dist/browser/memoryProfileLock.d.ts +2 -0
- package/dist/browser/memoryProfileLock.js +53 -0
- package/dist/browser/memoryProfileStore.d.ts +2 -0
- package/dist/browser/memoryProfileStore.js +31 -0
- package/dist/browser/navigationAudit.d.ts +16 -0
- package/dist/browser/navigationAudit.js +53 -0
- package/dist/browser/persistedEnv.d.ts +3 -0
- package/dist/browser/persistedEnv.js +15 -0
- package/dist/browser/profileLock.d.ts +40 -0
- package/dist/browser/profileLock.js +10 -0
- package/dist/browser/profileStore.d.ts +23 -0
- package/dist/browser/profileStore.js +39 -0
- package/dist/browser/runLiveness.d.ts +13 -0
- package/dist/browser/runLiveness.js +39 -0
- package/dist/browser/sitePolicy.d.ts +10 -0
- package/dist/browser/sitePolicy.js +61 -0
- package/dist/browser/stageBrowserEnv.d.ts +22 -0
- package/dist/browser/stageBrowserEnv.js +147 -0
- package/dist/browser/stageProfileLock.d.ts +17 -0
- package/dist/browser/stageProfileLock.js +39 -0
- package/dist/cli/browserCommand.d.ts +44 -0
- package/dist/cli/browserCommand.js +472 -0
- package/dist/cli/doctorCommand.js +7 -0
- package/dist/cli/hostClient.d.ts +20 -0
- package/dist/cli/hostClient.js +36 -2
- package/dist/cli/initCommand.d.ts +1 -0
- package/dist/cli/initCommand.js +23 -7
- package/dist/cli/initTemplates.d.ts +5 -2
- package/dist/cli/initTemplates.js +25 -2
- package/dist/cli/providersCommand.d.ts +1 -1
- package/dist/cli/providersCommand.js +4 -4
- package/dist/cli/runsCommand.js +6 -2
- package/dist/cli/triggerCommand.d.ts +12 -0
- package/dist/cli/triggerCommand.js +183 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +23 -6
- package/dist/cli.js +32 -3
- package/dist/config/browseCatalog.d.ts +1 -0
- package/dist/config/browseCatalog.js +5 -4
- package/dist/config/builtinSkills.d.ts +4 -0
- package/dist/config/builtinSkills.js +23 -0
- package/dist/config/catalogRelativePath.d.ts +1 -0
- package/dist/config/catalogRelativePath.js +10 -2
- package/dist/config/createTrigger.d.ts +27 -0
- package/dist/config/createTrigger.js +232 -0
- package/dist/config/draftPackage.d.ts +117 -0
- package/dist/config/draftPackage.js +550 -0
- package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
- package/dist/config/ensureCatalogScanRoot.js +73 -0
- package/dist/config/hostConfig.d.ts +2 -0
- package/dist/config/hostConfig.js +27 -0
- package/dist/config/listSkills.d.ts +2 -2
- package/dist/config/listSkills.js +17 -3
- package/dist/config/loadOutcome.d.ts +6 -0
- package/dist/config/loadPipeline.js +16 -0
- package/dist/config/loadStage.js +7 -0
- package/dist/config/loadStageflowManifest.js +25 -0
- package/dist/config/loadTrigger.d.ts +9 -0
- package/dist/config/loadTrigger.js +120 -0
- package/dist/config/multiProjectCatalog.d.ts +2 -0
- package/dist/config/multiProjectCatalog.js +26 -18
- package/dist/config/normalizePipelineStageEntry.js +8 -1
- package/dist/config/parseCompletionContract.js +6 -0
- package/dist/config/pipelineStageKeys.js +1 -0
- package/dist/config/publishDraftPackage.d.ts +7 -0
- package/dist/config/publishDraftPackage.js +25 -0
- package/dist/config/resolveCatalogContext.js +1 -1
- package/dist/config/resolvePipelineDag.js +1 -1
- package/dist/config/scanCatalogPaths.d.ts +1 -1
- package/dist/config/scanCatalogPaths.js +6 -2
- package/dist/config/stageBrowser.d.ts +12 -0
- package/dist/config/stageBrowser.js +118 -0
- package/dist/config/triggerCatalog.d.ts +10 -0
- package/dist/config/triggerCatalog.js +37 -0
- package/dist/config/validateCatalog.d.ts +13 -3
- package/dist/config/validateCatalog.js +83 -2
- package/dist/config/validateCompletionContract.js +8 -0
- package/dist/envelope/preEmitChecks.d.ts +1 -1
- package/dist/envelope/preEmitChecks.js +8 -0
- package/dist/logging/redact.d.ts +4 -0
- package/dist/logging/redact.js +17 -0
- package/dist/mcp/catalogTools.js +43 -8
- package/dist/mcp/controlTools.js +3 -1
- package/dist/mcp/deps.d.ts +2 -0
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/tools.js +2 -0
- package/dist/mcp/triggerTools.d.ts +3 -0
- package/dist/mcp/triggerTools.js +176 -0
- package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
- package/dist/operatorAgent/docsRetrieval.js +269 -0
- package/dist/operatorAgent/draftContext.d.ts +16 -0
- package/dist/operatorAgent/draftContext.js +68 -0
- package/dist/operatorAgent/fakeHost.d.ts +39 -0
- package/dist/operatorAgent/fakeHost.js +92 -0
- package/dist/operatorAgent/host.d.ts +40 -0
- package/dist/operatorAgent/host.js +179 -0
- package/dist/operatorAgent/index.d.ts +9 -0
- package/dist/operatorAgent/index.js +8 -0
- package/dist/operatorAgent/piModel.d.ts +80 -0
- package/dist/operatorAgent/piModel.js +528 -0
- package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
- package/dist/operatorAgent/proposals.d.ts +32 -0
- package/dist/operatorAgent/proposals.js +144 -0
- package/dist/operatorAgent/types.d.ts +128 -0
- package/dist/operatorAgent/types.js +1 -0
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/project/findManifestRoot.d.ts +3 -0
- package/dist/project/findManifestRoot.js +35 -0
- package/dist/project/resolveStageflowContext.d.ts +7 -0
- package/dist/project/resolveStageflowContext.js +44 -28
- package/dist/runstore/assertTmpdir.js +6 -2
- package/dist/runstore/diskUsage.d.ts +1 -1
- package/dist/runstore/diskUsage.js +2 -7
- package/dist/runstore/port.d.ts +28 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +90 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
- package/dist/runstore/sqlite/migrations/index.js +6 -0
- package/dist/runtime/answerResume.d.ts +1 -0
- package/dist/runtime/answerResume.js +3 -1
- package/dist/runtime/completionCheckRunner.d.ts +12 -1
- package/dist/runtime/completionCheckRunner.js +22 -0
- package/dist/runtime/credentialBinding.d.ts +7 -2
- package/dist/runtime/credentialBinding.js +20 -28
- package/dist/runtime/emailSource.d.ts +72 -0
- package/dist/runtime/emailSource.js +304 -0
- package/dist/runtime/githubPollSource.d.ts +86 -0
- package/dist/runtime/githubPollSource.js +284 -0
- package/dist/runtime/pipelineRunner.d.ts +4 -0
- package/dist/runtime/pipelineRunner.js +3 -0
- package/dist/runtime/pipelineScheduler.d.ts +2 -0
- package/dist/runtime/pipelineScheduler.js +86 -1
- package/dist/runtime/repositoryMaterialize.d.ts +1 -1
- package/dist/runtime/runManager.d.ts +11 -0
- package/dist/runtime/runManager.js +161 -3
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +1 -0
- package/dist/runtime/scheduleSource.d.ts +38 -0
- package/dist/runtime/scheduleSource.js +119 -0
- package/dist/runtime/settingsFile.d.ts +7 -2
- package/dist/runtime/settingsFile.js +66 -56
- package/dist/runtime/stageAttemptBootstrap.js +55 -3
- package/dist/runtime/stageEnvironment.js +1 -0
- package/dist/runtime/stageHitl.d.ts +1 -0
- package/dist/runtime/stageHitl.js +5 -1
- package/dist/runtime/stageProcessLauncher.d.ts +3 -0
- package/dist/runtime/stageProcessLauncher.js +52 -5
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +30 -6
- package/dist/runtime/stageStreamLog.d.ts +1 -0
- package/dist/runtime/stageStreamLog.js +2 -1
- package/dist/runtime/triggerCatalog.d.ts +6 -0
- package/dist/runtime/triggerCatalog.js +24 -0
- package/dist/runtime/triggerEventMatch.d.ts +2 -0
- package/dist/runtime/triggerEventMatch.js +6 -0
- package/dist/runtime/triggerPort.d.ts +14 -0
- package/dist/runtime/triggerPort.js +1 -0
- package/dist/runtime/triggerRunner.d.ts +17 -0
- package/dist/runtime/triggerRunner.js +89 -0
- package/dist/runtime/verifiedStageExecution.d.ts +7 -0
- package/dist/runtime/verifiedStageExecution.js +14 -0
- package/dist/runtime/webhookSignature.d.ts +1 -0
- package/dist/runtime/webhookSignature.js +12 -0
- package/dist/server/bootstrap.d.ts +6 -0
- package/dist/server/bootstrap.js +70 -0
- package/dist/server/controlToken.js +13 -0
- package/dist/server/createHttpHost.js +3 -0
- package/dist/server/healthSurfaces.js +31 -6
- package/dist/server/http.d.ts +15 -0
- package/dist/server/http.js +1251 -74
- package/dist/server/requestAuthContext.d.ts +1 -1
- package/dist/server/triggerRoutes.d.ts +15 -0
- package/dist/server/triggerRoutes.js +268 -0
- package/dist/tools/askOperator.d.ts +3 -2
- package/dist/types/completion.d.ts +3 -0
- package/dist/types/pipeline.d.ts +3 -1
- package/dist/types/preEmitCheck.d.ts +7 -0
- package/dist/types/stage.d.ts +14 -0
- package/dist/types/stageflowManifest.d.ts +3 -0
- package/dist/types/trigger.d.ts +18 -0
- package/dist/types/trigger.js +1 -0
- package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
- package/dist/ui/assets/index-p8Iou3zk.js +133 -0
- package/dist/ui/index.html +2 -2
- package/dist/workshop/autosave.d.ts +58 -0
- package/dist/workshop/autosave.js +222 -0
- package/dist/workshop/buildStore.d.ts +79 -0
- package/dist/workshop/buildStore.js +326 -0
- package/dist/workshop/chatTurn.d.ts +232 -0
- package/dist/workshop/chatTurn.js +475 -0
- package/dist/workshop/modelSettings.d.ts +18 -0
- package/dist/workshop/modelSettings.js +44 -0
- package/dist/workshop/sessionStore.d.ts +61 -0
- package/dist/workshop/sessionStore.js +254 -0
- package/dist/workshop/workshopPin.d.ts +72 -0
- package/dist/workshop/workshopPin.js +193 -0
- package/examples/README.md +2 -1
- package/examples/browser-session/README.md +77 -0
- package/examples/browser-session/browser-session.pipeline.yaml +50 -0
- package/examples/browser-session/browser-session.task.yaml +6 -0
- package/examples/browser-session/check-login.yaml +22 -0
- package/examples/browser-session/fixture-server.mjs +64 -0
- package/examples/browser-session/human-login.yaml +31 -0
- package/examples/browser-session/merge.yaml +36 -0
- package/examples/browser-session/work-a.yaml +41 -0
- package/examples/browser-session/work-b.yaml +41 -0
- package/examples/conditional-fork/decide.yaml +1 -1
- package/examples/github-release/README.md +17 -3
- package/examples/stage-mcp/README.md +3 -2
- package/examples/stageflow.yaml +51 -0
- package/package.json +9 -4
- package/skills/stageflow/references/control-surface.md +3 -1
- package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
- package/skills/stageflow-run/SKILL.md +2 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +3 -1
- package/skills/stageflow-setup/SKILL.md +11 -0
- package/dist/ui/assets/index-B6hlSXXn.js +0 -120
|
@@ -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 {};
|
package/dist/package-meta.d.ts
CHANGED
package/dist/package-meta.js
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
50
|
+
projectRoot: manifestRoot,
|
|
23
51
|
globalHome,
|
|
24
|
-
isGitProject
|
|
25
|
-
manifest:
|
|
26
|
-
manifestStatus: "
|
|
52
|
+
isGitProject,
|
|
53
|
+
manifest: outcome.value,
|
|
54
|
+
manifestStatus: "ok",
|
|
27
55
|
manifestIssues: [],
|
|
28
56
|
};
|
|
29
57
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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:
|
|
65
|
+
projectRoot: os.homedir(),
|
|
38
66
|
globalHome,
|
|
39
|
-
isGitProject:
|
|
67
|
+
isGitProject: false,
|
|
40
68
|
manifest: null,
|
|
41
|
-
manifestStatus: "
|
|
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:
|
|
63
|
-
manifestStatus: "
|
|
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
|
-
|
|
6
|
+
let raw = env.TMPDIR ?? env.TMP ?? env.TEMP;
|
|
5
7
|
if (raw === undefined || raw.trim() === "") {
|
|
6
|
-
|
|
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 → `
|
|
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 → `
|
|
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
|
-
|
|
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.
|
package/dist/runstore/port.d.ts
CHANGED
|
@@ -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);
|