@opengeni/worker-bundle 1.3.0 → 1.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 (33) hide show
  1. package/dist/activities/agent-turn/errors.d.ts +1 -13
  2. package/dist/activities/agent-turn/subscription-lease.d.ts +1 -0
  3. package/dist/activities/agent-turn/tool-policy.d.ts +10 -1
  4. package/dist/activities/agent-turn/web-search.d.ts +33 -0
  5. package/dist/activities/programmatic-approvals.d.ts +17 -0
  6. package/dist/{activities-control-PEXBWKQJ.js → activities-control-D5KVEPHI.js} +3 -3
  7. package/dist/{activities-turn-W27A2SBO.js → activities-turn-4Z3M7Z3F.js} +288 -23
  8. package/dist/activities-turn-4Z3M7Z3F.js.map +1 -0
  9. package/dist/{chunk-QUX5LT2B.js → chunk-BMTWULF7.js} +113 -30
  10. package/dist/chunk-BMTWULF7.js.map +1 -0
  11. package/dist/{chunk-XONQEVAD.js → chunk-FF4HPY7L.js} +1 -1
  12. package/dist/chunk-FF4HPY7L.js.map +1 -0
  13. package/dist/index.js +2 -2
  14. package/dist/observability-metrics.d.ts +1 -1
  15. package/package.json +21 -21
  16. package/src/activities/agent-turn/agent-build.ts +4 -2
  17. package/src/activities/agent-turn/errors.ts +82 -3
  18. package/src/activities/agent-turn/governance-model.ts +8 -8
  19. package/src/activities/agent-turn/sandbox-runtime.ts +16 -0
  20. package/src/activities/agent-turn/stream-attempt.ts +40 -6
  21. package/src/activities/agent-turn/subscription-lease.ts +10 -1
  22. package/src/activities/agent-turn/tool-environment.ts +19 -0
  23. package/src/activities/agent-turn/tool-policy.ts +25 -0
  24. package/src/activities/agent-turn/web-search.ts +252 -0
  25. package/src/activities/connector-attachments.ts +3 -1
  26. package/src/activities/programmatic-approvals.ts +26 -0
  27. package/src/activity-services.ts +8 -0
  28. package/src/observability-metrics.ts +1 -0
  29. package/src/sandbox-routing.ts +58 -24
  30. package/dist/activities-turn-W27A2SBO.js.map +0 -1
  31. package/dist/chunk-QUX5LT2B.js.map +0 -1
  32. package/dist/chunk-XONQEVAD.js.map +0 -1
  33. /package/dist/{activities-control-PEXBWKQJ.js.map → activities-control-D5KVEPHI.js.map} +0 -0
@@ -131,19 +131,7 @@ export declare function modelPreparationFailureEventPayload(error: unknown, dura
131
131
  failureCode?: "capture_in_progress" | "provider_recovery_in_progress" | "rotation_in_progress";
132
132
  retryable?: boolean;
133
133
  };
134
- /**
135
- * Recognize active-route transitions that cannot finish inside their
136
- * originating attempt. A Modal-home session may start on a Connected Machine
137
- * without creating or leasing its managed home box. When an explicit attach
138
- * clears the active pointer back to home, the pointer commit is authoritative,
139
- * but this attempt has no home session to serve the next sandbox operation.
140
- *
141
- * The Agents SDK may retain the typed routing error directly, through `cause`,
142
- * or inside an AggregateError from a parallel function-tool batch. Traverse
143
- * only those structural error links with a strict bound; never classify from
144
- * message text, which could originate in model or tool content.
145
- */
146
- export declare function sandboxRouteTransitionCode(error: unknown): "home_unavailable_this_turn" | "workspace_root_changed_this_turn" | "native_capabilities_changed_this_attempt" | null;
134
+ export declare function sandboxRouteTransitionCode(error: unknown): "home_unavailable_this_turn" | "workspace_root_changed_this_turn" | "native_capabilities_changed_this_attempt" | "home_backend_recovery_pending" | null;
147
135
  export declare function isSandboxRouteTransitionError(error: unknown): boolean;
148
136
  /** Backward-compatible name for callers/tests that only exercised the original
149
137
  * machine-to-home transition. */
@@ -27,6 +27,7 @@ export declare class SubscriptionTurnLease {
27
27
  private heartbeatInFlight;
28
28
  private heartbeatTimer;
29
29
  private readonly now;
30
+ private readonly renewalIntervalMs;
30
31
  constructor(leaseDeps: SubscriptionLeaseDeps);
31
32
  private expired;
32
33
  markLost: (reason: LeaseLossReason) => void;
@@ -1,4 +1,5 @@
1
- import { type ComputerToolMode, type LazyToolTransport } from "@opengeni/runtime";
1
+ import { type ComputerToolMode, type LazyToolTransport, type OpenGeniRuntime } from "@opengeni/runtime";
2
+ import type { TurnExecutionPolicyV1 } from "@opengeni/contracts";
2
3
  import { type ModelProviderApi, type ResolvedModelProvider, type Settings } from "@opengeni/config";
3
4
  import { type ModelAttachmentInputPolicy } from "../run-input.js";
4
5
  export declare function shouldStartOnTurnRecording(params: {
@@ -112,6 +113,14 @@ export declare function shouldDeferNonEagerToolPreparation(args: {
112
113
  triggerKind: "next" | "approval";
113
114
  triggerType: string;
114
115
  }): boolean;
116
+ /**
117
+ * Resolve the provider routing/gating shape for an accepted turn. The current
118
+ * definition is projected back onto the verified frozen policy: a turn
119
+ * accepted before hosted web search was enabled keeps its frozen tool set on
120
+ * every recovery attempt (stable tool prefix, exact accepted definition); the
121
+ * next accepted logical turn resolves the newly enabled tool.
122
+ */
123
+ export declare function resolveAcceptedTurnModel(runtime: Pick<OpenGeniRuntime, "resolveTurnModel">, settings: Settings, policy: TurnExecutionPolicyV1): ReturnType<OpenGeniRuntime["resolveTurnModel"]>;
115
124
  /**
116
125
  * Native web search is a runtime capability, not part of the session's MCP
117
126
  * allow-list. Attach it whenever the resolved provider advertises runnable
@@ -0,0 +1,33 @@
1
+ import type { AttemptToolDefinition } from "@opengeni/codemode";
2
+ import { type WebSearchProviderToolName, type WebSearchSettings } from "@opengeni/config";
3
+ import { type WebSearchBilling, type WebSearchCallScope } from "@opengeni/core";
4
+ import type { Observability } from "@opengeni/observability";
5
+ /**
6
+ * Which web search one turn gets: the provider's hosted tool, Opengeni's
7
+ * provider tools, or neither. Tool preparation and agent construction both
8
+ * call this with the same accepted model and settings, so they agree.
9
+ */
10
+ export declare function turnWebSearchPlan(resolvedModel: {
11
+ configured: {
12
+ hostedWebSearch: boolean;
13
+ };
14
+ provider: {
15
+ kind?: string;
16
+ };
17
+ } | null, settings: WebSearchSettings): {
18
+ hostedWebSearch: boolean;
19
+ providerTools: WebSearchProviderToolName[];
20
+ };
21
+ /**
22
+ * The `web_search` and `web_fetch` attempt tools for one turn. The caller
23
+ * decides which names the turn gets ({@link webSearchToolPlan}); this only
24
+ * builds them. Provider keys stay in the worker and never reach a sandbox.
25
+ */
26
+ export declare function webSearchToolDefinitions(input: {
27
+ settings: WebSearchSettings;
28
+ tools: readonly WebSearchProviderToolName[];
29
+ scope: WebSearchCallScope;
30
+ billing: WebSearchBilling;
31
+ observability: Pick<Observability, "warn">;
32
+ fetch?: typeof fetch;
33
+ }): AttemptToolDefinition[];
@@ -8,3 +8,20 @@ export declare function programmaticApproval(operation: CodemodeOperation): {
8
8
  requestId: string | undefined;
9
9
  };
10
10
  export declare function programmaticContinuationNote(operations: readonly CodemodeOperation[]): string | undefined;
11
+ /**
12
+ * Recover committed programmatic approval waits/results before the first model
13
+ * request without turning lazy MCP preparation into a first-request barrier.
14
+ *
15
+ * Only eager servers may gate the first request. Resuming a stored operation
16
+ * needs the prepared attempt tool environment, so this waits for full tool
17
+ * preparation only when there is something to resume: an approval decision
18
+ * trigger, or a cheap durable read showing unfinished operations for this
19
+ * logical turn (which covers every recovery that has stored work). Every other
20
+ * turn sends its first request without waiting on any lazy server.
21
+ */
22
+ export declare function recoverProgrammaticOperationsBeforeFirstRequest(input: {
23
+ approvalDecisionId: string | undefined;
24
+ hasUnfinishedOperations: () => Promise<boolean>;
25
+ toolPreparationReady: Promise<void> | null;
26
+ resumeApproved: (decisionOperationId: string | undefined) => Promise<CodemodeOperation[]> | undefined;
27
+ }): Promise<CodemodeOperation[]>;
@@ -34,7 +34,7 @@ import {
34
34
  warnDrainSnapshotFailure,
35
35
  withFirstPartyTools,
36
36
  workflowIdForSession
37
- } from "./chunk-QUX5LT2B.js";
37
+ } from "./chunk-BMTWULF7.js";
38
38
  import {
39
39
  inspectOpenSandboxKubernetesInventory,
40
40
  recordActiveUserGauges,
@@ -62,7 +62,7 @@ import {
62
62
  recordVerifiedSignupTrialSwitchGauge,
63
63
  recordWorkerDeathRecoveryMetrics,
64
64
  runtimeMetricsHooksForObservability
65
- } from "./chunk-XONQEVAD.js";
65
+ } from "./chunk-FF4HPY7L.js";
66
66
  import "./chunk-PZ5AY32C.js";
67
67
 
68
68
  // src/activities/knowledge-indexing.ts
@@ -8813,4 +8813,4 @@ export {
8813
8813
  createControlActivities,
8814
8814
  createControlActivitiesFromServices
8815
8815
  };
8816
- //# sourceMappingURL=activities-control-PEXBWKQJ.js.map
8816
+ //# sourceMappingURL=activities-control-D5KVEPHI.js.map
@@ -99,7 +99,7 @@ import {
99
99
  withFirstPartyTools,
100
100
  wrapLazyTurnBoxWithRouting,
101
101
  wrapTurnBoxWithRouting
102
- } from "./chunk-QUX5LT2B.js";
102
+ } from "./chunk-BMTWULF7.js";
103
103
  import {
104
104
  StreamTimingMetrics,
105
105
  makeMachineOpObserver,
@@ -132,7 +132,7 @@ import {
132
132
  runtimeMetricsHooksForObservability,
133
133
  sandboxAutomaticRecoveryOutcome,
134
134
  turnLifecycleMetricsFor
135
- } from "./chunk-XONQEVAD.js";
135
+ } from "./chunk-FF4HPY7L.js";
136
136
  import {
137
137
  __export
138
138
  } from "./chunk-PZ5AY32C.js";
@@ -1193,12 +1193,14 @@ import {
1193
1193
  } from "@opengeni/db";
1194
1194
 
1195
1195
  // src/activities/agent-turn/subscription-lease.ts
1196
+ var HEARTBEAT_INTERVAL_MS = 6e4;
1196
1197
  var SubscriptionTurnLease = class {
1197
1198
  constructor(leaseDeps) {
1198
1199
  this.leaseDeps = leaseDeps;
1199
1200
  if (!Number.isFinite(leaseDeps.ttlMs) || leaseDeps.ttlMs <= 0)
1200
1201
  throw new Error("Subscription lease TTL must be positive");
1201
1202
  this.now = leaseDeps.now ?? (() => performance.now());
1203
+ this.renewalIntervalMs = Math.min(HEARTBEAT_INTERVAL_MS, leaseDeps.ttlMs / 5);
1202
1204
  }
1203
1205
  held = false;
1204
1206
  lost = false;
@@ -1209,6 +1211,7 @@ var SubscriptionTurnLease = class {
1209
1211
  heartbeatInFlight = false;
1210
1212
  heartbeatTimer;
1211
1213
  now;
1214
+ renewalIntervalMs;
1212
1215
  expired = (deadline) => deadline === null || !Number.isFinite(deadline) || this.now() >= deadline;
1213
1216
  markLost = (reason) => {
1214
1217
  if (this.lost) return;
@@ -1231,6 +1234,8 @@ var SubscriptionTurnLease = class {
1231
1234
  return;
1232
1235
  }
1233
1236
  if (this.heartbeatInFlight) return;
1237
+ const renewAt = this.confirmedUntilMs - this.leaseDeps.ttlMs + this.renewalIntervalMs;
1238
+ if (this.now() < renewAt) return;
1234
1239
  this.heartbeatInFlight = true;
1235
1240
  const priorDeadline = this.confirmedUntilMs;
1236
1241
  const startedAt = this.now();
@@ -1257,7 +1262,7 @@ var SubscriptionTurnLease = class {
1257
1262
  };
1258
1263
  startHeartbeat = () => {
1259
1264
  if (!this.leaseDeps.getTurnId() || this.heartbeatTimer) return;
1260
- this.heartbeatTimer = setInterval(() => void this.renew("timer"), 6e4);
1265
+ this.heartbeatTimer = setInterval(() => void this.renew("timer"), HEARTBEAT_INTERVAL_MS);
1261
1266
  this.heartbeatTimer.unref?.();
1262
1267
  };
1263
1268
  stopHeartbeat = () => {
@@ -8101,6 +8106,13 @@ function programmaticContinuationNote(operations) {
8101
8106
  "Approval resumes the stored operation, not the earlier program's JavaScript stack. Read results with the current Codemode client's status/resume handle. Do not resubmit arguments for completed, running, waiting, or uncertain operations."
8102
8107
  ].join("\n");
8103
8108
  }
8109
+ async function recoverProgrammaticOperationsBeforeFirstRequest(input) {
8110
+ if (input.approvalDecisionId === void 0 && !await input.hasUnfinishedOperations()) {
8111
+ return [];
8112
+ }
8113
+ await input.toolPreparationReady;
8114
+ return await input.resumeApproved(input.approvalDecisionId) ?? [];
8115
+ }
8104
8116
 
8105
8117
  // src/activities/agent-turn/turn-sandbox-access.ts
8106
8118
  function ownedTurnSandboxForAgent(sandboxState) {
@@ -10251,11 +10263,33 @@ async function runTurnStreamAttempt(deps) {
10251
10263
  control.activityStatus = "idle";
10252
10264
  return claimedResult({ status: "idle" });
10253
10265
  };
10254
- await eventing.toolPreparationReady;
10255
- const codemodeOperations = await eventing.codemodeDispatcher?.resumeApproved(
10256
- `sandbox:${input.attemptId}`,
10257
- trigger.type === "user.approvalDecision" ? trigger.payload.approvalId : void 0
10258
- ) ?? [];
10266
+ const programmaticRecoveryStartedAt = performance.now();
10267
+ let programmaticRecoveryOutcome = "failed";
10268
+ const codemodeOperations = await recoverProgrammaticOperationsBeforeFirstRequest({
10269
+ approvalDecisionId: trigger.type === "user.approvalDecision" ? trigger.payload.approvalId : void 0,
10270
+ hasUnfinishedOperations: async () => (await listTurnCodemodeApprovals(db, {
10271
+ accountId: input.accountId,
10272
+ workspaceId: input.workspaceId,
10273
+ sessionId: input.sessionId,
10274
+ turnId: activeTurnId
10275
+ })).length > 0,
10276
+ toolPreparationReady: eventing.toolPreparationReady,
10277
+ resumeApproved: (decisionOperationId) => eventing.codemodeDispatcher?.resumeApproved(
10278
+ `sandbox:${input.attemptId}`,
10279
+ decisionOperationId
10280
+ )
10281
+ }).then((operations) => {
10282
+ programmaticRecoveryOutcome = "completed";
10283
+ return operations;
10284
+ }).finally(() => {
10285
+ recordTurnStartupPhase(observability, {
10286
+ phase: "programmatic_operation_recovery",
10287
+ provider: turnExecutionPolicy.providerId,
10288
+ backend: activeSandboxBackend ?? groupBoxBackend,
10289
+ outcome: programmaticRecoveryOutcome,
10290
+ durationSeconds: (performance.now() - programmaticRecoveryStartedAt) / 1e3
10291
+ });
10292
+ });
10259
10293
  codemodeContinuationNote = programmaticContinuationNote(codemodeOperations);
10260
10294
  programmaticApprovalAcknowledged = trigger.type === "user.approvalDecision" && codemodeOperations.some(
10261
10295
  (operation) => operation.operationId === trigger.payload.approvalId
@@ -12247,6 +12281,7 @@ import {
12247
12281
  desktopCapableBackend
12248
12282
  } from "@opengeni/runtime";
12249
12283
  import {
12284
+ configuredModelForAcceptedTurnExecutionPolicy,
12250
12285
  isDirectOpenAiApiBaseUrl
12251
12286
  } from "@opengeni/config";
12252
12287
 
@@ -12523,6 +12558,16 @@ function shouldDeferNonEagerToolPreparation(args) {
12523
12558
  args.lazyToolTransport && args.progressiveDisclosureEnabled && args.triggerKind === "next" && (args.triggerType === "user.message" || args.triggerType === "system.update.delivered")
12524
12559
  );
12525
12560
  }
12561
+ function resolveAcceptedTurnModel(runtime, settings, policy) {
12562
+ const current = runtime.resolveTurnModel(settings, policy.productModelId);
12563
+ if (!current) return null;
12564
+ const configured = configuredModelForAcceptedTurnExecutionPolicy(
12565
+ current.configured,
12566
+ current.provider,
12567
+ policy
12568
+ );
12569
+ return configured === current.configured ? current : { ...current, configured };
12570
+ }
12526
12571
  function hostedWebSearchForTurn(resolvedModel, deploymentWebSearchEnabled) {
12527
12572
  return resolvedModel?.configured.hostedWebSearch ?? deploymentWebSearchEnabled;
12528
12573
  }
@@ -12713,10 +12758,7 @@ async function prepareGovernanceAndModel(deps) {
12713
12758
  baseRunSettings
12714
12759
  )
12715
12760
  );
12716
- const resolvedModel = runtime.resolveTurnModel(
12717
- capabilitySettings,
12718
- turnExecutionPolicy.productModelId
12719
- );
12761
+ const resolvedModel = resolveAcceptedTurnModel(runtime, capabilitySettings, turnExecutionPolicy);
12720
12762
  const providerApi = resolvedModel?.provider.api ?? "responses";
12721
12763
  const nativeImageProviderBinding = providerApi === "responses" ? openAiHostedImageProviderBindingForTurn(capabilitySettings, resolvedModel) : null;
12722
12764
  const lazyToolTransport = lazyToolTransportForTurn(resolvedModel);
@@ -13476,6 +13518,7 @@ import {
13476
13518
  readLease as readLease2,
13477
13519
  accrueWarmSeconds,
13478
13520
  SandboxWorkspaceMutationFencedError,
13521
+ SandboxWorkspaceMutationOutputRejectedError,
13479
13522
  retainWorkspaceMutationProcess,
13480
13523
  retainedProviderCommandPersistence,
13481
13524
  SandboxRetainedProcessPromotionFencedError
@@ -13483,6 +13526,7 @@ import {
13483
13526
  import { createProviderCommandRetainer } from "@opengeni/db/retained-provider-commands";
13484
13527
  import {
13485
13528
  RoutingMutationOutcomeUnknownError,
13529
+ RoutingMutationOutputRejectedError,
13486
13530
  ProviderCommandStartOutcomeUnknownError,
13487
13531
  ProviderCommandObservationUnavailableError,
13488
13532
  isModalCommandStartOutcomeUnknownError as isModalCommandStartOutcomeUnknownError2,
@@ -13895,6 +13939,17 @@ function createSandboxTurnRuntime(deps) {
13895
13939
  try {
13896
13940
  await settleMutation("resolved");
13897
13941
  } catch (settlementError) {
13942
+ if (settlementError instanceof SandboxWorkspaceMutationOutputRejectedError && settlementError.matchesPhysicalSettlement({
13943
+ accountId: identity.accountId,
13944
+ workspaceId: identity.workspaceId,
13945
+ admission,
13946
+ operation,
13947
+ outcome: "resolved"
13948
+ })) {
13949
+ throw new RoutingMutationOutputRejectedError(operation, settlementError.code, {
13950
+ cause: settlementError
13951
+ });
13952
+ }
13898
13953
  throw new RoutingMutationOutcomeUnknownError(
13899
13954
  operation,
13900
13955
  `Platform workspace mutation "${operation}" returned from the provider but lost its durable settlement fence; its outcome is unknown and it was not replayed`,
@@ -32769,7 +32824,8 @@ import {
32769
32824
  } from "@opengeni/runtime";
32770
32825
  import {
32771
32826
  ChannelAPartialMutationError as ChannelAPartialMutationError2,
32772
- RoutingMutationOutcomeUnknownError as RoutingMutationOutcomeUnknownError2
32827
+ RoutingMutationOutcomeUnknownError as RoutingMutationOutcomeUnknownError2,
32828
+ isRoutingMutationOutputRejectedError
32773
32829
  } from "@opengeni/runtime/sandbox";
32774
32830
  import { connectorAttachmentSandboxPath as connectorAttachmentSandboxPath2 } from "@opengeni/runtime";
32775
32831
  var ConnectorAttachmentMaterializationError = class extends Error {
@@ -32878,7 +32934,7 @@ async function materializeConnectorAttachmentsInChannel(channel, request, option
32878
32934
  attachments: receipts
32879
32935
  });
32880
32936
  } catch (error51) {
32881
- if (error51 instanceof ChannelAPartialMutationError2 || error51 instanceof RoutingMutationOutcomeUnknownError2) {
32937
+ if (error51 instanceof ChannelAPartialMutationError2 || error51 instanceof RoutingMutationOutcomeUnknownError2 || isRoutingMutationOutputRejectedError(error51)) {
32882
32938
  throw error51;
32883
32939
  }
32884
32940
  throw new ConnectorAttachmentMaterializationError();
@@ -33492,7 +33548,8 @@ import {
33492
33548
  resolveTurnToolPolicy,
33493
33549
  scheduledTurnMcpServerIds,
33494
33550
  hasPermission as hasPermission2,
33495
- requireExplicitPermissionDelegation
33551
+ requireExplicitPermissionDelegation,
33552
+ createWebSearchBilling
33496
33553
  } from "@opengeni/core";
33497
33554
  import { SandboxChannelAService as SandboxChannelAService3 } from "@opengeni/runtime/sandbox";
33498
33555
  import { sandboxRunAs as sandboxRunAs4 } from "@opengeni/runtime";
@@ -33814,6 +33871,200 @@ function codeSearchToolDefinitions(input) {
33814
33871
  ];
33815
33872
  }
33816
33873
 
33874
+ // src/activities/agent-turn/web-search.ts
33875
+ import {
33876
+ WEB_FETCH_TOOL_NAME,
33877
+ WEB_SEARCH_TOOL_NAME,
33878
+ webSearchCallPricing,
33879
+ webSearchCreditMicros,
33880
+ webSearchProviderConfig,
33881
+ webSearchToolPlan
33882
+ } from "@opengeni/config";
33883
+ import {
33884
+ WebSearchBillingRefusedError
33885
+ } from "@opengeni/core";
33886
+ import {
33887
+ WEB_FETCH_TOOL_DESCRIPTION,
33888
+ WEB_FETCH_RETAINED_MAX_CHARS,
33889
+ WEB_SEARCH_TOOL_DESCRIPTION,
33890
+ WebSearchProviderError,
33891
+ WebToolArgumentError,
33892
+ createWebFetchProvider,
33893
+ createWebSearchProvider,
33894
+ parseWebFetchArguments,
33895
+ parseWebSearchArguments,
33896
+ renderWebPageWindow,
33897
+ renderWebSearchResults,
33898
+ webFetchInputSchema,
33899
+ webSearchInputSchema
33900
+ } from "@opengeni/runtime/web-search";
33901
+ function turnWebSearchPlan(resolvedModel, settings) {
33902
+ return webSearchToolPlan(settings, {
33903
+ hostedWebSearch: hostedWebSearchForTurn(resolvedModel, settings.webSearchEnabled),
33904
+ // SuperGrok's native search is added by its transport, not as an agent
33905
+ // tool, so provider tools would only duplicate it.
33906
+ transportHostedSearch: resolvedModel?.provider.kind === "xai-subscription"
33907
+ });
33908
+ }
33909
+ var PAGE_CACHE_ENTRIES = 16;
33910
+ function textResult2(text, isError) {
33911
+ return { isError, content: [{ type: "text", text }] };
33912
+ }
33913
+ function settledCost(operationId, operation, provider, pricing, reportedCostMicros) {
33914
+ const basis = pricing.explicit ? "configured_price" : reportedCostMicros !== void 0 ? "provider_reported" : "list_price";
33915
+ const providerMicros = basis === "provider_reported" ? reportedCostMicros : pricing.providerMicros;
33916
+ return {
33917
+ operationId,
33918
+ operation,
33919
+ provider,
33920
+ providerMicros,
33921
+ creditMicros: webSearchCreditMicros(providerMicros, pricing.marginBps),
33922
+ marginBps: pricing.marginBps,
33923
+ basis
33924
+ };
33925
+ }
33926
+ function providerFailure(error51) {
33927
+ return `${error51.message}${error51.retryable ? ". You may retry shortly." : "."}`;
33928
+ }
33929
+ function webSearchToolDefinitions(input) {
33930
+ const config2 = webSearchProviderConfig(input.settings);
33931
+ if (!config2 || input.tools.length === 0) return [];
33932
+ const adapterInput = {
33933
+ timeoutMs: config2.timeoutMs,
33934
+ ...input.fetch ? { fetch: input.fetch } : {}
33935
+ };
33936
+ const settle = async (cost) => {
33937
+ try {
33938
+ await input.billing.settle(input.scope, cost);
33939
+ } catch (error51) {
33940
+ input.observability.warn("web search usage settlement failed", {
33941
+ operation: cost.operation,
33942
+ provider: cost.provider,
33943
+ error: error51 instanceof Error ? error51.message : String(error51)
33944
+ });
33945
+ }
33946
+ };
33947
+ const definitions = [];
33948
+ if (input.tools.includes(WEB_SEARCH_TOOL_NAME)) {
33949
+ const provider = createWebSearchProvider({ ...adapterInput, endpoint: config2.search });
33950
+ const pricing = webSearchCallPricing(config2, "search");
33951
+ definitions.push({
33952
+ identity: { serverId: "opengeni", toolName: WEB_SEARCH_TOOL_NAME },
33953
+ modelName: WEB_SEARCH_TOOL_NAME,
33954
+ codemodePath: ["opengeni", WEB_SEARCH_TOOL_NAME],
33955
+ title: "Search the web",
33956
+ description: WEB_SEARCH_TOOL_DESCRIPTION,
33957
+ inputSchema: webSearchInputSchema,
33958
+ annotations: {
33959
+ title: "Search the web",
33960
+ readOnlyHint: true,
33961
+ destructiveHint: false,
33962
+ idempotentHint: true,
33963
+ openWorldHint: true
33964
+ },
33965
+ source: "opengeni",
33966
+ approval: "none",
33967
+ execute: async (args, context) => {
33968
+ let request;
33969
+ try {
33970
+ request = parseWebSearchArguments(args);
33971
+ await input.billing.admit(input.scope, pricing.providerMicros);
33972
+ } catch (error51) {
33973
+ if (error51 instanceof WebToolArgumentError || error51 instanceof WebSearchBillingRefusedError)
33974
+ return textResult2(error51.message, true);
33975
+ throw error51;
33976
+ }
33977
+ try {
33978
+ const response = await provider.search(request, { signal: context.signal });
33979
+ await settle(
33980
+ settledCost(
33981
+ context.operationId,
33982
+ "search",
33983
+ config2.search.provider,
33984
+ pricing,
33985
+ response.reportedCostMicros
33986
+ )
33987
+ );
33988
+ return textResult2(renderWebSearchResults(request.query, response.results), false);
33989
+ } catch (error51) {
33990
+ if (context.signal?.aborted) throw error51;
33991
+ if (error51 instanceof WebSearchProviderError) {
33992
+ return textResult2(`Web search failed: ${providerFailure(error51)}`, true);
33993
+ }
33994
+ throw error51;
33995
+ }
33996
+ }
33997
+ });
33998
+ }
33999
+ if (input.tools.includes(WEB_FETCH_TOOL_NAME) && config2.fetch) {
34000
+ const fetchEndpoint = config2.fetch;
34001
+ const provider = createWebFetchProvider({ ...adapterInput, endpoint: fetchEndpoint });
34002
+ const pricing = webSearchCallPricing(config2, "fetch");
34003
+ const pages = /* @__PURE__ */ new Map();
34004
+ definitions.push({
34005
+ identity: { serverId: "opengeni", toolName: WEB_FETCH_TOOL_NAME },
34006
+ modelName: WEB_FETCH_TOOL_NAME,
34007
+ codemodePath: ["opengeni", WEB_FETCH_TOOL_NAME],
34008
+ title: "Read a web page",
34009
+ description: WEB_FETCH_TOOL_DESCRIPTION,
34010
+ inputSchema: webFetchInputSchema,
34011
+ annotations: {
34012
+ title: "Read a web page",
34013
+ readOnlyHint: true,
34014
+ destructiveHint: false,
34015
+ idempotentHint: true,
34016
+ openWorldHint: true
34017
+ },
34018
+ source: "opengeni",
34019
+ approval: "none",
34020
+ execute: async (args, context) => {
34021
+ let request;
34022
+ try {
34023
+ request = parseWebFetchArguments(args);
34024
+ } catch (error51) {
34025
+ if (error51 instanceof WebToolArgumentError) return textResult2(error51.message, true);
34026
+ throw error51;
34027
+ }
34028
+ const cached2 = pages.get(request.url);
34029
+ if (cached2) return textResult2(renderWebPageWindow(cached2, request), false);
34030
+ try {
34031
+ await input.billing.admit(input.scope, pricing.providerMicros);
34032
+ } catch (error51) {
34033
+ if (error51 instanceof WebSearchBillingRefusedError) return textResult2(error51.message, true);
34034
+ throw error51;
34035
+ }
34036
+ try {
34037
+ const page = await provider.fetch({ url: request.url }, { signal: context.signal });
34038
+ await settle(
34039
+ settledCost(
34040
+ context.operationId,
34041
+ "fetch",
34042
+ fetchEndpoint.provider,
34043
+ pricing,
34044
+ page.reportedCostMicros
34045
+ )
34046
+ );
34047
+ if (pages.size >= PAGE_CACHE_ENTRIES) {
34048
+ pages.delete(pages.keys().next().value);
34049
+ }
34050
+ pages.set(request.url, {
34051
+ ...page,
34052
+ content: page.content.slice(0, WEB_FETCH_RETAINED_MAX_CHARS + 1)
34053
+ });
34054
+ return textResult2(renderWebPageWindow(page, request), false);
34055
+ } catch (error51) {
34056
+ if (context.signal?.aborted) throw error51;
34057
+ if (error51 instanceof WebSearchProviderError) {
34058
+ return textResult2(`Web fetch failed: ${providerFailure(error51)}`, true);
34059
+ }
34060
+ throw error51;
34061
+ }
34062
+ }
34063
+ });
34064
+ }
34065
+ return definitions;
34066
+ }
34067
+
33817
34068
  // src/activities/agent-turn/skill-tools.ts
33818
34069
  import { createHash as createHash13 } from "crypto";
33819
34070
  import {
@@ -33983,14 +34234,14 @@ async function readResult(files, identity, args, context, activeHistory) {
33983
34234
  if (args.listFiles === true) {
33984
34235
  return {
33985
34236
  kind: "list",
33986
- result: textResult2({ ...identity, ...listSkillPaths(files, PORTABLE_SKILL_MAX_FILES) })
34237
+ result: textResult3({ ...identity, ...listSkillPaths(files, PORTABLE_SKILL_MAX_FILES) })
33987
34238
  };
33988
34239
  }
33989
34240
  const selected = readSkillFiles(files, args.paths);
33990
34241
  if (args.paths === void 0 && context.caller.kind === "model" && activeHistory && await inActiveHistory(activeHistory, identity, selected.files[0])) {
33991
34242
  return {
33992
34243
  kind: "already_in_context",
33993
- result: textResult2({
34244
+ result: textResult3({
33994
34245
  ...identity,
33995
34246
  alreadyInContext: true,
33996
34247
  message: `Already in context: SKILL.md of ${JSON.stringify(args.skill)}${identity.revisionId ? ` revision ${identity.revisionId}` : ""} was returned earlier in this conversation and is unchanged. Use that copy. Re-read only if you need a fresh copy: call skill_read with paths ["SKILL.md"].`
@@ -33998,12 +34249,12 @@ async function readResult(files, identity, args, context, activeHistory) {
33998
34249
  };
33999
34250
  }
34000
34251
  if (args.paths !== void 0) {
34001
- return { kind: "files", result: textResult2({ ...identity, ...selected }) };
34252
+ return { kind: "files", result: textResult3({ ...identity, ...selected }) };
34002
34253
  }
34003
34254
  const scripts = skillScriptIndex(files);
34004
34255
  return {
34005
34256
  kind: "full",
34006
- result: textResult2({ ...identity, ...selected, ...scripts ?? {} })
34257
+ result: textResult3({ ...identity, ...selected, ...scripts ?? {} })
34007
34258
  };
34008
34259
  }
34009
34260
  function observe(telemetry, read) {
@@ -34040,7 +34291,7 @@ function withSkillUse(read, origin, identity, telemetry) {
34040
34291
  return read.result;
34041
34292
  }
34042
34293
  }
34043
- function textResult2(output) {
34294
+ function textResult3(output) {
34044
34295
  return {
34045
34296
  isError: false,
34046
34297
  content: [{ type: "text", text: JSON.stringify(output) }],
@@ -35504,6 +35755,19 @@ async function prepareTurnToolRuntime(deps) {
35504
35755
  const attemptToolFamilies = resolveAgentToolFamilies(session.agent, {
35505
35756
  hasSkills: skillCatalog.length > 0
35506
35757
  });
35758
+ const webSearchTools = webSearchToolDefinitions({
35759
+ settings: runSettings,
35760
+ tools: turnWebSearchPlan(resolvedModel, runSettings).providerTools,
35761
+ scope: {
35762
+ accountId: input.accountId,
35763
+ workspaceId: input.workspaceId,
35764
+ sessionId: input.sessionId,
35765
+ turnId: turn.id,
35766
+ attemptId: input.attemptId
35767
+ },
35768
+ billing: createWebSearchBilling({ db, settings: runSettings }),
35769
+ observability
35770
+ });
35507
35771
  const attemptToolDefinitions = [
35508
35772
  ...operationReadStore ? [
35509
35773
  createOperationReadAttemptToolDefinition({
@@ -35582,7 +35846,8 @@ async function prepareTurnToolRuntime(deps) {
35582
35846
  ...interactionInterventionResume ? { interventionResume: interactionInterventionResume } : {}
35583
35847
  }),
35584
35848
  ...googleDrivePublicationTool && googleDrivePublicationAllowed ? [googleDrivePublicationTool] : [],
35585
- ...codeSearchTools
35849
+ ...codeSearchTools,
35850
+ ...webSearchTools
35586
35851
  ].filter((tool) => attemptToolFamilies.allowsFunctionTool(tool.modelName));
35587
35852
  recordTurnStartupPhase(observability, {
35588
35853
  phase: "tool_context_preparation",
@@ -37401,7 +37666,7 @@ async function buildTurnAgent(deps) {
37401
37666
  variableNames: Object.keys(workspaceVariableSet.values)
37402
37667
  }
37403
37668
  } : {};
37404
- const hostedWebSearch = hostedWebSearchForTurn(resolvedModel, runSettings.webSearchEnabled);
37669
+ const hostedWebSearch = turnWebSearchPlan(resolvedModel, runSettings).hostedWebSearch;
37405
37670
  const resolveImageReferences = async (references) => await resolveImageGenerationReferencesForTool({
37406
37671
  db,
37407
37672
  objectStorage,
@@ -40683,4 +40948,4 @@ export {
40683
40948
  createTurnActivities,
40684
40949
  createTurnActivitiesFromServices
40685
40950
  };
40686
- //# sourceMappingURL=activities-turn-W27A2SBO.js.map
40951
+ //# sourceMappingURL=activities-turn-4Z3M7Z3F.js.map