bazilion 0.5.0 → 0.6.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.
Files changed (118) hide show
  1. package/README.md +14 -9
  2. package/dist/cli.js +365 -140
  3. package/dist/cli.js.map +1 -1
  4. package/dist/daemon.js +4452 -16989
  5. package/dist/daemon.js.map +1 -1
  6. package/dist/web/client/assets/AgentAvatar-Cq7PVBE_.js +1 -0
  7. package/dist/web/client/assets/AgentTabs-M5K8emJ8.js +1 -0
  8. package/dist/web/client/assets/Button-BefUtjMZ.js +1 -0
  9. package/dist/web/client/assets/ChatPane-C8zQfpzH.js +74 -0
  10. package/dist/web/client/assets/ConfigTabs-DRqAymcN.js +1 -0
  11. package/dist/web/client/assets/FieldRow-BqWnxZiX.js +1 -0
  12. package/dist/web/client/assets/MembersEditor-B-47GLUb.js +1 -0
  13. package/dist/web/client/assets/SpawnTeamModal-DiiC7Nc0.js +1 -0
  14. package/dist/web/client/assets/TemplatesTabs-DB4KERTQ.js +1 -0
  15. package/dist/web/client/assets/_id-B7gv1aPQ.js +1 -0
  16. package/dist/web/client/assets/_id-C7d1AqS0.js +1 -0
  17. package/dist/web/client/assets/_id-DhiwjC8u.js +1 -0
  18. package/dist/web/client/assets/_id-p8lUuNFm.js +1 -0
  19. package/dist/web/client/assets/agents-v8G-wt0M.js +1 -0
  20. package/dist/web/client/assets/config-BdmYpS_y.js +3 -0
  21. package/dist/web/client/assets/groups-DiY_ExGM.js +1 -0
  22. package/dist/web/client/assets/inbox-DtfVx246.js +1 -0
  23. package/dist/web/client/assets/index-BQUL6oyH.js +12 -0
  24. package/dist/web/client/assets/login-CVS70W9g.js +1 -0
  25. package/dist/web/client/assets/mcp-BDLQq8OZ.js +1 -0
  26. package/dist/web/client/assets/memory-6GGuAXwx.js +1 -0
  27. package/dist/web/client/assets/profile-groups-BA8rwZMA.js +1 -0
  28. package/dist/web/client/assets/profiles-C0aAcPZg.js +1 -0
  29. package/dist/web/client/assets/routes-DgAhnKFw.js +1 -0
  30. package/dist/web/client/assets/services-BZ86QEah.js +1 -0
  31. package/dist/web/client/assets/skills-mv_NNNwo.js +1 -0
  32. package/dist/web/client/assets/styles-CmqUskDu.css +2 -0
  33. package/dist/web/client/assets/telegram-5f9WXs3x.js +1 -0
  34. package/dist/web/client/assets/tokens-DHiYsFUh.js +1 -0
  35. package/dist/web/client/assets/triggers-CjC8Ljrz.js +1 -0
  36. package/dist/web/client/assets/welcome-C-Cu-ziL.js +1 -0
  37. package/dist/web/client/assets/wire-constants-BkT5-597.js +1 -0
  38. package/dist/web/client/baziu.svg +8 -0
  39. package/dist/web/server/assets/AgentAvatar-8gTAy_tP.js +37 -0
  40. package/dist/web/server/assets/AgentTabs-BOjTN7QF.js +34 -0
  41. package/dist/web/server/assets/Button-Dsnw22OF.js +17 -0
  42. package/dist/web/server/assets/ChatPane-BvpswYvF.js +1084 -0
  43. package/dist/web/server/assets/ConfigTabs-CyuxzesF.js +41 -0
  44. package/dist/web/server/assets/FieldRow-DwyqycWI.js +94 -0
  45. package/dist/web/server/assets/MembersEditor-BWQztvrX.js +131 -0
  46. package/dist/web/server/assets/Sidebar-DmEiewRN.js +529 -0
  47. package/dist/web/server/assets/SpawnTeamModal-dSN421k_.js +107 -0
  48. package/dist/web/server/assets/TemplatesTabs---7h5GYa.js +30 -0
  49. package/dist/web/server/assets/_id-BZ-YCWSw.js +16 -0
  50. package/dist/web/server/assets/_id-Bm4-_AP-.js +367 -0
  51. package/dist/web/server/assets/_id-C78M9_j9.js +12 -0
  52. package/dist/web/server/assets/_id-CGZbudCl.js +28 -0
  53. package/dist/web/server/assets/_id-CHNm7EAd.js +161 -0
  54. package/dist/web/server/assets/_id-CrQbC5L8.js +16 -0
  55. package/dist/web/server/assets/_id-Crcw2HhT.js +38 -0
  56. package/dist/web/server/assets/_id-CvIGhq4U.js +16 -0
  57. package/dist/web/server/assets/_id-DF3pfi9M.js +27 -0
  58. package/dist/web/server/assets/_id-Dc8P9sy2.js +24 -0
  59. package/dist/web/server/assets/_id-GJk3EYTT.js +662 -0
  60. package/dist/web/server/assets/_id-pd4S7LTi.js +467 -0
  61. package/dist/web/server/assets/_tanstack-start-manifest_v-CPyKYMJg.js +174 -0
  62. package/dist/web/server/assets/agents-70EwQJR6.js +210 -0
  63. package/dist/web/server/assets/agents-Ds4lcREv.js +26 -0
  64. package/dist/web/server/assets/agents-K4Smhnq3.js +14 -0
  65. package/dist/web/server/assets/auth-DpAGLHpA.js +26 -0
  66. package/dist/web/server/assets/config-CMXUx_IF.js +19 -0
  67. package/dist/web/server/assets/config-DQbCsY_l.js +382 -0
  68. package/dist/web/server/assets/config-DxCGYgR9.js +12 -0
  69. package/dist/web/server/assets/createServerRpc-CBTntoKx.js +12 -0
  70. package/dist/web/server/assets/createSsrRpc-DHBVUN8O.js +16 -0
  71. package/dist/web/server/assets/daemon-client-2NRMzlTr.js +96 -0
  72. package/dist/web/server/assets/empty-plugin-adapters-Bos_V2s-.js +5 -0
  73. package/dist/web/server/assets/groups-CXVew7BP.js +150 -0
  74. package/dist/web/server/assets/groups-Cms8cvH5.js +12 -0
  75. package/dist/web/server/assets/groups-Dmcvw5ya.js +21 -0
  76. package/dist/web/server/assets/inbox-0QfwsrGo.js +27 -0
  77. package/dist/web/server/assets/inbox-8HybA2Ws.js +133 -0
  78. package/dist/web/server/assets/inbox-B7UWoICA.js +42 -0
  79. package/dist/web/server/assets/login-CNznncgA.js +58 -0
  80. package/dist/web/server/assets/login-CQybKk7Y.js +74 -0
  81. package/dist/web/server/assets/mcp-42BrkI1I.js +12 -0
  82. package/dist/web/server/assets/mcp-DIa0kIOf.js +268 -0
  83. package/dist/web/server/assets/mcp-DsTjY3uz.js +12 -0
  84. package/dist/web/server/assets/memory-BfrMp8iY.js +284 -0
  85. package/dist/web/server/assets/memory-Dr0lOM2y.js +28 -0
  86. package/dist/web/server/assets/memory-EEgLDfCj.js +16 -0
  87. package/dist/web/server/assets/profile-groups-BW5gU-fG.js +12 -0
  88. package/dist/web/server/assets/profile-groups-B_kIT6BU.js +208 -0
  89. package/dist/web/server/assets/profile-groups-C5isDI2t.js +28 -0
  90. package/dist/web/server/assets/profiles-BP1v-jna.js +453 -0
  91. package/dist/web/server/assets/profiles-BbPTpmyq.js +26 -0
  92. package/dist/web/server/assets/profiles-Cy5auEj8.js +12 -0
  93. package/dist/web/server/assets/router-CL100-Ly.js +460 -0
  94. package/dist/web/server/assets/routes-CzJLQxcS.js +14 -0
  95. package/dist/web/server/assets/routes-D2H1Hnoh.js +57 -0
  96. package/dist/web/server/assets/routes-Dzk8jvIo.js +33 -0
  97. package/dist/web/server/assets/services-Bgj6M2gC.js +12 -0
  98. package/dist/web/server/assets/services-DEgYsWJ4.js +17 -0
  99. package/dist/web/server/assets/services-DFenjiw0.js +94 -0
  100. package/dist/web/server/assets/skills-BASIP_rT.js +12 -0
  101. package/dist/web/server/assets/skills-C-JPRCHh.js +12 -0
  102. package/dist/web/server/assets/skills-DZXN680t.js +261 -0
  103. package/dist/web/server/assets/start-cOXH7eTI.js +4 -0
  104. package/dist/web/server/assets/telegram-BbvqVWxa.js +583 -0
  105. package/dist/web/server/assets/telegram-Bq49AtnP.js +12 -0
  106. package/dist/web/server/assets/telegram-sUckI94O.js +12 -0
  107. package/dist/web/server/assets/tokens-BERRE0wB.js +14 -0
  108. package/dist/web/server/assets/tokens-CImgxhDI.js +193 -0
  109. package/dist/web/server/assets/tokens-Dwn00iV7.js +12 -0
  110. package/dist/web/server/assets/triggers-DtexqbNH.js +16 -0
  111. package/dist/web/server/assets/triggers-TyIjwSYn.js +26 -0
  112. package/dist/web/server/assets/triggers-_iSeahif.js +191 -0
  113. package/dist/web/server/assets/welcome-BkQ9NC5K.js +81 -0
  114. package/dist/web/server/assets/wire-constants-Deb4NxBb.js +16 -0
  115. package/dist/web/server/server.js +5899 -0
  116. package/dist/web-server.js +42 -0
  117. package/dist/web-server.js.map +1 -0
  118. package/package.json +10 -4
@@ -0,0 +1,24 @@
1
+ import { t as createServerFn } from "../server.js";
2
+ import { t as createServerRpc } from "./createServerRpc-CBTntoKx.js";
3
+ import { n as daemonClient } from "./daemon-client-2NRMzlTr.js";
4
+ //#region src/routes/profile-groups/$id.tsx?tss-serverfn-split
5
+ var fetchDetail_createServerFn_handler = createServerRpc({
6
+ id: "86dad0c33c97a6f0913d1f38e9b01ac4144f7d0970b45fdb3470e9bc9a78e0e0",
7
+ name: "fetchDetail",
8
+ filename: "src/routes/profile-groups/$id.tsx"
9
+ }, (opts) => fetchDetail.__executeServer(opts));
10
+ var fetchDetail = createServerFn({ method: "POST" }).inputValidator((d) => d).handler(fetchDetail_createServerFn_handler, async ({ data }) => {
11
+ const c = daemonClient();
12
+ const [detail, profiles, models] = await Promise.all([
13
+ c.get(`/api/profile-groups/${encodeURIComponent(data.id)}`),
14
+ c.get("/api/profiles"),
15
+ c.get("/api/config/available-models")
16
+ ]);
17
+ return {
18
+ detail,
19
+ profiles,
20
+ modelGroups: models.groups
21
+ };
22
+ });
23
+ //#endregion
24
+ export { fetchDetail_createServerFn_handler };
@@ -0,0 +1,662 @@
1
+ import { t as Route } from "./_id-CvIGhq4U.js";
2
+ import { t as AgentTabs } from "./AgentTabs-BOjTN7QF.js";
3
+ import { t as AgentAvatar } from "./AgentAvatar-8gTAy_tP.js";
4
+ import { t as ChatPane } from "./ChatPane-BvpswYvF.js";
5
+ import { r as REASONING_LEVELS } from "./wire-constants-Deb4NxBb.js";
6
+ import { useState } from "react";
7
+ import { useRouter } from "@tanstack/react-router";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/components/CopyButton.tsx
10
+ function CopyButton({ value, className, ariaLabel = "Copy to clipboard", title = "Copy to clipboard" }) {
11
+ const [copied, setCopied] = useState(false);
12
+ async function copy() {
13
+ if (!value) return;
14
+ let ok = false;
15
+ try {
16
+ if (navigator.clipboard?.writeText) {
17
+ await navigator.clipboard.writeText(value);
18
+ ok = true;
19
+ }
20
+ } catch {}
21
+ if (!ok) {
22
+ const ta = document.createElement("textarea");
23
+ ta.value = value;
24
+ ta.setAttribute("readonly", "");
25
+ ta.style.position = "fixed";
26
+ ta.style.opacity = "0";
27
+ document.body.appendChild(ta);
28
+ ta.select();
29
+ try {
30
+ ok = document.execCommand("copy");
31
+ } catch {}
32
+ ta.remove();
33
+ }
34
+ if (ok) {
35
+ setCopied(true);
36
+ setTimeout(() => setCopied(false), 1e3);
37
+ }
38
+ }
39
+ return /* @__PURE__ */ jsx("button", {
40
+ type: "button",
41
+ onClick: copy,
42
+ title,
43
+ "aria-label": ariaLabel,
44
+ className: `inline-flex h-[1.3rem] w-[1.6rem] items-center justify-center rounded-sm border border-transparent text-mocha-light transition-colors hover:border-frost hover:bg-snow hover:text-mocha ${copied ? "border-[#bcd9bc] bg-[#eef7ee] text-[#3b7a3b] hover:border-[#bcd9bc] hover:bg-[#eef7ee] hover:text-[#3b7a3b]" : ""} ${className ?? ""}`,
45
+ children: copied ? "✓" : "⧉"
46
+ });
47
+ }
48
+ //#endregion
49
+ //#region src/routes/agents/$id/index.tsx?tsr-split=component
50
+ function AgentDetailPage() {
51
+ const { resolved, initialMessages, sessionHead, groups, skills, modelGroups, telegramConfigured } = Route.useLoaderData();
52
+ const router = useRouter();
53
+ async function archive() {
54
+ if (!confirm(`archive "${resolved.agent.name}"? (reversible)`)) return;
55
+ await fetch(`/api/agents/${resolved.agent.id}/archive`, { method: "POST" });
56
+ await router.invalidate();
57
+ }
58
+ async function unarchive() {
59
+ await fetch(`/api/agents/${resolved.agent.id}/unarchive`, { method: "POST" });
60
+ await router.invalidate();
61
+ }
62
+ async function del() {
63
+ const msg = `Permanently delete agent "${resolved.agent.name}"?\n\nThis removes the DB rows (messages, triggers, skill attachments) AND the agent's on-disk directory (memory, templates, state, sessions). This cannot be undone.`;
64
+ if (!confirm(msg)) return;
65
+ const res = await fetch(`/api/agents/${resolved.agent.id}`, { method: "DELETE" });
66
+ if (res.ok || res.status === 204) window.location.assign("/agents");
67
+ else alert(`failed: ${res.statusText}`);
68
+ }
69
+ return /* @__PURE__ */ jsxs("div", { children: [
70
+ /* @__PURE__ */ jsxs("header", {
71
+ className: "mb-8",
72
+ children: [
73
+ /* @__PURE__ */ jsxs("div", {
74
+ className: "flex items-center gap-3",
75
+ children: [/* @__PURE__ */ jsx(AgentAvatar, {
76
+ identity: resolved.agent.identity,
77
+ size: 44
78
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
79
+ className: "mb-0",
80
+ children: resolved.agent.name
81
+ }), (resolved.agent.identity?.creature || resolved.agent.identity?.vibe) && /* @__PURE__ */ jsx("p", {
82
+ className: "muted mt-0.5 text-sm",
83
+ children: [resolved.agent.identity?.creature, resolved.agent.identity?.vibe].filter(Boolean).join(" · ")
84
+ })] })]
85
+ }),
86
+ /* @__PURE__ */ jsxs("div", {
87
+ className: "mt-2 flex flex-wrap items-center gap-x-5 gap-y-2",
88
+ children: [
89
+ /* @__PURE__ */ jsxs(Tag, { children: [/* @__PURE__ */ jsx("span", {
90
+ className: "px-1.5 font-mono text-[0.95em] text-mocha select-all",
91
+ children: resolved.agent.id
92
+ }), /* @__PURE__ */ jsx(CopyButton, {
93
+ value: resolved.agent.id,
94
+ ariaLabel: "Copy agent UUID",
95
+ title: "Copy UUID to clipboard"
96
+ })] }),
97
+ /* @__PURE__ */ jsxs(Tag, { children: ["status: ", /* @__PURE__ */ jsx("span", {
98
+ className: "ml-1",
99
+ children: resolved.agent.status
100
+ })] }),
101
+ /* @__PURE__ */ jsxs(Tag, { children: ["profile: ", /* @__PURE__ */ jsx("code", {
102
+ className: "ml-1 bg-transparent px-0",
103
+ children: resolved.profile.id
104
+ })] }),
105
+ /* @__PURE__ */ jsxs(Tag, { children: [
106
+ "group:",
107
+ " ",
108
+ /* @__PURE__ */ jsx("a", {
109
+ href: `/groups/${encodeURIComponent(resolved.group.id)}`,
110
+ className: "ml-1 bg-transparent px-0 font-mono underline",
111
+ children: resolved.group.id
112
+ }),
113
+ " ",
114
+ "·",
115
+ " ",
116
+ /* @__PURE__ */ jsx("a", {
117
+ href: `/groups/${encodeURIComponent(resolved.group.id)}/memory`,
118
+ className: "ml-0.5 underline",
119
+ children: "shared memory →"
120
+ })
121
+ ] }),
122
+ /* @__PURE__ */ jsxs(Tag, { children: ["model: ", /* @__PURE__ */ jsx("code", {
123
+ className: "ml-1 bg-transparent px-0",
124
+ children: resolved.model
125
+ })] }),
126
+ /* @__PURE__ */ jsxs(Tag, { children: [
127
+ "reasoning:",
128
+ " ",
129
+ /* @__PURE__ */ jsx("code", {
130
+ className: "ml-1 bg-transparent px-0",
131
+ children: resolved.reasoningLevel
132
+ })
133
+ ] })
134
+ ]
135
+ }),
136
+ /* @__PURE__ */ jsxs("div", {
137
+ className: "mt-3 flex flex-wrap gap-2",
138
+ children: [resolved.agent.status === "archived" ? /* @__PURE__ */ jsx("button", {
139
+ type: "button",
140
+ onClick: unarchive,
141
+ className: "ghost-btn",
142
+ children: "unarchive"
143
+ }) : /* @__PURE__ */ jsx("button", {
144
+ type: "button",
145
+ onClick: archive,
146
+ className: "ghost-btn",
147
+ children: "archive"
148
+ }), /* @__PURE__ */ jsx("button", {
149
+ type: "button",
150
+ onClick: del,
151
+ className: "ghost-btn border-[rgba(196,135,138,0.4)] text-[#9B3D3D] hover:bg-[rgba(196,135,138,0.08)]",
152
+ children: "delete permanently"
153
+ })]
154
+ }),
155
+ /* @__PURE__ */ jsx(SettingsDetails, {
156
+ agentId: resolved.agent.id,
157
+ currentModelOverride: resolved.agent.modelOverride ?? null,
158
+ profileDefaultModel: resolved.profile.defaultModel,
159
+ currentReasoning: resolved.reasoningLevel,
160
+ modelGroups
161
+ })
162
+ ]
163
+ }),
164
+ resolved.agent.status === "archived" && /* @__PURE__ */ jsxs("div", {
165
+ className: "mb-4 rounded-md border border-frost bg-fawn/35 px-4 py-2 text-[0.9em] text-chocolate",
166
+ children: [
167
+ /* @__PURE__ */ jsx("strong", {
168
+ className: "text-[#7a4e2a]",
169
+ children: "This agent is archived."
170
+ }),
171
+ " It's hidden from the default agent list; use ",
172
+ /* @__PURE__ */ jsx("em", { children: "unarchive" }),
173
+ " above to bring it back, or",
174
+ " ",
175
+ /* @__PURE__ */ jsx("em", { children: "delete permanently" }),
176
+ " to remove its data entirely."
177
+ ]
178
+ }),
179
+ /* @__PURE__ */ jsx(AgentTabs, {
180
+ agentId: resolved.agent.id,
181
+ active: "chat",
182
+ archived: resolved.agent.status === "archived"
183
+ }),
184
+ /* @__PURE__ */ jsx(SectionTitle, { children: "Chat" }),
185
+ /* @__PURE__ */ jsx("div", {
186
+ className: "h-[70vh]",
187
+ children: /* @__PURE__ */ jsx(ChatPane, {
188
+ agentId: resolved.agent.id,
189
+ agentName: resolved.agent.name,
190
+ initialMessages,
191
+ initialSessionHead: sessionHead
192
+ })
193
+ }),
194
+ /* @__PURE__ */ jsx("p", {
195
+ className: "mt-2 text-[0.82em] text-mocha-light",
196
+ children: "chat history persists in bazilion across navigation and restarts."
197
+ }),
198
+ /* @__PURE__ */ jsxs("section", {
199
+ className: "mt-10",
200
+ children: [
201
+ /* @__PURE__ */ jsx(SectionTitle, { children: "Group" }),
202
+ /* @__PURE__ */ jsxs("p", {
203
+ className: "text-[0.9em] text-mocha-light",
204
+ children: [
205
+ "Current group: ",
206
+ /* @__PURE__ */ jsx("code", { children: resolved.group.id }),
207
+ " (",
208
+ resolved.group.name,
209
+ ") —",
210
+ " ",
211
+ /* @__PURE__ */ jsx("code", { children: resolved.group.path }),
212
+ ".",
213
+ " ",
214
+ /* @__PURE__ */ jsx("a", {
215
+ href: `/groups/${resolved.group.id}`,
216
+ children: "view / edit USER.md"
217
+ })
218
+ ]
219
+ }),
220
+ /* @__PURE__ */ jsx(MoveGroupForm, {
221
+ agentId: resolved.agent.id,
222
+ currentGroupId: resolved.group.id,
223
+ groups
224
+ })
225
+ ]
226
+ }),
227
+ /* @__PURE__ */ jsxs("section", {
228
+ className: "mt-10",
229
+ children: [/* @__PURE__ */ jsx(SectionTitle, { children: "Skills" }), /* @__PURE__ */ jsx(SkillsTable, {
230
+ agentId: resolved.agent.id,
231
+ attached: new Set(resolved.skills),
232
+ all: skills
233
+ })]
234
+ }),
235
+ telegramConfigured && /* @__PURE__ */ jsxs("section", {
236
+ className: "mt-10",
237
+ children: [/* @__PURE__ */ jsx(SectionTitle, { children: "Telegram" }), /* @__PURE__ */ jsx(TelegramSection, { agent: resolved.agent })]
238
+ })
239
+ ] });
240
+ }
241
+ function SectionTitle({ children }) {
242
+ return /* @__PURE__ */ jsx("h3", {
243
+ className: "mb-3 font-body text-[0.85em] font-semibold uppercase tracking-wider text-mocha-light",
244
+ children
245
+ });
246
+ }
247
+ function Tag({ children }) {
248
+ return /* @__PURE__ */ jsx("span", {
249
+ className: "inline-flex items-center gap-1 rounded-full border border-frost bg-ivory px-3 py-0.5 text-[0.84em] text-mocha-light",
250
+ children
251
+ });
252
+ }
253
+ function SettingsDetails({ agentId, currentModelOverride, profileDefaultModel, currentReasoning, modelGroups }) {
254
+ const router = useRouter();
255
+ const [modelOverride, setModelOverride] = useState(currentModelOverride ?? "");
256
+ const [reasoning, setReasoning] = useState(currentReasoning);
257
+ const [saving, setSaving] = useState(false);
258
+ const [savedFlash, setSavedFlash] = useState(false);
259
+ const [error, setError] = useState(null);
260
+ const currentInGroups = currentModelOverride !== null && modelGroups.some((g) => g.models.some((m) => `${g.provider}:${m}` === currentModelOverride));
261
+ async function save(e) {
262
+ e.preventDefault();
263
+ setSaving(true);
264
+ setError(null);
265
+ try {
266
+ const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}`, {
267
+ method: "PATCH",
268
+ headers: { "content-type": "application/json" },
269
+ body: JSON.stringify({
270
+ modelOverride: modelOverride === "" ? null : modelOverride,
271
+ reasoningLevel: reasoning
272
+ })
273
+ });
274
+ if (!res.ok) {
275
+ const body = await res.json().catch(() => null);
276
+ throw new Error(body?.error ?? res.statusText);
277
+ }
278
+ setSavedFlash(true);
279
+ setTimeout(() => setSavedFlash(false), 1500);
280
+ await router.invalidate();
281
+ } catch (err) {
282
+ setError(err.message);
283
+ } finally {
284
+ setSaving(false);
285
+ }
286
+ }
287
+ return /* @__PURE__ */ jsxs("details", {
288
+ className: "mt-3 text-[0.9em]",
289
+ children: [/* @__PURE__ */ jsx("summary", {
290
+ className: "cursor-pointer text-mocha-light",
291
+ children: "settings"
292
+ }), /* @__PURE__ */ jsxs("form", {
293
+ onSubmit: save,
294
+ className: "mt-2 flex flex-wrap items-end gap-3 border-t border-dashed border-frost py-3",
295
+ children: [
296
+ /* @__PURE__ */ jsxs("label", {
297
+ className: "flex flex-col gap-1 text-[0.85em] text-mocha-light",
298
+ children: ["model override", /* @__PURE__ */ jsxs("select", {
299
+ name: "modelOverride",
300
+ value: modelOverride,
301
+ onChange: (e) => setModelOverride(e.target.value),
302
+ className: "min-w-[16rem] rounded-sm border border-frost bg-snow px-2 py-1.5",
303
+ children: [
304
+ /* @__PURE__ */ jsxs("option", {
305
+ value: "",
306
+ children: [
307
+ "inherit from profile (",
308
+ profileDefaultModel,
309
+ ")"
310
+ ]
311
+ }),
312
+ currentModelOverride !== null && !currentInGroups && /* @__PURE__ */ jsxs("option", {
313
+ value: currentModelOverride,
314
+ children: [currentModelOverride, " (not in curated list)"]
315
+ }),
316
+ modelGroups.map((g) => /* @__PURE__ */ jsx("optgroup", {
317
+ label: g.provider,
318
+ children: g.models.map((m) => {
319
+ const value = `${g.provider}:${m}`;
320
+ return /* @__PURE__ */ jsx("option", {
321
+ value,
322
+ children: value
323
+ }, value);
324
+ })
325
+ }, g.provider))
326
+ ]
327
+ })]
328
+ }),
329
+ /* @__PURE__ */ jsxs("label", {
330
+ className: "flex flex-col gap-1 text-[0.85em] text-mocha-light",
331
+ children: ["reasoning level", /* @__PURE__ */ jsx("select", {
332
+ name: "reasoningLevel",
333
+ value: reasoning,
334
+ onChange: (e) => setReasoning(e.target.value),
335
+ className: "min-w-[16rem] rounded-sm border border-frost bg-snow px-2 py-1.5",
336
+ children: REASONING_LEVELS.map((lvl) => /* @__PURE__ */ jsx("option", {
337
+ value: lvl,
338
+ children: lvl
339
+ }, lvl))
340
+ })]
341
+ }),
342
+ /* @__PURE__ */ jsx("button", {
343
+ type: "submit",
344
+ disabled: saving,
345
+ children: saving ? "saving…" : "save"
346
+ }),
347
+ savedFlash && /* @__PURE__ */ jsx("span", {
348
+ className: "text-[0.85em] text-[#3b7a3b]",
349
+ children: "saved ✓"
350
+ }),
351
+ error && /* @__PURE__ */ jsx("span", {
352
+ className: "text-[0.85em] text-[#9B3D3D]",
353
+ children: error
354
+ })
355
+ ]
356
+ })]
357
+ });
358
+ }
359
+ function MoveGroupForm({ agentId, currentGroupId, groups }) {
360
+ const router = useRouter();
361
+ const [groupId, setGroupId] = useState(currentGroupId);
362
+ const [moving, setMoving] = useState(false);
363
+ const [err, setErr] = useState(null);
364
+ async function move(e) {
365
+ e.preventDefault();
366
+ if (groupId === currentGroupId) return;
367
+ setMoving(true);
368
+ setErr(null);
369
+ try {
370
+ const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}/group`, {
371
+ method: "PATCH",
372
+ headers: { "content-type": "application/json" },
373
+ body: JSON.stringify({ groupId })
374
+ });
375
+ if (!res.ok) {
376
+ const body = await res.json().catch(() => null);
377
+ throw new Error(body?.error ?? res.statusText);
378
+ }
379
+ await router.invalidate();
380
+ } catch (e2) {
381
+ setErr(e2.message);
382
+ } finally {
383
+ setMoving(false);
384
+ }
385
+ }
386
+ return /* @__PURE__ */ jsxs("form", {
387
+ onSubmit: move,
388
+ className: "mt-2 flex flex-wrap items-end gap-2",
389
+ children: [
390
+ /* @__PURE__ */ jsxs("label", {
391
+ className: "flex flex-col gap-1 text-[0.85em] text-mocha-light",
392
+ children: ["move to:", /* @__PURE__ */ jsx("select", {
393
+ value: groupId,
394
+ onChange: (e) => setGroupId(e.target.value),
395
+ className: "min-w-[18rem] rounded-sm border border-frost bg-snow px-2 py-1.5",
396
+ children: groups.map((g) => /* @__PURE__ */ jsxs("option", {
397
+ value: g.id,
398
+ children: [
399
+ g.id,
400
+ " — ",
401
+ g.path
402
+ ]
403
+ }, g.id))
404
+ })]
405
+ }),
406
+ /* @__PURE__ */ jsx("button", {
407
+ type: "submit",
408
+ disabled: moving || groupId === currentGroupId,
409
+ children: moving ? "moving…" : "move"
410
+ }),
411
+ err && /* @__PURE__ */ jsx("span", {
412
+ className: "text-[0.85em] text-[#9B3D3D]",
413
+ children: err
414
+ })
415
+ ]
416
+ });
417
+ }
418
+ function SkillsTable({ agentId, attached, all }) {
419
+ const router = useRouter();
420
+ const [pending, setPending] = useState(null);
421
+ const [err, setErr] = useState(null);
422
+ async function attach(name) {
423
+ setPending(name);
424
+ setErr(null);
425
+ try {
426
+ const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}/skills`, {
427
+ method: "POST",
428
+ headers: { "content-type": "application/json" },
429
+ body: JSON.stringify({ skill: name })
430
+ });
431
+ if (!res.ok) {
432
+ const body = await res.json().catch(() => null);
433
+ throw new Error(body?.error ?? res.statusText);
434
+ }
435
+ await router.invalidate();
436
+ } catch (e) {
437
+ setErr(e.message);
438
+ } finally {
439
+ setPending(null);
440
+ }
441
+ }
442
+ async function detach(name) {
443
+ setPending(name);
444
+ setErr(null);
445
+ try {
446
+ const res = await fetch(`/api/agents/${encodeURIComponent(agentId)}/skills/${encodeURIComponent(name)}`, { method: "DELETE" });
447
+ if (!res.ok && res.status !== 204) {
448
+ const body = await res.json().catch(() => null);
449
+ throw new Error(body?.error ?? res.statusText);
450
+ }
451
+ await router.invalidate();
452
+ } catch (e) {
453
+ setErr(e.message);
454
+ } finally {
455
+ setPending(null);
456
+ }
457
+ }
458
+ if (all.length === 0) return /* @__PURE__ */ jsx("p", {
459
+ className: "text-[0.9em] text-mocha-light",
460
+ children: "no skills installed"
461
+ });
462
+ return /* @__PURE__ */ jsxs(Fragment, { children: [err && /* @__PURE__ */ jsx("p", {
463
+ className: "mb-2 text-[0.85em] text-[#9B3D3D]",
464
+ children: err
465
+ }), /* @__PURE__ */ jsxs("table", { children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
466
+ /* @__PURE__ */ jsx("th", { children: "name" }),
467
+ /* @__PURE__ */ jsx("th", { children: "description" }),
468
+ /* @__PURE__ */ jsx("th", {})
469
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: all.map((s) => {
470
+ const isAttached = attached.has(s.name);
471
+ const busy = pending === s.name;
472
+ return /* @__PURE__ */ jsxs("tr", { children: [
473
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx("code", { children: s.name }) }),
474
+ /* @__PURE__ */ jsx("td", { children: s.description || /* @__PURE__ */ jsx("span", {
475
+ className: "text-mocha-light",
476
+ children: "—"
477
+ }) }),
478
+ /* @__PURE__ */ jsx("td", { children: isAttached ? /* @__PURE__ */ jsx("button", {
479
+ type: "button",
480
+ className: "ghost-btn",
481
+ disabled: busy,
482
+ onClick: () => detach(s.name),
483
+ children: busy ? "…" : "detach"
484
+ }) : /* @__PURE__ */ jsx("button", {
485
+ type: "button",
486
+ className: "ghost-btn",
487
+ disabled: busy,
488
+ onClick: () => attach(s.name),
489
+ children: busy ? "…" : "attach"
490
+ }) })
491
+ ] }, s.name);
492
+ }) })] })] });
493
+ }
494
+ function TelegramSection({ agent }) {
495
+ const router = useRouter();
496
+ const [busy, setBusy] = useState(false);
497
+ const [error, setError] = useState(null);
498
+ const [mirrorMode, setMirrorMode] = useState(agent.telegramMirrorMode);
499
+ const [iconEmoji, setIconEmoji] = useState(agent.telegramIconEmoji ?? "");
500
+ const isBound = agent.telegramTopicId !== null;
501
+ async function bind() {
502
+ setBusy(true);
503
+ setError(null);
504
+ try {
505
+ const res = await fetch(`/api/agents/${encodeURIComponent(agent.id)}/telegram/bind`, { method: "POST" });
506
+ if (!res.ok) {
507
+ const body = await res.json().catch(() => null);
508
+ throw new Error(body?.error ?? res.statusText);
509
+ }
510
+ const result = await res.json();
511
+ window.open(result.deepLink, "_blank", "noopener");
512
+ await router.invalidate();
513
+ } catch (e) {
514
+ setError(e.message);
515
+ } finally {
516
+ setBusy(false);
517
+ }
518
+ }
519
+ async function unbind() {
520
+ if (!confirm("Unbind this agent from its Telegram topic? The topic stays in Telegram as an orphan.")) return;
521
+ setBusy(true);
522
+ setError(null);
523
+ try {
524
+ const res = await fetch(`/api/agents/${encodeURIComponent(agent.id)}/telegram/binding`, { method: "DELETE" });
525
+ if (!res.ok) throw new Error(res.statusText);
526
+ await router.invalidate();
527
+ } catch (e) {
528
+ setError(e.message);
529
+ } finally {
530
+ setBusy(false);
531
+ }
532
+ }
533
+ async function saveMirrorMode(next) {
534
+ setMirrorMode(next);
535
+ try {
536
+ const res = await fetch(`/api/agents/${encodeURIComponent(agent.id)}`, {
537
+ method: "PATCH",
538
+ headers: { "content-type": "application/json" },
539
+ body: JSON.stringify({ telegramMirrorMode: next })
540
+ });
541
+ if (!res.ok) throw new Error(res.statusText);
542
+ await router.invalidate();
543
+ } catch (e) {
544
+ setMirrorMode(agent.telegramMirrorMode);
545
+ setError(e.message);
546
+ }
547
+ }
548
+ async function saveIcon() {
549
+ setError(null);
550
+ try {
551
+ const res = await fetch(`/api/agents/${encodeURIComponent(agent.id)}`, {
552
+ method: "PATCH",
553
+ headers: { "content-type": "application/json" },
554
+ body: JSON.stringify({ telegramIconEmoji: iconEmoji.trim() || null })
555
+ });
556
+ if (!res.ok) throw new Error(res.statusText);
557
+ await router.invalidate();
558
+ } catch (e) {
559
+ setError(e.message);
560
+ }
561
+ }
562
+ return /* @__PURE__ */ jsxs("div", {
563
+ className: "text-[0.9em]",
564
+ children: [
565
+ /* @__PURE__ */ jsx("div", {
566
+ className: "mb-3 flex items-center gap-3",
567
+ children: isBound ? /* @__PURE__ */ jsxs(Fragment, { children: [
568
+ /* @__PURE__ */ jsx("span", {
569
+ className: "rounded-full bg-emerald-100 px-2 py-0.5 text-xs font-semibold text-emerald-800",
570
+ children: "bound"
571
+ }),
572
+ /* @__PURE__ */ jsxs("span", {
573
+ className: "font-mono text-mocha",
574
+ children: ["topic #", agent.telegramTopicId]
575
+ }),
576
+ /* @__PURE__ */ jsx("button", {
577
+ type: "button",
578
+ onClick: bind,
579
+ disabled: busy,
580
+ className: "ghost-btn",
581
+ title: "Open the topic in Telegram (works on desktop/Android; iOS shows the topic list)",
582
+ children: "open in Telegram"
583
+ }),
584
+ /* @__PURE__ */ jsx("button", {
585
+ type: "button",
586
+ onClick: unbind,
587
+ disabled: busy,
588
+ className: "ghost-btn border-[rgba(196,135,138,0.4)] text-[#9B3D3D] hover:bg-[rgba(196,135,138,0.08)]",
589
+ children: "unbind"
590
+ })
591
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
592
+ className: "rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-800",
593
+ children: "unbound"
594
+ }), /* @__PURE__ */ jsx("button", {
595
+ type: "button",
596
+ onClick: bind,
597
+ disabled: busy,
598
+ className: "ghost-btn",
599
+ title: "Create a Telegram topic for this agent",
600
+ children: busy ? "binding…" : "bind a topic"
601
+ })] })
602
+ }),
603
+ /* @__PURE__ */ jsxs("div", {
604
+ className: "flex items-center gap-2",
605
+ children: [/* @__PURE__ */ jsx("label", {
606
+ htmlFor: "mirror-mode",
607
+ className: "text-mocha-light",
608
+ children: "mirror mode:"
609
+ }), /* @__PURE__ */ jsxs("select", {
610
+ id: "mirror-mode",
611
+ value: mirrorMode,
612
+ onChange: (e) => void saveMirrorMode(e.target.value),
613
+ className: "rounded-md border bg-background px-2 py-1 font-mono text-sm",
614
+ disabled: busy,
615
+ children: [/* @__PURE__ */ jsx("option", {
616
+ value: "minimal",
617
+ children: "minimal — assistant messages only"
618
+ }), /* @__PURE__ */ jsx("option", {
619
+ value: "verbose",
620
+ children: "verbose — also tool calls"
621
+ })]
622
+ })]
623
+ }),
624
+ /* @__PURE__ */ jsxs("div", {
625
+ className: "mt-2 flex items-center gap-2",
626
+ children: [
627
+ /* @__PURE__ */ jsx("label", {
628
+ htmlFor: "topic-icon",
629
+ className: "text-mocha-light",
630
+ children: "topic icon:"
631
+ }),
632
+ /* @__PURE__ */ jsx("input", {
633
+ id: "topic-icon",
634
+ value: iconEmoji,
635
+ onChange: (e) => setIconEmoji(e.target.value),
636
+ placeholder: "(profile default)",
637
+ maxLength: 8,
638
+ className: "w-32 rounded-md border bg-background px-2 py-1 font-mono text-sm",
639
+ disabled: busy
640
+ }),
641
+ /* @__PURE__ */ jsx("button", {
642
+ type: "button",
643
+ onClick: () => void saveIcon(),
644
+ disabled: busy || iconEmoji === (agent.telegramIconEmoji ?? ""),
645
+ className: "ghost-btn",
646
+ children: "save icon"
647
+ })
648
+ ]
649
+ }),
650
+ error && /* @__PURE__ */ jsx("p", {
651
+ className: "mt-2 text-sm text-rose-700",
652
+ children: error
653
+ }),
654
+ /* @__PURE__ */ jsx("p", {
655
+ className: "mt-3 text-[0.85em] text-mocha-light",
656
+ children: "When bound, every assistant turn for this agent gets mirrored to the topic. Typing in the topic triggers a new turn. iOS Telegram clients have a known limitation that prevents inline link deep-links from opening directly into a topic — bound state is still functional; navigate via the topic list manually."
657
+ })
658
+ ]
659
+ });
660
+ }
661
+ //#endregion
662
+ export { AgentDetailPage as component };