@opengeni/worker-bundle 0.28.4-canary.0 → 0.28.4-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -782,7 +782,7 @@ function temporalWorkflowExecutionNotFound(error) {
782
782
  return isGrpcServiceError(error) && error.code === 5;
783
783
  }
784
784
  async function createDefaultWorkerActivities(role, dependencies = {}) {
785
- return role === "control" ? (await import("./activities-control-J5QNRWVH.js")).createControlActivities(dependencies) : (await import("./activities-turn-ZJBY3W5D.js")).createTurnActivities(dependencies);
785
+ return role === "control" ? (await import("./activities-control-MKGEOBXT.js")).createControlActivities(dependencies) : (await import("./activities-turn-5MZZ6HQA.js")).createTurnActivities(dependencies);
786
786
  }
787
787
  function resolveOpenGeniWorkflowDefinition(moduleUrl = import.meta.url) {
788
788
  const modulePath = fileURLToPath(moduleUrl).replaceAll("\\", "/");
@@ -2,7 +2,7 @@ import { type Settings } from "@opengeni/config";
2
2
  import { type Database, type EnrollmentRecord } from "@opengeni/db";
3
3
  import type { OpStreamOutputFrame } from "@opengeni/runtime/sandbox";
4
4
  import type { EventBus } from "@opengeni/events";
5
- import { type EstablishedSandboxSession, type RoutingSandboxFirstOperationObserver, type RoutingSandboxOperationObserver, type SelfhostedOpObserver, type SelfhostedRelayConfig, type OpStreamJournal } from "@opengeni/runtime";
5
+ import { type EstablishedSandboxSession, type RoutingSandboxFirstOperationObserver, type RoutingSandboxOperationObserver, type SelfhostedOpObserver, type SelfhostedConnectionBinding, type SelfhostedRelayConfig, type OpStreamJournal } from "@opengeni/runtime";
6
6
  export type RoutingWiringServices = {
7
7
  db: Database;
8
8
  settings: Settings;
@@ -179,6 +179,8 @@ export type SelfhostedTurnSessionArgs = {
179
179
  operationResourcePolicy: EnrollmentRecord["operationPolicy"];
180
180
  operationResourcePolicySupported: boolean;
181
181
  operationCpuQuotaSupported: boolean;
182
+ /** Feature advertisement only; refreshed by exact live admission for each operation. */
183
+ transactionalFsWriteSupported?: boolean;
182
184
  /** The active pointer's epoch — the control-op fence echoed to the agent. */
183
185
  epoch: number;
184
186
  /** The run's declared sandbox environment (the SAME object fed to buildAgent +
@@ -197,6 +199,9 @@ export type SelfhostedTurnSessionArgs = {
197
199
  sessionId: string;
198
200
  };
199
201
  };
202
+ /** Project one enrollment read into one exact runtime route. Capability and
203
+ * process identity must be taken from the same snapshot. */
204
+ export declare function connectionBindingFor(services: RoutingWiringServices, enrollment: EnrollmentRecord | null): SelfhostedConnectionBinding | null;
200
205
  export declare function establishSelfhostedTurnSession(services: RoutingWiringServices, args: SelfhostedTurnSessionArgs): Promise<EstablishedSandboxSession>;
201
206
  /** Whether the routing proxy should wrap the turn box: the hot-swap feature is
202
207
  * gated by the selfhosted flag (the active pointer + swap tools are only
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/worker-bundle",
3
- "version": "0.28.4-canary.0",
3
+ "version": "0.28.4-canary.1",
4
4
  "description": "OpenGeni worker entry and reusable embedded lifecycle, shipped with a release-coherent pre-bundled Temporal workflow artifact.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -54,23 +54,23 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@llamaindex/liteparse": "2.14.2",
57
- "@opengeni/agent-proto": "^0.5.1-canary.26",
58
- "@opengeni/capabilities": "^0.3.3-canary.8",
59
- "@opengeni/codemode": "^0.5.6-canary.0",
60
- "@opengeni/codex": "^0.2.22-canary.8",
61
- "@opengeni/config": "^1.1.2-canary.0",
62
- "@opengeni/contracts": "^3.0.2-canary.0",
63
- "@opengeni/core": "^2.9.4-canary.0",
64
- "@opengeni/db": "^4.3.3-canary.0",
65
- "@opengeni/documents": "^0.8.27-canary.0",
66
- "@opengeni/events": "^0.4.25-canary.0",
67
- "@opengeni/github": "^0.7.10-canary.0",
68
- "@opengeni/network": "^0.3.1-canary.8",
69
- "@opengeni/observability": "^0.8.26-canary.0",
70
- "@opengeni/runtime": "^2.5.3-canary.0",
71
- "@opengeni/storage": "^0.2.127-canary.0",
72
- "@opengeni/tool-gateway": "^0.1.7-canary.0",
73
- "@opengeni/xai-subscription": "^0.1.4-canary.8",
57
+ "@opengeni/agent-proto": "^0.5.1-canary.27",
58
+ "@opengeni/capabilities": "^0.3.3-canary.9",
59
+ "@opengeni/codemode": "^0.5.6-canary.1",
60
+ "@opengeni/codex": "^0.2.22-canary.9",
61
+ "@opengeni/config": "^1.1.2-canary.1",
62
+ "@opengeni/contracts": "^3.0.2-canary.1",
63
+ "@opengeni/core": "^2.9.4-canary.1",
64
+ "@opengeni/db": "^4.3.3-canary.1",
65
+ "@opengeni/documents": "^0.8.27-canary.1",
66
+ "@opengeni/events": "^0.4.25-canary.1",
67
+ "@opengeni/github": "^0.7.10-canary.1",
68
+ "@opengeni/network": "^0.3.1-canary.9",
69
+ "@opengeni/observability": "^0.8.26-canary.1",
70
+ "@opengeni/runtime": "^2.5.3-canary.1",
71
+ "@opengeni/storage": "^0.2.127-canary.1",
72
+ "@opengeni/tool-gateway": "^0.1.7-canary.1",
73
+ "@opengeni/xai-subscription": "^0.1.4-canary.9",
74
74
  "@temporalio/activity": "^1.17.0",
75
75
  "@temporalio/client": "^1.17.0",
76
76
  "@temporalio/worker": "^1.17.0",
@@ -5,7 +5,7 @@ import {
5
5
  CodexCredentialLeaseAttemptFencedError,
6
6
  CodexCredentialFailoverExhaustedError,
7
7
  CODEX_CREDENTIAL_LEASE_TTL_MS,
8
- recordSessionActiveCodexCredential,
8
+ recordSessionCodexSelectionForTurnAttempt,
9
9
  setSessionCodexPinInTransaction,
10
10
  settleCodexCredentialFailover,
11
11
  withSessionCodexCapacityMutation,
@@ -288,19 +288,40 @@ export async function selectCodexTurnCapacity(
288
288
  leases.codex.confirmedUntilMs !== null;
289
289
  if (leases.codex.held) leases.codex.startHeartbeat();
290
290
 
291
+ const eligibleCount = leased.accounts.filter((account) =>
292
+ isCodexCredentialEligible(account, new Date()),
293
+ ).length;
294
+ const selectionReceipt = providerTurn.effectiveCodexCredentialId
295
+ ? await recordSessionCodexSelectionForTurnAttempt(db, {
296
+ workspaceId: input.workspaceId,
297
+ sessionId: input.sessionId,
298
+ turnId,
299
+ attemptId: input.attemptId,
300
+ executionGeneration: attempt.executionGeneration,
301
+ credentialId: providerTurn.effectiveCodexCredentialId,
302
+ strategy: leased.rotationStrategy,
303
+ reusedLease: leased.reused,
304
+ pinnedCredentialId: sessionPin,
305
+ pinSource: sessionPinSource,
306
+ eligibleCount,
307
+ connectedCount: leased.accounts.length,
308
+ })
309
+ : null;
310
+ if (selectionReceipt)
311
+ await publishDurableSessionEvents(
312
+ bus,
313
+ input.workspaceId,
314
+ input.sessionId,
315
+ selectionReceipt.events,
316
+ );
317
+ const selectionDiagnostics = selectionReceipt?.diagnostics ?? null;
291
318
  const actualOutcome = providerTurn.effectiveCodexCredentialId
292
319
  ? "selected"
293
320
  : rotationDecision.kind === "allCapped" || rotationDecision.kind === "allocatorDisabled"
294
321
  ? "waiting"
295
322
  : "none";
296
- const actualReason = providerTurn.effectiveCodexCredentialId
297
- ? leased.reused
298
- ? "lease_reused"
299
- : sessionPin === providerTurn.effectiveCodexCredentialId
300
- ? "pin"
301
- : rotationDecision.kind === "active" && rotationDecision.moved
302
- ? "rotation"
303
- : "active"
323
+ const actualReason = selectionDiagnostics
324
+ ? selectionDiagnostics.reason
304
325
  : rotationDecision.kind === "allCapped"
305
326
  ? "all_capped"
306
327
  : rotationDecision.kind === "allocatorDisabled"
@@ -362,9 +383,6 @@ export async function selectCodexTurnCapacity(
362
383
  });
363
384
  }
364
385
 
365
- const eligibleCount = leased.accounts.filter((account) =>
366
- isCodexCredentialEligible(account, new Date()),
367
- ).length;
368
386
  const poolDepth = eligibleCount === 0 ? "zero" : eligibleCount === 1 ? "one" : "many";
369
387
  observability.incrementCounter({
370
388
  name: "opengeni_codex_pool_observations_total",
@@ -690,34 +708,7 @@ export async function selectCodexTurnCapacity(
690
708
  return { exit: claimedResult({ status: "cancelled" }) };
691
709
  }
692
710
  if (providerTurn.effectiveCodexCredentialId) {
693
- const priorAccountId = lockedSessionCodexState.lastCredentialId;
694
- if (priorAccountId !== providerTurn.effectiveCodexCredentialId) {
695
- await recordSessionActiveCodexCredential(
696
- db,
697
- input.workspaceId,
698
- input.sessionId,
699
- providerTurn.effectiveCodexCredentialId,
700
- );
701
- const rotated = rotationDecision.kind === "active" && rotationDecision.moved;
702
- await eventing.publish([
703
- {
704
- type: "codex.account.switched",
705
- payload: {
706
- fromAccountId: priorAccountId,
707
- toAccountId: providerTurn.effectiveCodexCredentialId,
708
- reason: rotated ? "rotation" : "manual",
709
- },
710
- },
711
- ]);
712
- }
713
-
714
- const selectionReason = leased.reused
715
- ? "lease_reused"
716
- : sessionPin === providerTurn.effectiveCodexCredentialId
717
- ? "pin"
718
- : rotationDecision.kind === "active" && rotationDecision.moved
719
- ? "rotation"
720
- : "active";
711
+ const selectionReason = selectionDiagnostics!.reason;
721
712
  observability.incrementCounter({
722
713
  name: "opengeni_codex_credential_selections_total",
723
714
  help: "Codex credential selections by strategy and reason.",
@@ -727,19 +718,6 @@ export async function selectCodexTurnCapacity(
727
718
  reason: selectionReason,
728
719
  },
729
720
  });
730
- await eventing.publish([
731
- {
732
- type: "codex.credential.selected",
733
- payload: {
734
- credentialId: providerTurn.effectiveCodexCredentialId,
735
- strategy: leased.rotationStrategy,
736
- reason: selectionReason,
737
- eligibleCount,
738
- connectedCount: leased.accounts.length,
739
- reused: leased.reused,
740
- },
741
- },
742
- ]);
743
721
  }
744
722
  } catch (error) {
745
723
  credentialSelectionOutcome = "failed";
@@ -0,0 +1 @@
1
+ export { codexSelectionDiagnostics } from "@opengeni/db";
@@ -475,6 +475,8 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
475
475
  liveMachineEnrollment?.agentCapabilities.operationResourcePolicy === true,
476
476
  operationCpuQuotaSupported:
477
477
  liveMachineEnrollment?.agentCapabilities.operationCpuQuota === true,
478
+ transactionalFsWriteSupported:
479
+ liveMachineEnrollment?.agentCapabilities.transactionalFsWrite === true,
478
480
  ...(activeSandboxRecord!.scope === "user" && fileAuthoritySubjectId
479
481
  ? {
480
482
  personalMachineAttempt: {
@@ -1,6 +1,5 @@
1
1
  import type { AttemptToolDefinition } from "@opengeni/codemode";
2
2
  import { PublicSkillSearchError, type PublicSkillSearchClient } from "@opengeni/core";
3
- import { listSkillLibraryEntries } from "@opengeni/runtime/skill-library";
4
3
 
5
4
  export type WorkspaceSkillSearchEntry = Readonly<{
6
5
  id: string;
@@ -23,7 +22,7 @@ export function createSkillSearchAttemptToolDefinition(input: {
23
22
  codemodePath: ["opengeni", "skill_search"],
24
23
  title: "Search Skills",
25
24
  description:
26
- "Find installed workspace Skills or available curated and public Skills. Returns identifiers and install sources, not file contents. Search never installs or starts a sandbox. Scope defaults to all; use installed to avoid external search.",
25
+ "Find installed workspace Skills or available public Skills. Returns identifiers and install sources, not file contents. Search never installs or starts a sandbox. Scope defaults to all; use installed to avoid external search.",
27
26
  inputSchema: {
28
27
  type: "object",
29
28
  properties: {
@@ -60,19 +59,6 @@ export function createSkillSearchAttemptToolDefinition(input: {
60
59
  source: entry.source ?? "workspace",
61
60
  installed: !entry.source || entry.source === "workspace",
62
61
  }));
63
- const libraryHits =
64
- scope === "installed"
65
- ? []
66
- : listSkillLibraryEntries()
67
- .filter(matches)
68
- .slice(0, limit)
69
- .map((entry) => ({
70
- id: `library:${entry.id}`,
71
- name: entry.name,
72
- description: entry.description,
73
- source: "library" as const,
74
- libraryId: entry.id,
75
- }));
76
62
  let publicResult: Awaited<ReturnType<PublicSkillSearchClient["search"]>> | null = null;
77
63
  let publicError: {
78
64
  source: "skills_sh";
@@ -93,7 +79,7 @@ export function createSkillSearchAttemptToolDefinition(input: {
93
79
  }
94
80
  const output = {
95
81
  workspace: workspaceHits,
96
- library: libraryHits,
82
+ library: [], // Retained response field for older clients.
97
83
  public: publicResult?.items.map((entry) => ({ ...entry })) ?? [],
98
84
  // A provider outage is not an empty search. Keep local results useful
99
85
  // and expose partial failure explicitly, without leaking network details.
@@ -18,6 +18,9 @@ export type CodexFleetActualDecisionV1 = {
18
18
  candidateKey: string | null;
19
19
  reason:
20
20
  | "lease_reused"
21
+ | "affinity_reused"
22
+ | "assigned"
23
+ | "switched"
21
24
  | "pin"
22
25
  | "rotation"
23
26
  | "active"
@@ -26,6 +26,7 @@ import {
26
26
  appendSessionEventToSandboxGroup,
27
27
  bindRetainedProcessProviderIdentity,
28
28
  claimWorkspaceArchiveCapture,
29
+ releaseWorkspaceArchiveCapture,
29
30
  claimSandboxCheckpointArtifactsForGc,
30
31
  claimTerminalRetainedProcesses,
31
32
  countActiveRetainedProcessesByOwnerState,
@@ -249,6 +250,7 @@ export type TerminateBoxFn = (
249
250
  captureDisposition?: DrainCaptureDisposition,
250
251
  capturePolicy?: ProviderWorkspaceCapturePolicy | null,
251
252
  diskBackedArchives?: boolean,
253
+ releaseFailedCapture?: () => Promise<void>,
252
254
  ) => Promise<boolean | ProviderTerminationOutcome>;
253
255
 
254
256
  export type SweepModalOrphansFn = (
@@ -350,12 +352,13 @@ export class SandboxProviderCaptureTimeoutError extends Error {
350
352
  }
351
353
  }
352
354
 
353
- async function awaitProviderCaptureWithLatePublication<T>(input: {
355
+ export async function awaitProviderCaptureWithLatePublication<T>(input: {
354
356
  capture: Promise<T>;
355
357
  timeoutMs: number;
356
358
  timeoutError: SandboxProviderCaptureTimeoutError;
357
359
  publishLate: (value: T) => Promise<void>;
358
360
  observeLateFailure: (error: unknown) => void;
361
+ observeLatePublicationFailure: (error: unknown) => void;
359
362
  }): Promise<T> {
360
363
  let timeout: ReturnType<typeof setTimeout> | undefined;
361
364
  const timeoutError = input.timeoutError;
@@ -373,7 +376,9 @@ async function awaitProviderCaptureWithLatePublication<T>(input: {
373
376
  // durable archive publication: never provider teardown or a cold commit from
374
377
  // an activity Temporal has already rejected. A replaced claim fences this
375
378
  // callback; Modal candidates then flow to artifact-ledger GC.
376
- void input.capture.then(input.publishLate).catch(input.observeLateFailure);
379
+ void input.capture
380
+ .then(input.publishLate, input.observeLateFailure)
381
+ .catch(input.observeLatePublicationFailure);
377
382
  throw error;
378
383
  } finally {
379
384
  if (timeout) clearTimeout(timeout);
@@ -482,6 +487,7 @@ export function createSandboxLeaseActivities(
482
487
  captureDisposition,
483
488
  capturePolicy,
484
489
  diskBackedArchives,
490
+ releaseFailedCapture,
485
491
  ) =>
486
492
  await terminateProviderBox(
487
493
  settings,
@@ -494,6 +500,7 @@ export function createSandboxLeaseActivities(
494
500
  captureDisposition,
495
501
  capturePolicy,
496
502
  diskBackedArchives,
503
+ releaseFailedCapture,
497
504
  ));
498
505
  const sweepModalOrphans: SweepModalOrphansFn =
499
506
  options.sweepModalOrphans ?? sweepModalOrphansForConfiguredBackend;
@@ -2803,6 +2810,17 @@ async function terminateDrainableBox(
2803
2810
  captureDisposition,
2804
2811
  capturePolicy,
2805
2812
  Boolean(objectStorage),
2813
+ async () => {
2814
+ if (!captureClaim || !lease.instanceId) return;
2815
+ await releaseWorkspaceArchiveCapture(db, {
2816
+ accountId,
2817
+ workspaceId: row.workspaceId,
2818
+ sandboxGroupId: row.sandboxGroupId,
2819
+ captureId: captureClaim.id,
2820
+ expectedEpoch: row.leaseEpoch,
2821
+ expectedInstanceId: lease.instanceId,
2822
+ });
2823
+ },
2806
2824
  );
2807
2825
  const terminated = typeof termination === "boolean" ? termination : termination.terminated;
2808
2826
  if (!terminated) {
@@ -2936,10 +2954,21 @@ export async function terminateProviderBox(
2936
2954
  captureDisposition: DrainCaptureDisposition = "capture_required",
2937
2955
  claimedCapturePolicy?: ProviderWorkspaceCapturePolicy | null,
2938
2956
  diskBackedArchives = false,
2957
+ releaseFailedCapture?: () => Promise<void>,
2939
2958
  ): Promise<ProviderTerminationOutcome> {
2940
2959
  const durableBackendId = (lease.resumeBackendId ?? lease.backend) as string;
2941
2960
  const backend = sandboxBackendForSdkBackendId(durableBackendId) ?? durableBackendId;
2942
2961
  const logIdentity = providerDrainLogIdentity(lease, backend);
2962
+ const releaseAfterCaptureFailure = async (): Promise<void> => {
2963
+ try {
2964
+ await releaseFailedCapture?.();
2965
+ } catch (error) {
2966
+ observability.warn("sandbox reaper: failed capture claim release failed", {
2967
+ ...logIdentity,
2968
+ error: error instanceof Error ? error.message : String(error),
2969
+ });
2970
+ }
2971
+ };
2943
2972
  // 'none' / no backend -> nothing to terminate.
2944
2973
  if (!backend || backend === "none") {
2945
2974
  return { terminated: true, providerMissingBeforeCapture: false };
@@ -3187,7 +3216,17 @@ export async function terminateProviderBox(
3187
3216
  await disposeWorkspaceArchive(archive);
3188
3217
  }
3189
3218
  },
3219
+ observeLatePublicationFailure: (error) => {
3220
+ observability.warn("sandbox reaper: late capture publication cleanup failed", {
3221
+ ...logIdentity,
3222
+ error: error instanceof Error ? error.message : String(error),
3223
+ });
3224
+ },
3190
3225
  observeLateFailure: (error) => {
3226
+ // Capture has now rejected and this timed-out path can never reach
3227
+ // provider teardown. Release only our unpublished claim so a waiting
3228
+ // turn can re-arm the intact live box; stale callbacks are DB-fenced.
3229
+ void releaseAfterCaptureFailure();
3191
3230
  observability.warn("sandbox reaper: timed-out provider capture later failed", {
3192
3231
  ...logIdentity,
3193
3232
  error: error instanceof Error ? error.message : String(error),
@@ -3209,6 +3248,12 @@ export async function terminateProviderBox(
3209
3248
  error: error instanceof Error ? error.message : String(error),
3210
3249
  },
3211
3250
  );
3251
+ // An unresolved timeout is not proof of capture failure: retain the fence
3252
+ // until its provider promise settles. A settled failure cannot enter the
3253
+ // teardown path below, so its unpublished claim may safely be released.
3254
+ if (!(error instanceof SandboxProviderCaptureTimeoutError)) {
3255
+ await releaseAfterCaptureFailure();
3256
+ }
3212
3257
  // NEVER terminate a box whose snapshot we could not capture.
3213
3258
  throw error;
3214
3259
  }
@@ -1383,6 +1383,8 @@ export type SelfhostedTurnSessionArgs = {
1383
1383
  operationResourcePolicy: EnrollmentRecord["operationPolicy"];
1384
1384
  operationResourcePolicySupported: boolean;
1385
1385
  operationCpuQuotaSupported: boolean;
1386
+ /** Feature advertisement only; refreshed by exact live admission for each operation. */
1387
+ transactionalFsWriteSupported?: boolean;
1386
1388
  /** The active pointer's epoch — the control-op fence echoed to the agent. */
1387
1389
  epoch: number;
1388
1390
  /** The run's declared sandbox environment (the SAME object fed to buildAgent +
@@ -1442,7 +1444,7 @@ function opStreamDepsFor(
1442
1444
 
1443
1445
  /** Project one enrollment read into one exact runtime route. Capability and
1444
1446
  * process identity must be taken from the same snapshot. */
1445
- function connectionBindingFor(
1447
+ export function connectionBindingFor(
1446
1448
  services: RoutingWiringServices,
1447
1449
  enrollment: EnrollmentRecord | null,
1448
1450
  ): SelfhostedConnectionBinding | null {
@@ -1456,6 +1458,7 @@ function connectionBindingFor(
1456
1458
  operationResourcePolicy: enrollment.operationPolicy,
1457
1459
  operationResourcePolicySupported: enrollment.agentCapabilities.operationResourcePolicy === true,
1458
1460
  operationCpuQuotaSupported: enrollment.agentCapabilities.operationCpuQuota === true,
1461
+ transactionalFsWriteSupported: enrollment.agentCapabilities.transactionalFsWrite === true,
1459
1462
  };
1460
1463
  }
1461
1464
 
@@ -1526,6 +1529,7 @@ export async function establishSelfhostedTurnSession(
1526
1529
  operationResourcePolicy: args.operationResourcePolicy,
1527
1530
  operationResourcePolicySupported: args.operationResourcePolicySupported,
1528
1531
  operationCpuQuotaSupported: args.operationCpuQuotaSupported,
1532
+ transactionalFsWriteSupported: args.transactionalFsWriteSupported === true,
1529
1533
  resolveOperationAdmission: async () => {
1530
1534
  const enrollment = args.personalMachineAttempt
1531
1535
  ? await resolvePersonalMachineConnectionForAttempt(db, {