sideagent 0.3.0 → 0.5.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 +12 -0
- package/dist/chunk-5H62LFNC.js +10 -0
- package/dist/chunk-73FXXOAR.js +43 -0
- package/dist/index.d.ts +29 -9
- package/dist/index.js +670 -483
- package/dist/keys.d.ts +16 -0
- package/dist/keys.js +12 -0
- package/dist/server/index.d.ts +89 -60
- package/dist/server/index.js +943 -660
- package/dist/testing/mock-llm.js +37 -6
- package/dist/workers-ai/index.js +47 -15
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,41 +1,65 @@
|
|
|
1
|
-
// src/client/AgentChat.tsx
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
PUBLISHABLE_KEY_HEADER,
|
|
3
|
+
parsePublishableKey,
|
|
4
|
+
publishableEndpoint
|
|
5
|
+
} from "./chunk-73FXXOAR.js";
|
|
6
|
+
|
|
7
|
+
// src/client/AgentChat.tsx
|
|
9
8
|
import {
|
|
10
|
-
CheckCircledIcon,
|
|
11
9
|
ChatBubbleIcon,
|
|
12
|
-
ChevronRightIcon,
|
|
13
|
-
ArrowLeftIcon,
|
|
14
|
-
ArrowUpIcon,
|
|
15
10
|
CounterClockwiseClockIcon,
|
|
16
11
|
Cross2Icon,
|
|
17
|
-
CrossCircledIcon,
|
|
18
|
-
ExternalLinkIcon,
|
|
19
|
-
FileTextIcon,
|
|
20
|
-
ExclamationTriangleIcon,
|
|
21
12
|
OpenInNewWindowIcon,
|
|
22
13
|
PlusIcon,
|
|
23
|
-
ReloadIcon,
|
|
24
|
-
TrashIcon,
|
|
25
14
|
ViewVerticalIcon
|
|
26
15
|
} from "@radix-ui/react-icons";
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
import {
|
|
17
|
+
useCallback,
|
|
18
|
+
useContext as useContext3,
|
|
19
|
+
useEffect as useEffect2,
|
|
20
|
+
useMemo as useMemo2,
|
|
21
|
+
useRef as useRef2,
|
|
22
|
+
useState as useState3
|
|
23
|
+
} from "react";
|
|
30
24
|
|
|
31
|
-
// src/client/
|
|
32
|
-
|
|
25
|
+
// src/client/Chat.tsx
|
|
26
|
+
import {
|
|
27
|
+
AssistantRuntimeProvider,
|
|
28
|
+
ComposerPrimitive,
|
|
29
|
+
ThreadPrimitive,
|
|
30
|
+
useLocalRuntime
|
|
31
|
+
} from "@assistant-ui/react";
|
|
32
|
+
import { ArrowUpIcon } from "@radix-ui/react-icons";
|
|
33
|
+
import { useMemo, useRef, useState } from "react";
|
|
34
|
+
|
|
35
|
+
// src/client/ApprovalCard.tsx
|
|
36
|
+
import { ChevronRightIcon, ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
|
37
|
+
|
|
38
|
+
// src/client/format.ts
|
|
39
|
+
var pretty = (v) => {
|
|
33
40
|
try {
|
|
34
|
-
|
|
35
|
-
} catch
|
|
36
|
-
|
|
41
|
+
return JSON.stringify(v, null, 2);
|
|
42
|
+
} catch {
|
|
43
|
+
return String(v);
|
|
37
44
|
}
|
|
38
|
-
}
|
|
45
|
+
};
|
|
46
|
+
var ago = (t) => {
|
|
47
|
+
const s = Math.round((t - Date.now()) / 1e3);
|
|
48
|
+
const rtf = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
49
|
+
const a = Math.abs(s);
|
|
50
|
+
if (a < 60) return rtf.format(s, "second");
|
|
51
|
+
if (a < 3600) return rtf.format(Math.round(s / 60), "minute");
|
|
52
|
+
if (a < 86400) return rtf.format(Math.round(s / 3600), "hour");
|
|
53
|
+
return rtf.format(Math.round(s / 86400), "day");
|
|
54
|
+
};
|
|
55
|
+
var httpUrl = (u) => {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(String(u));
|
|
58
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
39
63
|
|
|
40
64
|
// src/client/results.tsx
|
|
41
65
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -113,366 +137,281 @@ function ResultView({ result }) {
|
|
|
113
137
|
return /* @__PURE__ */ jsx("div", { children: formatCell(result) });
|
|
114
138
|
}
|
|
115
139
|
|
|
116
|
-
// src/client/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
--sa-surface:var(--sideagent-surface,#f5f5f5);
|
|
124
|
-
--sa-accent:var(--sideagent-accent,#171717);
|
|
125
|
-
--sa-accent-fg:var(--sideagent-accent-fg,#ffffff);
|
|
126
|
-
--sa-danger:var(--sideagent-danger,#b42318);
|
|
127
|
-
--sa-radius:var(--sideagent-radius,10px);
|
|
128
|
-
--sa-font:var(--sideagent-font,inherit);
|
|
129
|
-
color-scheme:light;
|
|
140
|
+
// src/client/ApprovalCard.tsx
|
|
141
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
142
|
+
function summaryFields(args) {
|
|
143
|
+
const a = args && typeof args === "object" ? args : {};
|
|
144
|
+
const body = a.body && typeof a.body === "object" && !Array.isArray(a.body) ? a.body : {};
|
|
145
|
+
const { body: _body, ...rest } = a;
|
|
146
|
+
return Object.entries({ ...rest, ...body }).filter(([, v]) => ["string", "number", "boolean"].includes(typeof v)).map(([k, v]) => [humanize(k), String(v)]);
|
|
130
147
|
}
|
|
131
|
-
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
function ApprovalCard({ event, onDecide }) {
|
|
149
|
+
const action = humanize(event.operationId).toLowerCase();
|
|
150
|
+
const fields = summaryFields(event.args);
|
|
151
|
+
return /* @__PURE__ */ jsxs2("div", { className: "sa-approval", role: "alertdialog", "aria-label": "Approval needed", children: [
|
|
152
|
+
/* @__PURE__ */ jsxs2("strong", { className: "sa-approval-title", children: [
|
|
153
|
+
/* @__PURE__ */ jsx2(ExclamationTriangleIcon, {}),
|
|
154
|
+
" Confirm before I continue"
|
|
155
|
+
] }),
|
|
156
|
+
/* @__PURE__ */ jsxs2("p", { className: "sa-approval-text", children: [
|
|
157
|
+
"I'd like to ",
|
|
158
|
+
/* @__PURE__ */ jsx2("strong", { children: action }),
|
|
159
|
+
". Nothing changes until you approve."
|
|
160
|
+
] }),
|
|
161
|
+
fields.length > 0 && /* @__PURE__ */ jsx2("dl", { className: "sa-fields", children: fields.map(([k, v]) => /* @__PURE__ */ jsxs2("div", { className: "sa-field", children: [
|
|
162
|
+
/* @__PURE__ */ jsx2("dt", { children: k }),
|
|
163
|
+
/* @__PURE__ */ jsx2("dd", { children: v })
|
|
164
|
+
] }, k)) }),
|
|
165
|
+
/* @__PURE__ */ jsxs2("details", { className: "sa-details", children: [
|
|
166
|
+
/* @__PURE__ */ jsxs2("summary", { children: [
|
|
167
|
+
/* @__PURE__ */ jsx2(ChevronRightIcon, {}),
|
|
168
|
+
" Details"
|
|
169
|
+
] }),
|
|
170
|
+
/* @__PURE__ */ jsxs2("pre", { children: [
|
|
171
|
+
event.method,
|
|
172
|
+
" ",
|
|
173
|
+
event.path,
|
|
174
|
+
"\n",
|
|
175
|
+
pretty(event.args)
|
|
176
|
+
] })
|
|
177
|
+
] }),
|
|
178
|
+
/* @__PURE__ */ jsxs2("div", { className: "sa-row", children: [
|
|
179
|
+
/* @__PURE__ */ jsx2("button", { className: "sa-btn sa-btn-primary", onClick: () => onDecide("approve"), children: "Approve" }),
|
|
180
|
+
/* @__PURE__ */ jsx2("button", { className: "sa-btn", onClick: () => onDecide("reject"), children: "Reject" })
|
|
181
|
+
] })
|
|
182
|
+
] });
|
|
136
183
|
}
|
|
137
|
-
:is(.dark,[data-theme=dark],[data-bs-theme=dark]) .sa-root[data-theme=auto],
|
|
138
|
-
.sa-root[data-theme=dark]{
|
|
139
|
-
--sa-bg:var(--sideagent-bg,#171717);--sa-fg:var(--sideagent-fg,#f5f5f5);--sa-muted:var(--sideagent-muted,#a3a3a3);
|
|
140
|
-
--sa-border:var(--sideagent-border,#2e2e2e);--sa-surface:var(--sideagent-surface,#262626);
|
|
141
|
-
--sa-accent:var(--sideagent-accent,#f5f5f5);--sa-accent-fg:var(--sideagent-accent-fg,#171717);color-scheme:dark}
|
|
142
|
-
.sa-root{font-family:var(--sa-font);font-size:14px;line-height:1.5;color:var(--sa-fg);box-sizing:border-box;-webkit-font-smoothing:antialiased}
|
|
143
|
-
.sa-root *{box-sizing:border-box}
|
|
144
|
-
.sa-panel{display:flex;flex-direction:column;background:var(--sa-bg);border:1px solid var(--sa-border);overflow:hidden}
|
|
145
|
-
.sa-floating .sa-panel{position:fixed;right:16px;bottom:80px;width:min(400px,calc(100vw - 32px));height:min(600px,calc(100vh - 104px));border-radius:calc(var(--sa-radius) + 4px);box-shadow:0 8px 30px rgba(0,0,0,.12);z-index:2147483000}
|
|
146
|
-
.sa-sidebar .sa-panel{position:fixed;top:0;right:0;bottom:0;width:min(400px,100vw);border-width:0 0 0 1px;z-index:2147483000}
|
|
147
|
-
.sa-shell{display:flex;height:100dvh;width:100%;min-width:0}
|
|
148
|
-
.sa-shell-main{flex:1;min-width:0;overflow:auto}
|
|
149
|
-
.sa-docked{display:contents}
|
|
150
|
-
.sa-docked .sa-panel{position:relative;flex:none;max-width:70vw;height:100%;border-width:0 0 0 1px;z-index:auto}
|
|
151
|
-
.sa-resizer{position:absolute;top:0;bottom:0;left:-3px;width:7px;cursor:col-resize;touch-action:none;z-index:1}
|
|
152
|
-
.sa-resizer::after{content:"";position:absolute;top:0;bottom:0;left:3px;width:2px;background:transparent;transition:background .15s}
|
|
153
|
-
.sa-resizer:hover::after,.sa-resizer:focus-visible::after,.sa-resizing .sa-resizer::after{background:var(--sa-muted)}
|
|
154
|
-
.sa-resizer:focus-visible{outline:none}
|
|
155
|
-
.sa-resizing{user-select:none;cursor:col-resize}
|
|
156
|
-
.sa-fab{position:fixed;right:16px;bottom:16px;width:48px;height:48px;border-radius:50%;border:0;background:var(--sa-accent);color:var(--sa-accent-fg);cursor:pointer;display:grid;place-items:center;box-shadow:0 4px 14px rgba(0,0,0,.2);z-index:2147483000}
|
|
157
|
-
.sa-header{display:flex;align-items:center;gap:6px;padding:10px 12px;border-bottom:1px solid var(--sa-border)}
|
|
158
|
-
.sa-title{font-weight:600;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
159
|
-
.sa-btn{border:1px solid var(--sa-border);background:transparent;color:var(--sa-fg);border-radius:calc(var(--sa-radius) - 2px);padding:4px 9px;min-height:28px;font:inherit;font-size:12px;cursor:pointer}
|
|
160
|
-
.sa-btn:hover{background:var(--sa-surface)}
|
|
161
|
-
.sa-btn[aria-pressed=true]{background:var(--sa-surface);border-color:var(--sa-muted)}
|
|
162
|
-
.sa-btn-primary{background:var(--sa-accent);color:var(--sa-accent-fg);border-color:var(--sa-accent)}
|
|
163
|
-
.sa-btn-primary:hover{background:var(--sa-accent);opacity:.88}
|
|
164
|
-
.sa-btn:disabled{opacity:.5;cursor:not-allowed}
|
|
165
|
-
.sa-root :is(button,textarea):focus-visible{outline:2px solid var(--sa-fg);outline-offset:2px}
|
|
166
|
-
.sa-viewport{flex:1;overflow-y:auto;padding:14px 12px;display:flex;flex-direction:column;gap:10px}
|
|
167
|
-
.sa-empty{color:var(--sa-muted);text-align:center;margin:auto;padding:24px}
|
|
168
|
-
.sa-msg{max-width:90%;white-space:pre-wrap;word-break:break-word}
|
|
169
|
-
.sa-user{align-self:flex-end;background:var(--sa-surface);padding:7px 12px;border-radius:calc(var(--sa-radius) + 4px)}
|
|
170
|
-
.sa-assistant{align-self:flex-start}
|
|
171
|
-
.sa-md{white-space:normal;min-width:0}
|
|
172
|
-
.sa-md>:first-child{margin-top:0}.sa-md>:last-child{margin-bottom:0}
|
|
173
|
-
.sa-md :is(p,ul,ol,pre,table,blockquote){margin:0 0 .6em}
|
|
174
|
-
.sa-md :is(ul,ol){padding-left:1.4em}
|
|
175
|
-
.sa-md :is(h1,h2,h3,h4){font-size:1em;font-weight:600;margin:.8em 0 .3em}
|
|
176
|
-
.sa-md a{color:inherit;text-decoration:underline}
|
|
177
|
-
.sa-md code{font-family:ui-monospace,monospace;font-size:.9em;background:var(--sa-surface);padding:1px 4px;border-radius:4px}
|
|
178
|
-
.sa-md pre{background:var(--sa-surface);padding:8px 10px;border-radius:calc(var(--sa-radius) - 2px);overflow-x:auto}
|
|
179
|
-
.sa-md pre code{background:none;padding:0}
|
|
180
|
-
.sa-md blockquote{border-left:3px solid var(--sa-border);padding-left:10px;color:var(--sa-muted);margin-left:0}
|
|
181
|
-
.sa-md table{border-collapse:collapse;display:block;overflow-x:auto}
|
|
182
|
-
.sa-md :is(th,td){border:1px solid var(--sa-border);padding:3px 8px;text-align:left}
|
|
183
|
-
.sa-icon-btn{display:inline-grid;place-items:center;min-width:28px;padding:4px}
|
|
184
|
-
.sa-icon-btn svg{width:15px;height:15px}
|
|
185
|
-
.sa-tool{align-self:stretch;border:1px solid var(--sa-border);border-radius:var(--sa-radius);padding:8px 10px;font-size:13px;min-width:0}
|
|
186
|
-
.sa-tool-head{display:flex;align-items:center;gap:6px;font-weight:600;font-size:12.5px;margin-bottom:6px}
|
|
187
|
-
.sa-tool[data-state=done] .sa-tool-head svg{color:var(--sa-muted)}
|
|
188
|
-
.sa-tool[data-state=error] .sa-tool-head,.sa-tool[data-state=rejected] .sa-tool-head{color:var(--sa-danger)}
|
|
189
|
-
.sa-spin{animation:sa-rot 1s linear infinite}
|
|
190
|
-
@keyframes sa-rot{to{transform:rotate(360deg)}}
|
|
191
|
-
@media (prefers-reduced-motion:reduce){.sa-spin{animation:none}}
|
|
192
|
-
.sa-table-wrap{overflow:auto;max-height:240px;border:1px solid var(--sa-border);border-radius:calc(var(--sa-radius) - 2px)}
|
|
193
|
-
.sa-table{border-collapse:collapse;width:100%;font-size:12.5px}
|
|
194
|
-
.sa-table th,.sa-table td{padding:5px 9px;text-align:left;white-space:nowrap;border-bottom:1px solid var(--sa-border)}
|
|
195
|
-
.sa-table th{position:sticky;top:0;background:var(--sa-surface);font-weight:600;color:var(--sa-muted)}
|
|
196
|
-
.sa-table tr:last-child td{border-bottom:0}
|
|
197
|
-
.sa-table .sa-num{text-align:right;font-variant-numeric:tabular-nums}
|
|
198
|
-
.sa-more{color:var(--sa-muted);font-size:12px;margin-top:4px}
|
|
199
|
-
.sa-fields{margin:0;display:grid;gap:4px}
|
|
200
|
-
.sa-field{display:grid;grid-template-columns:minmax(80px,35%) 1fr;gap:8px}
|
|
201
|
-
.sa-field dt{color:var(--sa-muted)}
|
|
202
|
-
.sa-field dd{margin:0;word-break:break-word}
|
|
203
|
-
.sa-list{margin:0;padding-left:18px}
|
|
204
|
-
.sa-sources{margin:0;padding:0;list-style:none;display:grid;gap:4px}
|
|
205
|
-
.sa-sources li{display:flex;align-items:center;gap:6px}
|
|
206
|
-
.sa-sources svg{flex:none;color:var(--sa-muted)}
|
|
207
|
-
.sa-sources a{color:inherit;text-decoration:underline;text-underline-offset:2px;display:inline-flex;align-items:center;gap:4px}
|
|
208
|
-
.sa-details{margin-top:6px}
|
|
209
|
-
.sa-details summary{cursor:pointer;color:var(--sa-muted);font-size:12px;list-style:none;display:flex;align-items:center;gap:2px}
|
|
210
|
-
.sa-details summary::-webkit-details-marker{display:none}
|
|
211
|
-
.sa-details[open] summary svg{transform:rotate(90deg)}
|
|
212
|
-
.sa-details pre{margin:6px 0 0;white-space:pre-wrap;word-break:break-word;max-height:160px;overflow:auto;font-size:11.5px}
|
|
213
|
-
.sa-approval-title{display:flex;align-items:center;gap:6px}
|
|
214
|
-
.sa-approval{margin:0 12px 8px;border:1px solid var(--sa-fg);border-radius:var(--sa-radius);padding:10px;background:var(--sa-surface)}
|
|
215
|
-
.sa-approval pre{margin:6px 0;white-space:pre-wrap;word-break:break-word;max-height:120px;overflow:auto;font-size:12px}
|
|
216
|
-
.sa-approval-text{margin:6px 0}
|
|
217
|
-
.sa-approval .sa-fields{margin:0 0 6px}
|
|
218
|
-
.sa-approval .sa-row{margin-top:8px}
|
|
219
|
-
.sa-row{display:flex;gap:8px}
|
|
220
|
-
.sa-composer{display:flex;flex-direction:column;gap:4px;margin:0 12px 12px;padding:10px 10px 8px 14px;border:1px solid var(--sa-border);border-radius:18px;background:var(--sa-surface)}
|
|
221
|
-
.sa-input{width:100%;resize:none;border:0;background:transparent;padding:2px 0;font:inherit;color:inherit;max-height:160px}
|
|
222
|
-
.sa-root .sa-input:focus-visible{outline:none}
|
|
223
|
-
.sa-input::placeholder{color:var(--sa-muted)}
|
|
224
|
-
.sa-composer-bar{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
|
225
|
-
.sa-hint{color:var(--sa-muted);font-size:12px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
226
|
-
.sa-send{flex:none;display:grid;place-items:center;width:32px;height:32px;border-radius:50%;border:0;background:var(--sa-accent);color:var(--sa-accent-fg);cursor:pointer}
|
|
227
|
-
.sa-send:hover{opacity:.88}
|
|
228
|
-
.sa-send:disabled{opacity:.35;cursor:not-allowed}
|
|
229
|
-
.sa-send svg{width:16px;height:16px}
|
|
230
|
-
.sa-chatwrap{display:flex;flex-direction:column;flex:1;min-height:0}
|
|
231
|
-
.sa-chatwrap[hidden]{display:none}
|
|
232
|
-
.sa-history{gap:6px}
|
|
233
|
-
.sa-back{align-self:flex-start;display:inline-flex;align-items:center;gap:4px;margin-bottom:4px}
|
|
234
|
-
.sa-thread{display:flex;gap:6px;align-items:stretch}
|
|
235
|
-
.sa-thread-open{flex:1;min-width:0;text-align:left;border:1px solid var(--sa-border);background:transparent;color:inherit;border-radius:var(--sa-radius);padding:8px 10px;font:inherit;cursor:pointer;display:flex;flex-direction:column;gap:2px}
|
|
236
|
-
.sa-thread-open:hover{background:var(--sa-surface)}
|
|
237
|
-
.sa-thread-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
238
|
-
.sa-error{color:var(--sa-danger)}
|
|
239
|
-
`;
|
|
240
184
|
|
|
241
|
-
// src/client/
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
});
|
|
250
|
-
if (!res.ok || !res.body) {
|
|
251
|
-
let message = `Request failed (${res.status})`;
|
|
252
|
-
try {
|
|
253
|
-
const j = await res.json();
|
|
254
|
-
if (j?.error) message = String(j.error);
|
|
255
|
-
} catch {
|
|
256
|
-
}
|
|
257
|
-
yield { type: "error", code: `http_${res.status}`, message };
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
const reader = res.body.getReader();
|
|
261
|
-
const decoder = new TextDecoder();
|
|
262
|
-
let buf = "";
|
|
263
|
-
for (; ; ) {
|
|
264
|
-
const { done, value } = await reader.read();
|
|
265
|
-
if (done) break;
|
|
266
|
-
buf += decoder.decode(value, { stream: true });
|
|
267
|
-
let i;
|
|
268
|
-
while ((i = buf.indexOf("\n\n")) >= 0) {
|
|
269
|
-
const block = buf.slice(0, i);
|
|
270
|
-
buf = buf.slice(i + 2);
|
|
271
|
-
if (block.startsWith("data: ")) yield JSON.parse(block.slice(6));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
async function callJson(body, opts) {
|
|
276
|
-
const res = await fetch(opts.endpoint, {
|
|
277
|
-
method: "POST",
|
|
278
|
-
credentials: opts.credentials ?? "same-origin",
|
|
279
|
-
headers: { "content-type": "application/json", ...opts.headers },
|
|
280
|
-
body: JSON.stringify(body)
|
|
281
|
-
});
|
|
282
|
-
let data = null;
|
|
283
|
-
try {
|
|
284
|
-
data = await res.json();
|
|
285
|
-
} catch {
|
|
286
|
-
}
|
|
287
|
-
return { status: res.status, data };
|
|
185
|
+
// src/client/context.ts
|
|
186
|
+
import { createContext, useContext } from "react";
|
|
187
|
+
var DockContext = createContext(false);
|
|
188
|
+
var AgentContext = createContext(null);
|
|
189
|
+
function useAgent() {
|
|
190
|
+
const ctx = useContext(AgentContext);
|
|
191
|
+
if (!ctx) throw new Error("useAgent must be used inside <AgentProvider>");
|
|
192
|
+
return ctx;
|
|
288
193
|
}
|
|
194
|
+
var RenderersContext = createContext(void 0);
|
|
289
195
|
|
|
290
|
-
// src/client/
|
|
291
|
-
|
|
292
|
-
var MODE_KEY = "sideagent:mode";
|
|
293
|
-
var WIDTH_KEY = "sideagent:width";
|
|
294
|
-
var threadKey = (endpoint) => `sideagent:thread:${endpoint}`;
|
|
295
|
-
function storeGet(k) {
|
|
296
|
-
try {
|
|
297
|
-
return localStorage.getItem(k);
|
|
298
|
-
} catch {
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
function storeSet(k, v) {
|
|
196
|
+
// src/client/hooks.ts
|
|
197
|
+
function safely(fn) {
|
|
303
198
|
try {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
199
|
+
fn?.();
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.error("[sideagent] hook threw:", err);
|
|
307
202
|
}
|
|
308
203
|
}
|
|
309
|
-
function toInitialMessages(messages) {
|
|
310
|
-
return (messages ?? []).map(
|
|
311
|
-
(m) => m.role === "user" ? { role: "user", content: m.parts.map((p) => ({ type: "text", text: p.type === "text" ? p.text : "" })) } : {
|
|
312
|
-
role: "assistant",
|
|
313
|
-
content: m.parts.map(
|
|
314
|
-
(p) => p.type === "text" ? { type: "text", text: p.text } : { type: "tool-call", toolCallId: p.callId, toolName: p.operationId, args: p.args ?? {}, argsText: pretty(p.args), result: p.result }
|
|
315
|
-
),
|
|
316
|
-
status: { type: "complete", reason: "stop" }
|
|
317
|
-
}
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
204
|
function fireComplete(hooks, ev) {
|
|
321
205
|
const oe = { operationId: ev.operationId, status: ev.status, args: ev.args, result: ev.result };
|
|
322
206
|
safely(() => hooks?.onAnyOperation?.(oe));
|
|
323
207
|
safely(() => hooks?.onOperationComplete?.[ev.operationId]?.(oe));
|
|
324
208
|
for (const tag of ev.invalidates) safely(() => hooks?.onInvalidate?.(tag, oe));
|
|
325
209
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (!ctx) throw new Error("useAgent must be used inside <AgentProvider>");
|
|
345
|
-
return ctx;
|
|
346
|
-
}
|
|
347
|
-
function AgentProvider({ children, className, style, ...chat }) {
|
|
348
|
-
const [open, setOpen] = useState(chat.defaultOpen ?? false);
|
|
349
|
-
const controls = useMemo(() => ({ open, setOpen }), [open]);
|
|
350
|
-
return /* @__PURE__ */ jsx2(AgentContext.Provider, { value: controls, children: /* @__PURE__ */ jsx2(DockContext.Provider, { value: true, children: /* @__PURE__ */ jsxs2("div", { className: `sa-shell${className ? ` ${className}` : ""}`, style, children: [
|
|
351
|
-
/* @__PURE__ */ jsx2("div", { className: "sa-shell-main", children }),
|
|
352
|
-
/* @__PURE__ */ jsx2(AgentChat, { ...chat })
|
|
353
|
-
] }) }) });
|
|
354
|
-
}
|
|
355
|
-
function readMode(fallback) {
|
|
356
|
-
try {
|
|
357
|
-
const v = localStorage.getItem(MODE_KEY);
|
|
358
|
-
return v && MODES.includes(v) ? v : fallback;
|
|
359
|
-
} catch {
|
|
360
|
-
return fallback;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
var pretty = (v) => {
|
|
364
|
-
try {
|
|
365
|
-
return JSON.stringify(v, null, 2);
|
|
366
|
-
} catch {
|
|
367
|
-
return String(v);
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
var Text = ({ text }) => /* @__PURE__ */ jsx2("span", { children: text });
|
|
371
|
-
var RenderersContext = createContext(void 0);
|
|
372
|
-
var httpUrl = (u) => {
|
|
373
|
-
try {
|
|
374
|
-
const url = new URL(String(u));
|
|
375
|
-
return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
|
|
376
|
-
} catch {
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
};
|
|
210
|
+
|
|
211
|
+
// src/client/Messages.tsx
|
|
212
|
+
import { MessagePrimitive } from "@assistant-ui/react";
|
|
213
|
+
import Markdown from "react-markdown";
|
|
214
|
+
import remarkGfm from "remark-gfm";
|
|
215
|
+
|
|
216
|
+
// src/client/ToolCard.tsx
|
|
217
|
+
import {
|
|
218
|
+
CheckCircledIcon,
|
|
219
|
+
ChevronRightIcon as ChevronRightIcon2,
|
|
220
|
+
CrossCircledIcon,
|
|
221
|
+
ExternalLinkIcon,
|
|
222
|
+
FileTextIcon,
|
|
223
|
+
ReloadIcon
|
|
224
|
+
} from "@radix-ui/react-icons";
|
|
225
|
+
import { useContext as useContext2 } from "react";
|
|
226
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
227
|
+
var SEARCH_TOOL = "search_knowledge";
|
|
380
228
|
function Sources({ result }) {
|
|
381
229
|
const sources = result?.sources ?? [];
|
|
382
|
-
if (typeof result?.error === "string") return /* @__PURE__ */
|
|
383
|
-
if (!sources.length) return /* @__PURE__ */
|
|
384
|
-
return /* @__PURE__ */
|
|
230
|
+
if (typeof result?.error === "string") return /* @__PURE__ */ jsx3("div", { className: "sa-error", children: result.error });
|
|
231
|
+
if (!sources.length) return /* @__PURE__ */ jsx3("div", { className: "sa-more", children: "No relevant sources found." });
|
|
232
|
+
return /* @__PURE__ */ jsx3("ul", { className: "sa-sources", children: sources.map((s) => {
|
|
385
233
|
const href = s.url ? httpUrl(s.url) : null;
|
|
386
|
-
return /* @__PURE__ */
|
|
387
|
-
/* @__PURE__ */
|
|
388
|
-
href ? /* @__PURE__ */
|
|
234
|
+
return /* @__PURE__ */ jsxs3("li", { children: [
|
|
235
|
+
/* @__PURE__ */ jsx3(FileTextIcon, {}),
|
|
236
|
+
href ? /* @__PURE__ */ jsxs3("a", { href, target: "_blank", rel: "noopener noreferrer", children: [
|
|
389
237
|
s.title,
|
|
390
238
|
" ",
|
|
391
|
-
/* @__PURE__ */
|
|
392
|
-
] }) : /* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ jsx3(ExternalLinkIcon, {})
|
|
240
|
+
] }) : /* @__PURE__ */ jsx3("span", { children: s.title })
|
|
393
241
|
] }, s.id);
|
|
394
242
|
}) });
|
|
395
243
|
}
|
|
396
244
|
function ToolCard(props) {
|
|
397
|
-
const renderers =
|
|
245
|
+
const renderers = useContext2(RenderersContext);
|
|
398
246
|
const { toolName, args, result } = props;
|
|
399
247
|
const rejected = result?.rejected === true;
|
|
400
248
|
const failed = !rejected && typeof result?.error === "string";
|
|
401
249
|
const running = result === void 0;
|
|
402
250
|
const custom = renderers?.[toolName];
|
|
403
|
-
return /* @__PURE__ */
|
|
404
|
-
/* @__PURE__ */
|
|
405
|
-
running ? /* @__PURE__ */
|
|
406
|
-
/* @__PURE__ */
|
|
251
|
+
return /* @__PURE__ */ jsxs3("div", { className: "sa-tool", "data-state": running ? "running" : rejected ? "rejected" : failed ? "error" : "done", children: [
|
|
252
|
+
/* @__PURE__ */ jsxs3("div", { className: "sa-tool-head", children: [
|
|
253
|
+
running ? /* @__PURE__ */ jsx3(ReloadIcon, { className: "sa-spin" }) : rejected || failed ? /* @__PURE__ */ jsx3(CrossCircledIcon, {}) : /* @__PURE__ */ jsx3(CheckCircledIcon, {}),
|
|
254
|
+
/* @__PURE__ */ jsx3("span", { children: toolName === SEARCH_TOOL ? "Sources" : humanize(toolName) })
|
|
407
255
|
] }),
|
|
408
|
-
!running && (custom ? custom({ operationId: toolName, args, result }) : toolName ===
|
|
409
|
-
/* @__PURE__ */
|
|
410
|
-
/* @__PURE__ */
|
|
411
|
-
/* @__PURE__ */
|
|
256
|
+
!running && (custom ? custom({ operationId: toolName, args, result }) : toolName === SEARCH_TOOL ? /* @__PURE__ */ jsx3(Sources, { result }) : /* @__PURE__ */ jsx3(ResultView, { result })),
|
|
257
|
+
/* @__PURE__ */ jsxs3("details", { className: "sa-details", children: [
|
|
258
|
+
/* @__PURE__ */ jsxs3("summary", { children: [
|
|
259
|
+
/* @__PURE__ */ jsx3(ChevronRightIcon2, {}),
|
|
412
260
|
" Details"
|
|
413
261
|
] }),
|
|
414
|
-
/* @__PURE__ */
|
|
415
|
-
!running && /* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ jsx3("pre", { children: pretty(args) }),
|
|
263
|
+
!running && /* @__PURE__ */ jsx3("pre", { children: pretty(result) })
|
|
416
264
|
] })
|
|
417
265
|
] });
|
|
418
266
|
}
|
|
267
|
+
|
|
268
|
+
// src/client/Messages.tsx
|
|
269
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
270
|
+
var Text = ({ text }) => /* @__PURE__ */ jsx4("span", { children: text });
|
|
419
271
|
var mdComponents = {
|
|
420
|
-
a: ({ node: _n, ...p }) => /* @__PURE__ */
|
|
272
|
+
a: ({ node: _n, ...p }) => /* @__PURE__ */ jsx4("a", { ...p, target: "_blank", rel: "noopener noreferrer nofollow" })
|
|
421
273
|
};
|
|
422
|
-
var UserMessage = () => /* @__PURE__ */
|
|
423
|
-
var AssistantMessage = () => /* @__PURE__ */
|
|
274
|
+
var UserMessage = () => /* @__PURE__ */ jsx4(MessagePrimitive.Root, { className: "sa-msg sa-user", children: /* @__PURE__ */ jsx4(MessagePrimitive.Parts, { components: { Text } }) });
|
|
275
|
+
var AssistantMessage = () => /* @__PURE__ */ jsx4(MessagePrimitive.Root, { style: { display: "contents" }, children: /* @__PURE__ */ jsx4(
|
|
424
276
|
MessagePrimitive.Parts,
|
|
425
277
|
{
|
|
426
278
|
components: {
|
|
427
|
-
Text: ({ text }) => text ? /* @__PURE__ */
|
|
279
|
+
Text: ({ text }) => text ? /* @__PURE__ */ jsx4("div", { className: "sa-msg sa-assistant sa-md", children: /* @__PURE__ */ jsx4(Markdown, { remarkPlugins: [remarkGfm], components: mdComponents, children: text }) }) : null,
|
|
428
280
|
tools: { Fallback: ToolCard }
|
|
429
281
|
}
|
|
430
282
|
}
|
|
431
|
-
) });
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
283
|
+
) });
|
|
284
|
+
|
|
285
|
+
// src/client/parts.ts
|
|
286
|
+
function upsertTool(parts, callId, patch) {
|
|
287
|
+
const i = parts.findIndex((p) => p.type === "tool-call" && p.toolCallId === callId);
|
|
288
|
+
if (i < 0)
|
|
289
|
+
return [...parts, { type: "tool-call", toolCallId: callId, toolName: "", args: {}, argsText: "{}", ...patch }];
|
|
290
|
+
return parts.map((p, j) => j === i ? { ...p, ...patch } : p);
|
|
291
|
+
}
|
|
292
|
+
var argsOf = (v) => v ?? {};
|
|
293
|
+
function applyEvent(parts, ev) {
|
|
294
|
+
switch (ev.type) {
|
|
295
|
+
case "text.delta": {
|
|
296
|
+
const tail = parts[parts.length - 1];
|
|
297
|
+
if (tail?.type === "text") return [...parts.slice(0, -1), { type: "text", text: tail.text + ev.text }];
|
|
298
|
+
return [...parts, { type: "text", text: ev.text }];
|
|
299
|
+
}
|
|
300
|
+
case "approval.request":
|
|
301
|
+
return upsertTool(parts, ev.callId, {
|
|
302
|
+
toolName: ev.operationId,
|
|
303
|
+
args: argsOf(ev.args),
|
|
304
|
+
argsText: pretty(ev.args)
|
|
305
|
+
});
|
|
306
|
+
case "tool.start":
|
|
307
|
+
return upsertTool(parts, ev.callId, { toolName: ev.operationId });
|
|
308
|
+
case "tool.rejected":
|
|
309
|
+
return upsertTool(parts, ev.callId, { toolName: ev.operationId, result: { rejected: true } });
|
|
310
|
+
case "tool.complete":
|
|
311
|
+
return upsertTool(parts, ev.callId, {
|
|
312
|
+
toolName: ev.operationId,
|
|
313
|
+
args: argsOf(ev.args),
|
|
314
|
+
argsText: pretty(ev.args),
|
|
315
|
+
result: ev.result ?? null
|
|
316
|
+
});
|
|
317
|
+
case "error":
|
|
318
|
+
return [...parts, { type: "text", text: `\u26A0 ${ev.message}` }];
|
|
319
|
+
default:
|
|
320
|
+
return [...parts];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function toInitialMessages(messages) {
|
|
324
|
+
return (messages ?? []).map(
|
|
325
|
+
(m) => m.role === "user" ? {
|
|
326
|
+
role: "user",
|
|
327
|
+
content: m.parts.map((p) => ({ type: "text", text: p.type === "text" ? p.text : "" }))
|
|
328
|
+
} : {
|
|
329
|
+
role: "assistant",
|
|
330
|
+
content: m.parts.map(
|
|
331
|
+
(p) => p.type === "text" ? { type: "text", text: p.text } : {
|
|
332
|
+
type: "tool-call",
|
|
333
|
+
toolCallId: p.callId,
|
|
334
|
+
toolName: p.operationId,
|
|
335
|
+
args: argsOf(p.args),
|
|
336
|
+
argsText: pretty(p.args),
|
|
337
|
+
result: p.result
|
|
338
|
+
}
|
|
339
|
+
),
|
|
340
|
+
status: { type: "complete", reason: "stop" }
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/client/transport.ts
|
|
346
|
+
async function requestHeaders(opts) {
|
|
347
|
+
const dynamic = opts.getHeaders ? await opts.getHeaders() : {};
|
|
348
|
+
return { "content-type": "application/json", ...dynamic, ...opts.headers };
|
|
349
|
+
}
|
|
350
|
+
async function* streamChat(body, opts) {
|
|
351
|
+
let headers;
|
|
352
|
+
try {
|
|
353
|
+
headers = await requestHeaders(opts);
|
|
354
|
+
} catch {
|
|
355
|
+
yield { type: "error", code: "headers_failed", message: "Could not get credentials. Sign in again." };
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const res = await fetch(opts.endpoint, {
|
|
359
|
+
method: "POST",
|
|
360
|
+
credentials: opts.credentials ?? "same-origin",
|
|
361
|
+
headers,
|
|
362
|
+
body: JSON.stringify(body),
|
|
363
|
+
signal: opts.signal
|
|
364
|
+
});
|
|
365
|
+
if (!res.ok || !res.body) {
|
|
366
|
+
let message = `Request failed (${res.status})`;
|
|
367
|
+
try {
|
|
368
|
+
const j = await res.json();
|
|
369
|
+
if (j?.error) message = String(j.error);
|
|
370
|
+
} catch {
|
|
371
|
+
}
|
|
372
|
+
yield { type: "error", code: `http_${res.status}`, message };
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const reader = res.body.getReader();
|
|
376
|
+
const decoder = new TextDecoder();
|
|
377
|
+
let buf = "";
|
|
378
|
+
for (; ; ) {
|
|
379
|
+
const { done, value } = await reader.read();
|
|
380
|
+
if (done) break;
|
|
381
|
+
buf += decoder.decode(value, { stream: true });
|
|
382
|
+
let i;
|
|
383
|
+
while ((i = buf.indexOf("\n\n")) >= 0) {
|
|
384
|
+
const block = buf.slice(0, i);
|
|
385
|
+
buf = buf.slice(i + 2);
|
|
386
|
+
if (block.startsWith("data: ")) yield JSON.parse(block.slice(6));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
437
389
|
}
|
|
438
|
-
function
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
/* @__PURE__ */ jsx2(ChevronRightIcon, {}),
|
|
458
|
-
" Details"
|
|
459
|
-
] }),
|
|
460
|
-
/* @__PURE__ */ jsxs2("pre", { children: [
|
|
461
|
-
event.method,
|
|
462
|
-
" ",
|
|
463
|
-
event.path,
|
|
464
|
-
"\n",
|
|
465
|
-
pretty(event.args)
|
|
466
|
-
] })
|
|
467
|
-
] }),
|
|
468
|
-
/* @__PURE__ */ jsxs2("div", { className: "sa-row", children: [
|
|
469
|
-
/* @__PURE__ */ jsx2("button", { className: "sa-btn sa-btn-primary", onClick: () => onDecide("approve"), children: "Approve" }),
|
|
470
|
-
/* @__PURE__ */ jsx2("button", { className: "sa-btn", onClick: () => onDecide("reject"), children: "Reject" })
|
|
471
|
-
] })
|
|
472
|
-
] });
|
|
390
|
+
async function callJson(body, opts) {
|
|
391
|
+
let headers;
|
|
392
|
+
try {
|
|
393
|
+
headers = await requestHeaders(opts);
|
|
394
|
+
} catch {
|
|
395
|
+
return { status: 0, data: null };
|
|
396
|
+
}
|
|
397
|
+
const res = await fetch(opts.endpoint, {
|
|
398
|
+
method: "POST",
|
|
399
|
+
credentials: opts.credentials ?? "same-origin",
|
|
400
|
+
headers,
|
|
401
|
+
body: JSON.stringify(body)
|
|
402
|
+
});
|
|
403
|
+
let data = null;
|
|
404
|
+
try {
|
|
405
|
+
data = await res.json();
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
return { status: res.status, data };
|
|
473
409
|
}
|
|
410
|
+
|
|
411
|
+
// src/client/Chat.tsx
|
|
412
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
474
413
|
function Chat(props) {
|
|
475
|
-
const { endpoint, hooks, credentials, headers } = props;
|
|
414
|
+
const { endpoint, hooks, credentials, headers, getHeaders } = props;
|
|
476
415
|
const latest = useRef(props);
|
|
477
416
|
latest.current = props;
|
|
478
417
|
const threadId = useRef(props.session.threadId);
|
|
@@ -485,53 +424,31 @@ function Chat(props) {
|
|
|
485
424
|
const last = [...messages].reverse().find((m) => m.role === "user");
|
|
486
425
|
const text = (last?.content ?? []).map((c) => c.type === "text" ? c.text : "").join("\n").trim();
|
|
487
426
|
const ctx = typeof p.context === "function" ? p.context() : p.context;
|
|
488
|
-
|
|
489
|
-
const upsertTool = (callId, patch) => {
|
|
490
|
-
let part = parts.find((x) => x.type === "tool-call" && x.toolCallId === callId);
|
|
491
|
-
if (!part) {
|
|
492
|
-
part = { type: "tool-call", toolCallId: callId, toolName: "", args: {}, argsText: "{}" };
|
|
493
|
-
parts.push(part);
|
|
494
|
-
}
|
|
495
|
-
Object.assign(part, patch);
|
|
496
|
-
};
|
|
427
|
+
let parts = [];
|
|
497
428
|
safely(p.hooks?.onTurnStart);
|
|
498
429
|
let body = { threadId: threadId.current, message: text, context: ctx };
|
|
499
430
|
try {
|
|
500
431
|
for (; ; ) {
|
|
501
432
|
let pending;
|
|
502
|
-
for await (const ev of streamChat(body, {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
upsertTool(ev.callId, { toolName: ev.operationId });
|
|
520
|
-
break;
|
|
521
|
-
case "tool.rejected":
|
|
522
|
-
upsertTool(ev.callId, { toolName: ev.operationId, result: { rejected: true } });
|
|
523
|
-
break;
|
|
524
|
-
case "tool.complete": {
|
|
525
|
-
upsertTool(ev.callId, { toolName: ev.operationId, args: ev.args ?? {}, argsText: pretty(ev.args), result: ev.result ?? null });
|
|
526
|
-
fireComplete(p.hooks, ev);
|
|
527
|
-
break;
|
|
528
|
-
}
|
|
529
|
-
case "error":
|
|
530
|
-
parts.push({ type: "text", text: `\u26A0 ${ev.message}` });
|
|
531
|
-
safely(() => p.hooks?.onError?.({ code: ev.code, message: ev.message }));
|
|
532
|
-
break;
|
|
433
|
+
for await (const ev of streamChat(body, {
|
|
434
|
+
endpoint: p.endpoint,
|
|
435
|
+
credentials: p.credentials,
|
|
436
|
+
headers: p.headers,
|
|
437
|
+
getHeaders: p.getHeaders,
|
|
438
|
+
signal: abortSignal
|
|
439
|
+
})) {
|
|
440
|
+
parts = applyEvent(parts, ev);
|
|
441
|
+
if (ev.type === "turn.start") {
|
|
442
|
+
threadId.current = ev.threadId;
|
|
443
|
+
p.onThread(ev.threadId);
|
|
444
|
+
} else if (ev.type === "approval.request") {
|
|
445
|
+
pending = ev;
|
|
446
|
+
} else if (ev.type === "tool.complete") {
|
|
447
|
+
fireComplete(p.hooks, ev);
|
|
448
|
+
} else if (ev.type === "error") {
|
|
449
|
+
safely(() => p.hooks?.onError?.({ code: ev.code, message: ev.message }));
|
|
533
450
|
}
|
|
534
|
-
yield { content: parts
|
|
451
|
+
yield { content: parts };
|
|
535
452
|
}
|
|
536
453
|
if (!pending) break;
|
|
537
454
|
const req = pending;
|
|
@@ -547,7 +464,7 @@ function Chat(props) {
|
|
|
547
464
|
setApproval(null);
|
|
548
465
|
safely(p.hooks?.onTurnEnd);
|
|
549
466
|
}
|
|
550
|
-
yield { content: parts
|
|
467
|
+
yield { content: parts };
|
|
551
468
|
}
|
|
552
469
|
}),
|
|
553
470
|
// endpoint/hooks/etc. are read through `latest`, so the adapter stays stable.
|
|
@@ -558,7 +475,10 @@ function Chat(props) {
|
|
|
558
475
|
const req = loadedPending;
|
|
559
476
|
if (!req || !threadId.current) return;
|
|
560
477
|
setLoadedPending(null);
|
|
561
|
-
for await (const ev of streamChat(
|
|
478
|
+
for await (const ev of streamChat(
|
|
479
|
+
{ threadId: threadId.current, approval: { token: req.token, decision } },
|
|
480
|
+
{ endpoint, credentials, headers, getHeaders }
|
|
481
|
+
)) {
|
|
562
482
|
if (ev.type === "tool.complete") fireComplete(hooks, ev);
|
|
563
483
|
if (ev.type === "error") safely(() => hooks?.onError?.({ code: ev.code, message: ev.message }));
|
|
564
484
|
}
|
|
@@ -566,25 +486,43 @@ function Chat(props) {
|
|
|
566
486
|
};
|
|
567
487
|
const shownApproval = approval?.event ?? loadedPending;
|
|
568
488
|
const decide = (d) => approval ? approval.decide(d) : void decideLoaded(d);
|
|
569
|
-
return /* @__PURE__ */
|
|
570
|
-
/* @__PURE__ */
|
|
571
|
-
/* @__PURE__ */
|
|
572
|
-
/* @__PURE__ */
|
|
489
|
+
return /* @__PURE__ */ jsx5(RenderersContext.Provider, { value: props.resultRenderers, children: /* @__PURE__ */ jsx5(AssistantRuntimeProvider, { runtime, children: /* @__PURE__ */ jsxs4(ThreadPrimitive.Root, { style: { display: "contents" }, children: [
|
|
490
|
+
/* @__PURE__ */ jsxs4(ThreadPrimitive.Viewport, { className: "sa-viewport", children: [
|
|
491
|
+
/* @__PURE__ */ jsx5(ThreadPrimitive.Empty, { children: /* @__PURE__ */ jsx5("div", { className: "sa-empty", children: "Ask me to look things up or make changes. I will ask before changing anything." }) }),
|
|
492
|
+
/* @__PURE__ */ jsx5(ThreadPrimitive.Messages, { components: { UserMessage, AssistantMessage } })
|
|
573
493
|
] }),
|
|
574
|
-
shownApproval && /* @__PURE__ */
|
|
575
|
-
/* @__PURE__ */
|
|
576
|
-
/* @__PURE__ */
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
494
|
+
shownApproval && /* @__PURE__ */ jsx5(ApprovalCard, { event: shownApproval, onDecide: decide }),
|
|
495
|
+
/* @__PURE__ */ jsxs4(ComposerPrimitive.Root, { className: "sa-composer", children: [
|
|
496
|
+
/* @__PURE__ */ jsx5(
|
|
497
|
+
ComposerPrimitive.Input,
|
|
498
|
+
{
|
|
499
|
+
className: "sa-input",
|
|
500
|
+
rows: 2,
|
|
501
|
+
placeholder: props.placeholder ?? "Message\u2026",
|
|
502
|
+
disabled: !!shownApproval
|
|
503
|
+
}
|
|
504
|
+
),
|
|
505
|
+
/* @__PURE__ */ jsxs4("div", { className: "sa-composer-bar", children: [
|
|
506
|
+
/* @__PURE__ */ jsx5("span", { className: "sa-hint", children: "Enter to send \xB7 Shift+Enter for a new line" }),
|
|
507
|
+
/* @__PURE__ */ jsx5(ComposerPrimitive.Send, { className: "sa-send", "aria-label": "Send", title: "Send", children: /* @__PURE__ */ jsx5(ArrowUpIcon, {}) })
|
|
580
508
|
] })
|
|
581
509
|
] })
|
|
582
510
|
] }) }) });
|
|
583
511
|
}
|
|
512
|
+
|
|
513
|
+
// src/client/HistoryList.tsx
|
|
514
|
+
import { ArrowLeftIcon, TrashIcon } from "@radix-ui/react-icons";
|
|
515
|
+
import { useEffect, useState as useState2 } from "react";
|
|
516
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
584
517
|
function HistoryList(props) {
|
|
585
|
-
const [threads, setThreads] =
|
|
586
|
-
const [error, setError] =
|
|
587
|
-
const opts = {
|
|
518
|
+
const [threads, setThreads] = useState2(null);
|
|
519
|
+
const [error, setError] = useState2(false);
|
|
520
|
+
const opts = {
|
|
521
|
+
endpoint: props.endpoint,
|
|
522
|
+
credentials: props.credentials,
|
|
523
|
+
headers: props.headers,
|
|
524
|
+
getHeaders: props.getHeaders
|
|
525
|
+
};
|
|
588
526
|
useEffect(() => {
|
|
589
527
|
callJson({ action: "threads.list" }, opts).then((r) => r.data?.threads ? setThreads(r.data.threads) : setError(true)).catch(() => setError(true));
|
|
590
528
|
}, []);
|
|
@@ -593,38 +531,244 @@ function HistoryList(props) {
|
|
|
593
531
|
setThreads((t) => t?.filter((x) => x.id !== id) ?? null);
|
|
594
532
|
props.onDeleted(id);
|
|
595
533
|
};
|
|
596
|
-
return /* @__PURE__ */
|
|
597
|
-
/* @__PURE__ */
|
|
598
|
-
/* @__PURE__ */
|
|
534
|
+
return /* @__PURE__ */ jsxs5("div", { className: "sa-viewport sa-history", children: [
|
|
535
|
+
/* @__PURE__ */ jsxs5("button", { className: "sa-btn sa-back", onClick: props.onClose, children: [
|
|
536
|
+
/* @__PURE__ */ jsx6(ArrowLeftIcon, {}),
|
|
599
537
|
" Back to chat"
|
|
600
538
|
] }),
|
|
601
|
-
error && /* @__PURE__ */
|
|
602
|
-
threads === null && !error && /* @__PURE__ */
|
|
603
|
-
threads?.length === 0 && /* @__PURE__ */
|
|
604
|
-
threads?.map((t) => /* @__PURE__ */
|
|
605
|
-
/* @__PURE__ */
|
|
606
|
-
/* @__PURE__ */
|
|
607
|
-
/* @__PURE__ */
|
|
539
|
+
error && /* @__PURE__ */ jsx6("div", { className: "sa-error", children: "Could not load history." }),
|
|
540
|
+
threads === null && !error && /* @__PURE__ */ jsx6("div", { className: "sa-more", children: "Loading\u2026" }),
|
|
541
|
+
threads?.length === 0 && /* @__PURE__ */ jsx6("div", { className: "sa-empty", children: "No conversations yet." }),
|
|
542
|
+
threads?.map((t) => /* @__PURE__ */ jsxs5("div", { className: "sa-thread", children: [
|
|
543
|
+
/* @__PURE__ */ jsxs5("button", { className: "sa-thread-open", onClick: () => props.onOpen(t.id), children: [
|
|
544
|
+
/* @__PURE__ */ jsx6("span", { className: "sa-thread-title", children: t.title }),
|
|
545
|
+
/* @__PURE__ */ jsx6("span", { className: "sa-more", children: ago(t.updatedAt) })
|
|
608
546
|
] }),
|
|
609
|
-
/* @__PURE__ */
|
|
547
|
+
/* @__PURE__ */ jsx6(
|
|
548
|
+
"button",
|
|
549
|
+
{
|
|
550
|
+
className: "sa-btn sa-icon-btn",
|
|
551
|
+
"aria-label": "Delete conversation",
|
|
552
|
+
title: "Delete",
|
|
553
|
+
onClick: () => void remove(t.id),
|
|
554
|
+
children: /* @__PURE__ */ jsx6(TrashIcon, {})
|
|
555
|
+
}
|
|
556
|
+
)
|
|
610
557
|
] }, t.id))
|
|
611
558
|
] });
|
|
612
559
|
}
|
|
560
|
+
|
|
561
|
+
// src/client/storage.ts
|
|
562
|
+
var MODE_KEY = "sideagent:mode";
|
|
563
|
+
var WIDTH_KEY = "sideagent:width";
|
|
564
|
+
var threadKey = (endpoint, storageKey) => `sideagent:thread:${endpoint}${storageKey ? `:${storageKey}` : ""}`;
|
|
565
|
+
var MODES = ["floating", "sidebar"];
|
|
566
|
+
function storeGet(k) {
|
|
567
|
+
try {
|
|
568
|
+
return localStorage.getItem(k);
|
|
569
|
+
} catch {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function storeSet(k, v) {
|
|
574
|
+
try {
|
|
575
|
+
if (v === null) localStorage.removeItem(k);
|
|
576
|
+
else localStorage.setItem(k, v);
|
|
577
|
+
} catch {
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function readMode(fallback) {
|
|
581
|
+
const v = storeGet(MODE_KEY);
|
|
582
|
+
return v && MODES.includes(v) ? v : fallback;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/client/styles.ts
|
|
586
|
+
var CSS = `
|
|
587
|
+
.sa-root{
|
|
588
|
+
--sa-bg:var(--sideagent-bg,#ffffff);
|
|
589
|
+
--sa-fg:var(--sideagent-fg,#171717);
|
|
590
|
+
--sa-muted:var(--sideagent-muted,#6b6b6b);
|
|
591
|
+
--sa-border:var(--sideagent-border,#e5e5e5);
|
|
592
|
+
--sa-surface:var(--sideagent-surface,#f5f5f5);
|
|
593
|
+
--sa-accent:var(--sideagent-accent,#171717);
|
|
594
|
+
--sa-accent-fg:var(--sideagent-accent-fg,#ffffff);
|
|
595
|
+
--sa-danger:var(--sideagent-danger,#b42318);
|
|
596
|
+
--sa-radius:var(--sideagent-radius,10px);
|
|
597
|
+
--sa-font:var(--sideagent-font,inherit);
|
|
598
|
+
color-scheme:light;
|
|
599
|
+
}
|
|
600
|
+
@media (prefers-color-scheme:dark){
|
|
601
|
+
.sa-root[data-theme=auto]{
|
|
602
|
+
--sa-bg:var(--sideagent-bg,#171717);--sa-fg:var(--sideagent-fg,#f5f5f5);--sa-muted:var(--sideagent-muted,#a3a3a3);
|
|
603
|
+
--sa-border:var(--sideagent-border,#2e2e2e);--sa-surface:var(--sideagent-surface,#262626);
|
|
604
|
+
--sa-accent:var(--sideagent-accent,#f5f5f5);--sa-accent-fg:var(--sideagent-accent-fg,#171717);color-scheme:dark}
|
|
605
|
+
}
|
|
606
|
+
:is(.dark,[data-theme=dark],[data-bs-theme=dark]) .sa-root[data-theme=auto],
|
|
607
|
+
.sa-root[data-theme=dark]{
|
|
608
|
+
--sa-bg:var(--sideagent-bg,#171717);--sa-fg:var(--sideagent-fg,#f5f5f5);--sa-muted:var(--sideagent-muted,#a3a3a3);
|
|
609
|
+
--sa-border:var(--sideagent-border,#2e2e2e);--sa-surface:var(--sideagent-surface,#262626);
|
|
610
|
+
--sa-accent:var(--sideagent-accent,#f5f5f5);--sa-accent-fg:var(--sideagent-accent-fg,#171717);color-scheme:dark}
|
|
611
|
+
.sa-root{font-family:var(--sa-font);font-size:14px;line-height:1.5;color:var(--sa-fg);box-sizing:border-box;-webkit-font-smoothing:antialiased}
|
|
612
|
+
.sa-root *{box-sizing:border-box}
|
|
613
|
+
.sa-panel{display:flex;flex-direction:column;background:var(--sa-bg);border:1px solid var(--sa-border);overflow:hidden}
|
|
614
|
+
.sa-floating .sa-panel{position:fixed;right:16px;bottom:80px;width:min(400px,calc(100vw - 32px));height:min(600px,calc(100vh - 104px));border-radius:calc(var(--sa-radius) + 4px);box-shadow:0 8px 30px rgba(0,0,0,.12);z-index:2147483000}
|
|
615
|
+
.sa-sidebar .sa-panel{position:fixed;top:0;right:0;bottom:0;width:min(400px,100vw);border-width:0 0 0 1px;z-index:2147483000}
|
|
616
|
+
.sa-shell{display:flex;height:100dvh;width:100%;min-width:0}
|
|
617
|
+
.sa-shell-main{flex:1;min-width:0;overflow:auto}
|
|
618
|
+
.sa-docked{display:contents}
|
|
619
|
+
.sa-docked .sa-panel{position:relative;flex:none;max-width:70vw;height:100%;border-width:0 0 0 1px;z-index:auto}
|
|
620
|
+
.sa-resizer{position:absolute;top:0;bottom:0;left:-3px;width:7px;cursor:col-resize;touch-action:none;z-index:1}
|
|
621
|
+
.sa-resizer::after{content:"";position:absolute;top:0;bottom:0;left:3px;width:2px;background:transparent;transition:background .15s}
|
|
622
|
+
.sa-resizer:hover::after,.sa-resizer:focus-visible::after,.sa-resizing .sa-resizer::after{background:var(--sa-muted)}
|
|
623
|
+
.sa-resizer:focus-visible{outline:none}
|
|
624
|
+
.sa-resizing{user-select:none;cursor:col-resize}
|
|
625
|
+
.sa-fab{position:fixed;right:calc(16px + env(safe-area-inset-right,0px));bottom:calc(16px + env(safe-area-inset-bottom,0px));width:48px;height:48px;border-radius:50%;border:0;background:var(--sa-accent);color:var(--sa-accent-fg);cursor:pointer;display:grid;place-items:center;box-shadow:0 4px 14px rgba(0,0,0,.2);z-index:2147483000}
|
|
626
|
+
.sa-header{display:flex;align-items:center;gap:6px;padding:10px 12px;border-bottom:1px solid var(--sa-border)}
|
|
627
|
+
.sa-title{font-weight:600;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
628
|
+
.sa-btn{border:1px solid var(--sa-border);background:transparent;color:var(--sa-fg);border-radius:calc(var(--sa-radius) - 2px);padding:4px 9px;min-height:28px;font:inherit;font-size:12px;cursor:pointer}
|
|
629
|
+
.sa-btn:hover{background:var(--sa-surface)}
|
|
630
|
+
.sa-btn[aria-pressed=true]{background:var(--sa-surface);border-color:var(--sa-muted)}
|
|
631
|
+
.sa-btn-primary{background:var(--sa-accent);color:var(--sa-accent-fg);border-color:var(--sa-accent)}
|
|
632
|
+
.sa-btn-primary:hover{background:var(--sa-accent);opacity:.88}
|
|
633
|
+
.sa-btn:disabled{opacity:.5;cursor:not-allowed}
|
|
634
|
+
.sa-root :is(button,textarea):focus-visible{outline:2px solid var(--sa-fg);outline-offset:2px}
|
|
635
|
+
.sa-viewport{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:14px 12px;display:flex;flex-direction:column;gap:10px}
|
|
636
|
+
.sa-empty{color:var(--sa-muted);text-align:center;margin:auto;padding:24px}
|
|
637
|
+
.sa-msg{max-width:90%;white-space:pre-wrap;word-break:break-word}
|
|
638
|
+
.sa-user{align-self:flex-end;background:var(--sa-surface);padding:7px 12px;border-radius:calc(var(--sa-radius) + 4px)}
|
|
639
|
+
.sa-assistant{align-self:flex-start}
|
|
640
|
+
.sa-md{white-space:normal;min-width:0}
|
|
641
|
+
.sa-md>:first-child{margin-top:0}.sa-md>:last-child{margin-bottom:0}
|
|
642
|
+
.sa-md :is(p,ul,ol,pre,table,blockquote){margin:0 0 .6em}
|
|
643
|
+
.sa-md :is(ul,ol){padding-left:1.4em}
|
|
644
|
+
.sa-md :is(h1,h2,h3,h4){font-size:1em;font-weight:600;margin:.8em 0 .3em}
|
|
645
|
+
.sa-md a{color:inherit;text-decoration:underline}
|
|
646
|
+
.sa-md code{font-family:ui-monospace,monospace;font-size:.9em;background:var(--sa-surface);padding:1px 4px;border-radius:4px}
|
|
647
|
+
.sa-md pre{background:var(--sa-surface);padding:8px 10px;border-radius:calc(var(--sa-radius) - 2px);overflow-x:auto}
|
|
648
|
+
.sa-md pre code{background:none;padding:0}
|
|
649
|
+
.sa-md blockquote{border-left:3px solid var(--sa-border);padding-left:10px;color:var(--sa-muted);margin-left:0}
|
|
650
|
+
.sa-md table{border-collapse:collapse;display:block;overflow-x:auto}
|
|
651
|
+
.sa-md :is(th,td){border:1px solid var(--sa-border);padding:3px 8px;text-align:left}
|
|
652
|
+
.sa-icon-btn{display:inline-grid;place-items:center;min-width:28px;padding:4px}
|
|
653
|
+
.sa-icon-btn svg{width:15px;height:15px}
|
|
654
|
+
.sa-tool{align-self:stretch;border:1px solid var(--sa-border);border-radius:var(--sa-radius);padding:8px 10px;font-size:13px;min-width:0}
|
|
655
|
+
.sa-tool-head{display:flex;align-items:center;gap:6px;font-weight:600;font-size:12.5px;margin-bottom:6px}
|
|
656
|
+
.sa-tool[data-state=done] .sa-tool-head svg{color:var(--sa-muted)}
|
|
657
|
+
.sa-tool[data-state=error] .sa-tool-head,.sa-tool[data-state=rejected] .sa-tool-head{color:var(--sa-danger)}
|
|
658
|
+
.sa-spin{animation:sa-rot 1s linear infinite}
|
|
659
|
+
@keyframes sa-rot{to{transform:rotate(360deg)}}
|
|
660
|
+
@media (prefers-reduced-motion:reduce){.sa-spin{animation:none}}
|
|
661
|
+
.sa-table-wrap{overflow:auto;max-height:240px;border:1px solid var(--sa-border);border-radius:calc(var(--sa-radius) - 2px)}
|
|
662
|
+
.sa-table{border-collapse:collapse;width:100%;font-size:12.5px}
|
|
663
|
+
.sa-table th,.sa-table td{padding:5px 9px;text-align:left;white-space:nowrap;border-bottom:1px solid var(--sa-border)}
|
|
664
|
+
.sa-table th{position:sticky;top:0;background:var(--sa-surface);font-weight:600;color:var(--sa-muted)}
|
|
665
|
+
.sa-table tr:last-child td{border-bottom:0}
|
|
666
|
+
.sa-table .sa-num{text-align:right;font-variant-numeric:tabular-nums}
|
|
667
|
+
.sa-more{color:var(--sa-muted);font-size:12px;margin-top:4px}
|
|
668
|
+
.sa-fields{margin:0;display:grid;gap:4px}
|
|
669
|
+
.sa-field{display:grid;grid-template-columns:minmax(80px,35%) 1fr;gap:8px}
|
|
670
|
+
.sa-field dt{color:var(--sa-muted)}
|
|
671
|
+
.sa-field dd{margin:0;word-break:break-word}
|
|
672
|
+
.sa-list{margin:0;padding-left:18px}
|
|
673
|
+
.sa-sources{margin:0;padding:0;list-style:none;display:grid;gap:4px}
|
|
674
|
+
.sa-sources li{display:flex;align-items:center;gap:6px}
|
|
675
|
+
.sa-sources svg{flex:none;color:var(--sa-muted)}
|
|
676
|
+
.sa-sources a{color:inherit;text-decoration:underline;text-underline-offset:2px;display:inline-flex;align-items:center;gap:4px}
|
|
677
|
+
.sa-details{margin-top:6px}
|
|
678
|
+
.sa-details summary{cursor:pointer;color:var(--sa-muted);font-size:12px;list-style:none;display:flex;align-items:center;gap:2px}
|
|
679
|
+
.sa-details summary::-webkit-details-marker{display:none}
|
|
680
|
+
.sa-details[open] summary svg{transform:rotate(90deg)}
|
|
681
|
+
.sa-details pre{margin:6px 0 0;white-space:pre-wrap;word-break:break-word;max-height:160px;overflow:auto;font-size:11.5px}
|
|
682
|
+
.sa-approval-title{display:flex;align-items:center;gap:6px}
|
|
683
|
+
.sa-approval{margin:0 12px 8px;border:1px solid var(--sa-fg);border-radius:var(--sa-radius);padding:10px;background:var(--sa-surface)}
|
|
684
|
+
.sa-approval pre{margin:6px 0;white-space:pre-wrap;word-break:break-word;max-height:120px;overflow:auto;font-size:12px}
|
|
685
|
+
.sa-approval-text{margin:6px 0}
|
|
686
|
+
.sa-approval .sa-fields{margin:0 0 6px}
|
|
687
|
+
.sa-approval .sa-row{margin-top:8px}
|
|
688
|
+
.sa-row{display:flex;gap:8px}
|
|
689
|
+
.sa-composer{display:flex;flex-direction:column;gap:4px;margin:0 12px 12px;padding:10px 10px 8px 14px;border:1px solid var(--sa-border);border-radius:18px;background:var(--sa-surface)}
|
|
690
|
+
.sa-input{width:100%;resize:none;border:0;background:transparent;padding:2px 0;font:inherit;color:inherit;max-height:160px}
|
|
691
|
+
.sa-root .sa-input:focus-visible{outline:none}
|
|
692
|
+
.sa-input::placeholder{color:var(--sa-muted)}
|
|
693
|
+
.sa-composer-bar{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
|
694
|
+
.sa-hint{color:var(--sa-muted);font-size:12px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
695
|
+
.sa-send{flex:none;margin-left:auto;display:grid;place-items:center;width:32px;height:32px;border-radius:50%;border:0;background:var(--sa-accent);color:var(--sa-accent-fg);cursor:pointer}
|
|
696
|
+
.sa-send:hover{opacity:.88}
|
|
697
|
+
.sa-send:disabled{opacity:.35;cursor:not-allowed}
|
|
698
|
+
.sa-send svg{width:16px;height:16px}
|
|
699
|
+
.sa-chatwrap{display:flex;flex-direction:column;flex:1;min-height:0}
|
|
700
|
+
.sa-chatwrap[hidden]{display:none}
|
|
701
|
+
.sa-history{gap:6px}
|
|
702
|
+
.sa-back{align-self:flex-start;display:inline-flex;align-items:center;gap:4px;margin-bottom:4px}
|
|
703
|
+
.sa-thread{display:flex;gap:6px;align-items:stretch}
|
|
704
|
+
.sa-thread-open{flex:1;min-width:0;text-align:left;border:1px solid var(--sa-border);background:transparent;color:inherit;border-radius:var(--sa-radius);padding:8px 10px;font:inherit;cursor:pointer;display:flex;flex-direction:column;gap:2px}
|
|
705
|
+
.sa-thread-open:hover{background:var(--sa-surface)}
|
|
706
|
+
.sa-thread-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
707
|
+
.sa-error{color:var(--sa-danger)}
|
|
708
|
+
@media (max-width:640px){
|
|
709
|
+
.sa-root .sa-panel{position:fixed;inset:0;width:auto!important;max-width:none;height:auto;border:0;border-radius:0;box-shadow:none;z-index:2147483000;
|
|
710
|
+
padding:env(safe-area-inset-top,0px) env(safe-area-inset-right,0px) env(safe-area-inset-bottom,0px) env(safe-area-inset-left,0px)}
|
|
711
|
+
.sa-resizer,.sa-mode-btn{display:none}
|
|
712
|
+
.sa-input{font-size:16px}
|
|
713
|
+
.sa-msg{max-width:100%}
|
|
714
|
+
.sa-user{max-width:85%}
|
|
715
|
+
.sa-approval{max-height:55dvh;overflow-y:auto}
|
|
716
|
+
}
|
|
717
|
+
@media (pointer:coarse){
|
|
718
|
+
.sa-btn{min-height:36px}
|
|
719
|
+
.sa-icon-btn{min-width:36px}
|
|
720
|
+
.sa-icon-btn svg{width:17px;height:17px}
|
|
721
|
+
.sa-send{width:36px;height:36px}
|
|
722
|
+
.sa-hint{display:none}
|
|
723
|
+
}
|
|
724
|
+
`;
|
|
725
|
+
|
|
726
|
+
// src/client/target.ts
|
|
727
|
+
function resolveTarget(props) {
|
|
728
|
+
if (props.publishableKey === void 0) {
|
|
729
|
+
if (!props.endpoint) throw new Error("sideagent: pass endpoint or publishableKey");
|
|
730
|
+
return { endpoint: props.endpoint, headers: props.headers };
|
|
731
|
+
}
|
|
732
|
+
const parts = parsePublishableKey(props.publishableKey);
|
|
733
|
+
if (!parts) throw new Error("sideagent: invalid publishableKey");
|
|
734
|
+
return {
|
|
735
|
+
// An explicit endpoint wins, e.g. a proxy in front of sideagent Cloud.
|
|
736
|
+
endpoint: props.endpoint || publishableEndpoint(parts),
|
|
737
|
+
headers: { ...props.headers, [PUBLISHABLE_KEY_HEADER]: props.publishableKey }
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/client/AgentChat.tsx
|
|
742
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
743
|
+
var MIN_WIDTH = 300;
|
|
744
|
+
var DEFAULT_WIDTH = 400;
|
|
745
|
+
var maxWidth = () => Math.max(MIN_WIDTH, Math.min(760, Math.round(window.innerWidth * 0.7)));
|
|
746
|
+
var clampWidth = (w) => Math.min(maxWidth(), Math.max(MIN_WIDTH, Math.round(w)));
|
|
613
747
|
function AgentChat(props) {
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
748
|
+
const target = useMemo2(
|
|
749
|
+
() => resolveTarget(props),
|
|
750
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
751
|
+
[props.endpoint, props.publishableKey, props.headers]
|
|
752
|
+
);
|
|
753
|
+
const [mode, setModeState] = useState3(() => readMode(props.mode ?? "floating"));
|
|
754
|
+
const [localOpen, setLocalOpen] = useState3(props.defaultOpen ?? false);
|
|
755
|
+
const shared = useContext3(AgentContext);
|
|
617
756
|
const open = shared ? shared.open : localOpen;
|
|
618
757
|
const setOpen = shared ? shared.setOpen : setLocalOpen;
|
|
619
|
-
const [view, setView] =
|
|
620
|
-
const [session, setSession] =
|
|
621
|
-
const [loading, setLoading] =
|
|
622
|
-
const docked =
|
|
623
|
-
const [width, setWidth] =
|
|
624
|
-
const [resizing, setResizing] =
|
|
625
|
-
const booted =
|
|
626
|
-
const key = threadKey(props.
|
|
627
|
-
const opts = {
|
|
758
|
+
const [view, setView] = useState3("chat");
|
|
759
|
+
const [session, setSession] = useState3({ key: 0 });
|
|
760
|
+
const [loading, setLoading] = useState3(false);
|
|
761
|
+
const docked = useContext3(DockContext) && mode === "sidebar";
|
|
762
|
+
const [width, setWidth] = useState3(() => clampWidth(Number(storeGet(WIDTH_KEY)) || DEFAULT_WIDTH));
|
|
763
|
+
const [resizing, setResizing] = useState3(false);
|
|
764
|
+
const booted = useRef2(false);
|
|
765
|
+
const key = threadKey(target.endpoint, props.storageKey);
|
|
766
|
+
const opts = {
|
|
767
|
+
endpoint: target.endpoint,
|
|
768
|
+
credentials: props.credentials,
|
|
769
|
+
headers: target.headers,
|
|
770
|
+
getHeaders: props.getHeaders
|
|
771
|
+
};
|
|
628
772
|
const setMode = useCallback((m) => {
|
|
629
773
|
setModeState(m);
|
|
630
774
|
storeSet(MODE_KEY, m);
|
|
@@ -649,7 +793,7 @@ function AgentChat(props) {
|
|
|
649
793
|
}
|
|
650
794
|
},
|
|
651
795
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
652
|
-
[
|
|
796
|
+
[target, props.credentials, props.getHeaders]
|
|
653
797
|
);
|
|
654
798
|
const resizeTo = useCallback((w) => {
|
|
655
799
|
const next = clampWidth(w);
|
|
@@ -680,73 +824,116 @@ function AgentChat(props) {
|
|
|
680
824
|
setView("chat");
|
|
681
825
|
};
|
|
682
826
|
const visible = open;
|
|
683
|
-
|
|
827
|
+
const shownKey = useRef2(key);
|
|
828
|
+
useEffect2(() => {
|
|
829
|
+
if (shownKey.current === key) return;
|
|
830
|
+
shownKey.current = key;
|
|
831
|
+
booted.current = false;
|
|
832
|
+
setView("chat");
|
|
833
|
+
setSession((s) => ({ key: s.key + 1 }));
|
|
834
|
+
}, [key]);
|
|
835
|
+
useEffect2(() => {
|
|
684
836
|
if (!visible || booted.current) return;
|
|
685
837
|
booted.current = true;
|
|
686
838
|
const last = storeGet(key);
|
|
687
839
|
if (last) void openThread(last);
|
|
688
840
|
}, [visible, key, openThread]);
|
|
689
|
-
return /* @__PURE__ */
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
{
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
841
|
+
return /* @__PURE__ */ jsxs6(
|
|
842
|
+
"div",
|
|
843
|
+
{
|
|
844
|
+
className: `sa-root sa-${mode}${docked ? " sa-docked" : ""}${resizing ? " sa-resizing" : ""}`,
|
|
845
|
+
"data-theme": props.theme ?? "auto",
|
|
846
|
+
children: [
|
|
847
|
+
/* @__PURE__ */ jsx7("style", { children: CSS }),
|
|
848
|
+
!open && /* @__PURE__ */ jsx7("button", { className: "sa-fab", "aria-label": "Open assistant", onClick: () => setOpen(true), children: /* @__PURE__ */ jsx7(ChatBubbleIcon, { width: 22, height: 22 }) }),
|
|
849
|
+
visible && /* @__PURE__ */ jsxs6("section", { className: "sa-panel", "aria-label": props.title ?? "Assistant", style: docked ? { width } : void 0, children: [
|
|
850
|
+
docked && /* @__PURE__ */ jsx7(
|
|
851
|
+
"div",
|
|
852
|
+
{
|
|
853
|
+
className: "sa-resizer",
|
|
854
|
+
role: "separator",
|
|
855
|
+
"aria-orientation": "vertical",
|
|
856
|
+
"aria-label": "Resize assistant",
|
|
857
|
+
"aria-valuemin": MIN_WIDTH,
|
|
858
|
+
"aria-valuemax": maxWidth(),
|
|
859
|
+
"aria-valuenow": width,
|
|
860
|
+
tabIndex: 0,
|
|
861
|
+
onPointerDown: onHandleDown,
|
|
862
|
+
onPointerMove: onHandleMove,
|
|
863
|
+
onPointerUp: onHandleUp,
|
|
864
|
+
onPointerCancel: onHandleUp,
|
|
865
|
+
onKeyDown: onHandleKey
|
|
866
|
+
}
|
|
867
|
+
),
|
|
868
|
+
/* @__PURE__ */ jsxs6("header", { className: "sa-header", children: [
|
|
869
|
+
/* @__PURE__ */ jsx7("span", { className: "sa-title", children: props.title ?? "Assistant" }),
|
|
870
|
+
props.allowModeSwitch !== false && /* @__PURE__ */ jsx7(
|
|
871
|
+
"button",
|
|
872
|
+
{
|
|
873
|
+
className: "sa-btn sa-icon-btn sa-mode-btn",
|
|
874
|
+
onClick: () => setMode(mode === "floating" ? "sidebar" : "floating"),
|
|
875
|
+
title: mode === "floating" ? "Dock to sidebar" : "Float",
|
|
876
|
+
"aria-label": mode === "floating" ? "Dock to sidebar" : "Float",
|
|
877
|
+
children: mode === "floating" ? /* @__PURE__ */ jsx7(ViewVerticalIcon, {}) : /* @__PURE__ */ jsx7(OpenInNewWindowIcon, {})
|
|
878
|
+
}
|
|
879
|
+
),
|
|
880
|
+
/* @__PURE__ */ jsx7(
|
|
881
|
+
"button",
|
|
882
|
+
{
|
|
883
|
+
className: "sa-btn sa-icon-btn",
|
|
884
|
+
"aria-pressed": view === "history",
|
|
885
|
+
onClick: () => setView((v) => v === "history" ? "chat" : "history"),
|
|
886
|
+
title: "History",
|
|
887
|
+
"aria-label": "History",
|
|
888
|
+
children: /* @__PURE__ */ jsx7(CounterClockwiseClockIcon, {})
|
|
889
|
+
}
|
|
890
|
+
),
|
|
891
|
+
/* @__PURE__ */ jsx7("button", { className: "sa-btn sa-icon-btn", onClick: newChat, title: "New chat", "aria-label": "New chat", children: /* @__PURE__ */ jsx7(PlusIcon, {}) }),
|
|
892
|
+
/* @__PURE__ */ jsx7("button", { className: "sa-btn sa-icon-btn", onClick: () => setOpen(false), "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx7(Cross2Icon, {}) })
|
|
893
|
+
] }),
|
|
894
|
+
view === "history" && /* @__PURE__ */ jsx7(
|
|
895
|
+
HistoryList,
|
|
896
|
+
{
|
|
897
|
+
...opts,
|
|
898
|
+
onOpen: (id) => void openThread(id),
|
|
899
|
+
onClose: () => setView("chat"),
|
|
900
|
+
onDeleted: (id) => {
|
|
901
|
+
if (id === session.threadId) newChat();
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
),
|
|
905
|
+
/* @__PURE__ */ jsx7("div", { className: "sa-chatwrap", hidden: view === "history", children: loading ? /* @__PURE__ */ jsx7("div", { className: "sa-viewport", children: /* @__PURE__ */ jsx7("div", { className: "sa-more", children: "Loading\u2026" }) }) : /* @__PURE__ */ jsx7(
|
|
906
|
+
Chat,
|
|
907
|
+
{
|
|
908
|
+
...props,
|
|
909
|
+
...target,
|
|
910
|
+
session,
|
|
911
|
+
onThread: (id) => storeSet(key, id),
|
|
912
|
+
onReload: () => session.threadId && void openThread(session.threadId)
|
|
913
|
+
},
|
|
914
|
+
session.key
|
|
915
|
+
) })
|
|
916
|
+
] })
|
|
917
|
+
]
|
|
918
|
+
}
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// src/client/AgentProvider.tsx
|
|
923
|
+
import { useMemo as useMemo3, useState as useState4 } from "react";
|
|
924
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
925
|
+
function AgentProvider({
|
|
926
|
+
children,
|
|
927
|
+
className,
|
|
928
|
+
style,
|
|
929
|
+
...chat
|
|
930
|
+
}) {
|
|
931
|
+
const [open, setOpen] = useState4(chat.defaultOpen ?? false);
|
|
932
|
+
const controls = useMemo3(() => ({ open, setOpen }), [open]);
|
|
933
|
+
return /* @__PURE__ */ jsx8(AgentContext.Provider, { value: controls, children: /* @__PURE__ */ jsx8(DockContext.Provider, { value: true, children: /* @__PURE__ */ jsxs7("div", { className: `sa-shell${className ? ` ${className}` : ""}`, style, children: [
|
|
934
|
+
/* @__PURE__ */ jsx8("div", { className: "sa-shell-main", children }),
|
|
935
|
+
/* @__PURE__ */ jsx8(AgentChat, { ...chat })
|
|
936
|
+
] }) }) });
|
|
750
937
|
}
|
|
751
938
|
export {
|
|
752
939
|
AgentChat,
|