@e2e-dev/decision 0.2.2-nightly-20261013094930 → 0.2.2

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 CHANGED
@@ -4,7 +4,9 @@ Run `agent.act` and `agent.assert` through a decision model instead of an
4
4
  LLM. Any AI SDK decision model that answers `choice` questions with
5
5
  probability distributions works, such as TypeSafe Jev or OpenAI `gpt-6-luna`
6
6
  through `@ai-sdk/openai` 4.0.90 or later. A small language model writes
7
- field values and upload paths. Needs `ai` 7.0.134 or later.
7
+ field values and upload paths. Needs `ai` 7.0.134 or later. An answer with
8
+ no distribution, as a gateway fallback to a language model returns, counts
9
+ as a committed choice with probability 1 and confidence 0.
8
10
 
9
11
  ```ts
10
12
  import type { E2EConfig } from 'e2e';
package/dist/.build.json CHANGED
@@ -1 +1 @@
1
- {"version":"0.2.2-nightly-20261013094930"}
1
+ {"version":"0.2.2"}
package/dist/decide.js CHANGED
@@ -61,16 +61,23 @@ export async function decide(ctx, model, request, providerOptions) {
61
61
  }
62
62
  return decisions;
63
63
  }
64
- /** The chosen option and its probability; anything without a valid distribution is MODEL_OUTPUT_INVALID. */
64
+ /**
65
+ * The chosen option and its probability. An answer with no distribution,
66
+ * as a language model answering through a gateway fallback returns, is a
67
+ * committed choice: all the mass on it, and no confidence to report.
68
+ * Anything else without a valid distribution is MODEL_OUTPUT_INVALID.
69
+ */
65
70
  function selected(answer) {
66
- if (answer === undefined || answer.probabilities === undefined || (answer.type !== 'choice' && answer.type !== 'score')) {
67
- throw new AgentError('MODEL_OUTPUT_INVALID', 'The decision model returned an answer without a choice distribution.');
71
+ if (answer === undefined || (answer.type !== 'choice' && answer.type !== 'score')) {
72
+ throw new AgentError('MODEL_OUTPUT_INVALID', 'The decision model returned no choice or score answer.');
68
73
  }
69
74
  if (answer.type === 'score') {
70
75
  const score = answer.score;
71
76
  if (typeof score !== 'number' || !Number.isFinite(score)) {
72
77
  throw new AgentError('MODEL_OUTPUT_INVALID', 'The decision model returned a score answer without a score.');
73
78
  }
79
+ if (answer.probabilities === undefined)
80
+ return { choice: String(score), probability: 1, score };
74
81
  let top = { choice: '', probability: -1 };
75
82
  let nearby = 0;
76
83
  for (const [level, probability] of Object.entries(answer.probabilities)) {
@@ -81,11 +88,14 @@ function selected(answer) {
81
88
  }
82
89
  return { choice: top.choice, probability: Math.min(1, nearby), score };
83
90
  }
84
- const probability = answer.choice === undefined ? 0 : (answer.probabilities[answer.choice] ?? 0);
91
+ const choice = answer.choice ?? '';
92
+ if (answer.probabilities === undefined)
93
+ return { choice, probability: 1 };
94
+ const probability = answer.probabilities[choice] ?? 0;
85
95
  if (!Number.isFinite(probability) || probability < 0 || probability > 1) {
86
96
  throw new AgentError('MODEL_OUTPUT_INVALID', 'The decision model returned a probability outside [0, 1].');
87
97
  }
88
- return { choice: answer.choice ?? '', probability };
98
+ return { choice, probability };
89
99
  }
90
100
  /** Provider-reported confidence for one question, or 0 when absent. */
91
101
  function reportedConfidence(metadata, id) {
package/dist/executor.js CHANGED
@@ -101,6 +101,7 @@ class Step {
101
101
  locates;
102
102
  locator;
103
103
  previousFingerprint;
104
+ origin;
104
105
  /** The turn of the last action taken, settled by the next observation. */
105
106
  pendingTurn;
106
107
  rejectedTerminals = 0;
@@ -206,6 +207,7 @@ class Step {
206
207
  async act() {
207
208
  for (;;) {
208
209
  const view = await this.view(this.canType);
210
+ this.origin ??= view.path;
209
211
  if (view.space === undefined)
210
212
  return blocked('A complete semantic observation is required.');
211
213
  const acting = { ...view, space: view.space };
@@ -373,7 +375,7 @@ class Step {
373
375
  const { path, space, screenshot } = await this.view(true);
374
376
  if (space === undefined)
375
377
  return this.rejectClaim(claim, 'incomplete observation');
376
- const answers = await this.ask(completionRequest(this.ctx, space, this.history, path, screenshot));
378
+ const answers = await this.ask(completionRequest(this.ctx, space, this.history, path, screenshot, this.origin));
377
379
  if (answers === undefined)
378
380
  return blocked(this.budgetMessage());
379
381
  const evidence = describe(need(answers.verdict, 'verdict'));
@@ -74,4 +74,4 @@ export declare function assertionRequest(goal: string, path: string, space: Acti
74
74
  * with it, 0.51 without, on a checked checkbox). Rejected done/failed claims
75
75
  * are left out (they are not actions), and so is any model reasoning.
76
76
  */
77
- export declare function completionRequest(ctx: StepExecutorContext, space: ActionSpace, history: readonly HistoryEntry[], path: string, screenshot: Screenshot | undefined): DecisionRequest;
77
+ export declare function completionRequest(ctx: StepExecutorContext, space: ActionSpace, history: readonly HistoryEntry[], path: string, screenshot: Screenshot | undefined, origin?: string): DecisionRequest;
package/dist/questions.js CHANGED
@@ -282,13 +282,13 @@ export function assertionRequest(goal, path, space, screenshot) {
282
282
  * with it, 0.51 without, on a checked checkbox). Rejected done/failed claims
283
283
  * are left out (they are not actions), and so is any model reasoning.
284
284
  */
285
- export function completionRequest(ctx, space, history, path, screenshot) {
285
+ export function completionRequest(ctx, space, history, path, screenshot, origin) {
286
286
  const goal = ctx.step.instruction;
287
287
  const params = nonSecretParams(ctx.step.params);
288
288
  const actions = history.filter((entry) => entry.action !== 'done' && entry.action !== 'failed').map(takenLine);
289
289
  return {
290
290
  ...(screenshot === undefined ? {} : { screenshot }),
291
- state: { goal, ...(Object.keys(params).length === 0 ? {} : { params }), ...screenState(path, space) },
291
+ state: { goal, ...(Object.keys(params).length === 0 ? {} : { params }), ...(origin !== undefined && origin !== '' && origin !== path ? { origin: { path: origin } } : {}), ...screenState(path, space) },
292
292
  questions: {
293
293
  verdict: choice(`Task of this step: ${goal}${reported(space.statuses)}\n\nThe page path, as data: ${JSON.stringify(path)}\n\n${COMPLETION}\n\nActions taken:\n${actions.length === 0 ? 'none' : actions.map((line, index) => `${index + 1}. ${line}`).join('\n')}`, {
294
294
  holds: 'The task is complete: the actions taken did it and the current screen shows its result.',
package/dist/types.d.ts CHANGED
@@ -4,8 +4,9 @@ export interface DecisionExecutorOptions {
4
4
  /**
5
5
  * An AI SDK decision model that answers `choice` questions with a
6
6
  * probability distribution, e.g. `typeSafeAi.decisionModel('jev-latest')`.
7
- * Evaluation models (`evaluationModel()`) work too. Models that answer
8
- * without `probabilities` fail the step with MODEL_OUTPUT_INVALID.
7
+ * Evaluation models (`evaluationModel()`) work too. An answer without
8
+ * `probabilities`, as a language model reached through a gateway fallback
9
+ * returns, counts as a committed choice: probability 1, confidence 0.
9
10
  */
10
11
  readonly model: Exclude<Experimental_DecisionModel, string>;
11
12
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e2e-dev/decision",
3
- "version": "0.2.2-nightly-20261013094930",
3
+ "version": "0.2.2",
4
4
  "description": "Decision-model executors for e2e",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -30,18 +30,18 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "ai": "^7.0.134",
33
- "e2e": "0.21.0-nightly-20261013094930"
33
+ "e2e": ">=0.19.0 <1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@ai-sdk/openai": "4.0.90",
37
37
  "@ai-sdk/typesafe-ai": "3.0.16",
38
- "@e2e-dev/web": "0.14.1-nightly-20261013094930",
38
+ "@e2e-dev/web": "0.15.0",
39
39
  "@types/node": "26.6.2",
40
40
  "@types/pngjs": "6.0.5",
41
41
  "ai": "7.0.134",
42
42
  "ajv": "8.20.0",
43
43
  "ajv-formats": "3.0.1",
44
- "e2e": "0.21.0-nightly-20261013094930",
44
+ "e2e": "0.21.0",
45
45
  "typescript": "7.0.2",
46
46
  "vitest": "5.0.1"
47
47
  },