cairn-engine 0.3.0 → 1.1.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 (43) hide show
  1. package/README.md +24 -0
  2. package/dist/adapters/context/inline.d.ts +0 -2
  3. package/dist/adapters/context/inline.js +1 -5
  4. package/dist/adapters/context/inline.js.map +1 -1
  5. package/dist/adapters/critics/assertion.d.ts +30 -2
  6. package/dist/adapters/critics/assertion.js +53 -2
  7. package/dist/adapters/critics/assertion.js.map +1 -1
  8. package/dist/adapters/critics/llm.d.ts +12 -5
  9. package/dist/adapters/critics/llm.js +32 -11
  10. package/dist/adapters/critics/llm.js.map +1 -1
  11. package/dist/adapters/drivers/chrome.d.ts +14 -3
  12. package/dist/adapters/drivers/chrome.js +44 -19
  13. package/dist/adapters/drivers/chrome.js.map +1 -1
  14. package/dist/adapters/drivers/fake.d.ts +1 -0
  15. package/dist/adapters/drivers/fake.js +8 -0
  16. package/dist/adapters/drivers/fake.js.map +1 -1
  17. package/dist/adapters/drivers/self-heal.d.ts +4 -0
  18. package/dist/adapters/drivers/self-heal.js +8 -1
  19. package/dist/adapters/drivers/self-heal.js.map +1 -1
  20. package/dist/adapters/planners/static.d.ts +1 -1
  21. package/dist/adapters/planners/static.js +4 -2
  22. package/dist/adapters/planners/static.js.map +1 -1
  23. package/dist/browser.d.ts +26 -0
  24. package/dist/browser.js +23 -0
  25. package/dist/browser.js.map +1 -0
  26. package/dist/core/discover.d.ts +1 -1
  27. package/dist/core/discover.js +112 -33
  28. package/dist/core/discover.js.map +1 -1
  29. package/dist/core/pipeline.d.ts +7 -2
  30. package/dist/core/pipeline.js +10 -33
  31. package/dist/core/pipeline.js.map +1 -1
  32. package/dist/core/ports.d.ts +26 -3
  33. package/dist/core/steps.d.ts +22 -0
  34. package/dist/core/steps.js +57 -0
  35. package/dist/core/steps.js.map +1 -0
  36. package/dist/core/types.d.ts +20 -3
  37. package/dist/index.d.ts +4 -2
  38. package/dist/index.js +3 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/run.d.ts +8 -0
  41. package/dist/run.js +6 -3
  42. package/dist/run.js.map +1 -1
  43. package/package.json +9 -2
@@ -3,8 +3,10 @@ export class StaticPlanner {
3
3
  constructor(scenario) {
4
4
  this.scenario = scenario;
5
5
  }
6
- async plan(_ctx) {
7
- return this.scenario;
6
+ async plan(ctx) {
7
+ // A custom ContextProvider can relabel the run through intent; on the default
8
+ // replay path the task is scenario.name, so intent === name and this is a no-op.
9
+ return { ...this.scenario, name: ctx.intent || this.scenario.name };
8
10
  }
9
11
  }
10
12
  //# sourceMappingURL=static.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"static.js","sourceRoot":"","sources":["../../../src/adapters/planners/static.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,QAAkB;QAAlB,aAAQ,GAAR,QAAQ,CAAU;IAAG,CAAC;IAEnD,KAAK,CAAC,IAAI,CAAC,IAAa;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF"}
1
+ {"version":3,"file":"static.js","sourceRoot":"","sources":["../../../src/adapters/planners/static.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,QAAkB;QAAlB,aAAQ,GAAR,QAAQ,CAAU;IAAG,CAAC;IAEnD,KAAK,CAAC,IAAI,CAAC,GAAY;QACrB,8EAA8E;QAC9E,iFAAiF;QACjF,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACtE,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Browser-safe surface of cairn-engine — the runtime-agnostic core plus the pure adapters,
3
+ * for environments without Node (a Chrome extension, a web app). Excludes the Node-only
4
+ * adapters (Chrome DevTools MCP driver, Claude CLI client, fs-based reporters / skill store)
5
+ * and `runScenario`, which statically wires them.
6
+ *
7
+ * Compose `runHarness` with your own `Driver` (e.g. a CDP/extension driver) and, for discover or
8
+ * `expect`, a fetch-based `LlmClient` (`AnthropicLlmClient`). Same core, different hands.
9
+ */
10
+ export * from "./core/types.js";
11
+ export * from "./core/ports.js";
12
+ export { runHarness } from "./core/pipeline.js";
13
+ export type { RunHarnessOptions } from "./core/pipeline.js";
14
+ export { BuiltinStepHandler, CustomStepHandler, defaultStepHandlers } from "./core/steps.js";
15
+ export { InlineContextProvider } from "./adapters/context/inline.js";
16
+ export { StaticPlanner } from "./adapters/planners/static.js";
17
+ export { AssertionCritic, checkAssertion, resolveAssertion, judgeAssertion, MechanicalAssertionHandler, CustomAssertionHandler, } from "./adapters/critics/assertion.js";
18
+ export type { CustomCheck, CustomChecks } from "./adapters/critics/assertion.js";
19
+ export { LlmCritic, ExpectAssertionHandler, summarizeEvidence } from "./adapters/critics/llm.js";
20
+ export { ConsoleReporter } from "./adapters/reporters/console.js";
21
+ export { FakeDriver } from "./adapters/drivers/fake.js";
22
+ export { SelfHealingDriver, parseHealChoice } from "./adapters/drivers/self-heal.js";
23
+ export type { Heal, SelfHealOptions } from "./adapters/drivers/self-heal.js";
24
+ export { AnthropicLlmClient } from "./adapters/llm/anthropic.js";
25
+ export { discover, parseDecision } from "./core/discover.js";
26
+ export type { DiscoverOptions, Decision } from "./core/discover.js";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Browser-safe surface of cairn-engine — the runtime-agnostic core plus the pure adapters,
3
+ * for environments without Node (a Chrome extension, a web app). Excludes the Node-only
4
+ * adapters (Chrome DevTools MCP driver, Claude CLI client, fs-based reporters / skill store)
5
+ * and `runScenario`, which statically wires them.
6
+ *
7
+ * Compose `runHarness` with your own `Driver` (e.g. a CDP/extension driver) and, for discover or
8
+ * `expect`, a fetch-based `LlmClient` (`AnthropicLlmClient`). Same core, different hands.
9
+ */
10
+ export * from "./core/types.js";
11
+ export * from "./core/ports.js";
12
+ export { runHarness } from "./core/pipeline.js";
13
+ export { BuiltinStepHandler, CustomStepHandler, defaultStepHandlers } from "./core/steps.js";
14
+ export { InlineContextProvider } from "./adapters/context/inline.js";
15
+ export { StaticPlanner } from "./adapters/planners/static.js";
16
+ export { AssertionCritic, checkAssertion, resolveAssertion, judgeAssertion, MechanicalAssertionHandler, CustomAssertionHandler, } from "./adapters/critics/assertion.js";
17
+ export { LlmCritic, ExpectAssertionHandler, summarizeEvidence } from "./adapters/critics/llm.js";
18
+ export { ConsoleReporter } from "./adapters/reporters/console.js";
19
+ export { FakeDriver } from "./adapters/drivers/fake.js";
20
+ export { SelfHealingDriver, parseHealChoice } from "./adapters/drivers/self-heal.js";
21
+ export { AnthropicLlmClient } from "./adapters/llm/anthropic.js";
22
+ export { discover, parseDecision } from "./core/discover.js";
23
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE7F,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACjG,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
@@ -24,6 +24,6 @@ export interface Decision {
24
24
  reason?: string;
25
25
  assertions?: Assertion[];
26
26
  }
27
- /** First JSON object in a model reply, tolerating code fences and surrounding prose. */
27
+ /** First JSON object in a model reply, tolerating fences, prose, and trailing extra objects. */
28
28
  export declare function parseDecision(text: string): Decision;
29
29
  export declare function discover(intent: string, opts: DiscoverOptions): Promise<Scenario>;
@@ -33,49 +33,119 @@ function buildPrompt(intent, elements, steps, failures) {
33
33
  `What is the single next action? Respond with JSON only.`,
34
34
  ].join("\n");
35
35
  }
36
- /** First JSON object in a model reply, tolerating code fences and surrounding prose. */
36
+ /** First JSON object in a model reply, tolerating fences, prose, and trailing extra objects. */
37
37
  export function parseDecision(text) {
38
- let s = text.trim();
39
- s = s.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
40
- const start = s.indexOf("{");
41
- const end = s.lastIndexOf("}");
42
- if (start === -1 || end === -1 || end < start) {
38
+ const obj = extractFirstJsonObject(text);
39
+ if (!obj)
43
40
  throw new Error(`no JSON object in model reply: ${text.slice(0, 200)}`);
44
- }
45
- const obj = JSON.parse(s.slice(start, end + 1));
46
41
  if (!obj.action)
47
- throw new Error(`decision missing "action": ${s.slice(0, 200)}`);
42
+ throw new Error(`decision missing "action": ${text.slice(0, 200)}`);
48
43
  return obj;
49
44
  }
50
- const KNOWN_KINDS = new Set(["navigated", "no-failed-requests", "no-console-errors", "request-status"]);
51
- /** Drop assertions the deterministic critic can't evaluate; fall back to a safe default. */
52
- function sanitizeAssertions(input) {
53
- const valid = (input ?? []).filter((a) => a && KNOWN_KINDS.has(a.kind));
54
- return valid.length ? valid : [{ kind: "no-failed-requests" }];
45
+ /**
46
+ * Extract the FIRST complete balanced {...} object. Slicing first-`{` to last-`}` breaks
47
+ * when a model emits two objects or trailing text (a real crash seen on complex flows);
48
+ * this scans braces (string-aware) and stops at the first balanced close.
49
+ */
50
+ function extractFirstJsonObject(text) {
51
+ const s = text.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
52
+ const start = s.indexOf("{");
53
+ if (start === -1)
54
+ return undefined;
55
+ let depth = 0;
56
+ let inStr = false;
57
+ let esc = false;
58
+ for (let i = start; i < s.length; i++) {
59
+ const ch = s[i];
60
+ if (inStr) {
61
+ if (esc)
62
+ esc = false;
63
+ else if (ch === "\\")
64
+ esc = true;
65
+ else if (ch === '"')
66
+ inStr = false;
67
+ }
68
+ else if (ch === '"')
69
+ inStr = true;
70
+ else if (ch === "{")
71
+ depth++;
72
+ else if (ch === "}" && --depth === 0) {
73
+ try {
74
+ return JSON.parse(s.slice(start, i + 1));
75
+ }
76
+ catch {
77
+ return undefined;
78
+ }
79
+ }
80
+ }
81
+ return undefined;
82
+ }
83
+ /**
84
+ * Ground the frozen scenario's assertions in what actually happened, not what the LLM
85
+ * guessed — it would propose `navigated` even on a SPA that never navigates, making every
86
+ * replay fail. Always check requests; add `navigated` only if the run truly navigated;
87
+ * keep any LLM-proposed `request-status` (still deterministic).
88
+ */
89
+ function deriveAssertions(proposed, evidence) {
90
+ const out = [{ kind: "no-failed-requests" }];
91
+ const { navigated, finalUrl } = evidence.execution;
92
+ // assert reaching the RIGHT destination (host+path), not just "navigated" — catches a flow
93
+ // that lands on an error/wrong page yet technically navigated.
94
+ if (navigated && finalUrl)
95
+ out.push({ kind: "navigated", to: destinationKey(finalUrl) });
96
+ else if (navigated)
97
+ out.push({ kind: "navigated" });
98
+ for (const a of proposed ?? []) {
99
+ if (a && a.kind === "request-status")
100
+ out.push(a);
101
+ }
102
+ return out;
103
+ }
104
+ /** host + path of a url (query/hash dropped) — a stable, meaningful destination to assert. */
105
+ function destinationKey(url) {
106
+ try {
107
+ const u = new URL(url);
108
+ return `${u.host}${u.pathname}`.replace(/\/$/, "");
109
+ }
110
+ catch {
111
+ return url;
112
+ }
55
113
  }
56
114
  /** Execute a non-`done` decision and return the Step it produced. Throws if it fails. */
57
115
  async function applyDecision(driver, decision) {
58
- const needText = () => {
116
+ // Enrich the target with resilient locators (role + structural index) before acting, and
117
+ // freeze the enriched target — so replay survives a UI rename without the LLM.
118
+ const located = () => {
59
119
  if (!decision.text)
60
120
  throw new Error(`${decision.action} decision missing "text"`);
61
- return decision.text;
121
+ return driver.locate({ text: decision.text });
62
122
  };
63
123
  switch (decision.action) {
64
- case "click":
65
- await driver.click({ text: needText() });
66
- return { kind: "click", target: { text: needText() } };
67
- case "doubleClick":
68
- await driver.doubleClick({ text: needText() });
69
- return { kind: "doubleClick", target: { text: needText() } };
70
- case "hover":
71
- await driver.hover({ text: needText() });
72
- return { kind: "hover", target: { text: needText() } };
73
- case "type":
74
- await driver.type({ text: needText() }, decision.value ?? "");
75
- return { kind: "type", target: { text: needText() }, text: decision.value ?? "" };
76
- case "select":
77
- await driver.select({ text: needText() }, decision.value ?? "");
78
- return { kind: "select", target: { text: needText() }, value: decision.value ?? "" };
124
+ case "click": {
125
+ const target = await located();
126
+ await driver.click(target);
127
+ return { kind: "click", target };
128
+ }
129
+ case "doubleClick": {
130
+ const target = await located();
131
+ await driver.doubleClick(target);
132
+ return { kind: "doubleClick", target };
133
+ }
134
+ case "hover": {
135
+ const target = await located();
136
+ await driver.hover(target);
137
+ return { kind: "hover", target };
138
+ }
139
+ case "type": {
140
+ const target = await located();
141
+ await driver.type(target, decision.value ?? "");
142
+ return { kind: "type", target, text: decision.value ?? "" };
143
+ }
144
+ case "select": {
145
+ const target = await located();
146
+ await driver.select(target, decision.value ?? "");
147
+ return { kind: "select", target, value: decision.value ?? "" };
148
+ }
79
149
  case "pressKey":
80
150
  if (!decision.key)
81
151
  throw new Error('pressKey decision missing "key"');
@@ -108,10 +178,19 @@ export async function discover(intent, opts) {
108
178
  await driver.settle();
109
179
  const elements = await driver.snapshot();
110
180
  const reply = await llm.complete(buildPrompt(intent, elements, steps, failures), { system: SYSTEM });
111
- const decision = parseDecision(reply);
181
+ let decision;
182
+ try {
183
+ decision = parseDecision(reply);
184
+ }
185
+ catch {
186
+ // A malformed reply must not kill the whole discovery — nudge and retry.
187
+ failures.push("your previous reply was not a single valid JSON action object");
188
+ continue;
189
+ }
112
190
  if (decision.action === "done") {
113
191
  onStep?.(decision);
114
- return { name: intent, steps, assertions: sanitizeAssertions(decision.assertions) };
192
+ const evidence = await driver.observe();
193
+ return { name: intent, steps, assertions: deriveAssertions(decision.assertions, evidence) };
115
194
  }
116
195
  try {
117
196
  const step = await applyDecision(driver, decision);
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/core/discover.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/core/discover.ts"],"names":[],"mappings":"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"}
@@ -3,16 +3,21 @@
3
3
  * is injected (invariant #2); with a fixed-scenario Planner + deterministic Critic, no LLM
4
4
  * runs (invariant #4).
5
5
  */
6
- import type { Harness } from "./ports.js";
6
+ import type { CustomAction, Harness, StepHandler } from "./ports.js";
7
7
  import type { Result, StepProgress } from "./types.js";
8
8
  /**
9
9
  * Seams a host (CLI, desktop app, CI) plugs into — the engine emits/accepts, the host
10
10
  * decides what to do. `onStep` for a live timeline, `captureScreenshots` for visual
11
- * replay, `signal` for a Stop button. None of these put UI in the engine.
11
+ * replay, `signal` for a Stop button, `actions` for product-defined interactions. None of
12
+ * these put UI in the engine.
12
13
  */
13
14
  export interface RunHarnessOptions {
14
15
  signal?: AbortSignal;
15
16
  onStep?: (progress: StepProgress) => void;
16
17
  captureScreenshots?: boolean;
18
+ /** Product-defined interactions for `{ kind: "custom", name }` steps, registered by name. */
19
+ actions?: Record<string, CustomAction>;
20
+ /** Replace the Execute-stage dispatch chain entirely (advanced); defaults to built-ins + `actions`. */
21
+ stepHandlers?: StepHandler[];
17
22
  }
18
23
  export declare function runHarness(harness: Harness, task: string, opts?: RunHarnessOptions): Promise<Result>;
@@ -1,35 +1,11 @@
1
- async function executeStep(driver, step) {
1
+ import { defaultStepHandlers } from "./steps.js";
2
+ /** Route one step to the first handler that supports it; record success/failure either way. */
3
+ async function executeStep(handlers, step, driver) {
2
4
  try {
3
- switch (step.kind) {
4
- case "goto":
5
- await driver.goto(step.url);
6
- break;
7
- case "click":
8
- await driver.click(step.target);
9
- break;
10
- case "doubleClick":
11
- await driver.doubleClick(step.target);
12
- break;
13
- case "hover":
14
- await driver.hover(step.target);
15
- break;
16
- case "type":
17
- await driver.type(step.target, step.text);
18
- break;
19
- case "select":
20
- await driver.select(step.target, step.value);
21
- break;
22
- case "pressKey":
23
- await driver.pressKey(step.key);
24
- break;
25
- case "scroll":
26
- await driver.scroll(step.direction);
27
- break;
28
- default: {
29
- const unhandled = step;
30
- throw new Error(`unhandled step kind: ${JSON.stringify(unhandled)}`);
31
- }
32
- }
5
+ const handler = handlers.find((h) => h.supports(step));
6
+ if (!handler)
7
+ throw new Error(`no step handler for kind "${step.kind}"`);
8
+ await handler.execute(step, driver);
33
9
  return { step, ok: true };
34
10
  }
35
11
  catch (err) {
@@ -38,6 +14,7 @@ async function executeStep(driver, step) {
38
14
  }
39
15
  export async function runHarness(harness, task, opts = {}) {
40
16
  const { context, planner, driver, critic, reporter } = harness;
17
+ const handlers = opts.stepHandlers ?? defaultStepHandlers(opts.actions ?? {});
41
18
  const ctx = await context.provide(task);
42
19
  const scenario = await planner.plan(ctx);
43
20
  // Drive steps; stop on the first failure but still observe the resulting state.
@@ -45,7 +22,7 @@ export async function runHarness(harness, task, opts = {}) {
45
22
  try {
46
23
  for (const step of scenario.steps) {
47
24
  opts.signal?.throwIfAborted(); // cooperative cancellation between steps (host owns Stop)
48
- const result = await executeStep(driver, step);
25
+ const result = await executeStep(handlers, step, driver);
49
26
  actions.push(result);
50
27
  if (opts.onStep) {
51
28
  const screenshot = opts.captureScreenshots ? await driver.screenshot().catch(() => undefined) : undefined;
@@ -61,7 +38,7 @@ export async function runHarness(harness, task, opts = {}) {
61
38
  ...observed,
62
39
  execution: { ...observed.execution, actions, blocked: actions.some((a) => !a.ok) },
63
40
  };
64
- const verdict = await critic.judge(evidence, scenario.assertions);
41
+ const verdict = await critic.judge(evidence, scenario.assertions, ctx);
65
42
  const out = { scenario: scenario.name, context: ctx, evidence, verdict };
66
43
  await reporter.emit(out);
67
44
  return out;
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/core/pipeline.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * The ports of the cairn engine (invariant #2: add behavior by implementing one of these,
3
3
  * never by branching inside a stage). Core depends only on these; `../adapters` implement them.
4
4
  */
5
- import type { Assertion, Context, Evidence, PageElement, Result, Scenario, SettleOptions, Target, Verdict } from "./types.js";
5
+ import type { Assertion, AssertionResult, Context, Evidence, PageElement, Result, Scenario, SettleOptions, Step, Target, Verdict } from "./types.js";
6
6
  /** Grounding from any source (NL, git diff, ticket, RAG). */
7
7
  export interface ContextProvider {
8
8
  provide(task: string): Promise<Context>;
@@ -18,6 +18,8 @@ export interface Driver {
18
18
  doubleClick(target: Target): Promise<void>;
19
19
  hover(target: Target): Promise<void>;
20
20
  type(target: Target, text: string): Promise<void>;
21
+ /** Resolve a target and return it enriched with resilient locators (role, structural index) for freezing. */
22
+ locate(target: Target): Promise<Target>;
21
23
  /** Choose an option in a `<select>` dropdown. */
22
24
  select(target: Target, value: string): Promise<void>;
23
25
  /** Press a key or combo (e.g. "Enter", "Escape", "Control+a"). */
@@ -32,6 +34,17 @@ export interface Driver {
32
34
  observe(): Promise<Evidence>;
33
35
  close(): Promise<void>;
34
36
  }
37
+ /** A product-defined interaction for a `{ kind: "custom", name }` step — composes the Driver. */
38
+ export type CustomAction = (driver: Driver, params: Record<string, unknown>) => Promise<void>;
39
+ /**
40
+ * One link in the Execute stage's dispatch chain (invariant #2): the pipeline routes each Step
41
+ * to the first handler that `supports` it, instead of branching inside the stage. Built-in kinds
42
+ * and product `custom` actions resolve through this one seam (Spring `HandlerAdapter`-style).
43
+ */
44
+ export interface StepHandler {
45
+ supports(step: Step): boolean;
46
+ execute(step: Step, driver: Driver): Promise<void>;
47
+ }
35
48
  export interface Skill {
36
49
  name: string;
37
50
  scenario: Scenario;
@@ -39,9 +52,19 @@ export interface Skill {
39
52
  export interface SkillStore {
40
53
  resolve(name: string): Promise<Skill | undefined>;
41
54
  }
42
- /** Judges evidence against assertions (mechanical, baseline, or LLM). */
55
+ /**
56
+ * One link in the Judge stage's dispatch chain (mirror of StepHandler): a Critic routes each
57
+ * Assertion to the first handler that `supports` it. Mechanical, product `custom`, and LLM
58
+ * `expect` checks compose as separate handlers — critics differ only by which they register.
59
+ * Optional `ctx` grounds LLM judgment (e.g. the task intent); deterministic handlers ignore it.
60
+ */
61
+ export interface AssertionHandler {
62
+ supports(assertion: Assertion): boolean;
63
+ judge(assertion: Assertion, evidence: Evidence, ctx?: Context): AssertionResult | Promise<AssertionResult>;
64
+ }
65
+ /** Judges evidence against assertions (mechanical, baseline, or LLM). Optional `ctx` grounds LLM judgment (e.g. the task intent); deterministic critics ignore it, so replay stays deterministic (invariant #4). */
43
66
  export interface Critic {
44
- judge(evidence: Evidence, assertions: Assertion[]): Promise<Verdict>;
67
+ judge(evidence: Evidence, assertions: Assertion[], ctx?: Context): Promise<Verdict>;
45
68
  }
46
69
  /** Emits a result anywhere — console, json, an arbitrary tracker. */
47
70
  export interface Reporter {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Execute-stage step handlers (invariant #2). The pipeline routes each Step to the first
3
+ * handler that `supports` it (DispatcherServlet-style) — built-in kinds and product-defined
4
+ * `custom` actions resolve through the same `StepHandler` seam, so adding an action means
5
+ * registering a handler, never editing a stage. Depends only on core ports/types.
6
+ */
7
+ import type { CustomAction, Driver, StepHandler } from "./ports.js";
8
+ import type { Step } from "./types.js";
9
+ /** Handles cairn's built-in step vocabulary — every kind except product-defined `custom`. */
10
+ export declare class BuiltinStepHandler implements StepHandler {
11
+ supports(step: Step): boolean;
12
+ execute(step: Step, driver: Driver): Promise<void>;
13
+ }
14
+ /** Handles product-defined `{ kind: "custom", name }` steps via a name→action registry. */
15
+ export declare class CustomStepHandler implements StepHandler {
16
+ private readonly actions;
17
+ constructor(actions?: Record<string, CustomAction>);
18
+ supports(step: Step): boolean;
19
+ execute(step: Step, driver: Driver): Promise<void>;
20
+ }
21
+ /** The engine's default Execute-stage chain: built-ins first, then product `custom` actions. */
22
+ export declare function defaultStepHandlers(actions?: Record<string, CustomAction>): StepHandler[];
@@ -0,0 +1,57 @@
1
+ /** Handles cairn's built-in step vocabulary — every kind except product-defined `custom`. */
2
+ export class BuiltinStepHandler {
3
+ supports(step) {
4
+ return step.kind !== "custom";
5
+ }
6
+ async execute(step, driver) {
7
+ switch (step.kind) {
8
+ case "goto":
9
+ return driver.goto(step.url);
10
+ case "click":
11
+ return driver.click(step.target);
12
+ case "doubleClick":
13
+ return driver.doubleClick(step.target);
14
+ case "hover":
15
+ return driver.hover(step.target);
16
+ case "type":
17
+ return driver.type(step.target, step.text);
18
+ case "select":
19
+ return driver.select(step.target, step.value);
20
+ case "pressKey":
21
+ return driver.pressKey(step.key);
22
+ case "scroll":
23
+ return driver.scroll(step.direction);
24
+ case "custom":
25
+ // Owned by CustomStepHandler; reaching here means a handler-ordering bug, not bad input.
26
+ throw new Error(`built-in handler received custom step "${step.name}"`);
27
+ default: {
28
+ // Exhaustiveness guard: a new Step kind that no case handles fails to compile here.
29
+ const unhandled = step;
30
+ throw new Error(`unhandled step kind: ${JSON.stringify(unhandled)}`);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ /** Handles product-defined `{ kind: "custom", name }` steps via a name→action registry. */
36
+ export class CustomStepHandler {
37
+ actions;
38
+ constructor(actions = {}) {
39
+ this.actions = actions;
40
+ }
41
+ supports(step) {
42
+ return step.kind === "custom";
43
+ }
44
+ async execute(step, driver) {
45
+ if (step.kind !== "custom")
46
+ throw new Error(`custom handler received "${step.kind}" step`);
47
+ const action = this.actions[step.name];
48
+ if (!action)
49
+ throw new Error(`no handler registered for custom action "${step.name}"`);
50
+ await action(driver, step.params ?? {});
51
+ }
52
+ }
53
+ /** The engine's default Execute-stage chain: built-ins first, then product `custom` actions. */
54
+ export function defaultStepHandlers(actions = {}) {
55
+ return [new BuiltinStepHandler(), new CustomStepHandler(actions)];
56
+ }
57
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../../src/core/steps.ts"],"names":[],"mappings":"AASA,6FAA6F;AAC7F,MAAM,OAAO,kBAAkB;IAC7B,QAAQ,CAAC,IAAU;QACjB,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAU,EAAE,MAAc;QACtC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,MAAM;gBACT,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/B,KAAK,OAAO;gBACV,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,KAAK,aAAa;gBAChB,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzC,KAAK,OAAO;gBACV,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,KAAK,MAAM;gBACT,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,KAAK,QAAQ;gBACX,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAChD,KAAK,UAAU;gBACb,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,KAAK,QAAQ;gBACX,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvC,KAAK,QAAQ;gBACX,yFAAyF;gBACzF,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;YAC1E,OAAO,CAAC,CAAC,CAAC;gBACR,oFAAoF;gBACpF,MAAM,SAAS,GAAU,IAAI,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,2FAA2F;AAC3F,MAAM,OAAO,iBAAiB;IACC;IAA7B,YAA6B,UAAwC,EAAE;QAA1C,YAAO,GAAP,OAAO,CAAmC;IAAG,CAAC;IAE3E,QAAQ,CAAC,IAAU;QACjB,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAU,EAAE,MAAc;QACtC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC;QAC3F,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACvF,MAAM,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;CACF;AAED,gGAAgG;AAChG,MAAM,UAAU,mBAAmB,CAAC,UAAwC,EAAE;IAC5E,OAAO,CAAC,IAAI,kBAAkB,EAAE,EAAE,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;AACpE,CAAC"}
@@ -1,8 +1,6 @@
1
1
  /** Domain types for the pipeline (Context → Plan → Execute → Judge → Report). App-agnostic by invariant #1. */
2
2
  export interface Context {
3
3
  intent: string;
4
- baseUrl?: string;
5
- notes?: string[];
6
4
  }
7
5
  export type Step = {
8
6
  kind: "goto";
@@ -30,10 +28,23 @@ export type Step = {
30
28
  } | {
31
29
  kind: "scroll";
32
30
  direction?: "down" | "up";
31
+ }
32
+ /** A product-defined interaction: the host registers a handler for `name`. */
33
+ | {
34
+ kind: "custom";
35
+ name: string;
36
+ params?: Record<string, unknown>;
33
37
  };
34
- /** Locate an element by intent, not a driver handle: `text` = accessible name, `selector` = CSS fallback. */
38
+ /**
39
+ * Locate an element by intent, not a driver handle. A frozen target carries several
40
+ * locators so replay survives UI change without falling back to the LLM:
41
+ * `text` (accessible name) is primary; `role` + `index` (position among same-role elements)
42
+ * is a rename-resilient fallback; `selector` is a CSS escape hatch.
43
+ */
35
44
  export interface Target {
36
45
  text?: string;
46
+ role?: string;
47
+ index?: number;
37
48
  selector?: string;
38
49
  }
39
50
  /**
@@ -54,6 +65,12 @@ export type Assertion = {
54
65
  } | {
55
66
  kind: "expect";
56
67
  criterion: string;
68
+ }
69
+ /** A product-defined success criterion: the host registers a handler for `name`. */
70
+ | {
71
+ kind: "custom";
72
+ name: string;
73
+ params?: Record<string, unknown>;
57
74
  };
58
75
  export interface Scenario {
59
76
  name: string;
package/dist/index.d.ts CHANGED
@@ -3,12 +3,14 @@ export * from "./core/types.js";
3
3
  export * from "./core/ports.js";
4
4
  export { runHarness } from "./core/pipeline.js";
5
5
  export type { RunHarnessOptions } from "./core/pipeline.js";
6
+ export { BuiltinStepHandler, CustomStepHandler, defaultStepHandlers } from "./core/steps.js";
6
7
  export { runScenario, needsLlmCritic, applyHeals } from "./run.js";
7
8
  export type { RunScenarioOptions, RunScenarioResult } from "./run.js";
8
9
  export { InlineContextProvider } from "./adapters/context/inline.js";
9
10
  export { StaticPlanner } from "./adapters/planners/static.js";
10
- export { AssertionCritic, checkAssertion } from "./adapters/critics/assertion.js";
11
- export { LlmCritic, summarizeEvidence } from "./adapters/critics/llm.js";
11
+ export { AssertionCritic, checkAssertion, resolveAssertion, judgeAssertion, MechanicalAssertionHandler, CustomAssertionHandler, } from "./adapters/critics/assertion.js";
12
+ export type { CustomCheck, CustomChecks } from "./adapters/critics/assertion.js";
13
+ export { LlmCritic, ExpectAssertionHandler, summarizeEvidence } from "./adapters/critics/llm.js";
12
14
  export { ConsoleReporter } from "./adapters/reporters/console.js";
13
15
  export { JsonReporter } from "./adapters/reporters/json.js";
14
16
  export { FakeDriver } from "./adapters/drivers/fake.js";