@opengeni/core 2.5.3 → 2.6.4

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 (39) hide show
  1. package/dist/access/index.d.ts +24 -0
  2. package/dist/canonical-human-identities.js +2 -2
  3. package/dist/{chunk-ZVZJTMSV.js → chunk-OF65T3PM.js} +2 -2
  4. package/dist/{chunk-YGOMUGYS.js → chunk-QO5GVFFO.js} +17 -8
  5. package/dist/{chunk-YGOMUGYS.js.map → chunk-QO5GVFFO.js.map} +1 -1
  6. package/dist/domain/company-brain-governed-writes.d.ts +15 -6
  7. package/dist/domain/company-profile-agent-admin.d.ts +3 -2
  8. package/dist/domain/environments.d.ts +1 -1
  9. package/dist/domain/memory-slack-delivery.d.ts +4 -1
  10. package/dist/domain/personal-connection-delegations.d.ts +1 -0
  11. package/dist/domain/pr-review.d.ts +1 -1
  12. package/dist/domain/scheduled-tasks.d.ts +12 -0
  13. package/dist/domain/sessions.d.ts +14 -1
  14. package/dist/domain/workspace-members.d.ts +8 -1
  15. package/dist/index.js +686 -380
  16. package/dist/index.js.map +1 -1
  17. package/dist/managed-auth-session-sets.d.ts +18 -0
  18. package/dist/managed-auth-session-sets.js +3 -1
  19. package/dist/sandbox/fleet.d.ts +6 -4
  20. package/dist/sandbox/routing.d.ts +7 -2
  21. package/dist/sandbox/runtime-settings.d.ts +17 -1
  22. package/package.json +10 -10
  23. package/src/access/index.ts +140 -5
  24. package/src/application/user-resource-grants.ts +31 -2
  25. package/src/billing/limits.ts +5 -2
  26. package/src/domain/company-brain-governed-writes.ts +29 -13
  27. package/src/domain/company-profile-agent-admin.ts +3 -2
  28. package/src/domain/environments.ts +6 -34
  29. package/src/domain/memory-slack-delivery.ts +30 -0
  30. package/src/domain/personal-connection-delegations.ts +40 -7
  31. package/src/domain/remember.ts +5 -6
  32. package/src/domain/scheduled-tasks.ts +94 -0
  33. package/src/domain/sessions.ts +117 -9
  34. package/src/domain/workspace-members.ts +34 -2
  35. package/src/managed-auth-session-sets.ts +38 -11
  36. package/src/sandbox/fleet.ts +20 -17
  37. package/src/sandbox/routing.ts +18 -4
  38. package/src/sandbox/runtime-settings.ts +32 -0
  39. /package/dist/{chunk-ZVZJTMSV.js.map → chunk-OF65T3PM.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  // apps/api/src/sandbox/fleet.ts — the FLEET service backing the fleet MCP tools
2
2
  // (M7): list / attach / swap / run_on / provision over the heterogeneous fleet
3
- // (the session's Modal group box + the workspace's enrolled selfhosted machines).
3
+ // (the session's managed group box + the workspace's enrolled selfhosted machines).
4
4
  //
5
5
  // Each operation is workspace-scoped (the caller's grant) and, for the
6
6
  // session-pointer mutations (attach/swap), session-scoped (the worker-signed
@@ -12,6 +12,7 @@
12
12
  // single op WITHOUT touching the active pointer.
13
13
 
14
14
  import type { Settings } from "@opengeni/config";
15
+ import type { SandboxBackend } from "@opengeni/contracts";
15
16
  import {
16
17
  authorizePersonalMachineForAttempt,
17
18
  getEnrollment,
@@ -45,6 +46,7 @@ import {
45
46
  type SelfhostedOperationResourcePolicy,
46
47
  } from "@opengeni/runtime/sandbox";
47
48
  import { relayConfigFromSettings } from "./routing";
49
+ import { managedSessionGroupBackend } from "./runtime-settings";
48
50
 
49
51
  export type FleetServices = {
50
52
  db: Database;
@@ -74,8 +76,8 @@ export type FleetContext = {
74
76
  /** The calling session (the pointer the attach/swap mutates + whose group box
75
77
  * is the default fleet member). */
76
78
  sessionId: string;
77
- /** The session's own group sandbox backend (modal/selfhosted/…). */
78
- sessionBackend: string;
79
+ /** The session's durable home-compute policy. */
80
+ sessionBackend: SandboxBackend;
79
81
  /** The session's own group sandbox id (the lease group). */
80
82
  sessionGroupId: string;
81
83
  };
@@ -264,8 +266,9 @@ async function probeEnrollment(
264
266
  * List the fleet: the session's own group box when it has one (a synthetic
265
267
  * entry) + the workspace's first-class selfhosted sandboxes (each probed for
266
268
  * liveness), each with an `active` marker derived from the session's active
267
- * pointer. A backend:none session has no synthetic home entry; a null pointer
268
- * then means no compute is attached.
269
+ * pointer. A backend:none session and a machine-home session on a deployment
270
+ * without a managed provider have no synthetic group entry; a null pointer then
271
+ * means no compute is attached.
269
272
  */
270
273
  export async function listFleet(
271
274
  services: FleetServices,
@@ -285,8 +288,12 @@ export async function listFleet(
285
288
  };
286
289
 
287
290
  const entries: FleetSandboxEntry[] = [];
291
+ const groupBackend = managedSessionGroupBackend(
292
+ services.settings.sandboxBackend,
293
+ ctx.sessionBackend,
294
+ );
288
295
 
289
- if (ctx.sessionBackend !== "none") {
296
+ if (groupBackend) {
290
297
  // The session's own group box (the default/home sandbox; null active pointer ==
291
298
  // this box). A session/group row is not provider existence. Online requires a
292
299
  // warm lease, observed provider existence, and verified workspace readiness.
@@ -317,17 +324,10 @@ export async function listFleet(
317
324
  ? "unavailable"
318
325
  : groupRecovering
319
326
  ? "recovering"
320
- : ctx.sessionBackend === "selfhosted"
321
- ? "unavailable"
322
- : "wakeable";
327
+ : "wakeable";
323
328
  entries.push({
324
329
  id: ctx.sessionGroupId,
325
- kind:
326
- ctx.sessionBackend === "selfhosted"
327
- ? "selfhosted"
328
- : ctx.sessionBackend === "opensandbox"
329
- ? "opensandbox"
330
- : "modal",
330
+ kind: groupBackend === "opensandbox" ? "opensandbox" : "modal",
331
331
  name: "session sandbox",
332
332
  liveness: groupOnline ? "online" : groupRecovering ? "reconnecting" : "offline",
333
333
  active: groupActive,
@@ -467,10 +467,13 @@ async function resolveTarget(
467
467
  > {
468
468
  // The session's own group box → the default pointer (null).
469
469
  if (target === ctx.sessionGroupId || target === "session" || target === "default") {
470
- if (ctx.sessionBackend === "none") {
470
+ if (!managedSessionGroupBackend(services.settings.sandboxBackend, ctx.sessionBackend)) {
471
471
  return {
472
472
  ok: false,
473
- reason: "this session has no home sandbox; attach a Connected Machine",
473
+ reason:
474
+ ctx.sessionBackend === "none"
475
+ ? "this session has no home sandbox; attach a Connected Machine"
476
+ : "this deployment has no managed session sandbox; select an enrolled machine",
474
477
  code: "unsupported_backend_context",
475
478
  };
476
479
  }
@@ -26,6 +26,7 @@ import {
26
26
  verifyDirectWorkspaceMutationSettlement,
27
27
  verifyRetainedProcessMutationSettlement,
28
28
  type Database,
29
+ type SandboxRetainedProcess,
29
30
  type SandboxWorkspaceMutationAdmission,
30
31
  } from "@opengeni/db";
31
32
  import { settleSessionBackgroundCommandForRetainedProcess } from "@opengeni/db/session-background-commands";
@@ -86,6 +87,20 @@ export function directRetainedProcessMatchesBackend(
86
87
  );
87
88
  }
88
89
 
90
+ export function retainedProcessBackgroundSettlement(
91
+ process: Pick<SandboxRetainedProcess, "state" | "exitCode" | "settlementReason">,
92
+ fallback: RoutingRetainedProcessTerminalProof,
93
+ ): { outcome: "exited" | "lost"; exitCode: number | null; reason: string } {
94
+ if (process.state === "active") {
95
+ throw new Error("Retained-process settlement returned an active durable process");
96
+ }
97
+ return {
98
+ outcome: process.state,
99
+ exitCode: process.exitCode,
100
+ reason: process.settlementReason ?? fallback.reason,
101
+ };
102
+ }
103
+
89
104
  export type ChannelARoutingServices = {
90
105
  db: Database;
91
106
  settings: Settings;
@@ -423,7 +438,7 @@ export function wrapChannelABoxWithRouting(
423
438
  "API retained-process settlement lost its exact durable backend identity",
424
439
  );
425
440
  }
426
- await settleRetainedProcess(db, {
441
+ const settlement = await settleRetainedProcess(db, {
427
442
  accountId: ids.accountId,
428
443
  workspaceId: ids.workspaceId,
429
444
  sessionId: ids.sessionId,
@@ -434,14 +449,13 @@ export function wrapChannelABoxWithRouting(
434
449
  reason: proof.reason,
435
450
  idleGraceMs: settings.sandboxIdleGraceMs,
436
451
  });
452
+ const backgroundSettlement = retainedProcessBackgroundSettlement(settlement.process, proof);
437
453
  await settleSessionBackgroundCommandForRetainedProcess(db, {
438
454
  accountId: ids.accountId,
439
455
  workspaceId: ids.workspaceId,
440
456
  sessionId: ids.sessionId,
441
457
  retainedProcessId: process.id,
442
- outcome: proof.outcome,
443
- exitCode: proof.exitCode,
444
- reason: proof.reason,
458
+ ...backgroundSettlement,
445
459
  });
446
460
  }
447
461
  : undefined;
@@ -4,6 +4,7 @@ import {
4
4
  type RigVersion,
5
5
  type Session,
6
6
  type SandboxBackend,
7
+ type SandboxOs,
7
8
  } from "@opengeni/contracts";
8
9
  import {
9
10
  getRigVersion,
@@ -18,6 +19,37 @@ import {
18
19
  rigProviderImageProviderBindingKeyHash,
19
20
  } from "../rigs/provider-images";
20
21
 
22
+ export type ManagedSessionGroupBackend = Exclude<SandboxBackend, "none" | "selfhosted">;
23
+
24
+ /**
25
+ * Resolve the provider backend behind a session's synthetic managed group.
26
+ *
27
+ * A machine-home session keeps `selfhosted` as its durable policy while a
28
+ * non-null active pointer selects the enrolled machine. Clearing that pointer
29
+ * is an explicit switch to the deployment's managed group, so API readiness,
30
+ * viewer attachment, Channel-A, and worker turns must all select the same
31
+ * provider. Deployments configured with `none` or `selfhosted` have no managed
32
+ * fallback to expose.
33
+ */
34
+ export function managedSessionGroupBackend(
35
+ deploymentBackend: SandboxBackend,
36
+ sessionBackend: SandboxBackend,
37
+ ): ManagedSessionGroupBackend | null {
38
+ if (sessionBackend === "none") return null;
39
+ const backend = sessionBackend === "selfhosted" ? deploymentBackend : sessionBackend;
40
+ return backend === "none" || backend === "selfhosted" ? null : backend;
41
+ }
42
+
43
+ /** A machine-home row carries the machine's host OS; its managed group uses the
44
+ * platform's canonical managed-sandbox OS instead. Ordinary sessions preserve
45
+ * their explicitly selected OS. */
46
+ export function managedSessionGroupOs(
47
+ sessionBackend: SandboxBackend,
48
+ sessionOs: SandboxOs,
49
+ ): SandboxOs {
50
+ return sessionBackend === "selfhosted" ? "linux" : sessionOs;
51
+ }
52
+
21
53
  /** Pre-V2 Packs are the sole compatibility path where a Pack can still own a
22
54
  * sandbox image. V2 installations select a Rig instead. Keep this predicate at
23
55
  * the shared runtime boundary so turns, API-direct tools, viewers, Browser and