@guuey/agent-client 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/block-ui.d.ts +6 -1
- package/dist/block-ui.d.ts.map +1 -1
- package/dist/block-ui.js +21 -3
- package/dist/card-mount.d.ts +69 -0
- package/dist/card-mount.d.ts.map +1 -0
- package/dist/card-mount.js +83 -0
- package/dist/ggui-render.d.ts +161 -0
- package/dist/ggui-render.d.ts.map +1 -0
- package/dist/ggui-render.js +164 -0
- package/dist/history.d.ts +12 -0
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +17 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/link-prompt.d.ts +22 -0
- package/dist/link-prompt.d.ts.map +1 -0
- package/dist/link-prompt.js +29 -0
- package/dist/sse.d.ts +19 -0
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +38 -0
- package/dist/types.d.ts +82 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -1
- package/dist/useAgentInvoke.d.ts.map +1 -1
- package/dist/useAgentInvoke.js +99 -12
- package/dist/web-adapters.d.ts +78 -14
- package/dist/web-adapters.d.ts.map +1 -1
- package/dist/web-adapters.js +115 -21
- package/package.json +2 -2
- package/src/block-ui.ts +19 -3
- package/src/card-mount.ts +101 -0
- package/src/ggui-render.ts +270 -0
- package/src/history.ts +17 -0
- package/src/index.ts +27 -0
- package/src/link-prompt.ts +34 -0
- package/src/sse.ts +36 -0
- package/src/types.ts +85 -3
- package/src/useAgentInvoke.ts +106 -12
- package/src/web-adapters.ts +169 -24
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for the pod's `POST /v1/link-prompt/dismiss` route (nocode-runtime
|
|
3
|
+
* linkcoh T4) — the "don't ask again" action on the
|
|
4
|
+
* {@link ProfileLinkRequest} prompt (see `./sse`'s `parseLinkRequest` and
|
|
5
|
+
* `useAgentInvoke`'s `profileLinkRequest` state). Host-agnostic like
|
|
6
|
+
* {@link fetchThreadHistory} in `./history`: takes a bearer directly (the
|
|
7
|
+
* route authenticates ONLY off the verified bearer — the request body is
|
|
8
|
+
* never read server-side, so there is nothing else to send) and an
|
|
9
|
+
* injectable `fetchImpl` for tests, defaulting to the global `fetch`.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Dismiss the pending cross-app profile link prompt for the calling byo
|
|
13
|
+
* end-user. `baseUrl` is the public read-plane base already ending in `/v1`
|
|
14
|
+
* (same convention as {@link fetchThreadHistory}'s `baseUrl`); `bearer` is the
|
|
15
|
+
* caller's byo access token, sent as `Authorization: Bearer <bearer>` — the
|
|
16
|
+
* ONLY thing the pod's route reads to determine the dismissal subject.
|
|
17
|
+
*
|
|
18
|
+
* Resolves on a 204 (the route's only success response); throws on any
|
|
19
|
+
* non-2xx status (401 unauthenticated, 403 non-byo caller, 500 write failure).
|
|
20
|
+
*/
|
|
21
|
+
export declare function dismissLinkPrompt(baseUrl: string, bearer: string, fetchImpl?: typeof fetch): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=link-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-prompt.d.ts","sourceRoot":"","sources":["../src/link-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client for the pod's `POST /v1/link-prompt/dismiss` route (nocode-runtime
|
|
3
|
+
* linkcoh T4) — the "don't ask again" action on the
|
|
4
|
+
* {@link ProfileLinkRequest} prompt (see `./sse`'s `parseLinkRequest` and
|
|
5
|
+
* `useAgentInvoke`'s `profileLinkRequest` state). Host-agnostic like
|
|
6
|
+
* {@link fetchThreadHistory} in `./history`: takes a bearer directly (the
|
|
7
|
+
* route authenticates ONLY off the verified bearer — the request body is
|
|
8
|
+
* never read server-side, so there is nothing else to send) and an
|
|
9
|
+
* injectable `fetchImpl` for tests, defaulting to the global `fetch`.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Dismiss the pending cross-app profile link prompt for the calling byo
|
|
13
|
+
* end-user. `baseUrl` is the public read-plane base already ending in `/v1`
|
|
14
|
+
* (same convention as {@link fetchThreadHistory}'s `baseUrl`); `bearer` is the
|
|
15
|
+
* caller's byo access token, sent as `Authorization: Bearer <bearer>` — the
|
|
16
|
+
* ONLY thing the pod's route reads to determine the dismissal subject.
|
|
17
|
+
*
|
|
18
|
+
* Resolves on a 204 (the route's only success response); throws on any
|
|
19
|
+
* non-2xx status (401 unauthenticated, 403 non-byo caller, 500 write failure).
|
|
20
|
+
*/
|
|
21
|
+
export async function dismissLinkPrompt(baseUrl, bearer, fetchImpl = fetch) {
|
|
22
|
+
const res = await fetchImpl(`${baseUrl}/link-prompt/dismiss`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { Authorization: `Bearer ${bearer}` },
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
throw new Error(`link prompt dismiss failed: ${res.status}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/sse.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* platform dependencies — unit-tested in isolation (`sse.test.ts`) and shared
|
|
4
4
|
* verbatim across web (Studio) and React-Native (Portal).
|
|
5
5
|
*/
|
|
6
|
+
import type { ProfileConsentRequest, ProfileLinkRequest } from "./types";
|
|
6
7
|
export interface ParsedSseEvent {
|
|
7
8
|
event: string;
|
|
8
9
|
data: unknown;
|
|
@@ -40,4 +41,22 @@ export declare function reduceAssistantText(current: string, data: unknown): str
|
|
|
40
41
|
export declare function extractAssistantText(data: unknown): string;
|
|
41
42
|
/** Read a string field off an SSE `data` object, or undefined. */
|
|
42
43
|
export declare function stringField(data: unknown, key: string): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a `profile-consent-needed` SSE payload into a typed
|
|
46
|
+
* {@link ProfileConsentRequest}, or `null` if it does not conform. `appId`
|
|
47
|
+
* must be a non-empty string and `requested` exactly `"read"` or
|
|
48
|
+
* `"read-write"`; extra keys are tolerated (ignored). Returns a fresh
|
|
49
|
+
* normalized object so callers get exactly the typed shape, never the raw
|
|
50
|
+
* wire payload with unknown extras.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseConsentRequest(data: unknown): ProfileConsentRequest | null;
|
|
53
|
+
/**
|
|
54
|
+
* Parse a `profile-link-needed` SSE payload into a typed
|
|
55
|
+
* {@link ProfileLinkRequest}, or `null` if it does not conform. Same shape +
|
|
56
|
+
* validation as {@link parseConsentRequest} (`appId` non-empty string,
|
|
57
|
+
* `requested` exactly `"read"` or `"read-write"`, extra keys tolerated) — the
|
|
58
|
+
* pod emits an identically-shaped payload for both events; only the event
|
|
59
|
+
* NAME (and what it means to the consumer) differs.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseLinkRequest(data: unknown): ProfileLinkRequest | null;
|
|
43
62
|
//# sourceMappingURL=sse.d.ts.map
|
package/dist/sse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqBzF;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAM1E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAgC1D;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI1E"}
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqBzF;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAM1E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAgC1D;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI1E;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,qBAAqB,GAAG,IAAI,CAO/E;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,GAAG,IAAI,CAOzE"}
|
package/dist/sse.js
CHANGED
|
@@ -105,3 +105,41 @@ export function stringField(data, key) {
|
|
|
105
105
|
const v = data[key];
|
|
106
106
|
return typeof v === "string" ? v : undefined;
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Parse a `profile-consent-needed` SSE payload into a typed
|
|
110
|
+
* {@link ProfileConsentRequest}, or `null` if it does not conform. `appId`
|
|
111
|
+
* must be a non-empty string and `requested` exactly `"read"` or
|
|
112
|
+
* `"read-write"`; extra keys are tolerated (ignored). Returns a fresh
|
|
113
|
+
* normalized object so callers get exactly the typed shape, never the raw
|
|
114
|
+
* wire payload with unknown extras.
|
|
115
|
+
*/
|
|
116
|
+
export function parseConsentRequest(data) {
|
|
117
|
+
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
118
|
+
return null;
|
|
119
|
+
const appId = data.appId;
|
|
120
|
+
const requested = data.requested;
|
|
121
|
+
if (typeof appId !== "string" || appId.length === 0)
|
|
122
|
+
return null;
|
|
123
|
+
if (requested !== "read" && requested !== "read-write")
|
|
124
|
+
return null;
|
|
125
|
+
return { appId, requested };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Parse a `profile-link-needed` SSE payload into a typed
|
|
129
|
+
* {@link ProfileLinkRequest}, or `null` if it does not conform. Same shape +
|
|
130
|
+
* validation as {@link parseConsentRequest} (`appId` non-empty string,
|
|
131
|
+
* `requested` exactly `"read"` or `"read-write"`, extra keys tolerated) — the
|
|
132
|
+
* pod emits an identically-shaped payload for both events; only the event
|
|
133
|
+
* NAME (and what it means to the consumer) differs.
|
|
134
|
+
*/
|
|
135
|
+
export function parseLinkRequest(data) {
|
|
136
|
+
if (typeof data !== "object" || data === null || Array.isArray(data))
|
|
137
|
+
return null;
|
|
138
|
+
const appId = data.appId;
|
|
139
|
+
const requested = data.requested;
|
|
140
|
+
if (typeof appId !== "string" || appId.length === 0)
|
|
141
|
+
return null;
|
|
142
|
+
if (requested !== "read" && requested !== "read-write")
|
|
143
|
+
return null;
|
|
144
|
+
return { appId, requested };
|
|
145
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* (which also carries anonymous identity) — are INJECTED by the consumer via
|
|
7
7
|
* {@link AgentInvokeAdapters}. Web (Studio) passes localStorage / crypto /
|
|
8
8
|
* credentialed-cookie fetch; React-Native (Portal) passes AsyncStorage /
|
|
9
|
-
* getRandomValues / header-identity SSE fetch.
|
|
9
|
+
* getRandomValues / header-identity SSE fetch. Anonymous identity is per-host,
|
|
10
|
+
* not per-platform: a web host with no usable cookie jar (an embedded
|
|
11
|
+
* third-party iframe) carries its own guest secret in a header too — see
|
|
12
|
+
* `createWebAdapters`'s `getGuestSecret`. This mirrors the ggui
|
|
10
13
|
* `MessageStorageAdapter` injection pattern.
|
|
11
14
|
*/
|
|
12
15
|
import type { AgReduceResult, JsonValue } from "@silverprotocol/core";
|
|
@@ -15,6 +18,33 @@ export interface AgentMessage {
|
|
|
15
18
|
role: "user" | "assistant";
|
|
16
19
|
text: string;
|
|
17
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* A cross-app profile consent request surfaced mid-stream by the pod's
|
|
23
|
+
* `profile-consent-needed` SSE event (nocode-runtime T6). Emitted when the
|
|
24
|
+
* agent declares a profile intent the caller has NOT yet granted for this app,
|
|
25
|
+
* so the consumer UI can prompt the user to authorize `read` or `read-write`
|
|
26
|
+
* access. `requested` mirrors the pod's `ProfileAccess` posture verbatim; the
|
|
27
|
+
* literal union is inlined rather than imported to keep this client SDK free of
|
|
28
|
+
* any backend-package dependency.
|
|
29
|
+
*/
|
|
30
|
+
export interface ProfileConsentRequest {
|
|
31
|
+
appId: string;
|
|
32
|
+
requested: "read" | "read-write";
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A cross-app profile LINK invite surfaced mid-stream by the pod's
|
|
36
|
+
* `profile-link-needed` SSE event (nocode-runtime linkcoh T3). Emitted when an
|
|
37
|
+
* unlinked byo end-user's declared profile posture booted, inviting them to
|
|
38
|
+
* link their guuey account (via the named `/link` ceremony) so they earn the
|
|
39
|
+
* guuey-wide cross-app profile. `requested` mirrors the pod's `ProfileAccess`
|
|
40
|
+
* posture verbatim (the builder's declared access, not a live ask) — the
|
|
41
|
+
* literal union is inlined rather than imported, same rationale as
|
|
42
|
+
* {@link ProfileConsentRequest}.
|
|
43
|
+
*/
|
|
44
|
+
export interface ProfileLinkRequest {
|
|
45
|
+
appId: string;
|
|
46
|
+
requested: "read" | "read-write";
|
|
47
|
+
}
|
|
18
48
|
/**
|
|
19
49
|
* A persisted generative-UI card rehydrated from thread history — the verbatim
|
|
20
50
|
* `AgArtifact` snapshot the pod stored on a `kind: "card"` row, tagged with its
|
|
@@ -51,7 +81,7 @@ export interface InvokeRequest {
|
|
|
51
81
|
* Opens an invoke request and yields decoded UTF-8 text chunks of the SSE
|
|
52
82
|
* stream (the hook accumulates + parses frames itself). MUST throw on a
|
|
53
83
|
* non-OK response or network failure. Owns headers + identity entirely, so
|
|
54
|
-
* the hook never sees cookies or
|
|
84
|
+
* the hook never sees cookies, bearer tokens, or guest secrets.
|
|
55
85
|
*/
|
|
56
86
|
export type InvokeTransport = (req: InvokeRequest) => AsyncIterable<string>;
|
|
57
87
|
/**
|
|
@@ -102,10 +132,35 @@ export interface UseAgentInvokeOptions {
|
|
|
102
132
|
*/
|
|
103
133
|
preserveBlocks?: boolean;
|
|
104
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The per-turn lifecycle (guuey#91), derived ENTIRELY from frames the pod
|
|
137
|
+
* already emits — no protocol addition:
|
|
138
|
+
*
|
|
139
|
+
* - `ready` — no turn in flight (initial, after `done`/failure/abort).
|
|
140
|
+
* - `connecting` — `send()` fired, no `session` frame yet. With
|
|
141
|
+
* scale-to-zero pods this phase can span a cold start, so hosts typically
|
|
142
|
+
* swap to "waking your agent" copy after a few seconds.
|
|
143
|
+
* - `thinking` — the pod is awake and the turn is running, but no text is
|
|
144
|
+
* flowing and no tool is announced (between `session` and the first
|
|
145
|
+
* content, and between a `tool.done` and whatever follows it).
|
|
146
|
+
* - `using-tool` — a `tool.start` frame arrived; {@link UseAgentInvokeReturn.activeTool}
|
|
147
|
+
* carries the wire tool name until the matching `tool.done`.
|
|
148
|
+
* - `responding` — assistant text is arriving (`text.start`/`text.delta`
|
|
149
|
+
* silver frames, or bypass text/assistant frames).
|
|
150
|
+
*
|
|
151
|
+
* Failure keeps its own channel ({@link UseAgentInvokeReturn.error}) — there
|
|
152
|
+
* is deliberately no `error` status: after any terminal outcome the status
|
|
153
|
+
* returns to `ready` so the composer re-enables.
|
|
154
|
+
*/
|
|
155
|
+
export type AgentInvokeStatus = "ready" | "connecting" | "thinking" | "using-tool" | "responding";
|
|
105
156
|
export interface UseAgentInvokeReturn {
|
|
106
157
|
messages: AgentMessage[];
|
|
107
158
|
send: (input: string) => Promise<void>;
|
|
108
|
-
|
|
159
|
+
/** The per-turn lifecycle — see {@link AgentInvokeStatus}. Anything other
|
|
160
|
+
* than `ready` means a turn is in flight (the old `isStreaming === true`). */
|
|
161
|
+
status: AgentInvokeStatus;
|
|
162
|
+
/** The active tool's wire name while `status === 'using-tool'`, else null. */
|
|
163
|
+
activeTool: string | null;
|
|
109
164
|
error: string | null;
|
|
110
165
|
threadId: string | null;
|
|
111
166
|
/** Abort the in-flight turn (the stream stops; partial text is kept). */
|
|
@@ -141,5 +196,29 @@ export interface UseAgentInvokeReturn {
|
|
|
141
196
|
* `reset()` clears it back to `[]`.
|
|
142
197
|
*/
|
|
143
198
|
historyCards: HistoryCard[];
|
|
199
|
+
/**
|
|
200
|
+
* The latest cross-app profile consent request the pod asked for on THIS
|
|
201
|
+
* conversation, or `null`. Set from a well-formed `profile-consent-needed`
|
|
202
|
+
* SSE event (see {@link ProfileConsentRequest}); malformed payloads are
|
|
203
|
+
* dropped and leave the field untouched. `reset()` and an app switch clear
|
|
204
|
+
* it back to `null`. Consumers that never render a consent prompt (e.g.
|
|
205
|
+
* Studio) simply ignore this field.
|
|
206
|
+
*/
|
|
207
|
+
profileConsentRequest: ProfileConsentRequest | null;
|
|
208
|
+
/** Dismiss the pending {@link profileConsentRequest} (back to `null`). */
|
|
209
|
+
clearProfileConsentRequest: () => void;
|
|
210
|
+
/**
|
|
211
|
+
* The latest cross-app profile LINK invite the pod asked for on THIS
|
|
212
|
+
* conversation, or `null`. Set from a well-formed `profile-link-needed`
|
|
213
|
+
* SSE event (see {@link ProfileLinkRequest}); malformed payloads are
|
|
214
|
+
* dropped and leave the field untouched. `reset()` and an app switch clear
|
|
215
|
+
* it back to `null`. Consumers that never render a link prompt simply
|
|
216
|
+
* ignore this field. Distinct from {@link profileConsentRequest}: this one
|
|
217
|
+
* invites an UNLINKED byo user to link their account; consent asks an
|
|
218
|
+
* already-linked user to grant an app read/read-write access.
|
|
219
|
+
*/
|
|
220
|
+
profileLinkRequest: ProfileLinkRequest | null;
|
|
221
|
+
/** Dismiss the pending {@link profileLinkRequest} (back to `null`). */
|
|
222
|
+
clearProfileLinkRequest: () => void;
|
|
144
223
|
}
|
|
145
224
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* (which also carries anonymous identity) — are INJECTED by the consumer via
|
|
7
7
|
* {@link AgentInvokeAdapters}. Web (Studio) passes localStorage / crypto /
|
|
8
8
|
* credentialed-cookie fetch; React-Native (Portal) passes AsyncStorage /
|
|
9
|
-
* getRandomValues / header-identity SSE fetch.
|
|
9
|
+
* getRandomValues / header-identity SSE fetch. Anonymous identity is per-host,
|
|
10
|
+
* not per-platform: a web host with no usable cookie jar (an embedded
|
|
11
|
+
* third-party iframe) carries its own guest secret in a header too — see
|
|
12
|
+
* `createWebAdapters`'s `getGuestSecret`. This mirrors the ggui
|
|
10
13
|
* `MessageStorageAdapter` injection pattern.
|
|
11
14
|
*/
|
|
12
15
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgentInvoke.d.ts","sourceRoot":"","sources":["../src/useAgentInvoke.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAgentInvoke.d.ts","sourceRoot":"","sources":["../src/useAgentInvoke.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAGV,YAAY,EAEZ,iBAAiB,EAGjB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAMjB,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,iBAAiB,EACzB,eAAe,EAAE,YAAY,EAAE,GAC9B,kBAAkB,CAIpB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,oBAAoB,CAqUhF"}
|
package/dist/useAgentInvoke.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
25
25
|
import { Reducer } from "@silverprotocol/core";
|
|
26
|
-
import { parseSseEvents, reduceAssistantText, stringField } from "./sse";
|
|
26
|
+
import { parseConsentRequest, parseLinkRequest, parseSseEvents, reduceAssistantText, stringField, } from "./sse";
|
|
27
27
|
import { ingestMessageFrame } from "./blocks";
|
|
28
28
|
function threadStorageKey(appId) {
|
|
29
29
|
return `guuey:thread:${appId ?? "default"}`;
|
|
@@ -45,7 +45,11 @@ export function applyHistoryResult(result, currentMessages) {
|
|
|
45
45
|
export function useAgentInvoke(opts) {
|
|
46
46
|
const { endpointUrl, appId } = opts;
|
|
47
47
|
const [messages, setMessages] = useState([]);
|
|
48
|
-
|
|
48
|
+
// Per-turn lifecycle (guuey#91) — derived purely from the frames below; see
|
|
49
|
+
// the `AgentInvokeStatus` doc for the state meanings. `activeTool` carries
|
|
50
|
+
// the wire tool name only while status is 'using-tool'.
|
|
51
|
+
const [status, setStatus] = useState("ready");
|
|
52
|
+
const [activeTool, setActiveTool] = useState(null);
|
|
49
53
|
const [error, setError] = useState(null);
|
|
50
54
|
const [threadId, setThreadId] = useState(null);
|
|
51
55
|
// Opt-in block-preserving transcript. `reduceResult` follows the
|
|
@@ -57,6 +61,15 @@ export function useAgentInvoke(opts) {
|
|
|
57
61
|
// contract). Independent of the live `reduceResult` fold — populated only
|
|
58
62
|
// when a card-carrying history load seeds the transcript.
|
|
59
63
|
const [historyCards, setHistoryCards] = useState([]);
|
|
64
|
+
// The pod's latest cross-app profile consent ask on this conversation (T6's
|
|
65
|
+
// `profile-consent-needed` SSE event), or null. Cleared on app switch /
|
|
66
|
+
// reset / explicit dismiss. Consumers with no consent UI just ignore it.
|
|
67
|
+
const [profileConsentRequest, setProfileConsentRequest] = useState(null);
|
|
68
|
+
// The pod's latest cross-app profile LINK invite on this conversation (T3's
|
|
69
|
+
// `profile-link-needed` SSE event), or null. Cleared on app switch / reset /
|
|
70
|
+
// explicit dismiss, same lifecycle as `profileConsentRequest` — the two are
|
|
71
|
+
// independent (an unlinked-invite vs an already-linked consent ask).
|
|
72
|
+
const [profileLinkRequest, setProfileLinkRequest] = useState(null);
|
|
60
73
|
const abortRef = useRef(null);
|
|
61
74
|
// Mirror the latest threadId + adapters into refs so `send` reads fresh
|
|
62
75
|
// values without depending on them (keeps the callback identity stable and
|
|
@@ -64,9 +77,13 @@ export function useAgentInvoke(opts) {
|
|
|
64
77
|
const threadIdRef = useRef(null);
|
|
65
78
|
const adaptersRef = useRef(opts.adapters);
|
|
66
79
|
adaptersRef.current = opts.adapters;
|
|
67
|
-
// The per-conversation AgJSON
|
|
80
|
+
// The per-conversation AgJSON fold (only built when `preserveBlocks`).
|
|
68
81
|
// Lazily (re)created on the first valid AgEvent after a fresh start / reset,
|
|
69
82
|
// so an off run never constructs one and a bypass run never allocates.
|
|
83
|
+
// The core Reducer carries `_meta` onto tool-result blocks as of
|
|
84
|
+
// `@silverprotocol/core` 0.4.1 (workspace#9), so BOTH generative-UI channels
|
|
85
|
+
// (MCP-Apps `_meta.ui`, ggui's render bootstrap) survive the fold natively —
|
|
86
|
+
// the old guuey-side `BlockFold` carriage wrapper is deleted.
|
|
70
87
|
const reducerRef = useRef(null);
|
|
71
88
|
const preserveBlocksRef = useRef(opts.preserveBlocks ?? false);
|
|
72
89
|
preserveBlocksRef.current = opts.preserveBlocks ?? false;
|
|
@@ -85,12 +102,16 @@ export function useAgentInvoke(opts) {
|
|
|
85
102
|
setThreadId(null);
|
|
86
103
|
setMessages([]);
|
|
87
104
|
setError(null);
|
|
88
|
-
|
|
105
|
+
setStatus("ready");
|
|
106
|
+
setActiveTool(null);
|
|
89
107
|
// Fresh conversation → drop the old fold; the reducer is rebuilt lazily on
|
|
90
108
|
// the next valid AgEvent. Persisted cards are re-seeded below from history.
|
|
91
109
|
reducerRef.current = null;
|
|
92
110
|
setReduceResult(null);
|
|
93
111
|
setHistoryCards([]);
|
|
112
|
+
// A prior app's consent ask must never leak into the new conversation.
|
|
113
|
+
setProfileConsentRequest(null);
|
|
114
|
+
setProfileLinkRequest(null);
|
|
94
115
|
let cancelled = false;
|
|
95
116
|
const key = threadStorageKey(appId);
|
|
96
117
|
const hydration = Promise.resolve(adaptersRef.current.storage.load(key))
|
|
@@ -171,18 +192,27 @@ export function useAgentInvoke(opts) {
|
|
|
171
192
|
void adaptersRef.current.storage.save(threadStorageKey(appId), "");
|
|
172
193
|
setMessages([]);
|
|
173
194
|
setError(null);
|
|
174
|
-
|
|
195
|
+
setStatus("ready");
|
|
196
|
+
setActiveTool(null);
|
|
175
197
|
// Re-create the reducer for the new conversation (rebuilt lazily on the
|
|
176
198
|
// next valid AgEvent) and clear the exposed fold + any rehydrated cards.
|
|
177
199
|
reducerRef.current = null;
|
|
178
200
|
setReduceResult(null);
|
|
179
201
|
setHistoryCards([]);
|
|
202
|
+
setProfileConsentRequest(null);
|
|
203
|
+
setProfileLinkRequest(null);
|
|
180
204
|
}, [appId]);
|
|
205
|
+
const clearProfileConsentRequest = useCallback(() => {
|
|
206
|
+
setProfileConsentRequest(null);
|
|
207
|
+
}, []);
|
|
208
|
+
const clearProfileLinkRequest = useCallback(() => {
|
|
209
|
+
setProfileLinkRequest(null);
|
|
210
|
+
}, []);
|
|
181
211
|
const send = useCallback(async (input) => {
|
|
182
|
-
if (!endpointUrl || !input.trim() ||
|
|
212
|
+
if (!endpointUrl || !input.trim() || status !== "ready")
|
|
183
213
|
return;
|
|
184
214
|
setError(null);
|
|
185
|
-
|
|
215
|
+
setStatus("connecting");
|
|
186
216
|
setMessages((prev) => [...prev, { role: "user", text: input }, { role: "assistant", text: "" }]);
|
|
187
217
|
const controller = new AbortController();
|
|
188
218
|
abortRef.current = controller;
|
|
@@ -194,7 +224,7 @@ export function useAgentInvoke(opts) {
|
|
|
194
224
|
await hydrationRef.current;
|
|
195
225
|
}
|
|
196
226
|
if (controller.signal.aborted) {
|
|
197
|
-
|
|
227
|
+
setStatus("ready");
|
|
198
228
|
abortRef.current = null;
|
|
199
229
|
return;
|
|
200
230
|
}
|
|
@@ -226,6 +256,10 @@ export function useAgentInvoke(opts) {
|
|
|
226
256
|
buffer = rest;
|
|
227
257
|
for (const ev of events) {
|
|
228
258
|
if (ev.event === "session") {
|
|
259
|
+
// The pod is awake and the turn is admitted — 'connecting' ends
|
|
260
|
+
// here (this frame arrives within ~1s of a warm pod; a cold
|
|
261
|
+
// scale-to-zero start is exactly the long 'connecting' phase).
|
|
262
|
+
setStatus("thinking");
|
|
229
263
|
const tid = stringField(ev.data, "threadId");
|
|
230
264
|
if (tid) {
|
|
231
265
|
threadIdRef.current = tid;
|
|
@@ -234,6 +268,26 @@ export function useAgentInvoke(opts) {
|
|
|
234
268
|
}
|
|
235
269
|
}
|
|
236
270
|
else if (ev.event === "message") {
|
|
271
|
+
// Status derivation (guuey#91) — read the frame's `type` before
|
|
272
|
+
// the text fold. Silver frames announce tools + text explicitly;
|
|
273
|
+
// bypass frames ('text' / 'assistant' SDKMessages) only ever
|
|
274
|
+
// carry assistant text, so they map to 'responding'. Unknown
|
|
275
|
+
// types deliberately leave the status untouched.
|
|
276
|
+
const frameType = stringField(ev.data, "type");
|
|
277
|
+
if (frameType === "tool.start") {
|
|
278
|
+
setStatus("using-tool");
|
|
279
|
+
setActiveTool(stringField(ev.data, "name") ?? null);
|
|
280
|
+
}
|
|
281
|
+
else if (frameType === "tool.done") {
|
|
282
|
+
setStatus("thinking");
|
|
283
|
+
setActiveTool(null);
|
|
284
|
+
}
|
|
285
|
+
else if (frameType === "text.start" ||
|
|
286
|
+
frameType === "text.delta" ||
|
|
287
|
+
frameType === "text" ||
|
|
288
|
+
frameType === "assistant") {
|
|
289
|
+
setStatus("responding");
|
|
290
|
+
}
|
|
237
291
|
renderAssistant(reduceAssistantText(assistantText, ev.data));
|
|
238
292
|
// Additively fold the SAME frame into the AgJSON reducer when
|
|
239
293
|
// opted in. The text surface above is untouched; only VALID
|
|
@@ -253,7 +307,24 @@ export function useAgentInvoke(opts) {
|
|
|
253
307
|
else if (ev.event === "error") {
|
|
254
308
|
setError(stringField(ev.data, "message") ?? "agent error");
|
|
255
309
|
}
|
|
256
|
-
|
|
310
|
+
else if (ev.event === "profile-consent-needed") {
|
|
311
|
+
// Cross-app profile consent ask (T6). Only a well-formed payload
|
|
312
|
+
// updates state; a malformed one is dropped, leaving any prior
|
|
313
|
+
// valid request untouched (never clobbered to null).
|
|
314
|
+
const parsed = parseConsentRequest(ev.data);
|
|
315
|
+
if (parsed)
|
|
316
|
+
setProfileConsentRequest(parsed);
|
|
317
|
+
}
|
|
318
|
+
else if (ev.event === "profile-link-needed") {
|
|
319
|
+
// Cross-app profile LINK invite (linkcoh T3) for an unlinked byo
|
|
320
|
+
// caller. Same drop-if-malformed contract as consent above.
|
|
321
|
+
const parsed = parseLinkRequest(ev.data);
|
|
322
|
+
if (parsed)
|
|
323
|
+
setProfileLinkRequest(parsed);
|
|
324
|
+
}
|
|
325
|
+
// `done` needs no handling — the stream closes after it. Any other
|
|
326
|
+
// (unknown) event falls through silently — there is no default
|
|
327
|
+
// branch, so a consumer that never renders a field is unaffected.
|
|
257
328
|
}
|
|
258
329
|
}
|
|
259
330
|
}
|
|
@@ -263,7 +334,8 @@ export function useAgentInvoke(opts) {
|
|
|
263
334
|
}
|
|
264
335
|
}
|
|
265
336
|
finally {
|
|
266
|
-
|
|
337
|
+
setStatus("ready");
|
|
338
|
+
setActiveTool(null);
|
|
267
339
|
abortRef.current = null;
|
|
268
340
|
// A turn aborted before any assistant text streamed leaves an empty
|
|
269
341
|
// placeholder bubble — drop it so a stopped turn doesn't linger as a
|
|
@@ -277,6 +349,21 @@ export function useAgentInvoke(opts) {
|
|
|
277
349
|
});
|
|
278
350
|
}
|
|
279
351
|
}
|
|
280
|
-
}, [endpointUrl, appId,
|
|
281
|
-
return {
|
|
352
|
+
}, [endpointUrl, appId, status]);
|
|
353
|
+
return {
|
|
354
|
+
messages,
|
|
355
|
+
send,
|
|
356
|
+
status,
|
|
357
|
+
activeTool,
|
|
358
|
+
error,
|
|
359
|
+
threadId,
|
|
360
|
+
abort,
|
|
361
|
+
reset,
|
|
362
|
+
reduceResult,
|
|
363
|
+
historyCards,
|
|
364
|
+
profileConsentRequest,
|
|
365
|
+
clearProfileConsentRequest,
|
|
366
|
+
profileLinkRequest,
|
|
367
|
+
clearProfileLinkRequest,
|
|
368
|
+
};
|
|
282
369
|
}
|
package/dist/web-adapters.d.ts
CHANGED
|
@@ -24,14 +24,25 @@ export declare const localStorageThreadStore: ThreadIdStore;
|
|
|
24
24
|
/** Crypto-strong client-message id, with a non-crypto fallback. */
|
|
25
25
|
export declare function webGenerateId(): string;
|
|
26
26
|
/**
|
|
27
|
-
* Web SSE transport.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* Web SSE transport. Exactly ONE identity carrier per request, in order:
|
|
28
|
+
*
|
|
29
|
+
* 1. `accessToken` → `Authorization: Bearer` — the pod identifies the caller
|
|
30
|
+
* by their verified access token (the same identity the history read
|
|
31
|
+
* plane uses, so persisted threads round-trip on reload).
|
|
32
|
+
* 2. a well-formed `guestSecret` → `x-guuey-guest` — the caller owns and
|
|
33
|
+
* persists its own anonymous secret. The path for hosts with no usable
|
|
34
|
+
* cookie jar: React-Native, and the embedded widget, whose third-party
|
|
35
|
+
* iframe cannot rely on the pod's cookie surviving browser partitioning.
|
|
36
|
+
* The pod never mints a cookie for a header client.
|
|
37
|
+
* 3. neither → `credentials: "include"`, which round-trips the HttpOnly
|
|
38
|
+
* `guuey_guest` cookie the pod mints for anonymous browser callers.
|
|
39
|
+
*
|
|
40
|
+
* Never two at once: a bearer wins over a guest secret, and a request that
|
|
41
|
+
* carries either header does NOT also send cookie credentials.
|
|
42
|
+
*
|
|
32
43
|
* Reads the body via `ReadableStream.getReader()` (browser).
|
|
33
44
|
*/
|
|
34
|
-
export declare function fetchStreamTransport(req: InvokeRequest, accessToken?: string | null): AsyncGenerator<string>;
|
|
45
|
+
export declare function fetchStreamTransport(req: InvokeRequest, accessToken?: string | null, guestSecret?: string | null): AsyncGenerator<string>;
|
|
35
46
|
export interface CreateWebAdaptersOptions {
|
|
36
47
|
/**
|
|
37
48
|
* Public read-plane base (ending in `/v1`) for transcript history. When
|
|
@@ -42,18 +53,71 @@ export interface CreateWebAdaptersOptions {
|
|
|
42
53
|
* Resolve the caller's Cognito access token (fresh), or `null` when signed
|
|
43
54
|
* out. When a token is present the chat transport AND the history read
|
|
44
55
|
* authenticate as that user, so a reload restores the transcript. Without
|
|
45
|
-
* a token
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
56
|
+
* a token, identity falls to {@link getGuestSecret} (if supplied) and then
|
|
57
|
+
* to the guest cookie.
|
|
58
|
+
*
|
|
59
|
+
* Called with `{ forceRefresh: true }` exactly once: when the history read
|
|
60
|
+
* gets a 401 on a token this resolver already returned (a token cached
|
|
61
|
+
* before the mount-time history read fired can be stale by the time it
|
|
62
|
+
* runs — the same window the send path's own 401-retry closes). A resolver
|
|
63
|
+
* that caches (Amplify's `fetchAuthSession` does, and so does the widget's
|
|
64
|
+
* `createHostTokenProvider`) MUST bypass that cache for a forced call and
|
|
65
|
+
* obtain a genuinely fresh token — returning the SAME stale value would
|
|
66
|
+
* make the retry indistinguishable from not retrying at all. A resolver
|
|
67
|
+
* with nothing fresher to offer returns `null`, and the read surfaces the
|
|
68
|
+
* ORIGINAL 401 rather than replaying the value that just failed.
|
|
69
|
+
*/
|
|
70
|
+
getAccessToken?: (opts?: {
|
|
71
|
+
forceRefresh?: boolean;
|
|
72
|
+
}) => Promise<string | null>;
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the caller's own persisted anonymous guest secret (64 lowercase
|
|
75
|
+
* hex chars), or `null` when there is none. Supply this on hosts whose
|
|
76
|
+
* cookie jar can't carry the pod's HttpOnly `guuey_guest` — notably the
|
|
77
|
+
* embedded widget, a third-party iframe whose cookies browsers partition
|
|
78
|
+
* or block outright.
|
|
79
|
+
*
|
|
80
|
+
* With a secret, BOTH the chat transport and the history read send
|
|
81
|
+
* `x-guuey-guest`, so an anonymous transcript replays on reload the same
|
|
82
|
+
* way a signed-in one does — the read plane identifies a guest by that
|
|
83
|
+
* header (it cannot see the HttpOnly cookie, which is why a cookie-only
|
|
84
|
+
* caller still gets no history).
|
|
85
|
+
*
|
|
86
|
+
* Called once per request, so a rotated secret takes effect immediately.
|
|
87
|
+
* A value that isn't 64 lowercase hex is ignored (never sent) and the
|
|
88
|
+
* request falls through to cookie mode.
|
|
89
|
+
*
|
|
90
|
+
* **Supply at most ONE identity resolver per mode.** Anonymous hosts pass
|
|
91
|
+
* this one; identified hosts pass {@link getAccessToken} and surface a token
|
|
92
|
+
* failure rather than continuing. Passing BOTH is a hazard, not a fallback
|
|
93
|
+
* chain: `getAccessToken` resolving `null` is indistinguishable here from
|
|
94
|
+
* "signed out on purpose", so a merely *expired or unavailable* token
|
|
95
|
+
* silently downgrades the caller to the anonymous identity. The request then
|
|
96
|
+
* SUCCEEDS — the pod accepts anonymous invokes unconditionally — but the
|
|
97
|
+
* turns land in a different thread (the pod forks on an owner mismatch
|
|
98
|
+
* rather than appending), unreachable from the identified session, which
|
|
99
|
+
* gets its own transcript back minus those turns on the next good load. A
|
|
100
|
+
* 401-then-re-request-token retry loop is exactly this window.
|
|
101
|
+
*
|
|
102
|
+
* MUST be synchronous and MUST NOT throw: a throw propagates and fails the
|
|
103
|
+
* invoke. This is a real hazard for the widget, not a formality —
|
|
104
|
+
* `localStorage` access raises `SecurityError` in a third-party iframe with
|
|
105
|
+
* storage blocked (Safari's default for embedded content), which is normal
|
|
106
|
+
* operation here. A host reading storage owns that handling and MUST return
|
|
107
|
+
* `null` on a blocked read, the way {@link localStorageThreadStore} does for
|
|
108
|
+
* the threadId; `null` degrades to cookie mode, whereas a throw takes the
|
|
109
|
+
* chat down. Deliberately NOT caught at this seam: catching a host-supplied
|
|
110
|
+
* callback would also swallow ordinary host bugs into a silent anonymous
|
|
111
|
+
* downgrade — the same failure this docblock warns about above.
|
|
49
112
|
*/
|
|
50
|
-
|
|
113
|
+
getGuestSecret?: () => string | null;
|
|
51
114
|
}
|
|
52
115
|
/**
|
|
53
116
|
* Build the web host-adapter bundle for {@link useAgentInvoke}. Pass an
|
|
54
|
-
* access-token resolver
|
|
55
|
-
*
|
|
56
|
-
*
|
|
117
|
+
* access-token resolver and/or a guest-secret resolver (plus the read-plane
|
|
118
|
+
* base) to give the chat transport an identity the read plane can also see,
|
|
119
|
+
* which is what enables transcript restore on reload; omit both for a
|
|
120
|
+
* cookie-only, history-less bundle.
|
|
57
121
|
*/
|
|
58
122
|
export declare function createWebAdapters(opts?: CreateWebAdaptersOptions): AgentInvokeAdapters;
|
|
59
123
|
//# sourceMappingURL=web-adapters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-adapters.d.ts","sourceRoot":"","sources":["../src/web-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EAEb,aAAa,EACd,MAAM,SAAS,CAAC;AAGjB;;;;;;GAMG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAGzC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;gBAFtB,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA;CAKzB;AAED,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,EAAE,aAiBrC,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,aAAa,IAAI,MAAM,CAKtC;
|
|
1
|
+
{"version":3,"file":"web-adapters.d.ts","sourceRoot":"","sources":["../src/web-adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EACV,mBAAmB,EACnB,aAAa,EAEb,aAAa,EACd,MAAM,SAAS,CAAC;AAGjB;;;;;;GAMG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAGzC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;gBAFtB,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA;CAKzB;AAED,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,EAAE,aAiBrC,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAwCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAuB,oBAAoB,CACzC,GAAG,EAAE,aAAa,EAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,EAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAC1B,cAAc,CAAC,MAAM,CAAC,CA4CxB;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACH,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,GAAE,wBAA6B,GAClC,mBAAmB,CAwErB"}
|