opensteer 0.5.5 → 0.5.6

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/CHANGELOG.md CHANGED
@@ -27,6 +27,8 @@
27
27
  - Cloud mode now falls back to `OPENSTEER_API_KEY` when `cloud.apiKey` is omitted.
28
28
  - Added automatic `.env` loading from `storage.rootDir` (default `process.cwd()`) so constructor config can consume env vars without requiring `import 'dotenv/config'`.
29
29
  - `.env` autoload follows common precedence (`.env.<NODE_ENV>.local`, `.env.local`, `.env.<NODE_ENV>`, `.env`) with `.env.local` skipped in `test`, does not overwrite existing env values, and can be disabled via `OPENSTEER_DISABLE_DOTENV_AUTOLOAD`.
30
+ - Opensteer now reuses one resolved runtime env snapshot for config, CUA provider key resolution, and built-in AI resolve/extract provider setup; dotenv loading still does not mutate global `process.env`.
31
+ - AI helper exports now accept optional `env` maps (`getModelProvider`, `createResolveCallback`, `createExtractCallback`) for deterministic provider initialization without relying on ambient process env state.
30
32
  - Mutating actions now include smart best-effort post-action wait with per-action
31
33
  profiles and optional per-call overrides via `wait`.
32
34
  - Added structured interaction diagnostics via `OpensteerActionError` for
@@ -5,12 +5,13 @@ import {
5
5
  buildExtractSystemPrompt,
6
6
  buildExtractUserPrompt,
7
7
  getModelProvider
8
- } from "./chunk-QHZFY3ZK.js";
8
+ } from "./chunk-FAHE5DB2.js";
9
9
 
10
10
  // src/ai/extractor.ts
11
11
  function createExtractCallback(model, options) {
12
12
  const temperature = options?.temperature ?? 1;
13
13
  const maxTokens = options?.maxTokens ?? null;
14
+ const env = options?.env;
14
15
  return async (args) => {
15
16
  let generateText;
16
17
  try {
@@ -21,7 +22,7 @@ function createExtractCallback(model, options) {
21
22
  `To use AI extraction with model '${model}', install 'ai' with your package manager.`
22
23
  );
23
24
  }
24
- const modelProvider = await getModelProvider(model);
25
+ const modelProvider = await getModelProvider(model, { env });
25
26
  const request = {
26
27
  model: modelProvider,
27
28
  system: buildExtractSystemPrompt(),
@@ -2,12 +2,13 @@ import {
2
2
  buildResolveSystemPrompt,
3
3
  buildResolveUserPrompt,
4
4
  getModelProvider
5
- } from "./chunk-QHZFY3ZK.js";
5
+ } from "./chunk-FAHE5DB2.js";
6
6
 
7
7
  // src/ai/resolver.ts
8
8
  function createResolveCallback(model, options) {
9
9
  const temperature = options?.temperature ?? 1;
10
10
  const maxTokens = options?.maxTokens ?? null;
11
+ const env = options?.env;
11
12
  return async (args) => {
12
13
  let generateObject;
13
14
  let z;
@@ -26,7 +27,7 @@ function createResolveCallback(model, options) {
26
27
  `To use AI resolution with model '${model}', install 'zod' with your package manager.`
27
28
  );
28
29
  }
29
- const modelProvider = await getModelProvider(model);
30
+ const modelProvider = await getModelProvider(model, { env });
30
31
  const schema = z.object({
31
32
  element: z.number().describe(
32
33
  "Counter number of the matching element, or -1 if no match"
@@ -1,17 +1,49 @@
1
1
  // src/ai/model.ts
2
+ var OPENAI_PROVIDER_INFO = {
3
+ pkg: "@ai-sdk/openai",
4
+ providerFn: "openai",
5
+ factoryFn: "createOpenAI",
6
+ apiKeyEnvVar: "OPENAI_API_KEY",
7
+ baseUrlEnvVar: "OPENAI_BASE_URL"
8
+ };
9
+ var ANTHROPIC_PROVIDER_INFO = {
10
+ pkg: "@ai-sdk/anthropic",
11
+ providerFn: "anthropic",
12
+ factoryFn: "createAnthropic",
13
+ apiKeyEnvVar: "ANTHROPIC_API_KEY",
14
+ baseUrlEnvVar: "ANTHROPIC_BASE_URL"
15
+ };
16
+ var GOOGLE_PROVIDER_INFO = {
17
+ pkg: "@ai-sdk/google",
18
+ providerFn: "google",
19
+ factoryFn: "createGoogleGenerativeAI",
20
+ apiKeyEnvVar: "GOOGLE_GENERATIVE_AI_API_KEY"
21
+ };
22
+ var XAI_PROVIDER_INFO = {
23
+ pkg: "@ai-sdk/xai",
24
+ providerFn: "xai",
25
+ factoryFn: "createXai",
26
+ apiKeyEnvVar: "XAI_API_KEY"
27
+ };
28
+ var GROQ_PROVIDER_INFO = {
29
+ pkg: "@ai-sdk/groq",
30
+ providerFn: "groq",
31
+ factoryFn: "createGroq",
32
+ apiKeyEnvVar: "GROQ_API_KEY"
33
+ };
2
34
  var PROVIDER_MAP = {
3
- "openai/": { pkg: "@ai-sdk/openai", providerFn: "openai" },
4
- "anthropic/": { pkg: "@ai-sdk/anthropic", providerFn: "anthropic" },
5
- "google/": { pkg: "@ai-sdk/google", providerFn: "google" },
6
- "xai/": { pkg: "@ai-sdk/xai", providerFn: "xai" },
7
- "gpt-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
8
- "o1-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
9
- "o3-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
10
- "o4-": { pkg: "@ai-sdk/openai", providerFn: "openai" },
11
- "claude-": { pkg: "@ai-sdk/anthropic", providerFn: "anthropic" },
12
- "gemini-": { pkg: "@ai-sdk/google", providerFn: "google" },
13
- "grok-": { pkg: "@ai-sdk/xai", providerFn: "xai" },
14
- "groq/": { pkg: "@ai-sdk/groq", providerFn: "groq" }
35
+ "openai/": OPENAI_PROVIDER_INFO,
36
+ "anthropic/": ANTHROPIC_PROVIDER_INFO,
37
+ "google/": GOOGLE_PROVIDER_INFO,
38
+ "xai/": XAI_PROVIDER_INFO,
39
+ "gpt-": OPENAI_PROVIDER_INFO,
40
+ "o1-": OPENAI_PROVIDER_INFO,
41
+ "o3-": OPENAI_PROVIDER_INFO,
42
+ "o4-": OPENAI_PROVIDER_INFO,
43
+ "claude-": ANTHROPIC_PROVIDER_INFO,
44
+ "gemini-": GOOGLE_PROVIDER_INFO,
45
+ "grok-": XAI_PROVIDER_INFO,
46
+ "groq/": GROQ_PROVIDER_INFO
15
47
  };
16
48
  function resolveProviderInfo(modelStr) {
17
49
  for (const [prefix, info] of Object.entries(PROVIDER_MAP)) {
@@ -28,7 +60,7 @@ function resolveProviderInfo(modelStr) {
28
60
  );
29
61
  }
30
62
  }
31
- return { pkg: "@ai-sdk/openai", providerFn: "openai" };
63
+ return OPENAI_PROVIDER_INFO;
32
64
  }
33
65
  function stripProviderPrefix(modelStr) {
34
66
  const slash = modelStr.indexOf("/");
@@ -39,23 +71,50 @@ function stripProviderPrefix(modelStr) {
39
71
  }
40
72
  return modelStr;
41
73
  }
42
- async function getModelProvider(modelStr) {
43
- const { pkg, providerFn } = resolveProviderInfo(modelStr);
74
+ function normalizeEnvValue(value) {
75
+ if (typeof value !== "string") return void 0;
76
+ const trimmed = value.trim();
77
+ return trimmed.length ? trimmed : void 0;
78
+ }
79
+ function buildFactoryOptions(provider, env) {
80
+ const apiKey = normalizeEnvValue(env[provider.apiKeyEnvVar]);
81
+ if (!apiKey) {
82
+ throw new Error(
83
+ `API key is missing in the resolved Opensteer environment. Set ${provider.apiKeyEnvVar} in your runtime environment or .env file under storage.rootDir.`
84
+ );
85
+ }
86
+ const baseURL = provider.baseUrlEnvVar ? normalizeEnvValue(env[provider.baseUrlEnvVar]) : void 0;
87
+ return {
88
+ apiKey,
89
+ ...baseURL ? { baseURL } : {}
90
+ };
91
+ }
92
+ async function getModelProvider(modelStr, options = {}) {
93
+ const info = resolveProviderInfo(modelStr);
44
94
  let mod;
45
95
  try {
46
- mod = await import(pkg);
96
+ mod = await import(info.pkg);
47
97
  } catch {
48
98
  throw new Error(
49
- `To use AI resolution with model '${modelStr}', install 'ai' and '${pkg}' with your package manager.`
99
+ `To use AI resolution with model '${modelStr}', install 'ai' and '${info.pkg}' with your package manager.`
50
100
  );
51
101
  }
52
- const provider = mod[providerFn];
53
- if (typeof provider !== "function") {
102
+ const providerExportName = options.env ? info.factoryFn : info.providerFn;
103
+ const providerExport = mod[providerExportName];
104
+ if (typeof providerExport !== "function") {
54
105
  throw new Error(
55
- `Provider '${providerFn}' not found in '${pkg}'. Ensure you have the latest version installed.`
106
+ `Provider '${providerExportName}' not found in '${info.pkg}'. Ensure you have the latest version installed.`
56
107
  );
57
108
  }
58
109
  const modelId = stripProviderPrefix(modelStr);
110
+ const provider = options.env != null ? providerExport(
111
+ buildFactoryOptions(info, options.env)
112
+ ) : providerExport;
113
+ if (typeof provider !== "function") {
114
+ throw new Error(
115
+ `Provider '${providerExportName}' from '${info.pkg}' did not return a model factory function.`
116
+ );
117
+ }
59
118
  return provider(modelId);
60
119
  }
61
120
 
@@ -8196,7 +8196,7 @@ function resolveCloudSelection(config, env = process.env) {
8196
8196
  source: "default"
8197
8197
  };
8198
8198
  }
8199
- function resolveConfig(input = {}) {
8199
+ function resolveConfigWithEnv(input = {}) {
8200
8200
  const processEnv = process.env;
8201
8201
  const debugHint = typeof input.debug === "boolean" ? input.debug : parseBool(processEnv.OPENSTEER_DEBUG) === true;
8202
8202
  const initialRootDir = input.storage?.rootDir ?? process.cwd();
@@ -8289,7 +8289,10 @@ function resolveConfig(input = {}) {
8289
8289
  baseUrl: envBaseUrl
8290
8290
  };
8291
8291
  }
8292
- return resolved;
8292
+ return {
8293
+ config: resolved,
8294
+ env
8295
+ };
8293
8296
  }
8294
8297
  function resolveNamespace(config, rootDir) {
8295
8298
  if (config.name && config.name.trim()) {
@@ -9036,7 +9039,9 @@ function minimizePathMatchClauses(path5, mode) {
9036
9039
  (clause) => clause?.kind === "position"
9037
9040
  );
9038
9041
  let keptPositions = [];
9039
- if (!attrClauses.length) {
9042
+ if (mode === "field") {
9043
+ keptPositions = pickMinimalPositionClauses(positionClauses);
9044
+ } else if (!attrClauses.length) {
9040
9045
  keptPositions = pickMinimalPositionClauses(positionClauses);
9041
9046
  } else if (mode === "item-root" && !isLast) {
9042
9047
  keptPositions = [];
@@ -9130,7 +9135,7 @@ function relaxPathForSingleSample(path5, mode) {
9130
9135
  const match = (node.match || []).filter((clause) => {
9131
9136
  if (!clause || typeof clause !== "object") return false;
9132
9137
  if (clause.kind === "position") {
9133
- if (mode === "field") return false;
9138
+ if (mode === "field") return true;
9134
9139
  return !isLast;
9135
9140
  }
9136
9141
  const key = String(clause.key || "").trim().toLowerCase();
@@ -9681,6 +9686,196 @@ function clonePersistedExtractNode(node) {
9681
9686
  return JSON.parse(JSON.stringify(node));
9682
9687
  }
9683
9688
 
9689
+ // src/extraction/array-field-validation.ts
9690
+ async function stripRedundantPositionClauses(payload, page) {
9691
+ const cloned = structuredClone(payload);
9692
+ await processObjectNode(cloned, page);
9693
+ return cloned;
9694
+ }
9695
+ async function processNode(node, page) {
9696
+ if (isPersistedArrayNode(node)) {
9697
+ await processArrayNode(node, page);
9698
+ return;
9699
+ }
9700
+ if (isPersistedObjectNode(node)) {
9701
+ await processObjectNode(node, page);
9702
+ }
9703
+ }
9704
+ async function processObjectNode(node, page) {
9705
+ for (const child of Object.values(node)) {
9706
+ await processNode(child, page);
9707
+ }
9708
+ }
9709
+ async function processArrayNode(node, page) {
9710
+ for (const variant of node.$array.variants) {
9711
+ try {
9712
+ await pruneVariantPositions(variant, page);
9713
+ } catch {
9714
+ }
9715
+ await processNode(variant.item, page);
9716
+ }
9717
+ }
9718
+ function collectValueNodes(node) {
9719
+ if (isPersistedValueNode(node)) {
9720
+ return [
9721
+ {
9722
+ path: node.$path,
9723
+ replacePath(path5) {
9724
+ node.$path = path5;
9725
+ }
9726
+ }
9727
+ ];
9728
+ }
9729
+ if (!isPersistedObjectNode(node)) return [];
9730
+ const refs = [];
9731
+ const visit = (current) => {
9732
+ for (const [key, child] of Object.entries(current)) {
9733
+ if (isPersistedValueNode(child)) {
9734
+ refs.push({
9735
+ path: child.$path,
9736
+ replacePath(path5) {
9737
+ const next = current[key];
9738
+ if (!isPersistedValueNode(next)) return;
9739
+ next.$path = path5;
9740
+ }
9741
+ });
9742
+ continue;
9743
+ }
9744
+ if (isPersistedObjectNode(child)) {
9745
+ visit(child);
9746
+ }
9747
+ }
9748
+ };
9749
+ visit(node);
9750
+ return refs;
9751
+ }
9752
+ function hasPositionClause(path5) {
9753
+ return path5.nodes.some(
9754
+ (node) => (node.match || []).some((clause) => clause.kind === "position")
9755
+ );
9756
+ }
9757
+ function areArraysEqual(left, right) {
9758
+ if (left.length !== right.length) return false;
9759
+ for (let i = 0; i < left.length; i++) {
9760
+ if (left[i] !== right[i]) return false;
9761
+ }
9762
+ return true;
9763
+ }
9764
+ async function pruneVariantPositions(variant, page) {
9765
+ const refs = collectValueNodes(variant.item).filter(
9766
+ (ref) => hasPositionClause(ref.path)
9767
+ );
9768
+ if (!refs.length) return;
9769
+ const plans = refs.map((ref, index) => {
9770
+ const withPos = buildArrayFieldPathCandidates(ref.path);
9771
+ const strippedPath = stripPositionClauses2(ref.path);
9772
+ const withoutPos = buildArrayFieldPathCandidates(strippedPath);
9773
+ if (areArraysEqual(withPos, withoutPos)) return null;
9774
+ const plan = {
9775
+ key: String(index),
9776
+ strippedPath,
9777
+ replacePath: ref.replacePath,
9778
+ selectors: {
9779
+ withPos,
9780
+ withoutPos
9781
+ }
9782
+ };
9783
+ return plan;
9784
+ }).filter((plan) => !!plan);
9785
+ if (!plans.length) return;
9786
+ const selectorMap = {};
9787
+ for (const plan of plans) {
9788
+ selectorMap[plan.key] = plan.selectors;
9789
+ }
9790
+ const validatedKeys = new Set(plans.map((plan) => plan.key));
9791
+ const items = await queryAllByElementPath(page, variant.itemParentPath);
9792
+ if (!items.length) return;
9793
+ try {
9794
+ for (const item of items) {
9795
+ const results = await validateRowSelectors(item, selectorMap);
9796
+ const failedKeys = [];
9797
+ for (const key of validatedKeys) {
9798
+ if (results[key] === true) continue;
9799
+ failedKeys.push(key);
9800
+ }
9801
+ for (const key of failedKeys) {
9802
+ validatedKeys.delete(key);
9803
+ }
9804
+ if (!validatedKeys.size) break;
9805
+ }
9806
+ } finally {
9807
+ await Promise.all(
9808
+ items.map(async (item) => {
9809
+ try {
9810
+ await item.dispose();
9811
+ } catch {
9812
+ }
9813
+ })
9814
+ );
9815
+ }
9816
+ for (const plan of plans) {
9817
+ if (!validatedKeys.has(plan.key)) continue;
9818
+ plan.replacePath(plan.strippedPath);
9819
+ }
9820
+ }
9821
+ async function validateRowSelectors(item, fields) {
9822
+ return item.evaluate((element, selectorMap) => {
9823
+ const tryFirst = (root, selectors) => {
9824
+ let fallback = null;
9825
+ for (const selector of selectors) {
9826
+ if (!selector) continue;
9827
+ let matches = [];
9828
+ try {
9829
+ matches = Array.from(root.querySelectorAll(selector));
9830
+ } catch {
9831
+ matches = [];
9832
+ }
9833
+ if (!matches.length) continue;
9834
+ if (matches.length === 1) {
9835
+ return matches[0];
9836
+ }
9837
+ if (!fallback) {
9838
+ fallback = matches[0];
9839
+ }
9840
+ }
9841
+ return fallback;
9842
+ };
9843
+ const out = {};
9844
+ for (const [key, selectors] of Object.entries(selectorMap)) {
9845
+ const original = tryFirst(element, selectors.withPos);
9846
+ if (!original) {
9847
+ out[key] = false;
9848
+ continue;
9849
+ }
9850
+ let strippedUnique = null;
9851
+ for (const selector of selectors.withoutPos) {
9852
+ if (!selector) continue;
9853
+ let matches = [];
9854
+ try {
9855
+ matches = Array.from(element.querySelectorAll(selector));
9856
+ } catch {
9857
+ matches = [];
9858
+ }
9859
+ if (matches.length === 1) {
9860
+ strippedUnique = matches[0];
9861
+ break;
9862
+ }
9863
+ }
9864
+ out[key] = strippedUnique === original;
9865
+ }
9866
+ return out;
9867
+ }, fields);
9868
+ }
9869
+ function stripPositionClauses2(path5) {
9870
+ return {
9871
+ context: path5.context,
9872
+ nodes: path5.nodes.map((node) => ({
9873
+ ...node,
9874
+ match: (node.match || []).filter((clause) => clause.kind !== "position")
9875
+ }))
9876
+ };
9877
+ }
9878
+
9684
9879
  // src/cloud/runtime.ts
9685
9880
  var DEFAULT_CLOUD_BASE_URL = "https://api.opensteer.com";
9686
9881
  function createCloudRuntimeState(key, baseUrl = resolveCloudBaseUrl(), authScheme = "api-key") {
@@ -9727,6 +9922,7 @@ var CLOUD_INTERACTION_METHODS = /* @__PURE__ */ new Set([
9727
9922
  ]);
9728
9923
  var Opensteer = class _Opensteer {
9729
9924
  config;
9925
+ runtimeEnv;
9730
9926
  aiResolve;
9731
9927
  aiExtract;
9732
9928
  namespace;
@@ -9740,14 +9936,17 @@ var Opensteer = class _Opensteer {
9740
9936
  snapshotCache = null;
9741
9937
  agentExecutionInFlight = false;
9742
9938
  constructor(config = {}) {
9743
- const resolved = resolveConfig(config);
9939
+ const resolvedRuntime = resolveConfigWithEnv(config);
9940
+ const resolved = resolvedRuntime.config;
9941
+ const runtimeEnv = resolvedRuntime.env;
9744
9942
  const cloudSelection = resolveCloudSelection({
9745
9943
  cloud: resolved.cloud
9746
- });
9944
+ }, runtimeEnv);
9747
9945
  const model = resolved.model;
9748
9946
  this.config = resolved;
9749
- this.aiResolve = this.createLazyResolveCallback(model);
9750
- this.aiExtract = this.createLazyExtractCallback(model);
9947
+ this.runtimeEnv = runtimeEnv;
9948
+ this.aiResolve = this.createLazyResolveCallback(model, runtimeEnv);
9949
+ this.aiExtract = this.createLazyExtractCallback(model, runtimeEnv);
9751
9950
  const rootDir = resolved.storage?.rootDir || process.cwd();
9752
9951
  this.namespace = resolveNamespace(resolved, rootDir);
9753
9952
  this.storage = new LocalSelectorStorage(rootDir, this.namespace, {
@@ -9779,13 +9978,13 @@ var Opensteer = class _Opensteer {
9779
9978
  `[opensteer] ${context}: ${normalized.message}${codeSuffix}`
9780
9979
  );
9781
9980
  }
9782
- createLazyResolveCallback(model) {
9981
+ createLazyResolveCallback(model, env) {
9783
9982
  let resolverPromise = null;
9784
9983
  return async (...args) => {
9785
9984
  try {
9786
9985
  if (!resolverPromise) {
9787
- resolverPromise = import("./resolver-ZREUOOTV.js").then(
9788
- (m) => m.createResolveCallback(model)
9986
+ resolverPromise = import("./resolver-MGN64KCP.js").then(
9987
+ (m) => m.createResolveCallback(model, { env })
9789
9988
  );
9790
9989
  }
9791
9990
  const resolver = await resolverPromise;
@@ -9796,13 +9995,13 @@ var Opensteer = class _Opensteer {
9796
9995
  }
9797
9996
  };
9798
9997
  }
9799
- createLazyExtractCallback(model) {
9998
+ createLazyExtractCallback(model, env) {
9800
9999
  let extractorPromise = null;
9801
10000
  const extract = async (args) => {
9802
10001
  try {
9803
10002
  if (!extractorPromise) {
9804
- extractorPromise = import("./extractor-CZFCFUME.js").then(
9805
- (m) => m.createExtractCallback(model)
10003
+ extractorPromise = import("./extractor-4Q3TFZJB.js").then(
10004
+ (m) => m.createExtractCallback(model, { env })
9806
10005
  );
9807
10006
  }
9808
10007
  const extractor = await extractorPromise;
@@ -10079,10 +10278,11 @@ var Opensteer = class _Opensteer {
10079
10278
  this.snapshotCache = null;
10080
10279
  }
10081
10280
  static from(page, config = {}) {
10082
- const resolvedConfig = resolveConfig(config);
10281
+ const resolvedRuntime = resolveConfigWithEnv(config);
10282
+ const resolvedConfig = resolvedRuntime.config;
10083
10283
  const cloudSelection = resolveCloudSelection({
10084
10284
  cloud: resolvedConfig.cloud
10085
- });
10285
+ }, resolvedRuntime.env);
10086
10286
  if (cloudSelection.cloud) {
10087
10287
  throw cloudUnsupportedMethodError(
10088
10288
  "Opensteer.from(page)",
@@ -11057,7 +11257,7 @@ var Opensteer = class _Opensteer {
11057
11257
  const data = await this.extractFields(fields);
11058
11258
  if (storageKey && schemaHash && (!stored || stored.schemaHash !== schemaHash)) {
11059
11259
  const persistedFields = await this.resolveFieldTargetsToPersistableFields(fields);
11060
- this.persistExtractPaths(
11260
+ await this.persistExtractPaths(
11061
11261
  storageKey,
11062
11262
  options.description,
11063
11263
  persistedFields,
@@ -11091,13 +11291,12 @@ var Opensteer = class _Opensteer {
11091
11291
  const resolvedFields = await this.resolveFieldTargetsToPersistableFields(fields);
11092
11292
  let persisted = false;
11093
11293
  if (storageKey) {
11094
- this.persistExtractPaths(
11294
+ persisted = await this.persistExtractPaths(
11095
11295
  storageKey,
11096
11296
  options.description,
11097
11297
  resolvedFields,
11098
11298
  schemaHash
11099
11299
  );
11100
- persisted = true;
11101
11300
  }
11102
11301
  return {
11103
11302
  namespace: this.storage.getNamespace(),
@@ -11129,7 +11328,8 @@ var Opensteer = class _Opensteer {
11129
11328
  agent(config) {
11130
11329
  const resolvedAgentConfig = resolveAgentConfig({
11131
11330
  agentConfig: config,
11132
- fallbackModel: this.config.model
11331
+ fallbackModel: this.config.model,
11332
+ env: this.runtimeEnv
11133
11333
  });
11134
11334
  return {
11135
11335
  execute: async (instructionOrOptions) => {
@@ -11558,7 +11758,7 @@ var Opensteer = class _Opensteer {
11558
11758
  this.storage.saveRegistry(registry);
11559
11759
  return true;
11560
11760
  }
11561
- persistExtractPaths(id, description, fields, schemaHash) {
11761
+ async persistExtractPaths(id, description, fields, schemaHash) {
11562
11762
  const now = Date.now();
11563
11763
  const safeFile = this.storage.getSelectorFileName(id);
11564
11764
  const existing = this.storage.readSelector(id);
@@ -11579,11 +11779,19 @@ var Opensteer = class _Opensteer {
11579
11779
  }
11580
11780
  );
11581
11781
  const persistedPayload = buildPersistedExtractPayload(normalizedFields);
11782
+ let validatedPayload = persistedPayload;
11783
+ try {
11784
+ validatedPayload = await stripRedundantPositionClauses(
11785
+ persistedPayload,
11786
+ this.page
11787
+ );
11788
+ } catch {
11789
+ }
11582
11790
  const payload = {
11583
11791
  id,
11584
11792
  method: "extract",
11585
11793
  description: description || "Extraction paths",
11586
- path: persistedPayload,
11794
+ path: validatedPayload,
11587
11795
  schemaHash,
11588
11796
  metadata: {
11589
11797
  createdAt,
@@ -12282,6 +12490,8 @@ export {
12282
12490
  extractArrayWithPaths,
12283
12491
  extractArrayRowsWithPaths,
12284
12492
  countArrayItemsWithPath,
12493
+ queryAllByElementPath,
12494
+ buildArrayFieldPathCandidates,
12285
12495
  listTabs,
12286
12496
  createTab,
12287
12497
  switchTab,