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.
- package/README.md +20 -1
- package/dist/adapters/grok.js +6 -1
- package/dist/adapters/grok.js.map +1 -1
- package/dist/approvalHttp.js +61 -66
- package/dist/approvalHttp.js.map +1 -1
- package/dist/bridge.js +3 -2
- package/dist/bridge.js.map +1 -1
- package/dist/claudeOrchestrator.js +15 -0
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/connect.d.ts +47 -0
- package/dist/commands/connect.js +419 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/patchworkInit.d.ts +7 -0
- package/dist/commands/patchworkInit.js +26 -0
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +14 -0
- package/dist/commands/recipe.js +51 -0
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipeInstall.js +15 -0
- package/dist/commands/recipeInstall.js.map +1 -1
- package/dist/connectors/postgres.d.ts +7 -0
- package/dist/connectors/postgres.js +16 -2
- package/dist/connectors/postgres.js.map +1 -1
- package/dist/connectors/snowflake.js +6 -0
- package/dist/connectors/snowflake.js.map +1 -1
- package/dist/connectors/tokenStorage.d.ts +8 -0
- package/dist/connectors/tokenStorage.js +42 -3
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/drivers/claude/api.js +15 -2
- package/dist/drivers/claude/api.js.map +1 -1
- package/dist/drivers/claude/subprocess.js +1 -29
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/gemini/index.js +9 -5
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/openai/index.js +29 -4
- package/dist/drivers/openai/index.js.map +1 -1
- package/dist/drivers/outputCap.d.ts +27 -0
- package/dist/drivers/outputCap.js +50 -0
- package/dist/drivers/outputCap.js.map +1 -0
- package/dist/featureFlags.d.ts +9 -0
- package/dist/featureFlags.js +16 -0
- package/dist/featureFlags.js.map +1 -1
- package/dist/fp/commandDescription.d.ts +6 -0
- package/dist/fp/commandDescription.js +4 -4
- package/dist/fp/commandDescription.js.map +1 -1
- package/dist/fp/policyParser.js +13 -7
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/haltPushDispatch.js +7 -22
- package/dist/haltPushDispatch.js.map +1 -1
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -1
- package/dist/mcpRoutes.js +1 -1
- package/dist/mcpRoutes.js.map +1 -1
- package/dist/oauth.d.ts +13 -0
- package/dist/oauth.js +34 -1
- package/dist/oauth.js.map +1 -1
- package/dist/recipeOrchestration.js +10 -0
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.d.ts +39 -0
- package/dist/recipeRoutes.js +214 -12
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +20 -0
- package/dist/recipes/agentExecutor.js +32 -10
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +41 -2
- package/dist/recipes/chainedRunner.js +134 -6
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/connectorPreflight.js +30 -0
- package/dist/recipes/connectorPreflight.js.map +1 -1
- package/dist/recipes/haltCategory.d.ts +5 -0
- package/dist/recipes/haltCategory.js +7 -1
- package/dist/recipes/haltCategory.js.map +1 -1
- package/dist/recipes/judgeVerdict.d.ts +10 -0
- package/dist/recipes/judgeVerdict.js +10 -0
- package/dist/recipes/judgeVerdict.js.map +1 -1
- package/dist/recipes/names.d.ts +5 -0
- package/dist/recipes/names.js +10 -5
- package/dist/recipes/names.js.map +1 -1
- package/dist/recipes/parser.js +32 -9
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/pricing/costRouter.d.ts +43 -0
- package/dist/recipes/pricing/costRouter.js +44 -0
- package/dist/recipes/pricing/costRouter.js.map +1 -0
- package/dist/recipes/pricing/priceTable.d.ts +76 -0
- package/dist/recipes/pricing/priceTable.js +144 -0
- package/dist/recipes/pricing/priceTable.js.map +1 -0
- package/dist/recipes/runBudget.d.ts +82 -32
- package/dist/recipes/runBudget.js +182 -49
- package/dist/recipes/runBudget.js.map +1 -1
- package/dist/recipes/schema.d.ts +25 -4
- package/dist/recipes/schemaGenerator.d.ts +9 -0
- package/dist/recipes/schemaGenerator.js +386 -75
- package/dist/recipes/schemaGenerator.js.map +1 -1
- package/dist/recipes/simulation/aggregateRunRisk.d.ts +43 -0
- package/dist/recipes/simulation/aggregateRunRisk.js +117 -0
- package/dist/recipes/simulation/aggregateRunRisk.js.map +1 -0
- package/dist/recipes/simulation/costProjector.d.ts +32 -0
- package/dist/recipes/simulation/costProjector.js +194 -0
- package/dist/recipes/simulation/costProjector.js.map +1 -0
- package/dist/recipes/simulation/sideEffects.d.ts +32 -0
- package/dist/recipes/simulation/sideEffects.js +62 -0
- package/dist/recipes/simulation/sideEffects.js.map +1 -0
- package/dist/recipes/simulation/simulate.d.ts +36 -0
- package/dist/recipes/simulation/simulate.js +264 -0
- package/dist/recipes/simulation/simulate.js.map +1 -0
- package/dist/recipes/simulation/simulateMockedRun.d.ts +52 -0
- package/dist/recipes/simulation/simulateMockedRun.js +72 -0
- package/dist/recipes/simulation/simulateMockedRun.js.map +1 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.d.ts +31 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.js +50 -0
- package/dist/recipes/simulation/synthesizeMockedOutputs.js.map +1 -0
- package/dist/recipes/simulation/types.d.ts +198 -0
- package/dist/recipes/simulation/types.js +30 -0
- package/dist/recipes/simulation/types.js.map +1 -0
- package/dist/recipes/stepObservation.d.ts +12 -0
- package/dist/recipes/stepObservation.js +21 -1
- package/dist/recipes/stepObservation.js.map +1 -1
- package/dist/recipes/tools/airtable.d.ts +15 -0
- package/dist/recipes/tools/airtable.js +240 -0
- package/dist/recipes/tools/airtable.js.map +1 -0
- package/dist/recipes/tools/caldiy.d.ts +13 -0
- package/dist/recipes/tools/caldiy.js +214 -0
- package/dist/recipes/tools/caldiy.js.map +1 -0
- package/dist/recipes/tools/circleci.d.ts +10 -0
- package/dist/recipes/tools/circleci.js +204 -0
- package/dist/recipes/tools/circleci.js.map +1 -0
- package/dist/recipes/tools/cloudflare.d.ts +13 -0
- package/dist/recipes/tools/cloudflare.js +211 -0
- package/dist/recipes/tools/cloudflare.js.map +1 -0
- package/dist/recipes/tools/docs.d.ts +18 -0
- package/dist/recipes/tools/docs.js +94 -0
- package/dist/recipes/tools/docs.js.map +1 -0
- package/dist/recipes/tools/elasticsearch.d.ts +11 -0
- package/dist/recipes/tools/elasticsearch.js +156 -0
- package/dist/recipes/tools/elasticsearch.js.map +1 -0
- package/dist/recipes/tools/figma.d.ts +12 -0
- package/dist/recipes/tools/figma.js +194 -0
- package/dist/recipes/tools/figma.js.map +1 -0
- package/dist/recipes/tools/grafana.d.ts +11 -0
- package/dist/recipes/tools/grafana.js +216 -0
- package/dist/recipes/tools/grafana.js.map +1 -0
- package/dist/recipes/tools/http.d.ts +1 -1
- package/dist/recipes/tools/http.js +10 -28
- package/dist/recipes/tools/http.js.map +1 -1
- package/dist/recipes/tools/index.d.ts +26 -0
- package/dist/recipes/tools/index.js +26 -0
- package/dist/recipes/tools/index.js.map +1 -1
- package/dist/recipes/tools/monday.d.ts +22 -0
- package/dist/recipes/tools/monday.js +242 -0
- package/dist/recipes/tools/monday.js.map +1 -0
- package/dist/recipes/tools/obsidian.d.ts +15 -0
- package/dist/recipes/tools/obsidian.js +172 -0
- package/dist/recipes/tools/obsidian.js.map +1 -0
- package/dist/recipes/tools/paystack.d.ts +11 -0
- package/dist/recipes/tools/paystack.js +211 -0
- package/dist/recipes/tools/paystack.js.map +1 -0
- package/dist/recipes/tools/pipedrive.d.ts +16 -0
- package/dist/recipes/tools/pipedrive.js +233 -0
- package/dist/recipes/tools/pipedrive.js.map +1 -0
- package/dist/recipes/tools/postgres.d.ts +15 -0
- package/dist/recipes/tools/postgres.js +185 -0
- package/dist/recipes/tools/postgres.js.map +1 -0
- package/dist/recipes/tools/posthog.d.ts +16 -0
- package/dist/recipes/tools/posthog.js +219 -0
- package/dist/recipes/tools/posthog.js.map +1 -0
- package/dist/recipes/tools/redis.d.ts +9 -0
- package/dist/recipes/tools/redis.js +140 -0
- package/dist/recipes/tools/redis.js.map +1 -0
- package/dist/recipes/tools/resend.d.ts +8 -0
- package/dist/recipes/tools/resend.js +153 -0
- package/dist/recipes/tools/resend.js.map +1 -0
- package/dist/recipes/tools/salesforce.d.ts +16 -0
- package/dist/recipes/tools/salesforce.js +184 -0
- package/dist/recipes/tools/salesforce.js.map +1 -0
- package/dist/recipes/tools/sendgrid.d.ts +9 -0
- package/dist/recipes/tools/sendgrid.js +174 -0
- package/dist/recipes/tools/sendgrid.js.map +1 -0
- package/dist/recipes/tools/shopify.d.ts +16 -0
- package/dist/recipes/tools/shopify.js +265 -0
- package/dist/recipes/tools/shopify.js.map +1 -0
- package/dist/recipes/tools/snowflake.d.ts +16 -0
- package/dist/recipes/tools/snowflake.js +173 -0
- package/dist/recipes/tools/snowflake.js.map +1 -0
- package/dist/recipes/tools/stripe.js +13 -12
- package/dist/recipes/tools/stripe.js.map +1 -1
- package/dist/recipes/tools/supabase.d.ts +9 -0
- package/dist/recipes/tools/supabase.js +132 -0
- package/dist/recipes/tools/supabase.js.map +1 -0
- package/dist/recipes/tools/todoist.d.ts +15 -0
- package/dist/recipes/tools/todoist.js +227 -0
- package/dist/recipes/tools/todoist.js.map +1 -0
- package/dist/recipes/tools/twilio.d.ts +11 -0
- package/dist/recipes/tools/twilio.js +180 -0
- package/dist/recipes/tools/twilio.js.map +1 -0
- package/dist/recipes/tools/vercel.d.ts +9 -0
- package/dist/recipes/tools/vercel.js +145 -0
- package/dist/recipes/tools/vercel.js.map +1 -0
- package/dist/recipes/tools/webflow.d.ts +11 -0
- package/dist/recipes/tools/webflow.js +243 -0
- package/dist/recipes/tools/webflow.js.map +1 -0
- package/dist/recipes/tools/woocommerce.d.ts +18 -0
- package/dist/recipes/tools/woocommerce.js +259 -0
- package/dist/recipes/tools/woocommerce.js.map +1 -0
- package/dist/recipes/tools/wrapConnectorExecute.d.ts +25 -0
- package/dist/recipes/tools/wrapConnectorExecute.js +37 -0
- package/dist/recipes/tools/wrapConnectorExecute.js.map +1 -0
- package/dist/recipes/validation.js +264 -0
- package/dist/recipes/validation.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +112 -3
- package/dist/recipes/yamlRunner.js +482 -19
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/runLog.d.ts +46 -0
- package/dist/runLog.js +4 -0
- package/dist/runLog.js.map +1 -1
- package/dist/schemas/recipe.v1.json +217 -11
- package/dist/server.d.ts +3 -0
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -1
- package/dist/streamableHttp.js +3 -2
- package/dist/streamableHttp.js.map +1 -1
- package/dist/tools/gitWrite.js +5 -1
- package/dist/tools/gitWrite.js.map +1 -1
- package/dist/tools/terminal.js +15 -49
- package/dist/tools/terminal.js.map +1 -1
- package/dist/transport.d.ts +1 -0
- package/dist/transport.js +7 -0
- package/dist/transport.js.map +1 -1
- package/package.json +3 -3
- package/scripts/mcp-stdio-shim.cjs +218 -84
- 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":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,+EAA+E;AAC/E,SAAS,MAAM,CAAC,IAAc;IAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC;QAC5B,CAAC,CAAC,CAAE,MAAM,CAAC,GAAG,GAAG,CAAC,CAAY,GAAI,MAAM,CAAC,GAAG,CAAY,CAAC,GAAG,CAAC;QAC7D,CAAC,CAAE,MAAM,CAAC,GAAG,CAAY,CAAC;AAC9B,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,IAAsB,EACtB,MAAoB,EACpB,OAAiD,EAAE;IAEnD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iBAAiB,CAAC;IACtD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK;SAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpB,MAAM,UAAU,GAAG,YAAY,CAAC,MAAM,CAAC;IAEvC,2EAA2E;IAC3E,8DAA8D;IAC9D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC1E,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,KAAK,EAAE,aAAa;YACpB,UAAU,EAAE,MAAM;YAClB,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,CAAC;YACb,mBAAmB,EAAE,CAAC;YACtB,eAAe,EAAE,IAAI;YACrB,cAAc,EAAE,IAAI;YACpB,eAAe,EAAE,IAAI;YACrB,GAAG,EAAE,IAAI;YACT,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,IAAI;YACZ,iBAAiB,EAAE,CAAC;YACpB,IAAI,EAAE,uDAAuD;SAC9D,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC;QACxB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,OAAO,IAAI,gBAAgB;KACxC,CAAC,CAAC;IAEH,2CAA2C;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,MAAM;gBAAE,SAAS;YACtB,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;gBACzC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACpC,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YACD,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC1C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACxC,CAAC;YACD,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QACD,IAAI,WAAW;YAAE,UAAU,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,mBAAmB,GAAG,CAAC,CAAC;IAC5B,IAAI,iBAAiB,GAAG,IAAI,CAAC;IAE7B,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAgB,CAAC;QAC9C,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAC3C,IAAI,UAAU,EAAE,CAAC;YACf,iBAAiB,IAAI,CAAC,CAAC;YACvB,mBAAmB,IAAI,CAAC,CAAC;YACzB,gBAAgB,GAAG,IAAI,CAAC;YACxB,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjC,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS;gBAAE,iBAAiB,GAAG,KAAK,CAAC;YAC/D,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,OAAO,GAAG,IAAI,CAAC;gBACf,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnC,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;gBACnC,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,wDAAwD;YACxD,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9C,iBAAiB,GAAG,KAAK,CAAC;YAC1B,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC/B,mBAAmB,IAAI,CAAC,CAAC;gBACzB,gBAAgB,GAAG,IAAI,CAAC;gBACxB,QAAQ,IAAI,YAAY,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GACT,iBAAiB,GAAG,CAAC;QACnB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,mBAAmB,GAAG,CAAC;YACvB,CAAC,CAAC,WAAW;YACb,CAAC,CAAC,aAAa,CAAC;IACtB,MAAM,UAAU,GACd,KAAK,KAAK,SAAS;QACjB,CAAC,CAAC,iBAAiB,IAAI,iBAAiB,KAAK,UAAU;YACrD,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,KAAK;QACT,CAAC,CAAC,KAAK,KAAK,WAAW;YACrB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,MAAM,CAAC;IAEf,MAAM,IAAI,GAAG,SAAS,CAAC;QACrB,KAAK;QACL,UAAU;QACV,UAAU;QACV,iBAAiB;QACjB,UAAU;QACV,OAAO;QACP,WAAW;KACZ,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;QACL,UAAU;QACV,UAAU;QACV,UAAU;QACV,mBAAmB;QACnB,2EAA2E;QAC3E,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;QACnD,cAAc,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;QAClD,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QACpD,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI;QACzC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;QACvC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;QACvC,iBAAiB;QACjB,IAAI;KACL,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACvB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACnC,CAAC;AAED,SAAS,SAAS,CAAC,CAQlB;IACC,IAAI,CAAC,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;QAC9B,OAAO,oDAAoD,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;QAC5B,OAAO,+IAA+I,CAAC;IACzJ,CAAC;IACD,UAAU;IACV,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,UAAU,4BAA4B,CAAC,CAAC,UAAU,0BAA0B,CAAC,CAAC,UAAU,cAAc,CAAC;IAC/I,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACd,OAAO,yCAAyC,GAAG,0CAA0C,CAAC;IAChG,CAAC;IACD,OAAO,mCAAmC,GAAG,gHAAgH,CAAC;AAChK,CAAC"}
|
|
@@ -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;
|