@opengeni/runtime 3.3.0 → 3.4.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 (64) hide show
  1. package/dist/agent-LJT5NPWT.js +9 -0
  2. package/dist/assets/codemode-client.json +1 -1
  3. package/dist/browser-current-tab.d.ts +46 -0
  4. package/dist/browser-model-text.d.ts +126 -0
  5. package/dist/{chunk-XRX57ZQO.js → chunk-3DFH3ZHU.js} +784 -121
  6. package/dist/chunk-3DFH3ZHU.js.map +1 -0
  7. package/dist/{chunk-HJSIPTFA.js → chunk-4XDJ4TJA.js} +2 -2
  8. package/dist/{chunk-XQ23VCZ4.js → chunk-6FQ6KEII.js} +3 -3
  9. package/dist/{chunk-7LJ7KDPI.js → chunk-ERD4ICVB.js} +4 -3
  10. package/dist/{chunk-EK7IW2FR.js → chunk-ETTCGPBT.js} +224 -6
  11. package/dist/chunk-ETTCGPBT.js.map +1 -0
  12. package/dist/{chunk-7OUG7WBV.js → chunk-GWRBALGQ.js} +181 -13
  13. package/dist/chunk-GWRBALGQ.js.map +1 -0
  14. package/dist/{chunk-YG26XE7B.js → chunk-JYDG2A7H.js} +2 -2
  15. package/dist/{chunk-RSHQSLEJ.js → chunk-KIL4FGX5.js} +3 -3
  16. package/dist/{chunk-NWS4HPB7.js → chunk-MIAL5I4O.js} +2 -2
  17. package/dist/{chunk-Z5U6WE22.js → chunk-PRZPGRIK.js} +2 -2
  18. package/dist/{chunk-KCPW4K7L.js → chunk-PZQUUBEB.js} +2 -2
  19. package/dist/{chunk-5WJOPANS.js → chunk-Y5THZH5N.js} +7 -7
  20. package/dist/codex-tool-search.d.ts +9 -0
  21. package/dist/gmail-rest-mcp.js +8 -8
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.js +14 -12
  24. package/dist/interaction-tool-json-schema.d.ts +27 -0
  25. package/dist/{lazy-manifest-AJ6XPJ4F.js → lazy-manifest-BLZ3UZLX.js} +4 -4
  26. package/dist/lazy-tool-transport.d.ts +10 -0
  27. package/dist/{localSources-SSIEH5F5.js → localSources-I6SIQ6CX.js} +6 -6
  28. package/dist/model-call.js +3 -3
  29. package/dist/sandbox/browser-control-client.d.ts +77 -0
  30. package/dist/sandbox/index.d.ts +1 -1
  31. package/dist/sandbox/index.js +7 -7
  32. package/dist/sandbox/selfhosted/session.d.ts +3 -0
  33. package/dist/slack-rest-mcp.js +8 -8
  34. package/dist/workspace-tool-gateway.js +12 -12
  35. package/package.json +14 -14
  36. package/src/browser-current-tab.ts +139 -0
  37. package/src/browser-model-text.ts +378 -0
  38. package/src/codex-tool-search.ts +53 -0
  39. package/src/index.ts +39 -1
  40. package/src/interaction-tool-json-schema.ts +281 -0
  41. package/src/interaction-tools.ts +425 -235
  42. package/src/lazy-tool-transport.ts +229 -10
  43. package/src/model-input.ts +12 -1
  44. package/src/sandbox/browser-control-client.ts +336 -0
  45. package/src/sandbox/index.ts +4 -0
  46. package/src/sandbox/selfhosted/session.ts +38 -0
  47. package/dist/agent-CTPLA3HP.js +0 -9
  48. package/dist/browser-action-json-schema.d.ts +0 -4
  49. package/dist/chunk-7OUG7WBV.js.map +0 -1
  50. package/dist/chunk-EK7IW2FR.js.map +0 -1
  51. package/dist/chunk-XRX57ZQO.js.map +0 -1
  52. package/src/browser-action-json-schema.ts +0 -10
  53. /package/dist/{agent-CTPLA3HP.js.map → agent-LJT5NPWT.js.map} +0 -0
  54. /package/dist/{chunk-HJSIPTFA.js.map → chunk-4XDJ4TJA.js.map} +0 -0
  55. /package/dist/{chunk-XQ23VCZ4.js.map → chunk-6FQ6KEII.js.map} +0 -0
  56. /package/dist/{chunk-7LJ7KDPI.js.map → chunk-ERD4ICVB.js.map} +0 -0
  57. /package/dist/{chunk-YG26XE7B.js.map → chunk-JYDG2A7H.js.map} +0 -0
  58. /package/dist/{chunk-RSHQSLEJ.js.map → chunk-KIL4FGX5.js.map} +0 -0
  59. /package/dist/{chunk-NWS4HPB7.js.map → chunk-MIAL5I4O.js.map} +0 -0
  60. /package/dist/{chunk-Z5U6WE22.js.map → chunk-PRZPGRIK.js.map} +0 -0
  61. /package/dist/{chunk-KCPW4K7L.js.map → chunk-PZQUUBEB.js.map} +0 -0
  62. /package/dist/{chunk-5WJOPANS.js.map → chunk-Y5THZH5N.js.map} +0 -0
  63. /package/dist/{lazy-manifest-AJ6XPJ4F.js.map → lazy-manifest-BLZ3UZLX.js.map} +0 -0
  64. /package/dist/{localSources-SSIEH5F5.js.map → localSources-I6SIQ6CX.js.map} +0 -0
@@ -0,0 +1,281 @@
1
+ import type { AttemptToolJsonSchema } from "@opengeni/contracts";
2
+ import { z } from "zod";
3
+
4
+ type JsonSchemaNode = Record<string, unknown>;
5
+
6
+ /** Keywords that only constrain validation. The tool's Zod schema still
7
+ * enforces them before execution, and rejects with the exact field. */
8
+ const VALIDATION_ONLY_KEYWORDS = new Set(["$schema", "$id", "minLength", "maxLength"]);
9
+ /** Larger numeric bounds are overflow guards, not guidance. */
10
+ const MEANINGFUL_NUMERIC_BOUND = 100_000;
11
+ const MEANINGFUL_MAX_ITEMS = 64;
12
+ /** Longer patterns (UUIDs, control-character guards) are validation noise. */
13
+ const MEANINGFUL_PATTERN_LENGTH = 32;
14
+
15
+ /**
16
+ * The JSON Schema an interaction tool shows the model.
17
+ *
18
+ * Model providers expand every `$ref` when they render a tool, so a tagged
19
+ * union such as a browser action, whose locator union repeats in each of its
20
+ * twenty variants, costs several thousand tokens. Each tagged union of objects
21
+ * is shown as one object instead: the tag lists every variant, each field
22
+ * appears once, and one compact line names the fields each variant takes.
23
+ * Validation-only limits are left out. The tool's own Zod schema stays the
24
+ * authority and rejects any invalid combination with the exact field.
25
+ */
26
+ export function interactionToolInputJsonSchema(schema: z.ZodType): AttemptToolJsonSchema {
27
+ const generated = z.toJSONSchema(schema, { target: "draft-2020-12" }) as JsonSchemaNode;
28
+ return modelFacingSchema(generated) as AttemptToolJsonSchema;
29
+ }
30
+
31
+ /** Per-variant field lists of a flattened union, for the enclosing summary. */
32
+ const flattenedVariants = new WeakMap<JsonSchemaNode, Array<[string, string[]]>>();
33
+
34
+ const LOCATOR_FIELDS = new Set(["locator", "from", "to"]);
35
+
36
+ /**
37
+ * Let the model pass an element ref as a bare string wherever a locator is
38
+ * expected (`"locator":"e1a2b3c4d"`). It is the common case and a third of
39
+ * the output tokens of `{kind:'ref',ref}`. The tool expands it before
40
+ * validation.
41
+ */
42
+ export function withRefShorthandLocators(schema: AttemptToolJsonSchema): AttemptToolJsonSchema {
43
+ return refShorthand(schema) as AttemptToolJsonSchema;
44
+ }
45
+
46
+ function refShorthand(node: unknown): unknown {
47
+ if (Array.isArray(node)) return node.map(refShorthand);
48
+ if (!isNode(node)) return node;
49
+ const out: JsonSchemaNode = {};
50
+ for (const [key, value] of Object.entries(node)) {
51
+ if (key !== "properties" || !isNode(value)) {
52
+ out[key] = refShorthand(value);
53
+ continue;
54
+ }
55
+ const properties: JsonSchemaNode = {};
56
+ for (const [field, schema] of Object.entries(value)) {
57
+ properties[field] =
58
+ LOCATOR_FIELDS.has(field) && isRefLocator(schema)
59
+ ? { anyOf: [{ type: "string", description: "An element ref." }, refShorthand(schema)] }
60
+ : refShorthand(schema);
61
+ }
62
+ out[key] = properties;
63
+ }
64
+ return out;
65
+ }
66
+
67
+ function isRefLocator(schema: unknown): boolean {
68
+ if (!isNode(schema)) return false;
69
+ if (Array.isArray(schema.anyOf)) return schema.anyOf.some(isRefLocator);
70
+ return isNode(schema.properties) && (tagValues(schema.properties.kind) ?? []).includes("ref");
71
+ }
72
+
73
+ function modelFacingSchema(node: unknown): unknown {
74
+ if (Array.isArray(node)) return node.map(modelFacingSchema);
75
+ if (!isNode(node)) return node;
76
+ const out: JsonSchemaNode = {};
77
+ for (const [key, value] of Object.entries(node)) {
78
+ if (VALIDATION_ONLY_KEYWORDS.has(key)) continue;
79
+ if (key === "additionalProperties" && value === false) continue;
80
+ if (
81
+ (key === "minimum" || key === "maximum") &&
82
+ typeof value === "number" &&
83
+ Math.abs(value) >= MEANINGFUL_NUMERIC_BOUND
84
+ ) {
85
+ continue;
86
+ }
87
+ if (key === "minItems" && typeof value === "number" && value <= 1) continue;
88
+ if (key === "maxItems" && typeof value === "number" && value > MEANINGFUL_MAX_ITEMS) continue;
89
+ if (
90
+ key === "pattern" &&
91
+ typeof value === "string" &&
92
+ value.length > MEANINGFUL_PATTERN_LENGTH
93
+ ) {
94
+ continue;
95
+ }
96
+ out[key] = modelFacingSchema(value);
97
+ }
98
+ for (const unionKey of ["oneOf", "anyOf"] as const) {
99
+ const members = out[unionKey];
100
+ if (!Array.isArray(members)) continue;
101
+ const flattened = flattenTaggedUnion(members);
102
+ if (!flattened) continue;
103
+ delete out[unionKey];
104
+ const { description, ...rest } = flattened;
105
+ Object.assign(out, rest);
106
+ const own = typeof out.description === "string" ? out.description : "";
107
+ out.description = own ? `${own} ${String(description)}` : description;
108
+ flattenedVariants.set(out, flattenedVariants.get(flattened) ?? []);
109
+ }
110
+ return out;
111
+ }
112
+
113
+ /** One object for a union whose members are objects sharing a string tag.
114
+ * Null when the union is not such a tagged union. */
115
+ function flattenTaggedUnion(members: unknown[]): JsonSchemaNode | null {
116
+ if (members.length < 2 || !members.every(isObjectSchema)) return null;
117
+ const objects = members as Array<JsonSchemaNode & { properties: Record<string, unknown> }>;
118
+ const tag = ["type", "kind", "operation"].find((candidate) =>
119
+ objects.every((member) => tagValues(member.properties[candidate]) !== null),
120
+ );
121
+ if (!tag) return null;
122
+
123
+ const variants: Array<[string, string[]]> = [];
124
+ const tagValuesInOrder: string[] = [];
125
+ const properties = new Map<string, unknown[]>();
126
+ for (const member of objects) {
127
+ const nested = flattenedVariants.get(member);
128
+ const required = new Set(Array.isArray(member.required) ? (member.required as string[]) : []);
129
+ if (nested) {
130
+ variants.push(...nested);
131
+ } else {
132
+ const fields = Object.keys(member.properties)
133
+ .filter((field) => field !== tag)
134
+ .map((field) => (required.has(field) ? field : `${field}?`));
135
+ for (const value of tagValues(member.properties[tag])!) variants.push([value, fields]);
136
+ }
137
+ for (const value of tagValues(member.properties[tag])!) {
138
+ if (!tagValuesInOrder.includes(value)) tagValuesInOrder.push(value);
139
+ }
140
+ for (const [field, schema] of Object.entries(member.properties)) {
141
+ if (field === tag) continue;
142
+ const seen = properties.get(field) ?? [];
143
+ if (!seen.some((existing) => sameSchema(existing, schema))) seen.push(schema);
144
+ properties.set(field, seen);
145
+ }
146
+ }
147
+
148
+ const merged: Record<string, unknown> = {
149
+ [tag]: { type: "string", enum: tagValuesInOrder },
150
+ };
151
+ for (const [field, schemas] of properties) {
152
+ merged[field] = schemas.length === 1 ? schemas[0] : mergeAlternatives(schemas);
153
+ }
154
+ const summary = mergeSameTagVariants(variants)
155
+ .map(([value, fields]) => (fields.length > 0 ? `${value}: ${fields.join(", ")}` : value))
156
+ .join("; ");
157
+ const flattened: JsonSchemaNode = {
158
+ type: "object",
159
+ properties: merged,
160
+ required: [tag],
161
+ description: `Fields by ${tag} (? = optional): ${summary}.`,
162
+ };
163
+ flattenedVariants.set(flattened, variants);
164
+ return flattened;
165
+ }
166
+
167
+ /**
168
+ * One summary entry per tag value. Several members can share a value (for
169
+ * example one variant with an optional id and one without); a field is then
170
+ * required only when every such member requires it.
171
+ */
172
+ function mergeSameTagVariants(variants: Array<[string, string[]]>): Array<[string, string[]]> {
173
+ const byValue = new Map<string, Array<string[]>>();
174
+ for (const [value, fields] of variants) {
175
+ const seen = byValue.get(value) ?? [];
176
+ seen.push(fields);
177
+ byValue.set(value, seen);
178
+ }
179
+ return [...byValue].map(([value, members]) => {
180
+ if (members.length === 1) return [value, members[0]!];
181
+ const names: string[] = [];
182
+ for (const fields of members) {
183
+ for (const field of fields) {
184
+ const name = field.endsWith("?") ? field.slice(0, -1) : field;
185
+ if (!names.includes(name)) names.push(name);
186
+ }
187
+ }
188
+ return [
189
+ value,
190
+ names.map((name) => (members.every((fields) => fields.includes(name)) ? name : `${name}?`)),
191
+ ];
192
+ });
193
+ }
194
+
195
+ function mergeAlternatives(schemas: unknown[]): unknown {
196
+ if (schemas.every((schema) => tagValues(schema) !== null)) {
197
+ return { type: "string", enum: [...new Set(schemas.flatMap((schema) => tagValues(schema)!))] };
198
+ }
199
+ return { anyOf: schemas };
200
+ }
201
+
202
+ function tagValues(schema: unknown): string[] | null {
203
+ if (!isNode(schema)) return null;
204
+ if (typeof schema.const === "string") return [schema.const];
205
+ if (
206
+ Array.isArray(schema.enum) &&
207
+ schema.enum.length > 0 &&
208
+ schema.enum.every((value) => typeof value === "string")
209
+ ) {
210
+ return schema.enum as string[];
211
+ }
212
+ return null;
213
+ }
214
+
215
+ function isObjectSchema(schema: unknown): boolean {
216
+ return isNode(schema) && schema.type === "object" && isNode(schema.properties);
217
+ }
218
+
219
+ function sameSchema(left: unknown, right: unknown): boolean {
220
+ return JSON.stringify(left) === JSON.stringify(right);
221
+ }
222
+
223
+ function isNode(value: unknown): value is JsonSchemaNode {
224
+ return typeof value === "object" && value !== null && !Array.isArray(value);
225
+ }
226
+
227
+ const MAX_REPORTED_ISSUES = 4;
228
+ const MAX_ISSUE_MESSAGE_CHARS = 240;
229
+
230
+ /**
231
+ * Name exactly what is wrong with invalid tool arguments. A union failure
232
+ * reports the variant the arguments chose (the one whose tag matched), so a
233
+ * click without a locator role says `action.locator.role`, not only "invalid".
234
+ */
235
+ export function describeInvalidToolArguments(error: z.ZodError): string {
236
+ const issues = leafIssues(error.issues, []).slice(0, MAX_REPORTED_ISSUES);
237
+ const described = issues.map(({ path, message }) => {
238
+ const text = message.replace(/^Invalid input: /u, "").slice(0, MAX_ISSUE_MESSAGE_CHARS);
239
+ return path.length > 0 ? `${path.join(".")}: ${text}` : text;
240
+ });
241
+ return `Interaction tool arguments are invalid: ${described.join("; ")}.`;
242
+ }
243
+
244
+ type LeafIssue = { path: PropertyKey[]; message: string };
245
+
246
+ function leafIssues(issues: readonly z.core.$ZodIssue[], base: PropertyKey[]): LeafIssue[] {
247
+ const leaves: LeafIssue[] = [];
248
+ for (const issue of issues) {
249
+ const path = [...base, ...issue.path];
250
+ if (issue.code === "invalid_union" && issue.errors.length > 0) {
251
+ leaves.push(...leafIssues(chosenUnionBranch(issue.errors), path));
252
+ continue;
253
+ }
254
+ if (issue.code === "unrecognized_keys") {
255
+ leaves.push({ path, message: `unexpected field ${issue.keys.join(", ")}` });
256
+ continue;
257
+ }
258
+ leaves.push({ path, message: issue.message });
259
+ }
260
+ return leaves;
261
+ }
262
+
263
+ /** The union branch the arguments meant: one whose tag matched, else the
264
+ * branch that reports the unknown tag, else the one with the fewest issues. */
265
+ function chosenUnionBranch(branches: z.core.$ZodIssue[][]): z.core.$ZodIssue[] {
266
+ const tagMismatch = (issue: z.core.$ZodIssue): boolean =>
267
+ (issue.code === "invalid_union" && issue.errors.length === 0) ||
268
+ (issue.code === "invalid_value" &&
269
+ ["type", "kind", "operation"].includes(String(issue.path.at(-1))));
270
+ const matching = branches.filter((branch) => !branch.some(tagMismatch));
271
+ if (matching.length > 0) {
272
+ return matching.reduce((best, branch) => (branch.length < best.length ? branch : best));
273
+ }
274
+ const unknownTag = branches.find((branch) =>
275
+ branch.some((issue) => issue.code === "invalid_union" && issue.errors.length === 0),
276
+ );
277
+ return (
278
+ unknownTag?.filter((issue) => issue.code === "invalid_union") ??
279
+ branches.reduce((best, branch) => (branch.length < best.length ? branch : best))
280
+ );
281
+ }