patchwork-os 0.2.0-beta.10.canary.98 → 0.2.0-beta.11

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 (230) hide show
  1. package/README.md +20 -1
  2. package/dist/adapters/grok.js +6 -1
  3. package/dist/adapters/grok.js.map +1 -1
  4. package/dist/approvalHttp.js +61 -66
  5. package/dist/approvalHttp.js.map +1 -1
  6. package/dist/bridge.js +3 -2
  7. package/dist/bridge.js.map +1 -1
  8. package/dist/claudeOrchestrator.js +15 -0
  9. package/dist/claudeOrchestrator.js.map +1 -1
  10. package/dist/commands/connect.d.ts +47 -0
  11. package/dist/commands/connect.js +419 -0
  12. package/dist/commands/connect.js.map +1 -0
  13. package/dist/commands/patchworkInit.d.ts +7 -0
  14. package/dist/commands/patchworkInit.js +26 -0
  15. package/dist/commands/patchworkInit.js.map +1 -1
  16. package/dist/commands/recipe.d.ts +14 -0
  17. package/dist/commands/recipe.js +51 -0
  18. package/dist/commands/recipe.js.map +1 -1
  19. package/dist/commands/recipeInstall.js +15 -0
  20. package/dist/commands/recipeInstall.js.map +1 -1
  21. package/dist/connectors/postgres.d.ts +7 -0
  22. package/dist/connectors/postgres.js +16 -2
  23. package/dist/connectors/postgres.js.map +1 -1
  24. package/dist/connectors/snowflake.js +6 -0
  25. package/dist/connectors/snowflake.js.map +1 -1
  26. package/dist/connectors/tokenStorage.d.ts +8 -0
  27. package/dist/connectors/tokenStorage.js +42 -3
  28. package/dist/connectors/tokenStorage.js.map +1 -1
  29. package/dist/drivers/claude/api.js +15 -2
  30. package/dist/drivers/claude/api.js.map +1 -1
  31. package/dist/drivers/claude/subprocess.js +1 -29
  32. package/dist/drivers/claude/subprocess.js.map +1 -1
  33. package/dist/drivers/gemini/index.js +9 -5
  34. package/dist/drivers/gemini/index.js.map +1 -1
  35. package/dist/drivers/openai/index.js +29 -4
  36. package/dist/drivers/openai/index.js.map +1 -1
  37. package/dist/drivers/outputCap.d.ts +27 -0
  38. package/dist/drivers/outputCap.js +50 -0
  39. package/dist/drivers/outputCap.js.map +1 -0
  40. package/dist/featureFlags.d.ts +9 -0
  41. package/dist/featureFlags.js +16 -0
  42. package/dist/featureFlags.js.map +1 -1
  43. package/dist/fp/commandDescription.d.ts +6 -0
  44. package/dist/fp/commandDescription.js +4 -4
  45. package/dist/fp/commandDescription.js.map +1 -1
  46. package/dist/fp/policyParser.js +13 -7
  47. package/dist/fp/policyParser.js.map +1 -1
  48. package/dist/haltPushDispatch.js +7 -22
  49. package/dist/haltPushDispatch.js.map +1 -1
  50. package/dist/index.js +148 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/mcpRoutes.js +1 -1
  53. package/dist/mcpRoutes.js.map +1 -1
  54. package/dist/oauth.d.ts +13 -0
  55. package/dist/oauth.js +34 -1
  56. package/dist/oauth.js.map +1 -1
  57. package/dist/recipeOrchestration.js +10 -0
  58. package/dist/recipeOrchestration.js.map +1 -1
  59. package/dist/recipeRoutes.d.ts +39 -0
  60. package/dist/recipeRoutes.js +214 -12
  61. package/dist/recipeRoutes.js.map +1 -1
  62. package/dist/recipes/agentExecutor.d.ts +20 -0
  63. package/dist/recipes/agentExecutor.js +32 -10
  64. package/dist/recipes/agentExecutor.js.map +1 -1
  65. package/dist/recipes/chainedRunner.d.ts +41 -2
  66. package/dist/recipes/chainedRunner.js +134 -6
  67. package/dist/recipes/chainedRunner.js.map +1 -1
  68. package/dist/recipes/connectorPreflight.js +30 -0
  69. package/dist/recipes/connectorPreflight.js.map +1 -1
  70. package/dist/recipes/haltCategory.d.ts +5 -0
  71. package/dist/recipes/haltCategory.js +7 -1
  72. package/dist/recipes/haltCategory.js.map +1 -1
  73. package/dist/recipes/judgeVerdict.d.ts +10 -0
  74. package/dist/recipes/judgeVerdict.js +10 -0
  75. package/dist/recipes/judgeVerdict.js.map +1 -1
  76. package/dist/recipes/names.d.ts +5 -0
  77. package/dist/recipes/names.js +10 -5
  78. package/dist/recipes/names.js.map +1 -1
  79. package/dist/recipes/parser.js +32 -9
  80. package/dist/recipes/parser.js.map +1 -1
  81. package/dist/recipes/pricing/costRouter.d.ts +43 -0
  82. package/dist/recipes/pricing/costRouter.js +44 -0
  83. package/dist/recipes/pricing/costRouter.js.map +1 -0
  84. package/dist/recipes/pricing/priceTable.d.ts +76 -0
  85. package/dist/recipes/pricing/priceTable.js +144 -0
  86. package/dist/recipes/pricing/priceTable.js.map +1 -0
  87. package/dist/recipes/runBudget.d.ts +82 -32
  88. package/dist/recipes/runBudget.js +182 -49
  89. package/dist/recipes/runBudget.js.map +1 -1
  90. package/dist/recipes/schema.d.ts +25 -4
  91. package/dist/recipes/schemaGenerator.d.ts +9 -0
  92. package/dist/recipes/schemaGenerator.js +386 -75
  93. package/dist/recipes/schemaGenerator.js.map +1 -1
  94. package/dist/recipes/simulation/aggregateRunRisk.d.ts +43 -0
  95. package/dist/recipes/simulation/aggregateRunRisk.js +117 -0
  96. package/dist/recipes/simulation/aggregateRunRisk.js.map +1 -0
  97. package/dist/recipes/simulation/costProjector.d.ts +32 -0
  98. package/dist/recipes/simulation/costProjector.js +194 -0
  99. package/dist/recipes/simulation/costProjector.js.map +1 -0
  100. package/dist/recipes/simulation/sideEffects.d.ts +32 -0
  101. package/dist/recipes/simulation/sideEffects.js +62 -0
  102. package/dist/recipes/simulation/sideEffects.js.map +1 -0
  103. package/dist/recipes/simulation/simulate.d.ts +36 -0
  104. package/dist/recipes/simulation/simulate.js +264 -0
  105. package/dist/recipes/simulation/simulate.js.map +1 -0
  106. package/dist/recipes/simulation/simulateMockedRun.d.ts +52 -0
  107. package/dist/recipes/simulation/simulateMockedRun.js +72 -0
  108. package/dist/recipes/simulation/simulateMockedRun.js.map +1 -0
  109. package/dist/recipes/simulation/synthesizeMockedOutputs.d.ts +31 -0
  110. package/dist/recipes/simulation/synthesizeMockedOutputs.js +50 -0
  111. package/dist/recipes/simulation/synthesizeMockedOutputs.js.map +1 -0
  112. package/dist/recipes/simulation/types.d.ts +198 -0
  113. package/dist/recipes/simulation/types.js +30 -0
  114. package/dist/recipes/simulation/types.js.map +1 -0
  115. package/dist/recipes/stepObservation.d.ts +12 -0
  116. package/dist/recipes/stepObservation.js +21 -1
  117. package/dist/recipes/stepObservation.js.map +1 -1
  118. package/dist/recipes/tools/airtable.d.ts +15 -0
  119. package/dist/recipes/tools/airtable.js +240 -0
  120. package/dist/recipes/tools/airtable.js.map +1 -0
  121. package/dist/recipes/tools/caldiy.d.ts +13 -0
  122. package/dist/recipes/tools/caldiy.js +214 -0
  123. package/dist/recipes/tools/caldiy.js.map +1 -0
  124. package/dist/recipes/tools/circleci.d.ts +10 -0
  125. package/dist/recipes/tools/circleci.js +204 -0
  126. package/dist/recipes/tools/circleci.js.map +1 -0
  127. package/dist/recipes/tools/cloudflare.d.ts +13 -0
  128. package/dist/recipes/tools/cloudflare.js +211 -0
  129. package/dist/recipes/tools/cloudflare.js.map +1 -0
  130. package/dist/recipes/tools/docs.d.ts +18 -0
  131. package/dist/recipes/tools/docs.js +94 -0
  132. package/dist/recipes/tools/docs.js.map +1 -0
  133. package/dist/recipes/tools/elasticsearch.d.ts +11 -0
  134. package/dist/recipes/tools/elasticsearch.js +156 -0
  135. package/dist/recipes/tools/elasticsearch.js.map +1 -0
  136. package/dist/recipes/tools/figma.d.ts +12 -0
  137. package/dist/recipes/tools/figma.js +194 -0
  138. package/dist/recipes/tools/figma.js.map +1 -0
  139. package/dist/recipes/tools/grafana.d.ts +11 -0
  140. package/dist/recipes/tools/grafana.js +216 -0
  141. package/dist/recipes/tools/grafana.js.map +1 -0
  142. package/dist/recipes/tools/http.d.ts +1 -1
  143. package/dist/recipes/tools/http.js +10 -28
  144. package/dist/recipes/tools/http.js.map +1 -1
  145. package/dist/recipes/tools/index.d.ts +26 -0
  146. package/dist/recipes/tools/index.js +26 -0
  147. package/dist/recipes/tools/index.js.map +1 -1
  148. package/dist/recipes/tools/monday.d.ts +22 -0
  149. package/dist/recipes/tools/monday.js +242 -0
  150. package/dist/recipes/tools/monday.js.map +1 -0
  151. package/dist/recipes/tools/obsidian.d.ts +15 -0
  152. package/dist/recipes/tools/obsidian.js +172 -0
  153. package/dist/recipes/tools/obsidian.js.map +1 -0
  154. package/dist/recipes/tools/paystack.d.ts +11 -0
  155. package/dist/recipes/tools/paystack.js +211 -0
  156. package/dist/recipes/tools/paystack.js.map +1 -0
  157. package/dist/recipes/tools/pipedrive.d.ts +16 -0
  158. package/dist/recipes/tools/pipedrive.js +233 -0
  159. package/dist/recipes/tools/pipedrive.js.map +1 -0
  160. package/dist/recipes/tools/postgres.d.ts +15 -0
  161. package/dist/recipes/tools/postgres.js +185 -0
  162. package/dist/recipes/tools/postgres.js.map +1 -0
  163. package/dist/recipes/tools/posthog.d.ts +16 -0
  164. package/dist/recipes/tools/posthog.js +219 -0
  165. package/dist/recipes/tools/posthog.js.map +1 -0
  166. package/dist/recipes/tools/redis.d.ts +9 -0
  167. package/dist/recipes/tools/redis.js +140 -0
  168. package/dist/recipes/tools/redis.js.map +1 -0
  169. package/dist/recipes/tools/resend.d.ts +8 -0
  170. package/dist/recipes/tools/resend.js +153 -0
  171. package/dist/recipes/tools/resend.js.map +1 -0
  172. package/dist/recipes/tools/salesforce.d.ts +16 -0
  173. package/dist/recipes/tools/salesforce.js +184 -0
  174. package/dist/recipes/tools/salesforce.js.map +1 -0
  175. package/dist/recipes/tools/sendgrid.d.ts +9 -0
  176. package/dist/recipes/tools/sendgrid.js +174 -0
  177. package/dist/recipes/tools/sendgrid.js.map +1 -0
  178. package/dist/recipes/tools/shopify.d.ts +16 -0
  179. package/dist/recipes/tools/shopify.js +265 -0
  180. package/dist/recipes/tools/shopify.js.map +1 -0
  181. package/dist/recipes/tools/snowflake.d.ts +16 -0
  182. package/dist/recipes/tools/snowflake.js +173 -0
  183. package/dist/recipes/tools/snowflake.js.map +1 -0
  184. package/dist/recipes/tools/stripe.js +13 -12
  185. package/dist/recipes/tools/stripe.js.map +1 -1
  186. package/dist/recipes/tools/supabase.d.ts +9 -0
  187. package/dist/recipes/tools/supabase.js +132 -0
  188. package/dist/recipes/tools/supabase.js.map +1 -0
  189. package/dist/recipes/tools/todoist.d.ts +15 -0
  190. package/dist/recipes/tools/todoist.js +227 -0
  191. package/dist/recipes/tools/todoist.js.map +1 -0
  192. package/dist/recipes/tools/twilio.d.ts +11 -0
  193. package/dist/recipes/tools/twilio.js +180 -0
  194. package/dist/recipes/tools/twilio.js.map +1 -0
  195. package/dist/recipes/tools/vercel.d.ts +9 -0
  196. package/dist/recipes/tools/vercel.js +145 -0
  197. package/dist/recipes/tools/vercel.js.map +1 -0
  198. package/dist/recipes/tools/webflow.d.ts +11 -0
  199. package/dist/recipes/tools/webflow.js +243 -0
  200. package/dist/recipes/tools/webflow.js.map +1 -0
  201. package/dist/recipes/tools/woocommerce.d.ts +18 -0
  202. package/dist/recipes/tools/woocommerce.js +259 -0
  203. package/dist/recipes/tools/woocommerce.js.map +1 -0
  204. package/dist/recipes/tools/wrapConnectorExecute.d.ts +25 -0
  205. package/dist/recipes/tools/wrapConnectorExecute.js +37 -0
  206. package/dist/recipes/tools/wrapConnectorExecute.js.map +1 -0
  207. package/dist/recipes/validation.js +264 -0
  208. package/dist/recipes/validation.js.map +1 -1
  209. package/dist/recipes/yamlRunner.d.ts +112 -3
  210. package/dist/recipes/yamlRunner.js +482 -19
  211. package/dist/recipes/yamlRunner.js.map +1 -1
  212. package/dist/runLog.d.ts +46 -0
  213. package/dist/runLog.js +4 -0
  214. package/dist/runLog.js.map +1 -1
  215. package/dist/schemas/recipe.v1.json +217 -11
  216. package/dist/server.d.ts +3 -0
  217. package/dist/server.js +4 -0
  218. package/dist/server.js.map +1 -1
  219. package/dist/streamableHttp.js +3 -2
  220. package/dist/streamableHttp.js.map +1 -1
  221. package/dist/tools/gitWrite.js +5 -1
  222. package/dist/tools/gitWrite.js.map +1 -1
  223. package/dist/tools/terminal.js +15 -49
  224. package/dist/tools/terminal.js.map +1 -1
  225. package/dist/transport.d.ts +1 -0
  226. package/dist/transport.js +7 -0
  227. package/dist/transport.js.map +1 -1
  228. package/package.json +3 -3
  229. package/scripts/mcp-stdio-shim.cjs +218 -84
  230. package/templates/recipes/incident-to-pr.yaml +187 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * What-If Preview — run-level risk aggregation.
3
+ *
4
+ * Two pure pieces:
5
+ * 1. `computeEffectiveRisks` — blast-radius propagation. A step's effective
6
+ * risk is at least as high as the highest risk among the steps that must
7
+ * have run before it (its transitive dependencies). Chained recipes use
8
+ * the real dependency DAG; flat recipes model the linear sequence
9
+ * (step N implicitly depends on N-1).
10
+ * 2. `summarizeRunRisk` — a transparent 0–100 workflow score derived from
11
+ * step counts, returned WITH its components so it is never a black box.
12
+ *
13
+ * Risk tier source is `riskDefault` (already enriched onto the plan step),
14
+ * NOT `classifyTool` — which mis-tiers namespaced recipe tool ids to a uniform
15
+ * "medium" (see the investigation report, §3b).
16
+ */
17
+ const RISK_ORDER = { low: 0, medium: 1, high: 2 };
18
+ const RISK_BY_ORDER = ["low", "medium", "high"];
19
+ export function maxRisk(a, b) {
20
+ return RISK_ORDER[a] >= RISK_ORDER[b] ? a : b;
21
+ }
22
+ /**
23
+ * Compute each step's effective (blast-radius-aware) risk.
24
+ *
25
+ * Returns a map keyed by step id. Cycles are broken safely: a step already on
26
+ * the current resolution stack contributes only its own base risk to break the
27
+ * loop, so a malformed DAG can never infinite-loop the planner.
28
+ */
29
+ export function computeEffectiveRisks(steps, topology) {
30
+ const byId = new Map();
31
+ for (const s of steps)
32
+ byId.set(s.id, s);
33
+ const memo = new Map();
34
+ if (topology === "flat") {
35
+ // Linear: running max down the list.
36
+ let running = "low";
37
+ for (const s of steps) {
38
+ running = maxRisk(running, s.baseRisk);
39
+ memo.set(s.id, running);
40
+ }
41
+ return memo;
42
+ }
43
+ // Chained: forward propagation along the dependency edges.
44
+ const resolving = new Set();
45
+ const resolve = (id) => {
46
+ const cached = memo.get(id);
47
+ if (cached !== undefined)
48
+ return cached;
49
+ const step = byId.get(id);
50
+ if (!step)
51
+ return "low";
52
+ if (resolving.has(id))
53
+ return step.baseRisk; // cycle guard
54
+ resolving.add(id);
55
+ let eff = step.baseRisk;
56
+ for (const dep of step.dependencies ?? []) {
57
+ if (dep === id || !byId.has(dep))
58
+ continue;
59
+ eff = maxRisk(eff, resolve(dep));
60
+ }
61
+ resolving.delete(id);
62
+ memo.set(id, eff);
63
+ return eff;
64
+ };
65
+ for (const s of steps)
66
+ resolve(s.id);
67
+ return memo;
68
+ }
69
+ const clamp100 = (n) => Math.max(0, Math.min(100, n));
70
+ /**
71
+ * Derive a transparent 0–100 workflow risk score + tier from per-step tallies.
72
+ * The weights are intentionally simple and surfaced via `components` so a
73
+ * consumer can always explain the number.
74
+ */
75
+ export function summarizeRunRisk(steps) {
76
+ const components = {
77
+ highSteps: 0,
78
+ mediumSteps: 0,
79
+ writeSteps: 0,
80
+ connectorWriteSteps: 0,
81
+ externalHttpSteps: 0,
82
+ unresolvedSteps: 0,
83
+ };
84
+ let highestOrder = 0;
85
+ for (const s of steps) {
86
+ highestOrder = Math.max(highestOrder, RISK_ORDER[s.effectiveRisk]);
87
+ if (s.effectiveRisk === "high")
88
+ components.highSteps += 1;
89
+ else if (s.effectiveRisk === "medium")
90
+ components.mediumSteps += 1;
91
+ if (s.sideEffect === "local-write" || s.sideEffect === "connector-write") {
92
+ components.writeSteps += 1;
93
+ }
94
+ if (s.sideEffect === "connector-write")
95
+ components.connectorWriteSteps += 1;
96
+ if (s.sideEffect === "external-http")
97
+ components.externalHttpSteps += 1;
98
+ if (!s.resolved)
99
+ components.unresolvedSteps += 1;
100
+ }
101
+ const score = clamp100(components.highSteps * 30 +
102
+ components.mediumSteps * 10 +
103
+ components.writeSteps * 8 +
104
+ components.connectorWriteSteps * 10 +
105
+ components.externalHttpSteps * 6 +
106
+ components.unresolvedSteps * 5);
107
+ const highestStepRisk = RISK_BY_ORDER[highestOrder] ?? "low";
108
+ let tier;
109
+ if (highestStepRisk === "high" || score >= 60)
110
+ tier = "high";
111
+ else if (highestStepRisk === "medium" || score >= 25)
112
+ tier = "medium";
113
+ else
114
+ tier = "low";
115
+ return { score, tier, components, highestStepRisk };
116
+ }
117
+ //# sourceMappingURL=aggregateRunRisk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregateRunRisk.js","sourceRoot":"","sources":["../../../src/recipes/simulation/aggregateRunRisk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * What-If Preview (P3) — cost projection from the P1 token corpus.
3
+ *
4
+ * Walks a recipe's agent steps and projects per-step token usage from the
5
+ * persisted history (`RunStepResult.inputTokens/outputTokens/costUsd`, P1).
6
+ * When a step has >= `threshold` historical samples the projection is the
7
+ * MEDIAN of those runs ("history" basis); otherwise it falls back to the
8
+ * coarse chars/4 prompt heuristic ("heuristic"); a recipe with no agent steps
9
+ * is "unavailable".
10
+ *
11
+ * Honesty rules carried over from P1:
12
+ * - USD is projected ONLY from historical `costUsd` (present only for billable
13
+ * drivers + priced models). The default `claude-code`/subprocess driver has
14
+ * tokens but NO cost → we project TOKENS with a notional note and leave
15
+ * `usd` null. Never a $0 placeholder.
16
+ * - `confidence` is a first-class, loud field. `high` only when every agent
17
+ * step has enough samples; `low` when some are heuristic; `none` when there
18
+ * is nothing to go on.
19
+ */
20
+ import type { RecipeDryRunPlan } from "../../commands/recipe.js";
21
+ import type { RecipeRunLog } from "../../runLog.js";
22
+ import type { SimulationCost } from "./types.js";
23
+ /**
24
+ * Project the cost of a recipe from its accrued run history. Pure except the
25
+ * `runLog` read. Degrades to the P0 heuristic/unavailable shape when there is
26
+ * no usable history, so callers can apply it unconditionally when a runLog is
27
+ * available.
28
+ */
29
+ export declare function projectCost(plan: RecipeDryRunPlan, runLog: RecipeRunLog, opts?: {
30
+ maxRuns?: number;
31
+ threshold?: number;
32
+ }): SimulationCost;
@@ -0,0 +1,194 @@
1
+ /**
2
+ * What-If Preview (P3) — cost projection from the P1 token corpus.
3
+ *
4
+ * Walks a recipe's agent steps and projects per-step token usage from the
5
+ * persisted history (`RunStepResult.inputTokens/outputTokens/costUsd`, P1).
6
+ * When a step has >= `threshold` historical samples the projection is the
7
+ * MEDIAN of those runs ("history" basis); otherwise it falls back to the
8
+ * coarse chars/4 prompt heuristic ("heuristic"); a recipe with no agent steps
9
+ * is "unavailable".
10
+ *
11
+ * Honesty rules carried over from P1:
12
+ * - USD is projected ONLY from historical `costUsd` (present only for billable
13
+ * drivers + priced models). The default `claude-code`/subprocess driver has
14
+ * tokens but NO cost → we project TOKENS with a notional note and leave
15
+ * `usd` null. Never a $0 placeholder.
16
+ * - `confidence` is a first-class, loud field. `high` only when every agent
17
+ * step has enough samples; `low` when some are heuristic; `none` when there
18
+ * is nothing to go on.
19
+ */
20
+ const CHARS_PER_TOKEN = 4;
21
+ const DEFAULT_MAX_RUNS = 50;
22
+ const DEFAULT_THRESHOLD = 5;
23
+ /** Median of a numeric array (average of the two middle values for even n). */
24
+ function median(nums) {
25
+ if (nums.length === 0)
26
+ return 0;
27
+ const sorted = [...nums].sort((a, b) => a - b);
28
+ const mid = Math.floor(sorted.length / 2);
29
+ return sorted.length % 2 === 0
30
+ ? (sorted[mid - 1] + sorted[mid]) / 2
31
+ : sorted[mid];
32
+ }
33
+ /**
34
+ * Project the cost of a recipe from its accrued run history. Pure except the
35
+ * `runLog` read. Degrades to the P0 heuristic/unavailable shape when there is
36
+ * no usable history, so callers can apply it unconditionally when a runLog is
37
+ * available.
38
+ */
39
+ export function projectCost(plan, runLog, opts = {}) {
40
+ const threshold = opts.threshold ?? DEFAULT_THRESHOLD;
41
+ const agentStepIds = plan.steps
42
+ .filter((s) => s.type === "agent")
43
+ .map((s) => s.id);
44
+ const agentSteps = agentStepIds.length;
45
+ // Prompt-heuristic fallback (chars/4) for steps lacking history. Only flat
46
+ // agent steps carry `prompt` in the plan; chained ones don't.
47
+ const promptTokensById = new Map();
48
+ for (const s of plan.steps) {
49
+ if (s.type === "agent" && typeof s.prompt === "string" && s.prompt.length) {
50
+ promptTokensById.set(s.id, Math.ceil(s.prompt.length / CHARS_PER_TOKEN));
51
+ }
52
+ }
53
+ if (agentSteps === 0) {
54
+ return {
55
+ basis: "unavailable",
56
+ confidence: "none",
57
+ sampleRuns: 0,
58
+ agentSteps: 0,
59
+ estimatedAgentSteps: 0,
60
+ estPromptTokens: null,
61
+ estInputTokens: null,
62
+ estOutputTokens: null,
63
+ usd: null,
64
+ minUsd: null,
65
+ maxUsd: null,
66
+ historyAgentSteps: 0,
67
+ note: "No AI/agent steps — this recipe incurs no model cost.",
68
+ };
69
+ }
70
+ const runs = runLog.query({
71
+ recipe: plan.recipe,
72
+ limit: opts.maxRuns ?? DEFAULT_MAX_RUNS,
73
+ });
74
+ // Collect per-step samples across history.
75
+ const samples = new Map();
76
+ for (const id of agentStepIds) {
77
+ samples.set(id, { input: [], output: [], cost: [] });
78
+ }
79
+ let sampleRuns = 0;
80
+ for (const run of runs) {
81
+ let contributed = false;
82
+ for (const step of run.stepResults ?? []) {
83
+ const bucket = samples.get(step.id);
84
+ if (!bucket)
85
+ continue;
86
+ if (typeof step.inputTokens === "number") {
87
+ bucket.input.push(step.inputTokens);
88
+ contributed = true;
89
+ }
90
+ if (typeof step.outputTokens === "number") {
91
+ bucket.output.push(step.outputTokens);
92
+ }
93
+ if (typeof step.costUsd === "number") {
94
+ bucket.cost.push(step.costUsd);
95
+ }
96
+ }
97
+ if (contributed)
98
+ sampleRuns += 1;
99
+ }
100
+ let estInput = 0;
101
+ let estOutput = 0;
102
+ let expectedUsd = 0;
103
+ let minUsd = 0;
104
+ let maxUsd = 0;
105
+ let anyTokenEstimate = false;
106
+ let anyCost = false;
107
+ let historyAgentSteps = 0;
108
+ let estimatedAgentSteps = 0;
109
+ let allStepsConfident = true;
110
+ for (const id of agentStepIds) {
111
+ const bucket = samples.get(id);
112
+ const hasHistory = bucket.input.length > 0;
113
+ if (hasHistory) {
114
+ historyAgentSteps += 1;
115
+ estimatedAgentSteps += 1;
116
+ anyTokenEstimate = true;
117
+ estInput += median(bucket.input);
118
+ estOutput += bucket.output.length > 0 ? median(bucket.output) : 0;
119
+ if (bucket.input.length < threshold)
120
+ allStepsConfident = false;
121
+ if (bucket.cost.length > 0) {
122
+ anyCost = true;
123
+ expectedUsd += median(bucket.cost);
124
+ minUsd += Math.min(...bucket.cost);
125
+ maxUsd += Math.max(...bucket.cost);
126
+ }
127
+ }
128
+ else {
129
+ // No history — chars/4 prompt heuristic when available.
130
+ const promptTokens = promptTokensById.get(id);
131
+ allStepsConfident = false;
132
+ if (promptTokens !== undefined) {
133
+ estimatedAgentSteps += 1;
134
+ anyTokenEstimate = true;
135
+ estInput += promptTokens;
136
+ }
137
+ }
138
+ }
139
+ const basis = historyAgentSteps > 0
140
+ ? "history"
141
+ : estimatedAgentSteps > 0
142
+ ? "heuristic"
143
+ : "unavailable";
144
+ const confidence = basis === "history"
145
+ ? allStepsConfident && historyAgentSteps === agentSteps
146
+ ? "high"
147
+ : "low"
148
+ : basis === "heuristic"
149
+ ? "low"
150
+ : "none";
151
+ const note = buildNote({
152
+ basis,
153
+ confidence,
154
+ sampleRuns,
155
+ historyAgentSteps,
156
+ agentSteps,
157
+ anyCost,
158
+ expectedUsd,
159
+ });
160
+ return {
161
+ basis,
162
+ confidence,
163
+ sampleRuns,
164
+ agentSteps,
165
+ estimatedAgentSteps,
166
+ // Back-compat: estPromptTokens kept as the chars/4 sum (input-only proxy).
167
+ estPromptTokens: anyTokenEstimate ? estInput : null,
168
+ estInputTokens: anyTokenEstimate ? estInput : null,
169
+ estOutputTokens: anyTokenEstimate ? estOutput : null,
170
+ usd: anyCost ? round4(expectedUsd) : null,
171
+ minUsd: anyCost ? round4(minUsd) : null,
172
+ maxUsd: anyCost ? round4(maxUsd) : null,
173
+ historyAgentSteps,
174
+ note,
175
+ };
176
+ }
177
+ function round4(n) {
178
+ return Math.round(n * 1e4) / 1e4;
179
+ }
180
+ function buildNote(p) {
181
+ if (p.basis === "unavailable") {
182
+ return "No usable history or prompts — cost not estimable.";
183
+ }
184
+ if (p.basis === "heuristic") {
185
+ return "Low-confidence chars/4 token estimate over agent prompts (no run history yet). USD not projected — accrue runs for a history-backed estimate.";
186
+ }
187
+ // history
188
+ const cov = `${p.historyAgentSteps}/${p.agentSteps} agent step(s) backed by ${p.sampleRuns} prior run(s) (median; ${p.confidence} confidence)`;
189
+ if (p.anyCost) {
190
+ return `Token + USD projection from history — ${cov}. USD covers billable-driver steps only.`;
191
+ }
192
+ return `Token projection from history — ${cov}. USD not projected: the served driver is a subscription/subprocess driver that is not billed (notional only).`;
193
+ }
194
+ //# sourceMappingURL=costProjector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"costProjector.js","sourceRoot":"","sources":["../../../src/recipes/simulation/costProjector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * What-If Preview — static side-effect classification.
3
+ *
4
+ * Classifies a single plan step into a {@link SideEffectKind} using ONLY
5
+ * registry metadata already present on the dry-run plan step
6
+ * (`type` / `tool` / `namespace` / `isWrite` / `isConnector` / `resolved`).
7
+ * No execution, no network, no token resolution.
8
+ */
9
+ import type { SideEffectKind } from "./types.js";
10
+ export interface SideEffectInput {
11
+ type: "tool" | "agent" | "recipe";
12
+ tool?: string;
13
+ namespace?: string;
14
+ resolved?: boolean;
15
+ isWrite?: boolean;
16
+ isConnector?: boolean;
17
+ }
18
+ /**
19
+ * Map a plan step to its side-effect class. Pure and total.
20
+ *
21
+ * Precedence:
22
+ * agent → agent-llm; recipe → nested-recipe.
23
+ * unresolved tool → unknown (we cannot claim a side effect we can't see).
24
+ * http/webhook namespace → external-http (write or read).
25
+ * connector → connector-write / connector-read on isWrite.
26
+ * otherwise local-write / local-read on isWrite.
27
+ */
28
+ export declare function classifyStepSideEffect(step: SideEffectInput): SideEffectKind;
29
+ /** Human-readable, one-line description of a side-effect class. */
30
+ export declare const SIDE_EFFECT_LABELS: Record<SideEffectKind, string>;
31
+ /** Empty side-effect tally with every kind present (so consumers can index safely). */
32
+ export declare function emptySideEffectCounts(): Record<SideEffectKind, number>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * What-If Preview — static side-effect classification.
3
+ *
4
+ * Classifies a single plan step into a {@link SideEffectKind} using ONLY
5
+ * registry metadata already present on the dry-run plan step
6
+ * (`type` / `tool` / `namespace` / `isWrite` / `isConnector` / `resolved`).
7
+ * No execution, no network, no token resolution.
8
+ */
9
+ /** Namespaces whose writes are arbitrary outbound HTTP rather than a SaaS connector. */
10
+ const EXTERNAL_HTTP_NAMESPACES = new Set(["http", "webhook"]);
11
+ /**
12
+ * Map a plan step to its side-effect class. Pure and total.
13
+ *
14
+ * Precedence:
15
+ * agent → agent-llm; recipe → nested-recipe.
16
+ * unresolved tool → unknown (we cannot claim a side effect we can't see).
17
+ * http/webhook namespace → external-http (write or read).
18
+ * connector → connector-write / connector-read on isWrite.
19
+ * otherwise local-write / local-read on isWrite.
20
+ */
21
+ export function classifyStepSideEffect(step) {
22
+ if (step.type === "agent")
23
+ return "agent-llm";
24
+ if (step.type === "recipe")
25
+ return "nested-recipe";
26
+ // type === "tool"
27
+ if (step.resolved === false)
28
+ return "unknown";
29
+ const ns = step.namespace ?? step.tool?.split(".")[0];
30
+ if (ns && EXTERNAL_HTTP_NAMESPACES.has(ns))
31
+ return "external-http";
32
+ const isWrite = step.isWrite === true;
33
+ if (step.isConnector === true) {
34
+ return isWrite ? "connector-write" : "connector-read";
35
+ }
36
+ return isWrite ? "local-write" : "local-read";
37
+ }
38
+ /** Human-readable, one-line description of a side-effect class. */
39
+ export const SIDE_EFFECT_LABELS = {
40
+ "local-read": "reads local/workspace state",
41
+ "local-write": "writes local/workspace state",
42
+ "connector-read": "reads an external connector",
43
+ "connector-write": "writes to an external connector",
44
+ "external-http": "makes an outbound HTTP/webhook call",
45
+ "agent-llm": "runs an AI/agent step (not executed in simulation)",
46
+ "nested-recipe": "delegates to a nested recipe",
47
+ unknown: "unresolved tool — side effect unknown",
48
+ };
49
+ /** Empty side-effect tally with every kind present (so consumers can index safely). */
50
+ export function emptySideEffectCounts() {
51
+ return {
52
+ "local-read": 0,
53
+ "local-write": 0,
54
+ "connector-read": 0,
55
+ "connector-write": 0,
56
+ "external-http": 0,
57
+ "agent-llm": 0,
58
+ "nested-recipe": 0,
59
+ unknown: 0,
60
+ };
61
+ }
62
+ //# sourceMappingURL=sideEffects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sideEffects.js","sourceRoot":"","sources":["../../../src/recipes/simulation/sideEffects.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,wFAAwF;AACxF,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAW9D;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAqB;IAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,WAAW,CAAC;IAC9C,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,eAAe,CAAC;IAEnD,kBAAkB;IAClB,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK;QAAE,OAAO,SAAS,CAAC;IAE9C,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE,IAAI,wBAAwB,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,eAAe,CAAC;IAEnE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IACtC,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,gBAAgB,CAAC;IACxD,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC;AAChD,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,MAAM,kBAAkB,GAAmC;IAChE,YAAY,EAAE,6BAA6B;IAC3C,aAAa,EAAE,8BAA8B;IAC7C,gBAAgB,EAAE,6BAA6B;IAC/C,iBAAiB,EAAE,iCAAiC;IACpD,eAAe,EAAE,qCAAqC;IACtD,WAAW,EAAE,oDAAoD;IACjE,eAAe,EAAE,8BAA8B;IAC/C,OAAO,EAAE,uCAAuC;CACjD,CAAC;AAEF,uFAAuF;AACvF,MAAM,UAAU,qBAAqB;IACnC,OAAO;QACL,YAAY,EAAE,CAAC;QACf,aAAa,EAAE,CAAC;QAChB,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,CAAC;QACpB,eAAe,EAAE,CAAC;QAClB,WAAW,EAAE,CAAC;QACd,eAAe,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * What-If Preview — static simulation core.
3
+ *
4
+ * `simulateFromPlan` is a PURE transform: dry-run plan → simulation report.
5
+ * It performs no I/O and has no clock dependency (it reuses the plan's
6
+ * `generatedAt`), so it is fully deterministic and unit-testable. The
7
+ * I/O-bearing entrypoint (`runRecipeSimulate`, which produces the plan first)
8
+ * lives in `src/commands/recipe.ts`.
9
+ */
10
+ import type { RecipeDryRunPlan } from "../../commands/recipe.js";
11
+ import type { MockedRunResult } from "./simulateMockedRun.js";
12
+ import { type RecipeSimulationReport } from "./types.js";
13
+ /** Pure transform: a dry-run plan → a What-If Preview simulation report. */
14
+ export declare function simulateFromPlan(plan: RecipeDryRunPlan): RecipeSimulationReport;
15
+ /**
16
+ * Extract the step ids a `when:` condition references via `{{ ... }}`
17
+ * placeholders. Covers the common dotted forms the chained runner exposes
18
+ * (`steps.<id>.data`, `outputs.<id>`, or a bare `<id>`). A coarse regex is
19
+ * deliberate — we only need to know WHICH step ids the branch depends on so we
20
+ * can decide whether the branch is determinable from history.
21
+ */
22
+ export declare function extractReferencedStepIds(condition: string): Set<string>;
23
+ /**
24
+ * P2 — compose the mocked report: take the static report and OVERLAY the
25
+ * results of the trace-seeded mocked sandbox run.
26
+ *
27
+ * - `fidelity:"mocked"` + `sampleRuns`.
28
+ * - each step's `mockedFrom` ("history" | "synthesized").
29
+ * - `branches[].outcome` recomputed: `"taken"`/`"skipped"` when EVERY step id
30
+ * the `when:` references is in `historyStepIds` (determinable from real
31
+ * data), else `"undetermined"`.
32
+ *
33
+ * Risk / side-effect / cost / approvals sections are carried over unchanged
34
+ * from the static report. `gatedOnRecipeSteps` stays false (P0/P4 truth).
35
+ */
36
+ export declare function simulateMockedFromPlan(plan: RecipeDryRunPlan, mocked: MockedRunResult): RecipeSimulationReport;