@opengeni/worker-bundle 4.4.1 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1477,7 +1477,7 @@ async function createDefaultWorkerActivities(role, dependencies = {}) {
1477
1477
  if (dependencies.objectStorage == null) {
1478
1478
  prepareObjectStorageProvider(dependencies.settings ?? getSettings());
1479
1479
  }
1480
- return role === "control" ? (await import("./activities-control-BXAGB3AU.js")).createControlActivities(dependencies) : (await import("./activities-turn-G33RWTX2.js")).createTurnActivities(dependencies);
1480
+ return role === "control" ? (await import("./activities-control-BXAGB3AU.js")).createControlActivities(dependencies) : (await import("./activities-turn-B73PNSEA.js")).createTurnActivities(dependencies);
1481
1481
  }
1482
1482
  function resolveOpenGeniWorkflowDefinition(moduleUrl = import.meta.url) {
1483
1483
  const modulePath = fileURLToPath(moduleUrl).replaceAll("\\", "/");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/worker-bundle",
3
- "version": "4.4.1",
3
+ "version": "4.5.0",
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": {
@@ -56,32 +56,32 @@
56
56
  "@1password/sdk": "0.5.0",
57
57
  "@keeper-security/secrets-manager-core": "17.6.0",
58
58
  "@llamaindex/liteparse": "2.14.2",
59
- "@opengeni/agent-proto": "4.4.1",
60
- "@opengeni/capabilities": "4.4.1",
61
- "@opengeni/codemode": "4.4.1",
62
- "@opengeni/codex": "4.4.1",
63
- "@opengeni/config": "4.4.1",
64
- "@opengeni/contracts": "4.4.1",
65
- "@opengeni/core": "4.4.1",
66
- "@opengeni/db": "4.4.1",
67
- "@opengeni/decisions": "4.4.1",
68
- "@opengeni/documents": "4.4.1",
69
- "@opengeni/events": "4.4.1",
70
- "@opengeni/github": "4.4.1",
71
- "@opengeni/interaction": "4.4.1",
72
- "@opengeni/jev": "4.4.1",
73
- "@opengeni/network": "4.4.1",
74
- "@opengeni/observability": "4.4.1",
75
- "@opengeni/runtime": "4.4.1",
76
- "@opengeni/sdk": "4.4.1",
77
- "@opengeni/storage": "4.4.1",
78
- "@opengeni/subscriptions": "4.4.1",
79
- "@opengeni/tool-gateway": "4.4.1",
80
- "@opengeni/vaults": "4.4.1",
81
- "@opengeni/vaults-keeper": "4.4.1",
82
- "@opengeni/vaults-onepassword": "4.4.1",
83
- "@opengeni/vaults-protonpass": "4.4.1",
84
- "@opengeni/xai-subscription": "4.4.1",
59
+ "@opengeni/agent-proto": "4.5.0",
60
+ "@opengeni/capabilities": "4.5.0",
61
+ "@opengeni/codemode": "4.5.0",
62
+ "@opengeni/codex": "4.5.0",
63
+ "@opengeni/config": "4.5.0",
64
+ "@opengeni/contracts": "4.5.0",
65
+ "@opengeni/core": "4.5.0",
66
+ "@opengeni/db": "4.5.0",
67
+ "@opengeni/decisions": "4.5.0",
68
+ "@opengeni/documents": "4.5.0",
69
+ "@opengeni/events": "4.5.0",
70
+ "@opengeni/github": "4.5.0",
71
+ "@opengeni/interaction": "4.5.0",
72
+ "@opengeni/jev": "4.5.0",
73
+ "@opengeni/network": "4.5.0",
74
+ "@opengeni/observability": "4.5.0",
75
+ "@opengeni/runtime": "4.5.0",
76
+ "@opengeni/sdk": "4.5.0",
77
+ "@opengeni/storage": "4.5.0",
78
+ "@opengeni/subscriptions": "4.5.0",
79
+ "@opengeni/tool-gateway": "4.5.0",
80
+ "@opengeni/vaults": "4.5.0",
81
+ "@opengeni/vaults-keeper": "4.5.0",
82
+ "@opengeni/vaults-onepassword": "4.5.0",
83
+ "@opengeni/vaults-protonpass": "4.5.0",
84
+ "@opengeni/xai-subscription": "4.5.0",
85
85
  "@temporalio/activity": "^1.17.0",
86
86
  "@temporalio/client": "^1.17.0",
87
87
  "@temporalio/worker": "^1.17.0",
@@ -0,0 +1,207 @@
1
+ import {
2
+ runOwnedSandboxSetup,
3
+ withRunCredentialsSession,
4
+ type GitCredentialTokenWriterSession,
5
+ type PreparedAgentInput,
6
+ type RunCredentialCommandSession,
7
+ } from "@opengeni/runtime";
8
+ import {
9
+ recordSandboxSharedPreparation,
10
+ recordTurnStartupPhase,
11
+ } from "../../observability-metrics";
12
+ import { createSharedRigSetupCoordinator } from "./sandbox-shared-preparation";
13
+ import type { TurnStreamAttemptDeps } from "./stream-attempt";
14
+ import { ownedTurnSandboxForAgent } from "./turn-sandbox-access";
15
+
16
+ export type EagerOwnedSandboxSetupDeps = Pick<
17
+ TurnStreamAttemptDeps,
18
+ | "input"
19
+ | "db"
20
+ | "observability"
21
+ | "runtimeCancellationSignal"
22
+ | "sandboxState"
23
+ | "eventing"
24
+ | "leases"
25
+ | "runWorkspaceMutationForSandbox"
26
+ | "attachGitCredentialRenewal"
27
+ | "attachRunCredentialRenewal"
28
+ | "activeSandboxBackend"
29
+ | "groupBoxBackend"
30
+ | "turnExecutionPolicy"
31
+ | "agent"
32
+ | "runSettings"
33
+ | "initialRunCredentialMaterial"
34
+ | "initialGitCredentials"
35
+ | "sandboxEnvironment"
36
+ | "fileResourceDownloads"
37
+ >;
38
+
39
+ export type EagerOwnedSandboxSetupOptions = {
40
+ /** The worker delivers this turn's file resources itself. */
41
+ fileDownloadsMaterialized: boolean;
42
+ /** Receives the runtime's file-failure note when the runtime delivers files. */
43
+ preparedInput?: PreparedAgentInput;
44
+ };
45
+
46
+ /**
47
+ * Whether this turn's platform setup runs eagerly against an owned box the
48
+ * worker already holds. Lazy boxes run the same setup in their first-operation
49
+ * provisioner, and connected machines receive no platform setup.
50
+ */
51
+ export function hasEagerOwnedSandboxSetup(
52
+ sandboxState: EagerOwnedSandboxSetupDeps["sandboxState"],
53
+ ): boolean {
54
+ return Boolean(
55
+ sandboxState.resolvedSandbox &&
56
+ !sandboxState.lazyOwnedSandbox &&
57
+ ownedTurnSandboxForAgent(sandboxState),
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Run the turn's platform setup (rig setup, credential hooks, repository
63
+ * clones) against the eager owned box. The setup settles its workspace
64
+ * admission before returning, so no admission stays open into the model stream.
65
+ */
66
+ export async function runEagerOwnedSandboxSetup(
67
+ deps: EagerOwnedSandboxSetupDeps,
68
+ options: EagerOwnedSandboxSetupOptions,
69
+ ): Promise<void> {
70
+ const { input, sandboxState, eventing, leases, observability } = deps;
71
+ const eagerResolvedSandbox = sandboxState.resolvedSandbox;
72
+ const ownedEstablished = ownedTurnSandboxForAgent(sandboxState);
73
+ if (!eagerResolvedSandbox || sandboxState.lazyOwnedSandbox || !ownedEstablished) return;
74
+ const ownedSandboxSetupStartedAt = performance.now();
75
+ let ownedSandboxSetupOutcome: "completed" | "failed" = "completed";
76
+ try {
77
+ const eagerSetupSession = sandboxState.setupBoxSession ?? ownedEstablished.session;
78
+ // The worker owns platform setup for owned boxes, so the runtime skips its
79
+ // credential session callback. Materialize host-managed run credentials
80
+ // before any setup command (including provider login hooks), then decorate
81
+ // setup commands so they source the active generation.
82
+ await deps.attachRunCredentialRenewal(
83
+ eagerSetupSession as RunCredentialCommandSession,
84
+ deps.initialRunCredentialMaterial,
85
+ eagerResolvedSandbox,
86
+ );
87
+ const eagerCredentialSetupSession = deps.initialRunCredentialMaterial
88
+ ? withRunCredentialsSession(eagerSetupSession as object, input.sessionId)
89
+ : eagerSetupSession;
90
+ await deps.runWorkspaceMutationForSandbox(
91
+ eagerResolvedSandbox,
92
+ "eagerOwnedSandboxSetup",
93
+ async () =>
94
+ await runOwnedSandboxSetup(
95
+ deps.agent,
96
+ ownedEstablished.session as never,
97
+ eagerCredentialSetupSession as never,
98
+ {
99
+ settings: deps.runSettings,
100
+ environment: deps.sandboxEnvironment,
101
+ ...(options.preparedInput ? { preparedInput: options.preparedInput } : {}),
102
+ ...(options.fileDownloadsMaterialized ? { fileDownloadsMaterialized: true } : {}),
103
+ onRuntimeEvent: async (event) => {
104
+ await leases.renewServing("runtime_event");
105
+ if (leases.servingLost()) {
106
+ throw new Error("Provider credential lease expired during sandbox setup");
107
+ }
108
+ await eventing.publish([{ type: event.type, payload: event.payload }], true);
109
+ },
110
+ ...(eventing.toolCancellationFenceRef.current
111
+ ? {
112
+ commandRunner: eventing.toolCancellationFenceRef.current.runSandboxCommand.bind(
113
+ eventing.toolCancellationFenceRef.current,
114
+ ),
115
+ }
116
+ : {}),
117
+ ...(sandboxState.sandboxGroupId && sandboxState.sandboxHolderId
118
+ ? {
119
+ coordinateSharedRigSetup: createSharedRigSetupCoordinator({
120
+ db: deps.db,
121
+ accountId: input.accountId,
122
+ workspaceId: input.workspaceId,
123
+ sandboxGroupId: sandboxState.sandboxGroupId,
124
+ attemptId: input.attemptId,
125
+ holderId: sandboxState.sandboxHolderId,
126
+ sandbox: eagerResolvedSandbox,
127
+ ...(deps.runtimeCancellationSignal
128
+ ? { signal: deps.runtimeCancellationSignal }
129
+ : {}),
130
+ observe: (measurement) =>
131
+ recordSandboxSharedPreparation(observability, measurement),
132
+ }),
133
+ }
134
+ : {}),
135
+ },
136
+ ),
137
+ );
138
+ await deps.attachGitCredentialRenewal(
139
+ eagerSetupSession as GitCredentialTokenWriterSession,
140
+ deps.initialGitCredentials,
141
+ );
142
+ } catch (error) {
143
+ ownedSandboxSetupOutcome = "failed";
144
+ throw error;
145
+ } finally {
146
+ recordTurnStartupPhase(observability, {
147
+ phase: "owned_sandbox_setup",
148
+ provider: deps.turnExecutionPolicy.providerId,
149
+ backend: deps.activeSandboxBackend ?? deps.groupBoxBackend,
150
+ outcome: ownedSandboxSetupOutcome,
151
+ durationSeconds: (performance.now() - ownedSandboxSetupStartedAt) / 1_000,
152
+ count: deps.fileResourceDownloads.length,
153
+ });
154
+ }
155
+ }
156
+
157
+ /**
158
+ * The eager platform setup for each model stream of one turn attempt.
159
+ *
160
+ * The first stream's setup may already be running: it needs nothing from the
161
+ * model input, so the worker starts it with file delivery and input
162
+ * preparation instead of after them. Each later stream of the attempt runs the
163
+ * idempotent setup again, as before.
164
+ */
165
+ export type EagerOwnedSandboxSetup = {
166
+ /** Run, or join the already started, setup for the next model stream. */
167
+ beforeModelStream(options: EagerOwnedSandboxSetupOptions): Promise<void>;
168
+ /** Resolves once a started setup has finished, whatever its outcome. */
169
+ settled(): Promise<void>;
170
+ };
171
+
172
+ export function createEagerOwnedSandboxSetup(
173
+ deps: EagerOwnedSandboxSetupDeps,
174
+ start?: { fileDownloadsMaterialized: boolean },
175
+ runSetup: typeof runEagerOwnedSandboxSetup = runEagerOwnedSandboxSetup,
176
+ ): EagerOwnedSandboxSetup {
177
+ const startedFor = start ? sandboxIdentity(deps.sandboxState) : null;
178
+ let started: Promise<void> | null = start
179
+ ? runSetup(deps, {
180
+ fileDownloadsMaterialized: start.fileDownloadsMaterialized,
181
+ })
182
+ : null;
183
+ // Observing the outcome here keeps an early failure from being unhandled;
184
+ // the first model stream still awaits the original promise and rethrows it.
185
+ const settled = started ? started.then(noop, noop) : Promise.resolve();
186
+ return {
187
+ beforeModelStream: async (options) => {
188
+ if (started) {
189
+ const prestarted = started;
190
+ started = null;
191
+ await prestarted;
192
+ // A box rebound while the setup ran (rotation, recovery) gets its
193
+ // own setup; the prestarted one only covered the earlier box.
194
+ if (sandboxIdentity(deps.sandboxState) === startedFor) return;
195
+ }
196
+ await runSetup(deps, options);
197
+ },
198
+ settled: () => settled,
199
+ };
200
+ }
201
+
202
+ function sandboxIdentity(sandboxState: EagerOwnedSandboxSetupDeps["sandboxState"]): string | null {
203
+ const resolved = sandboxState.resolvedSandbox;
204
+ return resolved ? `${resolved.leaseEpoch}:${resolved.established.instanceId}` : null;
205
+ }
206
+
207
+ function noop(): void {}