@opengeni/worker-bundle 1.0.1 → 2.0.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 (122) hide show
  1. package/dist/activities/agent-turn/codex.d.ts +1 -1
  2. package/dist/activities/agent-turn/compaction-prep.d.ts +1 -5
  3. package/dist/activities/agent-turn/errors.d.ts +31 -5
  4. package/dist/activities/agent-turn/finalization-monitor.d.ts +17 -0
  5. package/dist/activities/agent-turn/finalization.d.ts +1 -1
  6. package/dist/activities/agent-turn/governance-model.d.ts +0 -2
  7. package/dist/activities/agent-turn/quiescence.d.ts +1 -0
  8. package/dist/activities/agent-turn/recovery-warning.d.ts +8 -0
  9. package/dist/activities/agent-turn/run-credentials.d.ts +0 -1
  10. package/dist/activities/agent-turn/sandbox-establish.d.ts +1 -1
  11. package/dist/activities/agent-turn/sandbox-route.d.ts +0 -20
  12. package/dist/activities/agent-turn/sandbox-runtime.d.ts +1 -1
  13. package/dist/activities/agent-turn/skill-remove.d.ts +14 -0
  14. package/dist/activities/agent-turn/skill-search.d.ts +1 -1
  15. package/dist/activities/agent-turn/stream-attempt.d.ts +1 -0
  16. package/dist/activities/agent-turn/tool-environment.d.ts +0 -4
  17. package/dist/activities/agent-turn/turn-context.d.ts +2 -0
  18. package/dist/activities/agent-turn/turn-sandbox-access.d.ts +3 -0
  19. package/dist/activities/codex-capacity.d.ts +6 -1
  20. package/dist/activities/codex-rotation.d.ts +1 -1
  21. package/dist/activities/context-compaction.d.ts +7 -1
  22. package/dist/activities/mcp-account-routes.d.ts +22 -0
  23. package/dist/activities/mcp-credentials.d.ts +12 -13
  24. package/dist/activities/mcp-operation-authority.d.ts +0 -5
  25. package/dist/activities/rig-verification.d.ts +2 -3
  26. package/dist/activities/sandbox-images.d.ts +1 -1
  27. package/dist/activities/sandbox-lease.d.ts +4 -3
  28. package/dist/activities/session-state.d.ts +57 -2
  29. package/dist/activities/types.d.ts +12 -3
  30. package/dist/{activities-control-HMFGSKC5.js → activities-control-BOLIY3H4.js} +366 -187
  31. package/dist/activities-control-BOLIY3H4.js.map +1 -0
  32. package/dist/activities-control.d.ts +110 -2
  33. package/dist/{activities-turn-AP67JADS.js → activities-turn-2B3WMHBB.js} +767 -471
  34. package/dist/activities-turn-2B3WMHBB.js.map +1 -0
  35. package/dist/activities.d.ts +110 -2
  36. package/dist/artifact-materializer-entry.js +5 -2
  37. package/dist/artifact-materializer-entry.js.map +1 -1
  38. package/dist/artifact-outbox-entry.js +2 -2
  39. package/dist/artifact-outbox-entry.js.map +1 -1
  40. package/dist/{chunk-A4XQWVZK.js → chunk-I7HKKMUJ.js} +77 -1
  41. package/dist/chunk-I7HKKMUJ.js.map +1 -0
  42. package/dist/{chunk-CYITU5UH.js → chunk-TVEE2UUC.js} +275 -146
  43. package/dist/chunk-TVEE2UUC.js.map +1 -0
  44. package/dist/editable-artifact-materializer-service.d.ts +2 -2
  45. package/dist/editable-artifact-outbox-service.d.ts +2 -2
  46. package/dist/index.d.ts +55 -1
  47. package/dist/index.js +4 -3
  48. package/dist/index.js.map +1 -1
  49. package/dist/observability-metrics.d.ts +11 -3
  50. package/dist/retained-process-diagnostics.d.ts +4 -0
  51. package/dist/sandbox-resume.d.ts +2 -12
  52. package/dist/sandbox-routing.d.ts +5 -3
  53. package/dist/sandbox-snapshot-diagnostics.d.ts +20 -0
  54. package/dist/warm-capture-holder.d.ts +3 -0
  55. package/dist/workflow-bundle.js +51 -11
  56. package/package.json +15 -15
  57. package/src/activities/agent-turn/agent-build.ts +42 -5
  58. package/src/activities/agent-turn/claim.ts +12 -0
  59. package/src/activities/agent-turn/codex-capacity.ts +26 -4
  60. package/src/activities/agent-turn/codex.ts +6 -2
  61. package/src/activities/agent-turn/compaction-prep.ts +53 -72
  62. package/src/activities/agent-turn/errors.ts +98 -10
  63. package/src/activities/agent-turn/failure-settlement.ts +49 -28
  64. package/src/activities/agent-turn/finalization-monitor.ts +121 -0
  65. package/src/activities/agent-turn/finalization.ts +79 -21
  66. package/src/activities/agent-turn/governance-model.ts +15 -19
  67. package/src/activities/agent-turn/history.ts +24 -10
  68. package/src/activities/agent-turn/quiescence.ts +3 -0
  69. package/src/activities/agent-turn/recovery-warning.ts +18 -0
  70. package/src/activities/agent-turn/run-credentials.ts +3 -7
  71. package/src/activities/agent-turn/run.ts +68 -52
  72. package/src/activities/agent-turn/sandbox-establish.ts +10 -2
  73. package/src/activities/agent-turn/sandbox-route.ts +0 -20
  74. package/src/activities/agent-turn/sandbox-runtime.ts +40 -7
  75. package/src/activities/agent-turn/skill-remove.ts +46 -0
  76. package/src/activities/agent-turn/skill-save.ts +1 -1
  77. package/src/activities/agent-turn/skill-search.ts +1 -1
  78. package/src/activities/agent-turn/skill-selection.ts +2 -0
  79. package/src/activities/agent-turn/skill-tools.ts +10 -5
  80. package/src/activities/agent-turn/stream-attempt.ts +38 -15
  81. package/src/activities/agent-turn/tool-environment.ts +44 -29
  82. package/src/activities/agent-turn/turn-context.ts +3 -0
  83. package/src/activities/agent-turn/turn-sandbox-access.ts +7 -1
  84. package/src/activities/capabilities.ts +17 -2
  85. package/src/activities/codex-capacity.ts +38 -9
  86. package/src/activities/codex-rotation.ts +18 -5
  87. package/src/activities/context-compaction.ts +26 -4
  88. package/src/activities/goals.ts +1 -0
  89. package/src/activities/google-drive-publication.ts +1 -3
  90. package/src/activities/mcp-account-routes.ts +139 -0
  91. package/src/activities/mcp-credentials.ts +83 -194
  92. package/src/activities/mcp-operation-authority.ts +16 -41
  93. package/src/activities/parent-wake.ts +8 -0
  94. package/src/activities/rig-verification.ts +38 -37
  95. package/src/activities/run-input.ts +25 -16
  96. package/src/activities/sandbox-images.ts +0 -1
  97. package/src/activities/sandbox-lease.ts +177 -44
  98. package/src/activities/scheduled-tasks.ts +118 -93
  99. package/src/activities/session-state.ts +83 -3
  100. package/src/activities/types.ts +18 -2
  101. package/src/activities/workflow-wake.ts +2 -17
  102. package/src/editable-artifact-materializer-service.ts +7 -3
  103. package/src/editable-artifact-outbox-service.ts +4 -4
  104. package/src/github-rest-mcp.ts +0 -1
  105. package/src/index.ts +2 -1
  106. package/src/observability-metrics.ts +105 -2
  107. package/src/personal-github-git-credentials.ts +0 -1
  108. package/src/pr-review-credentials.ts +0 -2
  109. package/src/retained-process-diagnostics.ts +50 -0
  110. package/src/sandbox-resume.ts +58 -73
  111. package/src/sandbox-routing.ts +44 -19
  112. package/src/sandbox-snapshot-diagnostics.ts +112 -0
  113. package/src/warm-capture-holder.ts +15 -0
  114. package/src/workflows/session.ts +64 -2
  115. package/dist/activities/agent-turn/knowledge-sources.d.ts +0 -20
  116. package/dist/activities/packs.d.ts +0 -55
  117. package/dist/activities-control-HMFGSKC5.js.map +0 -1
  118. package/dist/activities-turn-AP67JADS.js.map +0 -1
  119. package/dist/chunk-A4XQWVZK.js.map +0 -1
  120. package/dist/chunk-CYITU5UH.js.map +0 -1
  121. package/src/activities/agent-turn/knowledge-sources.ts +0 -86
  122. package/src/activities/packs.ts +0 -166
@@ -21,9 +21,9 @@ import {
21
21
  type OpenGeniRuntime,
22
22
  } from "@opengeni/runtime";
23
23
  import { settingsWithResolvedModelContext, type Settings } from "@opengeni/config";
24
+ import { projectReasoningConfigurations, supportsReasoningConfiguration } from "@opengeni/codex";
24
25
  import { settingsWithSessionMcpServersForRun } from "../capabilities";
25
26
  import { resolveRigProviderImageForRun } from "@opengeni/core";
26
- import { resolveWorkspacePackRuntime, settingsWithPackSandboxImage } from "../packs";
27
27
  import { createModelHistoryAttachmentProjector } from "../run-input";
28
28
  import type {
29
29
  TurnActivityServices as ActivityServices,
@@ -84,7 +84,6 @@ export type GovernanceModelDeps = {
84
84
 
85
85
  export type GovernanceModelOk = {
86
86
  runtimePreparationStartedAt: number;
87
- packRuntime: Awaited<ReturnType<typeof resolveWorkspacePackRuntime>>;
88
87
  rigVersion:
89
88
  | NonNullable<Awaited<ReturnType<typeof materializeRigVersionForAttempt>>>["version"]
90
89
  | null;
@@ -189,17 +188,15 @@ export async function prepareGovernanceAndModel(
189
188
  attemptId: input.attemptId,
190
189
  executionGeneration: turn.executionGeneration,
191
190
  };
192
- // Independent workspace reads after the personal-resource fence. Pack,
193
- // installed skills, frozen rig, governance snapshots, and model policy do
191
+ // Independent workspace reads after the personal-resource fence. The
192
+ // frozen rig, governance snapshots, and model policy do
194
193
  // not depend on each other. Company-brain selection still waits on the
195
194
  // snapshots below so its receipt stays exact.
196
195
  const [
197
- packRuntime,
198
196
  rigMaterialization,
199
197
  [workspace, companyProfileSnapshot, instructionPolicySnapshot, preferenceSnapshot],
200
198
  workspaceModelPolicy,
201
199
  ] = await Promise.all([
202
- resolveWorkspacePackRuntime(db, input.workspaceId),
203
200
  session.rigId && session.rigVersionId
204
201
  ? (async () =>
205
202
  await materializeRigVersionForAttempt(db, {
@@ -280,14 +277,7 @@ export async function prepareGovernanceAndModel(
280
277
  // Contribution telemetry must never change model execution semantics.
281
278
  }
282
279
  // A Rig is always a setup/check layer over the deployment platform sandbox.
283
- // The pre-v2 Pack image path remains only for rig-less compatibility sessions.
284
- const logicalSandboxSettings = rigVersion
285
- ? capabilitySettings
286
- : settingsWithPackSandboxImage(
287
- capabilitySettings,
288
- packRuntime.sandboxImage,
289
- packRuntime.sandboxProviderImages,
290
- );
280
+ const logicalSandboxSettings = capabilitySettings;
291
281
  const providerImageSelection = await resolveRigProviderImageForRun(
292
282
  logicalSandboxSettings,
293
283
  rigVersion,
@@ -299,9 +289,8 @@ export async function prepareGovernanceAndModel(
299
289
  ? (providerImageSelection.imageId ?? undefined)
300
290
  : undefined;
301
291
  const baseRunSettings = {
302
- // IMAGE PRECEDENCE: a Rig uses the deployment platform base; a rig-less
303
- // pre-v2 Pack may retain its compatibility image. A matching verified
304
- // provider-native ID is then applied only to fresh creation without
292
+ // A Rig uses the deployment platform base. A matching verified
293
+ // provider-native ID is applied only to fresh creation without
305
294
  // changing the logical lease image.
306
295
  ...providerImageSettings,
307
296
  openaiModel: turn.model,
@@ -365,12 +354,20 @@ export async function prepareGovernanceAndModel(
365
354
  fileIds,
366
355
  }),
367
356
  );
357
+ const useReasoningUpdates =
358
+ runSettings.reasoningConfigurationUpdatesEnabled &&
359
+ providerApi === "responses" &&
360
+ (resolvedModel?.provider.id === "codex" || resolvedModel?.provider.id === "openai") &&
361
+ supportsReasoningConfiguration(turnExecutionPolicy.upstreamModelId, turn.reasoningEffort);
368
362
  const modelHistoryProjector = async (
369
363
  items: Array<Record<string, unknown>>,
370
364
  projectionOptions?: Parameters<typeof attachmentProjector>[1],
371
365
  ) =>
372
366
  projectModelInputForCapabilities(
373
- await attachmentProjector(items, projectionOptions),
367
+ projectReasoningConfigurations(
368
+ await attachmentProjector(items, projectionOptions),
369
+ useReasoningUpdates,
370
+ ),
374
371
  modelInputPolicy,
375
372
  );
376
373
  const generatedImageHistoryProjector = async (
@@ -460,7 +457,6 @@ export async function prepareGovernanceAndModel(
460
457
  return {
461
458
  ok: {
462
459
  runtimePreparationStartedAt,
463
- packRuntime,
464
460
  rigVersion,
465
461
  rigName,
466
462
  agentHumanInputEnabled,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  normalizeProtocolJsonValue,
3
+ TURN_OPERATIONAL_NOTICE_PREFIX,
3
4
  sanitizeHistoryItemsForModel,
4
5
  toolCallIdFromSdkItem,
5
6
  } from "@opengeni/runtime";
@@ -31,15 +32,20 @@ export function historyRowsToAppend(
31
32
  }
32
33
  // Canonical model-facing inputs (including machine-input batches) are
33
34
  // persisted before inference and therefore live inside the prefix represented
34
- // by persistedHistoryCount. System messages synthesized only for this
35
- // attempt—recovery diagnostics, credential notices, attachment materialization
36
- // notes—must not accidentally become conversation memory during reconciliation.
37
- // Advance the in-memory watermark past them, but only allocate durable
38
- // positions to actual model/tool output.
35
+ // by persistedHistoryCount. Explicitly turn-scoped operational notices remain
36
+ // at their original positions: deleting them rewrites the model-visible prefix
37
+ // on the next turn and during compaction. Unscoped synthetic system messages
38
+ // still cannot silently become permanent conversation instructions.
39
39
  const rows: Array<{ position: number; item: Record<string, unknown> }> = [];
40
40
  for (const [offset, item] of modelReady.slice(persistedHistoryCount).entries()) {
41
41
  if (item.type === "message" && item.role === "system") {
42
- continue;
42
+ const content =
43
+ typeof item.content === "string"
44
+ ? item.content
45
+ : Array.isArray(item.content)
46
+ ? item.content.map((part: { text?: string }) => part.text ?? "").join("")
47
+ : "";
48
+ if (!content.startsWith(TURN_OPERATIONAL_NOTICE_PREFIX)) continue;
43
49
  }
44
50
  rows.push({
45
51
  position: nextPosition + rows.length,
@@ -149,10 +155,18 @@ export function pendingToolCallFromSdkEvent(event: unknown): {
149
155
  return null;
150
156
  }
151
157
  const raw = item.rawItem as Record<string, unknown>;
152
- // The hosted image call is a complete provider fact carried by one item; it
153
- // never receives a separate function result and therefore must not enter the
154
- // pending function-call ledger.
155
- if (raw.type === "hosted_tool_call" && raw.name === "image_generation_call") return null;
158
+ // Provider-executed search/image calls carry their outcome in this item,
159
+ // not a later function result. Registering them as pending leaves an
160
+ // impossible receipt behind even after the turn completes. Hosted MCP
161
+ // approval requests are different: they still require a response.
162
+ const providerData = raw.providerData as Record<string, unknown> | undefined;
163
+ if (
164
+ raw.type === "hosted_tool_call" &&
165
+ raw.name !== "mcp_approval_request" &&
166
+ providerData?.type !== "mcp_approval_request" &&
167
+ (raw.status === "completed" || raw.name === "image_generation_call")
168
+ )
169
+ return null;
156
170
  const callId = toolCallIdFromSdkItem(raw) ?? raw.id;
157
171
  const callType = raw.type;
158
172
  if (typeof callId !== "string" || callId.length === 0 || typeof callType !== "string") {
@@ -198,13 +198,16 @@ export async function drainAttemptOwnedSandboxWriters(input: {
198
198
  gitCredentialRenewals: readonly Pick<GitCredentialRenewalController, "stop">[];
199
199
  codemodeTokenRenewal: Pick<CodemodeTokenRenewalController, "stop"> | null;
200
200
  runCredentialRenewal: Pick<RunCredentialRenewalController, "stop"> | null;
201
+ onStage?: (stage: "tool_writers" | "credential_renewals") => void;
201
202
  }): Promise<void> {
203
+ input.onStage?.("tool_writers");
202
204
  if (input.toolCancellationFence) {
203
205
  input.toolCancellationFence.cancel(
204
206
  input.cancellationReason ?? new Error("TURN_ATTEMPT_FENCED"),
205
207
  );
206
208
  await input.toolCancellationFence.waitForQuiescence();
207
209
  }
210
+ input.onStage?.("credential_renewals");
208
211
  await Promise.all(input.gitCredentialRenewals.map(async (renewal) => await renewal.stop()));
209
212
  await input.codemodeTokenRenewal?.stop();
210
213
  await input.runCredentialRenewal?.stop();
@@ -0,0 +1,18 @@
1
+ import { getSandboxRecoveryDiscontinuity, type Database } from "@opengeni/db";
2
+
3
+ /** Opt-in belongs to this warning-aware worker implementation, not the DB
4
+ * client, role defaults or deployment configuration. Claim stamps it locally. */
5
+ export const FILESYSTEM_DISCONTINUITY_PROTOCOL = 1 as const;
6
+
7
+ export async function recoveryAwareSessionInstructions(
8
+ db: Database,
9
+ workspaceId: string,
10
+ session: { id: string; instructions?: string | null },
11
+ ): Promise<string> {
12
+ const filesystemDiscontinuity = await getSandboxRecoveryDiscontinuity(
13
+ db,
14
+ workspaceId,
15
+ session.id,
16
+ );
17
+ return [session.instructions, filesystemDiscontinuity].filter(Boolean).join("\n\n");
18
+ }
@@ -287,13 +287,10 @@ export async function prepareRunCredentials(deps: PrepareRunCredentialsDeps) {
287
287
  // provider may supply it; unset still self-mints GitHub from settings.
288
288
  // gitToken/gitTokens are undefined on the selfhosted skip path (the machine
289
289
  // uses its own git creds).
290
- // Git and MCP credentials share one lineage snapshot for this turn. A
291
- // host that supplies both ports must never see two independently resolved
292
- // roots for the same execution merely because the call sites are far apart.
290
+ // Git credential providers share one lineage snapshot for this turn.
291
+ // MCP access uses the native accepted connection context independently.
293
292
  const needsHostCredentialRoot = Boolean(
294
- connectionCredentials?.gitCredentials ||
295
- personalGitHubCredentials ||
296
- connectionCredentials?.mcpCredentials,
293
+ connectionCredentials?.gitCredentials || personalGitHubCredentials,
297
294
  );
298
295
  const hostCredentialRootSessionId = needsHostCredentialRoot
299
296
  ? await getSessionRootId(db, input.workspaceId, input.sessionId)
@@ -748,7 +745,6 @@ export async function prepareRunCredentials(deps: PrepareRunCredentialsDeps) {
748
745
  initialRunCredentialMaterial,
749
746
  runCredentialsNote,
750
747
  authorizeGitHubTokenMint,
751
- hostCredentialRootSessionId,
752
748
  gitCredentialAuthority,
753
749
  codemodeAuthority,
754
750
  sandboxArtifactRuntime,
@@ -1,5 +1,5 @@
1
+ import { getSessionAuthorityProjection } from "@opengeni/db";
1
2
  import { createKnowledgeSourceSyncActivities } from "../knowledge-source-sync";
2
- import { prepareTurnKnowledgeSources } from "./knowledge-sources";
3
3
  import {
4
4
  assertModelConnectionAllowsTurn,
5
5
  freezeAgentLearningPolicy,
@@ -11,7 +11,11 @@ import {
11
11
  getMaterializedSandboxFileResources,
12
12
  markSandboxFileResourcesMaterialized,
13
13
  } from "@opengeni/db";
14
- import { sandboxOperationMetricObserver } from "@opengeni/observability";
14
+ import {
15
+ sandboxOperationMetricObserver,
16
+ turnExecutionTelemetryKey,
17
+ withTraceContext,
18
+ } from "@opengeni/observability";
15
19
  import {
16
20
  REMOTE_COMPACTION_V2_BETA_FEATURE,
17
21
  REMOTE_COMPACTION_V2_IMPLEMENTATION,
@@ -152,7 +156,11 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
152
156
  // final model checkpoint happens while the activity still owns its complete
153
157
  // turn graph and must not suppress collection after that graph is released.
154
158
  const turnCompletionMemoryCollector = createModelCheckpointMemoryCollector();
155
- return async function runAgentTurn(input: RunAgentTurnInput): Promise<RunAgentTurnResult> {
159
+ const runAgentTurn = async (
160
+ input: RunAgentTurnInput,
161
+ resolvedServices: ActivityServices,
162
+ activitySpan: ReturnType<ActivityServices["observability"]["startSpan"]>,
163
+ ): Promise<RunAgentTurnResult> => {
156
164
  const {
157
165
  settings,
158
166
  catalogSourceSettings = settings,
@@ -169,7 +177,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
169
177
  connectionCredentials,
170
178
  personalGitHubCredentials,
171
179
  startVideoGenerationWorkflow,
172
- } = await services();
180
+ } = resolvedServices;
173
181
  const activityContext = currentActivityContext();
174
182
  const cancellationSignal = activityContext?.cancellationSignal;
175
183
  // Temporal cancellation is not the only way an activity loses ownership:
@@ -207,11 +215,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
207
215
  });
208
216
  const dispatchId = activityContext?.info.activityId ?? randomUUID();
209
217
  const activityStarted = performance.now();
210
- const activitySpan = observability.startSpan("worker.run_agent_segment", {
211
- "opengeni.session_id": input.sessionId,
212
- "opengeni.workflow_id": input.workflowId,
213
- "opengeni.trigger_kind": input.trigger.kind,
214
- });
215
218
  const acknowledgeLostAttemptOwnership = (): void => {
216
219
  // A stale terminal/recovery settlement can lose either to a benign
217
220
  // successor or to Pause/Steer closing this exact attempt. Only the
@@ -434,8 +437,26 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
434
437
  executionGeneration: attempt.executionGeneration,
435
438
  },
436
439
  });
440
+ const attachmentAuthority = await getSessionAuthorityProjection(
441
+ db,
442
+ input.workspaceId,
443
+ input.sessionId,
444
+ );
445
+ if (!attachmentAuthority) throw new Error("Session attachment authority unavailable");
437
446
  return await withSessionRlsActorContext(
438
447
  {
448
+ sessionAttachmentReadAccess: {
449
+ sessionId: input.sessionId,
450
+ authorityEpoch: attachmentAuthority.authorityEpoch,
451
+ actor: {
452
+ kind: "agent_attempt",
453
+ subjectId: "service:agent-turn",
454
+ callerSessionId: input.sessionId,
455
+ turnId: attempt.turnId,
456
+ attemptId: input.attemptId,
457
+ executionGeneration: attempt.executionGeneration,
458
+ },
459
+ },
439
460
  subjectId: "service:agent-turn",
440
461
  initiatingHumanSubjectId: fileAuthoritySubjectId,
441
462
  privateFileOwnerSubjectId:
@@ -511,7 +532,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
511
532
  });
512
533
  const {
513
534
  runtimePreparationStartedAt,
514
- packRuntime,
515
535
  rigVersion,
516
536
  rigName,
517
537
  agentHumanInputEnabled,
@@ -556,6 +576,12 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
556
576
  runSettings,
557
577
  input.workspaceId,
558
578
  providerTurn.effectiveCodexCredentialId ?? "",
579
+ undefined,
580
+ {
581
+ turnId: turn.id,
582
+ holderId: leases.codex.holderId!,
583
+ generation: leases.codex.generation!,
584
+ },
559
585
  );
560
586
  const resolveTrackedToken = async (
561
587
  resolve: () => ReturnType<typeof resolver.getToken>,
@@ -1030,44 +1056,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1030
1056
  knowledgeRecoveryNote =
1031
1057
  "[OpenGeni confirmation recovery unavailable] Do not assume an earlier remember confirmation was published. Inspect the current Knowledge or instruction review.";
1032
1058
  }
1033
- const attachmentPreparationNote = await prepareTurnKnowledgeSources({
1034
- settings: runSettings,
1035
- scope: {
1036
- accountId: input.accountId,
1037
- workspaceId: input.workspaceId,
1038
- sessionId: input.sessionId,
1039
- turnId: turn.id,
1040
- attemptId: input.attemptId,
1041
- executionGeneration: attempt.executionGeneration,
1042
- },
1043
- // Only attachments on this accepted turn are new source inputs.
1044
- // Session-level file receipts remain historical context and must
1045
- // never trigger metadata reads, downloads, or re-preparation.
1046
- resources: turn.resources,
1047
- selectedTools: session.firstPartyMcpTools,
1048
- permissions: session.firstPartyMcpPermissions,
1049
- learningMode: learning.effective.knowledge,
1050
- signal: cancellationSignal,
1051
- onOutcome: async (outcome) => {
1052
- if (outcome.status === "disabled") return;
1053
- await eventing.publish!(
1054
- [
1055
- {
1056
- type:
1057
- outcome.status === "failed"
1058
- ? "knowledge.source.failed"
1059
- : "knowledge.source.prepared",
1060
- payload: outcome,
1061
- },
1062
- ],
1063
- true,
1064
- );
1065
- },
1066
- });
1067
-
1068
- const knowledgeSourcePreparationNote =
1069
- [knowledgeRecoveryNote, attachmentPreparationNote].filter(Boolean).join("\n\n") ||
1070
- undefined;
1059
+ const knowledgeSourcePreparationNote = knowledgeRecoveryNote;
1071
1060
 
1072
1061
  const sandboxRoute = await resolveSandboxRoute({
1073
1062
  input,
@@ -1148,7 +1137,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1148
1137
  establishPolicy,
1149
1138
  initialRunCredentialMaterial,
1150
1139
  runCredentialsNote,
1151
- hostCredentialRootSessionId,
1152
1140
  codemodeAuthority,
1153
1141
  sandboxArtifactRuntime,
1154
1142
  sandboxEnvironment,
@@ -1268,7 +1256,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1268
1256
  ];
1269
1257
  const toolRuntime = await prepareTurnToolRuntime({
1270
1258
  fetchKnowledgeSource: sourceActivities.runKnowledgeSourceSyncBatch,
1271
- selectedSkillActivations: packRuntime.skillActivations,
1272
1259
  input,
1273
1260
  catalogSourceSettings,
1274
1261
  db,
@@ -1277,7 +1264,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1277
1264
  objectStorage,
1278
1265
  observability,
1279
1266
  cancellationSignal,
1280
- connectionCredentials,
1281
1267
  eventing,
1282
1268
  attempt,
1283
1269
  sandboxState,
@@ -1295,7 +1281,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1295
1281
  lazyToolTransport,
1296
1282
  turnTools,
1297
1283
  connectionScope,
1298
- hostCredentialRootSessionId,
1299
1284
  sandboxArtifactRuntime,
1300
1285
  activeSandboxBackend,
1301
1286
  groupBoxBackend,
@@ -1771,4 +1756,35 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1771
1756
  });
1772
1757
  }
1773
1758
  };
1759
+ return async (input: RunAgentTurnInput): Promise<RunAgentTurnResult> => {
1760
+ const resolvedServices = await services();
1761
+ const correlationId = turnExecutionTelemetryKey(
1762
+ input.workspaceId,
1763
+ input.sessionId,
1764
+ input.attemptId,
1765
+ );
1766
+ const span = resolvedServices.observability.startSpan(
1767
+ "worker.run_agent_segment",
1768
+ {
1769
+ "opengeni.trigger_kind": input.trigger.kind,
1770
+ correlationId,
1771
+ },
1772
+ { parent: null },
1773
+ );
1774
+ try {
1775
+ return await withTraceContext(span, () => {
1776
+ try {
1777
+ resolvedServices.observability.info("worker execution started", { correlationId });
1778
+ } catch {
1779
+ // Correlation diagnostics never affect execution or admission.
1780
+ }
1781
+ return runAgentTurn(input, resolvedServices, span);
1782
+ });
1783
+ } catch (error) {
1784
+ span.end({ error });
1785
+ throw error;
1786
+ } finally {
1787
+ span.end();
1788
+ }
1789
+ };
1774
1790
  }
@@ -6,7 +6,10 @@ import {
6
6
  assertPersonalMachineForAttempt,
7
7
  type SandboxRecord,
8
8
  } from "@opengeni/db";
9
- import { sandboxOperationMetricObserver } from "@opengeni/observability";
9
+ import {
10
+ sandboxOperationMetricObserver,
11
+ sandboxCaptureWaitMetricObserver,
12
+ } from "@opengeni/observability";
10
13
  import {
11
14
  withRunCredentialsSession,
12
15
  runOwnedSandboxSetup,
@@ -29,7 +32,7 @@ import {
29
32
  type MintedRunGitCredentials,
30
33
  type SandboxCodemodeAuthority,
31
34
  } from "../environment";
32
- import { rigProviderImageSourceImage } from "../packs";
35
+ import { rigProviderImageSourceImage } from "../sandbox-images";
33
36
  import type { TurnActivityServices as ActivityServices, RunAgentTurnInput } from "../types";
34
37
  import type { currentActivityContext } from "../streaming";
35
38
  import { resumeBoxForTurn, type ResumedTurnSandbox } from "../../sandbox-resume";
@@ -444,6 +447,7 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
444
447
  bus,
445
448
  onOp: machineOpObserver.observer,
446
449
  onSandboxOperation: sandboxOperationObserver,
450
+ onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
447
451
  opJournal,
448
452
  },
449
453
  {
@@ -515,6 +519,7 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
515
519
  opJournal,
516
520
  onOp: machineOpObserver.observer,
517
521
  onSandboxOperation: sandboxOperationObserver,
522
+ onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
518
523
  onHomeSandboxRebound,
519
524
  ...(runtimeCancellationSignal ? { waitSignal: runtimeCancellationSignal } : {}),
520
525
  },
@@ -755,6 +760,7 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
755
760
  bus,
756
761
  opJournal,
757
762
  onSandboxOperation: sandboxOperationObserver,
763
+ onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
758
764
  onHomeSandboxLost: publishSandboxLost,
759
765
  onHomeSandboxRebound,
760
766
  ...(runtimeCancellationSignal ? { waitSignal: runtimeCancellationSignal } : {}),
@@ -1004,6 +1010,7 @@ export async function bindLazySandboxProvisioner(
1004
1010
  {
1005
1011
  settings: runSettings,
1006
1012
  environment: sandboxEnvironment,
1013
+ recordLazyManifest: true,
1007
1014
  onRuntimeEvent: async (event) => {
1008
1015
  await eventing.publish?.([{ type: event.type, payload: event.payload }], true);
1009
1016
  },
@@ -1122,6 +1129,7 @@ export async function bindLazySandboxProvisioner(
1122
1129
  opJournal,
1123
1130
  onOp: machineOpObserver.observer,
1124
1131
  onSandboxOperation: sandboxOperationObserver,
1132
+ onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
1125
1133
  onHomeSandboxLost: publishSandboxLost,
1126
1134
  onHomeSandboxRebound,
1127
1135
  ...(runtimeCancellationSignal ? { waitSignal: runtimeCancellationSignal } : {}),
@@ -268,15 +268,6 @@ export async function reconcileActiveSandboxPointer(
268
268
  return { pointer: reread, record: rereadRecord };
269
269
  }
270
270
 
271
- /**
272
- * Warm the Modal private-registry image for the image ref this turn actually
273
- * resolved, not only the deployment-global OPENGENI_MODAL_IMAGE_REF warmed at
274
- * worker boot. A provider-native modalImageId bypasses registry import and is
275
- * resolved by ModalImageSelector.fromId during create. Otherwise packs can
276
- * override `modalImageRef` per workspace/turn, so a private pack image must be
277
- * resolved before sandbox creation or Modal falls back to the unauthenticated
278
- * `fromTag` path.
279
- */
280
271
  export async function ensureTurnModalRegistryImage(
281
272
  runSettings: Settings,
282
273
  sandboxCreationBackend: Settings["sandboxBackend"] | undefined,
@@ -302,17 +293,6 @@ export type SandboxArtifactRuntimeAdmission = Readonly<{
302
293
  environment: Readonly<Record<string, string>>;
303
294
  }>;
304
295
 
305
- /**
306
- * Admit the optional native standalone-file runtime only for the deployment's
307
- * exact base sandbox image contract. A pack/rig image override is an
308
- * independent filesystem and therefore fails closed even when the deployment
309
- * base image is capable. Collaborative artifact skills are admitted separately
310
- * from the frozen canonical tool catalog.
311
- *
312
- * This keeps lazy provisioning intact: CI/release proves the image closure,
313
- * while a before-agent-start doctor verifies the actual box before any model
314
- * call. No speculative sandbox is created merely to populate the skill index.
315
- */
316
296
  export function sandboxArtifactRuntimeAdmission(
317
297
  deploymentSettings: Settings,
318
298
  runSettings: Settings,
@@ -8,6 +8,7 @@ import {
8
8
  } from "@opengeni/db";
9
9
  import {
10
10
  RoutingMutationOutcomeUnknownError,
11
+ runManagedCodemodeClientHook,
11
12
  type EstablishedSandboxSession,
12
13
  } from "@opengeni/runtime";
13
14
  import {
@@ -18,7 +19,10 @@ import {
18
19
  import { createRuntimeBatcher, currentActivityContext } from "../streaming";
19
20
  import type { TurnActivityServices as ActivityServices, RunAgentTurnInput } from "../types";
20
21
  import { maybePersistWarmWorkspaceSnapshot, type ResumedTurnSandbox } from "../../sandbox-resume";
21
- import { recordCreditMicros } from "../../observability-metrics";
22
+ import {
23
+ recordCreditMicros,
24
+ runtimeMetricsHooksForObservability,
25
+ } from "../../observability-metrics";
22
26
  import { ChannelAPartialMutationError } from "@opengeni/runtime/sandbox";
23
27
 
24
28
  import { safeErrorDiagnostic } from "./errors";
@@ -124,16 +128,37 @@ export function createSandboxTurnRuntime(deps: SandboxTurnRuntimeDeps) {
124
128
  // alive. Keep setup/snapshot persistence on the rebound raw session while
125
129
  // preserving the SDK-owned routing proxy for eager turns. Lazy turns hold the
126
130
  // proxy separately, so their worker-side handle may replace its raw session.
127
- const onHomeSandboxRebound = (rebound: {
131
+ const onHomeSandboxRebound = async (rebound: {
128
132
  established: EstablishedSandboxSession;
129
133
  leaseEpoch: number;
130
- }): void => {
134
+ }): Promise<void> => {
131
135
  const current = sandboxState.resolvedSandbox;
136
+ if (!current) throw new Error("Home sandbox rebound before turn sandbox admission");
137
+ if (
138
+ rebound.established.backendId !== "selfhosted" &&
139
+ rebound.established.instanceId !== current.established.instanceId
140
+ ) {
141
+ // An older archive may lack this worker release's selected client. Only
142
+ // prepare that client, never replay turn-start hooks/credentials/resources.
143
+ // Fence the replacement itself, not the stale turn-start handle. Both
144
+ // mutation admission and settlement precede raw-state/route publication.
145
+ const commandFence = eventing.toolCancellationFenceRef.current;
146
+ await runWorkspaceMutationForSandbox(
147
+ { ...current, established: rebound.established, leaseEpoch: rebound.leaseEpoch },
148
+ "homeSandboxClientPreparation",
149
+ () =>
150
+ runManagedCodemodeClientHook(rebound.established.session as never, {
151
+ environment: {},
152
+ ...(commandFence
153
+ ? { commandRunner: commandFence.runSandboxCommand.bind(commandFence) }
154
+ : {}),
155
+ }),
156
+ );
157
+ }
132
158
  const previousSession = current?.established.session;
133
159
  const preserveRoutingProxy =
134
160
  current !== null && previousSession !== sandboxState.setupBoxSession;
135
161
  sandboxState.setupBoxSession = rebound.established.session;
136
- if (!current) return;
137
162
  current.leaseEpoch = rebound.leaseEpoch;
138
163
  current.established = preserveRoutingProxy
139
164
  ? {
@@ -539,8 +564,10 @@ export function createSandboxTurnRuntime(deps: SandboxTurnRuntimeDeps) {
539
564
  // MID-SESSION snapshot (sandbox-file-persistence): while the turn holds
540
565
  // the box, fold a fresh /workspace snapshot onto the lease every
541
566
  // sandboxSnapshotIntervalMs, so a box death the reaper never sees
542
- // (Modal hard timeout mid-busy, OOM, infra) costs at most one interval
543
- // of work — a legit multi-day turn is otherwise completely unprotected
567
+ // (Modal hard timeout mid-busy, OOM, infra) has a recent recovery point
568
+ // when capture succeeds. This interval is not a maximum recovery-point
569
+ // age: live writers and failed captures can defer publication. A legit
570
+ // multi-day turn is otherwise completely unprotected
544
571
  // (the reaper only drain-persists IDLE leases). Uses the UN-proxied box
545
572
  // session (setupBoxSession): the routing veneer could swap mid-op and a
546
573
  // selfhosted target has no persistWorkspace anyway. Best-effort +
@@ -558,7 +585,13 @@ export function createSandboxTurnRuntime(deps: SandboxTurnRuntimeDeps) {
558
585
  })
559
586
  ) {
560
587
  const snapshot = maybePersistWarmWorkspaceSnapshot(
561
- { db, settings, objectStorage },
588
+ {
589
+ db,
590
+ settings,
591
+ objectStorage,
592
+ sandboxMetrics: runtimeMetricsHooksForObservability(observability),
593
+ observability,
594
+ },
562
595
  {
563
596
  accountId: input.accountId,
564
597
  workspaceId: input.workspaceId,
@@ -0,0 +1,46 @@
1
+ import type { AttemptToolDefinition } from "@opengeni/codemode";
2
+ import { z } from "zod";
3
+ import type { SkillWriteReceipt } from "@opengeni/contracts";
4
+
5
+ export const SkillRemoveRequest = z
6
+ .object({
7
+ operationId: z.uuid(),
8
+ skillId: z.uuid(),
9
+ expectedRevisionId: z.uuid().nullable(),
10
+ expectedScopeVersion: z.number().int().positive(),
11
+ reason: z.string().min(1).max(2000),
12
+ })
13
+ .strict();
14
+
15
+ export function createSkillRemoveAttemptToolDefinition(input: {
16
+ authorize: () => Promise<void>;
17
+ remove: (request: z.infer<typeof SkillRemoveRequest>) => Promise<SkillWriteReceipt>;
18
+ }): AttemptToolDefinition {
19
+ return {
20
+ identity: { serverId: "opengeni", toolName: "skill_remove" },
21
+ modelName: "skill_remove",
22
+ codemodePath: ["opengeni", "skill_remove"],
23
+ title: "Permanently remove Skill",
24
+ description:
25
+ "Permanently delete a saved Skill and ALL its stored revisions, not a recoverable hide. Conversations remain unchanged. Read the exact Skill first; supply its current active revision and scope version. Built-in, repository and inline session Skills cannot be removed. Automatic applies deletion; Review first retains an explicitly labeled deletion proposal for human review in Knowledge > Needs review; Off refuses changes. Reuse the same operationId and exact arguments after an uncertain result. Never report a pending proposal as deleted.",
26
+ inputSchema: JSON.parse(JSON.stringify(z.toJSONSchema(SkillRemoveRequest))),
27
+ annotations: {
28
+ readOnlyHint: false,
29
+ destructiveHint: true,
30
+ idempotentHint: true,
31
+ openWorldHint: false,
32
+ },
33
+ source: "opengeni",
34
+ approval: "none",
35
+ execute: async (args) => {
36
+ await input.authorize();
37
+ const output = await input.remove(SkillRemoveRequest.parse(args));
38
+ const text = JSON.stringify(output);
39
+ return {
40
+ isError: false,
41
+ content: [{ type: "text", text }],
42
+ structuredContent: JSON.parse(text),
43
+ };
44
+ },
45
+ };
46
+ }