@alexkroman1/aai-ui 16.0.1 → 17.0.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 +3 -2
- package/dist/_page-shell.d.ts +11 -0
- package/dist/{chat-view-DsylXJsw.js → chat-view-P12pHKQU.js} +1 -1
- package/dist/{client-config-DJQHnYjm.js → client-config-CFhkMkmv.js} +15 -13
- package/dist/client-config.d.ts +14 -13
- package/dist/components/audio-result.d.ts +2 -2
- package/dist/components/chat-view.js +1 -1
- package/dist/components/controls.js +1 -1
- package/dist/components/facts.d.ts +1 -1
- package/dist/components/message-list.js +1 -1
- package/dist/components/tool-call-block.js +1 -1
- package/dist/components/workflow-run-panel.d.ts +1 -1
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/default-client/assets/index-18HWH46b.css +2 -0
- package/dist/default-client/assets/index-gJbwWrWS.js +121 -0
- package/dist/default-client/index.html +2 -2
- package/dist/default-client.d.ts +21 -0
- package/dist/define-client.js +4 -4
- package/dist/hooks.d.ts +20 -10
- package/dist/hooks.js +1 -1
- package/dist/index.d.ts +15 -12
- package/dist/index.js +578 -437
- package/dist/internal.js +3 -3
- package/dist/{message-list-BLyPRS1Q.js → message-list-CJX560b5.js} +1 -1
- package/dist/page.d.ts +47 -21
- package/dist/{session-core-gwePM95B.js → session-core-DHE1bQ1J.js} +388 -172
- package/dist/session-core-audio-effects.d.ts +26 -0
- package/dist/session-core-audio-setup.d.ts +38 -31
- package/dist/session-core-audio-state.d.ts +152 -0
- package/dist/session-core-messages.d.ts +9 -10
- package/dist/session-core-types.d.ts +13 -21
- package/dist/session-core.js +1 -1
- package/dist/use-run-key.d.ts +1 -1
- package/dist/use-workflow-form.d.ts +1 -1
- package/dist/{use-workflow-run-CNahvlgL.js → use-workflow-run-ZqJWJn2-.js} +24 -11
- package/dist/workflow-client.d.ts +26 -12
- package/package.json +2 -2
- package/styles.css +2 -2
- package/dist/default-client/assets/index-CMh4E-Ci.css +0 -2
- package/dist/default-client/assets/index-eOmPq2sS.js +0 -284
- package/dist/{tool-call-block-C2t_5fpp.js → tool-call-block-4gXoL_eP.js} +1 -1
- package/dist/{url-chips-D62RTz7v.js → url-chips-0n35fPJY.js} +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session core's side of the audio path: every socket frame and snapshot
|
|
3
|
+
* write the statechart decides on but cannot make itself.
|
|
4
|
+
*
|
|
5
|
+
* The split is the one `s2s-lifecycle.ts` draws in the runtime. WHEN a
|
|
6
|
+
* microphone opens, whether an opening is still wanted, and where audio goes
|
|
7
|
+
* before there is anywhere to play it are `session-core-audio-state.ts`'s;
|
|
8
|
+
* this module is HOW — and it is the only half that touches `conn`, the
|
|
9
|
+
* snapshot, or the wire, which is what lets the machine be specced without a
|
|
10
|
+
* browser.
|
|
11
|
+
*/
|
|
12
|
+
import type { SessionCommand } from "@alexkroman1/aai/protocol";
|
|
13
|
+
import type { AudioPathEffects } from "./session-core-audio-state.ts";
|
|
14
|
+
import type { SessionStateMachine } from "./session-core-state.ts";
|
|
15
|
+
import { type ConnState, type SessionSnapshot } from "./session-core-types.ts";
|
|
16
|
+
/** What the audio effects need from the owning session core. */
|
|
17
|
+
export type AudioEffectsDeps = {
|
|
18
|
+
conn: ConnState;
|
|
19
|
+
updateState: (partial: Partial<SessionSnapshot>) => void;
|
|
20
|
+
/** The session's state and error, as one fact — see `session-core-state.ts`. */
|
|
21
|
+
agentState: SessionStateMachine;
|
|
22
|
+
sendJson: (msg: SessionCommand) => void;
|
|
23
|
+
sendAudio: (bytes: ArrayBuffer) => void;
|
|
24
|
+
};
|
|
25
|
+
/** Build the effects for one session's audio path. */
|
|
26
|
+
export declare function createAudioEffects(deps: AudioEffectsDeps): AudioPathEffects;
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* How the audio path is OPENED: the module load, the mic grant, the worklet
|
|
3
|
+
* registration, and the {@link VoiceIO} those three produce.
|
|
4
|
+
*
|
|
5
|
+
* WHEN one is opened, whether it is still wanted by the time it settles, and
|
|
6
|
+
* what happens to audio that arrives meanwhile all live in
|
|
7
|
+
* `session-core-audio-state.ts` — the same split `s2s-lifecycle.ts` and
|
|
8
|
+
* `pipeline-speech-edges.ts` draw in the runtime, and for the same reason:
|
|
9
|
+
* nothing here reads or writes session state, so it is callable from an
|
|
10
|
+
* invoked actor and testable without one.
|
|
11
|
+
*/
|
|
2
12
|
import type { VoiceIO } from "./audio.ts";
|
|
3
|
-
import type { SessionStateMachine } from "./session-core-state.ts";
|
|
4
|
-
import { type ConnState, type SessionSnapshot } from "./session-core-types.ts";
|
|
5
|
-
/** Dependencies `initAudioCapture` needs from the owning session core. */
|
|
6
|
-
export type AudioSetupDeps = {
|
|
7
|
-
sendJson: (msg: SessionCommand) => void;
|
|
8
|
-
sendAudio: (bytes: ArrayBuffer) => void;
|
|
9
|
-
updateState: (partial: Partial<SessionSnapshot>) => void;
|
|
10
|
-
/** The session's state and error, as one fact — see `session-core-state.ts`. */
|
|
11
|
-
agentState: SessionStateMachine;
|
|
12
|
-
/** Turn-boundary-guarded drain from the message handlers — replays a
|
|
13
|
-
* buffered `audio_done` without stomping a barge-in's state. */
|
|
14
|
-
settleWhenAudioDrained: (io: VoiceIO) => void;
|
|
15
|
-
/** Release the mic/VoiceIO (used when the audio path dies non-fatally). */
|
|
16
|
-
cleanupAudio: () => void;
|
|
17
|
-
};
|
|
18
13
|
type AudioModules = [typeof import("./audio.ts"), string, string];
|
|
19
14
|
/**
|
|
20
15
|
* Load the audio module and worklet sources, memoized so a prefetch at
|
|
@@ -23,23 +18,35 @@ type AudioModules = [typeof import("./audio.ts"), string, string];
|
|
|
23
18
|
* load clears the memo so the next attempt retries the import.
|
|
24
19
|
*/
|
|
25
20
|
export declare function loadAudioModules(): Promise<AudioModules>;
|
|
21
|
+
/** The audio parameters the server's `config` frame carries. */
|
|
22
|
+
export type AudioPathConfig = {
|
|
23
|
+
sampleRate: number;
|
|
24
|
+
ttsSampleRate: number;
|
|
25
|
+
};
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* What a live audio path reports back.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
* Both of the last two used to be guarded at the callback by
|
|
30
|
+
* `conn.generation.isCurrent(gen)`; they are plain reports now, and whether
|
|
31
|
+
* one still matters is the state machine's answer — see
|
|
32
|
+
* `session-core-audio-state.ts`.
|
|
33
|
+
*/
|
|
34
|
+
export type AudioPathCallbacks = {
|
|
35
|
+
/** Buffered PCM16 from the microphone, for the socket. */
|
|
36
|
+
onMicData(pcm16: ArrayBuffer): void;
|
|
37
|
+
/** Unplayed agent audio in the buffer, in ms — the host's closed playback loop. */
|
|
38
|
+
onProgress(bufferedMs: number): void;
|
|
39
|
+
/** A worklet processor died after setup: the audio path is gone. */
|
|
40
|
+
onFailure(message: string): void;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Open one audio path: load the worklets, ask for the microphone, and build
|
|
44
|
+
* the {@link VoiceIO} wired to `callbacks`.
|
|
37
45
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
46
|
+
* Rejects when any of the three refuses — a denied mic prompt being the
|
|
47
|
+
* ordinary case. The caller owns closing what this resolves: a path that comes
|
|
48
|
+
* up for a connection nobody wants any more is a live microphone, and no
|
|
49
|
+
* amount of cancellation upstream releases the device.
|
|
40
50
|
*/
|
|
41
|
-
export declare function
|
|
42
|
-
sampleRate: number;
|
|
43
|
-
ttsSampleRate: number;
|
|
44
|
-
}, deps: AudioSetupDeps): Promise<void>;
|
|
51
|
+
export declare function openAudioPath(config: AudioPathConfig, callbacks: AudioPathCallbacks): Promise<VoiceIO>;
|
|
45
52
|
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser session's AUDIO PATH — the mic grant, the worklets, and the
|
|
3
|
+
* {@link VoiceIO} they produce — as a statechart.
|
|
4
|
+
*
|
|
5
|
+
* It was a latch, an epoch counter and three buffers spread across
|
|
6
|
+
* `ConnState`, and every one of them was a question the position already
|
|
7
|
+
* answers:
|
|
8
|
+
*
|
|
9
|
+
* - **`audioSetupInFlight` was a dedup, and dedup is a state.**
|
|
10
|
+
* `initAudioCapture` opened with `if (conn.audioSetupInFlight) return` and
|
|
11
|
+
* closed with "only the init that still owns the flag may clear it", which is
|
|
12
|
+
* an ownership problem a boolean cannot express and a position does not have.
|
|
13
|
+
* Here `starting` simply does not handle `START`, and there is no flag to
|
|
14
|
+
* hand back.
|
|
15
|
+
* - **`conn.generation` existed for this module and nothing else.** Four
|
|
16
|
+
* `bump()` sites, four `isCurrent(gen)` reads, and its own field on
|
|
17
|
+
* `ConnState`. All four bumps sat immediately after a `cleanupAudio()` that
|
|
18
|
+
* had *just* cleared the in-flight flag — the reconnect close handler said so
|
|
19
|
+
* outright ("cleanupAudio just cleared the in-flight flag, so a survivor
|
|
20
|
+
* would otherwise pass the same-generation guard and double-run"). Two
|
|
21
|
+
* mechanisms for one question, the second one layered on because the first
|
|
22
|
+
* could not stop the work already running. A stopped `invoke` can, so the
|
|
23
|
+
* counter is gone.
|
|
24
|
+
* - **A counter cannot tell two bring-ups in ONE generation apart, and they
|
|
25
|
+
* happen.** A repeated `config` frame on a live connection re-opened the path
|
|
26
|
+
* without bumping anything, so the outgoing `VoiceIO`'s `onError` still
|
|
27
|
+
* passed `isCurrent(gen)` and could tear down the replacement that had just
|
|
28
|
+
* taken its slot. `PROGRESS` and `IO_FAILED` carry the instance that fired
|
|
29
|
+
* them now, and identity is exact where a counter was approximate.
|
|
30
|
+
* - **`preInitAudio` / `preInitDone` are only meaningful while the path is
|
|
31
|
+
* down.** As connection fields they were readable — and writable — from
|
|
32
|
+
* anywhere; as context of `down`/`starting` they are cleared by entering
|
|
33
|
+
* `down` rather than by three call sites remembering to.
|
|
34
|
+
*
|
|
35
|
+
* ## What cancellation does NOT do, and the one guard that had to survive
|
|
36
|
+
*
|
|
37
|
+
* Stopping the `bringUp` actor hides its resolution; it does not close a
|
|
38
|
+
* microphone the browser has already granted. So `bringUp` checks its own
|
|
39
|
+
* `signal` after `openAudioPath` settles and releases what it built. That is
|
|
40
|
+
* the single remaining "is this still wanted?" test, and it lives in the
|
|
41
|
+
* producer — which owns the value nobody adopted — rather than at the four
|
|
42
|
+
* consumer sites that used to re-ask.
|
|
43
|
+
*
|
|
44
|
+
* ## Effects stay in the session core
|
|
45
|
+
*
|
|
46
|
+
* The machine owns WHEN. Every HOW — the socket frame, the snapshot write, the
|
|
47
|
+
* turn-boundary drain — arrives as an injected {@link AudioPathEffects} call,
|
|
48
|
+
* the same shape `s2s-lifecycle.ts` uses. So this module touches no socket and
|
|
49
|
+
* no snapshot.
|
|
50
|
+
*/
|
|
51
|
+
import type { VoiceIO } from "./audio.ts";
|
|
52
|
+
import type { AudioPathCallbacks, AudioPathConfig } from "./session-core-audio-setup.ts";
|
|
53
|
+
/** Where the audio path is. */
|
|
54
|
+
export type AudioPathPhase = "down" | "starting" | "up";
|
|
55
|
+
/**
|
|
56
|
+
* The session core's side of the machine: everything the audio path decides to
|
|
57
|
+
* do but does not know how to.
|
|
58
|
+
*/
|
|
59
|
+
export type AudioPathEffects = {
|
|
60
|
+
/**
|
|
61
|
+
* Open one audio path — `openAudioPath` in `session-core-audio-setup.ts`.
|
|
62
|
+
* Injected rather than imported so this module needs no browser: the specs
|
|
63
|
+
* hand it a promise they resolve by hand, which is the only way to hold a
|
|
64
|
+
* bring-up open across the events that abandon it.
|
|
65
|
+
*/
|
|
66
|
+
open(config: AudioPathConfig, callbacks: AudioPathCallbacks): Promise<VoiceIO>;
|
|
67
|
+
/** Relay captured microphone audio to the server. */
|
|
68
|
+
sendMicAudio(pcm16: ArrayBuffer): void;
|
|
69
|
+
/** Tell the server how much agent audio is still unplayed. */
|
|
70
|
+
reportProgress(bufferedMs: number): void;
|
|
71
|
+
/** Tell the server the microphone is live. */
|
|
72
|
+
announceReady(): void;
|
|
73
|
+
/** The microphone is live: the snapshot's `recording`. */
|
|
74
|
+
micLive(): void;
|
|
75
|
+
/** Back to the mic, with no audio left to wait for. */
|
|
76
|
+
listen(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Wait for `io` to drain, then listen — guarded by the turn boundary, so a
|
|
79
|
+
* completion landing after a barge-in or a teardown is discarded.
|
|
80
|
+
*/
|
|
81
|
+
settleWhenDrained(io: VoiceIO): void;
|
|
82
|
+
/**
|
|
83
|
+
* Report a non-fatal audio failure and stop the mic. Non-fatal because the
|
|
84
|
+
* socket may well still be fine, so a later server frame is allowed to
|
|
85
|
+
* recover the banner — see `session-core-state.ts`.
|
|
86
|
+
*/
|
|
87
|
+
reportFailure(message: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* The audio path is gone: end whatever turn it was playing.
|
|
90
|
+
*
|
|
91
|
+
* Paired with the release below rather than folded into it, because the
|
|
92
|
+
* bump has to happen on every path into `down` — including a bring-up that
|
|
93
|
+
* failed before there was anything to close.
|
|
94
|
+
*/
|
|
95
|
+
endTurn(): void;
|
|
96
|
+
/** Close `io` and let go of the microphone. Best-effort. */
|
|
97
|
+
release(io: VoiceIO): void;
|
|
98
|
+
};
|
|
99
|
+
/** Everything that happens to the audio path. */
|
|
100
|
+
export type AudioPathEvent =
|
|
101
|
+
/** The server's `config` frame: bring the path up for these rates. */
|
|
102
|
+
{
|
|
103
|
+
type: "START";
|
|
104
|
+
config: AudioPathConfig;
|
|
105
|
+
}
|
|
106
|
+
/** A PCM16 chunk arrived for playback. */
|
|
107
|
+
| {
|
|
108
|
+
type: "CHUNK";
|
|
109
|
+
chunk: ArrayBuffer;
|
|
110
|
+
}
|
|
111
|
+
/** The server finished sending this turn's audio. */
|
|
112
|
+
| {
|
|
113
|
+
type: "DONE";
|
|
114
|
+
}
|
|
115
|
+
/** A live path reported its unplayed depth. */
|
|
116
|
+
| {
|
|
117
|
+
type: "PROGRESS";
|
|
118
|
+
io: VoiceIO;
|
|
119
|
+
bufferedMs: number;
|
|
120
|
+
}
|
|
121
|
+
/** A live path's worklet died. */
|
|
122
|
+
| {
|
|
123
|
+
type: "IO_FAILED";
|
|
124
|
+
io: VoiceIO;
|
|
125
|
+
message: string;
|
|
126
|
+
}
|
|
127
|
+
/** Release the audio path: hang-up, reconnect, fatal frame, session end. */
|
|
128
|
+
| {
|
|
129
|
+
type: "TEARDOWN";
|
|
130
|
+
};
|
|
131
|
+
/** The session core's handle on its audio path. */
|
|
132
|
+
export type AudioPath = {
|
|
133
|
+
/** Where the path is. */
|
|
134
|
+
phase(): AudioPathPhase;
|
|
135
|
+
/** The server named the audio rates: bring the path up. */
|
|
136
|
+
start(config: AudioPathConfig): void;
|
|
137
|
+
/** Play a chunk, or hold it until there is somewhere to play it. */
|
|
138
|
+
enqueue(chunk: ArrayBuffer): void;
|
|
139
|
+
/** The server finished this turn's audio. */
|
|
140
|
+
done(): void;
|
|
141
|
+
/**
|
|
142
|
+
* Barge-in: discard what is queued for playback.
|
|
143
|
+
*
|
|
144
|
+
* A read rather than an event — it moves the path nowhere, and a turn
|
|
145
|
+
* boundary is the caller's to declare (see `bargeIn`).
|
|
146
|
+
*/
|
|
147
|
+
flush(): void;
|
|
148
|
+
/** Release the microphone and forget anything buffered. */
|
|
149
|
+
teardown(): void;
|
|
150
|
+
};
|
|
151
|
+
/** Create the audio path for one browser session. */
|
|
152
|
+
export declare function createAudioPath(effects: AudioPathEffects): AudioPath;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AudioPath } from "./session-core-audio-state.ts";
|
|
1
2
|
import type { SessionStateMachine } from "./session-core-state.ts";
|
|
2
3
|
import { type ConnState, type SessionSnapshot } from "./session-core-types.ts";
|
|
3
4
|
/**
|
|
@@ -28,8 +29,14 @@ type MessageHandlerDeps = {
|
|
|
28
29
|
conn: ConnState;
|
|
29
30
|
/** The session's state and error, as one fact — see `session-core-state.ts`. */
|
|
30
31
|
agentState: SessionStateMachine;
|
|
31
|
-
/**
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* The microphone and the playback queue — see
|
|
34
|
+
* `session-core-audio-state.ts`. Frames say what happened (`enqueue`,
|
|
35
|
+
* `done`, `teardown`); whether the path is up, and what to do with audio
|
|
36
|
+
* that arrives before it is, are the machine's answers rather than this
|
|
37
|
+
* module's.
|
|
38
|
+
*/
|
|
39
|
+
audio: AudioPath;
|
|
33
40
|
};
|
|
34
41
|
type MessageHandlers = {
|
|
35
42
|
/**
|
|
@@ -42,14 +49,6 @@ type MessageHandlers = {
|
|
|
42
49
|
* otherwise `undefined`.
|
|
43
50
|
*/
|
|
44
51
|
handleMessage(data: unknown): SessionConfigMessage | undefined;
|
|
45
|
-
/**
|
|
46
|
-
* Wait for `io`'s playback queue to drain, then transition to `"listening"`
|
|
47
|
-
* — guarded by the same turn-boundary generation the live `audio.completed`
|
|
48
|
-
* path uses. `initAudioCapture` routes the pre-init greeting replay
|
|
49
|
-
* through this so a barge-in mid-greeting can't be stomped by the
|
|
50
|
-
* replayed completion resolving late.
|
|
51
|
-
*/
|
|
52
|
-
settleWhenAudioDrained(io: NonNullable<ConnState["voiceIO"]>): void;
|
|
53
52
|
};
|
|
54
53
|
/**
|
|
55
54
|
* Create the server→client message handlers for one session core.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Split out of `session-core.ts` to keep that module focused on behaviour.
|
|
5
5
|
*/
|
|
6
6
|
import type { Epoch } from "@alexkroman1/aai/internal";
|
|
7
|
-
import type {
|
|
7
|
+
import type { AudioPath } from "./session-core-audio-state.ts";
|
|
8
8
|
import type { AgentState, ChatMessage, SessionError, ToolCallInfo, WebSocketConstructor } from "./types.ts";
|
|
9
9
|
/**
|
|
10
10
|
* A custom event emitted by the agent via `ctx.send(event, data)` — the
|
|
@@ -223,11 +223,18 @@ export type BrowserSession = {
|
|
|
223
223
|
[Symbol.dispose](): void;
|
|
224
224
|
};
|
|
225
225
|
/**
|
|
226
|
-
* Shared mutable connection state
|
|
226
|
+
* Shared mutable connection state.
|
|
227
227
|
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
228
|
+
* **It used to carry the audio path too**, and the four fields that went are
|
|
229
|
+
* worth naming because the shape recurs: a `voiceIO` slot, an
|
|
230
|
+
* `audioSetupInFlight` latch, a `generation` epoch whose only readers were the
|
|
231
|
+
* bring-up's four staleness checks, and the two pre-init buffers. Each was a
|
|
232
|
+
* question about a position the connection did not have, readable and writable
|
|
233
|
+
* from anywhere. They are the audio path's own statechart now — see
|
|
234
|
+
* `session-core-audio-state.ts`.
|
|
235
|
+
*
|
|
236
|
+
* What is left is what the CONNECTION really owns: the socket, the server's
|
|
237
|
+
* idle retirement, and the turn boundary.
|
|
231
238
|
*/
|
|
232
239
|
export type ConnState = {
|
|
233
240
|
ws: InstanceType<WebSocketConstructor> | null;
|
|
@@ -242,12 +249,6 @@ export type ConnState = {
|
|
|
242
249
|
* reconnect on demand).
|
|
243
250
|
*/
|
|
244
251
|
retiredByServer: boolean;
|
|
245
|
-
voiceIO: VoiceIO | null;
|
|
246
|
-
audioSetupInFlight: boolean;
|
|
247
|
-
/** Connection epoch, bumped on each connect()/retry (see `createEpoch`).
|
|
248
|
-
* Prevents a stale initAudioCapture from assigning its voiceIO to a newer
|
|
249
|
-
* connection. */
|
|
250
|
-
generation: Epoch;
|
|
251
252
|
/**
|
|
252
253
|
* Turn epoch, bumped at every turn boundary — a committed user turn, a
|
|
253
254
|
* barge-in, a reset, AND every audio-path teardown (`cleanupAudio`).
|
|
@@ -261,15 +262,6 @@ export type ConnState = {
|
|
|
261
262
|
* later — a dead session reporting a live mic.
|
|
262
263
|
*/
|
|
263
264
|
turn: Epoch;
|
|
264
|
-
/** Audio chunks that arrived before `voiceIO` was initialized — drained into
|
|
265
|
-
* the playback worklet once init completes. Closes the race between the
|
|
266
|
-
* server starting greeting audio (immediately on S2S connect) and the
|
|
267
|
-
* client awaiting mic permission + worklet registration. */
|
|
268
|
-
preInitAudio: ArrayBuffer[];
|
|
269
|
-
/** True if `audio_done` arrived before `voiceIO` was initialized. The done
|
|
270
|
-
* signal must be replayed after draining preInitAudio, or a short greeting
|
|
271
|
-
* buffered during mic-permission never finishes playing. */
|
|
272
|
-
preInitDone: boolean;
|
|
273
265
|
};
|
|
274
266
|
/**
|
|
275
267
|
* The two liveness fields at rest.
|
|
@@ -303,4 +295,4 @@ export declare const STOPPED: {
|
|
|
303
295
|
* bump on its own now reads as a deliberate choice rather than a forgotten
|
|
304
296
|
* flush.
|
|
305
297
|
*/
|
|
306
|
-
export declare function bargeIn(conn: ConnState): void;
|
|
298
|
+
export declare function bargeIn(conn: ConnState, audio: AudioPath): void;
|
package/dist/session-core.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createBrowserSession } from "./session-core-
|
|
1
|
+
import { t as createBrowserSession } from "./session-core-DHE1bQ1J.js";
|
|
2
2
|
export { createBrowserSession };
|
package/dist/use-run-key.d.ts
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
* coming back on Friday to press Stop is the ordinary case rather than an edge
|
|
52
52
|
* one, and a tab-scoped key would answer that with an empty form beside a run
|
|
53
53
|
* still posting somewhere. It is as far as a key can go without a login, and no
|
|
54
|
-
* further. `podcast-digest` is that template, and the reason this hook is still
|
|
54
|
+
* further. `podcast-digest-workflow` is that template, and the reason this hook is still
|
|
55
55
|
* called by name anywhere; the other five take the tab-scoped default the
|
|
56
56
|
* submit hook mints for them.
|
|
57
57
|
*
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* A page that submits a workflow always needs the same four pieces of state:
|
|
12
12
|
* the run id, whether a submit is in flight, whether the RUN is still going, and
|
|
13
|
-
* whichever of the two failed. `link-digest` writes them out by hand, which is
|
|
13
|
+
* whichever of the two failed. `link-digest-workflow` writes them out by hand, which is
|
|
14
14
|
* the right shape for a template teaching the primitives and the wrong shape to
|
|
15
15
|
* write a third time — and it is easy to get subtly wrong: dropping the previous
|
|
16
16
|
* run id before the new `POST` returns is what stops a finished result sitting
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as pageBaseUrl } from "./_utils-DnQDM9Uy.js";
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
2
|
import { errorMessage } from "@alexkroman1/aai";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { omitUndefined } from "@alexkroman1/aai/utils";
|
|
5
|
-
import {
|
|
5
|
+
import { createAgentClient, isTerminal, isTerminal as isTerminal$1, readEventStream } from "@alexkroman1/aai/workflow-api";
|
|
6
6
|
//#region src/workflow-client.ts
|
|
7
7
|
/**
|
|
8
8
|
* Browser client for the workflow HTTP API (`aai/host/workflow-api.ts`).
|
|
@@ -31,20 +31,28 @@ import { createWorkflowApiClient, isTerminal, isTerminal as isTerminal$1, readEv
|
|
|
31
31
|
* fast path under it in `workflow-events.ts`.
|
|
32
32
|
*/
|
|
33
33
|
/**
|
|
34
|
-
* Create a
|
|
34
|
+
* Create a client for the agent serving this page.
|
|
35
|
+
*
|
|
36
|
+
* **You usually do not need one.** Every hook and component here builds this
|
|
37
|
+
* exact client lazily and once when no `api` is passed
|
|
38
|
+
* (`_workflow-api-ref.ts`), so `useWorkflowSubmit("digest")` already talks to
|
|
39
|
+
* the right agent. Reach for this when the client has to be DIFFERENT from that
|
|
40
|
+
* default — another agent's `baseUrl`, or a `token` — or when a page wants
|
|
41
|
+
* `config()` and the run calls on one object.
|
|
35
42
|
*
|
|
36
|
-
*
|
|
37
|
-
* in a ref precisely so a fresh object per
|
|
38
|
-
* but a client built in render is still a new
|
|
39
|
-
* reads as though it were free.
|
|
43
|
+
* If you do build one, hoist it out of the component that uses it.
|
|
44
|
+
* `useWorkflowRun` holds the client in a ref precisely so a fresh object per
|
|
45
|
+
* render does not restart its watch, but a client built in render is still a new
|
|
46
|
+
* `fetch` closure every time and reads as though it were free.
|
|
40
47
|
*
|
|
41
48
|
* @example
|
|
42
49
|
* ```tsx
|
|
43
50
|
* import { createWorkflowApi, useWorkflowRun } from "@alexkroman1/aai-ui";
|
|
44
51
|
* import { useState } from "react";
|
|
45
52
|
*
|
|
46
|
-
* //
|
|
47
|
-
*
|
|
53
|
+
* // A DIFFERENT agent than the one serving this page, so the client is
|
|
54
|
+
* // explicit — and module scope, not render scope, per above.
|
|
55
|
+
* const api = createWorkflowApi({ baseUrl: "https://agents.example/digest" });
|
|
48
56
|
*
|
|
49
57
|
* function StartDigest() {
|
|
50
58
|
* const [runId, setRunId] = useState<string>();
|
|
@@ -62,12 +70,17 @@ import { createWorkflowApiClient, isTerminal, isTerminal as isTerminal$1, readEv
|
|
|
62
70
|
*
|
|
63
71
|
* @param options - See {@link WorkflowApiOptions}. Both fields are optional; the
|
|
64
72
|
* default base URL is the page's own origin and path.
|
|
65
|
-
* @returns
|
|
73
|
+
* @returns Every workflow call plus `config()` and `baseUrl` — see
|
|
74
|
+
* {@link AgentClient}. It was the narrow {@link WorkflowApi}, which left a page
|
|
75
|
+
* that also wanted the agent's own name building a SECOND client (or a bare
|
|
76
|
+
* `fetch` and a hand-written URL join) for one read; the SDK documents
|
|
77
|
+
* `createAgentClient` as a superset of the same routes, so delegating to it
|
|
78
|
+
* widens the return without a second implementation of anything.
|
|
66
79
|
*
|
|
67
80
|
* @public
|
|
68
81
|
*/
|
|
69
82
|
function createWorkflowApi(options = {}) {
|
|
70
|
-
return
|
|
83
|
+
return createAgentClient({
|
|
71
84
|
baseUrl: options.baseUrl ?? pageBaseUrl(),
|
|
72
85
|
...omitUndefined({ token: options.token })
|
|
73
86
|
});
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* fast path under it in `workflow-events.ts`.
|
|
26
26
|
*/
|
|
27
27
|
import type { WorkflowRunSnapshot } from "@alexkroman1/aai/workflow-api";
|
|
28
|
-
import { type
|
|
28
|
+
import { type AgentClient } from "@alexkroman1/aai/workflow-api";
|
|
29
29
|
/**
|
|
30
30
|
* A run's observable state.
|
|
31
31
|
*
|
|
@@ -51,13 +51,14 @@ export type WorkflowRun<R = unknown> = WorkflowRunSnapshot<R>;
|
|
|
51
51
|
* re-exported so a page needs ONE import to type its runs and render its form.
|
|
52
52
|
*/
|
|
53
53
|
/**
|
|
54
|
-
* The
|
|
54
|
+
* The workflow half of what {@link createWorkflowApi} returns — every hook here
|
|
55
|
+
* takes it, because a run is all a hook needs.
|
|
55
56
|
*
|
|
56
57
|
* Re-exported from the SDK rather than declared here: it IS the SDK's client,
|
|
57
58
|
* and a structural restatement would be a second thing to keep in step with the
|
|
58
59
|
* routes for no gain.
|
|
59
60
|
*/
|
|
60
|
-
export type { WorkflowApi, WorkflowInputOf, WorkflowOutputOf, WorkflowRunStatus, WorkflowSummary, } from "@alexkroman1/aai/workflow-api";
|
|
61
|
+
export type { AgentClient, WorkflowApi, WorkflowInputOf, WorkflowOutputOf, WorkflowRunStatus, WorkflowSummary, } from "@alexkroman1/aai/workflow-api";
|
|
61
62
|
/**
|
|
62
63
|
* A run status nothing will change again.
|
|
63
64
|
*
|
|
@@ -84,20 +85,28 @@ export type WorkflowApiOptions = {
|
|
|
84
85
|
token?: string;
|
|
85
86
|
};
|
|
86
87
|
/**
|
|
87
|
-
* Create a
|
|
88
|
+
* Create a client for the agent serving this page.
|
|
88
89
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
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.
|
|
93
101
|
*
|
|
94
102
|
* @example
|
|
95
103
|
* ```tsx
|
|
96
104
|
* import { createWorkflowApi, useWorkflowRun } from "@alexkroman1/aai-ui";
|
|
97
105
|
* import { useState } from "react";
|
|
98
106
|
*
|
|
99
|
-
* //
|
|
100
|
-
*
|
|
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" });
|
|
101
110
|
*
|
|
102
111
|
* function StartDigest() {
|
|
103
112
|
* const [runId, setRunId] = useState<string>();
|
|
@@ -115,8 +124,13 @@ export type WorkflowApiOptions = {
|
|
|
115
124
|
*
|
|
116
125
|
* @param options - See {@link WorkflowApiOptions}. Both fields are optional; the
|
|
117
126
|
* default base URL is the page's own origin and path.
|
|
118
|
-
* @returns
|
|
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.
|
|
119
133
|
*
|
|
120
134
|
* @public
|
|
121
135
|
*/
|
|
122
|
-
export declare function createWorkflowApi(options?: WorkflowApiOptions):
|
|
136
|
+
export declare function createWorkflowApi(options?: WorkflowApiOptions): AgentClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"use-stick-to-bottom": "^1.1.6",
|
|
47
47
|
"use-sync-external-store": "^1.6.0",
|
|
48
48
|
"xstate": "^5.32.6",
|
|
49
|
-
"@alexkroman1/aai": "
|
|
49
|
+
"@alexkroman1/aai": "17.0.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@tailwindcss/vite": "^4.3.3",
|
package/styles.css
CHANGED
|
@@ -101,8 +101,8 @@
|
|
|
101
101
|
* reaches the TEMPLATES.** Every template's `client.tsx` opens with
|
|
102
102
|
* `import "@alexkroman1/aai-ui/styles.css"`, and several then declare keyframes
|
|
103
103
|
* of their own in an inline `<style>` block: `dc-pulse`/`dc-slide-in`
|
|
104
|
-
* (dispatch-
|
|
105
|
-
* (
|
|
104
|
+
* (emergency-dispatch-agent), `rt-pulse`/`rt-slide-in` (retail), `crisisPulse`
|
|
105
|
+
* (tabletop-rpg-agent), and `ic-flicker`/`ic-scanline`/`ic-pulse` (text-adventure-agent,
|
|
106
106
|
* which flickers the whole viewport's opacity and sweeps a scanline across the
|
|
107
107
|
* page — the worst thing in the repo for a motion-sensitive reader). Nothing in
|
|
108
108
|
* this package knows those names, and no per-component fix could: it would have
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-600:oklch(57.7% .245 27.325);--spacing:.25rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-aai-surface:var(--aai-surface,#fff);--color-aai-text:var(--aai-text,#1b1a18);--color-aai-border:var(--aai-border,#dcd7cc);--font-aai:"Monument Grotesk", "ABC Monument Grotesk", ui-sans-serif, system-ui, -apple-system, sans-serif;--font-aai-serif:"Source Serif 4", "Source Serif Pro", Charter, "Iowan Old Style", Georgia, serif;--font-aai-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;--radius-aai:4px}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body{margin:0;padding:0}}@layer components;@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.order-last{order:9999}.col-span-2{grid-column:span 2/span 2}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.m-0{margin:0}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.my-1\.5{margin-block:calc(var(--spacing) * 1.5)}.my-2\.5{margin-block:calc(var(--spacing) * 2.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.box-border{box-sizing:border-box}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1{height:var(--spacing)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-9{height:calc(var(--spacing) * 9)}.h-11{height:calc(var(--spacing) * 11)}.h-\[7px\]{height:7px}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[40vh\]{max-height:40vh}.min-h-0{min-height:0}.min-h-5{min-height:calc(var(--spacing) * 5)}.min-h-screen{min-height:100vh}.w-1{width:var(--spacing)}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-4{width:calc(var(--spacing) * 4)}.w-\[7px\]{width:7px}.w-fit{width:fit-content}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-75{max-width:calc(var(--spacing) * 75)}.max-w-105{max-width:calc(var(--spacing) * 105)}.max-w-190{max-width:calc(var(--spacing) * 190)}.max-w-\[82\%\]{max-width:82%}.max-w-\[min\(78\%\,64ch\)\]{max-width:min(78%,64ch)}.min-w-0{min-width:0}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.basis-full{flex-basis:100%}.border-collapse{border-collapse:collapse}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-pointer{cursor:pointer}.resize{resize:both}.\[scrollbar-width\:none\]{scrollbar-width:none}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{appearance:none}.grid-cols-\[1fr_320px\]{grid-template-columns:1fr 320px}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.self-end{align-self:flex-end}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded-aai{border-radius:var(--radius-aai)}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-none{--tw-border-style:none;border-style:none}.border-\(--aai-btn-bd\){border-color:var(--aai-btn-bd)}.border-aai-border{border-color:var(--color-aai-border)}.bg-\(--aai-btn-bg\){background-color:var(--aai-btn-bg)}.bg-aai-surface{background-color:var(--color-aai-surface)}.bg-transparent{background-color:#0000}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-7{padding:calc(var(--spacing) * 7)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-7{padding-inline:calc(var(--spacing) * 7)}.px-10{padding-inline:calc(var(--spacing) * 10)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-12{padding-block:calc(var(--spacing) * 12)}.pl-1\.5{padding-left:calc(var(--spacing) * 1.5)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.text-left{text-align:left}.font-aai{font-family:var(--font-aai)}.font-aai-mono{font-family:var(--font-aai-mono)}.font-aai-serif{font-family:var(--font-aai-serif)}.font-mono{font-family:var(--font-mono)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.625rem\]{font-size:.625rem}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12\.5px\]{font-size:12.5px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[17px\]{font-size:17px}.text-\[22px\]{font-size:22px}.text-\[32px\]{font-size:32px}.leading-4{--tw-leading:calc(var(--spacing) * 4);line-height:calc(var(--spacing) * 4)}.leading-\[1\.2\]{--tw-leading:1.2;line-height:1.2}.leading-\[1\.15\]{--tw-leading:1.15;line-height:1.15}.leading-\[22px\]{--tw-leading:22px;line-height:22px}.leading-\[23px\]{--tw-leading:23px;line-height:23px}.leading-\[130\%\]{--tw-leading:130%;line-height:130%}.leading-none{--tw-leading:1;line-height:1}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.2px\]{--tw-tracking:-.2px;letter-spacing:-.2px}.tracking-\[1\.2px\]{--tw-tracking:1.2px;letter-spacing:1.2px}.tracking-\[1\.4px\]{--tw-tracking:1.4px;letter-spacing:1.4px}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.text-balance{text-wrap:balance}.wrap-break-word{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\(--aai-btn-fg\){color:var(--aai-btn-fg)}.text-aai-text{color:var(--color-aai-text)}.text-red-600{color:var(--color-red-600)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_0_6px_currentColor\]{--tw-shadow:0 0 6px var(--tw-shadow-color,currentColor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring,.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.file\:mr-3::file-selector-button{margin-right:calc(var(--spacing) * 3)}.file\:cursor-pointer::file-selector-button{cursor:pointer}.file\:rounded-aai::file-selector-button{border-radius:var(--radius-aai)}.file\:border-0::file-selector-button{border-style:var(--tw-border-style);border-width:0}.file\:px-3::file-selector-button{padding-inline:calc(var(--spacing) * 3)}.file\:py-1\.5::file-selector-button{padding-block:calc(var(--spacing) * 1.5)}.file\:font-aai::file-selector-button{font-family:var(--font-aai)}.file\:text-sm::file-selector-button{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.file\:font-medium::file-selector-button{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.file\:\[color\:var\(--aai-file-button-fg\)\]::file-selector-button{color:var(--aai-file-button-fg)}.file\:\[background\:var\(--aai-file-button-bg\)\]::file-selector-button{background:var(--aai-file-button-bg)}.first\:mt-0:first-child{margin-top:0}.last\:mb-0:last-child{margin-bottom:0}.focus-visible\:\[outline\:2px_solid\]:focus-visible{outline:2px solid}.focus-visible\:\[outline-offset\:2px\]:focus-visible{outline-offset:2px}@media (hover:hover){.enabled\:hover\:border-\(--aai-btn-bd-hover\):enabled:hover{border-color:var(--aai-btn-bd-hover)}.enabled\:hover\:bg-\(--aai-btn-bg-hover\):enabled:hover{background-color:var(--aai-btn-bg-hover)}.enabled\:hover\:text-\(--aai-btn-fg-hover\):enabled:hover{color:var(--aai-btn-fg-hover)}}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:ml-auto{margin-left:auto}.sm\:max-w-\[60\%\]{max-width:60%}.sm\:basis-auto{flex-basis:auto}.sm\:px-16{padding-inline:calc(var(--spacing) * 16)}.sm\:py-14{padding-block:calc(var(--spacing) * 14)}}@media (width>=48rem){.md\:order-none{order:0}.md\:h-screen{height:100vh}.md\:max-h-none{max-height:none}.md\:w-\(--aai-sidebar-w\){width:var(--aai-sidebar-w)}.md\:flex-row{flex-direction:row}.md\:border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.md\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.md\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.md\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}}}@keyframes aai-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.82)}}@keyframes aai-bounce{0%,80%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}@keyframes aai-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.tool-shimmer{-webkit-text-fill-color:transparent;background:linear-gradient(90deg,currentColor 25%,#0000 50%,currentColor 75%) 0 0/200% 100%;-webkit-background-clip:text;background-clip:text;animation:2s infinite aai-shimmer}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.aai-scroll{scrollbar-width:thin;scrollbar-color:var(--aai-scrollbar-thumb,var(--aai-border,#dcd7cc)) var(--aai-scrollbar-track,transparent)}.aai-scroll::-webkit-scrollbar{width:6px}.aai-scroll::-webkit-scrollbar-track{background:var(--aai-scrollbar-track,transparent)}.aai-scroll::-webkit-scrollbar-thumb{background:var(--aai-scrollbar-thumb,var(--aai-border,#dcd7cc));border-radius:3px}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}}
|