@alexkroman1/aai-ui 0.12.3 → 0.14.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 +253 -0
- package/dist/_colors-CpZO-88A.js +78 -0
- package/dist/_module-url-C13kAJ87.js +15 -0
- package/dist/_page-shell.d.ts +11 -0
- package/dist/_recover-run.d.ts +79 -0
- package/dist/_repeat-until.d.ts +30 -0
- package/dist/_run-controls.d.ts +33 -0
- package/dist/_submission-state.d.ts +92 -0
- package/dist/_tap-to-talk-state.d.ts +88 -0
- package/dist/_utils-Mi-SCFA1.js +47 -0
- package/dist/_utils.d.ts +26 -0
- package/dist/_web-storage.d.ts +43 -0
- package/dist/_workflow-api-ref.d.ts +37 -0
- package/dist/_workflow-files.d.ts +34 -0
- package/dist/_workflow-test-defs.d.ts +34 -0
- package/dist/aai-logo-D8pMVn9_.js +85 -0
- package/dist/agent-state-labels.d.ts +65 -0
- package/dist/agent-state.d.ts +108 -0
- package/dist/audio/capture.d.ts +44 -0
- package/dist/audio/index.d.ts +18 -0
- package/dist/audio/index.js +431 -0
- package/dist/audio/preconnect.d.ts +87 -0
- package/dist/audio/voice-io.d.ts +112 -0
- package/dist/build-default-client.d.ts +1 -0
- package/dist/chat-view-BCbI7gD4.js +396 -0
- package/dist/client-config-C7fQrioM.js +139 -0
- package/dist/client-config.d.ts +119 -0
- package/dist/client-dir.d.ts +44 -0
- package/dist/client-dir.js +58 -0
- package/dist/client-identity.d.ts +131 -0
- package/dist/client-tool.d.ts +51 -0
- package/dist/components/_colors.d.ts +73 -0
- package/dist/components/_form-readiness.d.ts +56 -0
- package/dist/components/_form-values.d.ts +19 -0
- package/dist/components/aai-logo.d.ts +12 -0
- package/dist/components/audio-result.d.ts +94 -0
- package/dist/components/auto-scroll.d.ts +74 -0
- package/dist/components/bullet-list.d.ts +74 -0
- package/dist/components/button.d.ts +29 -21
- package/dist/components/button.js +62 -23
- package/dist/components/chat-view.d.ts +24 -18
- package/dist/components/chat-view.js +2 -60
- package/dist/components/console-shell.d.ts +85 -0
- package/dist/components/controls.d.ts +21 -6
- package/dist/components/controls.js +31 -19
- package/dist/components/conversation-view.d.ts +130 -0
- package/dist/components/eyebrow.d.ts +12 -0
- package/dist/components/facts.d.ts +81 -0
- package/dist/components/form-fields.d.ts +142 -0
- package/dist/components/form-types.d.ts +73 -0
- package/dist/components/form.d.ts +96 -0
- package/dist/components/markdown.d.ts +57 -0
- package/dist/components/message-list.d.ts +34 -7
- package/dist/components/message-list.js +2 -75
- package/dist/components/session-controls.d.ts +125 -0
- package/dist/components/session-error-banner.d.ts +69 -0
- package/dist/components/session-state-dot.d.ts +111 -0
- package/dist/components/sidebar-layout.d.ts +31 -10
- package/dist/components/sidebar-layout.js +32 -12
- package/dist/components/start-screen.d.ts +17 -6
- package/dist/components/start-screen.js +34 -14
- package/dist/components/tool-call-block.d.ts +15 -14
- package/dist/components/tool-call-block.js +2 -117
- package/dist/components/tool-call-row.d.ts +75 -0
- package/dist/components/tool-config-context.d.ts +22 -0
- package/dist/components/upload-progress.d.ts +76 -0
- package/dist/components/url-chips.d.ts +29 -0
- package/dist/components/workflow-fields.d.ts +46 -0
- package/dist/components/workflow-pending-note.d.ts +81 -0
- package/dist/components/workflow-progress.d.ts +81 -0
- package/dist/components/workflow-run-error.d.ts +48 -0
- package/dist/components/workflow-run-panel.d.ts +79 -0
- package/dist/context.d.ts +288 -0
- package/dist/context.js +382 -0
- package/dist/conversation-log.d.ts +88 -0
- package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
- package/dist/default-client/assets/audio-mpgipo2Q.js +1 -0
- package/dist/default-client/assets/capture-processor-D0q7Qrdd.js +90 -0
- package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
- package/dist/default-client/assets/index-DPfmSFmc.js +126 -0
- package/dist/default-client/assets/playback-processor-CjDbc9iH.js +314 -0
- package/dist/default-client/favicon.ico +0 -0
- package/dist/default-client/index.html +15 -0
- package/dist/default-client-entry.d.ts +6 -0
- package/dist/default-client.d.ts +19 -0
- package/dist/define-client.d.ts +169 -26
- package/dist/define-client.js +209 -55
- package/dist/eyebrow-UfmSz9yy.js +27 -0
- package/dist/hooks-DN4gHYsj.js +182 -0
- package/dist/hooks.d.ts +172 -0
- package/dist/hooks.js +3 -0
- package/dist/hooks.test-d.d.ts +14 -0
- package/dist/inbox-protocol.d.ts +128 -0
- package/dist/inbox.d.ts +132 -0
- package/dist/index.d.ts +126 -29
- package/dist/index.js +5500 -15
- package/dist/internal.d.ts +43 -0
- package/dist/internal.js +8 -0
- package/dist/linked-client.d.ts +70 -0
- package/dist/message-list-DHZcLhRA.js +682 -0
- package/dist/notice-player.d.ts +70 -0
- package/dist/page.d.ts +114 -0
- package/dist/phone.d.ts +50 -0
- package/dist/route-fetch.d.ts +79 -0
- package/dist/session/audio-effects.d.ts +37 -0
- package/dist/session/audio-setup.d.ts +57 -0
- package/dist/session/audio-state.d.ts +157 -0
- package/dist/session/browser-session.d.ts +39 -0
- package/dist/session/close.d.ts +26 -0
- package/dist/session/dial.d.ts +65 -0
- package/dist/session/handshake.d.ts +60 -0
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +2 -0
- package/dist/session/messages.d.ts +63 -0
- package/dist/session/mic.d.ts +64 -0
- package/dist/session/preconnect.d.ts +48 -0
- package/dist/session/reconnect.d.ts +25 -0
- package/dist/session/resume-store.d.ts +57 -0
- package/dist/session/state.d.ts +146 -0
- package/dist/session/ticket.d.ts +44 -0
- package/dist/session/types.d.ts +476 -0
- package/dist/session/url.d.ts +17 -0
- package/dist/session/user-turn.d.ts +55 -0
- package/dist/session-D9jM5-TF.js +2140 -0
- package/dist/stored-value.d.ts +103 -0
- package/dist/tool-call-block-uXtMOUWN.js +204 -0
- package/dist/tool-config-context-Es4YUzV2.js +19 -0
- package/dist/types.d.ts +272 -34
- package/dist/types.js +35 -4
- package/dist/upload/files.d.ts +83 -0
- package/dist/upload/index.d.ts +15 -0
- package/dist/upload/pause.d.ts +31 -0
- package/dist/upload/recall.d.ts +67 -0
- package/dist/upload/report.d.ts +26 -0
- package/dist/upload/session.d.ts +99 -0
- package/dist/url-chips-oyy_Eiqh.js +239 -0
- package/dist/use-client-runs.d.ts +87 -0
- package/dist/use-conversation-log.d.ts +86 -0
- package/dist/use-conversation.d.ts +131 -0
- package/dist/use-copy.d.ts +74 -0
- package/dist/use-download-url.d.ts +84 -0
- package/dist/use-flash.d.ts +61 -0
- package/dist/use-inbox.d.ts +101 -0
- package/dist/use-push-to-talk.d.ts +105 -0
- package/dist/use-route-mutation.d.ts +113 -0
- package/dist/use-route.d.ts +82 -0
- package/dist/use-run-key.d.ts +133 -0
- package/dist/use-session-controls.d.ts +69 -0
- package/dist/use-session-id.d.ts +57 -0
- package/dist/use-tap-to-talk.d.ts +138 -0
- package/dist/use-user-transcript-uyHhzy4d.js +73 -0
- package/dist/use-user-transcript.d.ts +75 -0
- package/dist/use-workflow-form.d.ts +285 -0
- package/dist/use-workflow-progress.d.ts +137 -0
- package/dist/use-workflow-run-DdhWmbx-.js +697 -0
- package/dist/use-workflow-run.d.ts +100 -0
- package/dist/use-workflow-runs.d.ts +71 -0
- package/dist/use-workflow-stream.d.ts +154 -0
- package/dist/use-workflows.d.ts +77 -0
- package/dist/workflow-client.d.ts +136 -0
- package/dist/workflow-def-types.d.ts +31 -0
- package/dist/workflow-events.d.ts +39 -0
- package/dist/workflow-field-kind.d.ts +95 -0
- package/dist/workflow-status-labels.d.ts +35 -0
- package/dist/worklets/_module-url.d.ts +10 -0
- package/dist/worklets/_playback-bench-harness.d.ts +181 -0
- package/dist/worklets/_playback-bench-host.d.ts +63 -0
- package/dist/worklets/_playback-bench-page.d.ts +65 -0
- package/dist/worklets/_tts-trace-harness.d.ts +142 -0
- package/dist/worklets/_worklet-test-utils.d.ts +27 -0
- package/dist/worklets/capture-processor.d.ts +4 -2
- package/dist/worklets/capture-processor.js +73 -33
- package/dist/worklets/playback-processor.d.ts +4 -2
- package/dist/worklets/playback-processor.js +258 -42
- package/package.json +59 -32
- package/styles.css +152 -30
- package/dist/_jsdom-setup.d.ts +0 -0
- package/dist/audio.d.ts +0 -45
- package/dist/audio.js +0 -121
- package/dist/client-context.d.ts +0 -33
- package/dist/client-context.js +0 -15
- package/dist/client-handler.d.ts +0 -43
- package/dist/components/app.d.ts +0 -23
- package/dist/components/app.js +0 -41
- package/dist/components/error-banner.d.ts +0 -21
- package/dist/components/error-banner.js +0 -27
- package/dist/components/message-bubble.d.ts +0 -23
- package/dist/components/message-bubble.js +0 -35
- package/dist/components/state-indicator.d.ts +0 -29
- package/dist/components/state-indicator.js +0 -37
- package/dist/components/thinking-indicator.d.ts +0 -16
- package/dist/components/thinking-indicator.js +0 -34
- package/dist/components/tool-icons.d.ts +0 -17
- package/dist/components/tool-icons.js +0 -128
- package/dist/components/transcript.d.ts +0 -25
- package/dist/components/transcript.js +0 -35
- package/dist/session-CWB7vmqz.js +0 -429
- package/dist/session.d.ts +0 -69
- package/dist/session.js +0 -2
- package/dist/signals.d.ts +0 -120
- package/dist/signals.js +0 -161
- package/dist/tsdown.config.d.ts +0 -2
- package/dist/types.test-d.d.ts +0 -7
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useWorkflowRun` — watch one run until it settles.
|
|
3
|
+
*
|
|
4
|
+
* Split from `workflow-client.ts` on the seam that module's doc already draws:
|
|
5
|
+
* everything there is a REQUEST (one call, one answer, no React), and everything
|
|
6
|
+
* here is the loop that keeps asking. They are read for different reasons — the
|
|
7
|
+
* client is what a script or a `curl` equivalent needs, this is what a page needs
|
|
8
|
+
* — and only this half imports React.
|
|
9
|
+
*
|
|
10
|
+
* `workflow-events.ts` sits under it as the streaming fast path, and
|
|
11
|
+
* `use-workflow-form.ts` above it as the form-shaped caller.
|
|
12
|
+
*/
|
|
13
|
+
import type { WorkflowApi, WorkflowRun } from "./workflow-client.ts";
|
|
14
|
+
/** How often {@link useWorkflowRun} re-reads a live run when it has to poll. */
|
|
15
|
+
export declare const DEFAULT_WORKFLOW_POLL_MS = 2000;
|
|
16
|
+
/**
|
|
17
|
+
* Consecutive "no such run" reads {@link useWorkflowRun} tolerates before giving
|
|
18
|
+
* up on the id.
|
|
19
|
+
*
|
|
20
|
+
* Small on purpose: a 404 is a stable answer, so the budget exists only to
|
|
21
|
+
* absorb a first read that races the run's creation — not to keep hoping.
|
|
22
|
+
* Unbounded, a stale id polls (and, on the platform, BROKERS) for as long as the
|
|
23
|
+
* tab is open.
|
|
24
|
+
*/
|
|
25
|
+
export declare const MAX_MISSING_READS = 3;
|
|
26
|
+
export type UseWorkflowRunResult<R = unknown> = {
|
|
27
|
+
/** Latest snapshot, or undefined before the first read lands. */
|
|
28
|
+
run: WorkflowRun<R> | undefined;
|
|
29
|
+
/** The last read's failure, cleared by the next successful one. */
|
|
30
|
+
error: string | undefined;
|
|
31
|
+
/** True while a non-terminal run is still being watched. */
|
|
32
|
+
polling: boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Watch one run until it reaches a terminal status.
|
|
36
|
+
*
|
|
37
|
+
* A watch rather than a subscription because a run is durable and the page is
|
|
38
|
+
* not: it can complete while the tab is closed, on a different sandbox, hours
|
|
39
|
+
* later. There is no session to reconnect — the id is the whole state.
|
|
40
|
+
*
|
|
41
|
+
* Which is also the limit of what this hook can do on its own. An id is state a
|
|
42
|
+
* RELOAD destroys, so a page holding nothing else comes back unable to name a
|
|
43
|
+
* run that is still going. The durable handle is `StartOptions.key`, read back
|
|
44
|
+
* with `find(workflow, key)`, and the hook that owns the id is where that
|
|
45
|
+
* belongs: `useWorkflowSubmit({ key, recover: true })` adopts the key's newest
|
|
46
|
+
* run as it mounts and passes the id here. See `_recover-run.ts` — the reason
|
|
47
|
+
* recovery is NOT in this hook is `reset()`, which leaves the owner holding no
|
|
48
|
+
* id on purpose, and a watcher that re-resolved one from a key would undo it.
|
|
49
|
+
*
|
|
50
|
+
* The stream (`GET /runs/:id/events`) is tried first and the poll is its
|
|
51
|
+
* fallback, so an agent deployed before that route existed still works. Watching
|
|
52
|
+
* STOPS on a terminal status, so a finished run costs nothing; passing
|
|
53
|
+
* `undefined` (nothing started yet) also costs nothing.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* import type { ToolInputSchema, WorkflowDef } from "@alexkroman1/aai";
|
|
58
|
+
* import type { WorkflowOutputOf } from "@alexkroman1/aai/workflow-api";
|
|
59
|
+
* import { useWorkflowRun } from "@alexkroman1/aai-ui";
|
|
60
|
+
*
|
|
61
|
+
* // A real page writes `import type { digest } from "./agent.ts"`. Stood in
|
|
62
|
+
* // for here so the example compiles on its own.
|
|
63
|
+
* declare const digest: WorkflowDef<ToolInputSchema, Promise<{ points: string[] }>>;
|
|
64
|
+
*
|
|
65
|
+
* type Digest = WorkflowOutputOf<typeof digest>;
|
|
66
|
+
*
|
|
67
|
+
* function RunPanel({ runId }: { runId: string | undefined }) {
|
|
68
|
+
* // The type argument is what makes `run.output` a `Digest` below.
|
|
69
|
+
* const { run, error, polling } = useWorkflowRun<Digest>(runId);
|
|
70
|
+
* if (error !== undefined) return <p role="alert">{error}</p>;
|
|
71
|
+
* if (run?.status === "completed") {
|
|
72
|
+
* return (
|
|
73
|
+
* <ul>
|
|
74
|
+
* {run.output.points.map((point) => (
|
|
75
|
+
* <li key={point}>{point}</li>
|
|
76
|
+
* ))}
|
|
77
|
+
* </ul>
|
|
78
|
+
* );
|
|
79
|
+
* }
|
|
80
|
+
* return <p>{polling ? "Working…" : "Nothing running."}</p>;
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @typeParam R - The workflow's output type. Supplying it is what makes
|
|
85
|
+
* `run.status === "completed"` narrow to a typed `run.output` instead of
|
|
86
|
+
* `unknown`. Derive it with `WorkflowOutputOf<typeof myWorkflow>` — a
|
|
87
|
+
* type-only import of `agent.ts` is erased, so it costs the bundle nothing.
|
|
88
|
+
* @param runId - The run to watch. `undefined` costs nothing, so a page may
|
|
89
|
+
* pass its state straight through before a run exists.
|
|
90
|
+
* @param options - `api` when the page holds its own client; `intervalMs` to
|
|
91
|
+
* change the poll interval the stream falls back to.
|
|
92
|
+
* @returns The latest snapshot, the last read's error, and whether the watch
|
|
93
|
+
* is still going — see {@link UseWorkflowRunResult}.
|
|
94
|
+
*
|
|
95
|
+
* @public
|
|
96
|
+
*/
|
|
97
|
+
export declare function useWorkflowRun<R = unknown>(runId: string | undefined, options?: {
|
|
98
|
+
api?: WorkflowApi;
|
|
99
|
+
intervalMs?: number;
|
|
100
|
+
}): UseWorkflowRunResult<R>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RUNS a workflow has had — the list a page shows beside its form.
|
|
3
|
+
*
|
|
4
|
+
* `useWorkflowRun` watches one run you already hold an id for, which is the
|
|
5
|
+
* right shape for the run a page just started and the wrong one for everything
|
|
6
|
+
* before it. A workflow app that only offers that leaves its own history
|
|
7
|
+
* unreachable: a page reload drops the id, and the only way back to yesterday's
|
|
8
|
+
* transcript is to have written the id down — which is what
|
|
9
|
+
* `transcription-workflow` asked people to do, with a text box for pasting one.
|
|
10
|
+
*
|
|
11
|
+
* `GET /workflows/runs?workflow=…` has always been able to answer this. This is
|
|
12
|
+
* the hook over it, so a page renders history instead of asking for an id.
|
|
13
|
+
*
|
|
14
|
+
* ## It re-reads on demand, and does not poll on its own
|
|
15
|
+
*
|
|
16
|
+
* A list is not a live view: the run a page cares about right now is already
|
|
17
|
+
* being watched by `useWorkflowRun`, and a second polling loop over the whole
|
|
18
|
+
* history would broker N requests a minute on the platform to re-learn what the
|
|
19
|
+
* first one already knows. So this reads once and hands back `refresh` — which
|
|
20
|
+
* a page calls when its own run settles, which is exactly when the list is
|
|
21
|
+
* stale.
|
|
22
|
+
*/
|
|
23
|
+
import type { WorkflowApi, WorkflowRun } from "./workflow-client.ts";
|
|
24
|
+
/** Options for {@link useWorkflowRuns}. */
|
|
25
|
+
export type UseWorkflowRunsOptions = {
|
|
26
|
+
/** The client to read with. Defaults to one for the page's own agent. */
|
|
27
|
+
api?: WorkflowApi;
|
|
28
|
+
/** Most runs to return, newest first. The agent clamps its own ceiling. */
|
|
29
|
+
limit?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Narrow to one correlation key — the `key` a run was started with.
|
|
32
|
+
*
|
|
33
|
+
* Omitted, the list is every recent run of the workflow, which is what an
|
|
34
|
+
* operator's page wants. A page showing "your" runs passes the key it started
|
|
35
|
+
* them with; there is no per-user filtering behind this, so the key IS the
|
|
36
|
+
* scoping mechanism.
|
|
37
|
+
*/
|
|
38
|
+
key?: string;
|
|
39
|
+
/** Skip the read entirely — for a page that does not know its workflow yet. */
|
|
40
|
+
skip?: boolean;
|
|
41
|
+
};
|
|
42
|
+
/** What {@link useWorkflowRuns} reports. */
|
|
43
|
+
export type UseWorkflowRunsResult<R = unknown> = {
|
|
44
|
+
/** The runs, newest first. Empty until the first read lands. */
|
|
45
|
+
runs: WorkflowRun<R>[];
|
|
46
|
+
/** True until the first read settles, and during an explicit refresh. */
|
|
47
|
+
loading: boolean;
|
|
48
|
+
/** The read's failure, alongside an empty list — which is why it exists. */
|
|
49
|
+
error: string | undefined;
|
|
50
|
+
/** Re-read now. Call it when a run this page started reaches a terminal status. */
|
|
51
|
+
refresh: () => void;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Read a workflow's recent runs.
|
|
55
|
+
*
|
|
56
|
+
* @typeParam R - The workflow's output type, so a completed run's `output` is
|
|
57
|
+
* typed rather than `unknown`. Derive it with `WorkflowOutputOf`.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```tsx
|
|
61
|
+
* import { useWorkflowRuns } from "@alexkroman1/aai-ui";
|
|
62
|
+
*
|
|
63
|
+
* function History() {
|
|
64
|
+
* const { runs } = useWorkflowRuns("transcribe", { limit: 10 });
|
|
65
|
+
* return <ul>{runs.map((run) => <li key={run.runId}>{run.status}</li>)}</ul>;
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @public
|
|
70
|
+
*/
|
|
71
|
+
export declare function useWorkflowRuns<R = unknown>(workflow: string | undefined, options?: UseWorkflowRunsOptions): UseWorkflowRunsResult<R>;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starting a run BEFORE its file has finished uploading.
|
|
3
|
+
*
|
|
4
|
+
* `useWorkflowSubmit` (`use-workflow-form.ts`) stores every file and then starts the
|
|
5
|
+
* run, because a `POST /workflows/uploads` cannot answer with an id until the last
|
|
6
|
+
* byte is in — the store writes an ordinary upload's record last, so "incomplete"
|
|
7
|
+
* and "no such upload" are deliberately the same answer. For a long recording that
|
|
8
|
+
* order is most of the wall clock.
|
|
9
|
+
*
|
|
10
|
+
* This hook inverts it, and it takes ONE extra idea to do so: the id is the
|
|
11
|
+
* CLIENT's. It mints one, starts the run on it, and `PUT`s the whole file in a
|
|
12
|
+
* single streaming request; the upload record exists from the first byte with
|
|
13
|
+
* `complete: false` and its `size` grows, so the run reads whatever has arrived.
|
|
14
|
+
*
|
|
15
|
+
* ```text
|
|
16
|
+
* start run ─┬──────────────────────────────────────────────► (still running)
|
|
17
|
+
* │
|
|
18
|
+
* PUT /uploads/<id> ═══════════════════════════════════► wake
|
|
19
|
+
* (one request; the run polls `size` and `complete`)
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ## What it does NOT do, which is the point
|
|
23
|
+
*
|
|
24
|
+
* There is no cutting, no part numbering, no terminator and no per-part request. An
|
|
25
|
+
* earlier version of this hook took a `cut` callback and uploaded N parts into a
|
|
26
|
+
* "group" that a separate call had to seal; it worked, and every piece of it was
|
|
27
|
+
* something the caller had to get right. The whole of that is replaced by the store
|
|
28
|
+
* publishing `size` as bytes land — which `stepReadUpload` already clamped to — so the
|
|
29
|
+
* run does exactly what it does over a finished file and simply waits for windows to
|
|
30
|
+
* become present.
|
|
31
|
+
*
|
|
32
|
+
* It also means the FORMAT knowledge stays where it already was. Deciding where a
|
|
33
|
+
* recording may be divided is the run's business (`planSegments` in the
|
|
34
|
+
* transcription template), and nothing here needs to know it is audio at all.
|
|
35
|
+
*
|
|
36
|
+
* ## Three things it owns
|
|
37
|
+
*
|
|
38
|
+
* - **The id.** Minted here, put in the run input where the workflow's `uploads`
|
|
39
|
+
* list says, and never seen by the page. It is a capability — anyone holding it
|
|
40
|
+
* can read the bytes back — so it is a `crypto.randomUUID()` rather than anything
|
|
41
|
+
* derived from the file, and the store refuses a second `PUT` to it.
|
|
42
|
+
* - **The wake after the upload.** `POST /workflows/runs/:id/wake` ends a pending
|
|
43
|
+
* `sleep`, and a run waiting on an upload is asleep between polls — so without
|
|
44
|
+
* this it learns the file is complete up to a poll interval late, every time. On
|
|
45
|
+
* the transcription template's 5-second interval that is most of the tail it has
|
|
46
|
+
* left to pay. Best-effort, because a wake that finds nothing sleeping answers 0
|
|
47
|
+
* and a missed one costs latency rather than correctness.
|
|
48
|
+
* - **Reporting the bytes.** The same `UploadStatus` `useWorkflowSubmit` reports,
|
|
49
|
+
* so `<UploadProgressBar>` renders either without knowing which hook it came from.
|
|
50
|
+
*
|
|
51
|
+
* ## A failed upload is RESUMED, and only a spent budget cancels the run
|
|
52
|
+
*
|
|
53
|
+
* An upload that dies stays in the store, incomplete, and `complete` never becomes
|
|
54
|
+
* true — so a run left behind polls until its own abandonment bound and then fails,
|
|
55
|
+
* minutes after the page has already reported the error.
|
|
56
|
+
*
|
|
57
|
+
* That used to be the whole story, and it threw away a run and a file together for
|
|
58
|
+
* what is usually one dropped connection near the end. **The resume lives in the
|
|
59
|
+
* SDK now** (`aai/sdk/_upload-resume.ts`): a round that fails for a reason that
|
|
60
|
+
* looks like an outage is re-entered with `resume: true`, sending only the windows
|
|
61
|
+
* the store does not already have, on a budget sized to outlast a redeploy. The run
|
|
62
|
+
* is still waiting on the same id, so a resume that succeeds is invisible to it.
|
|
63
|
+
*
|
|
64
|
+
* This hook used to hand-roll one such retry and no longer does — one resume with
|
|
65
|
+
* no wait in front of it covers a dropped connection and cannot cover the case that
|
|
66
|
+
* actually strands people, which is the agent restarting underneath the upload.
|
|
67
|
+
* Cancelling the run is what happens when the whole budget is spent, and it is the
|
|
68
|
+
* honest end to a submission that did not happen.
|
|
69
|
+
*
|
|
70
|
+
* ## Pausing is the same mechanism, asked for
|
|
71
|
+
*
|
|
72
|
+
* `pauseUpload()` aborts the bytes in flight and holds the uploader;
|
|
73
|
+
* `resumeUpload()` sends what is missing. The store cannot tell that from an
|
|
74
|
+
* outage, because there is nothing to tell apart — see `upload/session.ts`. The
|
|
75
|
+
* RUN is untouched either way: it goes on polling the id it was started with, and
|
|
76
|
+
* `stream.ts`'s idle bound (five minutes of no new bytes) is what decides that a
|
|
77
|
+
* pause has become an abandonment.
|
|
78
|
+
*/
|
|
79
|
+
import type { AnyWorkflowDef, WorkflowOutputOf } from "@alexkroman1/aai/workflow-api";
|
|
80
|
+
import type { UseWorkflowSubmitOptions, WorkflowSubmission } from "./use-workflow-form.ts";
|
|
81
|
+
import type { SubmitInputOf } from "./workflow-def-types.ts";
|
|
82
|
+
/**
|
|
83
|
+
* Options for {@link useWorkflowStream}.
|
|
84
|
+
*
|
|
85
|
+
* {@link UseWorkflowSubmitOptions} without `wait`, which is the synchronous
|
|
86
|
+
* mode: it holds the `POST` open until the run settles, and here the run is
|
|
87
|
+
* started before its bytes are, so there is nothing left to hold it for.
|
|
88
|
+
*
|
|
89
|
+
* Without `recover` either, and REFUSED rather than ignored: an option a hook
|
|
90
|
+
* accepts and does nothing with is the silent-no-op failure this repo keeps
|
|
91
|
+
* paying for. Adopting an earlier run by key would hand this hook a run whose
|
|
92
|
+
* input names an upload id it did not mint and is not filling — so the run
|
|
93
|
+
* would sit waiting for bytes nobody is sending until its own abandonment
|
|
94
|
+
* bound. That is the same reason `upload/recall.ts` deliberately does not
|
|
95
|
+
* recall for this hook, one layer up: here the id is part of a run's INPUT.
|
|
96
|
+
* `key` itself still works, and still makes the run findable — but it is NOT
|
|
97
|
+
* defaulted here the way `useWorkflowSubmit` defaults it, because the whole
|
|
98
|
+
* value of that default is the lookup this hook refuses, and minting a key
|
|
99
|
+
* nothing will ever read back is a slot left in storage for no one.
|
|
100
|
+
*
|
|
101
|
+
* `parallel` COMPOSES with what this hook is for rather than competing with it.
|
|
102
|
+
* The run still starts before the bytes, and the store still publishes how far
|
|
103
|
+
* the file is readable — that number is the CONTIGUOUS prefix, so a run reading
|
|
104
|
+
* ahead of the uplink sees the same growing file whether one connection or four
|
|
105
|
+
* are filling it. What changes is only how fast it grows.
|
|
106
|
+
*/
|
|
107
|
+
export type UseWorkflowStreamOptions = Omit<UseWorkflowSubmitOptions, "wait" | "recover">;
|
|
108
|
+
/**
|
|
109
|
+
* What {@link useWorkflowStream} returns: a {@link WorkflowSubmission}, exactly.
|
|
110
|
+
*
|
|
111
|
+
* The same eight fields `useWorkflowSubmit` returns, of which this hook is a
|
|
112
|
+
* drop-in sibling — same `<Form>`, same `<UploadProgressBar>`, same
|
|
113
|
+
* `<WorkflowProgress>`. An ALIAS rather than a second declaration of the eight:
|
|
114
|
+
* the two have to agree field for field to be drop-in, and two copies of a type
|
|
115
|
+
* that have to agree are two copies that can stop agreeing.
|
|
116
|
+
*
|
|
117
|
+
* Exactly two of the fields mean something different here, and both differences
|
|
118
|
+
* follow from WHEN the run is created — it exists before its bytes do:
|
|
119
|
+
*
|
|
120
|
+
* - `submit()` resolves when the UPLOAD finishes, not when the run is accepted;
|
|
121
|
+
* the run's own progress arrives through `run`. It still resolves rather than
|
|
122
|
+
* rejecting on a failed upload — the failure is reported through `error`, the
|
|
123
|
+
* way a form expects.
|
|
124
|
+
* - `run` is set from the moment the run EXISTS, which here is before the bytes
|
|
125
|
+
* are in. That is what lets a page render `<WorkflowProgress>` beside the
|
|
126
|
+
* upload bar rather than after it.
|
|
127
|
+
*/
|
|
128
|
+
export type WorkflowStreamSubmission<R = unknown, I = unknown> = WorkflowSubmission<R, I>;
|
|
129
|
+
/**
|
|
130
|
+
* Start a workflow run and stream a file into it while it works.
|
|
131
|
+
*
|
|
132
|
+
* The workflow declares which input property carries the upload
|
|
133
|
+
* (`workflow({ uploads: ["recording"] })`) — the same declaration
|
|
134
|
+
* `useWorkflowSubmit` reads, because what the property carries is an upload id
|
|
135
|
+
* either way. What differs is only WHEN the id becomes valid.
|
|
136
|
+
*
|
|
137
|
+
* @typeParam R - The workflow's output type, which is what makes
|
|
138
|
+
* `run.status === "completed"` narrow to a typed `run.output`. Derive it with
|
|
139
|
+
* `WorkflowOutputOf<typeof myWorkflow>`.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx no-check
|
|
143
|
+
* const { submit, run, upload, pending, error } = useWorkflowStream("transcribe");
|
|
144
|
+
*
|
|
145
|
+
* <Form onSubmit={(values) => submit(values)} error={error}>
|
|
146
|
+
* <WorkflowFields workflow="transcribe" />
|
|
147
|
+
* <UploadProgressBar upload={upload} />
|
|
148
|
+
* <SubmitButton pending={pending}>Transcribe</SubmitButton>
|
|
149
|
+
* </Form>
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @public
|
|
153
|
+
*/
|
|
154
|
+
export declare function useWorkflowStream<D extends AnyWorkflowDef>(workflow: string, options?: UseWorkflowStreamOptions): WorkflowStreamSubmission<WorkflowOutputOf<D>, SubmitInputOf<D>>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent's declared workflows — what a form is rendered FROM.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `use-workflow-form.ts` at the 500-line cap, along the seam that
|
|
5
|
+
* file's own doc already drew: it held "the two hooks a FORM needs", and only
|
|
6
|
+
* one of them is about a RUN. This is the other one, and it shares nothing with
|
|
7
|
+
* its former neighbours but the client ref every hook here uses — no state, no
|
|
8
|
+
* run id, no upload.
|
|
9
|
+
*/
|
|
10
|
+
import type { WorkflowSummary } from "@alexkroman1/aai/workflow-api";
|
|
11
|
+
import type { WorkflowApi } from "./workflow-client.ts";
|
|
12
|
+
/** Options for {@link useWorkflows}. */
|
|
13
|
+
export type UseWorkflowsOptions = {
|
|
14
|
+
/** The client to read the listing with. Defaults to one for the page's own agent. */
|
|
15
|
+
api?: WorkflowApi;
|
|
16
|
+
/**
|
|
17
|
+
* Skip the lookup entirely, reporting an empty listing that is not loading.
|
|
18
|
+
*
|
|
19
|
+
* For a caller that may or may not need the listing and cannot decide with a
|
|
20
|
+
* conditional hook — `<WorkflowFields>` handed a summary rather than a name is
|
|
21
|
+
* the one in this package. It reports `loading: false`, because a skipped
|
|
22
|
+
* lookup is finished rather than pending.
|
|
23
|
+
*/
|
|
24
|
+
skip?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/** What {@link useWorkflows} reports. */
|
|
27
|
+
export type UseWorkflowsResult = {
|
|
28
|
+
/** The agent's declared workflows, each with the JSON Schema of its input. */
|
|
29
|
+
workflows: WorkflowSummary[];
|
|
30
|
+
/** True until the listing lands, so a form can hold its fields back. */
|
|
31
|
+
loading: boolean;
|
|
32
|
+
/** The lookup's failure. Set alongside an EMPTY list, which is why it exists. */
|
|
33
|
+
error: string | undefined;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Read the agent's declared workflows.
|
|
37
|
+
*
|
|
38
|
+
* What `<WorkflowFields>` renders a form FROM: each summary carries the JSON
|
|
39
|
+
* Schema of that workflow's input, converted server-side precisely so a browser
|
|
40
|
+
* can read it.
|
|
41
|
+
*
|
|
42
|
+
* The failure is reported rather than swallowed, because the alternative is an
|
|
43
|
+
* empty list — which renders as a form with no fields and reads as "this agent
|
|
44
|
+
* declares no workflows" about an agent that was merely unreachable.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* import { useWorkflows } from "@alexkroman1/aai-ui";
|
|
49
|
+
*
|
|
50
|
+
* // A page rendering its own chrome from the listing — a picker, say. A form
|
|
51
|
+
* // for ONE workflow wants `<WorkflowFields workflow="name" />` instead,
|
|
52
|
+
* // which does this lookup itself.
|
|
53
|
+
* function WorkflowPicker({ onPick }: { onPick: (name: string) => void }) {
|
|
54
|
+
* const { workflows, loading, error } = useWorkflows();
|
|
55
|
+
* if (loading) return <p>Loading…</p>;
|
|
56
|
+
* if (error !== undefined) return <p role="alert">{error}</p>;
|
|
57
|
+
* return (
|
|
58
|
+
* <ul>
|
|
59
|
+
* {workflows.map((summary) => (
|
|
60
|
+
* <li key={summary.name}>
|
|
61
|
+
* <button type="button" onClick={() => onPick(summary.name)}>
|
|
62
|
+
* {summary.description ?? summary.name}
|
|
63
|
+
* </button>
|
|
64
|
+
* </li>
|
|
65
|
+
* ))}
|
|
66
|
+
* </ul>
|
|
67
|
+
* );
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @param options - See {@link UseWorkflowsOptions}.
|
|
72
|
+
* @returns The listing, its loading flag and its failure — see
|
|
73
|
+
* {@link UseWorkflowsResult}.
|
|
74
|
+
*
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
export declare function useWorkflows(options?: UseWorkflowsOptions): UseWorkflowsResult;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser client for the workflow HTTP API (`aai/host/workflow-api.ts`).
|
|
3
|
+
*
|
|
4
|
+
* This is the whole client half of a WORKFLOW APP: an agent whose front door is
|
|
5
|
+
* a form rather than a microphone (`workflowApp()`) starts runs
|
|
6
|
+
* here and watches them for the answer. It deliberately does NOT go through
|
|
7
|
+
* `BrowserSession` — there is no socket, no audio graph, and no session to resume.
|
|
8
|
+
*
|
|
9
|
+
* **The requests themselves are the SDK's now**
|
|
10
|
+
* (`createWorkflowApiClient`, `@alexkroman1/aai/workflow-api`), and what is left
|
|
11
|
+
* here is the one thing that is genuinely a BROWSER's: the default base URL.
|
|
12
|
+
* Every route, every query, the 404-is-an-answer rule and the `wait` clamp were
|
|
13
|
+
* written three times over — here, in the studio's Workflows card, and in
|
|
14
|
+
* `aai workflow` — and the parts the copies disagreed on were exactly the ones a
|
|
15
|
+
* reader cannot check by eye. The SDK module's doc carries that argument; this
|
|
16
|
+
* file must not grow a second implementation of any of it.
|
|
17
|
+
*
|
|
18
|
+
* The one thing worth knowing before using it: **a run outlives the page.**
|
|
19
|
+
* Starting one resolves as soon as the run is created, so `runId` is the only
|
|
20
|
+
* handle that matters and it stays valid across a reload, a different device, or
|
|
21
|
+
* `curl` — which is what makes `useWorkflowRun` a watch rather than a
|
|
22
|
+
* subscription to something the page owns.
|
|
23
|
+
*
|
|
24
|
+
* The loop that keeps asking lives in `use-workflow-run.ts`, and the streaming
|
|
25
|
+
* fast path under it in `workflow-events.ts`.
|
|
26
|
+
*/
|
|
27
|
+
import type { WorkflowRunSnapshot } from "@alexkroman1/aai/workflow-api";
|
|
28
|
+
import { type AgentClient } from "@alexkroman1/aai/workflow-api";
|
|
29
|
+
/**
|
|
30
|
+
* A run's observable state.
|
|
31
|
+
*
|
|
32
|
+
* Aliased from the SDK rather than restated. `import type` is erased entirely,
|
|
33
|
+
* so a second definition of the fields and the five-member status union would
|
|
34
|
+
* buy nothing and cost the one thing that matters — nothing would assert the two
|
|
35
|
+
* agree, so a status added to the SDK would never reach the browser type.
|
|
36
|
+
*
|
|
37
|
+
* `WorkflowRun` keeps the shorter name because it is what a page's own code
|
|
38
|
+
* writes; nothing in a browser needs the word "snapshot" to know a read returns
|
|
39
|
+
* one.
|
|
40
|
+
*
|
|
41
|
+
* It is GENERIC on the run's output, and a page supplies it — see
|
|
42
|
+
* {@link useWorkflowRun}. It does NOT have to restate that type: a page can name
|
|
43
|
+
* its own workflow and derive the rest with `WorkflowOutputOf`, pulling no
|
|
44
|
+
* server graph into the bundle.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export type WorkflowRun<R = unknown> = WorkflowRunSnapshot<R>;
|
|
49
|
+
/**
|
|
50
|
+
* A workflow's own output type, and the shape `GET /workflows` lists — both
|
|
51
|
+
* re-exported so a page needs ONE import to type its runs and render its form.
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* The workflow half of what {@link createWorkflowApi} returns — every hook here
|
|
55
|
+
* takes it, because a run is all a hook needs.
|
|
56
|
+
*
|
|
57
|
+
* Re-exported from the SDK rather than declared here: it IS the SDK's client,
|
|
58
|
+
* and a structural restatement would be a second thing to keep in step with the
|
|
59
|
+
* routes for no gain.
|
|
60
|
+
*/
|
|
61
|
+
export type { AgentClient, WorkflowApi, WorkflowInputOf, WorkflowOutputOf, WorkflowRunStatus, WorkflowSummary, } from "@alexkroman1/aai/workflow-api";
|
|
62
|
+
/**
|
|
63
|
+
* A run status nothing will change again.
|
|
64
|
+
*
|
|
65
|
+
* Re-exported from the SDK rather than defined here. A second implementation
|
|
66
|
+
* listing two of the three terminal statuses would leave a cancelled run polled
|
|
67
|
+
* forever by a page while the agent considered it finished — the kind of drift a
|
|
68
|
+
* status predicate beside the status union cannot have.
|
|
69
|
+
*/
|
|
70
|
+
export { isTerminal } from "@alexkroman1/aai/workflow-api";
|
|
71
|
+
export type WorkflowApiOptions = {
|
|
72
|
+
/**
|
|
73
|
+
* Base URL of the agent. Defaults to the page's own origin + path, which is
|
|
74
|
+
* right for a page the agent itself serves — the only case that exists today,
|
|
75
|
+
* and the reason this wrapper exists at all: the SDK client requires a base
|
|
76
|
+
* URL, because `location` does not exist in that half of the SDK.
|
|
77
|
+
*/
|
|
78
|
+
baseUrl?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Bearer for an agent whose operator set `AAI_WORKFLOW_API_TOKEN`. A page
|
|
81
|
+
* served to the public has nothing to put here (and should not — it would be
|
|
82
|
+
* readable in the bundle); this exists for a programmatic caller written
|
|
83
|
+
* against the same client.
|
|
84
|
+
*/
|
|
85
|
+
token?: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Create a client for the agent serving this page.
|
|
89
|
+
*
|
|
90
|
+
* **You usually do not need one.** Every hook and component here builds this
|
|
91
|
+
* exact client lazily and once when no `api` is passed
|
|
92
|
+
* (`_workflow-api-ref.ts`), so `useWorkflowSubmit("digest")` already talks to
|
|
93
|
+
* the right agent. Reach for this when the client has to be DIFFERENT from that
|
|
94
|
+
* default — another agent's `baseUrl`, or a `token` — or when a page wants
|
|
95
|
+
* `config()` and the run calls on one object.
|
|
96
|
+
*
|
|
97
|
+
* If you do build one, hoist it out of the component that uses it.
|
|
98
|
+
* `useWorkflowRun` holds the client in a ref precisely so a fresh object per
|
|
99
|
+
* render does not restart its watch, but a client built in render is still a new
|
|
100
|
+
* `fetch` closure every time and reads as though it were free.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```tsx
|
|
104
|
+
* import { createWorkflowApi, useWorkflowRun } from "@alexkroman1/aai-ui";
|
|
105
|
+
* import { useState } from "react";
|
|
106
|
+
*
|
|
107
|
+
* // A DIFFERENT agent than the one serving this page, so the client is
|
|
108
|
+
* // explicit — and module scope, not render scope, per above.
|
|
109
|
+
* const api = createWorkflowApi({ baseUrl: "https://agents.example/digest" });
|
|
110
|
+
*
|
|
111
|
+
* function StartDigest() {
|
|
112
|
+
* const [runId, setRunId] = useState<string>();
|
|
113
|
+
* const { run } = useWorkflowRun(runId, { api });
|
|
114
|
+
* return (
|
|
115
|
+
* <button
|
|
116
|
+
* type="button"
|
|
117
|
+
* onClick={() => void api.start("digest", { url: "…" }).then(setRunId)}
|
|
118
|
+
* >
|
|
119
|
+
* {run ? run.status : "Start"}
|
|
120
|
+
* </button>
|
|
121
|
+
* );
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @param options - See {@link WorkflowApiOptions}. Both fields are optional; the
|
|
126
|
+
* default base URL is the page's own origin and path.
|
|
127
|
+
* @returns Every workflow call plus `config()` and `baseUrl` — see
|
|
128
|
+
* {@link AgentClient}. It was the narrow {@link WorkflowApi}, which left a page
|
|
129
|
+
* that also wanted the agent's own name building a SECOND client (or a bare
|
|
130
|
+
* `fetch` and a hand-written URL join) for one read; the SDK documents
|
|
131
|
+
* `createAgentClient` as a superset of the same routes, so delegating to it
|
|
132
|
+
* widens the return without a second implementation of anything.
|
|
133
|
+
*
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
136
|
+
export declare function createWorkflowApi(options?: WorkflowApiOptions): AgentClient;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The narrowing the workflow hooks apply to a DEF's input type.
|
|
3
|
+
*
|
|
4
|
+
* It used to fall back to `unknown` so an un-parameterized
|
|
5
|
+
* `useWorkflowSubmit("digest")` kept compiling. It does not any more — the def
|
|
6
|
+
* is required — because the fallback was the last way to get an untyped
|
|
7
|
+
* `submit` back, and an escape hatch nobody needs is one somebody uses.
|
|
8
|
+
*
|
|
9
|
+
* There used to be a `SubmitOutputOf<D>` beside it. It was `WorkflowOutputOf<D>`
|
|
10
|
+
* spelled a second way and nothing else, so the hooks name `WorkflowOutputOf`
|
|
11
|
+
* directly — the SDK type this package already re-exports, and the one a page
|
|
12
|
+
* reading the rendered signature can click through to.
|
|
13
|
+
*/
|
|
14
|
+
import type { WorkflowInputOf } from "@alexkroman1/aai/workflow-api";
|
|
15
|
+
/**
|
|
16
|
+
* What `submit()` takes for `D` — `undefined` when `D` declares no schema.
|
|
17
|
+
*
|
|
18
|
+
* This is what {@link WorkflowInputOf} cannot say on its own: a def whose
|
|
19
|
+
* `input` schema is absent has no parsed input, and `never` as a parameter type
|
|
20
|
+
* accepts nothing at all — not even `undefined` — so a workflow that declares
|
|
21
|
+
* no schema would have an uncallable `submit`. It gets `undefined` instead, i.e.
|
|
22
|
+
* `submit(undefined)`. Explicit rather than `void`, which would let the argument
|
|
23
|
+
* be omitted and which Biome's `noConfusingVoidType` rejects outside a return or
|
|
24
|
+
* type-parameter position.
|
|
25
|
+
*
|
|
26
|
+
* The `[T] extends [never]` spelling is deliberate: a bare `T extends never`
|
|
27
|
+
* distributes over a naked type parameter and answers `never` for a union.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
export type SubmitInputOf<D> = [WorkflowInputOf<D>] extends [never] ? undefined : WorkflowInputOf<D>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watching a run over server-sent events — the PUSH half of `useWorkflowRun`.
|
|
3
|
+
*
|
|
4
|
+
* Its own module because the seam is clean: everything in `workflow-client.ts`
|
|
5
|
+
* is request/response shaping plus the poll, and this is one long-lived stream
|
|
6
|
+
* and the SSE parser it needs.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
import type { WorkflowApi, WorkflowRun } from "./workflow-client.ts";
|
|
11
|
+
/**
|
|
12
|
+
* The slice of the client this needs: one method.
|
|
13
|
+
*
|
|
14
|
+
* Narrowed rather than taking the whole `WorkflowApi`, and the narrowing is the
|
|
15
|
+
* honest statement — nothing here reads a run, starts one, or cancels one, it
|
|
16
|
+
* opens ONE stream. It also makes a test double a plain object rather than a
|
|
17
|
+
* six-method stub cast into shape. A real client satisfies it structurally.
|
|
18
|
+
*/
|
|
19
|
+
export type RunWatcher = Pick<WorkflowApi, "watch">;
|
|
20
|
+
/**
|
|
21
|
+
* Watch a run over SSE, falling back to the caller's poll on any failure.
|
|
22
|
+
*
|
|
23
|
+
* The poll stays the fallback rather than being replaced, and that is the whole
|
|
24
|
+
* shape of this: a stream is an optimisation over a mechanism that already
|
|
25
|
+
* works, so every way it can fail — an older agent with no `/events` route, a
|
|
26
|
+
* proxy that buffers, a network that drops it — has to degrade to the thing that
|
|
27
|
+
* does. What it buys is real, though: on the platform every polled read BROKERS,
|
|
28
|
+
* so N open tabs at `DEFAULT_WORKFLOW_POLL_MS` is N/2 brokered requests a
|
|
29
|
+
* second, each able to boot a sandbox. One stream per tab replaces all of it.
|
|
30
|
+
*
|
|
31
|
+
* `EventSource` is not used, for two reasons that both matter here: it cannot
|
|
32
|
+
* send an `Authorization` header (an agent with `AAI_WORKFLOW_API_TOKEN` set
|
|
33
|
+
* would be unreachable), and it reconnects on its own schedule, which would
|
|
34
|
+
* fight the caller's. A `fetch` stream gives both back.
|
|
35
|
+
*
|
|
36
|
+
* Returns a stop function. `onFallback` is called at most once, when this stream
|
|
37
|
+
* cannot be relied on and the poll should take over.
|
|
38
|
+
*/
|
|
39
|
+
export declare function watchRunEvents<R>(getClient: () => RunWatcher, runId: string, onRun: (run: WorkflowRun<R>) => void, onSettled: () => void, onFallback: () => void): () => void;
|