@opengeni/react 0.28.2 → 0.32.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.
Files changed (208) hide show
  1. package/dist/approvals.d.ts +14 -0
  2. package/dist/{chunk-M7X4JZOD.js → chunk-4QOQ2DSR.js} +8 -1
  3. package/dist/chunk-4QOQ2DSR.js.map +1 -0
  4. package/dist/{chunk-HQFPO7VX.js → chunk-7CJOAW3V.js} +24 -3
  5. package/dist/{chunk-HQFPO7VX.js.map → chunk-7CJOAW3V.js.map} +1 -1
  6. package/dist/{chunk-PMSGE233.js → chunk-A2WITZTI.js} +377 -113
  7. package/dist/chunk-A2WITZTI.js.map +1 -0
  8. package/dist/{chunk-BC6RKIHS.js → chunk-GZ4KR67U.js} +11 -11
  9. package/dist/{chunk-4F2YLLHP.js → chunk-KZRUYCL4.js} +14 -17
  10. package/dist/chunk-KZRUYCL4.js.map +1 -0
  11. package/dist/{chunk-OCHBZ2X7.js → chunk-TCCE5ZU5.js} +192 -28
  12. package/dist/chunk-TCCE5ZU5.js.map +1 -0
  13. package/dist/chunk-U255M5EZ.js +178 -0
  14. package/dist/chunk-U255M5EZ.js.map +1 -0
  15. package/dist/{chunk-XVVQP7A4.js → chunk-WY2QN2CX.js} +2539 -2174
  16. package/dist/chunk-WY2QN2CX.js.map +1 -0
  17. package/dist/chunk-ZNNE2VES.js +6413 -0
  18. package/dist/chunk-ZNNE2VES.js.map +1 -0
  19. package/dist/client.d.ts +33 -0
  20. package/dist/commands/index.d.ts +3 -0
  21. package/dist/commands/registry.d.ts +41 -0
  22. package/dist/commands/types.d.ts +85 -0
  23. package/dist/components/approval-surface.d.ts +28 -0
  24. package/dist/components/chat-composer.d.ts +51 -0
  25. package/dist/components/code-editor.d.ts +43 -0
  26. package/dist/components/command-palette.d.ts +25 -0
  27. package/dist/components/composer-transcription-control.d.ts +36 -0
  28. package/dist/components/composer.d.ts +277 -0
  29. package/dist/components/copy-button.d.ts +26 -0
  30. package/dist/components/desktop-viewer.d.ts +85 -0
  31. package/dist/components/diff-view.d.ts +38 -0
  32. package/dist/components/enrollment-consent.d.ts +33 -0
  33. package/dist/components/enrollment-device-flow.d.ts +27 -0
  34. package/dist/components/file-browser.d.ts +47 -0
  35. package/dist/components/fleet-tile.d.ts +18 -0
  36. package/dist/components/human-input-form.d.ts +47 -0
  37. package/dist/components/machine-card.d.ts +21 -0
  38. package/dist/components/machine-dock-bar.d.ts +31 -0
  39. package/dist/components/machine-health-pill.d.ts +9 -0
  40. package/dist/components/machine-metrics.d.ts +15 -0
  41. package/dist/components/machine-status-pill.d.ts +54 -0
  42. package/dist/components/machines/health.d.ts +25 -0
  43. package/dist/components/machines/machine-detail.d.ts +14 -0
  44. package/dist/components/machines/metric-history-chart.d.ts +24 -0
  45. package/dist/components/machines/metric-sparkline.d.ts +9 -0
  46. package/dist/components/machines/series.d.ts +24 -0
  47. package/dist/components/machines-dashboard.d.ts +30 -0
  48. package/dist/components/markdown.d.ts +31 -0
  49. package/dist/components/message-timeline.d.ts +98 -0
  50. package/dist/components/model-picker.d.ts +19 -0
  51. package/dist/components/pierre-diff.d.ts +34 -0
  52. package/dist/components/pierre-file.d.ts +27 -0
  53. package/dist/components/queue-draft-policy.d.ts +7 -0
  54. package/dist/components/queue-surface-implementation.d.ts +20 -0
  55. package/dist/components/queue-surface-state.d.ts +12 -0
  56. package/dist/components/queue-surface.d.ts +12 -0
  57. package/dist/components/sandbox-files.d.ts +49 -0
  58. package/dist/components/sandbox-terminal.d.ts +123 -0
  59. package/dist/components/sandbox-workspace.d.ts +128 -0
  60. package/dist/components/session-chrome.d.ts +70 -0
  61. package/dist/components/session-status.d.ts +25 -0
  62. package/dist/components/soften-streaming-markdown.d.ts +13 -0
  63. package/dist/components/stream-reveal.d.ts +37 -0
  64. package/dist/components/tip-follow.d.ts +139 -0
  65. package/dist/components/tooltip.d.ts +16 -0
  66. package/dist/components/workbench-changes.d.ts +34 -0
  67. package/dist/components/workspace-dock.d.ts +35 -0
  68. package/dist/composer.d.ts +11 -7
  69. package/dist/composer.js +9 -6
  70. package/dist/hooks/internal.d.ts +46 -0
  71. package/dist/hooks/use-available-models.d.ts +37 -0
  72. package/dist/hooks/use-billing-usage.d.ts +26 -0
  73. package/dist/hooks/use-codex-accounts.d.ts +69 -0
  74. package/dist/{use-composer-ClHy1rkL.d.ts → hooks/use-composer.d.ts} +16 -24
  75. package/dist/hooks/use-desktop-stream.d.ts +43 -0
  76. package/dist/hooks/use-environments.d.ts +35 -0
  77. package/dist/{use-file-attachments-BmT8WtqO.d.ts → hooks/use-file-attachments.d.ts} +6 -9
  78. package/dist/hooks/use-goal.d.ts +46 -0
  79. package/dist/hooks/use-human-input.d.ts +26 -0
  80. package/dist/hooks/use-last-started-turn-policy.d.ts +27 -0
  81. package/dist/hooks/use-machine-chip.d.ts +54 -0
  82. package/dist/hooks/use-machines.d.ts +81 -0
  83. package/dist/hooks/use-packs.d.ts +27 -0
  84. package/dist/hooks/use-relay-frame-stream.d.ts +53 -0
  85. package/dist/hooks/use-rigs.d.ts +87 -0
  86. package/dist/hooks/use-sandbox-files.d.ts +108 -0
  87. package/dist/hooks/use-sandbox-git.d.ts +58 -0
  88. package/dist/hooks/use-sandbox-terminal.d.ts +62 -0
  89. package/dist/hooks/use-scheduled-tasks.d.ts +15 -0
  90. package/dist/hooks/use-session-capabilities.d.ts +83 -0
  91. package/dist/hooks/use-session-control.d.ts +22 -0
  92. package/dist/hooks/use-session-events.d.ts +92 -0
  93. package/dist/hooks/use-session-lineage.d.ts +17 -0
  94. package/dist/hooks/use-session-mcp-approval-policy.d.ts +23 -0
  95. package/dist/hooks/use-session.d.ts +23 -0
  96. package/dist/hooks/use-slash-commands.d.ts +66 -0
  97. package/dist/hooks/use-terminal-stream.d.ts +46 -0
  98. package/dist/hooks/use-transcription.d.ts +73 -0
  99. package/dist/hooks/use-turn-queue.d.ts +44 -0
  100. package/dist/hooks/use-voice-input.d.ts +25 -0
  101. package/dist/hooks/use-windowed-sections.d.ts +42 -0
  102. package/dist/hooks/use-workspace-capture.d.ts +49 -0
  103. package/dist/hooks/use-workspace-edit.d.ts +73 -0
  104. package/dist/hooks/use-workspace-sessions.d.ts +30 -0
  105. package/dist/hooks/use-workspaces.d.ts +22 -0
  106. package/dist/human-input.d.ts +18 -0
  107. package/dist/index.d.ts +144 -2119
  108. package/dist/index.js +421 -324
  109. package/dist/index.js.map +1 -1
  110. package/dist/lib/clipboard.d.ts +7 -0
  111. package/dist/lib/cn.d.ts +3 -0
  112. package/dist/lib/format.d.ts +48 -0
  113. package/dist/lib/git-patch.d.ts +8 -0
  114. package/dist/lib/motion-inspect.d.ts +5 -0
  115. package/dist/lib/motion.d.ts +14 -0
  116. package/dist/lib/noto-sans-arabic-loader.d.ts +1 -0
  117. package/dist/lib/noto-sans-jp-loader.generated.d.ts +1 -0
  118. package/dist/lib/relay-wire.d.ts +30 -0
  119. package/dist/lib/use-portal-token-style.d.ts +9 -0
  120. package/dist/lib/use-theme-type.d.ts +5 -0
  121. package/dist/lib/use-unicode-fonts.d.ts +12 -0
  122. package/dist/lib/xterm-renderer.d.ts +19 -0
  123. package/dist/lib/xterm-theme.d.ts +75 -0
  124. package/dist/machines.d.ts +30 -413
  125. package/dist/machines.js +3 -3
  126. package/dist/model-policy.d.ts +32 -0
  127. package/dist/model-policy.js +33 -0
  128. package/dist/model-policy.js.map +1 -0
  129. package/dist/provider.d.ts +16 -0
  130. package/dist/{queue-surface-implementation-UQ535OKQ.js → queue-surface-implementation-THPAKGMD.js} +2 -2
  131. package/dist/{queue-surface-implementation-UQ535OKQ.js.map → queue-surface-implementation-THPAKGMD.js.map} +1 -1
  132. package/dist/session-context.d.ts +82 -0
  133. package/dist/session-ui.d.ts +10 -7
  134. package/dist/session-ui.js +9 -5
  135. package/dist/session.d.ts +28 -6
  136. package/dist/session.js +11 -11
  137. package/dist/timeline/activity-rail.d.ts +19 -0
  138. package/dist/timeline/disclosure-context.d.ts +14 -0
  139. package/dist/timeline/entrance.d.ts +14 -0
  140. package/dist/timeline/fleet-decision-projection.d.ts +9 -0
  141. package/dist/timeline/fleet-decision-row.d.ts +11 -0
  142. package/dist/timeline/fold-memory.d.ts +15 -0
  143. package/dist/timeline/index.d.ts +18 -0
  144. package/dist/timeline/parsers.d.ts +96 -0
  145. package/dist/timeline/projection.d.ts +38 -0
  146. package/dist/timeline/registry.d.ts +40 -0
  147. package/dist/timeline/screenshot-lightbox.d.ts +26 -0
  148. package/dist/timeline/seen-activity-ids.d.ts +2 -0
  149. package/dist/timeline/shared.d.ts +125 -0
  150. package/dist/timeline/tool-diff.d.ts +8 -0
  151. package/dist/timeline/tool-display-name.d.ts +10 -0
  152. package/dist/timeline/tool-renderers.d.ts +5 -0
  153. package/dist/timeline/turn-summary.d.ts +116 -0
  154. package/dist/{use-turn-queue-Drm_ypue.d.ts → timeline/types.d.ts} +57 -67
  155. package/dist/types/machines.d.ts +11 -0
  156. package/package.json +8 -4
  157. package/src/client.ts +1 -0
  158. package/src/components/composer-transcription-control.tsx +278 -130
  159. package/src/components/composer.tsx +20 -15
  160. package/src/components/copy-button.tsx +127 -0
  161. package/src/components/markdown.tsx +231 -32
  162. package/src/components/message-timeline.tsx +1382 -378
  163. package/src/components/model-picker.tsx +83 -43
  164. package/src/components/queue-surface-implementation.tsx +1 -1
  165. package/src/components/session-chrome.tsx +942 -0
  166. package/src/components/soften-streaming-markdown.ts +205 -0
  167. package/src/components/stream-reveal.ts +195 -0
  168. package/src/components/tip-follow.ts +459 -0
  169. package/src/components/tooltip.tsx +103 -0
  170. package/src/composer.ts +6 -0
  171. package/src/hooks/internal.ts +30 -4
  172. package/src/hooks/use-available-models.ts +51 -4
  173. package/src/hooks/use-composer.ts +24 -18
  174. package/src/hooks/use-last-started-turn-policy.ts +95 -0
  175. package/src/hooks/use-session-events.ts +359 -5
  176. package/src/hooks/use-voice-input.ts +232 -0
  177. package/src/index.ts +49 -1
  178. package/src/lib/clipboard.ts +58 -0
  179. package/src/lib/format.ts +18 -0
  180. package/src/lib/motion-inspect.ts +5 -0
  181. package/src/lib/motion.ts +80 -0
  182. package/src/model-policy.ts +210 -0
  183. package/src/session-ui.ts +7 -0
  184. package/src/session.ts +2 -0
  185. package/src/timeline/activity-rail.tsx +46 -7
  186. package/src/timeline/entrance.tsx +10 -0
  187. package/src/timeline/fold-memory.ts +52 -0
  188. package/src/timeline/index.ts +11 -1
  189. package/src/timeline/projection.ts +252 -47
  190. package/src/timeline/seen-activity-ids.ts +19 -0
  191. package/src/timeline/shared.tsx +1 -1
  192. package/src/timeline/tool-renderers.tsx +75 -6
  193. package/src/timeline/turn-summary.tsx +387 -97
  194. package/src/timeline/types.ts +47 -0
  195. package/styles/index.css +99 -7
  196. package/styles/tokens.css +44 -3
  197. package/dist/chunk-4F2YLLHP.js.map +0 -1
  198. package/dist/chunk-M7X4JZOD.js.map +0 -1
  199. package/dist/chunk-OCHBZ2X7.js.map +0 -1
  200. package/dist/chunk-PMSGE233.js.map +0 -1
  201. package/dist/chunk-WAA247AB.js +0 -3858
  202. package/dist/chunk-WAA247AB.js.map +0 -1
  203. package/dist/chunk-XVVQP7A4.js.map +0 -1
  204. package/dist/composer-BMzimGTO.d.ts +0 -528
  205. package/dist/session-C3tIXLim.d.ts +0 -264
  206. package/dist/session-context-CAjlaeZb.d.ts +0 -84
  207. package/dist/session-ui-B_7cfN5u.d.ts +0 -259
  208. /package/dist/{chunk-BC6RKIHS.js.map → chunk-GZ4KR67U.js.map} +0 -0
@@ -0,0 +1,35 @@
1
+ import type { CreateVariableSetRequest, UpdateVariableSetRequest, VariableSet, VariableSetVariableMetadata } from "@opengeni/sdk";
2
+ import { type ClientOverride } from "../provider";
3
+ export type UseVariableSetsOptions = ClientOverride & {
4
+ pollIntervalMs?: number | undefined;
5
+ enabled?: boolean | undefined;
6
+ };
7
+ export type UseVariableSetsResult = {
8
+ variableSets: VariableSet[];
9
+ loading: boolean;
10
+ error: Error | null;
11
+ refresh: () => Promise<void>;
12
+ create: (request: CreateVariableSetRequest) => Promise<VariableSet | null>;
13
+ update: (variableSetId: string, request: UpdateVariableSetRequest) => Promise<VariableSet | null>;
14
+ remove: (variableSetId: string) => Promise<boolean>;
15
+ /** Set/rotate a variable. Values are write-only — reads expose metadata only. */
16
+ setVariable: (variableSetId: string, name: string, value: string) => Promise<VariableSetVariableMetadata | null>;
17
+ deleteVariable: (variableSetId: string, name: string) => Promise<boolean>;
18
+ mutating: boolean;
19
+ mutationError: Error | null;
20
+ clearMutationError: () => void;
21
+ };
22
+ /**
23
+ * Variable sets (named, encrypted variable sets attached to sessions
24
+ * and scheduled tasks). Variable values are write-only end to end: this hook
25
+ * never sees a value after it is sent.
26
+ */
27
+ export declare function useVariableSets(options?: UseVariableSetsOptions): UseVariableSetsResult;
28
+ /** @deprecated use UseVariableSetsOptions */
29
+ export type UseEnvironmentsOptions = UseVariableSetsOptions;
30
+ /** @deprecated use UseVariableSetsResult */
31
+ export type UseEnvironmentsResult = UseVariableSetsResult & {
32
+ environments: VariableSet[];
33
+ };
34
+ /** @deprecated use useVariableSets */
35
+ export declare function useEnvironments(options?: UseEnvironmentsOptions): UseEnvironmentsResult;
@@ -1,7 +1,6 @@
1
- import { FileAsset, FileResourceRef } from '@opengeni/sdk';
2
- import { E as EmbeddedFileAttachmentClientOverride } from './session-context-CAjlaeZb.js';
3
-
4
- type UseFileAttachmentsOptions = EmbeddedFileAttachmentClientOverride & {
1
+ import type { FileAsset, FileResourceRef } from "@opengeni/sdk";
2
+ import { type EmbeddedFileAttachmentClientOverride } from "../session-context";
3
+ export type UseFileAttachmentsOptions = EmbeddedFileAttachmentClientOverride & {
5
4
  /**
6
5
  * Only files matching this predicate are accepted by {@link
7
6
  * UseFileAttachmentsResult.addFromPaste} (the clipboard path). Defaults to
@@ -11,7 +10,7 @@ type UseFileAttachmentsOptions = EmbeddedFileAttachmentClientOverride & {
11
10
  */
12
11
  pasteFilter?: ((file: File) => boolean) | undefined;
13
12
  };
14
- type FileAttachment = {
13
+ export type FileAttachment = {
15
14
  id: string;
16
15
  name: string;
17
16
  contentType: string;
@@ -23,7 +22,7 @@ type FileAttachment = {
23
22
  previewUrl?: string | undefined;
24
23
  error?: string | undefined;
25
24
  };
26
- type UseFileAttachmentsResult = {
25
+ export type UseFileAttachmentsResult = {
27
26
  attachments: FileAttachment[];
28
27
  /**
29
28
  * `FileResourceRef[]` for every attachment that finished uploading — feed
@@ -71,6 +70,4 @@ type UseFileAttachmentsResult = {
71
70
  * `resources`. Workspace-scoped, so it resolves both client and workspace from
72
71
  * the {@link OpenGeniProvider} (or a per-call `{ client, workspaceId }`).
73
72
  */
74
- declare function useFileAttachments(options?: UseFileAttachmentsOptions): UseFileAttachmentsResult;
75
-
76
- export { type FileAttachment as F, type UseFileAttachmentsResult as U, type UseFileAttachmentsOptions as a, useFileAttachments as u };
73
+ export declare function useFileAttachments(options?: UseFileAttachmentsOptions): UseFileAttachmentsResult;
@@ -0,0 +1,46 @@
1
+ import { type SessionEvent, type SessionGoal } from "@opengeni/sdk";
2
+ import { type EmbeddedGoalClientOverride } from "../session-context";
3
+ import { type SessionEventFeedOptions } from "./internal";
4
+ /** Event types that change the session goal (set/updated/completed/paused/...). */
5
+ export declare function isGoalEvent(event: Pick<SessionEvent, "type">): boolean;
6
+ /**
7
+ * Events that can change the server-authoritative continuation projection.
8
+ * The projection is derived from durable turn, session, control, and system
9
+ * update state, so those events refresh the goal without model polling.
10
+ */
11
+ export declare function isGoalRefreshEvent(event: Pick<SessionEvent, "type">): boolean;
12
+ export type UseGoalOptions = EmbeddedGoalClientOverride & SessionEventFeedOptions & {
13
+ /** Optional safety-net polling (ms). Off by default — event refreshes drive updates. */
14
+ pollIntervalMs?: number | undefined;
15
+ };
16
+ export type UseGoalResult = {
17
+ /** The session goal, or null when the session has none. */
18
+ goal: SessionGoal | null;
19
+ /** Convenience flags over `goal.status`. */
20
+ isActive: boolean;
21
+ isPaused: boolean;
22
+ isCompleted: boolean;
23
+ loading: boolean;
24
+ error: Error | null;
25
+ refresh: () => Promise<void>;
26
+ /** Pause the goal loop (PATCH status=paused). */
27
+ pause: (rationale?: string) => Promise<SessionGoal | null>;
28
+ /** Resume a paused goal: resets counters and re-arms continuations. */
29
+ resume: () => Promise<SessionGoal | null>;
30
+ /** Clear the session goal; goal-less sessions remain a successful no-op. */
31
+ clearGoal: () => Promise<void>;
32
+ /** Alias for `clearGoal`. */
33
+ deleteGoal: () => Promise<void>;
34
+ /** True while a pause/resume/clear is in flight. */
35
+ updating: boolean;
36
+ mutationError: Error | null;
37
+ clearMutationError: () => void;
38
+ };
39
+ /**
40
+ * The session's goal: state, the autonomy counters (`autoContinuations`,
41
+ * `noProgressStreak`), and pause/resume control. A goal-less session yields
42
+ * `goal: null` (the 404 is absorbed). Live-updates on goal, turn, session,
43
+ * control, and system-update events — pass `options.events` from
44
+ * `useSessionEvents` to reuse its stream.
45
+ */
46
+ export declare function useGoal(sessionId: string | null | undefined, options?: UseGoalOptions): UseGoalResult;
@@ -0,0 +1,26 @@
1
+ import type { SessionEvent, SessionHumanInputRequest, SubmitHumanInputResponseRequest } from "@opengeni/sdk";
2
+ import { type EmbeddedHumanInputClientOverride } from "../session-context";
3
+ import { type SessionEventFeedOptions } from "./internal";
4
+ /** Events that can create, settle, or invalidate an actionable request. */
5
+ export declare function isHumanInputEvent(event: Pick<SessionEvent, "type">): boolean;
6
+ export type UseHumanInputRequestsOptions = EmbeddedHumanInputClientOverride & SessionEventFeedOptions & {
7
+ /** Optional safety-net polling. Durable events drive refresh by default. */
8
+ pollIntervalMs?: number | undefined;
9
+ };
10
+ export type UseHumanInputRequestsResult = {
11
+ requests: SessionHumanInputRequest[];
12
+ loading: boolean;
13
+ error: Error | null;
14
+ refresh: () => Promise<void>;
15
+ respond: (requestId: string, response: SubmitHumanInputResponseRequest) => Promise<SessionEvent | null>;
16
+ respondingRequestId: string | null;
17
+ mutationError: Error | null;
18
+ clearMutationError: () => void;
19
+ };
20
+ /**
21
+ * Authoritative structured-human-input hook. The server's pending-request
22
+ * table is the read model; events only trigger reconciliation. This lets an
23
+ * embedded host use the same primitive with a shared event feed or its own SDK
24
+ * proxy without rebuilding request lifecycle logic.
25
+ */
26
+ export declare function useHumanInputRequests(sessionId: string | null | undefined, options?: UseHumanInputRequestsOptions): UseHumanInputRequestsResult;
@@ -0,0 +1,27 @@
1
+ import type { LatencyMode, ReasoningEffort, SessionEvent } from "@opengeni/sdk";
2
+ import { type ClientOverride } from "../session-context";
3
+ import { type SessionEventFeedOptions } from "./internal";
4
+ /** Refresh when a turn actually admits — not on composer picker / queue-only changes. */
5
+ export declare function isLastStartedTurnPolicyEvent(event: Pick<SessionEvent, "type">): boolean;
6
+ export type LastStartedTurnPolicy = {
7
+ model: string;
8
+ reasoningEffort: ReasoningEffort;
9
+ latencyMode: LatencyMode;
10
+ turnId: string;
11
+ };
12
+ export type UseLastStartedTurnPolicyOptions = ClientOverride & SessionEventFeedOptions & {
13
+ pollIntervalMs?: number | undefined;
14
+ };
15
+ export type UseLastStartedTurnPolicyResult = {
16
+ /** Null until some turn has durably emitted `turn.started`. */
17
+ policy: LastStartedTurnPolicy | null;
18
+ loading: boolean;
19
+ error: Error | null;
20
+ refresh: () => Promise<void>;
21
+ };
22
+ /**
23
+ * Model·effort·latency of the newest admitted turn (`turn.started`). This is
24
+ * historical runtime truth for the session header — not the composer next-turn
25
+ * picker and not the frozen session creation defaults.
26
+ */
27
+ export declare function useLastStartedTurnPolicy(sessionId: string | null | undefined, options?: UseLastStartedTurnPolicyOptions): UseLastStartedTurnPolicyResult;
@@ -0,0 +1,54 @@
1
+ import type { MachineState } from "@opengeni/sdk";
2
+ import type { SessionCapabilitiesState } from "./use-session-capabilities";
3
+ /** The one truthful machine indicator. Honest staleness beats
4
+ * fake liveness: a cold/asleep box reads "offline — as of <time>", never "live". */
5
+ export type MachineChipState = "live" | "waking" | "offline";
6
+ export type MachineChip = {
7
+ state: MachineChipState;
8
+ /** A ready-to-render label ("Live" / "Waking…" / "Offline — as of 3m ago"). */
9
+ label: string;
10
+ /** The capture time backing an offline/stale label (ISO), or null. M4 may
11
+ * reformat this; `label` already embeds a relative form. */
12
+ asOf: string | null;
13
+ };
14
+ export type DeriveMachineChipInput = {
15
+ /** `capabilities.liveness` — "warm" | "draining" | "cold" (or null pre-negotiation). */
16
+ liveness?: string | null | undefined;
17
+ /** `useSessionCapabilities().state` — drives the "waking" (negotiating) read. */
18
+ capabilitiesState?: SessionCapabilitiesState | null | undefined;
19
+ /** The ACTIVE machine's connection state (from `useMachines`), when known. A
20
+ * self-hosted machine that is `offline` cannot be remotely woken. */
21
+ activeMachineState?: MachineState | null | undefined;
22
+ /** Whether the active sandbox is a user-owned self-hosted machine (no remote wake). */
23
+ activeIsSelfhosted?: boolean | undefined;
24
+ /** An interaction (wake-on-edit, terminal focus) is actively warming the box. */
25
+ wantsWarm?: boolean | undefined;
26
+ /** The latest capture's `capturedAt` (ISO) — the "as of <time>" backing. */
27
+ capturedAt?: string | null | undefined;
28
+ /** Injectable clock for deterministic relative labels (defaults to Date.now). */
29
+ now?: number | undefined;
30
+ };
31
+ /** Compact relative-time for the "as of" label. Pure, no Intl dependency. */
32
+ export declare function formatAsOf(capturedAt: string, now: number): string;
33
+ /**
34
+ * Derive the machine-state chip from the live capability/liveness surface, the
35
+ * active machine's connection state, and the latest capture time. PURE (M4 renders
36
+ * it; the whole point is a deterministic, testable projection).
37
+ *
38
+ * Precedence:
39
+ * 1. warm/draining lease → **live** (a warm box always wins).
40
+ * 2. actively warming (negotiating, wake-on-edit, or a reconnecting/enrolling
41
+ * machine) → **waking**.
42
+ * 3. everything else (a cold/asleep box, a self-hosted machine that's offline,
43
+ * an error) → **offline**, labelled "as of <capture time>".
44
+ */
45
+ export declare function deriveMachineChip(input: DeriveMachineChipInput): MachineChip;
46
+ export type UseMachineChipOptions = DeriveMachineChipInput;
47
+ /**
48
+ * Thin memoized wrapper over `deriveMachineChip` for the dock header (M4). The
49
+ * dock already runs `useSessionCapabilities` + `useMachines` + `useWorkspace
50
+ * capture`; it feeds their `liveness` / `state` / active-machine state /
51
+ * `capturedAt` here. Pass a periodically-updated `now` if you want the relative
52
+ * "as of" label to tick.
53
+ */
54
+ export declare function useMachineChip(input: UseMachineChipOptions): MachineChip;
@@ -0,0 +1,81 @@
1
+ import { type ClientOverride } from "../provider";
2
+ import type { MachinesResponse, MachineView, MetricSample } from "../types/machines";
3
+ /**
4
+ * The slice of the SDK client the Machines surface needs. The method NAMES +
5
+ * SIGNATURES match M10's `OpenGeniClient` (`listMachines`, `machineMetricsSeries`)
6
+ * so the real SDK client satisfies this surface DIRECTLY for the read paths — no
7
+ * adapter needed. It is declared structurally (not a hard `OpenGeniClient` Pick)
8
+ * so a test/demo/Geni-frontend client can stand in, keeping the hook dual-consumer
9
+ * safe (works in apps/web AND the separate Geni frontend).
10
+ *
11
+ * The active-sandbox SWAP is now a typed SDK call (`swapActiveSandbox`, the M7
12
+ * user-authenticated REST equivalent of the `sandbox_swap` MCP tool). The real
13
+ * SDK client satisfies it structurally, so the default attach path is wired
14
+ * WHENEVER a sessionId is in scope (the swap is session-scoped). `attachMachine`
15
+ * stays an OPTIONAL escape hatch for a host that wants to supply its own swap
16
+ * adapter; when neither it nor a sessionId is present, attach is a no-op and the
17
+ * card hides the button.
18
+ */
19
+ export type MachinesClientLike = {
20
+ /** GET /v1/workspaces/:ws/machines — the dashboard list + active pointer. */
21
+ listMachines: (workspaceId: string, options?: {
22
+ sessionId?: string;
23
+ signal?: AbortSignal;
24
+ }) => Promise<MachinesResponse>;
25
+ /** GET .../machines/:enrollmentId/metrics/series — the downsampled history. */
26
+ machineMetricsSeries?: (workspaceId: string, enrollmentId: string, options?: {
27
+ window?: "15m" | "1h" | "6h" | "24h";
28
+ }) => Promise<MetricSample[]>;
29
+ /**
30
+ * POST .../sessions/:sessionId/active-sandbox — swap the session's active
31
+ * sandbox to a machine. The default swap path; the real SDK client provides it.
32
+ */
33
+ swapActiveSandbox?: (workspaceId: string, sessionId: string, request: {
34
+ target: string;
35
+ }) => Promise<unknown>;
36
+ /**
37
+ * Host-supplied swap adapter (an escape hatch). When present it wins over the
38
+ * default `swapActiveSandbox` path. Session-scoped, like the swap it backs.
39
+ */
40
+ attachMachine?: (workspaceId: string, sessionId: string, sandboxId: string) => Promise<unknown>;
41
+ };
42
+ export type UseMachinesOptions = ClientOverride & {
43
+ pollIntervalMs?: number | undefined;
44
+ enabled?: boolean | undefined;
45
+ /** Scope the list to a session (adds the synthetic Modal group box + pointer). */
46
+ sessionId?: string | undefined;
47
+ /**
48
+ * Override the client with one implementing `MachinesClientLike`. Defaults to
49
+ * the provider client cast to the surface (the real SDK client satisfies the
50
+ * read paths). An app supplies an adapter to wire `attachMachine` (the swap).
51
+ */
52
+ machinesClient?: MachinesClientLike | undefined;
53
+ };
54
+ export type UseMachinesResult = {
55
+ machines: MachineView[];
56
+ activeSandboxId: string | null;
57
+ activeEpoch: number;
58
+ loading: boolean;
59
+ error: Error | null;
60
+ refresh: () => Promise<void>;
61
+ /** Attach/swap the session's active sandbox to a machine (returns the new pointer). */
62
+ attach: (sandboxId: string) => Promise<boolean>;
63
+ /** Whether the host wired an attach/swap path (drives the card affordance). */
64
+ canAttach: boolean;
65
+ /** Fetch a downsampled metric series for one enrolled machine. */
66
+ fetchSeries: (enrollmentId: string, window?: "15m" | "1h" | "6h" | "24h") => Promise<MetricSample[]>;
67
+ attaching: boolean;
68
+ /** The sandbox id of the in-flight attach (for per-card spinner gating). */
69
+ attachingSandboxId: string | null;
70
+ mutationError: Error | null;
71
+ clearMutationError: () => void;
72
+ };
73
+ /**
74
+ * The workspace Machines fleet: the selfhosted enrollments + the session's Modal
75
+ * box, each with latest metrics + state, plus the active-sandbox pointer. Polls
76
+ * the M10 `GET /machines` endpoint and exposes attach/swap + a metric-series
77
+ * fetch. Renders via `<MachinesDashboard>`. Dual-consumer safe: it reads only the
78
+ * structural `MachinesClientLike` surface, so it works in apps/web AND the Geni
79
+ * frontend (each provides its own client/adapter).
80
+ */
81
+ export declare function useMachines(options?: UseMachinesOptions): UseMachinesResult;
@@ -0,0 +1,27 @@
1
+ import type { CapabilityPack, EnablePackRequest, PackInstallation, RegisterCapabilityPackRequest, WorkspaceRegisteredPack } from "@opengeni/sdk";
2
+ import { type ClientOverride } from "../provider";
3
+ export type UsePacksOptions = ClientOverride & {
4
+ pollIntervalMs?: number | undefined;
5
+ enabled?: boolean | undefined;
6
+ };
7
+ export type UsePacksResult = {
8
+ /** Built-in + registered packs available to the workspace. */
9
+ packs: CapabilityPack[];
10
+ /** Enable/disable state per pack. */
11
+ installations: PackInstallation[];
12
+ /** The installation for a pack id, if any. */
13
+ installationFor: (packId: string) => PackInstallation | null;
14
+ loading: boolean;
15
+ error: Error | null;
16
+ refresh: () => Promise<void>;
17
+ /** Register (or replace) a workspace-scoped pack manifest. */
18
+ register: (manifest: RegisterCapabilityPackRequest) => Promise<WorkspaceRegisteredPack | null>;
19
+ enable: (packId: string, request?: EnablePackRequest) => Promise<PackInstallation | null>;
20
+ /** Unregister a workspace-scoped pack (built-ins cannot be removed). */
21
+ remove: (packId: string) => Promise<boolean>;
22
+ mutating: boolean;
23
+ mutationError: Error | null;
24
+ clearMutationError: () => void;
25
+ };
26
+ /** Capability packs: catalog + installations + register/enable/unregister. */
27
+ export declare function usePacks(options?: UsePacksOptions): UsePacksResult;
@@ -0,0 +1,53 @@
1
+ import type { DesktopConnectionState, DesktopStreamCapability } from "@opengeni/sdk";
2
+ import { type RefObject } from "react";
3
+ /**
4
+ * The minimal WebSocket surface the frame renderer drives. Lets tests (and a
5
+ * future transport swap) inject a fake without a live socket — the exact
6
+ * analogue of `DesktopRfbFactory` for the noVNC path. Structurally a subset of
7
+ * the browser `WebSocket`.
8
+ */
9
+ export interface DesktopWebSocketLike {
10
+ binaryType: string;
11
+ send(data: ArrayBuffer): void;
12
+ close(): void;
13
+ addEventListener(type: "open" | "message" | "error" | "close", listener: (ev: {
14
+ data?: unknown;
15
+ } & Record<string, unknown>) => void): void;
16
+ removeEventListener(type: "open" | "message" | "error" | "close", listener: (ev: {
17
+ data?: unknown;
18
+ } & Record<string, unknown>) => void): void;
19
+ }
20
+ export type DesktopWebSocketFactory = (url: string) => DesktopWebSocketLike;
21
+ export type UseRelayFrameStreamOptions = {
22
+ /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */
23
+ capability: DesktopStreamCapability | null;
24
+ /** The mount target. A `<canvas>` is appended here on connect. */
25
+ containerRef: RefObject<HTMLDivElement | null>;
26
+ /** Custom socket factory (tests / a transport swap). Defaults to `new WebSocket(url)`. */
27
+ webSocketFactory?: DesktopWebSocketFactory | undefined;
28
+ };
29
+ export type UseRelayFrameStreamResult = {
30
+ state: DesktopConnectionState;
31
+ error: Error | null;
32
+ /** Tear down + reopen the socket (e.g. after a drop, once a fresh url arrives). */
33
+ reconnect: () => void;
34
+ };
35
+ /**
36
+ * VIEW-ONLY PNG-frame renderer for a SELF-HOSTED desktop stream — the relay
37
+ * transport (`transport: "relay-frames"`, `client: "frames"`). The self-hosted
38
+ * agent produces one PNG per frame as a protobuf datagram over the relay; there
39
+ * is no RFB/noVNC here. This hook opens the relay DESKTOP channel as a CLIENT
40
+ * (mirroring the proven diagnostic wire protocol), decodes each PNG onto a
41
+ * `<canvas>` mounted into `containerRef`, and drives the connection state
42
+ * machine: idle → connecting (ws opening) → connected (first painted frame) →
43
+ * error (ws error/close/ack rejection).
44
+ *
45
+ * It is DORMANT for any other transport (or no url): it stays idle and touches
46
+ * nothing, so the noVNC path owns the surface. SSR-safe: the socket + DOM attach
47
+ * live inside `useEffect`.
48
+ *
49
+ * Backpressure (critical — frames are ~1.8MB at ~10fps): at most one frame is
50
+ * decoded at a time; a frame arriving mid-decode replaces the pending one
51
+ * (latest-wins), so a slow decode can never build an unbounded queue.
52
+ */
53
+ export declare function useRelayFrameStream(options: UseRelayFrameStreamOptions): UseRelayFrameStreamResult;
@@ -0,0 +1,87 @@
1
+ import type { CreateRigRequest, ProposeRigChangeRequest, Rig, RigChange, RigVersion, UpdateRigRequest } from "@opengeni/sdk";
2
+ import { type ClientOverride } from "../provider";
3
+ export type UseRigsOptions = ClientOverride & {
4
+ pollIntervalMs?: number | undefined;
5
+ enabled?: boolean | undefined;
6
+ };
7
+ export type UseRigsResult = {
8
+ rigs: Rig[];
9
+ loading: boolean;
10
+ error: Error | null;
11
+ refresh: () => Promise<void>;
12
+ create: (request: CreateRigRequest) => Promise<Rig | null>;
13
+ update: (rigId: string, request: UpdateRigRequest) => Promise<Rig | null>;
14
+ remove: (rigId: string) => Promise<boolean>;
15
+ listVersions: (rigId: string) => Promise<RigVersion[] | null>;
16
+ activateVersion: (rigId: string, versionId: string) => Promise<RigVersion | null>;
17
+ listChanges: (rigId: string) => Promise<RigChange[] | null>;
18
+ proposeChange: (rigId: string, request: ProposeRigChangeRequest) => Promise<RigChange | null>;
19
+ mutating: boolean;
20
+ mutationError: Error | null;
21
+ clearMutationError: () => void;
22
+ };
23
+ /**
24
+ * Rigs (workspace-scoped, versioned sandbox machine definitions). The list
25
+ * polls; version/change reads are on-demand (they are per-rig detail, not part
26
+ * of the list surface).
27
+ */
28
+ export declare function useRigs(options?: UseRigsOptions): UseRigsResult;
29
+ export type UseRigOptions = ClientOverride & {
30
+ pollIntervalMs?: number | undefined;
31
+ enabled?: boolean | undefined;
32
+ };
33
+ export type UseRigResult = {
34
+ rig: Rig | null;
35
+ loading: boolean;
36
+ error: Error | null;
37
+ refresh: () => Promise<void>;
38
+ update: (request: UpdateRigRequest) => Promise<Rig | null>;
39
+ remove: () => Promise<boolean>;
40
+ activateVersion: (versionId: string) => Promise<RigVersion | null>;
41
+ proposeChange: (request: ProposeRigChangeRequest) => Promise<RigChange | null>;
42
+ /** Re-run verification for a change (asynchronous — poll for the outcome). */
43
+ verifyChange: (changeId: string) => Promise<RigChange | null>;
44
+ /** Promote a verified definition_edit into a new active version (rigs:manage). */
45
+ promoteChange: (changeId: string) => Promise<RigVersion | null>;
46
+ /** Re-verify the active version's checks in a clean throwaway sandbox. */
47
+ verify: () => Promise<{
48
+ ok: boolean;
49
+ versionId: string;
50
+ } | null>;
51
+ mutating: boolean;
52
+ mutationError: Error | null;
53
+ clearMutationError: () => void;
54
+ };
55
+ /**
56
+ * A single rig (its active version + counts), polled so verification and
57
+ * promotion state stay live. Owns the rig's write actions; the versions and
58
+ * changes lists are separate polled reads (`useRigVersions`/`useRigChanges`).
59
+ */
60
+ export declare function useRig(rigId: string, options?: UseRigOptions): UseRigResult;
61
+ export type UseRigVersionsOptions = ClientOverride & {
62
+ pollIntervalMs?: number | undefined;
63
+ enabled?: boolean | undefined;
64
+ };
65
+ export type UseRigVersionsResult = {
66
+ versions: RigVersion[];
67
+ loading: boolean;
68
+ error: Error | null;
69
+ refresh: () => Promise<void>;
70
+ };
71
+ /** A rig's append-only version history, newest-first (polled). */
72
+ export declare function useRigVersions(rigId: string, options?: UseRigVersionsOptions): UseRigVersionsResult;
73
+ export type UseRigChangesOptions = ClientOverride & {
74
+ pollIntervalMs?: number | undefined;
75
+ enabled?: boolean | undefined;
76
+ };
77
+ export type UseRigChangesResult = {
78
+ changes: RigChange[];
79
+ loading: boolean;
80
+ error: Error | null;
81
+ refresh: () => Promise<void>;
82
+ };
83
+ /**
84
+ * A rig's change queue (polled). The default poll cadence lets a change move
85
+ * through verifying → merged/rejected without a manual refresh.
86
+ */
87
+ export declare function useRigChanges(rigId: string, options?: UseRigChangesOptions): UseRigChangesResult;
@@ -0,0 +1,108 @@
1
+ import type { FsReadResponse, FsWriteResponse, OpenGeniRequestOptions, SessionEvent, WorkspaceCaptureManifest } from "@opengeni/sdk";
2
+ import { type ClientOverride } from "../provider";
3
+ /** The git-status overlay a file row may carry (tints modified files in the tree). */
4
+ export type FileTreeStatus = "added" | "modified" | "deleted" | "renamed" | "untracked";
5
+ export type CapturedFileUnavailableReason = "not-captured" | "too-large" | "content-missing";
6
+ /** A cold capture can index a path without retaining its contents. Components
7
+ * use this typed result to offer an explicit wake action instead of silently
8
+ * turning passive review into a live sandbox read. */
9
+ export declare class CapturedFileUnavailableError extends Error {
10
+ readonly path: string;
11
+ readonly reason: CapturedFileUnavailableReason;
12
+ readonly code = "captured_file_unavailable";
13
+ constructor(path: string, reason: CapturedFileUnavailableReason);
14
+ }
15
+ export declare class FileWriteConflictError extends Error {
16
+ readonly path: string;
17
+ readonly expectedContent: string;
18
+ readonly liveContent: string;
19
+ readonly code = "file_write_conflict";
20
+ constructor(path: string, expectedContent: string, liveContent: string);
21
+ }
22
+ export type SandboxWriteFileOptions = {
23
+ /** Exact text loaded into the editor. The live file is re-read before write;
24
+ * divergence fails closed with FileWriteConflictError. */
25
+ expectedContent?: string | undefined;
26
+ /** Deliberately bypass the expected-content guard after a visible conflict. */
27
+ force?: boolean | undefined;
28
+ };
29
+ /** A node in the Pierre file tree. `children === undefined` ⇒ an unexpanded dir
30
+ * (lazy treeMode); `children: []` ⇒ an expanded-but-empty dir. */
31
+ export type FileTreeNode = {
32
+ path: string;
33
+ name: string;
34
+ kind: "file" | "dir";
35
+ children?: FileTreeNode[] | undefined;
36
+ size?: number | null | undefined;
37
+ status?: FileTreeStatus | undefined;
38
+ /** A residue dir the capture COLLAPSED (node_modules, .git, dist, …): its
39
+ * contents were never indexed. Cold, expanding it can't list children — the
40
+ * UI shows an inline "contents on machine" row until the box is warm. */
41
+ truncated?: boolean | undefined;
42
+ };
43
+ export type UseSandboxFilesOptions = ClientOverride & {
44
+ /** Live event log (usually `useSessionEvents().events`) — drives auto-refresh
45
+ * on `fs.changed` / `git.changed`. */
46
+ events?: SessionEvent[] | undefined;
47
+ /** Initial path to list (workspace root by default). */
48
+ rootPath?: string | undefined;
49
+ /** Hold off the initial list (e.g. panel collapsed). Default true. */
50
+ enabled?: boolean | undefined;
51
+ /** The lease liveness ("cold" | "warm" | "draining"). The structured FileSystem
52
+ * capability is advertised even on a COLD box, so the mount-time list can race
53
+ * the box: it lists before the box is warm, gets an empty/errored result, and
54
+ * (with no `fs.changed` event) never re-lists. Passing liveness re-lists when
55
+ * the box first becomes warm, so the tree populates as soon as the box is up. */
56
+ liveness?: string | undefined;
57
+ /** The latest turn-end workspace capture (from `useWorkspaceCapture`). The tree
58
+ * paints immediately from this durable index, then a warm box reconciles live
59
+ * in place. If that live read fails, the capture remains a truthful read-only
60
+ * fallback instead of turning into an empty error surface. */
61
+ capture?: WorkspaceCaptureManifest | null | undefined;
62
+ /** Called when an OPTIMISTIC mutation is reverted because its background
63
+ * Channel-A op failed (e.g. a 409 rename collision). The host wires this to a
64
+ * toast — the tree silently rolls the node back, the user sees why. */
65
+ onMutationError?: ((error: Error, op: string) => void) | undefined;
66
+ };
67
+ export type UseSandboxFilesResult = {
68
+ /** The tree roots (the listed root's children). */
69
+ tree: FileTreeNode[];
70
+ /** Lazy-expand a directory node in place (lists its immediate children). */
71
+ expand: (path: string) => Promise<void>;
72
+ /** Paths whose lazy `fs.list` is currently in flight — the FileBrowser shows a
73
+ * spinner on these nodes so a 2-3s Channel-A list never looks frozen. */
74
+ expandingPaths: Set<string>;
75
+ /** Read a file for the preview pane (text or base64-for-binary, size-capped). */
76
+ readFile: (path: string, options?: OpenGeniRequestOptions) => Promise<FsReadResponse>;
77
+ /** Write a file (overwrite, last-writer-wins) — the editor save path.
78
+ * Optimistic: a brand-new file is spliced into the tree immediately and the
79
+ * Channel-A write runs in the background; on failure the splice is reverted. */
80
+ writeFile: (path: string, content: string, options?: SandboxWriteFileOptions) => Promise<FsWriteResponse>;
81
+ /** Create a new empty file (refuses to clobber an existing path: overwrite=false). */
82
+ createFile: (path: string) => Promise<void>;
83
+ /** Create a directory (recursive by default). */
84
+ createDir: (path: string) => Promise<void>;
85
+ /** Delete a path (pass recursive=true for a non-empty directory). */
86
+ deleteEntry: (path: string, recursive?: boolean) => Promise<void>;
87
+ /** Move / rename a path (rename == move). Refuses to clobber unless overwrite=true. */
88
+ moveEntry: (path: string, newPath: string, opts?: {
89
+ overwrite?: boolean;
90
+ }) => Promise<void>;
91
+ /** Re-list the whole tree from the root. */
92
+ refresh: () => Promise<void>;
93
+ /** Which source the tree is currently served from: the live box, the turn-end
94
+ * capture (cold/offline), or neither yet. M5's source badge reads this. */
95
+ source: "live" | "capture" | null;
96
+ /** When the served capture was taken (ISO), when `source === "capture"`. */
97
+ capturedAt: string | null;
98
+ loading: boolean;
99
+ error: Error | null;
100
+ };
101
+ /**
102
+ * Project the FileSystem service into a lazy-loaded Pierre tree. The initial
103
+ * list pulls one level (depth 1); `expand(path)` lists a directory's immediate
104
+ * children on demand (the fast lazy-tree UX). A git-status overlay tints
105
+ * modified files. Auto-refreshes when an `fs.changed` / `git.changed` event
106
+ * arrives on the live log.
107
+ */
108
+ export declare function useSandboxFiles(sessionId: string | null | undefined, options?: UseSandboxFilesOptions): UseSandboxFilesResult;
@@ -0,0 +1,58 @@
1
+ import type { GitFileDiff, SessionEvent, WorkspaceCaptureManifest } from "@opengeni/sdk";
2
+ import { type ClientOverride } from "../provider";
3
+ export type UseSandboxGitOptions = ClientOverride & {
4
+ /** Live event log (usually `useSessionEvents().events`) — drives auto-refresh
5
+ * on `git.changed`. */
6
+ events?: SessionEvent[] | undefined;
7
+ /** Repo root within the workspace (multi-repo). Default: workspace root. */
8
+ repoPath?: string | undefined;
9
+ /** All repository roots in the workspace. When supplied, status and diff are
10
+ * aggregated into one workspace-wide result and paths are workspace-relative. */
11
+ repoPaths?: readonly string[] | undefined;
12
+ /** Diff the staged index vs HEAD (`--cached`) instead of the working tree. */
13
+ staged?: boolean | undefined;
14
+ /** Hold off the initial fetch. Default true. */
15
+ enabled?: boolean | undefined;
16
+ /** Lease liveness ("warm" | "draining" | "cold"). When NOT warm, the diff is
17
+ * served from the capture (cold/offline) instead of a live `gitDiff` RPC. */
18
+ liveness?: string | undefined;
19
+ /** The latest turn-end capture (from `useWorkspaceCapture`). Seeds the diff
20
+ * immediately; a warm box reconciles live and leaves this durable review
21
+ * surface in place if the live request is temporarily unavailable. */
22
+ capture?: WorkspaceCaptureManifest | null | undefined;
23
+ };
24
+ /** A Git diff qualified into workspace scope. `repoRoot` is present for the
25
+ * workspace-wide path and lets review UI group real repositories without
26
+ * guessing from the first path segment. */
27
+ export type SandboxGitFileDiff = GitFileDiff & {
28
+ repoRoot?: string | undefined;
29
+ };
30
+ export type UseSandboxGitResult = {
31
+ /** Working-tree (or staged) diff vs HEAD — the structured hunks the Pierre
32
+ * diff view renders. */
33
+ diff: SandboxGitFileDiff[];
34
+ branch: string | null;
35
+ /** Whether a repo is actually mounted (drives "no repository" vs "no changes"). */
36
+ isRepo: boolean;
37
+ ahead: number;
38
+ behind: number;
39
+ /** Number and workspace-relative roots of repositories represented here. */
40
+ repoCount: number;
41
+ repoRoots: string[];
42
+ refresh: () => Promise<void>;
43
+ /** Which source the diff is served from: the live box or the turn-end capture. */
44
+ source: "live" | "capture" | null;
45
+ /** When the served capture was taken (ISO), when `source === "capture"`. */
46
+ capturedAt: string | null;
47
+ loading: boolean;
48
+ error: Error | null;
49
+ };
50
+ /**
51
+ * Project the Git service into the Pierre diff data contract: structured
52
+ * `GitFileDiff[]` (per-file hunks with per-line old/new numbers, rename
53
+ * detection, binary flag, add/del counts) plus branch + ahead/behind. When the
54
+ * box is warm the `git diff` runs in-box (API-direct); when it is cold/offline the
55
+ * diff is served from the turn-end capture instead. Refreshes on
56
+ * `git.changed`; reconciles live in place on the cold→warm transition.
57
+ */
58
+ export declare function useSandboxGit(sessionId: string | null | undefined, options?: UseSandboxGitOptions): UseSandboxGitResult;