@opengeni/worker-bundle 2.2.0 → 2.2.1-canary.36919050519001

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 (74) hide show
  1. package/dist/activities/agent-run-admission.d.ts +3 -1
  2. package/dist/activities/agent-turn/admission.d.ts +24 -3
  3. package/dist/activities/agent-turn/agent-build.d.ts +1 -0
  4. package/dist/activities/agent-turn/claude-usage-observer.d.ts +13 -1
  5. package/dist/activities/agent-turn/compaction-prep.d.ts +20 -0
  6. package/dist/activities/agent-turn/final-reply.d.ts +18 -0
  7. package/dist/activities/agent-turn/governance-model.d.ts +6 -0
  8. package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
  9. package/dist/activities/agent-turn/run-credentials.d.ts +2 -0
  10. package/dist/activities/agent-turn/sandbox-establish.d.ts +1 -0
  11. package/dist/activities/agent-turn/session-title.d.ts +3 -1
  12. package/dist/activities/agent-turn/stream-attempt.d.ts +1 -0
  13. package/dist/activities/agent-turn/turn-context.d.ts +1 -0
  14. package/dist/activities/automations.d.ts +3 -1
  15. package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
  16. package/dist/activities/goals.d.ts +8 -0
  17. package/dist/activities/run-credentials.d.ts +4 -2
  18. package/dist/activities/types.d.ts +1 -1
  19. package/dist/{activities-control-MHNA3CYV.js → activities-control-55ECL47G.js} +403 -116
  20. package/dist/activities-control-55ECL47G.js.map +1 -0
  21. package/dist/activities-control.d.ts +30 -0
  22. package/dist/{activities-turn-FQ5OO2WW.js → activities-turn-T7Y3BWYO.js} +678 -246
  23. package/dist/activities-turn-T7Y3BWYO.js.map +1 -0
  24. package/dist/activities.d.ts +30 -0
  25. package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
  26. package/dist/{chunk-RZAAGRFQ.js → chunk-OWNLATJL.js} +189 -80
  27. package/dist/chunk-OWNLATJL.js.map +1 -0
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.js +45 -23
  30. package/dist/index.js.map +1 -1
  31. package/dist/sandbox-resume.d.ts +4 -1
  32. package/dist/workflow-bundle.js +72 -10
  33. package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
  34. package/dist/workflows.d.ts +1 -0
  35. package/package.json +21 -20
  36. package/src/activities/agent-run-admission.ts +17 -1
  37. package/src/activities/agent-turn/admission.ts +64 -1
  38. package/src/activities/agent-turn/agent-build.ts +24 -3
  39. package/src/activities/agent-turn/claim.ts +56 -14
  40. package/src/activities/agent-turn/claude-usage-observer.ts +33 -1
  41. package/src/activities/agent-turn/compaction-prep.ts +67 -16
  42. package/src/activities/agent-turn/errors.ts +55 -0
  43. package/src/activities/agent-turn/failure-settlement.ts +26 -6
  44. package/src/activities/agent-turn/final-reply.ts +46 -0
  45. package/src/activities/agent-turn/governance-model.ts +12 -0
  46. package/src/activities/agent-turn/model-call-admission.ts +94 -0
  47. package/src/activities/agent-turn/run-credentials.ts +15 -3
  48. package/src/activities/agent-turn/run.ts +43 -5
  49. package/src/activities/agent-turn/sandbox-establish.ts +9 -5
  50. package/src/activities/agent-turn/session-title.ts +8 -2
  51. package/src/activities/agent-turn/stream-attempt.ts +144 -38
  52. package/src/activities/agent-turn/tool-environment.ts +51 -39
  53. package/src/activities/agent-turn/turn-context.ts +1 -0
  54. package/src/activities/automations.ts +55 -3
  55. package/src/activities/browser-deadline-checkpoint.ts +279 -0
  56. package/src/activities/capabilities.ts +52 -14
  57. package/src/activities/goals.ts +67 -95
  58. package/src/activities/knowledge-indexing.ts +29 -0
  59. package/src/activities/open-suffix-resume.ts +2 -1
  60. package/src/activities/run-credentials.ts +22 -16
  61. package/src/activities/scheduled-tasks.ts +83 -13
  62. package/src/activities/types.ts +1 -0
  63. package/src/activities/workspace-credential-provider.ts +12 -0
  64. package/src/activities-control.ts +2 -0
  65. package/src/activities.ts +3 -0
  66. package/src/browser-deadline-checkpoint-contract.ts +5 -0
  67. package/src/index.ts +53 -25
  68. package/src/sandbox-resume.ts +23 -8
  69. package/src/sandbox-routing.ts +4 -2
  70. package/src/workflows/browser-deadline-checkpoint.ts +50 -0
  71. package/src/workflows.ts +4 -0
  72. package/dist/activities-control-MHNA3CYV.js.map +0 -1
  73. package/dist/activities-turn-FQ5OO2WW.js.map +0 -1
  74. package/dist/chunk-RZAAGRFQ.js.map +0 -1
@@ -94,7 +94,7 @@ import {
94
94
  withFirstPartyTools,
95
95
  wrapLazyTurnBoxWithRouting,
96
96
  wrapTurnBoxWithRouting
97
- } from "./chunk-RZAAGRFQ.js";
97
+ } from "./chunk-OWNLATJL.js";
98
98
  import {
99
99
  StreamTimingMetrics,
100
100
  makeMachineOpObserver,
@@ -139,10 +139,13 @@ import {
139
139
  getSessionAuthorityProjection,
140
140
  readActiveSandbox as readActiveSandbox3,
141
141
  getWorkspaceCredentialProvider,
142
- loadClaudeSubscriptionUsageCredential
142
+ loadClaudeSubscriptionUsageCredential as loadClaudeSubscriptionUsageCredential2,
143
+ resolveClaudeSubscriptionCredential,
144
+ ClaudeSubscriptionReconnectRequired
143
145
  } from "@opengeni/db";
146
+ import { resolveAgentToolFamilies as resolveAgentToolFamilies3 } from "@opengeni/contracts";
144
147
  import {
145
- assertModelConnectionAllowsTurn,
148
+ assertModelConnectionAllowsTurn as assertModelConnectionAllowsTurn2,
146
149
  freezeAgentLearningPolicy as freezeAgentLearningPolicy2,
147
150
  recoverLegacyKnowledgeConfirmations,
148
151
  withSessionRlsActorContext
@@ -161,6 +164,7 @@ import {
161
164
  import {
162
165
  REMOTE_COMPACTION_V2_BETA_FEATURE,
163
166
  REMOTE_COMPACTION_V2_IMPLEMENTATION as REMOTE_COMPACTION_V2_IMPLEMENTATION2,
167
+ awaitModelCallAdmission,
164
168
  materializeSandboxFileDownloads as materializeSandboxFileDownloads3,
165
169
  sandboxFileDownloadFailureNote
166
170
  } from "@opengeni/runtime";
@@ -191,6 +195,17 @@ async function createClaudeUsageObserver(providers, latest, readCredential) {
191
195
  (provider) => provider.kind === "claude-subscription-workspace" || provider.kind === "claude-subscription-organization"
192
196
  ).map(async (provider) => {
193
197
  const scope = provider.kind === "claude-subscription-workspace" ? "workspace" : "organization";
198
+ const binding = provider.anthropic?.credentialBinding;
199
+ if (binding && provider.apiKey)
200
+ return [
201
+ provider.id,
202
+ {
203
+ scope,
204
+ token: provider.apiKey,
205
+ expectedConnectionId: binding.connectionId,
206
+ expectedCredentialVersion: binding.credentialVersion
207
+ }
208
+ ];
194
209
  const credential = await readCredential(scope).catch(() => null);
195
210
  if (!credential || credential.token !== provider.apiKey) return null;
196
211
  return [
@@ -205,7 +220,7 @@ async function createClaudeUsageObserver(providers, latest, readCredential) {
205
220
  })
206
221
  );
207
222
  const captured = new Map(bindings.filter((binding) => binding !== null));
208
- return (providerId, response) => {
223
+ const observe2 = (providerId, response) => {
209
224
  const binding = captured.get(providerId);
210
225
  if (!binding) return;
211
226
  const { scope, ...identity } = binding;
@@ -230,6 +245,16 @@ async function createClaudeUsageObserver(providers, latest, readCredential) {
230
245
  ...response.status === 401 ? { refresh: { status: "reconnect", checkedAt: (/* @__PURE__ */ new Date()).toISOString() } } : {}
231
246
  });
232
247
  };
248
+ return Object.assign(observe2, {
249
+ binding(providerId) {
250
+ return captured.get(providerId);
251
+ },
252
+ renew(providerId, credential) {
253
+ const binding = captured.get(providerId);
254
+ if (binding && binding.expectedConnectionId === credential.connectionId && binding.expectedCredentialVersion === credential.credentialVersion)
255
+ captured.set(providerId, { ...binding, token: credential.token });
256
+ }
257
+ });
233
258
  }
234
259
 
235
260
  // src/activities/agent-turn/run.ts
@@ -5245,7 +5270,10 @@ import {
5245
5270
  isSessionEventPersistenceError as isSessionEventPersistenceError2
5246
5271
  } from "@opengeni/db";
5247
5272
  import { publishDurableSessionEvents as publishDurableSessionEvents2 } from "@opengeni/events";
5248
- import { maxTurnsExceededRunState } from "@opengeni/runtime";
5273
+ import {
5274
+ maxTurnsExceededRunState,
5275
+ isModalTaskExecStartPreDispatchUnavailableError
5276
+ } from "@opengeni/runtime";
5249
5277
  import { ApplicationFailure, CancelledFailure } from "@temporalio/activity";
5250
5278
  import { TurnExecutionPolicyDefinitionMismatchError } from "@opengeni/config";
5251
5279
  import {
@@ -5256,7 +5284,7 @@ import {
5256
5284
  } from "@opengeni/codex";
5257
5285
 
5258
5286
  // src/activities/agent-turn/admission.ts
5259
- import { getBillingBalance, sumUsageQuantity } from "@opengeni/db";
5287
+ import { checkWorkspaceAllowance, getBillingBalance, sumUsageQuantity } from "@opengeni/db";
5260
5288
  import {
5261
5289
  configuredStaticUsageLimits
5262
5290
  } from "@opengeni/config";
@@ -5901,13 +5929,48 @@ function stableInteractionInterventionUuid(namespace, sessionId, turnId, toolCal
5901
5929
  return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`;
5902
5930
  }
5903
5931
  var BudgetExhaustedError = class extends Error {
5904
- constructor(message, serializedRunState) {
5932
+ constructor(message, serializedRunState, allowance = null) {
5905
5933
  super(message);
5906
5934
  this.serializedRunState = serializedRunState;
5935
+ this.allowance = allowance;
5907
5936
  this.name = "BudgetExhaustedError";
5908
5937
  }
5909
5938
  };
5910
- async function ensureRunAllowed(settings, db, accountId, workspaceId, isExternallyBilledTurn, entitlements, chargesOpenGeniCredits = !isExternallyBilledTurn, countsTowardTokenCap = !isExternallyBilledTurn) {
5939
+ var AllowanceExhaustedError = class extends Error {
5940
+ constructor(refusal) {
5941
+ super(refusal.message);
5942
+ this.refusal = refusal;
5943
+ this.name = "AllowanceExhaustedError";
5944
+ }
5945
+ code = "allowance_exhausted";
5946
+ };
5947
+ async function ensureRunAllowedBetweenModelCalls(input) {
5948
+ try {
5949
+ await ensureRunAllowed(
5950
+ input.settings,
5951
+ input.db,
5952
+ input.accountId,
5953
+ input.workspaceId,
5954
+ input.isExternallyBilledTurn,
5955
+ input.entitlements,
5956
+ input.chargesOpenGeniCredits,
5957
+ input.countsTowardTokenCap,
5958
+ input.initiatingHumanSubjectId
5959
+ );
5960
+ } catch (limitError) {
5961
+ let serializedRunState = null;
5962
+ try {
5963
+ serializedRunState = input.serializedRunState?.() ?? null;
5964
+ } catch {
5965
+ }
5966
+ throw new BudgetExhaustedError(
5967
+ limitError instanceof Error ? limitError.message : String(limitError),
5968
+ serializedRunState,
5969
+ limitError instanceof AllowanceExhaustedError ? limitError.refusal : null
5970
+ );
5971
+ }
5972
+ }
5973
+ async function ensureRunAllowed(settings, db, accountId, workspaceId, isExternallyBilledTurn, entitlements, chargesOpenGeniCredits = !isExternallyBilledTurn, countsTowardTokenCap = !isExternallyBilledTurn, initiatingHumanSubjectId = null) {
5911
5974
  if (chargesOpenGeniCredits && entitlements && (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed")) {
5912
5975
  const decision = await entitlements.admitRun({
5913
5976
  accountId,
@@ -5924,6 +5987,14 @@ async function ensureRunAllowed(settings, db, accountId, workspaceId, isExternal
5924
5987
  throw new Error("insufficient OpenGeni credits");
5925
5988
  }
5926
5989
  }
5990
+ if (chargesOpenGeniCredits) {
5991
+ const refusal = await checkWorkspaceAllowance(db, {
5992
+ accountId,
5993
+ workspaceId,
5994
+ subjectId: initiatingHumanSubjectId
5995
+ });
5996
+ if (refusal) throw new AllowanceExhaustedError(refusal);
5997
+ }
5927
5998
  if (settings.usageLimitsMode === "static" || settings.usageLimitsMode === "managed") {
5928
5999
  const limits = configuredStaticUsageLimits(settings);
5929
6000
  if (limits.maxMonthlyAgentRunsPerWorkspace) {
@@ -6998,19 +7069,22 @@ async function settleTurnFailure(deps) {
6998
7069
  await historySink.reconcileConversationTruth();
6999
7070
  if (!await eventing.settle({
7000
7071
  events: [
7072
+ ...error51.allowance ? [{ type: "usage.exhausted", payload: error51.allowance }] : [],
7001
7073
  {
7002
7074
  type: "turn.completed",
7003
7075
  payload: {
7004
7076
  output: "",
7005
7077
  segmentLimit: "budget_exhausted",
7006
- detail: error51.message
7078
+ detail: error51.message,
7079
+ ...error51.allowance ?? {}
7007
7080
  }
7008
7081
  },
7009
7082
  { type: "session.status.changed", payload: { status: "idle" } }
7010
7083
  ],
7011
7084
  turnStatus: "completed",
7012
7085
  sessionStatus: "idle",
7013
- activeTurnId: null
7086
+ activeTurnId: null,
7087
+ ...error51.allowance ? { allowanceGoalPause: { rationale: error51.allowance.message } } : {}
7014
7088
  })) {
7015
7089
  return claimedResult({ status: "cancelled" });
7016
7090
  }
@@ -7078,10 +7152,12 @@ async function settleTurnFailure(deps) {
7078
7152
  }
7079
7153
  }
7080
7154
  const earlyDefinitionMismatch = error51 instanceof TurnExecutionPolicyDefinitionMismatchError && !attempt.modelRequestStarted && !eventing.turnStartedPublished && !!attempt.turnId && !!attempt.triggerEventId && attempt.executionGeneration > 0;
7155
+ const earlyCommandStartUnavailable = isModalTaskExecStartPreDispatchUnavailableError(error51) && !attempt.modelRequestStarted && !eventing.turnStartedPublished && !!attempt.turnId && !!attempt.triggerEventId && attempt.executionGeneration > 0;
7156
+ const earlyRecoverableSetup = earlyDefinitionMismatch || earlyCommandStartUnavailable;
7081
7157
  let failure2 = earlyDefinitionMismatch ? { error: error51.message, code: error51.code, retryable: true } : codexTerminalFailure ?? agentRunFailurePayload(error51, {
7082
7158
  isCodexTurn: billingState.isCodexTurn
7083
7159
  });
7084
- if (attempt.turnId && (earlyDefinitionMismatch || failure2.retryable && eventing.publish && eventing.turnStartedPublished)) {
7160
+ if (attempt.turnId && (earlyRecoverableSetup || failure2.retryable && eventing.publish && eventing.turnStartedPublished)) {
7085
7161
  const nextProviderRecoveryCount = attempt.providerRecoveryCount + 1;
7086
7162
  const recoveryResult = providerRecoveryResult({
7087
7163
  failureCode: failure2.code,
@@ -7090,7 +7166,7 @@ async function settleTurnFailure(deps) {
7090
7166
  });
7091
7167
  try {
7092
7168
  if (recoveryResult.status === "recovering") {
7093
- if (!earlyDefinitionMismatch) {
7169
+ if (!earlyRecoverableSetup) {
7094
7170
  await flushRuntimeBatcher();
7095
7171
  await historySink.reconcileConversationTruth({ requireDurable: true });
7096
7172
  }
@@ -7121,13 +7197,13 @@ async function settleTurnFailure(deps) {
7121
7197
  return claimedResult(recoveryResult);
7122
7198
  }
7123
7199
  failure2 = providerRecoveryExhaustedFailure(failure2, recoveryResult);
7124
- if (earlyDefinitionMismatch) {
7200
+ if (earlyRecoverableSetup) {
7125
7201
  control.activityStatus = "failed";
7126
7202
  control.turnMetricOutcome = "failed";
7127
7203
  control.activityError = error51;
7128
7204
  throw ApplicationFailure.create({
7129
- message: `${error51.message}. Automatic same-turn configuration recovery exhausted after ${recoveryResult.providerRecoveryCount} retries.`,
7130
- type: "TurnExecutionPolicyDefinitionMismatchError",
7205
+ message: earlyDefinitionMismatch ? `${error51.message}. Automatic same-turn configuration recovery exhausted after ${recoveryResult.providerRecoveryCount} retries.` : failure2.error,
7206
+ type: earlyDefinitionMismatch ? "TurnExecutionPolicyDefinitionMismatchError" : "SandboxCommandStartUnavailableError",
7131
7207
  nonRetryable: true
7132
7208
  });
7133
7209
  }
@@ -7216,6 +7292,9 @@ async function settleTurnFailure(deps) {
7216
7292
  // src/activities/agent-turn/stream-attempt.ts
7217
7293
  import { measureMcpPhase, withMcpCallIdentity } from "@opengeni/observability";
7218
7294
  import {
7295
+ appendSessionHistoryItems as appendSessionHistoryItems3,
7296
+ sessionTurnFinalReplyFacts,
7297
+ sessionTurnHasFinalReplyNudge,
7219
7298
  getSessionHumanInputRequest,
7220
7299
  getWorkspace,
7221
7300
  recordUsageEvent as recordUsageEvent3,
@@ -7665,7 +7744,8 @@ async function resolveTurnSandboxAccess(sandboxState, sdkOwnedSandboxSession, un
7665
7744
  // src/activities/open-suffix-resume.ts
7666
7745
  import {
7667
7746
  OPEN_SUFFIX_RUN_STATE_BLOB,
7668
- approvalIdentifier
7747
+ approvalIdentifier,
7748
+ withPublicApprovalFields
7669
7749
  } from "@opengeni/contracts";
7670
7750
  import {
7671
7751
  appendSessionHistoryItems as appendSessionHistoryItems2,
@@ -7931,7 +8011,7 @@ async function settleOpenSuffixResumeIfNeeded(input) {
7931
8011
  ...requiresActionApprovals.length > 0 ? [
7932
8012
  {
7933
8013
  type: "session.requiresAction",
7934
- payload: { approvals: requiresActionApprovals }
8014
+ payload: { approvals: requiresActionApprovals.map(withPublicApprovalFields) }
7935
8015
  }
7936
8016
  ] : [],
7937
8017
  {
@@ -7969,9 +8049,85 @@ function interruptionCallIdsFromPause(input) {
7969
8049
  // src/activities/agent-turn/stream-attempt.ts
7970
8050
  import {
7971
8051
  OPEN_SUFFIX_RUN_STATE_BLOB as OPEN_SUFFIX_RUN_STATE_BLOB2,
7972
- resolveWorkspaceAgentHumanInputEnabled
8052
+ resolveWorkspaceAgentHumanInputEnabled,
8053
+ withPublicApprovalFields as withPublicApprovalFields2
7973
8054
  } from "@opengeni/contracts";
7974
8055
 
8056
+ // src/activities/agent-turn/model-call-admission.ts
8057
+ function createModelCallAdmission(input) {
8058
+ const receipts = /* @__PURE__ */ new WeakMap();
8059
+ const responseReceipts = /* @__PURE__ */ new Map();
8060
+ const pending = /* @__PURE__ */ new Set();
8061
+ const responseId = (event) => {
8062
+ const id = event?.response?.id;
8063
+ return typeof id === "string" && id.length > 0 ? id : null;
8064
+ };
8065
+ let latest = Promise.resolve();
8066
+ let failure2;
8067
+ let failed = false;
8068
+ let rejectHalted;
8069
+ const halted = new Promise((_, reject) => {
8070
+ rejectHalted = reject;
8071
+ });
8072
+ void halted.catch(() => void 0);
8073
+ const fail = (error51) => {
8074
+ if (failed) return;
8075
+ failed = true;
8076
+ failure2 = error51;
8077
+ rejectHalted(error51);
8078
+ for (const receipt of pending) receipt.reject(error51);
8079
+ pending.clear();
8080
+ responseReceipts.clear();
8081
+ };
8082
+ const abort = () => fail(input.signal.reason ?? new Error("Model stream cancelled"));
8083
+ input.signal.addEventListener("abort", abort, { once: true });
8084
+ if (input.signal.aborted) abort();
8085
+ return {
8086
+ beforeModelRequest: async () => {
8087
+ await Promise.race([latest, halted]);
8088
+ if (failed) throw failure2;
8089
+ await Promise.race([input.admit(), halted]);
8090
+ if (failed) throw failure2;
8091
+ },
8092
+ onModelResponse: (event) => {
8093
+ if (failed) return Promise.reject(failure2);
8094
+ const id = responseId(event);
8095
+ const existing = receipts.get(event) ?? (id ? responseReceipts.get(id) : void 0) ?? (!id && pending.size === 1 ? pending.values().next().value : void 0);
8096
+ if (existing) return existing.promise;
8097
+ let resolve;
8098
+ let reject;
8099
+ const promise2 = new Promise((yes, no) => {
8100
+ resolve = yes;
8101
+ reject = no;
8102
+ });
8103
+ void promise2.catch(() => void 0);
8104
+ const receipt = { promise: promise2, resolve, reject };
8105
+ receipts.set(event, receipt);
8106
+ if (id) responseReceipts.set(id, receipt);
8107
+ pending.add(receipt);
8108
+ latest = promise2;
8109
+ return promise2;
8110
+ },
8111
+ settle: (event) => {
8112
+ if (event.type !== "raw_model_stream_event" || !event.data || typeof event.data !== "object")
8113
+ return;
8114
+ if (event.data.type !== "response_done") return;
8115
+ const id = responseId(event.data);
8116
+ const receipt = receipts.get(event.data) ?? (id ? responseReceipts.get(id) : void 0) ?? (pending.size === 1 ? pending.values().next().value : void 0);
8117
+ if (!receipt) return;
8118
+ pending.delete(receipt);
8119
+ if (id) responseReceipts.delete(id);
8120
+ receipt.resolve();
8121
+ },
8122
+ fail,
8123
+ close: () => {
8124
+ input.signal.removeEventListener("abort", abort);
8125
+ if (pending.size > 0) fail(new Error("Model stream closed before response settlement"));
8126
+ responseReceipts.clear();
8127
+ }
8128
+ };
8129
+ }
8130
+
7975
8131
  // src/activities/agent-turn/session-title.ts
7976
8132
  import { hasPermission } from "@opengeni/core";
7977
8133
  import { isManagedOpenRouterFreeRoute } from "@opengeni/config";
@@ -7986,7 +8142,7 @@ function shouldRequestMissingSessionTitle(input) {
7986
8142
  const title = input.title?.trim() ?? "";
7987
8143
  const needsSemanticTitle = input.titleSource !== "user" && (!title || title === AUTOMATIC_SESSION_TITLE_FALLBACK);
7988
8144
  if (!needsSemanticTitle) return false;
7989
- if (!input.firstPartyMcpTools.includes("set_session_title")) return false;
8145
+ if (!input.agentConfig && !input.firstPartyMcpTools.includes("set_session_title")) return false;
7990
8146
  const permissions = input.firstPartyMcpPermissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS;
7991
8147
  return hasPermission([...permissions], "sessions:control");
7992
8148
  }
@@ -7994,7 +8150,7 @@ function routeAllowsSessionTitleRequests(resolvedModel) {
7994
8150
  return !resolvedModel || !isManagedOpenRouterFreeRoute(resolvedModel);
7995
8151
  }
7996
8152
  function sessionTitleToolPlan(input) {
7997
- const titleToolAvailable = input.shouldRequestTitle && input.tools.some((tool) => tool.kind === "mcp" && tool.id === "opengeni");
8153
+ const titleToolAvailable = input.shouldRequestTitle && (input.agentConfig != null || input.tools.some((tool) => tool.kind === "mcp" && tool.id === "opengeni"));
7998
8154
  const titleRequestAllowed = titleToolAvailable && input.routeAllowsTitleRequests;
7999
8155
  const generateTitleInParallel = titleRequestAllowed && input.parallelGenerationAvailable;
8000
8156
  const promoteTitleTool = titleRequestAllowed && !generateTitleInParallel;
@@ -8301,6 +8457,24 @@ function createSharedRigSetupCoordinator(options) {
8301
8457
  };
8302
8458
  }
8303
8459
 
8460
+ // src/activities/agent-turn/final-reply.ts
8461
+ function finalReplyNudge(turnId) {
8462
+ return {
8463
+ type: "message",
8464
+ role: "developer",
8465
+ content: [
8466
+ {
8467
+ type: "input_text",
8468
+ text: `[Runtime final-reply handoff for turn ${turnId}]
8469
+ Your final user-facing answer was empty. Reply to the user now with the requested deliverable or a concise outcome and its retained artifact link. Goal evidence is a short ledger proof, not the answer, and goal_complete does not deliver a chat reply. Use the completed work and tool results already in history; do not repeat completed tools or restart the task. If this is a late child result after goal completion, integrate material new findings and deliver any still-missing handoff. Do not change the completed goal or invent a result.`
8470
+ }
8471
+ ]
8472
+ };
8473
+ }
8474
+ function needsFinalReply(input) {
8475
+ return typeof input.output === "string" && input.output.trim().length === 0 && !input.inputWaitYielded && !input.interrupted && !input.maintenance && (input.toolsExecuted || input.completedGoal);
8476
+ }
8477
+
8304
8478
  // src/activities/agent-turn/stream-attempt.ts
8305
8479
  async function attachPendingUpdatesBeforePreparingModelInput(input) {
8306
8480
  if (input.shouldAttach && !await input.attachPendingUpdates()) return false;
@@ -8567,12 +8741,33 @@ async function runTurnStreamAttempt(deps) {
8567
8741
  const modelResponseState = createModelResponseEventState(claimedModelUsageSourceKeys);
8568
8742
  let latestAssistantMessageText = null;
8569
8743
  let workerPreparationTotalRecorded = false;
8744
+ let toolsExecuted = false;
8745
+ let finalReplyNudged = false;
8746
+ const revalidateModelCallAdmission = async () => {
8747
+ await historySink.reconcileConversationTruth({ requireDurable: true });
8748
+ await ensureRunAllowedBetweenModelCalls({
8749
+ settings,
8750
+ db,
8751
+ accountId: input.accountId,
8752
+ workspaceId: input.workspaceId,
8753
+ isExternallyBilledTurn: billingState.isExternallyBilledTurn,
8754
+ entitlements,
8755
+ chargesOpenGeniCredits: billingState.chargesOpenGeniCredits,
8756
+ countsTowardTokenCap: billingState.countsTowardTokenCap,
8757
+ initiatingHumanSubjectId: turn.initiatingHumanSubjectId
8758
+ });
8759
+ };
8570
8760
  const runStreamAttempt = async (options) => {
8571
8761
  if (!runInput) {
8572
8762
  throw new Error("Run input was not prepared");
8573
8763
  }
8574
- const responseCountBeforeStream = modelResponseState.responseCount;
8575
8764
  eventing.stream = void 0;
8765
+ if (options.requireTerminalModelResponse) await revalidateModelCallAdmission();
8766
+ const modelCallAdmission = createModelCallAdmission({
8767
+ signal: runtimeCancellationSignal,
8768
+ admit: revalidateModelCallAdmission
8769
+ });
8770
+ const responseCountBeforeStream = modelResponseState.responseCount;
8576
8771
  eventing.batcher = null;
8577
8772
  let currentToolBatchCallIds = /* @__PURE__ */ new Set();
8578
8773
  let currentToolBatchCompletedCallIds = /* @__PURE__ */ new Set();
@@ -8740,6 +8935,8 @@ async function runTurnStreamAttempt(deps) {
8740
8935
  }
8741
8936
  attempt.modelRequestStarted = true;
8742
8937
  return await runtime.runStream(agent, runInput, eventing.modelRunSettings, {
8938
+ beforeModelRequest: modelCallAdmission.beforeModelRequest,
8939
+ onModelResponse: modelCallAdmission.onModelResponse,
8743
8940
  signal: runtimeCancellationSignal,
8744
8941
  sandboxEnvironment,
8745
8942
  onModelVisibleContext: async (snapshot) => {
@@ -8846,7 +9043,13 @@ async function runTurnStreamAttempt(deps) {
8846
9043
  if (leases.xai.lost) {
8847
9044
  throw new Error("xAI credential lease expired before the model run");
8848
9045
  }
8849
- eventing.stream = await withProviderRequestContext(runStreamOnce);
9046
+ try {
9047
+ eventing.stream = await withProviderRequestContext(runStreamOnce);
9048
+ } catch (error51) {
9049
+ modelCallAdmission.fail(error51);
9050
+ modelCallAdmission.close();
9051
+ throw error51;
9052
+ }
8850
9053
  const streamTiming = new StreamTimingMetrics(observability, {
8851
9054
  provider: streamProvider
8852
9055
  });
@@ -8985,32 +9188,20 @@ async function runTurnStreamAttempt(deps) {
8985
9188
  await historySink.reconcileConversationTruth();
8986
9189
  turnLifecycleMetricsFor(observability).progress({ attemptId: input.attemptId });
8987
9190
  modelCheckpointMemoryCollector.schedule(observability);
8988
- try {
8989
- await ensureRunAllowed(
8990
- settings,
8991
- db,
8992
- input.accountId,
8993
- input.workspaceId,
8994
- billingState.isExternallyBilledTurn,
8995
- entitlements,
8996
- billingState.chargesOpenGeniCredits,
8997
- billingState.countsTowardTokenCap
8998
- );
8999
- } catch (limitError) {
9000
- let serializedRunState = null;
9001
- try {
9002
- serializedRunState = media.compactMediaRunState(
9003
- String(eventing.stream.state.toString())
9004
- );
9005
- } catch {
9006
- serializedRunState = null;
9007
- }
9008
- throw new BudgetExhaustedError(
9009
- limitError instanceof Error ? limitError.message : String(limitError),
9010
- serializedRunState
9011
- );
9012
- }
9191
+ await ensureRunAllowedBetweenModelCalls({
9192
+ settings,
9193
+ db,
9194
+ accountId: input.accountId,
9195
+ workspaceId: input.workspaceId,
9196
+ isExternallyBilledTurn: billingState.isExternallyBilledTurn,
9197
+ entitlements,
9198
+ chargesOpenGeniCredits: billingState.chargesOpenGeniCredits,
9199
+ countsTowardTokenCap: billingState.countsTowardTokenCap,
9200
+ initiatingHumanSubjectId: turn.initiatingHumanSubjectId,
9201
+ serializedRunState: () => media.compactMediaRunState(String(eventing.stream.state.toString()))
9202
+ });
9013
9203
  }
9204
+ modelCallAdmission.settle(next.value);
9014
9205
  const durableSdkEvent = generatedImageReceipt ? compactGeneratedImageSdkEvent(next.value, generatedImageReceipt) : next.value;
9015
9206
  const pendingToolCall = pendingToolCallFromSdkEvent(durableSdkEvent);
9016
9207
  if (pendingToolCall) {
@@ -9044,6 +9235,7 @@ async function runTurnStreamAttempt(deps) {
9044
9235
  }
9045
9236
  const completedToolCall = completedToolCallFromSdkEvent(durableSdkEvent);
9046
9237
  if (completedToolCall) {
9238
+ toolsExecuted = true;
9047
9239
  retainedScreenshotMetadata = media.retainedScreenshotReceiptsByCallId.get(completedToolCall.callId) ?? null;
9048
9240
  const typedScreenshot = retainedScreenshotMetadata ? null : typedScreenshotFromSdkEvent(durableSdkEvent);
9049
9241
  if (!retainedScreenshotMetadata && media.retainedSessionImageCallIds.has(completedToolCall.callId) && sdkEventContainsInlineImage(durableSdkEvent) && !typedScreenshot) {
@@ -9225,6 +9417,7 @@ async function runTurnStreamAttempt(deps) {
9225
9417
  }
9226
9418
  }
9227
9419
  } catch (error51) {
9420
+ modelCallAdmission.fail(error51);
9228
9421
  closeStreamWaitAdmission?.();
9229
9422
  if (fallbackProviderRequestLifecycleStartedAt !== null) {
9230
9423
  const durationMs = Math.max(
@@ -9274,6 +9467,7 @@ async function runTurnStreamAttempt(deps) {
9274
9467
  }
9275
9468
  throw error51;
9276
9469
  } finally {
9470
+ modelCallAdmission.close();
9277
9471
  if (!streamDone) {
9278
9472
  void iterator.return?.().catch(() => void 0);
9279
9473
  }
@@ -9284,7 +9478,7 @@ async function runTurnStreamAttempt(deps) {
9284
9478
  temporalCancellationSignal: cancellationSignal,
9285
9479
  runtimeCancellationSignal
9286
9480
  });
9287
- await eventing.preparedTools?.inputWaitYield?.sealForSettlement(runtimeCancellationSignal);
9481
+ await eventing.preparedTools?.inputWaitYield?.drainForHandoff(runtimeCancellationSignal);
9288
9482
  if (options.requireTerminalModelResponse && !eventing.preparedTools?.inputWaitYield?.yielded && eventing.stream.interruptions.length === 0 && modelResponseState.responseCount === responseCountBeforeStream) {
9289
9483
  throwIfWorkerShuttingDown();
9290
9484
  throwIfTurnCancelled();
@@ -9380,6 +9574,7 @@ async function runTurnStreamAttempt(deps) {
9380
9574
  }
9381
9575
  }
9382
9576
  if (eventing.stream.interruptions.length > 0) {
9577
+ await eventing.preparedTools?.inputWaitYield?.sealForSettlement(runtimeCancellationSignal);
9383
9578
  await historySink.reconcileConversationTruth({ requireDurable: true });
9384
9579
  const approvals = runtime.serializeApprovals(eventing.stream.interruptions);
9385
9580
  const humanInputInterruptions = runtime.serializeHumanInputRequests?.(eventing.stream.interruptions) ?? [];
@@ -9487,9 +9682,11 @@ async function runTurnStreamAttempt(deps) {
9487
9682
  type: "session.requiresAction",
9488
9683
  payload: {
9489
9684
  approvals: approvals.map(
9490
- (approval) => withMcpToolDisplayMetadata(
9491
- eventing.preparedTools?.mcpServers ?? [],
9492
- approval
9685
+ (approval) => withPublicApprovalFields2(
9686
+ withMcpToolDisplayMetadata(
9687
+ eventing.preparedTools?.mcpServers ?? [],
9688
+ approval
9689
+ )
9493
9690
  )
9494
9691
  )
9495
9692
  }
@@ -9520,6 +9717,48 @@ async function runTurnStreamAttempt(deps) {
9520
9717
  const finalOutput = String(
9521
9718
  requireAgentStreamFinalOutput(eventing.stream.finalOutput, inputWaitYielded)
9522
9719
  );
9720
+ const durableReplyFacts = !inputWaitYielded && finalOutput.trim().length === 0 ? await sessionTurnFinalReplyFacts(db, input.workspaceId, input.sessionId, activeTurnId) : { toolsExecuted: false, completedGoal: false };
9721
+ let emptyFinalReply = false;
9722
+ if (needsFinalReply({
9723
+ output: finalOutput,
9724
+ inputWaitYielded: inputWaitYielded || eventing.preparedTools?.inputWaitYield?.requested === true,
9725
+ interrupted: false,
9726
+ // interruption settlement returned above
9727
+ maintenance: turn.source === "compaction",
9728
+ toolsExecuted: toolsExecuted || finalReplyNudged || durableReplyFacts.toolsExecuted,
9729
+ completedGoal: durableReplyFacts.completedGoal
9730
+ })) {
9731
+ await historySink.reconcileConversationTruth({ requireDurable: true });
9732
+ if (finalReplyNudged || await sessionTurnHasFinalReplyNudge(
9733
+ db,
9734
+ input.workspaceId,
9735
+ input.sessionId,
9736
+ activeTurnId,
9737
+ finalReplyNudge(activeTurnId).content[0].text
9738
+ )) {
9739
+ emptyFinalReply = true;
9740
+ } else {
9741
+ const appended = await appendSessionHistoryItems3(db, {
9742
+ accountId: input.accountId,
9743
+ workspaceId: input.workspaceId,
9744
+ sessionId: input.sessionId,
9745
+ turnId: activeTurnId,
9746
+ expectedExecutionGeneration: attempt.executionGeneration,
9747
+ expectedAttemptId: input.attemptId,
9748
+ items: [
9749
+ {
9750
+ position: await nextSessionHistoryPosition2(db, input.workspaceId, input.sessionId),
9751
+ item: finalReplyNudge(activeTurnId)
9752
+ }
9753
+ ]
9754
+ });
9755
+ if (!appended) throw new TurnAttemptFencedError("turn ended before final reply handoff");
9756
+ finalReplyNudged = true;
9757
+ await prepareRunAttemptInput();
9758
+ return "reply_nudge";
9759
+ }
9760
+ }
9761
+ await eventing.preparedTools?.inputWaitYield?.sealForSettlement(runtimeCancellationSignal);
9523
9762
  const finalOutputAlreadyCompleted = latestStreamedAssistantText === finalOutput;
9524
9763
  const reply = await inputWaitReply({
9525
9764
  inputWaitYielded,
@@ -9539,7 +9778,11 @@ async function runTurnStreamAttempt(deps) {
9539
9778
  ...inputWaitYielded || finalOutputAlreadyCompleted ? [] : [{ type: "agent.message.completed", payload: { text: finalOutput } }],
9540
9779
  {
9541
9780
  type: "turn.completed",
9542
- payload: { output: finalOutput, ...reply === null ? {} : { reply } }
9781
+ payload: {
9782
+ output: finalOutput,
9783
+ ...reply === null ? {} : { reply },
9784
+ ...emptyFinalReply ? { emptyFinalReply: true } : {}
9785
+ }
9543
9786
  },
9544
9787
  { type: "session.status.changed", payload: { status: "idle" } }
9545
9788
  ],
@@ -9595,6 +9838,7 @@ async function runTurnStreamAttempt(deps) {
9595
9838
  })) {
9596
9839
  return claimedResult({ status: "cancelled" });
9597
9840
  }
9841
+ await revalidateModelCallAdmission();
9598
9842
  if (turn.source !== "compaction" && generateSessionTitleInParallel && runtime.generateSessionTitle && !runtimeCancellationSignal.aborted) {
9599
9843
  const serviceTier = serviceTierForLatencyMode(
9600
9844
  turnExecutionPolicy.providerId,
@@ -9626,11 +9870,19 @@ async function runTurnStreamAttempt(deps) {
9626
9870
  }
9627
9871
  try {
9628
9872
  let retriedAfterCompaction = false;
9873
+ finalReplyNudged = await sessionTurnHasFinalReplyNudge(
9874
+ db,
9875
+ input.workspaceId,
9876
+ input.sessionId,
9877
+ activeTurnId,
9878
+ finalReplyNudge(activeTurnId).content[0].text
9879
+ );
9629
9880
  while (true) {
9630
9881
  try {
9631
9882
  const result = await runStreamAttempt({
9632
9883
  requireTerminalModelResponse: retriedAfterCompaction
9633
9884
  });
9885
+ if (result === "reply_nudge") continue;
9634
9886
  if (retriedAfterCompaction) {
9635
9887
  observability.info("context compaction recovery succeeded after in-activity retry", {
9636
9888
  sessionId: input.sessionId,
@@ -9841,7 +10093,9 @@ import {
9841
10093
  loadOrganizationModelProviderApiKey,
9842
10094
  listWorkspaceProviderCustomModels,
9843
10095
  getWorkspaceProviderCustomModelForExecution,
9844
- loadWorkspaceProviderApiKey
10096
+ loadWorkspaceProviderApiKey,
10097
+ loadClaudeSubscriptionUsageCredential,
10098
+ assertModelConnectionAllowsTurn
9845
10099
  } from "@opengeni/db";
9846
10100
  async function settingsWithSessionMcpServersForRun(db, workspaceId, sessionId, attemptId, settings, options) {
9847
10101
  const encryptionKey = environmentsEncryptionKeyBytes(settings);
@@ -9999,12 +10253,24 @@ async function settingsWithOrganizationProviderCredentials(db, accountId, worksp
9999
10253
  if (kind === "claude_subscription" && !settings.claudeSubscriptionEnabled) continue;
10000
10254
  const models = await buildModels(kind, claudeProviderId(kind) + "/");
10001
10255
  result = withClaudeConnectionCatalog(result, { [kind]: { models } });
10002
- const credential = await loadOrganizationModelProviderApiKey(db, settings, {
10256
+ const binding = kind === "claude_subscription" ? await loadClaudeSubscriptionUsageCredential(db, settings, {
10257
+ accountId,
10258
+ workspaceId,
10259
+ scope: "organization"
10260
+ }) : null;
10261
+ const credential = kind === "claude_subscription" ? binding?.serializedCredential : await loadOrganizationModelProviderApiKey(db, settings, {
10003
10262
  accountId,
10004
10263
  workspaceId,
10005
10264
  providerKind: kind
10006
10265
  });
10007
- if (credential) result = withClaudeConnectionCredential(result, kind, credential);
10266
+ if (credential)
10267
+ result = withClaudeConnectionCredential(
10268
+ result,
10269
+ kind,
10270
+ credential,
10271
+ "organization",
10272
+ binding ?? void 0
10273
+ );
10008
10274
  const workspaceModels = await listWorkspaceProviderCustomModels(db, {
10009
10275
  accountId,
10010
10276
  workspaceId,
@@ -10027,15 +10293,27 @@ async function settingsWithOrganizationProviderCredentials(db, accountId, worksp
10027
10293
  { [kind]: { models: workspaceModels } },
10028
10294
  "workspace"
10029
10295
  );
10030
- const workspaceCredential = await loadWorkspaceProviderApiKey(
10031
- db,
10032
- settings,
10296
+ const workspaceBinding = kind === "claude_subscription" ? await loadClaudeSubscriptionUsageCredential(db, settings, {
10297
+ accountId,
10033
10298
  workspaceId,
10034
- kind,
10035
- workspaceModelId
10036
- );
10299
+ scope: "workspace"
10300
+ }) : null;
10301
+ if (workspaceBinding && workspaceModelId)
10302
+ await assertModelConnectionAllowsTurn(db, {
10303
+ workspaceId,
10304
+ subjectId: "worker:model-access",
10305
+ modelId: workspaceModelId,
10306
+ workspaceProviderConnectionId: workspaceBinding.connectionId
10307
+ });
10308
+ const workspaceCredential = kind === "claude_subscription" ? workspaceBinding?.serializedCredential : await loadWorkspaceProviderApiKey(db, settings, workspaceId, kind, workspaceModelId);
10037
10309
  if (workspaceCredential)
10038
- result = withClaudeConnectionCredential(result, kind, workspaceCredential, "workspace");
10310
+ result = withClaudeConnectionCredential(
10311
+ result,
10312
+ kind,
10313
+ workspaceCredential,
10314
+ "workspace",
10315
+ workspaceBinding ?? void 0
10316
+ );
10039
10317
  }
10040
10318
  return result;
10041
10319
  }
@@ -10253,20 +10531,27 @@ async function claimTurnAttempt(deps) {
10253
10531
  return attached.action === "claimed" && attached.turn.id === turn.id;
10254
10532
  };
10255
10533
  turnLifecycleMetricsFor(observability).start({ attemptId: input.attemptId });
10256
- await waitForTurnOperation(
10257
- ensureRunAllowed(
10258
- capabilitySettings,
10259
- db,
10260
- input.accountId,
10261
- input.workspaceId,
10262
- billingState.isExternallyBilledTurn,
10263
- entitlements,
10264
- billingState.chargesOpenGeniCredits,
10265
- billingState.countsTowardTokenCap
10266
- ),
10267
- cancellationSignal,
10268
- void 0
10269
- );
10534
+ let allowanceRefusal = null;
10535
+ try {
10536
+ await waitForTurnOperation(
10537
+ ensureRunAllowed(
10538
+ capabilitySettings,
10539
+ db,
10540
+ input.accountId,
10541
+ input.workspaceId,
10542
+ billingState.isExternallyBilledTurn,
10543
+ entitlements,
10544
+ billingState.chargesOpenGeniCredits,
10545
+ billingState.countsTowardTokenCap,
10546
+ turn.initiatingHumanSubjectId
10547
+ ),
10548
+ cancellationSignal,
10549
+ void 0
10550
+ );
10551
+ } catch (error51) {
10552
+ if (!(error51 instanceof AllowanceExhaustedError)) throw error51;
10553
+ allowanceRefusal = error51.refusal;
10554
+ }
10270
10555
  const throwIfWorkerShuttingDown = () => {
10271
10556
  const reason = activityContext?.cancellationSignal.reason;
10272
10557
  if (isWorkerShutdownCancellation(reason)) {
@@ -10371,6 +10656,7 @@ async function claimTurnAttempt(deps) {
10371
10656
  sessionStatus: inputSettlement.sessionStatus,
10372
10657
  activeTurnId: inputSettlement.activeTurnId,
10373
10658
  ...inputSettlement.suppressGoalContinuation !== void 0 ? { suppressGoalContinuation: inputSettlement.suppressGoalContinuation } : {},
10659
+ ...inputSettlement.allowanceGoalPause ? { allowanceGoalPause: inputSettlement.allowanceGoalPause } : {},
10374
10660
  events: inputs,
10375
10661
  ...runState ? { runState } : {},
10376
10662
  ...compactionRequestFailure ? { compactionRequestFailure } : {}
@@ -10418,6 +10704,31 @@ async function claimTurnAttempt(deps) {
10418
10704
  durationSeconds: (performance.now() - activityStarted) / 1e3
10419
10705
  });
10420
10706
  const turnStartSettlementStartedAt = performance.now();
10707
+ if (allowanceRefusal) {
10708
+ if (!await eventing.settle({
10709
+ events: [
10710
+ { type: "usage.exhausted", payload: allowanceRefusal },
10711
+ {
10712
+ type: "turn.completed",
10713
+ payload: {
10714
+ output: "",
10715
+ segmentLimit: "budget_exhausted",
10716
+ ...allowanceRefusal
10717
+ }
10718
+ },
10719
+ { type: "session.status.changed", payload: { status: "idle" } }
10720
+ ],
10721
+ turnStatus: "completed",
10722
+ sessionStatus: "idle",
10723
+ activeTurnId: null,
10724
+ allowanceGoalPause: { rationale: allowanceRefusal.message }
10725
+ })) {
10726
+ return { exit: claimedResult({ status: "cancelled" }) };
10727
+ }
10728
+ control.turnMetricOutcome = "completed";
10729
+ control.activityStatus = "idle";
10730
+ return { exit: claimedResult({ status: "idle" }) };
10731
+ }
10421
10732
  if (!await eventing.settle({
10422
10733
  events: [
10423
10734
  { type: "session.status.changed", payload: { status: "running" } },
@@ -11431,7 +11742,8 @@ import { retryWhileMissing as retryWhileMissing3 } from "@opengeni/storage";
11431
11742
  import { WorkspaceModelPolicyBlockedError } from "@opengeni/runtime";
11432
11743
  import {
11433
11744
  evaluateWorkspaceModelPolicy,
11434
- resolveWorkspaceAgentHumanInputEnabled as resolveWorkspaceAgentHumanInputEnabled2
11745
+ resolveWorkspaceAgentHumanInputEnabled as resolveWorkspaceAgentHumanInputEnabled2,
11746
+ resolveWorkspaceDefaultAgentIdentity
11435
11747
  } from "@opengeni/contracts";
11436
11748
  import { codeSearchEnabledForTurn } from "@opengeni/contracts/code-search";
11437
11749
 
@@ -11822,6 +12134,10 @@ async function prepareGovernanceAndModel(deps) {
11822
12134
  );
11823
12135
  const contextSelection = await resolveCompanyBrainContextSelection(db, governanceClaims);
11824
12136
  const workspaceAgentInstructions = contextSelection.legacyWorkspaceInstructions;
12137
+ const workspaceAgentIdentity = resolveWorkspaceDefaultAgentIdentity(
12138
+ workspace.settings,
12139
+ workspaceAgentInstructions
12140
+ ).identity;
11825
12141
  const memoryPromptMode = contextSelection.receipt.memoryPromptMode;
11826
12142
  assertWorkspaceHumanInputAllowed(agentHumanInputEnabled, "resume", humanInputResume !== null);
11827
12143
  const companyProfileIncluded = contextSelection.receipt.companyProfileIncluded;
@@ -11981,6 +12297,7 @@ async function prepareGovernanceAndModel(deps) {
11981
12297
  agentHumanInputEnabled,
11982
12298
  codeSearchEnabled,
11983
12299
  workspaceAgentInstructions,
12300
+ workspaceAgentIdentity,
11984
12301
  workspaceGovernance,
11985
12302
  structuredWorkspacePolicyActive,
11986
12303
  workspaceMemory,
@@ -12008,7 +12325,9 @@ import {
12008
12325
  appendSessionInstructions,
12009
12326
  appendWorkspaceGovernance,
12010
12327
  appendWorkspaceMemory,
12328
+ agentPromptResourcesFor,
12011
12329
  composeAgentInstructions,
12330
+ inspectPersistentAgentInstructions,
12012
12331
  requestRemoteCompactionV2,
12013
12332
  preparedCompactionRequest,
12014
12333
  queuePreparedCompaction,
@@ -12018,6 +12337,39 @@ import {
12018
12337
  estimateSerializedValueTokens,
12019
12338
  compactionSummaryOutputTokens as compactionSummaryOutputTokens2
12020
12339
  } from "@opengeni/runtime";
12340
+ function standaloneCompactionInstructions(input) {
12341
+ if (input.session.agent) {
12342
+ return inspectPersistentAgentInstructions(input.settings, {
12343
+ agentConfig: input.session.agent,
12344
+ ...input.workspaceAgentIdentity ? { workspaceAgentIdentity: input.workspaceAgentIdentity } : {},
12345
+ ...input.workspaceGovernance ? { workspaceGovernance: input.workspaceGovernance } : {},
12346
+ ...input.workspaceMemory ? { workspaceMemory: input.workspaceMemory } : {},
12347
+ ...input.session.instructions ? { sessionInstructions: input.session.instructions } : {},
12348
+ ...input.rig ? { rig: input.rig } : {},
12349
+ // Session-level facts only: per-turn attachments do not apply to a
12350
+ // standalone compaction request.
12351
+ agentPromptResources: agentPromptResourcesFor(
12352
+ input.settings,
12353
+ input.session.resources.filter((resource) => resource.kind !== "file"),
12354
+ input.rig ? { rig: input.rig } : {}
12355
+ )
12356
+ }).composed;
12357
+ }
12358
+ return appendWorkspaceMemory(
12359
+ appendSessionInstructions(
12360
+ appendWorkspaceGovernance(
12361
+ composeAgentInstructions(
12362
+ input.structuredWorkspacePolicyActive ? input.settings.agentInstructionsTemplate : input.workspaceAgentInstructions ?? input.settings.agentInstructionsTemplate,
12363
+ void 0,
12364
+ input.rig
12365
+ ),
12366
+ input.workspaceGovernance ?? void 0
12367
+ ),
12368
+ input.session.instructions ?? void 0
12369
+ ),
12370
+ input.workspaceMemory ?? void 0
12371
+ );
12372
+ }
12021
12373
  async function prepareCompaction(deps) {
12022
12374
  const {
12023
12375
  input,
@@ -12042,6 +12394,7 @@ async function prepareCompaction(deps) {
12042
12394
  turnExecutionPolicy,
12043
12395
  resolvedModel,
12044
12396
  workspaceAgentInstructions,
12397
+ workspaceAgentIdentity,
12045
12398
  workspaceGovernance,
12046
12399
  structuredWorkspacePolicyActive,
12047
12400
  workspaceMemory,
@@ -12185,20 +12538,16 @@ async function prepareCompaction(deps) {
12185
12538
  return claimedResult({ status: "idle" });
12186
12539
  };
12187
12540
  if (compactionOnlyTurn && !remoteV2CompactionNeedsAgentPrefix && !portableResponsesNeedsAgentPrefix) {
12188
- const compactionInstructions = appendWorkspaceMemory(
12189
- appendSessionInstructions(
12190
- appendWorkspaceGovernance(
12191
- composeAgentInstructions(
12192
- structuredWorkspacePolicyActive ? eventing.modelRunSettings.agentInstructionsTemplate : workspaceAgentInstructions ?? eventing.modelRunSettings.agentInstructionsTemplate,
12193
- void 0,
12194
- rigVersion && rigName ? { name: rigName, version: rigVersion.version } : void 0
12195
- ),
12196
- workspaceGovernance ?? void 0
12197
- ),
12198
- session.instructions ?? void 0
12199
- ),
12200
- workspaceMemory ?? void 0
12201
- );
12541
+ const compactionInstructions = standaloneCompactionInstructions({
12542
+ settings: eventing.modelRunSettings,
12543
+ session,
12544
+ workspaceAgentInstructions,
12545
+ workspaceAgentIdentity,
12546
+ workspaceGovernance,
12547
+ structuredWorkspacePolicyActive,
12548
+ workspaceMemory,
12549
+ rig: rigVersion && rigName ? { name: rigName, version: rigVersion.version } : void 0
12550
+ });
12202
12551
  const requested = await isSessionCompactionRequested2(db, input.workspaceId, input.sessionId);
12203
12552
  let outcome = null;
12204
12553
  if (requested) {
@@ -13267,6 +13616,7 @@ async function resolveSandboxRoute(deps) {
13267
13616
  machinePrimary,
13268
13617
  groupBoxBackend,
13269
13618
  groupBoxImage,
13619
+ groupBoxImagePolicy: "new_creates_only",
13270
13620
  sandboxCreationBackend,
13271
13621
  effectiveRunCredentialBackend
13272
13622
  };
@@ -13306,6 +13656,7 @@ async function establishTurnSandbox(deps) {
13306
13656
  machinePrimary,
13307
13657
  groupBoxBackend,
13308
13658
  groupBoxImage,
13659
+ groupBoxImagePolicy,
13309
13660
  rigVersion,
13310
13661
  turnResources
13311
13662
  } = deps;
@@ -13560,7 +13911,8 @@ async function establishTurnSandbox(deps) {
13560
13911
  os: groupBoxOs,
13561
13912
  environment: sandboxEnvironment,
13562
13913
  ...groupBoxImage ? {
13563
- image: groupBoxImage
13914
+ image: groupBoxImage,
13915
+ imagePolicy: groupBoxImagePolicy
13564
13916
  } : {},
13565
13917
  // The lazy acquire must enforce the same frozen rig authority
13566
13918
  // as the eager acquire; otherwise a warm box for another rig
@@ -13637,16 +13989,16 @@ async function establishTurnSandbox(deps) {
13637
13989
  backend: groupBoxBackend,
13638
13990
  os: groupBoxOs,
13639
13991
  environment: sandboxEnvironment,
13640
- // IMAGE IS SHARED STATE (B3): the container image
13641
- // this run resolves. The lease stamps it + conflicts on a live shared box
13642
- // running a DIFFERENT image (solo → durable rotation; N-holders →
13643
- // SandboxImageConflictError surfaced as an actionable turn error). Select
13644
- // the image for the actual group-box backend; a configured Modal image must
13992
+ // Deployment/workspace pins select NEW creates only. The lease
13993
+ // retains a live group's image through between-turn repins, even
13994
+ // with other holders; only rotation/reaping elects a new image.
13995
+ // Select the image for the actual group-box backend; a configured Modal image must
13645
13996
  // never override a Docker run. The selfhosted branch
13646
13997
  // (establishSelfhostedTurnSession) NEVER passes
13647
13998
  // an image — B3 lives only on this managed-box branch.
13648
13999
  ...groupBoxImage ? {
13649
- image: groupBoxImage
14000
+ image: groupBoxImage,
14001
+ imagePolicy: groupBoxImagePolicy
13650
14002
  } : {},
13651
14003
  // RIG IS SHARED STATE (M3): stamp the frozen rig version so the lease
13652
14004
  // conflicts on a live shared box set up under a different rig (solo
@@ -14342,6 +14694,100 @@ import {
14342
14694
  } from "@opengeni/runtime";
14343
14695
  import { codemodeWorkspaceUrl as codemodeWorkspaceUrl2 } from "@opengeni/config";
14344
14696
 
14697
+ // src/activities/mcp-account-routes.ts
14698
+ function accountRouteAuthNeededPayload(payload, bindings) {
14699
+ const binding = bindings?.find((candidate) => candidate.serverId === payload.serverId);
14700
+ return binding ? {
14701
+ ...payload,
14702
+ canonicalServerId: binding.canonicalServerId,
14703
+ connectionSubjectScope: binding.subjectScope
14704
+ } : payload;
14705
+ }
14706
+ function expandMcpAccountRoutes(input) {
14707
+ if (input.bindings == null) {
14708
+ return { settings: input.settings, tools: input.tools, accountLabels: /* @__PURE__ */ new Map() };
14709
+ }
14710
+ const configured = new Map(input.settings.mcpServers.map((server) => [server.id, server]));
14711
+ const byCanonical = /* @__PURE__ */ new Map();
14712
+ const routes = /* @__PURE__ */ new Set();
14713
+ const accounts = /* @__PURE__ */ new Set();
14714
+ for (const binding of input.bindings) {
14715
+ const account = JSON.stringify([binding.canonicalServerId, binding.connectionId]);
14716
+ if (routes.has(binding.serverId) || accounts.has(account) || configured.has(binding.serverId) || binding.subjectScope === "subject" !== (binding.ownerSubjectId !== null) || binding.connectionRef.authoritySource === "host" || binding.connectionRef.connectionId !== binding.connectionId || binding.connectionRef.subjectScope !== binding.subjectScope || binding.connectionRef.providerDomain !== binding.providerDomain || binding.connectionRef.kind !== binding.kind) {
14717
+ throw new Error("Invalid accepted MCP account route");
14718
+ }
14719
+ routes.add(binding.serverId);
14720
+ accounts.add(account);
14721
+ const siblings = byCanonical.get(binding.canonicalServerId) ?? [];
14722
+ siblings.push(binding);
14723
+ byCanonical.set(binding.canonicalServerId, siblings);
14724
+ }
14725
+ const servers = [];
14726
+ const tools = [];
14727
+ const accountLabels = /* @__PURE__ */ new Map();
14728
+ for (const tool of input.tools) {
14729
+ const bindings = byCanonical.get(tool.id);
14730
+ if (!bindings) {
14731
+ if (!configured.get(tool.id)?.connectionRef) tools.push(tool);
14732
+ continue;
14733
+ }
14734
+ const canonical = configured.get(tool.id);
14735
+ if (!canonical?.connectionRef) {
14736
+ throw new Error("Accepted MCP account route has no canonical connection configuration");
14737
+ }
14738
+ for (const binding of bindings) {
14739
+ if (canonical.connectionRef.authoritySource === "host" || canonical.connectionRef.providerDomain !== binding.providerDomain || canonical.connectionRef.kind && canonical.connectionRef.kind !== binding.kind) {
14740
+ throw new Error("Accepted MCP account route does not match canonical provider");
14741
+ }
14742
+ const { headers: _headers, ...config2 } = canonical;
14743
+ const label = `${canonical.name ?? canonical.id} \u2014 ${binding.subjectScope === "subject" ? "Personal" : "Workspace"}: ${binding.accountLabel}`;
14744
+ servers.push({
14745
+ ...config2,
14746
+ id: binding.serverId,
14747
+ name: label,
14748
+ connectionRef: structuredClone(binding.connectionRef)
14749
+ });
14750
+ tools.push({ ...tool, id: binding.serverId });
14751
+ accountLabels.set(binding.serverId, label);
14752
+ }
14753
+ }
14754
+ return {
14755
+ settings: {
14756
+ ...input.settings,
14757
+ mcpServers: [
14758
+ ...input.settings.mcpServers.filter((server) => !server.connectionRef),
14759
+ ...servers
14760
+ ]
14761
+ },
14762
+ tools,
14763
+ accountLabels
14764
+ };
14765
+ }
14766
+ function expandApiIntegrationAccountRoutes(input) {
14767
+ const selected = new Set(input.tools.map((tool) => tool.id));
14768
+ if (input.bindings == null)
14769
+ return input.integrations.filter((item) => selected.has(item.serverId));
14770
+ const acceptedBindings = input.bindings;
14771
+ return input.integrations.flatMap((integration) => {
14772
+ const bindings = acceptedBindings.filter(
14773
+ (binding) => binding.canonicalServerId === integration.serverId
14774
+ );
14775
+ if (bindings.length === 0)
14776
+ return !integration.connectionRef && selected.has(integration.serverId) ? [integration] : [];
14777
+ return bindings.filter((binding) => selected.has(binding.serverId)).map((binding) => {
14778
+ if (binding.connectionAuthorityGeneration === void 0) {
14779
+ throw new Error("Accepted API integration account route has no authority generation");
14780
+ }
14781
+ return {
14782
+ ...integration,
14783
+ serverId: binding.serverId,
14784
+ connectionRef: structuredClone(binding.connectionRef),
14785
+ connectionAuthorityGeneration: binding.connectionAuthorityGeneration
14786
+ };
14787
+ });
14788
+ });
14789
+ }
14790
+
14345
14791
  // src/activities/git-credential-renewal.ts
14346
14792
  var GIT_CREDENTIAL_DEFAULT_REFRESH_MS = 30 * 6e4;
14347
14793
  var GIT_CREDENTIAL_EXPIRY_LEAD_MS = 5 * 6e4;
@@ -14700,6 +15146,8 @@ function credentialProviderRequestBody(input, initiatingHumanSubjectId, initiati
14700
15146
  lane: "workspace",
14701
15147
  mcpServers: []
14702
15148
  }) {
15149
+ const via = input.initiatorContext.via;
15150
+ const latestHop = Array.isArray(via) ? via.at(-1) : null;
14703
15151
  return {
14704
15152
  type: "credentials.request",
14705
15153
  lane: selection.lane,
@@ -14714,6 +15162,11 @@ function credentialProviderRequestBody(input, initiatingHumanSubjectId, initiati
14714
15162
  turnId: input.turnId,
14715
15163
  attemptId: input.attemptId,
14716
15164
  initiator: { kind: input.initiator.kind, subjectId: input.initiator.subjectId },
15165
+ initiatorContext: {
15166
+ kind: latestHop?.kind === "agent" ? "agent" : input.initiator.kind === "subject" ? "human" : "service",
15167
+ initiator: input.initiator,
15168
+ context: input.initiatorContext
15169
+ },
14717
15170
  initiatingHumanSubjectId,
14718
15171
  initiatingHuman,
14719
15172
  sandboxBackend: input.effectiveSandboxBackend,
@@ -14905,7 +15358,7 @@ async function bindRunCredentialResolver(input) {
14905
15358
  mcpServers: selectedSessionRemoteMcpTargets(
14906
15359
  input.settings,
14907
15360
  input.session.mcpServers ?? [],
14908
- input.turn.tools ?? [],
15361
+ input.effectiveTools,
14909
15362
  (input.localMcpServerIds ?? []).map((id) => ({ id }))
14910
15363
  )
14911
15364
  }
@@ -14921,22 +15374,23 @@ async function bindRunCredentialResolver(input) {
14921
15374
  workspaceId: input.workspaceId,
14922
15375
  sessionId: input.session.id
14923
15376
  };
15377
+ const frozenRequest = structuredClone(
15378
+ buildRunCredentialsRequest({
15379
+ accountId: input.accountId,
15380
+ workspaceId: input.workspaceId,
15381
+ session: input.session,
15382
+ turn: input.turn,
15383
+ attemptId: input.attemptId,
15384
+ effectiveSandboxBackend: input.effectiveSandboxBackend,
15385
+ variableSet: input.variableSet,
15386
+ rootSessionId,
15387
+ purpose: "provision",
15388
+ forceRefresh: false
15389
+ })
15390
+ );
14924
15391
  return {
14925
15392
  resolve: async ({ purpose, forceRefresh }) => {
14926
- const resolution = await resolver(
14927
- buildRunCredentialsRequest({
14928
- accountId: input.accountId,
14929
- workspaceId: input.workspaceId,
14930
- session: input.session,
14931
- turn: input.turn,
14932
- attemptId: input.attemptId,
14933
- effectiveSandboxBackend: input.effectiveSandboxBackend,
14934
- variableSet: input.variableSet,
14935
- rootSessionId,
14936
- purpose,
14937
- forceRefresh
14938
- })
14939
- );
15393
+ const resolution = await resolver({ ...frozenRequest, purpose, forceRefresh });
14940
15394
  return normalizeRunCredentialsResolution(resolution, scope);
14941
15395
  }
14942
15396
  };
@@ -14981,13 +15435,19 @@ async function prepareRunCredentials(deps) {
14981
15435
  const platformGitResources = turnResources.filter(
14982
15436
  (resource) => resource.kind !== "repository" || repositoryHasExplicitGitConnection(resource)
14983
15437
  );
15438
+ const accountRoutes = expandMcpAccountRoutes({
15439
+ settings: runSettings,
15440
+ tools: deps.turnTools,
15441
+ bindings: turn.mcpAccountBindings
15442
+ });
14984
15443
  const runCredentialResolver = effectiveRunCredentialBackend === "none" || effectiveRunCredentialBackend === "selfhosted" ? null : await waitForTurnOperation(
14985
15444
  bindRunCredentialResolver({
14986
15445
  db,
14987
- settings: runSettings,
15446
+ settings: accountRoutes.settings,
14988
15447
  initiatingHumanSubjectId: turn.initiatingHumanSubjectId ?? null,
14989
15448
  connectionCredentials: connectionCredentials ?? null,
14990
15449
  localMcpServerIds: deps.localMcpServerIds ?? [],
15450
+ effectiveTools: accountRoutes.tools,
14991
15451
  accountId: input.accountId,
14992
15452
  workspaceId: input.workspaceId,
14993
15453
  session,
@@ -15026,9 +15486,9 @@ async function prepareRunCredentials(deps) {
15026
15486
  ) : null;
15027
15487
  const runMcpCredentials = new RunMcpCredentials(
15028
15488
  selectedSessionRemoteMcpTargets2(
15029
- runSettings,
15489
+ accountRoutes.settings,
15030
15490
  session.mcpServers ?? [],
15031
- turn.tools ?? [],
15491
+ accountRoutes.tools,
15032
15492
  (deps.localMcpServerIds ?? []).map((id) => ({ id }))
15033
15493
  ),
15034
15494
  {
@@ -16410,100 +16870,6 @@ function isGitHubApiDestination(value) {
16410
16870
  }
16411
16871
  }
16412
16872
 
16413
- // src/activities/mcp-account-routes.ts
16414
- function accountRouteAuthNeededPayload(payload, bindings) {
16415
- const binding = bindings?.find((candidate) => candidate.serverId === payload.serverId);
16416
- return binding ? {
16417
- ...payload,
16418
- canonicalServerId: binding.canonicalServerId,
16419
- connectionSubjectScope: binding.subjectScope
16420
- } : payload;
16421
- }
16422
- function expandMcpAccountRoutes(input) {
16423
- if (input.bindings == null) {
16424
- return { settings: input.settings, tools: input.tools, accountLabels: /* @__PURE__ */ new Map() };
16425
- }
16426
- const configured = new Map(input.settings.mcpServers.map((server) => [server.id, server]));
16427
- const byCanonical = /* @__PURE__ */ new Map();
16428
- const routes = /* @__PURE__ */ new Set();
16429
- const accounts = /* @__PURE__ */ new Set();
16430
- for (const binding of input.bindings) {
16431
- const account = JSON.stringify([binding.canonicalServerId, binding.connectionId]);
16432
- if (routes.has(binding.serverId) || accounts.has(account) || configured.has(binding.serverId) || binding.subjectScope === "subject" !== (binding.ownerSubjectId !== null) || binding.connectionRef.authoritySource === "host" || binding.connectionRef.connectionId !== binding.connectionId || binding.connectionRef.subjectScope !== binding.subjectScope || binding.connectionRef.providerDomain !== binding.providerDomain || binding.connectionRef.kind !== binding.kind) {
16433
- throw new Error("Invalid accepted MCP account route");
16434
- }
16435
- routes.add(binding.serverId);
16436
- accounts.add(account);
16437
- const siblings = byCanonical.get(binding.canonicalServerId) ?? [];
16438
- siblings.push(binding);
16439
- byCanonical.set(binding.canonicalServerId, siblings);
16440
- }
16441
- const servers = [];
16442
- const tools = [];
16443
- const accountLabels = /* @__PURE__ */ new Map();
16444
- for (const tool of input.tools) {
16445
- const bindings = byCanonical.get(tool.id);
16446
- if (!bindings) {
16447
- if (!configured.get(tool.id)?.connectionRef) tools.push(tool);
16448
- continue;
16449
- }
16450
- const canonical = configured.get(tool.id);
16451
- if (!canonical?.connectionRef) {
16452
- throw new Error("Accepted MCP account route has no canonical connection configuration");
16453
- }
16454
- for (const binding of bindings) {
16455
- if (canonical.connectionRef.authoritySource === "host" || canonical.connectionRef.providerDomain !== binding.providerDomain || canonical.connectionRef.kind && canonical.connectionRef.kind !== binding.kind) {
16456
- throw new Error("Accepted MCP account route does not match canonical provider");
16457
- }
16458
- const { headers: _headers, ...config2 } = canonical;
16459
- const label = `${canonical.name ?? canonical.id} \u2014 ${binding.subjectScope === "subject" ? "Personal" : "Workspace"}: ${binding.accountLabel}`;
16460
- servers.push({
16461
- ...config2,
16462
- id: binding.serverId,
16463
- name: label,
16464
- connectionRef: structuredClone(binding.connectionRef)
16465
- });
16466
- tools.push({ ...tool, id: binding.serverId });
16467
- accountLabels.set(binding.serverId, label);
16468
- }
16469
- }
16470
- return {
16471
- settings: {
16472
- ...input.settings,
16473
- mcpServers: [
16474
- ...input.settings.mcpServers.filter((server) => !server.connectionRef),
16475
- ...servers
16476
- ]
16477
- },
16478
- tools,
16479
- accountLabels
16480
- };
16481
- }
16482
- function expandApiIntegrationAccountRoutes(input) {
16483
- const selected = new Set(input.tools.map((tool) => tool.id));
16484
- if (input.bindings == null)
16485
- return input.integrations.filter((item) => selected.has(item.serverId));
16486
- const acceptedBindings = input.bindings;
16487
- return input.integrations.flatMap((integration) => {
16488
- const bindings = acceptedBindings.filter(
16489
- (binding) => binding.canonicalServerId === integration.serverId
16490
- );
16491
- if (bindings.length === 0)
16492
- return !integration.connectionRef && selected.has(integration.serverId) ? [integration] : [];
16493
- return bindings.filter((binding) => selected.has(binding.serverId)).map((binding) => {
16494
- if (binding.connectionAuthorityGeneration === void 0) {
16495
- throw new Error("Accepted API integration account route has no authority generation");
16496
- }
16497
- return {
16498
- ...integration,
16499
- serverId: binding.serverId,
16500
- connectionRef: structuredClone(binding.connectionRef),
16501
- connectionAuthorityGeneration: binding.connectionAuthorityGeneration
16502
- };
16503
- });
16504
- });
16505
- }
16506
-
16507
16873
  // src/activities/agent-turn/tool-environment.ts
16508
16874
  import { createMcpOperationPersistence } from "@opengeni/db/mcp-operations";
16509
16875
 
@@ -32257,14 +32623,17 @@ import {
32257
32623
  buildApiIntegrationMcpServers,
32258
32624
  resolveCatalogSettings as resolveCatalogSettings2,
32259
32625
  resolveWorkspaceModelSelection,
32626
+ loadWorkspaceCodexModelAvailability,
32260
32627
  withFrozenPersonalConnectionDelegations,
32261
- resolveSessionToolPolicy,
32628
+ resolveTurnToolPolicy,
32629
+ scheduledTurnMcpServerIds,
32262
32630
  hasPermission as hasPermission2
32263
32631
  } from "@opengeni/core";
32264
32632
  import { SandboxChannelAService as SandboxChannelAService3 } from "@opengeni/runtime/sandbox";
32265
32633
  import { sandboxRunAs as sandboxRunAs4 } from "@opengeni/runtime";
32266
32634
  import {
32267
- DEFAULT_FIRST_PARTY_MCP_PERMISSIONS as DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2
32635
+ DEFAULT_FIRST_PARTY_MCP_PERMISSIONS as DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2,
32636
+ resolveAgentToolFamilies
32268
32637
  } from "@opengeni/contracts";
32269
32638
 
32270
32639
  // src/activities/agent-turn/list-models.ts
@@ -33703,27 +34072,31 @@ async function prepareTurnToolPolicy(deps) {
33703
34072
  } = deps;
33704
34073
  const turnResources = mergeResourceRefs(session.resources, turn.resources);
33705
34074
  const runtimeResources = runtimeResourcesForTurn(session.resources, turn.resources);
33706
- const scheduledEffectiveMcpServerIds = (() => {
33707
- const value = turn.metadata && typeof turn.metadata === "object" && !Array.isArray(turn.metadata) ? turn.metadata.scheduledEffectiveMcpServerIds : null;
33708
- return Array.isArray(value) && value.every((id) => typeof id === "string") ? [...new Set(value)].sort() : null;
33709
- })();
33710
- const currentMcpServerIds = new Set(runSettings.mcpServers.map((server) => server.id));
33711
- const resolvedToolPolicy = resolveSessionToolPolicy({
34075
+ const resolvedToolPolicy = resolveTurnToolPolicy({
33712
34076
  toolPolicy: session.toolPolicy,
33713
- sessionTools: scheduledEffectiveMcpServerIds ? turn.tools : session.tools,
33714
- availableMcpServerIds: scheduledEffectiveMcpServerIds ? scheduledEffectiveMcpServerIds.filter((id) => currentMcpServerIds.has(id)) : [...currentMcpServerIds],
33715
- defaultMcpServerIds: scheduledEffectiveMcpServerIds ?? (session.toolPolicy.mode === "workspace_default" ? await workspaceSessionToolPolicyDefaultServerIds(
34077
+ session,
34078
+ turn,
34079
+ availableMcpServerIds: runSettings.mcpServers.map((server) => server.id),
34080
+ defaultMcpServerIds: scheduledTurnMcpServerIds(turn) === null && session.toolPolicy.mode === "workspace_default" ? await workspaceSessionToolPolicyDefaultServerIds(
33716
34081
  db,
33717
34082
  input.workspaceId,
33718
34083
  capabilitySettings,
33719
34084
  fileAuthoritySubjectId ?? void 0
33720
- ) : [])
34085
+ ) : []
33721
34086
  });
33722
34087
  const mcpAvailabilityNote = unavailableMcpOperationalContext({
33723
34088
  droppedIds: resolvedToolPolicy.effectivePolicy.droppedIds,
33724
34089
  droppedCount: resolvedToolPolicy.effectivePolicy.counts.dropped
33725
34090
  });
33726
- const effectivePolicyTools = resolvedToolPolicy.toolRefs;
34091
+ const families = resolveAgentToolFamilies(session.agent, {
34092
+ productServerIds: /* @__PURE__ */ new Set([
34093
+ ...session.mcpServers.map((server) => server.id),
34094
+ ...session.toolPolicy.mode !== "workspace_default" ? session.tools.map((tool) => tool.id) : []
34095
+ ])
34096
+ });
34097
+ const effectivePolicyTools = resolvedToolPolicy.toolRefs.filter(
34098
+ (tool) => tool.kind !== "mcp" || families.allowsMcpServer(tool.id)
34099
+ );
33727
34100
  const turnTools = withFirstPartyTools(runSettings, effectivePolicyTools);
33728
34101
  const connectionScope = {
33729
34102
  accountId: input.accountId,
@@ -34003,14 +34376,16 @@ async function prepareTurnToolRuntime(deps) {
34003
34376
  const effectiveFirstPartyPermissions = linkedAuthority ? (session.firstPartyMcpPermissions ?? DEFAULT_FIRST_PARTY_MCP_PERMISSIONS2).filter(
34004
34377
  (permission) => hasPermission2(linkedAuthority.permissions, permission)
34005
34378
  ) : session.firstPartyMcpPermissions;
34006
- const selectedFirstPartyMcpTools = allowedFirstPartyMcpToolsForSession(
34007
- runSettings,
34008
- session.firstPartyMcpTools
34379
+ const toolFamilies = resolveAgentToolFamilies(session.agent);
34380
+ const selectedFirstPartyMcpTools = toolFamilies.firstPartyTools(
34381
+ allowedFirstPartyMcpToolsForSession(runSettings, session.firstPartyMcpTools)
34009
34382
  );
34010
34383
  const titleToolPlan = sessionTitleToolPlan({
34384
+ agentConfig: session.agent,
34011
34385
  tools: turnTools,
34012
34386
  selectedFirstPartyMcpTools,
34013
34387
  shouldRequestTitle: shouldRequestMissingSessionTitle({
34388
+ agentConfig: session.agent,
34014
34389
  title: session.title,
34015
34390
  titleSource: session.titleSource,
34016
34391
  firstPartyMcpTools: selectedFirstPartyMcpTools,
@@ -34070,7 +34445,7 @@ async function prepareTurnToolRuntime(deps) {
34070
34445
  workspaceId: input.workspaceId,
34071
34446
  ...deps.fileAuthoritySubjectId ? { subjectId: deps.fileAuthoritySubjectId } : {}
34072
34447
  });
34073
- const skillCatalog = [
34448
+ const skillCatalog = toolFamilies.skills === false ? [] : [
34074
34449
  ...sharedSkillDescriptors.filter((entry) => entry.activationMode === "workspace_managed").map((entry) => ({
34075
34450
  id: entry.id,
34076
34451
  name: entry.title,
@@ -34242,6 +34617,9 @@ async function prepareTurnToolRuntime(deps) {
34242
34617
  );
34243
34618
  }
34244
34619
  });
34620
+ const attemptToolFamilies = resolveAgentToolFamilies(session.agent, {
34621
+ hasSkills: skillCatalog.length > 0
34622
+ });
34245
34623
  const attemptToolDefinitions = [
34246
34624
  ...operationReadStore ? [
34247
34625
  createOperationReadAttemptToolDefinition({
@@ -34283,7 +34661,8 @@ async function prepareTurnToolRuntime(deps) {
34283
34661
  organizationGatewayConnectionActive,
34284
34662
  organizationGatewayCustomModels,
34285
34663
  organizationOpenRouterConnectionActive,
34286
- organizationOpenRouterCustomModels
34664
+ organizationOpenRouterCustomModels,
34665
+ codexModelAvailability
34287
34666
  ] = await Promise.all([
34288
34667
  getWorkspaceConnectionModelRestrictions(
34289
34668
  db,
@@ -34326,10 +34705,12 @@ async function prepareTurnToolRuntime(deps) {
34326
34705
  accountId: input.accountId,
34327
34706
  workspaceId: input.workspaceId,
34328
34707
  providerKind: "openrouter"
34329
- })
34708
+ }),
34709
+ loadWorkspaceCodexModelAvailability(db, currentSettings, input.workspaceId)
34330
34710
  ]);
34331
34711
  return {
34332
34712
  selections: resolveWorkspaceModelSelection({
34713
+ observations: codexModelAvailability,
34333
34714
  connectionModelRestrictions,
34334
34715
  settings: currentSettings,
34335
34716
  policy,
@@ -34385,7 +34766,7 @@ async function prepareTurnToolRuntime(deps) {
34385
34766
  }),
34386
34767
  ...googleDrivePublicationTool && googleDrivePublicationAllowed ? [googleDrivePublicationTool] : [],
34387
34768
  ...codeSearchTools
34388
- ];
34769
+ ].filter((tool) => attemptToolFamilies.allowsFunctionTool(tool.modelName));
34389
34770
  recordTurnStartupPhase(observability, {
34390
34771
  phase: "tool_context_preparation",
34391
34772
  provider: turnExecutionPolicy.providerId,
@@ -34473,7 +34854,7 @@ async function prepareTurnToolRuntime(deps) {
34473
34854
  sessionAttachedRemoteMcpTargets: selectedSessionRemoteMcpTargets3(
34474
34855
  githubRestMcp.settings,
34475
34856
  session.mcpServers ?? [],
34476
- turn.tools ?? [],
34857
+ githubRestMcp.tools,
34477
34858
  localMcpServers
34478
34859
  ),
34479
34860
  ...deps.runMcpCredentials ? { runMcpCredentials: deps.runMcpCredentials } : {},
@@ -34917,7 +35298,8 @@ import {
34917
35298
  getExternalLinkTurnAuthorization as getExternalLinkTurnAuthorization2,
34918
35299
  getSessionTurnForAttempt as getSessionTurnForAttempt2,
34919
35300
  ensureSessionReasoningConfiguration,
34920
- ensureSessionSkillCatalog
35301
+ ensureSessionSkillCatalog,
35302
+ sessionHasToolRouterHistory
34921
35303
  } from "@opengeni/db";
34922
35304
  import {
34923
35305
  formatSkillCatalog,
@@ -36073,7 +36455,7 @@ function validateReferenceImage(bytes) {
36073
36455
  // src/activities/agent-turn/agent-build.ts
36074
36456
  import { SandboxChannelAService as SandboxChannelAService4 } from "@opengeni/runtime/sandbox";
36075
36457
  import { sandboxRunAs as sandboxRunAs5 } from "@opengeni/runtime";
36076
- import { VideoGenerationRejectedResult } from "@opengeni/contracts";
36458
+ import { VideoGenerationRejectedResult, resolveAgentToolFamilies as resolveAgentToolFamilies2 } from "@opengeni/contracts";
36077
36459
 
36078
36460
  // src/activities/agent-turn/video-reference-sandbox.ts
36079
36461
  async function resolveVideoReferenceSandboxAccess(request, sandboxState, sdkOwnedSandboxSession) {
@@ -36117,6 +36499,7 @@ async function buildTurnAgent(deps) {
36117
36499
  supportsImageInput,
36118
36500
  agentHumanInputEnabled,
36119
36501
  workspaceAgentInstructions,
36502
+ workspaceAgentIdentity,
36120
36503
  workspaceGovernance,
36121
36504
  structuredWorkspacePolicyActive,
36122
36505
  workspaceMemory,
@@ -36197,7 +36580,7 @@ async function buildTurnAgent(deps) {
36197
36580
  }
36198
36581
  });
36199
36582
  const imageGenerationOption = (() => {
36200
- if (!objectStorage) return {};
36583
+ if (!objectStorage || !resolveAgentToolFamilies2(session.agent).media) return {};
36201
36584
  if (nativeImageProviderBinding) {
36202
36585
  media.nativeImageGenerationRetention = {
36203
36586
  ...nativeImageProviderBinding,
@@ -36313,7 +36696,7 @@ async function buildTurnAgent(deps) {
36313
36696
  const videoGenerationPolicy = await getWorkspaceVideoGenerationPolicy2(db, input.workspaceId);
36314
36697
  const videoGenerationEnabled = videoGenerationPolicy.defaultModelId !== null && videoGenerationPolicy.enabledModelIds.length > 0;
36315
36698
  let videoGenerationCredential = null;
36316
- if (objectStorage && videoGenerationEnabled) {
36699
+ if (objectStorage && videoGenerationEnabled && resolveAgentToolFamilies2(session.agent).media) {
36317
36700
  if (videoGenerationPolicy.fundingSource === "opengeni_credits") {
36318
36701
  videoGenerationCredential = managedVideoGenerationCredentialLease(eventing.modelRunSettings);
36319
36702
  } else if (videoGenerationPolicy.fundingSource === "workspace_gateway") {
@@ -36505,6 +36888,11 @@ async function buildTurnAgent(deps) {
36505
36888
  expectedAttemptId: input.attemptId,
36506
36889
  effort: turn.reasoningEffort
36507
36890
  }) : turn.reasoningEffort;
36891
+ const toolRouterInHistory = session.agent ? await sessionHasToolRouterHistory(db, {
36892
+ accountId: input.accountId,
36893
+ workspaceId: input.workspaceId,
36894
+ sessionId: input.sessionId
36895
+ }) : false;
36508
36896
  const agent = (() => {
36509
36897
  const agentConstructionStartedAt = performance.now();
36510
36898
  let agentConstructionOutcome = "completed";
@@ -36523,6 +36911,7 @@ async function buildTurnAgent(deps) {
36523
36911
  }
36524
36912
  } : {},
36525
36913
  ...preparedTools.inputWaitYield ? { inputWaitYield: preparedTools.inputWaitYield } : {},
36914
+ ...session.agent ? { agentConfig: session.agent, toolRouterInHistory } : {},
36526
36915
  reasoningEffort: requestReasoningEffort,
36527
36916
  latencyMode: turnExecutionPolicy.latencyMode,
36528
36917
  ...serviceTier ? { serviceTier } : {},
@@ -36611,6 +37000,14 @@ async function buildTurnAgent(deps) {
36611
37000
  onRetainableSessionImageOutput: media.retainSessionImageAtToolBoundary,
36612
37001
  skillCatalog: deps.skillCatalog,
36613
37002
  skillCatalogInHistory: true,
37003
+ // A session with an agent configuration composes the modular prompt
37004
+ // (identity, base behavior, runtime mechanics, capability modules);
37005
+ // its workspace identity survives instruction policies. Null keeps
37006
+ // the legacy composition below byte-for-byte.
37007
+ ...session.agent ? {
37008
+ agentConfig: session.agent,
37009
+ ...workspaceAgentIdentity ? { workspaceAgentIdentity } : {}
37010
+ } : {},
36614
37011
  ...!structuredWorkspacePolicyActive && workspaceAgentInstructions ? { instructionsTemplate: workspaceAgentInstructions } : {},
36615
37012
  ...workspaceGovernance ? { workspaceGovernance } : {},
36616
37013
  ...workspaceMemory ? { workspaceMemory } : {},
@@ -36878,11 +37275,14 @@ function createRunAgentTurnActivity(services) {
36878
37275
  getModelRunSettings: () => eventing.modelRunSettings,
36879
37276
  getExecutionGeneration: () => attempt.executionGeneration
36880
37277
  });
36881
- const checkpointBeforeProviderDispatch = () => checkpointHistoryBeforeProviderDispatch(historySink, {
36882
- effectiveSandboxBackend: eventing.modelRunSettings.sandboxBackend,
36883
- routingEnabled: routingEnabled(settings),
36884
- readActiveSandbox: () => readActiveSandbox3(db, input.workspaceId, input.sessionId)
36885
- });
37278
+ const checkpointBeforeProviderDispatch = async () => {
37279
+ await awaitModelCallAdmission();
37280
+ await checkpointHistoryBeforeProviderDispatch(historySink, {
37281
+ effectiveSandboxBackend: eventing.modelRunSettings.sandboxBackend,
37282
+ routingEnabled: routingEnabled(settings),
37283
+ readActiveSandbox: () => readActiveSandbox3(db, input.workspaceId, input.sessionId)
37284
+ });
37285
+ };
36886
37286
  try {
36887
37287
  const claimed = await claimTurnAttempt({
36888
37288
  input,
@@ -37025,7 +37425,7 @@ function createRunAgentTurnActivity(services) {
37025
37425
  requiredGeneratedVideoFiles
37026
37426
  });
37027
37427
  if ("exit" in governance) return governance.exit;
37028
- await assertModelConnectionAllowsTurn(db, {
37428
+ await assertModelConnectionAllowsTurn2(db, {
37029
37429
  workspaceId: input.workspaceId,
37030
37430
  subjectId: turn.initiatingHumanSubjectId ?? "worker:model-access",
37031
37431
  modelId: turnExecutionPolicy.productModelId,
@@ -37039,6 +37439,7 @@ function createRunAgentTurnActivity(services) {
37039
37439
  agentHumanInputEnabled,
37040
37440
  codeSearchEnabled,
37041
37441
  workspaceAgentInstructions,
37442
+ workspaceAgentIdentity,
37042
37443
  workspaceGovernance,
37043
37444
  structuredWorkspacePolicyActive,
37044
37445
  workspaceMemory,
@@ -37221,8 +37622,12 @@ function createRunAgentTurnActivity(services) {
37221
37622
  credentialId: providerTurn.effectiveXaiCredentialId,
37222
37623
  authoritySnapshot: turn.xaiProviderAccountAuthoritySnapshot,
37223
37624
  hostedSearch: {
37224
- webSearch: runSettings.webSearchEnabled,
37225
- xSearch: runSettings.webSearchEnabled
37625
+ webSearch: resolveAgentToolFamilies3(session.agent, {
37626
+ webSearch: runSettings.webSearchEnabled
37627
+ }).webSearch,
37628
+ xSearch: resolveAgentToolFamilies3(session.agent, {
37629
+ webSearch: runSettings.webSearchEnabled
37630
+ }).webSearch
37226
37631
  },
37227
37632
  streamIdleTimeoutMs: runSettings.supergrokResponseStreamIdleTimeoutMs,
37228
37633
  nextRequestId: () => `${dispatchId}:xai:${++xaiModelRequestSequence}`,
@@ -37332,13 +37737,36 @@ function createRunAgentTurnActivity(services) {
37332
37737
  const claudeUsageObserver = await createClaudeUsageObserver(
37333
37738
  parseModelProvidersJson(runSettings.modelProvidersJson),
37334
37739
  providerTurn.latestClaudeUsage,
37335
- (scope) => loadClaudeSubscriptionUsageCredential(db, settings, {
37740
+ (scope) => loadClaudeSubscriptionUsageCredential2(db, settings, {
37336
37741
  accountId: input.accountId,
37337
37742
  workspaceId: input.workspaceId,
37338
37743
  scope
37339
37744
  })
37340
37745
  );
37341
- const withClaudeUsage = (fn) => withClaudeUsageObserver(claudeUsageObserver, fn);
37746
+ const withClaudeUsage = (fn) => withClaudeUsageObserver(claudeUsageObserver, fn, async (providerId, headers) => {
37747
+ const binding = claudeUsageObserver.binding(providerId);
37748
+ if (!binding) return headers;
37749
+ const credential = await resolveClaudeSubscriptionCredential(
37750
+ db,
37751
+ settings,
37752
+ {
37753
+ accountId: input.accountId,
37754
+ workspaceId: input.workspaceId,
37755
+ scope: binding.scope
37756
+ },
37757
+ {
37758
+ expectedConnectionId: binding.expectedConnectionId,
37759
+ expectedCredentialVersion: binding.expectedCredentialVersion
37760
+ }
37761
+ );
37762
+ if (!credential) return headers;
37763
+ if ("reconnectRequired" in credential)
37764
+ throw new ClaudeSubscriptionReconnectRequired();
37765
+ claudeUsageObserver.renew(providerId, credential);
37766
+ headers.set("authorization", `Bearer ${credential.token}`);
37767
+ headers.delete("x-api-key");
37768
+ return headers;
37769
+ });
37342
37770
  const withCodex = (fn) => codexContext ? codexRequestStorage.run(codexContext, fn) : fn();
37343
37771
  const withProviderRequestContext = (fn) => withClaudeUsage(
37344
37772
  () => providerTurn.xaiRequestContext ? xaiSubscriptionRequestStorage.run(providerTurn.xaiRequestContext, fn) : withCodex(fn)
@@ -37396,6 +37824,7 @@ function createRunAgentTurnActivity(services) {
37396
37824
  turnExecutionPolicy,
37397
37825
  resolvedModel,
37398
37826
  workspaceAgentInstructions,
37827
+ workspaceAgentIdentity,
37399
37828
  workspaceGovernance,
37400
37829
  structuredWorkspacePolicyActive,
37401
37830
  workspaceMemory,
@@ -37542,6 +37971,7 @@ These are publication receipts for the human's earlier answer. Do not call the r
37542
37971
  void 0
37543
37972
  );
37544
37973
  const runCredentials = await prepareRunCredentials({
37974
+ turnTools,
37545
37975
  localMcpServerIds: installedApiIntegrations.map((integration) => integration.serverId),
37546
37976
  input,
37547
37977
  settings,
@@ -37774,6 +38204,7 @@ These are publication receipts for the human's earlier answer. Do not call the r
37774
38204
  supportsImageInput,
37775
38205
  agentHumanInputEnabled,
37776
38206
  workspaceAgentInstructions,
38207
+ workspaceAgentIdentity,
37777
38208
  workspaceGovernance,
37778
38209
  structuredWorkspacePolicyActive,
37779
38210
  workspaceMemory,
@@ -37898,6 +38329,7 @@ These are publication receipts for the human's earlier answer. Do not call the r
37898
38329
  turnExecutionPolicy,
37899
38330
  resolvedModel,
37900
38331
  workspaceAgentInstructions,
38332
+ workspaceAgentIdentity,
37901
38333
  workspaceGovernance,
37902
38334
  structuredWorkspacePolicyActive,
37903
38335
  workspaceMemory,
@@ -39307,4 +39739,4 @@ export {
39307
39739
  createTurnActivities,
39308
39740
  createTurnActivitiesFromServices
39309
39741
  };
39310
- //# sourceMappingURL=activities-turn-FQ5OO2WW.js.map
39742
+ //# sourceMappingURL=activities-turn-T7Y3BWYO.js.map