@opengeni/worker-bundle 3.1.3-canary.38890816741001 → 3.3.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 (36) hide show
  1. package/dist/activities/agent-turn/admission.d.ts +1 -1
  2. package/dist/activities/agent-turn/secret-resolver.d.ts +4 -2
  3. package/dist/activities/types.d.ts +2 -1
  4. package/dist/{activities-control-ROJJNNTJ.js → activities-control-G2W4FEFP.js} +5 -4
  5. package/dist/{activities-control-ROJJNNTJ.js.map → activities-control-G2W4FEFP.js.map} +1 -1
  6. package/dist/{activities-turn-4SWYWKI5.js → activities-turn-CE2VIWIS.js} +15 -5
  7. package/dist/activities-turn-CE2VIWIS.js.map +1 -0
  8. package/dist/artifact-materializer-entry.js +0 -0
  9. package/dist/artifact-outbox-entry.js +0 -0
  10. package/dist/{chunk-NQDVJ6RI.js → chunk-5DF4V5RD.js} +4 -2
  11. package/dist/{chunk-NQDVJ6RI.js.map → chunk-5DF4V5RD.js.map} +1 -1
  12. package/dist/chunk-K225DMA7.js +85 -0
  13. package/dist/chunk-K225DMA7.js.map +1 -0
  14. package/dist/{chunk-6SHXF4SU.js → chunk-R5BHILMQ.js} +11 -4
  15. package/dist/chunk-R5BHILMQ.js.map +1 -0
  16. package/dist/{chunk-MVESOYFC.js → chunk-RSI63RZG.js} +5 -2
  17. package/dist/{chunk-MVESOYFC.js.map → chunk-RSI63RZG.js.map} +1 -1
  18. package/dist/external-vault-providers.d.ts +29 -0
  19. package/dist/index.js +11 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/{sandbox-lease-BJYU72N3.js → sandbox-lease-2IV3DNXR.js} +3 -3
  22. package/dist/sandbox-routing.d.ts +1 -0
  23. package/dist/workflow-bundle.js +1 -1
  24. package/package.json +27 -24
  25. package/src/activities/agent-turn/run.ts +6 -0
  26. package/src/activities/agent-turn/sandbox-establish.ts +2 -0
  27. package/src/activities/agent-turn/secret-resolver.ts +6 -0
  28. package/src/activities/sandbox-lease.ts +3 -0
  29. package/src/activities/types.ts +5 -1
  30. package/src/activity-services.ts +9 -1
  31. package/src/external-vault-providers.ts +136 -0
  32. package/src/index.ts +7 -1
  33. package/src/sandbox-routing.ts +3 -0
  34. package/dist/activities-turn-4SWYWKI5.js.map +0 -1
  35. package/dist/chunk-6SHXF4SU.js.map +0 -1
  36. /package/dist/{sandbox-lease-BJYU72N3.js.map → sandbox-lease-2IV3DNXR.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/worker-bundle",
3
- "version": "3.1.3-canary.38890816741001",
3
+ "version": "3.3.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": {
@@ -54,30 +54,33 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@1password/sdk": "0.5.0",
57
+ "@keeper-security/secrets-manager-core": "17.6.0",
57
58
  "@llamaindex/liteparse": "2.14.2",
58
- "@opengeni/agent-proto": "3.1.3-canary.38890816741001",
59
- "@opengeni/capabilities": "3.1.3-canary.38890816741001",
60
- "@opengeni/codemode": "3.1.3-canary.38890816741001",
61
- "@opengeni/codex": "3.1.3-canary.38890816741001",
62
- "@opengeni/config": "3.1.3-canary.38890816741001",
63
- "@opengeni/contracts": "3.1.3-canary.38890816741001",
64
- "@opengeni/core": "3.1.3-canary.38890816741001",
65
- "@opengeni/db": "3.1.3-canary.38890816741001",
66
- "@opengeni/documents": "3.1.3-canary.38890816741001",
67
- "@opengeni/events": "3.1.3-canary.38890816741001",
68
- "@opengeni/github": "3.1.3-canary.38890816741001",
69
- "@opengeni/interaction": "3.1.3-canary.38890816741001",
70
- "@opengeni/jev": "3.1.3-canary.38890816741001",
71
- "@opengeni/network": "3.1.3-canary.38890816741001",
72
- "@opengeni/observability": "3.1.3-canary.38890816741001",
73
- "@opengeni/runtime": "3.1.3-canary.38890816741001",
74
- "@opengeni/sdk": "3.1.3-canary.38890816741001",
75
- "@opengeni/storage": "3.1.3-canary.38890816741001",
76
- "@opengeni/subscriptions": "3.1.3-canary.38890816741001",
77
- "@opengeni/tool-gateway": "3.1.3-canary.38890816741001",
78
- "@opengeni/vaults": "3.1.3-canary.38890816741001",
79
- "@opengeni/vaults-onepassword": "3.1.3-canary.38890816741001",
80
- "@opengeni/xai-subscription": "3.1.3-canary.38890816741001",
59
+ "@opengeni/agent-proto": "3.3.0",
60
+ "@opengeni/capabilities": "3.3.0",
61
+ "@opengeni/codemode": "3.3.0",
62
+ "@opengeni/codex": "3.3.0",
63
+ "@opengeni/config": "3.3.0",
64
+ "@opengeni/contracts": "3.3.0",
65
+ "@opengeni/core": "3.3.0",
66
+ "@opengeni/db": "3.3.0",
67
+ "@opengeni/documents": "3.3.0",
68
+ "@opengeni/events": "3.3.0",
69
+ "@opengeni/github": "3.3.0",
70
+ "@opengeni/interaction": "3.3.0",
71
+ "@opengeni/jev": "3.3.0",
72
+ "@opengeni/network": "3.3.0",
73
+ "@opengeni/observability": "3.3.0",
74
+ "@opengeni/runtime": "3.3.0",
75
+ "@opengeni/sdk": "3.3.0",
76
+ "@opengeni/storage": "3.3.0",
77
+ "@opengeni/subscriptions": "3.3.0",
78
+ "@opengeni/tool-gateway": "3.3.0",
79
+ "@opengeni/vaults": "3.3.0",
80
+ "@opengeni/vaults-keeper": "3.3.0",
81
+ "@opengeni/vaults-onepassword": "3.3.0",
82
+ "@opengeni/vaults-protonpass": "3.3.0",
83
+ "@opengeni/xai-subscription": "3.3.0",
81
84
  "@temporalio/activity": "^1.17.0",
82
85
  "@temporalio/client": "^1.17.0",
83
86
  "@temporalio/worker": "^1.17.0",
@@ -1611,6 +1611,12 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
1611
1611
  subjectId: turn.initiatingHumanSubjectId ?? credentialSubjectId ?? null,
1612
1612
  hostVaultProvider: connectionCredentials?.vaultProvider,
1613
1613
  onePasswordVaultProvider: resolvedServices.onePasswordVaultProvider,
1614
+ externalVaultProviders: resolvedServices.externalVaultProviders ?? null,
1615
+ executionContext: {
1616
+ sessionId: input.sessionId,
1617
+ turnId: turn.id,
1618
+ attemptId: input.attemptId,
1619
+ },
1614
1620
  });
1615
1621
  const toolRuntime = await prepareTurnToolRuntime({
1616
1622
  vaultAccess,
@@ -542,6 +542,8 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
542
542
  liveMachineEnrollment?.agentCapabilities.operationCpuQuota === true,
543
543
  transactionalFsWriteSupported:
544
544
  liveMachineEnrollment?.agentCapabilities.transactionalFsWrite === true,
545
+ browserControlHttpSupported:
546
+ liveMachineEnrollment?.agentCapabilities.browserControlHttp === true,
545
547
  // Carried for shared machines too: the audience can narrow to Only me
546
548
  // mid-turn, and each operation then rechecks this authority.
547
549
  ...(fileAuthoritySubjectId
@@ -15,6 +15,7 @@ import {
15
15
  createSecretResolver,
16
16
  createWorkspaceVaultProviderRegistry,
17
17
  type OnePasswordVaultProviderFactory,
18
+ type ExternalVaultProviderFactories,
18
19
  } from "@opengeni/core";
19
20
  import { listVaultsForSubject, type Database } from "@opengeni/db";
20
21
  import {
@@ -22,6 +23,7 @@ import {
22
23
  type ProviderCallBudget,
23
24
  type VaultProvider,
24
25
  type VaultProviderRegistry,
26
+ type VaultProviderAuthority,
25
27
  } from "@opengeni/vaults";
26
28
 
27
29
  /**
@@ -49,6 +51,8 @@ export function createAttemptVaultAccess(input: {
49
51
  subjectId: string | null;
50
52
  hostVaultProvider: VaultProvider | null | undefined;
51
53
  onePasswordVaultProvider: OnePasswordVaultProviderFactory | null | undefined;
54
+ externalVaultProviders?: ExternalVaultProviderFactories | null;
55
+ executionContext?: Pick<VaultProviderAuthority, "sessionId" | "turnId" | "attemptId" | "siteId">;
52
56
  }): AttemptVaultAccess {
53
57
  return {
54
58
  providers: createWorkspaceVaultProviderRegistry({
@@ -58,6 +62,8 @@ export function createAttemptVaultAccess(input: {
58
62
  subjectId: input.subjectId,
59
63
  hostVaultProvider: input.hostVaultProvider ?? null,
60
64
  onePasswordVaultProvider: input.onePasswordVaultProvider ?? null,
65
+ externalVaultProviders: input.externalVaultProviders ?? null,
66
+ ...(input.executionContext ? { executionContext: input.executionContext } : {}),
61
67
  }),
62
68
  attemptBudget: createProviderCallBudget(VAULT_LIMITS.providerReadsPerAttempt),
63
69
  };
@@ -4105,6 +4105,9 @@ async function terminateDrainableBox(
4105
4105
  minIntervalMs: 0,
4106
4106
  pointInTimeCapture: providerWorkspaceCaptureIsPointInTime(backend, lease.resumeState),
4107
4107
  deadlineMandatoryCaptureLeadMs: sandboxDeadlineMandatoryCaptureLeadMs(settings),
4108
+ // An unknown-outcome setup request of a closed attempt never settles
4109
+ // on a live box; after the idle grace it no longer pins the drain.
4110
+ abandonedSetupCaptureAfterMs: Math.max(settings.sandboxIdleGraceMs, 60_000),
4108
4111
  providerReplaySafe: capturePolicy?.takeover === "same_request",
4109
4112
  takeoverSafe: capturePolicy !== null && capturePolicy.takeover !== "exclusive",
4110
4113
  });
@@ -18,7 +18,10 @@ import type { Observability } from "@opengeni/observability";
18
18
  import type { OpenGeniRuntime } from "@opengeni/runtime";
19
19
  import type { ObjectStorage } from "@opengeni/storage";
20
20
  import type { VaultConnectionCredentialsPort } from "@opengeni/vaults";
21
- import type { OnePasswordVaultProviderFactory } from "@opengeni/core";
21
+ import type {
22
+ ExternalVaultProviderFactories,
23
+ OnePasswordVaultProviderFactory,
24
+ } from "@opengeni/core";
22
25
 
23
26
  // Signal (start-if-needed) a session's Temporal workflow so a queued turn it
24
27
  // cannot otherwise observe gets claimed. Used to wake a PARENT session's
@@ -141,6 +144,7 @@ export type SharedActivityServices = {
141
144
  * `@opengeni/vaults-onepassword`); null leaves 1Password vaults unavailable.
142
145
  */
143
146
  onePasswordVaultProvider?: OnePasswordVaultProviderFactory | null;
147
+ externalVaultProviders?: ExternalVaultProviderFactories | null;
144
148
  /** Standalone, default-off personal GitHub smart-HTTP broker consumer. */
145
149
  personalGitHubCredentials?: ConnectionCredentialsPort["gitCredentials"] | null;
146
150
  };
@@ -12,13 +12,14 @@ import {
12
12
  } from "@opengeni/db";
13
13
  import { createNatsEventBus } from "@opengeni/events";
14
14
  import { createObservability } from "@opengeni/observability";
15
- import { resolveCatalogSettings } from "@opengeni/core";
15
+ import { createRemoteExternalVaultFactories, resolveCatalogSettings } from "@opengeni/core";
16
16
  import { createObjectStorage } from "@opengeni/storage";
17
17
  import type { ActivityDependencies, SharedActivityServices } from "./activities/types";
18
18
  import { observabilityEventBusOptions } from "./observability-metrics";
19
19
  import { buildPersonalGitHubGitCredentials } from "./personal-github-git-credentials";
20
20
  import { createStandaloneConnectionCredentialsPort } from "./pr-review-credentials";
21
21
  import { loadOnePasswordVaultProvider } from "./onepassword-vault-provider";
22
+ import { createNativeExternalVaultFactories } from "./external-vault-providers";
22
23
 
23
24
  /**
24
25
  * Build the dependency graph common to both worker roles exactly once.
@@ -92,6 +93,13 @@ export function createSharedActivityServices(
92
93
  dependencies.onePasswordVaultProvider === undefined
93
94
  ? loadOnePasswordVaultProvider
94
95
  : dependencies.onePasswordVaultProvider,
96
+ externalVaultProviders:
97
+ dependencies.externalVaultProviders === undefined
98
+ ? {
99
+ ...createRemoteExternalVaultFactories(settings),
100
+ ...createNativeExternalVaultFactories(settings),
101
+ }
102
+ : dependencies.externalVaultProviders,
95
103
  };
96
104
  })();
97
105
  return await servicesPromise;
@@ -0,0 +1,136 @@
1
+ import type { Settings } from "@opengeni/config";
2
+ import type {
3
+ ExternalVaultProviderFactories,
4
+ ExternalVaultProviderFactory,
5
+ StoredVaultProviderCredentials,
6
+ } from "@opengeni/core";
7
+ import { pinnedFetch, type PinnedFetchOptions } from "@opengeni/network";
8
+ import { VaultProviderError } from "@opengeni/vaults";
9
+ import type { KeeperRuntimeOptions } from "@opengeni/vaults-keeper";
10
+ import type { ProtonPassProviderPool } from "@opengeni/vaults-protonpass";
11
+
12
+ /** Test seams only; production passes nothing. */
13
+ export type NativeExternalVaultOptions = {
14
+ keeper?: Pick<KeeperRuntimeOptions, "queryFunction" | "fetchFile" | "sdk">;
15
+ /** Applies when this call creates the pool for its CLI path. */
16
+ protonpass?: { stateRoot?: string; hostEnv?: Readonly<Record<string, string | undefined>> };
17
+ };
18
+
19
+ /**
20
+ * Native external vault adapters that run inside this server: Keeper Secrets
21
+ * Manager always, Proton Pass only when the operator installed `pass-cli`
22
+ * (`OPENGENI_PROTON_PASS_CLI_PATH`). Both packages load through `import()`
23
+ * on first use (packages/vaults-keeper/test/import-guard.test.ts). Keep this
24
+ * file identical to apps/api/src/integrations/external-vault-providers.ts.
25
+ */
26
+ export function createNativeExternalVaultFactories(
27
+ settings: Settings,
28
+ options: NativeExternalVaultOptions = {},
29
+ ): ExternalVaultProviderFactories {
30
+ const factories: ExternalVaultProviderFactories = {
31
+ keeper: keeperFactory(settings, options.keeper ?? {}),
32
+ };
33
+ const cliPath = settings.protonPassCliPath;
34
+ if (cliPath) factories.protonpass = protonPassFactory(cliPath, options.protonpass ?? {});
35
+ return factories;
36
+ }
37
+
38
+ /** Logs out every pooled Proton Pass CLI session and wipes its state (graceful shutdown). */
39
+ export async function closeNativeExternalVaultProviders(): Promise<void> {
40
+ const pools = [...protonPools.values()];
41
+ protonPools.clear();
42
+ await Promise.all(pools.map(async (pool) => (await pool.catch(() => null))?.close()));
43
+ }
44
+
45
+ function mismatch(): never {
46
+ throw new VaultProviderError(
47
+ "provider_unavailable",
48
+ "The configured adapter does not match this connection.",
49
+ );
50
+ }
51
+
52
+ /**
53
+ * The fetch every Keeper request and file download uses: DNS-pinned and
54
+ * policy-checked (no private or special-use targets). The adapter adds
55
+ * HTTPS-only, no redirects, deadlines, size bounds and the caller's abort.
56
+ */
57
+ export function nativeKeeperFetch(
58
+ settings: Settings,
59
+ pinned: Pick<PinnedFetchOptions, "dnsLookup" | "fetchImpl" | "agentFactory"> = {},
60
+ ) {
61
+ return (input: string | URL, init: RequestInit) =>
62
+ pinnedFetch(input, init, settings, {
63
+ ...pinned,
64
+ label: "Keeper Secrets Manager",
65
+ requireHttpsOutsideLocalTest: true,
66
+ });
67
+ }
68
+
69
+ function keeperFactory(
70
+ settings: Settings,
71
+ seams: NonNullable<NativeExternalVaultOptions["keeper"]>,
72
+ ): ExternalVaultProviderFactory {
73
+ const fetch = nativeKeeperFetch(settings);
74
+ return async (input) => {
75
+ const credentials = input.credentials;
76
+ if (credentials.provider !== "keeper") mismatch();
77
+ const persist = input.persistCredentials;
78
+ const { openKeeperConnection } = await import("@opengeni/vaults-keeper");
79
+ const opened = await openKeeperConnection({
80
+ credentials:
81
+ "oneTimeToken" in credentials
82
+ ? {
83
+ oneTimeToken: credentials.oneTimeToken,
84
+ ...(credentials.hostname ? { hostname: credentials.hostname } : {}),
85
+ }
86
+ : { configuration: credentials.configuration },
87
+ // Without it openKeeperConnection refuses: a redeemed token or rotated
88
+ // key must never live only in memory.
89
+ ...(persist
90
+ ? {
91
+ persistConfiguration: (configuration: Record<string, string>) =>
92
+ persist({ provider: "keeper", configuration }),
93
+ }
94
+ : {}),
95
+ ...(input.operations ? { operations: input.operations } : {}),
96
+ // Cancels a pending bootstrap; a stored configuration opens without a request.
97
+ ...(input.signal ? { signal: input.signal } : {}),
98
+ fetch,
99
+ ...seams,
100
+ });
101
+ const stored: StoredVaultProviderCredentials = {
102
+ provider: "keeper",
103
+ configuration: opened.configuration,
104
+ };
105
+ return { provider: opened.provider, credentials: stored };
106
+ };
107
+ }
108
+
109
+ /** One pool per CLI path per process, so repeated factory creation never leaks sessions. */
110
+ const protonPools = new Map<string, Promise<ProtonPassProviderPool>>();
111
+
112
+ function protonPassFactory(
113
+ binaryPath: string,
114
+ seams: NonNullable<NativeExternalVaultOptions["protonpass"]>,
115
+ ): ExternalVaultProviderFactory {
116
+ return async (input) => {
117
+ const credentials = input.credentials;
118
+ if (credentials.provider !== "protonpass") mismatch();
119
+ let pool = protonPools.get(binaryPath);
120
+ if (!pool) {
121
+ pool = import("@opengeni/vaults-protonpass").then(({ createProtonPassProviderPool }) =>
122
+ createProtonPassProviderPool({ ...seams, binaryPath }),
123
+ );
124
+ protonPools.set(binaryPath, pool);
125
+ pool.catch(() => protonPools.delete(binaryPath));
126
+ }
127
+ // Sessions are scoped to the connection, so two connections never share
128
+ // CLI state even with the same token. Without a connection id (a setup
129
+ // probe) the provider gets an isolated session of its own.
130
+ const provider = (await pool).provider(credentials.token, {
131
+ ...(input.connectionId === undefined ? {} : { connectionId: input.connectionId }),
132
+ ...(input.operations ? { operations: input.operations } : {}),
133
+ });
134
+ return { provider, credentials: { provider: "protonpass", token: credentials.token } };
135
+ };
136
+ }
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ import { fileURLToPath } from "node:url";
42
42
  import type { createControlActivities } from "./activities-control";
43
43
  import type { createTurnActivities } from "./activities-turn";
44
44
  import type { ActivityDependencies } from "./activities/types";
45
+ import { closeNativeExternalVaultProviders } from "./external-vault-providers";
45
46
  import type {
46
47
  InspectSessionAttemptActivity,
47
48
  SignalCodexCapacityWorkflow,
@@ -1129,7 +1130,12 @@ export async function startWorker() {
1129
1130
  },
1130
1131
  });
1131
1132
  } finally {
1132
- await Promise.allSettled([bus?.close(), readinessDbClient.close(), dbClient.close()]);
1133
+ await Promise.allSettled([
1134
+ closeNativeExternalVaultProviders(),
1135
+ bus?.close(),
1136
+ readinessDbClient.close(),
1137
+ dbClient.close(),
1138
+ ]);
1133
1139
  disposeUnhandledRejectionBoundary();
1134
1140
  }
1135
1141
  }
@@ -1542,6 +1542,7 @@ export type SelfhostedTurnSessionArgs = {
1542
1542
  operationCpuQuotaSupported: boolean;
1543
1543
  /** Feature advertisement only; refreshed by exact live admission for each operation. */
1544
1544
  transactionalFsWriteSupported?: boolean;
1545
+ browserControlHttpSupported?: boolean;
1545
1546
  /** The active pointer's epoch — the control-op fence echoed to the agent. */
1546
1547
  epoch: number;
1547
1548
  /** The run's declared sandbox environment (the SAME object fed to buildAgent +
@@ -1618,6 +1619,7 @@ export function connectionBindingFor(
1618
1619
  operationResourcePolicySupported: enrollment.agentCapabilities.operationResourcePolicy === true,
1619
1620
  operationCpuQuotaSupported: enrollment.agentCapabilities.operationCpuQuota === true,
1620
1621
  transactionalFsWriteSupported: enrollment.agentCapabilities.transactionalFsWrite === true,
1622
+ browserControlHttpSupported: enrollment.agentCapabilities.browserControlHttp === true,
1621
1623
  };
1622
1624
  }
1623
1625
 
@@ -1693,6 +1695,7 @@ export async function establishSelfhostedTurnSession(
1693
1695
  operationResourcePolicySupported: args.operationResourcePolicySupported,
1694
1696
  operationCpuQuotaSupported: args.operationCpuQuotaSupported,
1695
1697
  transactionalFsWriteSupported: args.transactionalFsWriteSupported === true,
1698
+ browserControlHttpSupported: args.browserControlHttpSupported === true,
1696
1699
  resolveOperationAdmission: async () => {
1697
1700
  // The machine's audience can change in place during a turn. Read the
1698
1701
  // current scope on every admitted operation: a machine that is Only me now