@rulvar/bridge-ai-sdk 1.187.0 → 1.188.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.
package/dist/index.d.ts CHANGED
@@ -17,6 +17,22 @@ interface BridgeAiSdkOptions {
17
17
  provider?: string;
18
18
  /** Per-model capability overrides merged over the conservative defaults. */
19
19
  caps?: (model: string) => ModelCaps | Partial<ModelCaps>;
20
+ /**
21
+ * Provider-executed tool policy (RV1806). The wrapped provider can
22
+ * run tools SERVER-SIDE (web search, code execution, computer use):
23
+ * those calls never pass the engine's ToolDef registry, risk
24
+ * classes, ask rules, or approvals, and their effects happen on
25
+ * provider infrastructure regardless of any engine permission chain.
26
+ * The default 'deny' fails the turn with a typed terminal error the
27
+ * moment a provider-executed exchange appears, because a policy
28
+ * surface that cannot see a call must not silently absorb it.
29
+ * 'allow' opts in: the exchange is retained for prompt
30
+ * reconstruction exactly as before, and the finish metadata
31
+ * additionally names every provider-executed call
32
+ * (`providerExecutedTools: [{ toolName, toolCallId }]`) so the
33
+ * journaled record of the turn says what the provider ran.
34
+ */
35
+ providerExecutedTools?: "allow" | "deny";
20
36
  }
21
37
  /**
22
38
  * Wraps a Vercel AI SDK LanguageModelV4 as a ProviderAdapter. The bridge
package/dist/index.js CHANGED
@@ -106,6 +106,8 @@ function bridgeAiSdk(model, options = {}) {
106
106
  const id = options.id ?? model.provider;
107
107
  if (id === void 0 || id === "") throw new ConfigError("bridgeAiSdk requires a non-empty adapter id (model.provider was empty)");
108
108
  const family = options.provider ?? model.provider;
109
+ const providerExecutedTools = options.providerExecutedTools ?? "deny";
110
+ if (providerExecutedTools !== "allow" && providerExecutedTools !== "deny") throw new ConfigError("bridgeAiSdk options.providerExecutedTools must be 'allow' or 'deny' when given");
109
111
  const ids = new BridgeIdMap(createCanonicalIdMinter());
110
112
  return {
111
113
  id,
@@ -136,7 +138,7 @@ function bridgeAiSdk(model, options = {}) {
136
138
  };
137
139
  return;
138
140
  }
139
- const mapper = new StreamMapper(id, ids, built.effortDownmapped);
141
+ const mapper = new StreamMapper(id, ids, built.effortDownmapped, providerExecutedTools);
140
142
  try {
141
143
  for await (const part of iterateStream(result.stream)) {
142
144
  for (const event of mapper.map(part)) yield event;
@@ -394,16 +396,42 @@ var StreamMapper = class {
394
396
  adapterId;
395
397
  ids;
396
398
  effortDownmapped;
399
+ providerExecutedPolicy;
397
400
  startedToolWireIds = /* @__PURE__ */ new Set();
398
401
  providerExecutedWireIds = /* @__PURE__ */ new Set();
402
+ /** Provider-executed calls seen under 'allow' (RV1806), by wire id. */
403
+ providerExecutedSeen = /* @__PURE__ */ new Map();
399
404
  reasoning = /* @__PURE__ */ new Map();
400
405
  retained = [];
401
406
  warnings = [];
402
407
  response;
403
- constructor(adapterId, ids, effortDownmapped) {
408
+ constructor(adapterId, ids, effortDownmapped, providerExecutedPolicy = "deny") {
404
409
  this.adapterId = adapterId;
405
410
  this.ids = ids;
406
411
  this.effortDownmapped = effortDownmapped;
412
+ this.providerExecutedPolicy = providerExecutedPolicy;
413
+ }
414
+ /**
415
+ * The deny arm (RV1806): a provider-executed exchange under the
416
+ * default policy fails the turn typed. The refusal cannot un-run a
417
+ * tool the provider already executed server-side; it refuses to
418
+ * CONTINUE a turn the engine's permission chain cannot see, and the
419
+ * journaled terminal names the tool that ran.
420
+ */
421
+ denyProviderExecuted(toolName) {
422
+ this.terminal = true;
423
+ return [{
424
+ type: "error",
425
+ error: {
426
+ code: "agent",
427
+ message: `the bridged provider executed tool '${toolName}' server-side; provider-executed tools bypass the engine's ToolDef registry, risk classes, and approvals, and are refused by default. Opt in with bridgeAiSdk(model, { providerExecutedTools: 'allow' })`,
428
+ retryable: false,
429
+ data: {
430
+ kind: "terminal",
431
+ providerExecutedTool: toolName
432
+ }
433
+ }
434
+ }];
407
435
  }
408
436
  map(part) {
409
437
  switch (part.type) {
@@ -455,7 +483,9 @@ var StreamMapper = class {
455
483
  }
456
484
  case "tool-input-start":
457
485
  if (part.providerExecuted === true) {
486
+ if (this.providerExecutedPolicy === "deny") return this.denyProviderExecuted(part.toolName);
458
487
  this.providerExecutedWireIds.add(part.id);
488
+ this.providerExecutedSeen.set(part.id, part.toolName);
459
489
  return [];
460
490
  }
461
491
  this.startedToolWireIds.add(part.id);
@@ -474,7 +504,9 @@ var StreamMapper = class {
474
504
  case "tool-input-end": return [];
475
505
  case "tool-call": {
476
506
  if (part.providerExecuted === true) {
507
+ if (this.providerExecutedPolicy === "deny") return this.denyProviderExecuted(part.toolName);
477
508
  this.providerExecutedWireIds.add(part.toolCallId);
509
+ this.providerExecutedSeen.set(part.toolCallId, part.toolName);
478
510
  const parsed = parseToolArgs(part.input);
479
511
  this.retained.push({
480
512
  type: "tool-call",
@@ -587,6 +619,10 @@ var StreamMapper = class {
587
619
  if (typeof this.response.id === "string") bag.responseId = this.response.id;
588
620
  }
589
621
  if (part.providerMetadata !== void 0) bag.upstream = part.providerMetadata;
622
+ if (this.providerExecutedSeen.size > 0) bag.providerExecutedTools = [...this.providerExecutedSeen.entries()].map(([toolCallId, toolName]) => ({
623
+ toolName,
624
+ toolCallId
625
+ }));
590
626
  if (this.effortDownmapped) bag.effortDownmap = "max->xhigh";
591
627
  if (part.finishReason.unified === "other" && part.finishReason.raw !== void 0) bag.finishRaw = part.finishReason.raw;
592
628
  return [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/bridge-ai-sdk",
3
- "version": "1.187.0",
3
+ "version": "1.188.0",
4
4
  "description": "Rulvar bridge adapter wrapping any Vercel AI SDK LanguageModelV4 as a ProviderAdapter.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -23,14 +23,14 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@ai-sdk/provider": "^4.0.3",
26
- "@rulvar/core": "1.187.0"
26
+ "@rulvar/core": "1.188.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@ai-sdk/google": "^4.0.24",
30
30
  "@types/node": "^22.20.1",
31
31
  "tsdown": "^0.22.14",
32
32
  "typescript": "~6.0.3",
33
- "@rulvar/testing": "1.187.0"
33
+ "@rulvar/testing": "1.188.0"
34
34
  },
35
35
  "repository": {
36
36
  "type": "git",