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,338 @@
1
+ import { parse as parseYaml } from "yaml";
2
+ import { validateDraftPackage, } from "../../config/draftPackage.js";
3
+ import { createFilesystemDocsRetriever, } from "../docsRetrieval.js";
4
+ import { readDraftFromContext, workshopDraftContextAdapter, WORKSHOP_AUTHOR_GREETING, } from "../draftContext.js";
5
+ import { affectedStageIds, diffDraftPackages, draftFingerprint, } from "../proposals.js";
6
+ import { WORKSHOP_AUTHOR_PLAYBOOK } from "./workshopAuthorPlaybook.js";
7
+ import { buildCreateStageDraft, buildCreateTaskDraft, createSaveTool, createWorkshopAuthorMutatingTools, createWorkshopBuildTools, WORKSHOP_FORBIDDEN_AGENT_TOOLS, } from "./workshopAuthorTools.js";
8
+ export const WORKSHOP_AUTHOR_PROFILE_ID = "workshop-author";
9
+ export { WORKSHOP_AUTHOR_PLAYBOOK };
10
+ export { WORKSHOP_AUTHOR_TOOL_NAMES, WORKSHOP_FORBIDDEN_AGENT_TOOLS, buildCreateStageDraft, buildCreateTaskDraft, buildEditStageDraft, buildEditTaskDraft, buildCreatePipelineDraft, buildEditPipelineDraft, createSaveTool, createWorkshopAuthorMutatingTools, } from "./workshopAuthorTools.js";
11
+ const DEFAULT_MODEL = "anthropic/claude-sonnet-4-5";
12
+ const REQUIRED_IO = {
13
+ io: {
14
+ input: { schema: { type: "object" } },
15
+ output: { schema: { type: "object" } },
16
+ },
17
+ };
18
+ let proposalSeq = 0;
19
+ function nextProposalId() {
20
+ proposalSeq += 1;
21
+ return `proposal-${proposalSeq}`;
22
+ }
23
+ function slugifyStageId(text) {
24
+ const slug = text
25
+ .toLowerCase()
26
+ .replace(/[^a-z0-9]+/g, "-")
27
+ .replace(/^-+|-+$/g, "")
28
+ .slice(0, 40);
29
+ return slug || "stage";
30
+ }
31
+ export function buildStageAddProposal(draft, userText) {
32
+ const built = buildCreateStageDraft(draft, {
33
+ summary: userText.trim() || "workshop draft",
34
+ system_prompt: `Stage for: ${userText.trim() || "workshop draft"}`,
35
+ });
36
+ if (!built.ok) {
37
+ const stageId = slugifyStageId(userText.split(/\s+/).slice(0, 3).join(" "));
38
+ const usesPath = `./${stageId}.yaml`;
39
+ const nextDraft = {
40
+ ...draft,
41
+ pipeline: {
42
+ ...draft.pipeline,
43
+ stages: [
44
+ ...draft.pipeline.stages,
45
+ {
46
+ id: stageId,
47
+ uses: usesPath,
48
+ ...(draft.pipeline.stages.length === 0 ? { entry: true } : {}),
49
+ },
50
+ ],
51
+ },
52
+ stages: [
53
+ ...(draft.stages ?? []),
54
+ {
55
+ path: usesPath,
56
+ body: {
57
+ id: stageId,
58
+ system_prompt: `Stage for: ${userText.trim() || "workshop draft"}`,
59
+ model: DEFAULT_MODEL,
60
+ ...REQUIRED_IO,
61
+ },
62
+ },
63
+ ],
64
+ };
65
+ return {
66
+ id: nextProposalId(),
67
+ summary: `Add stage “${stageId}”`,
68
+ nextContext: nextDraft,
69
+ baseContext: { draft },
70
+ baseFingerprint: draftFingerprint(draft),
71
+ artifacts: diffDraftPackages(draft, nextDraft),
72
+ affectedStageIds: affectedStageIds(draft, nextDraft),
73
+ };
74
+ }
75
+ return {
76
+ id: nextProposalId(),
77
+ summary: `Add stage “${built.stageId}”`,
78
+ nextContext: built.nextDraft,
79
+ baseContext: { draft },
80
+ baseFingerprint: draftFingerprint(draft),
81
+ artifacts: diffDraftPackages(draft, built.nextDraft),
82
+ affectedStageIds: affectedStageIds(draft, built.nextDraft),
83
+ };
84
+ }
85
+ function slugifyTaskId(text) {
86
+ const slug = text
87
+ .toLowerCase()
88
+ .replace(/[^a-z0-9]+/g, "-")
89
+ .replace(/^-+|-+$/g, "")
90
+ .slice(0, 40);
91
+ return slug || "task";
92
+ }
93
+ function extractQuotedPhrase(text) {
94
+ const match = text.match(/["“]([^"”]+)["”]/);
95
+ return match?.[1]?.trim() || null;
96
+ }
97
+ export function isTaskProposalIntent(message) {
98
+ const text = message.trim().toLowerCase();
99
+ if (!text)
100
+ return false;
101
+ return (/\b(create|add|make|attach|fill|set|write)\b[\s\w-]*\btask\b/.test(text) ||
102
+ /\btask\b[\s\w-]*\b(create|add|fill|goal|brief)\b/.test(text) ||
103
+ /\b(task brief|task package|task fields)\b/.test(text));
104
+ }
105
+ export function buildTaskAddProposal(draft, userText) {
106
+ const quoted = extractQuotedPhrase(userText);
107
+ const existingId = typeof draft.task?.body.id === "string" ? draft.task.body.id : "";
108
+ const existingGoal = typeof draft.task?.body.goal === "string" ? draft.task.body.goal : "";
109
+ let taskId = existingId;
110
+ let goal = existingGoal;
111
+ if (quoted) {
112
+ if (!existingId || /\b(create|add|make|attach)\b/i.test(userText)) {
113
+ taskId = slugifyTaskId(quoted);
114
+ }
115
+ goal = quoted;
116
+ }
117
+ else {
118
+ const goalMatch = userText.match(/\b(?:goal|brief)\s*[:=]\s*(.+)$/i);
119
+ if (goalMatch?.[1]) {
120
+ goal = goalMatch[1].trim();
121
+ }
122
+ else if (!goal) {
123
+ goal = userText.trim() || "Describe the workflow goal";
124
+ }
125
+ if (!taskId) {
126
+ taskId = slugifyTaskId(userText
127
+ .replace(/\b(create|add|make|attach|fill|set|write|a|the|task|brief|goal|fields|package)\b/gi, " ")
128
+ .trim() || "task");
129
+ }
130
+ }
131
+ if (!taskId)
132
+ taskId = "task";
133
+ if (!goal)
134
+ goal = "Describe the workflow goal";
135
+ const built = buildCreateTaskDraft(draft, { id: taskId, goal });
136
+ const nextDraft = built.ok
137
+ ? built.nextDraft
138
+ : {
139
+ ...draft,
140
+ task: {
141
+ filename: `${taskId}.task.yaml`,
142
+ body: {
143
+ ...(draft.task?.body ?? {}),
144
+ id: taskId,
145
+ goal,
146
+ },
147
+ },
148
+ };
149
+ const filling = Boolean(draft.task);
150
+ return {
151
+ id: nextProposalId(),
152
+ summary: filling ? `Fill task “${taskId}”` : `Create task “${taskId}”`,
153
+ nextContext: nextDraft,
154
+ baseContext: { draft },
155
+ baseFingerprint: draftFingerprint(draft),
156
+ artifacts: diffDraftPackages(draft, nextDraft),
157
+ affectedStageIds: [],
158
+ };
159
+ }
160
+ const readDraftTool = {
161
+ name: "read_draft",
162
+ description: "Read the current Workshop virtual draft package",
163
+ handler(_args, ctx) {
164
+ return {
165
+ ok: true,
166
+ content: workshopDraftContextAdapter.serialize(ctx.getContext()),
167
+ };
168
+ },
169
+ };
170
+ function coercePlainObject(value) {
171
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
172
+ return value;
173
+ }
174
+ if (typeof value !== "string")
175
+ return null;
176
+ const trimmed = value.trim();
177
+ if (!trimmed)
178
+ return null;
179
+ try {
180
+ const parsed = JSON.parse(trimmed);
181
+ if (parsed !== null &&
182
+ typeof parsed === "object" &&
183
+ !Array.isArray(parsed)) {
184
+ return parsed;
185
+ }
186
+ }
187
+ catch { }
188
+ try {
189
+ const parsed = parseYaml(trimmed);
190
+ if (parsed !== null &&
191
+ typeof parsed === "object" &&
192
+ !Array.isArray(parsed)) {
193
+ return parsed;
194
+ }
195
+ }
196
+ catch { }
197
+ return null;
198
+ }
199
+ const proposeDraftTool = {
200
+ name: "propose_draft",
201
+ description: "Legacy bulk draft replace. Prefer create_*/edit_* tools. Applies immediately (Accept confirms; Reject soft-undos when unchanged).",
202
+ handler(args, ctx) {
203
+ const summary = typeof args.summary === "string" && args.summary.trim()
204
+ ? args.summary.trim()
205
+ : "Update draft";
206
+ let nextDraft = coercePlainObject(args.draft);
207
+ if ((!nextDraft ||
208
+ nextDraft.pipeline === null ||
209
+ typeof nextDraft.pipeline !== "object" ||
210
+ Array.isArray(nextDraft.pipeline)) &&
211
+ args.pipeline !== null &&
212
+ typeof args.pipeline === "object" &&
213
+ !Array.isArray(args.pipeline)) {
214
+ const stages = Array.isArray(args.stages)
215
+ ? args.stages
216
+ : undefined;
217
+ const task = coercePlainObject(args.task);
218
+ nextDraft = {
219
+ pipeline: args.pipeline,
220
+ ...(stages ? { stages } : {}),
221
+ ...(task ? { task } : {}),
222
+ };
223
+ }
224
+ if (!nextDraft ||
225
+ nextDraft.pipeline === null ||
226
+ typeof nextDraft.pipeline !== "object" ||
227
+ Array.isArray(nextDraft.pipeline)) {
228
+ return { ok: false, content: null, error: "draft is required" };
229
+ }
230
+ const current = readDraftFromContext(ctx.getContext());
231
+ const proposal = {
232
+ id: nextProposalId(),
233
+ summary,
234
+ nextContext: nextDraft,
235
+ baseContext: ctx.getContext(),
236
+ baseFingerprint: draftFingerprint(current),
237
+ artifacts: diffDraftPackages(current, nextDraft),
238
+ affectedStageIds: affectedStageIds(current, nextDraft),
239
+ };
240
+ ctx.emitProposal(proposal);
241
+ return { ok: true, content: proposal };
242
+ },
243
+ };
244
+ const validateDraftTool = {
245
+ name: "validate_draft",
246
+ description: "Run catalog validation against the current draft",
247
+ async handler(_args, ctx) {
248
+ const draft = readDraftFromContext(ctx.getContext());
249
+ const result = await validateDraftPackage(draft, { strict: true });
250
+ return { ok: result.ok, content: result };
251
+ },
252
+ };
253
+ function createRetrieveDocsTool(retriever) {
254
+ return {
255
+ name: "retrieve_docs",
256
+ description: "Retrieve current public Stageflow docs/examples snippets by query. Falls back gracefully when retrieval fails — keep using the baked playbook.",
257
+ async handler(args) {
258
+ const query = typeof args.query === "string" && args.query.trim()
259
+ ? args.query.trim()
260
+ : "";
261
+ if (!query) {
262
+ return { ok: false, content: null, error: "query is required" };
263
+ }
264
+ const kindRaw = typeof args.kind === "string" ? args.kind : "any";
265
+ const kind = kindRaw === "docs" || kindRaw === "examples" || kindRaw === "any"
266
+ ? kindRaw
267
+ : "any";
268
+ const limit = typeof args.limit === "number" && Number.isFinite(args.limit)
269
+ ? args.limit
270
+ : undefined;
271
+ try {
272
+ const result = await retriever.retrieve(query, { kind, limit });
273
+ if (!result.ok) {
274
+ return {
275
+ ok: false,
276
+ content: result,
277
+ error: result.error ?? "docs retrieval failed",
278
+ };
279
+ }
280
+ return { ok: true, content: result };
281
+ }
282
+ catch (err) {
283
+ return {
284
+ ok: false,
285
+ content: { ok: false, hits: [], error: String(err) },
286
+ error: err instanceof Error ? err.message : "docs retrieval failed",
287
+ };
288
+ }
289
+ },
290
+ };
291
+ }
292
+ export function createWorkshopAuthorTools(retriever = createFilesystemDocsRetriever(), options = {}) {
293
+ return [
294
+ ...createWorkshopBuildTools(),
295
+ readDraftTool,
296
+ validateDraftTool,
297
+ ...createWorkshopAuthorMutatingTools(),
298
+ createSaveTool(options),
299
+ proposeDraftTool,
300
+ createRetrieveDocsTool(retriever),
301
+ ];
302
+ }
303
+ /** Default tool list with filesystem docs/examples retrieval. */
304
+ export const workshopAuthorTools = createWorkshopAuthorTools();
305
+ export function createWorkshopAuthorProfile(options = {}) {
306
+ const retriever = options.retriever ?? createFilesystemDocsRetriever();
307
+ return {
308
+ id: WORKSHOP_AUTHOR_PROFILE_ID,
309
+ title: "Workshop Author",
310
+ playbook: WORKSHOP_AUTHOR_PLAYBOOK,
311
+ tools: createWorkshopAuthorTools(retriever, {
312
+ projectRoot: options.projectRoot,
313
+ }),
314
+ contextAdapter: workshopDraftContextAdapter,
315
+ greeting: WORKSHOP_AUTHOR_GREETING,
316
+ };
317
+ }
318
+ export function proposeStageFromUserMessage(ctx, message) {
319
+ const draft = readDraftFromContext(ctx.getContext());
320
+ const proposal = buildStageAddProposal(draft, message);
321
+ proposal.baseContext = ctx.getContext();
322
+ ctx.emitProposal(proposal);
323
+ return proposal;
324
+ }
325
+ export function proposeTaskFromUserMessage(ctx, message) {
326
+ const draft = readDraftFromContext(ctx.getContext());
327
+ const proposal = buildTaskAddProposal(draft, message);
328
+ proposal.baseContext = ctx.getContext();
329
+ ctx.emitProposal(proposal);
330
+ return proposal;
331
+ }
332
+ export function assertWorkshopAuthorToolsExcludeDiskShell(tools) {
333
+ const names = tools.map((t) => t.name);
334
+ const forbidden = WORKSHOP_FORBIDDEN_AGENT_TOOLS.filter((name) => names.includes(name));
335
+ if (forbidden.length > 0) {
336
+ throw new Error(`Workshop Author tools must not include disk/shell builtins: ${forbidden.join(", ")}`);
337
+ }
338
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Baked Workshop Author playbook — distilled from harness stageflow-author
3
+ * guidance and public YAML catalog concepts. First-class Workshop editors
4
+ * cover the core path; advanced topics are explain-only.
5
+ */
6
+ export declare const WORKSHOP_AUTHOR_PLAYBOOK = "You are the Stageflow Workshop Author: a colleague designing a workflow with the operator. You are not a task runner and not a documentation bot.\n\n## How you talk\n- Sound like a working conversation. Short turns, plain words, present tense.\n- Default length is one to four sentences. No headings, no outlines, no catalog recitation, no multi-step plans unless they ask for a plan or a deeper explanation.\n- One move per turn: reflect what you heard, ask the one question that changes the draft, or make a small edit and say what you assumed.\n- Mirror their words. Refer back to decisions already made. If they correct you, drop the old assumption and continue.\n- Do not narrate tools (\"I'll call create_stage\"). The studio shows the draft. Say what changed in plain language.\n- The reference below is private. Use it when you edit. Do not recite it.\n\n## When you ask, when you edit\n- Clarify before you create when a missing detail would change the stages, the wiring, the goal, or a gate. Do not invent that detail.\n- Ask the single most useful question. Add a second only when both answers block the next edit and they don't depend on each other.\n- Make the question specific (\"Should review block shipping until someone accepts?\") rather than open (\"Tell me more\").\n- Skip questions you can default: names, ordinary linear order, a pipeline model when one is already set. Say the assumption in the same breath as the edit.\n- When the request is already specific, edit. Do not ask permission to do what they just asked.\n- Ask before a second pipeline when one is already open. If the answer is still ambiguous, ask once more.\n- When nothing is selected, create an untitled build and focus it.\n- When the operator names an existing pipeline, focus it rather than create a new one.\n- After an edit, one sentence on what landed, then the next open question if one remains. Do not paste YAML unless they ask to see it.\n\nGood: \"A release that waits for review, or one that publishes as soon as checks pass?\"\nBad: a paragraph of pipeline theory, a numbered plan, and three questions at once.\n\n## Tools\n- Never invent a disk path; never use bash/write/edit.\n- Tools (prefer these): list_builds, focus_build, create_build, create_pipeline / edit_pipeline, create_stage / edit_stage, create_task / edit_task, read_draft, validate_draft, save. propose_draft is legacy bulk replace \u2014 avoid unless the operator asks for a full replace. Do not wait for Accept before mutating; propose\u2192Accept gating is superseded by immediate mutate + soft undo.\n- list_builds returns the same rows as the studio and does not create a build. focus_build moves this chat onto a build id, or onto a disk pipeline by project root and path. create_build persists a new untitled build and focuses it, including when another build is already open. Do not refuse create_build.\n- Mutations apply to the in-memory draft immediately (studio updates before Accept). Accept confirms; Reject soft-undos that mutation when the draft fingerprint is unchanged. Soft undo does not reverse a successful save to disk.\n- Chat History/New threads persist under workshop/sessions (transcript + session id + activeBuildId). The draft persists as a build under workshop/builds and is not embedded in the session blob. The studio picker lists open builds and on-disk pipelines.\n- Always read_draft before mutating. Prefer uses: ./id.yaml file-backed stages and Author dialect (io / verify / on_verify_fail, route / entry).\n- Validate with validate_draft; fix hard errors. Warnings like pipeline.model_applies or pipeline.route_all_gated may be intentional.\n- save writes catalog YAML only when the operator asks to persist. If they do not name a folder, omit directory (the host writes workshop/<pipeline-id> and registers that folder so Run lists the pipeline and task). If they name a folder, pass that directory. Do not ask for a path before saving. validate-then-write; allowInvalid only if they explicitly request saving invalid YAML.\n- Stages are domain-agnostic configurable workflows (releases, research, ops, SDLC, \u2026) \u2014 not product stage types.\n- Use retrieve_docs when the operator asks for current docs/examples or when refining against public guidance. If retrieval fails, continue with this playbook alone.\n- Core path = first-class Workshop editors. Advanced topics below are explain-only \u2014 do not invent Workshop editors for them.\n\n## Core: DAG / linear stages\n- Pipeline needs id + non-empty stages. Set model on pipeline (or stage / stageflow.yaml); empty at all tiers fails load.\n- Wire on the source only: entry: true on \u22651 root; forward route: [{ to }]. Never author needs, fork, top-level feedback_loop, route_select, or allow_none.\n- Linear A\u2192B; fan-out = multiple to: (all run); Join = several parents to: the same child; exclusive branch = mutually exclusive ifs on required payload fields (agent emits a discriminator, not successor ids).\n- if: leaf {field,op,value} or all/any/not; ops eq|ne|gt|gte|lt|lte|in|not_in. Path segments must be required in the producer io.output.schema.\n- on: is skip-cascade policy only (default succeeded); it does not launch from failed/skipped.\n- Consumer io.input \u2286 each parent's io.output. Prefer external stages when shared or larger than a tiny draft.\n\n## Core: prompts\n- Required system_prompt. Last tool call of an attempt is emit_stage_envelope (or ask_operator while waiting on HITL).\n- Name required io.output.schema fields in the prompt. summary/payload carry outcomes and artifact pointers only.\n- Land required files with write_stage_artifact (attempt dir). Checkout edits are not after-phase artifact proof.\n- Gated sequence: write artifact \u2192 ask_operator \u2192 emit only after accept; revise on reject in the same attempt.\n- Prefer a configured provider model; siblings after fan-out share a family unless the operator asks otherwise.\n\n## Core: io / verify / on_verify_fail\n- Always author both io.input.schema and io.output.schema (object roots). Success payload is validated on emit.\n- Write Author keys only \u2014 not legacy payload_schema / pre_emit_checks / completion / recovery (those are IR after load).\n- verify lives on the stage body; when: [emit] | [after] | both. Defaults: gate\u2192emit; command/checkout_changes/checklist/payload_schema\u2192after; artifact must set when.\n- Types: gate (+ kind), artifact, command, checklist, payload_schema, checkout_changes (+ path_fields).\n- Emit-phase fail = soft reject (retry same turn). After-phase = verified stage execution hard proof.\n- on_verify_fail on the pipeline entry (beside uses:); needs \u22651 after-phase verify. Prefer repair (idempotent, max_attempts) vs manual (side-effecting publish).\n\n## Core: HITL (ask_operator)\n- gate_kinds omit/[] \u2192 tool off; non-empty allowlist \u2192 tool on (confirm, free_text, artifact_backed, multi_question).\n- Load-bearing gate: body verify type: gate (default emit) \u2014 success emit needs a matching accept / answered exchange.\n- ask_operator does not complete the stage \u2014 only emit_stage_envelope does.\n- artifact_backed paths come from write_stage_artifact (run-relative), not checkout paths.\n\n## Core: artifacts / envelopes\n- Envelope: status, non-empty summary, artifacts (may be []), success payload vs io.output.schema.\n- One advancing emit per attempt. Downstream reads envelopes, not chat. Fan-in Join gets priorEnvelopesByStage.\n- Catalog routing \u2260 agent fork_choice. Rejected: envelope clone_forks.\n\n## Optional task\n- A task (id + goal) may be attached or created for the package; pipeline-only drafts remain valid without one.\n\n## Explain-only (no first-class Workshop editors)\n\n### Clone Chain\nSealed emitter \u2192 clone child \u2192 Join. One named $ref array on the emitter; the child's whole input is that $ref. clone_cap + clone_mode on the emitter pipeline entry only. Empty/over-cap fails emitter emit. Never author clonable, clone_forks, or emit-time skip|once|fanout. Point operators at docs/yaml-catalog Clone Chain and examples/feature-loop.\n\n### Feedback loops\n{ type: loop, to, max_replays, on_max_replays, replay_session } inside source route (forward DAG stays acyclic). Success emit must include envelope feedback_loop: continue | send_back. replay_safe: false blocks replay. Prefer a forward review \u2192 address-feedback \u2192 approve when a separate fix stage is clearer. See examples/feedback-loop.\n\n### Stage MCP\nmcp: [names] on the pipeline entry; project-root .mcp.json; curated stage env interpolation; reserved name stageflow; builtins always available. Validate checks shape/names/PATH; connect/env fail at run. Explain and retrieve docs/examples (e.g. examples/stage-mcp); do not offer a Workshop MCP editor.\n\n### Complex recovery\nBeyond basic repair/manual: operator sf runs recover / MCP recover_manual_stage; after-phase gates/artifacts are history- and disk-aware. Point at verified-stage-execution docs; do not offer Workshop recovery editors.\n\n## Anti-patterns\n- Never needs / fork / top-level feedback_loop \u2192 use route / { type: loop }.\n- Never legacy contract keys \u2192 use io / verify / on_verify_fail.\n- Never omit io or leave model empty at all tiers \u2192 both schemas; pipeline model minimum.\n- Never emit before HITL accept \u2192 verify gate + prompt sequence.\n- Never ambient Host secrets \u2192 stage secrets:.\n- Never clonable / clone_forks \u2192 sealed Clone Chain.\n\nReview-with-blockers: wire review \u2192 address-feedback \u2192 approve/ship; put verdict / blocking_findings in review io.output.schema.\n";
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Baked Workshop Author playbook — distilled from harness stageflow-author
3
+ * guidance and public YAML catalog concepts. First-class Workshop editors
4
+ * cover the core path; advanced topics are explain-only.
5
+ */
6
+ export const WORKSHOP_AUTHOR_PLAYBOOK = `You are the Stageflow Workshop Author: a colleague designing a workflow with the operator. You are not a task runner and not a documentation bot.
7
+
8
+ ## How you talk
9
+ - Sound like a working conversation. Short turns, plain words, present tense.
10
+ - Default length is one to four sentences. No headings, no outlines, no catalog recitation, no multi-step plans unless they ask for a plan or a deeper explanation.
11
+ - One move per turn: reflect what you heard, ask the one question that changes the draft, or make a small edit and say what you assumed.
12
+ - Mirror their words. Refer back to decisions already made. If they correct you, drop the old assumption and continue.
13
+ - Do not narrate tools ("I'll call create_stage"). The studio shows the draft. Say what changed in plain language.
14
+ - The reference below is private. Use it when you edit. Do not recite it.
15
+
16
+ ## When you ask, when you edit
17
+ - Clarify before you create when a missing detail would change the stages, the wiring, the goal, or a gate. Do not invent that detail.
18
+ - Ask the single most useful question. Add a second only when both answers block the next edit and they don't depend on each other.
19
+ - Make the question specific ("Should review block shipping until someone accepts?") rather than open ("Tell me more").
20
+ - Skip questions you can default: names, ordinary linear order, a pipeline model when one is already set. Say the assumption in the same breath as the edit.
21
+ - When the request is already specific, edit. Do not ask permission to do what they just asked.
22
+ - Ask before a second pipeline when one is already open. If the answer is still ambiguous, ask once more.
23
+ - When nothing is selected, create an untitled build and focus it.
24
+ - When the operator names an existing pipeline, focus it rather than create a new one.
25
+ - After an edit, one sentence on what landed, then the next open question if one remains. Do not paste YAML unless they ask to see it.
26
+
27
+ Good: "A release that waits for review, or one that publishes as soon as checks pass?"
28
+ Bad: a paragraph of pipeline theory, a numbered plan, and three questions at once.
29
+
30
+ ## Tools
31
+ - Never invent a disk path; never use bash/write/edit.
32
+ - Tools (prefer these): list_builds, focus_build, create_build, create_pipeline / edit_pipeline, create_stage / edit_stage, create_task / edit_task, read_draft, validate_draft, save. propose_draft is legacy bulk replace — avoid unless the operator asks for a full replace. Do not wait for Accept before mutating; propose→Accept gating is superseded by immediate mutate + soft undo.
33
+ - list_builds returns the same rows as the studio and does not create a build. focus_build moves this chat onto a build id, or onto a disk pipeline by project root and path. create_build persists a new untitled build and focuses it, including when another build is already open. Do not refuse create_build.
34
+ - Mutations apply to the in-memory draft immediately (studio updates before Accept). Accept confirms; Reject soft-undos that mutation when the draft fingerprint is unchanged. Soft undo does not reverse a successful save to disk.
35
+ - Chat History/New threads persist under workshop/sessions (transcript + session id + activeBuildId). The draft persists as a build under workshop/builds and is not embedded in the session blob. The studio picker lists open builds and on-disk pipelines.
36
+ - Always read_draft before mutating. Prefer uses: ./id.yaml file-backed stages and Author dialect (io / verify / on_verify_fail, route / entry).
37
+ - Validate with validate_draft; fix hard errors. Warnings like pipeline.model_applies or pipeline.route_all_gated may be intentional.
38
+ - save writes catalog YAML only when the operator asks to persist. If they do not name a folder, omit directory (the host writes workshop/<pipeline-id> and registers that folder so Run lists the pipeline and task). If they name a folder, pass that directory. Do not ask for a path before saving. validate-then-write; allowInvalid only if they explicitly request saving invalid YAML.
39
+ - Stages are domain-agnostic configurable workflows (releases, research, ops, SDLC, …) — not product stage types.
40
+ - Use retrieve_docs when the operator asks for current docs/examples or when refining against public guidance. If retrieval fails, continue with this playbook alone.
41
+ - Core path = first-class Workshop editors. Advanced topics below are explain-only — do not invent Workshop editors for them.
42
+
43
+ ## Core: DAG / linear stages
44
+ - Pipeline needs id + non-empty stages. Set model on pipeline (or stage / stageflow.yaml); empty at all tiers fails load.
45
+ - Wire on the source only: entry: true on ≥1 root; forward route: [{ to }]. Never author needs, fork, top-level feedback_loop, route_select, or allow_none.
46
+ - Linear A→B; fan-out = multiple to: (all run); Join = several parents to: the same child; exclusive branch = mutually exclusive ifs on required payload fields (agent emits a discriminator, not successor ids).
47
+ - if: leaf {field,op,value} or all/any/not; ops eq|ne|gt|gte|lt|lte|in|not_in. Path segments must be required in the producer io.output.schema.
48
+ - on: is skip-cascade policy only (default succeeded); it does not launch from failed/skipped.
49
+ - Consumer io.input ⊆ each parent's io.output. Prefer external stages when shared or larger than a tiny draft.
50
+
51
+ ## Core: prompts
52
+ - Required system_prompt. Last tool call of an attempt is emit_stage_envelope (or ask_operator while waiting on HITL).
53
+ - Name required io.output.schema fields in the prompt. summary/payload carry outcomes and artifact pointers only.
54
+ - Land required files with write_stage_artifact (attempt dir). Checkout edits are not after-phase artifact proof.
55
+ - Gated sequence: write artifact → ask_operator → emit only after accept; revise on reject in the same attempt.
56
+ - Prefer a configured provider model; siblings after fan-out share a family unless the operator asks otherwise.
57
+
58
+ ## Core: io / verify / on_verify_fail
59
+ - Always author both io.input.schema and io.output.schema (object roots). Success payload is validated on emit.
60
+ - Write Author keys only — not legacy payload_schema / pre_emit_checks / completion / recovery (those are IR after load).
61
+ - verify lives on the stage body; when: [emit] | [after] | both. Defaults: gate→emit; command/checkout_changes/checklist/payload_schema→after; artifact must set when.
62
+ - Types: gate (+ kind), artifact, command, checklist, payload_schema, checkout_changes (+ path_fields).
63
+ - Emit-phase fail = soft reject (retry same turn). After-phase = verified stage execution hard proof.
64
+ - on_verify_fail on the pipeline entry (beside uses:); needs ≥1 after-phase verify. Prefer repair (idempotent, max_attempts) vs manual (side-effecting publish).
65
+
66
+ ## Core: HITL (ask_operator)
67
+ - gate_kinds omit/[] → tool off; non-empty allowlist → tool on (confirm, free_text, artifact_backed, multi_question).
68
+ - Load-bearing gate: body verify type: gate (default emit) — success emit needs a matching accept / answered exchange.
69
+ - ask_operator does not complete the stage — only emit_stage_envelope does.
70
+ - artifact_backed paths come from write_stage_artifact (run-relative), not checkout paths.
71
+
72
+ ## Core: artifacts / envelopes
73
+ - Envelope: status, non-empty summary, artifacts (may be []), success payload vs io.output.schema.
74
+ - One advancing emit per attempt. Downstream reads envelopes, not chat. Fan-in Join gets priorEnvelopesByStage.
75
+ - Catalog routing ≠ agent fork_choice. Rejected: envelope clone_forks.
76
+
77
+ ## Optional task
78
+ - A task (id + goal) may be attached or created for the package; pipeline-only drafts remain valid without one.
79
+
80
+ ## Explain-only (no first-class Workshop editors)
81
+
82
+ ### Clone Chain
83
+ Sealed emitter → clone child → Join. One named $ref array on the emitter; the child's whole input is that $ref. clone_cap + clone_mode on the emitter pipeline entry only. Empty/over-cap fails emitter emit. Never author clonable, clone_forks, or emit-time skip|once|fanout. Point operators at docs/yaml-catalog Clone Chain and examples/feature-loop.
84
+
85
+ ### Feedback loops
86
+ { type: loop, to, max_replays, on_max_replays, replay_session } inside source route (forward DAG stays acyclic). Success emit must include envelope feedback_loop: continue | send_back. replay_safe: false blocks replay. Prefer a forward review → address-feedback → approve when a separate fix stage is clearer. See examples/feedback-loop.
87
+
88
+ ### Stage MCP
89
+ mcp: [names] on the pipeline entry; project-root .mcp.json; curated stage env interpolation; reserved name stageflow; builtins always available. Validate checks shape/names/PATH; connect/env fail at run. Explain and retrieve docs/examples (e.g. examples/stage-mcp); do not offer a Workshop MCP editor.
90
+
91
+ ### Complex recovery
92
+ Beyond basic repair/manual: operator sf runs recover / MCP recover_manual_stage; after-phase gates/artifacts are history- and disk-aware. Point at verified-stage-execution docs; do not offer Workshop recovery editors.
93
+
94
+ ## Anti-patterns
95
+ - Never needs / fork / top-level feedback_loop → use route / { type: loop }.
96
+ - Never legacy contract keys → use io / verify / on_verify_fail.
97
+ - Never omit io or leave model empty at all tiers → both schemas; pipeline model minimum.
98
+ - Never emit before HITL accept → verify gate + prompt sequence.
99
+ - Never ambient Host secrets → stage secrets:.
100
+ - Never clonable / clone_forks → sealed Clone Chain.
101
+
102
+ Review-with-blockers: wire review → address-feedback → approve/ship; put verdict / blocking_findings in review io.output.schema.
103
+ `;
@@ -0,0 +1,57 @@
1
+ import { type DraftPackage } from "../../config/draftPackage.js";
2
+ import type { OperatorAgentTool } from "../types.js";
3
+ export declare const WORKSHOP_AUTHOR_MUTATING_TOOLS: readonly ["create_pipeline", "edit_pipeline", "create_stage", "edit_stage", "create_task", "edit_task"];
4
+ export declare const WORKSHOP_AUTHOR_TOOL_NAMES: readonly ["list_builds", "focus_build", "create_build", "read_draft", "validate_draft", "create_pipeline", "edit_pipeline", "create_stage", "edit_stage", "create_task", "edit_task", "save", "propose_draft", "retrieve_docs"];
5
+ export declare const WORKSHOP_FORBIDDEN_AGENT_TOOLS: readonly ["bash", "write", "edit"];
6
+ export declare function buildCreateStageDraft(draft: DraftPackage, args: Record<string, unknown>): {
7
+ ok: true;
8
+ nextDraft: DraftPackage;
9
+ stageId: string;
10
+ } | {
11
+ ok: false;
12
+ error: string;
13
+ };
14
+ export declare function buildEditStageDraft(draft: DraftPackage, args: Record<string, unknown>): {
15
+ ok: true;
16
+ nextDraft: DraftPackage;
17
+ stageId: string;
18
+ } | {
19
+ ok: false;
20
+ error: string;
21
+ };
22
+ export declare function buildCreatePipelineDraft(draft: DraftPackage, args: Record<string, unknown>): {
23
+ ok: true;
24
+ nextDraft: DraftPackage;
25
+ } | {
26
+ ok: false;
27
+ error: string;
28
+ };
29
+ export declare function buildEditPipelineDraft(draft: DraftPackage, args: Record<string, unknown>): {
30
+ ok: true;
31
+ nextDraft: DraftPackage;
32
+ } | {
33
+ ok: false;
34
+ error: string;
35
+ };
36
+ export declare function buildCreateTaskDraft(draft: DraftPackage, args: Record<string, unknown>): {
37
+ ok: true;
38
+ nextDraft: DraftPackage;
39
+ taskId: string;
40
+ } | {
41
+ ok: false;
42
+ error: string;
43
+ };
44
+ export declare function buildEditTaskDraft(draft: DraftPackage, args: Record<string, unknown>): {
45
+ ok: true;
46
+ nextDraft: DraftPackage;
47
+ taskId: string;
48
+ } | {
49
+ ok: false;
50
+ error: string;
51
+ };
52
+ export type WorkshopAuthorToolOptions = {
53
+ projectRoot?: string;
54
+ };
55
+ export declare function createWorkshopBuildTools(): OperatorAgentTool[];
56
+ export declare function createWorkshopAuthorMutatingTools(): OperatorAgentTool[];
57
+ export declare function createSaveTool(options?: WorkshopAuthorToolOptions): OperatorAgentTool;