@opengeni/sdk 0.4.0 → 0.6.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/src/types.ts CHANGED
@@ -11,8 +11,9 @@ export type SessionStatus =
11
11
  | "failed"
12
12
  | "cancelled";
13
13
 
14
- // Mirror of `@opengeni/contracts` SandboxBackend (10 values; existing four keep
15
- // position). 3-way enum parity is pinned by `test/contract-parity.test.ts`.
14
+ // Mirror of `@opengeni/contracts` SandboxBackend (11 values; every member is
15
+ // additive at the end). 3-way enum parity is pinned by
16
+ // `test/contract-parity.test.ts`.
16
17
  export type SandboxBackend =
17
18
  | "docker"
18
19
  | "modal"
@@ -23,7 +24,8 @@ export type SandboxBackend =
23
24
  | "e2b"
24
25
  | "blaxel"
25
26
  | "cloudflare"
26
- | "vercel";
27
+ | "vercel"
28
+ | "selfhosted";
27
29
 
28
30
  // Mirror of `@opengeni/contracts` SandboxOs. Only "linux" is reachable in v1.
29
31
  export type SandboxOs = "linux" | "macos" | "windows";
@@ -43,7 +45,12 @@ export type CapabilityUnavailableReason =
43
45
  | "not_provisioned"
44
46
  | "disabled_by_policy"
45
47
  | "lease_cold"
46
- | "tier_headless";
48
+ | "tier_headless"
49
+ // selfhosted (bring-your-own-compute) negotiation states:
50
+ | "agent_offline"
51
+ | "agent_reconnecting"
52
+ | "consent_required"
53
+ | "display_unavailable";
47
54
 
48
55
  // Mirror of `@opengeni/contracts` SessionCapabilities (the negotiated handshake
49
56
  // document). The descriptor table itself is NOT mirrored — it lives in
@@ -77,8 +84,10 @@ export type SessionCapabilities = {
77
84
  reason: CapabilityUnavailableReason | null;
78
85
  };
79
86
  DesktopStream: {
80
- transport: "vnc-ws" | "rdp-ws" | "webrtc" | null;
81
- client: "novnc" | "web-rdp" | null;
87
+ // "relay-frames" + "frames": the selfhosted framebuffer stream — PNG-per-frame
88
+ // protobuf datagrams over the relay, painted by a canvas client (NOT RFB).
89
+ transport: "vnc-ws" | "rdp-ws" | "webrtc" | "relay-frames" | null;
90
+ client: "novnc" | "web-rdp" | "frames" | null;
82
91
  mode: "read-only" | "interactive";
83
92
  url: string | null;
84
93
  token: string | null;
@@ -236,6 +245,8 @@ export type Session = {
236
245
  accountId: string;
237
246
  status: SessionStatus;
238
247
  initialMessage: string;
248
+ title: string | null;
249
+ titleSource: "user" | "agent" | null;
239
250
  resources: ResourceRef[];
240
251
  tools: ToolRef[];
241
252
  metadata: Record<string, unknown>;
@@ -247,6 +258,10 @@ export type Session = {
247
258
  temporalWorkflowId: string | null;
248
259
  activeTurnId: string | null;
249
260
  lastSequence: number;
261
+ /** Multi-account Codex (P1): the account this session is pinned to (null ⇒ follow workspace active). */
262
+ codexPinnedCredentialId?: string | null;
263
+ /** Multi-account Codex (P1): the account the most recent turn ran on (the "Running on:" indicator). */
264
+ codexLastCredentialId?: string | null;
250
265
  createdAt: string;
251
266
  updatedAt: string;
252
267
  };
@@ -333,6 +348,9 @@ export const SESSION_EVENT_TYPES = [
333
348
  "terminal.pty.started",
334
349
  "terminal.pty.output.delta",
335
350
  "terminal.pty.exited",
351
+ "session.title_set",
352
+ // Multi-account Codex (P1): the session's inference account changed.
353
+ "codex.account.switched",
336
354
  ] as const;
337
355
 
338
356
  export type KnownSessionEventType = (typeof SESSION_EVENT_TYPES)[number];
@@ -575,6 +593,12 @@ export type CreateSessionRequest = {
575
593
  model?: string | undefined;
576
594
  reasoningEffort?: ReasoningEffort | undefined;
577
595
  sandboxBackend?: SandboxBackend | undefined;
596
+ // The enrolled machine (a sandbox id) to run this session on; seeds the
597
+ // active-sandbox pointer at creation so the first turn lands on it.
598
+ targetSandboxId?: string | undefined;
599
+ // Host working directory for a connected-machine target (the agent runs here;
600
+ // default = the machine's launch dir). Ignored for managed sandboxes.
601
+ workingDir?: string | undefined;
578
602
  environmentId?: string | undefined;
579
603
  goal?: GoalSpec | undefined;
580
604
  clientEventId?: string | undefined;
@@ -583,6 +607,13 @@ export type CreateSessionRequest = {
583
607
  // Distinct from the per-call clientEventId.
584
608
  idempotencyKey?: string | undefined;
585
609
  firstPartyMcpPermissions?: string[] | undefined;
610
+ // Shared-sandbox placement (mirror of `@opengeni/contracts` CreateSessionRequest.sandbox,
611
+ // addendum 05 §D.1). Three-way union; OMITTED ⇒ the context-dependent server default
612
+ // (from inside a session → "shared" with the creator's box, top-level → "new").
613
+ // - "shared": join the CREATOR's box (requires a parent session; top-level → 422).
614
+ // - "new": mint a fresh singleton box (group ≡ the new session's id).
615
+ // - {groupId}: join a SPECIFIC sibling group in THIS workspace (manager fan-out).
616
+ sandbox?: "shared" | "new" | { groupId: string } | undefined;
586
617
  };
587
618
 
588
619
  // --- Access, workspaces, API keys -------------------------------------------
@@ -620,6 +651,8 @@ export const KNOWN_PERMISSIONS = [
620
651
  "environments:manage",
621
652
  "environments:use",
622
653
  "goals:manage",
654
+ "enrollments:read",
655
+ "enrollments:manage",
623
656
  ] as const;
624
657
 
625
658
  export type KnownPermission = (typeof KNOWN_PERMISSIONS)[number];
@@ -648,6 +681,124 @@ export type ClientModel = {
648
681
  contextWindowTokens?: number | undefined;
649
682
  };
650
683
 
684
+ /**
685
+ * Connection state of a workspace's Codex (ChatGPT) subscription, returned by
686
+ * `GET /v1/workspaces/:id/codex/status`. `models` are the codex models the
687
+ * workspace can select (projected as ClientModel under their own "no credits"
688
+ * provider group), present only while connected.
689
+ */
690
+ export type CodexConnectionStatus = {
691
+ connected: boolean;
692
+ plan?: string | null;
693
+ valid?: boolean;
694
+ expiresAt?: string | null;
695
+ lastError?: string | null;
696
+ models?: ClientModel[];
697
+ /** The account a session runs on when unpinned (label for the in-session indicator). */
698
+ activeAccount?: { id: string; label?: string | null; chatgptAccountId?: string | null } | null;
699
+ /** How many Codex accounts the workspace has connected. */
700
+ accountCount?: number;
701
+ };
702
+
703
+ /**
704
+ * One normalized Codex usage window (5h or weekly), camelCase end-to-end (the
705
+ * route normalizes server-side; the web layer never re-hand-types snake_case).
706
+ * `percent` is authoritative; used/limit/remaining are a synthesized 0–100 scale
707
+ * (limit = 100) because the provider gives only a percentage. `remaining =
708
+ * 100 - percent` is the P3 rotation key. Identify the window by `limitWindowSeconds`
709
+ * (18000 ⇒ 5h, 604800 ⇒ weekly), never by position.
710
+ */
711
+ export type CodexUsageWindow = {
712
+ used: number;
713
+ limit: number;
714
+ remaining: number;
715
+ percent: number;
716
+ resetAt: string | null;
717
+ resetAfterSeconds: number | null;
718
+ limitWindowSeconds: number;
719
+ };
720
+
721
+ /** The normalized usage payload for one account — the P2/P3 contract. */
722
+ export type CodexUsagePayload = {
723
+ status: "ok" | "limit_reached" | "error" | "no-data";
724
+ planType: string | null;
725
+ fiveHour: CodexUsageWindow | null;
726
+ weekly: CodexUsageWindow | null;
727
+ limitReached: boolean;
728
+ fetchedAt: string;
729
+ /** Present only on an auth/refresh failure path. */
730
+ reason?: "needs_relogin";
731
+ additionalLimits?: Array<{ limitName: string; meteredFeature: string; fiveHour: CodexUsageWindow | null; weekly: CodexUsageWindow | null }>;
732
+ credits?: { hasCredits: boolean; unlimited: boolean; overageLimitReached: boolean; balance: string };
733
+ };
734
+
735
+ /** One connected Codex (ChatGPT) account in a workspace (multi-account P1). Metadata only. */
736
+ export type CodexAccount = {
737
+ id: string;
738
+ chatgptAccountId?: string | null;
739
+ label?: string | null;
740
+ email?: string | null;
741
+ plan?: string | null;
742
+ status: "active" | "needs_relogin" | "error";
743
+ active: boolean;
744
+ expiresAt?: string | null;
745
+ lastRefreshAt?: string | null;
746
+ lastError?: string | null;
747
+ // P2 CACHED usage (built from the persisted columns; renders bars off
748
+ // listCodexAccounts with no second call). null until the first live refresh.
749
+ fiveHour?: CodexUsageWindow | null;
750
+ weekly?: CodexUsageWindow | null;
751
+ usageCheckedAt?: string | null;
752
+ // P3 rotation cooldown: ISO timestamp until which this account is cooling-down
753
+ // (rotated-off after a usage cap). null/absent ⇒ not cooling.
754
+ exhaustedUntil?: string | null;
755
+ };
756
+
757
+ /** Per-workspace Codex rotation/active settings. P1: rotation inert, only activeCredentialId loads. */
758
+ export type CodexRotationSettings = {
759
+ rotationEnabled: boolean;
760
+ rotationStrategy: "most_remaining" | "round_robin" | "drain_then_next";
761
+ activeCredentialId: string | null;
762
+ };
763
+
764
+ /** GET /codex/accounts — the accounts list + the workspace active pointer + settings. */
765
+ export type CodexAccountsResponse = {
766
+ accounts: CodexAccount[];
767
+ activeAccountId: string | null;
768
+ settings: CodexRotationSettings;
769
+ };
770
+
771
+ /** Payload of a `codex.account.switched` session event. */
772
+ export type CodexAccountSwitchedPayload = {
773
+ fromAccountId: string | null;
774
+ toAccountId: string;
775
+ reason: "manual" | "exhausted" | "rotation";
776
+ // P4 connector-aware rotation: the session's used connectors that the new account
777
+ // does NOT cover (a prefer-not-require failover that dropped a connector). Present
778
+ // only on such a switch; the UI renders a "dropped <connector>" badge on the pill.
779
+ droppedConnectors?: string[];
780
+ };
781
+
782
+ /** Device-code start: show `userCode` at `verificationUri`, then poll with `state`. */
783
+ export type CodexConnectStart = {
784
+ userCode: string;
785
+ verificationUri: string;
786
+ intervalSeconds: number;
787
+ state: string;
788
+ };
789
+
790
+ /** Poll result: keep polling on `pending`, restart on `expired`, done on `connected`. */
791
+ export type CodexConnectPoll =
792
+ | { status: "pending" }
793
+ | { status: "expired" }
794
+ | { status: "connected"; plan?: string | null; accountId?: string; isActive?: boolean };
795
+
796
+ /** Remaining usage/limits for one account. `usage` is the normalized P2 payload. */
797
+ export type CodexUsage = { status: "ok" | "limit_reached" | "error" | "no-data"; usage: CodexUsagePayload | null };
798
+
799
+ /** Batched live-refresh response, keyed by credential id; each entry independently statused. */
800
+ export type CodexUsageMap = Record<string, CodexUsage>;
801
+
651
802
  /**
652
803
  * How a deployment expects clients to authenticate to it, surfaced so a UI can
653
804
  * wire up the right header/cookie without prior knowledge of the host setup.
@@ -829,6 +980,10 @@ export type UpdateSessionGoalRequest = {
829
980
  rationale?: string | undefined;
830
981
  };
831
982
 
983
+ export type UpdateSessionRequest = {
984
+ title: string;
985
+ };
986
+
832
987
  // --- Operator context controls (/clear, /compact) ----------------------------
833
988
 
834
989
  /** Outcome of a manual /compact trigger. */
@@ -1465,3 +1620,187 @@ export type ClientSessionEventInput =
1465
1620
  | UserMessageEventInput
1466
1621
  | UserInterruptEventInput
1467
1622
  | UserApprovalDecisionEventInput;
1623
+
1624
+ // ── Bring-your-own-compute: Machines dashboard + per-machine metrics (M10) ────
1625
+ // Hand-written mirrors of the `@opengeni/contracts` MetricSample / MachineView /
1626
+ // MachinesResponse / MachineMetricsSeriesResponse (pinned by contract-parity).
1627
+ // M9 imports THESE so the dashboard UI never drifts from the API.
1628
+
1629
+ /** A point-in-time machine metrics sample. `gpuUtilPct`/`gpuMemBytes` are null
1630
+ * when no GPU was present (not-reported, never a real zero); the bytes/load are
1631
+ * numbers; `sampledAt` is an ISO-8601 instant. */
1632
+ export type MetricSample = {
1633
+ cpuPct: number;
1634
+ load1: number;
1635
+ load5: number;
1636
+ load15: number;
1637
+ memUsedBytes: number;
1638
+ memTotalBytes: number;
1639
+ diskUsedBytes: number;
1640
+ diskTotalBytes: number;
1641
+ gpuUtilPct: number | null;
1642
+ gpuMemBytes: number | null;
1643
+ runQueue: number;
1644
+ sampledAt: string;
1645
+ };
1646
+
1647
+ /** The derived dashboard state of a machine (M3 liveness + consent/display
1648
+ * reasons + the in-flight device-flow). */
1649
+ export type MachineState =
1650
+ | "online"
1651
+ | "reconnecting"
1652
+ | "offline"
1653
+ | "consent_required"
1654
+ | "display_unavailable"
1655
+ | "enrolling";
1656
+
1657
+ export type MachineKind = "modal" | "selfhosted";
1658
+
1659
+ /** A machine as the Machines dashboard renders it (an enrolled selfhosted machine
1660
+ * or the session's synthetic Modal group box, `isSessionGroup: true`). */
1661
+ export type MachineView = {
1662
+ sandboxId: string;
1663
+ enrollmentId: string | null;
1664
+ name: string;
1665
+ kind: MachineKind;
1666
+ state: MachineState;
1667
+ active: boolean;
1668
+ isSessionGroup: boolean;
1669
+ os: string;
1670
+ arch: string;
1671
+ hasDisplay: boolean;
1672
+ allowScreenControl: boolean;
1673
+ sharedSessionCount: number;
1674
+ lastSeenAt: string | null;
1675
+ metrics: MetricSample | null;
1676
+ };
1677
+
1678
+ /** GET /v1/workspaces/:ws/machines — the dashboard list + the active-sandbox
1679
+ * pointer (null activeSandboxId == the session's own group box is active). */
1680
+ export type MachinesResponse = {
1681
+ activeSandboxId: string | null;
1682
+ activeEpoch: number;
1683
+ machines: MachineView[];
1684
+ };
1685
+
1686
+ /** GET /v1/workspaces/:ws/machines/:enrollmentId/metrics/series — the downsampled
1687
+ * (~1/min) history the dashboard time-range reads. */
1688
+ export type MachineMetricsSeriesResponse = {
1689
+ samples: MetricSample[];
1690
+ };
1691
+
1692
+ /** POST /v1/workspaces/:ws/sessions/:sessionId/active-sandbox — swap a session's
1693
+ * active sandbox. `target` is a `MachineView.sandboxId`, or "session"/"default"
1694
+ * to swap back to the session's own group box. */
1695
+ export type SwapActiveSandboxRequest = {
1696
+ target: string;
1697
+ };
1698
+
1699
+ /** The swap outcome (mirrors the server `FleetSwapResult`). `swapped` is true on a
1700
+ * successful repoint OR a no-op (already there); `reason` carries the failure
1701
+ * detail (unowned/offline target, or a lost epoch fence) when false. */
1702
+ export type SwapActiveSandboxResponse = {
1703
+ swapped: boolean;
1704
+ activeSandboxId: string | null;
1705
+ activeEpoch: number;
1706
+ reason?: string;
1707
+ };
1708
+
1709
+ // ── Self-hosted enrollment UX (design 11) ────────────────────────────────────
1710
+ // Hand-written mirrors of the `@opengeni/contracts` enrollment-UX request/response
1711
+ // shapes (the SDK keeps zero runtime deps). The click-Grant approve-page
1712
+ // lookup/deny + the headless enroll-token mint/exchange.
1713
+
1714
+ /** Mirror of `@opengeni/contracts` EnrollmentOs. */
1715
+ export type EnrollmentOs = "linux" | "macos" | "windows";
1716
+
1717
+ /** POST /v1/enrollments/device/lookup body. */
1718
+ export type DeviceEnrollmentLookupRequest = {
1719
+ userCode: string;
1720
+ };
1721
+
1722
+ /** The presentational machine details the consent screen renders. */
1723
+ export type DeviceEnrollmentLookupMachine = {
1724
+ machineName: string | null;
1725
+ os: EnrollmentOs;
1726
+ arch: string;
1727
+ canOfferDisplay: boolean;
1728
+ requestsScreenControl: boolean;
1729
+ };
1730
+
1731
+ /** POST /v1/enrollments/device/lookup response (no secrets, no device_code). */
1732
+ export type DeviceEnrollmentLookupResponse = {
1733
+ workspaceId: string;
1734
+ userCode: string;
1735
+ machine: DeviceEnrollmentLookupMachine;
1736
+ expiresAt: string;
1737
+ };
1738
+
1739
+ /** POST /v1/workspaces/:ws/enrollments/device/approve body. */
1740
+ export type DeviceEnrollmentApproveRequest = {
1741
+ userCode: string;
1742
+ allowScreenControl?: boolean;
1743
+ };
1744
+
1745
+ /** POST /v1/workspaces/:ws/enrollments/device/approve response. */
1746
+ export type DeviceEnrollmentApproveResponse = {
1747
+ approved: boolean;
1748
+ enrollmentId: string;
1749
+ sandboxId: string;
1750
+ allowScreenControl: boolean;
1751
+ };
1752
+
1753
+ /** POST /v1/workspaces/:ws/enrollments/device/deny body. */
1754
+ export type DeviceEnrollmentDenyRequest = {
1755
+ userCode: string;
1756
+ };
1757
+
1758
+ /** POST /v1/workspaces/:ws/enrollments/device/deny response. */
1759
+ export type DeviceEnrollmentDenyResponse = {
1760
+ denied: boolean;
1761
+ };
1762
+
1763
+ /** POST /v1/workspaces/:ws/enrollments/token body. */
1764
+ export type MintEnrollTokenRequest = {
1765
+ allowScreenControl?: boolean;
1766
+ };
1767
+
1768
+ /** POST /v1/workspaces/:ws/enrollments/token response. The `token` is SECRET. */
1769
+ export type MintEnrollTokenResponse = {
1770
+ token: string;
1771
+ expiresAt: string;
1772
+ expiresInSeconds: number;
1773
+ };
1774
+
1775
+ /** The credential payload the headless exchange returns (a subset of the agent's
1776
+ * EnrollmentCredentials — IDENTICAL to the device-flow poll authorized branch). */
1777
+ export type EnrollmentCredentials = {
1778
+ agentId: string;
1779
+ workspaceId: string;
1780
+ bearer: string;
1781
+ subjectPrefix: string;
1782
+ natsUrls: string[];
1783
+ relayUrl: string;
1784
+ relayToken: string;
1785
+ natsAccountCreds: string;
1786
+ updatePublicKey: string;
1787
+ consentedWholeMachine: boolean;
1788
+ consentedScreenControl: boolean;
1789
+ };
1790
+
1791
+ /** POST /v1/enrollments/token/exchange body (the headless / fleet enroll path). */
1792
+ export type EnrollTokenExchangeRequest = {
1793
+ token: string;
1794
+ publicKey: string;
1795
+ os?: EnrollmentOs;
1796
+ arch?: string;
1797
+ machineName?: string;
1798
+ exposure?: "whole-machine";
1799
+ canOfferDisplay?: boolean;
1800
+ requestsScreenControl?: boolean;
1801
+ };
1802
+
1803
+ /** POST /v1/enrollments/token/exchange response (wraps the credential shape). */
1804
+ export type EnrollTokenExchangeResponse = {
1805
+ credentials: EnrollmentCredentials;
1806
+ };