@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.
- package/README.md +110 -10
- package/dist/{chunk-TOJR776I.js → chunk-6BXBGJ3B.js} +45 -369
- package/dist/chunk-6BXBGJ3B.js.map +1 -0
- package/dist/chunk-6UT5OC3P.js +2133 -0
- package/dist/chunk-6UT5OC3P.js.map +1 -0
- package/dist/chunk-EBTT3MYN.js +92 -0
- package/dist/chunk-EBTT3MYN.js.map +1 -0
- package/dist/chunk-HHFA4AFX.js +425 -0
- package/dist/chunk-HHFA4AFX.js.map +1 -0
- package/dist/chunk-LAZ2A743.js +2393 -0
- package/dist/chunk-LAZ2A743.js.map +1 -0
- package/dist/chunk-MRSECXRP.js +563 -0
- package/dist/chunk-MRSECXRP.js.map +1 -0
- package/dist/chunk-TK7G6XLT.js +18 -0
- package/dist/chunk-TK7G6XLT.js.map +1 -0
- package/dist/composer-DvUaa5ki.d.ts +585 -0
- package/dist/composer.d.ts +6 -0
- package/dist/composer.js +56 -0
- package/dist/composer.js.map +1 -0
- package/dist/index.d.ts +1288 -1785
- package/dist/index.js +7015 -8806
- package/dist/index.js.map +1 -1
- package/dist/machines.d.ts +412 -3
- package/dist/machines.js +3 -1
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
- package/dist/session-Gd4iVPYw.d.ts +425 -0
- package/dist/session-context-DIFFlXKc.d.ts +52 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +50 -0
- package/dist/session.js.map +1 -0
- package/dist/use-composer-BCfm4mzX.d.ts +112 -0
- package/package.json +20 -4
- package/src/approvals.ts +5 -3
- package/src/client.ts +40 -0
- package/src/components/approval-surface.tsx +198 -0
- package/src/components/chat-composer.tsx +94 -993
- package/src/components/code-editor.tsx +104 -26
- package/src/components/command-palette.tsx +8 -2
- package/src/components/composer-transcription-control.tsx +211 -0
- package/src/components/composer.tsx +1536 -0
- package/src/components/desktop-viewer.tsx +3 -3
- package/src/components/enrollment-consent.tsx +2 -2
- package/src/components/file-browser.tsx +367 -29
- package/src/components/human-input-form.tsx +417 -0
- package/src/components/machine-card.tsx +1 -1
- package/src/components/machines-dashboard.tsx +3 -3
- package/src/components/message-timeline.tsx +15 -7
- package/src/components/model-picker.tsx +12 -3
- package/src/components/pierre-diff.tsx +32 -6
- package/src/components/queue-surface-implementation.tsx +1041 -0
- package/src/components/queue-surface-state.tsx +94 -0
- package/src/components/queue-surface.tsx +41 -559
- package/src/components/sandbox-files.tsx +138 -14
- package/src/components/sandbox-terminal.tsx +8 -2
- package/src/components/sandbox-workspace.tsx +339 -119
- package/src/components/workbench-changes.tsx +84 -33
- package/src/components/workspace-dock.tsx +188 -18
- package/src/composer.ts +60 -0
- package/src/hooks/internal.ts +115 -34
- package/src/hooks/use-composer.ts +335 -104
- package/src/hooks/use-human-input.ts +131 -0
- package/src/hooks/use-machine-chip.ts +2 -2
- package/src/hooks/use-machines.ts +27 -11
- package/src/hooks/use-sandbox-files.ts +310 -62
- package/src/hooks/use-sandbox-git.ts +154 -40
- package/src/hooks/use-sandbox-terminal.ts +28 -6
- package/src/hooks/use-session-capabilities.ts +38 -9
- package/src/hooks/use-session-control.ts +37 -8
- package/src/hooks/use-session-events.ts +522 -48
- package/src/hooks/use-slash-commands.ts +38 -38
- package/src/hooks/use-transcription.ts +757 -0
- package/src/hooks/use-turn-queue.ts +134 -66
- package/src/hooks/use-windowed-sections.ts +2 -2
- package/src/hooks/use-workspace-capture.ts +146 -40
- package/src/hooks/use-workspace-edit.ts +50 -14
- package/src/hooks/use-workspace-sessions.ts +3 -0
- package/src/human-input.ts +72 -0
- package/src/index.ts +76 -6
- package/src/lib/noto-sans-arabic-loader.ts +21 -0
- package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
- package/src/lib/use-portal-token-style.ts +51 -0
- package/src/lib/use-unicode-fonts.ts +57 -0
- package/src/provider.tsx +4 -69
- package/src/session-context.ts +135 -0
- package/src/session.ts +79 -0
- package/src/timeline/parsers.ts +35 -0
- package/src/timeline/projection.ts +12 -4
- package/src/timeline/tool-renderers.tsx +41 -0
- package/src/timeline/turn-summary.tsx +2 -2
- package/src/timeline/types.ts +16 -8
- package/styles/index.css +56 -0
- package/styles/index.d.ts +2 -0
- package/styles/tokens.css +11 -6
- package/styles/tokens.d.ts +2 -0
- package/dist/chunk-TOJR776I.js.map +0 -1
- 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
|
|
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"
|
|
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(
|
|
103
|
-
|
|
104
|
-
|
|
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 [
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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(
|