@opengeni/react 0.15.0 → 0.20.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 (101) hide show
  1. package/README.md +110 -10
  2. package/dist/{chunk-TOJR776I.js → chunk-6BXBGJ3B.js} +45 -369
  3. package/dist/chunk-6BXBGJ3B.js.map +1 -0
  4. package/dist/chunk-6UT5OC3P.js +2133 -0
  5. package/dist/chunk-6UT5OC3P.js.map +1 -0
  6. package/dist/chunk-EBTT3MYN.js +92 -0
  7. package/dist/chunk-EBTT3MYN.js.map +1 -0
  8. package/dist/chunk-HHFA4AFX.js +425 -0
  9. package/dist/chunk-HHFA4AFX.js.map +1 -0
  10. package/dist/chunk-LAZ2A743.js +2393 -0
  11. package/dist/chunk-LAZ2A743.js.map +1 -0
  12. package/dist/chunk-MRSECXRP.js +563 -0
  13. package/dist/chunk-MRSECXRP.js.map +1 -0
  14. package/dist/chunk-TK7G6XLT.js +18 -0
  15. package/dist/chunk-TK7G6XLT.js.map +1 -0
  16. package/dist/composer-DvUaa5ki.d.ts +585 -0
  17. package/dist/composer.d.ts +6 -0
  18. package/dist/composer.js +56 -0
  19. package/dist/composer.js.map +1 -0
  20. package/dist/index.d.ts +1288 -1785
  21. package/dist/index.js +7015 -8806
  22. package/dist/index.js.map +1 -1
  23. package/dist/machines.d.ts +412 -3
  24. package/dist/machines.js +3 -1
  25. package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
  26. package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
  27. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
  28. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
  29. package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
  30. package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
  31. package/dist/session-Gd4iVPYw.d.ts +425 -0
  32. package/dist/session-context-DIFFlXKc.d.ts +52 -0
  33. package/dist/session.d.ts +4 -0
  34. package/dist/session.js +50 -0
  35. package/dist/session.js.map +1 -0
  36. package/dist/use-composer-BCfm4mzX.d.ts +112 -0
  37. package/package.json +20 -4
  38. package/src/approvals.ts +5 -3
  39. package/src/client.ts +40 -0
  40. package/src/components/approval-surface.tsx +198 -0
  41. package/src/components/chat-composer.tsx +94 -993
  42. package/src/components/code-editor.tsx +104 -26
  43. package/src/components/command-palette.tsx +8 -2
  44. package/src/components/composer-transcription-control.tsx +211 -0
  45. package/src/components/composer.tsx +1536 -0
  46. package/src/components/desktop-viewer.tsx +3 -3
  47. package/src/components/enrollment-consent.tsx +2 -2
  48. package/src/components/file-browser.tsx +367 -29
  49. package/src/components/human-input-form.tsx +417 -0
  50. package/src/components/machine-card.tsx +1 -1
  51. package/src/components/machines-dashboard.tsx +3 -3
  52. package/src/components/message-timeline.tsx +15 -7
  53. package/src/components/model-picker.tsx +12 -3
  54. package/src/components/pierre-diff.tsx +32 -6
  55. package/src/components/queue-surface-implementation.tsx +1041 -0
  56. package/src/components/queue-surface-state.tsx +94 -0
  57. package/src/components/queue-surface.tsx +41 -559
  58. package/src/components/sandbox-files.tsx +138 -14
  59. package/src/components/sandbox-terminal.tsx +8 -2
  60. package/src/components/sandbox-workspace.tsx +339 -119
  61. package/src/components/workbench-changes.tsx +84 -33
  62. package/src/components/workspace-dock.tsx +188 -18
  63. package/src/composer.ts +60 -0
  64. package/src/hooks/internal.ts +115 -34
  65. package/src/hooks/use-composer.ts +335 -104
  66. package/src/hooks/use-human-input.ts +131 -0
  67. package/src/hooks/use-machine-chip.ts +2 -2
  68. package/src/hooks/use-machines.ts +27 -11
  69. package/src/hooks/use-sandbox-files.ts +310 -62
  70. package/src/hooks/use-sandbox-git.ts +154 -40
  71. package/src/hooks/use-sandbox-terminal.ts +28 -6
  72. package/src/hooks/use-session-capabilities.ts +38 -9
  73. package/src/hooks/use-session-control.ts +37 -8
  74. package/src/hooks/use-session-events.ts +522 -48
  75. package/src/hooks/use-slash-commands.ts +38 -38
  76. package/src/hooks/use-transcription.ts +757 -0
  77. package/src/hooks/use-turn-queue.ts +134 -66
  78. package/src/hooks/use-windowed-sections.ts +2 -2
  79. package/src/hooks/use-workspace-capture.ts +146 -40
  80. package/src/hooks/use-workspace-edit.ts +50 -14
  81. package/src/hooks/use-workspace-sessions.ts +3 -0
  82. package/src/human-input.ts +72 -0
  83. package/src/index.ts +76 -6
  84. package/src/lib/noto-sans-arabic-loader.ts +21 -0
  85. package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
  86. package/src/lib/use-portal-token-style.ts +51 -0
  87. package/src/lib/use-unicode-fonts.ts +57 -0
  88. package/src/provider.tsx +4 -69
  89. package/src/session-context.ts +135 -0
  90. package/src/session.ts +79 -0
  91. package/src/timeline/parsers.ts +35 -0
  92. package/src/timeline/projection.ts +12 -4
  93. package/src/timeline/tool-renderers.tsx +41 -0
  94. package/src/timeline/turn-summary.tsx +2 -2
  95. package/src/timeline/types.ts +16 -8
  96. package/styles/index.css +56 -0
  97. package/styles/index.d.ts +2 -0
  98. package/styles/tokens.css +11 -6
  99. package/styles/tokens.d.ts +2 -0
  100. package/dist/chunk-TOJR776I.js.map +0 -1
  101. package/dist/machines-BpdwuQcD.d.ts +0 -449
@@ -0,0 +1,131 @@
1
+ import type {
2
+ SessionEvent,
3
+ SessionHumanInputRequest,
4
+ SubmitHumanInputResponseRequest,
5
+ } from "@opengeni/sdk";
6
+ import { useCallback, useEffect, useRef, useState } from "react";
7
+ import {
8
+ useEmbeddedHumanInputSession,
9
+ type EmbeddedHumanInputClientOverride,
10
+ } from "../session-context";
11
+ import {
12
+ useDebouncedCallback,
13
+ useMutationRunner,
14
+ usePolledValue,
15
+ useSessionEventTrigger,
16
+ type SessionEventFeedOptions,
17
+ } from "./internal";
18
+
19
+ /** Events that can create, settle, or invalidate an actionable request. */
20
+ export function isHumanInputEvent(event: Pick<SessionEvent, "type">): boolean {
21
+ return (
22
+ event.type === "session.humanInput.requested" ||
23
+ event.type === "user.humanInputResponse" ||
24
+ event.type === "turn.completed" ||
25
+ event.type === "turn.failed" ||
26
+ event.type === "turn.cancelled"
27
+ );
28
+ }
29
+
30
+ export type UseHumanInputRequestsOptions = EmbeddedHumanInputClientOverride &
31
+ SessionEventFeedOptions & {
32
+ /** Optional safety-net polling. Durable events drive refresh by default. */
33
+ pollIntervalMs?: number | undefined;
34
+ };
35
+
36
+ export type UseHumanInputRequestsResult = {
37
+ requests: SessionHumanInputRequest[];
38
+ loading: boolean;
39
+ error: Error | null;
40
+ refresh: () => Promise<void>;
41
+ respond: (
42
+ requestId: string,
43
+ response: SubmitHumanInputResponseRequest,
44
+ ) => Promise<SessionEvent | null>;
45
+ respondingRequestId: string | null;
46
+ mutationError: Error | null;
47
+ clearMutationError: () => void;
48
+ };
49
+
50
+ /**
51
+ * Authoritative structured-human-input hook. The server's pending-request
52
+ * table is the read model; events only trigger reconciliation. This lets an
53
+ * embedded host use the same primitive with a shared event feed or its own SDK
54
+ * proxy without rebuilding request lifecycle logic.
55
+ */
56
+ export function useHumanInputRequests(
57
+ sessionId: string | null | undefined,
58
+ options: UseHumanInputRequestsOptions = {},
59
+ ): UseHumanInputRequestsResult {
60
+ const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
61
+ const enabled = (options.enabled ?? true) && Boolean(sessionId);
62
+ const load = useCallback(
63
+ async (): Promise<SessionHumanInputRequest[]> =>
64
+ sessionId
65
+ ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" })
66
+ : [],
67
+ [client, sessionId, workspaceId],
68
+ );
69
+ const state = usePolledValue(load, {
70
+ enabled,
71
+ pollIntervalMs: options.pollIntervalMs,
72
+ });
73
+ const mutation = useMutationRunner(`${workspaceId}\u0000${sessionId ?? ""}`);
74
+ const [respondingRequestId, setRespondingRequestId] = useState<string | null>(null);
75
+ const respondingRequestRef = useRef<string | null>(null);
76
+
77
+ useEffect(() => {
78
+ respondingRequestRef.current = null;
79
+ setRespondingRequestId(null);
80
+ }, [sessionId, workspaceId]);
81
+ useEffect(() => {
82
+ if (!sessionId || !enabled) return;
83
+ return registerSessionReconciler(sessionId, "human-input", state.refresh);
84
+ }, [enabled, registerSessionReconciler, sessionId, state.refresh]);
85
+
86
+ const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
87
+ useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
88
+ enabled,
89
+ ...(options.events !== undefined ? { events: options.events } : {}),
90
+ });
91
+
92
+ const respond = useCallback(
93
+ async (
94
+ requestId: string,
95
+ response: SubmitHumanInputResponseRequest,
96
+ ): Promise<SessionEvent | null> => {
97
+ if (!sessionId || respondingRequestRef.current !== null) return null;
98
+ respondingRequestRef.current = requestId;
99
+ setRespondingRequestId(requestId);
100
+ try {
101
+ const event = await mutation.run(() =>
102
+ client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
103
+ clientEventId: globalThis.crypto.randomUUID(),
104
+ }),
105
+ );
106
+ if (event) await state.refresh();
107
+ return event;
108
+ } finally {
109
+ if (respondingRequestRef.current === requestId) {
110
+ respondingRequestRef.current = null;
111
+ setRespondingRequestId(null);
112
+ }
113
+ }
114
+ },
115
+ [client, mutation, sessionId, state, workspaceId],
116
+ );
117
+
118
+ return {
119
+ requests: [...(state.data ?? [])].sort(
120
+ (left, right) =>
121
+ left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id),
122
+ ),
123
+ loading: state.loading,
124
+ error: state.error,
125
+ refresh: state.refresh,
126
+ respond,
127
+ respondingRequestId,
128
+ mutationError: mutation.mutationError,
129
+ clearMutationError: mutation.clearMutationError,
130
+ };
131
+ }
@@ -2,7 +2,7 @@ import { useMemo } from "react";
2
2
  import type { MachineState } from "@opengeni/sdk";
3
3
  import type { SessionCapabilitiesState } from "./use-session-capabilities";
4
4
 
5
- /** The one truthful machine indicator (dossier §3 #10). Honest staleness beats
5
+ /** The one truthful machine indicator. Honest staleness beats
6
6
  * fake liveness: a cold/asleep box reads "offline — as of <time>", never "live". */
7
7
  export type MachineChipState = "live" | "waking" | "offline";
8
8
 
@@ -70,7 +70,7 @@ export function deriveMachineChip(input: DeriveMachineChipInput): MachineChip {
70
70
  }
71
71
 
72
72
  // A self-hosted machine that reports offline cannot be woken remotely — honest
73
- // "offline", never "waking" (dossier §3 #5 / #10).
73
+ // "offline", never "waking".
74
74
  const selfhostedOffline =
75
75
  input.activeIsSelfhosted === true && input.activeMachineState === "offline";
76
76
 
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from "react";
1
+ import { useCallback, useRef, useState } from "react";
2
2
  import { useOpenGeni, type ClientOverride } from "../provider";
3
3
  import { useMutationRunner, usePolledValue } from "./internal";
4
4
  import type { MachinesResponse, MachineView, MetricSample } from "../types/machines";
@@ -23,7 +23,7 @@ export type MachinesClientLike = {
23
23
  /** GET /v1/workspaces/:ws/machines — the dashboard list + active pointer. */
24
24
  listMachines: (
25
25
  workspaceId: string,
26
- options?: { sessionId?: string },
26
+ options?: { sessionId?: string; signal?: AbortSignal },
27
27
  ) => Promise<MachinesResponse>;
28
28
  /** GET .../machines/:enrollmentId/metrics/series — the downsampled history. */
29
29
  machineMetricsSeries?: (
@@ -98,10 +98,19 @@ export function useMachines(options: UseMachinesOptions = {}): UseMachinesResult
98
98
  const machinesClient = (options.machinesClient ??
99
99
  (client as unknown as MachinesClientLike)) satisfies MachinesClientLike;
100
100
  const sessionId = options.sessionId;
101
+ const identityKey = `${workspaceId}\u0000${sessionId ?? ""}`;
102
+ const identityRef = useRef(identityKey);
103
+ identityRef.current = identityKey;
101
104
 
102
- const load = useCallback(async () => {
103
- return await machinesClient.listMachines(workspaceId, sessionId ? { sessionId } : undefined);
104
- }, [machinesClient, workspaceId, sessionId]);
105
+ const load = useCallback(
106
+ async (signal?: AbortSignal) => {
107
+ return await machinesClient.listMachines(workspaceId, {
108
+ ...(sessionId ? { sessionId } : {}),
109
+ ...(signal ? { signal } : {}),
110
+ });
111
+ },
112
+ [machinesClient, workspaceId, sessionId],
113
+ );
105
114
 
106
115
  const {
107
116
  data: loadedData,
@@ -112,9 +121,13 @@ export function useMachines(options: UseMachinesOptions = {}): UseMachinesResult
112
121
  pollIntervalMs: options.pollIntervalMs,
113
122
  enabled: options.enabled,
114
123
  });
115
- const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
124
+ const { run, mutating, mutationError, clearMutationError } = useMutationRunner(identityKey);
116
125
  // The sandbox id of the in-flight attach (drives the per-card spinner).
117
- const [attachingSandboxId, setAttachingSandboxId] = useState<string | null>(null);
126
+ const [attachState, setAttachState] = useState<{
127
+ identity: string;
128
+ sandboxId: string | null;
129
+ }>(() => ({ identity: identityKey, sandboxId: null }));
130
+ const attachingSandboxId = attachState.identity === identityKey ? attachState.sandboxId : null;
118
131
 
119
132
  const data = loadedData ?? EMPTY;
120
133
  // The swap is session-scoped: a host adapter (`attachMachine`) wins; otherwise
@@ -134,16 +147,19 @@ export function useMachines(options: UseMachinesOptions = {}): UseMachinesResult
134
147
  ? () => machinesClient.swapActiveSandbox!(workspaceId, sessionId, { target: sandboxId })
135
148
  : null;
136
149
  if (!runSwap) return false;
137
- setAttachingSandboxId(sandboxId);
150
+ const ownedIdentity = identityKey;
151
+ setAttachState({ identity: ownedIdentity, sandboxId });
138
152
  const result = await run(async () => {
139
153
  await runSwap();
140
154
  return true;
141
155
  });
142
- setAttachingSandboxId(null);
143
- if (result) await refresh();
156
+ if (identityRef.current === ownedIdentity) {
157
+ setAttachState({ identity: ownedIdentity, sandboxId: null });
158
+ if (result) await refresh();
159
+ }
144
160
  return result === true;
145
161
  },
146
- [machinesClient, workspaceId, sessionId, run, refresh],
162
+ [machinesClient, workspaceId, sessionId, identityKey, run, refresh],
147
163
  );
148
164
 
149
165
  const fetchSeries = useCallback(