@songsid/agend 2.2.0-beta.14 → 2.2.0-beta.16

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 (76) hide show
  1. package/dist/cli.js +58 -3
  2. package/dist/cli.js.map +1 -1
  3. package/dist/compact-watch.d.ts +78 -0
  4. package/dist/compact-watch.js +247 -0
  5. package/dist/compact-watch.js.map +1 -0
  6. package/dist/config-validator.js +14 -0
  7. package/dist/config-validator.js.map +1 -1
  8. package/dist/daemon.js +45 -5
  9. package/dist/daemon.js.map +1 -1
  10. package/dist/doctor.js +7 -0
  11. package/dist/doctor.js.map +1 -1
  12. package/dist/fleet-context.d.ts +2 -0
  13. package/dist/fleet-manager.d.ts +17 -3
  14. package/dist/fleet-manager.js +101 -19
  15. package/dist/fleet-manager.js.map +1 -1
  16. package/dist/instance-avatar.d.ts +4 -3
  17. package/dist/instance-avatar.js +6 -5
  18. package/dist/instance-avatar.js.map +1 -1
  19. package/dist/launchd-handoff.d.ts +43 -0
  20. package/dist/launchd-handoff.js +56 -0
  21. package/dist/launchd-handoff.js.map +1 -0
  22. package/dist/locale.js +24 -4
  23. package/dist/locale.js.map +1 -1
  24. package/dist/login-shell.d.ts +21 -0
  25. package/dist/login-shell.js +98 -0
  26. package/dist/login-shell.js.map +1 -0
  27. package/dist/provider-probe.d.ts +29 -0
  28. package/dist/provider-probe.js +198 -2
  29. package/dist/provider-probe.js.map +1 -1
  30. package/dist/public-web-gateway.js +2 -0
  31. package/dist/public-web-gateway.js.map +1 -1
  32. package/dist/quickstart-api.d.ts +1 -1
  33. package/dist/quickstart-api.js +8 -6
  34. package/dist/quickstart-api.js.map +1 -1
  35. package/dist/quickstart.d.ts +12 -0
  36. package/dist/quickstart.js +36 -6
  37. package/dist/quickstart.js.map +1 -1
  38. package/dist/sd-notify.d.ts +22 -1
  39. package/dist/sd-notify.js +63 -10
  40. package/dist/sd-notify.js.map +1 -1
  41. package/dist/service-installer.d.ts +24 -0
  42. package/dist/service-installer.js +49 -1
  43. package/dist/service-installer.js.map +1 -1
  44. package/dist/service-plan.d.ts +11 -0
  45. package/dist/service-plan.js +23 -0
  46. package/dist/service-plan.js.map +1 -1
  47. package/dist/topic-commands.d.ts +16 -0
  48. package/dist/topic-commands.js +39 -15
  49. package/dist/topic-commands.js.map +1 -1
  50. package/dist/transcript-jsonl.d.ts +7 -2
  51. package/dist/transcript-jsonl.js +13 -7
  52. package/dist/transcript-jsonl.js.map +1 -1
  53. package/dist/ui/chat-store.js +32 -2
  54. package/dist/ui/chat-strings.js +8 -2
  55. package/dist/ui/panel-chat.js +4 -1
  56. package/dist/ui/settings-dialogs.js +9 -4
  57. package/dist/ui/settings-strings.js +6 -2
  58. package/dist/ui/settings-token.js +7 -0
  59. package/dist/ui/settings-wizard.js +5 -3
  60. package/dist/ui/shared/app.css +1 -0
  61. package/dist/ui/shared/panel-view.js +8 -0
  62. package/dist/ui/shared/view-strings.js +8 -0
  63. package/dist/usage/providers.d.ts +28 -8
  64. package/dist/usage/providers.js +96 -41
  65. package/dist/usage/providers.js.map +1 -1
  66. package/dist/view-api.js +26 -9
  67. package/dist/view-api.js.map +1 -1
  68. package/dist/web-api.js +1 -1
  69. package/dist/web-api.js.map +1 -1
  70. package/dist/web-commands.d.ts +14 -1
  71. package/dist/web-commands.js +6 -3
  72. package/dist/web-commands.js.map +1 -1
  73. package/dist/web-upload.d.ts +7 -3
  74. package/dist/web-upload.js +20 -8
  75. package/dist/web-upload.js.map +1 -1
  76. package/package.json +1 -1
@@ -10,11 +10,14 @@ export const LONG_HEAD_BYTES = 8 * 1024;
10
10
  * final reply, a pasted prompt) — is read whole and parsed, up to this size. Longer: skipped like any long line.
11
11
  */
12
12
  export const WHOLE_LINE_MAX_BYTES = 16 * 1024 * 1024;
13
- /** The line of `bytes` bytes at `offset`, as text, when it is at most `max` bytes; null otherwise or when it cannot be read whole. */
14
- export async function readWholeLine(path, offset, bytes, max = WHOLE_LINE_MAX_BYTES) {
13
+ /**
14
+ * The line of `bytes` bytes at `offset`, as text, when it is at most `max` bytes; null otherwise or when it cannot be read whole.
15
+ * `handle`: read from this open file (the caller's, left open) instead of opening `path`.
16
+ */
17
+ export async function readWholeLine(path, offset, bytes, max = WHOLE_LINE_MAX_BYTES, handle) {
15
18
  if (bytes <= 0 || bytes > max)
16
19
  return null;
17
- const fh = await open(path, "r");
20
+ const fh = handle ?? await open(path, "r");
18
21
  try {
19
22
  const buf = Buffer.alloc(bytes);
20
23
  for (let filled = 0; filled < bytes;) {
@@ -26,7 +29,8 @@ export async function readWholeLine(path, offset, bytes, max = WHOLE_LINE_MAX_BY
26
29
  return buf.toString("utf-8");
27
30
  }
28
31
  finally {
29
- await fh.close();
32
+ if (!handle)
33
+ await fh.close();
30
34
  }
31
35
  }
32
36
  /** A look's records in file order: each complete line, and each long line where it sits among them. */
@@ -97,11 +101,12 @@ function headText(buf, max) {
97
101
  export async function readNewLines(path, fromOffset, opts = {}) {
98
102
  const maxBytes = Math.max(1, opts.maxBytes ?? READ_MAX_BYTES);
99
103
  const maxLine = Math.max(1, opts.maxLine ?? LINE_MAX_BYTES);
100
- const stats = await stat(path);
104
+ // `handle`: one file the caller opened and checked (its identity), read and sized through that handle only.
105
+ const stats = opts.handle ? await opts.handle.stat() : await stat(path);
101
106
  const empty = (offset, skipping, more, longEnd) => ({ lines: [], offsets: [], newOffset: offset, long: [], more, skipping, ...(longEnd !== undefined ? { longEnd } : {}) });
102
107
  if (stats.size <= fromOffset)
103
108
  return empty(fromOffset, !!opts.skipping, false);
104
- const fh = await open(path, "r");
109
+ const fh = opts.handle ?? await open(path, "r");
105
110
  try {
106
111
  const read = async (at, length) => {
107
112
  const buffer = Buffer.alloc(length);
@@ -158,7 +163,8 @@ export async function readNewLines(path, fromOffset, opts = {}) {
158
163
  return { lines, offsets, newOffset, long, more: window.length === budget && newOffset < stats.size, skipping: false, ...ended };
159
164
  }
160
165
  finally {
161
- await fh.close();
166
+ if (!opts.handle)
167
+ await fh.close();
162
168
  }
163
169
  }
164
170
  const BOUNDARY_CHUNK = 64 * 1024;
@@ -1 +1 @@
1
- {"version":3,"file":"transcript-jsonl.js","sourceRoot":"","sources":["../src/transcript-jsonl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transcript-jsonl.js","sourceRoot":"","sources":["../src/transcript-jsonl.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,13 @@ function writePollMine(mine) {
31
31
  try { globalThis.localStorage?.setItem(POLL_MINE_KEY, JSON.stringify(mine)); } catch { /* storage unavailable */ }
32
32
  }
33
33
 
34
+ /** Board 003648ac: an answer the server marks `sent` (handed over, and its text says only that) leaves after this. */
35
+ const SENT_CLEAR_MS = 8000;
36
+ const COMPACT_DONE_CLEAR_MS = 6000;
37
+ const COMPACT_UNKNOWN_CLEAR_MS = 12000;
38
+ /** The fleet stops following a /compact after 10 minutes (compact-watch.ts); the page waits a minute more. */
39
+ const COMPACT_PAGE_LIMIT_MS = 11 * 60_000;
40
+
34
41
  /** Build a store. `deps` (fetch, toast, announce, t, now) are injected so tests can drive every path. */
35
42
  export function createChatStore(deps) {
36
43
  const s = {
@@ -77,6 +84,7 @@ export function createChatStore(deps) {
77
84
  const waits = i.state === "awaiting_input" ? (typeof i.interaction_summary === "string" ? i.interaction_summary : "") : null;
78
85
  if (i.name === s.current && waits != null && s.awaiting[i.name] == null) deps.announce(tf("chat.awaiting", i.name));
79
86
  s.awaiting[i.name] = waits;
87
+ if (i.compact) noteCompact(i.name, i.compact);
80
88
  }
81
89
  changed(null);
82
90
  }
@@ -337,10 +345,32 @@ export function createChatStore(deps) {
337
345
  if (!yes || !alive()) { delete s.commands[target]; changed(target); return; }
338
346
  r = await post({ confirm: r.confirm.token });
339
347
  }
340
- s.commands[target] = r && r.choices ? { command, busy: false, choices: r.choices }
341
- : r && r.error ? { command, busy: false, error: r.error } : { command, busy: false, text: (r && r.text) || "" };
348
+ const follow = r && r.follow && r.follow.kind === "compact" ? r.follow : null;
349
+ showCommand(target, r && r.choices ? { command, busy: false, choices: r.choices }
350
+ : r && r.error ? { command, busy: false, error: r.error }
351
+ : { command, busy: false, text: (r && r.text) || "", ...(r && r.sent === true ? { sent: true } : {}), ...(follow ? { compact: follow.id ? "running" : "unknown", watch: follow.id } : {}) });
342
352
  changed(target);
343
353
  }
354
+ // Board 003648ac: an answer the server marks `sent` leaves by itself; a sent /compact says "compacting…" until the
355
+ // status frame's `compact` (same watch id) says it ended, then leaves when it is done. Everything else — a refusal
356
+ // (even one worded as text), a list to choose from, an answer to read, a compaction timed out or lost — stays.
357
+ function showCommand(target, card) {
358
+ s.commands[target] = card;
359
+ const later = (ms, fn) => deps.setTimeout(() => { if (s.commands[target] === card) { fn(); changed(target); } }, ms);
360
+ const leave = () => { delete s.commands[target]; };
361
+ if (card.busy || card.error || card.choices || card.compact === "timeout" || card.compact === "lost") return;
362
+ if (card.compact === "running") later(COMPACT_PAGE_LIMIT_MS, () => showCommand(target, { ...card, compact: "timeout" }));
363
+ else if (card.compact === "done") later(COMPACT_DONE_CLEAR_MS, leave);
364
+ else if (card.compact === "unknown") later(COMPACT_UNKNOWN_CLEAR_MS, leave);
365
+ else if (card.sent === true) later(SENT_CLEAR_MS, leave);
366
+ }
367
+ /** The status frame's word on a followed /compact: done, timed out, or lost. */
368
+ function noteCompact(name, view) {
369
+ const c = s.commands[name];
370
+ if (!c || c.compact !== "running" || !view || view.id !== c.watch || !["done", "timeout", "lost"].includes(view.state)) return;
371
+ showCommand(name, { ...c, compact: view.state });
372
+ changed(name);
373
+ }
344
374
  function dismissCommand(target) { delete s.commands[target]; changed(target); }
345
375
  /** Whether a command can start for `target` now (none is out): checked before a typed command line is consumed. */
346
376
  const commandFree = (target) => !(s.commands[target] && s.commands[target].busy);
@@ -4,6 +4,9 @@ import { register } from "/assets/app-i18n.js";
4
4
  register("chat", {
5
5
  en: {
6
6
  cmdConfirm: "Clear", cmdConfirmBackend: "Switch and restart", cmdRunning: "Running /{0}…", cmdNotCommand: "Not a command here — Enter sends it as a message.", cmdDismiss: "Dismiss", cmdCommands: "Commands",
7
+ compactRunning: "Compacting…", compactDone: "✅ Conversation compacted", compactUnknown: "This backend does not report when compaction ends.",
8
+ compactTimeout: "No sign that compaction finished within {0} minutes — check the agent's own chat.",
9
+ compactLost: "Can't follow this compaction: the command did not reach the agent, or the agent stopped or changed backend.",
7
10
  cmdChoose: "Choose", cmdCurrent: "current", chipCompact: "Compact", chipClear: "Clear…", chipContext: "Context {0}% used",
8
11
  chipModel: "Model: {0} — choose another", chipEffort: "Effort: {0} — choose another",
9
12
  cmd_ctx: "Context use, model and effort", cmd_compact: "Compact the conversation", cmd_clear: "Clear the conversation (asks first)",
@@ -19,7 +22,7 @@ register("chat", {
19
22
  a11yWorking: "{0} started working", a11yIdle: "{0} finished", a11yReplied: "{0} replied", conversationWith: "Conversation with {0}",
20
23
  stopping: "Stopping {0}…", awaiting: "{0} is waiting for your input", approxNote: "Read from the terminal, so approximate", elapsedTitle: "Time since this page saw it start (approximate)",
21
24
  dropHere: "Drop files to attach them", asText: "As text", pasteAttached: "A long paste ({0} characters) was attached as a text file", pasteKeptAsText: "No room for another attachment: the long paste stays as text",
22
- send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerMore: "/ for commands · Esc stops the reply", attachTitle: "Attach files (images, PDF, text — or paste / drop them)",
25
+ send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerMore: "/ for commands · Esc stops the reply", attachTitle: "Attach files (any type — or paste / drop them)",
23
26
  pvPreview: "Preview", pvStop: "Stop", pvDownload: "Download", pvMenu: "Preview options", pvHtml: "HTML",
24
27
  pvAllowButton: "Allow previews", pvAllow: "Allow HTML previews — they may be able to send data out", pvDisallow: "Stop allowing HTML previews",
25
28
  pvConfirm: "Allow HTML previews?\n\nThis applies on every device where you are signed in. A preview runs the agent's HTML in an isolated frame. It cannot use your login, but it may be able to send data out, and it can slow or freeze this tab. You still click Preview each time.",
@@ -89,6 +92,9 @@ register("chat", {
89
92
  },
90
93
  "zh-TW": {
91
94
  cmdConfirm: "清空", cmdConfirmBackend: "切換並重新啟動", cmdRunning: "執行 /{0} 中…", cmdNotCommand: "這裡沒有這個指令——按 Enter 會當成一般訊息送出。", cmdDismiss: "關閉", cmdCommands: "指令",
95
+ compactRunning: "壓縮中…", compactDone: "✅ 對話已壓縮", compactUnknown: "這個後端不會回報壓縮何時完成。",
96
+ compactTimeout: "{0} 分鐘內沒有看到壓縮完成的紀錄,請到代理自己的對話確認。",
97
+ compactLost: "無法再追蹤這次壓縮:指令沒有送到代理,或代理已停止、換了後端。",
92
98
  cmdChoose: "選擇", cmdCurrent: "目前", chipCompact: "壓縮", chipClear: "清空…", chipContext: "已用 {0}% 的 context",
93
99
  chipModel: "模型:{0}——選擇其他", chipEffort: "推理強度:{0}——選擇其他",
94
100
  cmd_ctx: "context 用量、模型與推理強度", cmd_compact: "壓縮對話", cmd_clear: "清空對話(會先確認)",
@@ -104,7 +110,7 @@ register("chat", {
104
110
  a11yWorking: "{0} 開始工作", a11yIdle: "{0} 已完成", a11yReplied: "{0} 已回覆", conversationWith: "與 {0} 的對話",
105
111
  stopping: "正在中止 {0}…", awaiting: "{0} 正在等你回應", approxNote: "從終端畫面判讀,僅供參考", elapsedTitle: "從這個頁面看到它開始工作起算(約略)",
106
112
  dropHere: "放開即可附加檔案", asText: "改為文字", pasteAttached: "貼上的長文字({0} 個字元)已改為文字檔附加", pasteKeptAsText: "附件已滿,貼上的長文字保留為一般文字",
107
- send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerMore: "/ 叫出指令 · Esc 中止回覆", attachTitle: "附加檔案(圖片、PDF、文字檔,也可以貼上或拖曳)",
113
+ send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerMore: "/ 叫出指令 · Esc 中止回覆", attachTitle: "附加檔案(任何類型,也可以貼上或拖曳)",
108
114
  pvPreview: "預覽", pvStop: "停止", pvDownload: "下載", pvMenu: "預覽選項", pvHtml: "HTML",
109
115
  pvAllowButton: "允許預覽", pvAllow: "允許預覽 HTML(預覽可能把資料傳出去)", pvDisallow: "不再允許預覽 HTML",
110
116
  pvConfirm: "要允許預覽 HTML 嗎?\n\n這會套用到你登入的每一台裝置。預覽會在隔離的框架裡執行 agent 的 HTML。它無法使用你的登入,但可能把資料傳出去,也可能讓這個分頁變慢或卡住。每次仍需要你按「預覽」。",
@@ -322,6 +322,9 @@ function CommandCard({ name, lease }) {
322
322
  aria-label=${t("chat.cmdDismiss")} onClick=${() => store.dismissCommand(name)}><${Icon} name="close" size=${14} /></button>`}</div>
323
323
  ${c.error ? html`<p class="cmd-text">${c.error}</p>` : null}
324
324
  ${c.text ? html`<pre class="cmd-text">${c.text}</pre>` : null}
325
+ ${c.compact ? html`<p class="cmd-text cmd-phase">${c.compact === "running" ? t("chat.compactRunning")
326
+ : c.compact === "done" ? t("chat.compactDone") : c.compact === "timeout" ? t("chat.compactTimeout", "10")
327
+ : c.compact === "lost" ? t("chat.compactLost") : t("chat.compactUnknown")}</p>` : null}
325
328
  ${c.choices && c.choices.prompt ? html`<pre class="cmd-text">${c.choices.prompt}</pre>` : null}
326
329
  ${c.choices ? html`<div class="cmd-choices" role="group" aria-label=${t("chat.cmdChoose")}>${c.choices.options.map((o) => html`<button key=${o.id} type="button"
327
330
  class=${`btn btn-sm${o.id === c.choices.current ? " current" : ""}`} aria-current=${o.id === c.choices.current ? "true" : undefined} onClick=${() => run(o.id)}>${o.label}</button>`)}</div>` : null}
@@ -434,7 +437,7 @@ function Composer({ name, lease, busy }) {
434
437
  ${files.length ? html`<div class="pending-files">${files.map((f, i) => html`<${FileChip} key=${`${f.name}-${i}`} f=${f} i=${i} name=${name} />`)}</div>` : null}
435
438
  <div class="composer">
436
439
  <button id="attachBtn" type="button" class="icon-btn" title=${t("chat.attachTitle")} aria-label=${t("chat.attachTitle")} onClick=${() => fileIn.current && fileIn.current.click()}><${Icon} name="attach" /></button>
437
- <input ref=${fileIn} type="file" multiple hidden accept="image/png,image/jpeg,image/gif,image/webp,application/pdf,text/*,.md,.csv,.json,.log,.yaml,.yml"
440
+ <input ref=${fileIn} type="file" multiple hidden
438
441
  onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }} />
439
442
  <textarea id="msgIn" ref=${input} rows="1" value=${value} placeholder=${t("chat.composerPlaceholder", name)} aria-label=${t("chat.composerPlaceholder", name)}
440
443
  autocomplete="off" aria-controls=${palette ? "cmdPalette" : undefined} aria-expanded=${palette ? "true" : "false"}
@@ -10,7 +10,7 @@ import { Icon } from "/assets/ui-icons.js";
10
10
  import { toast } from "/assets/ui-toast.js";
11
11
  import { confirmDialog } from "/assets/ui-confirm.js";
12
12
  import { api, confirmedWrite, newKey } from "./settings-confirm.js";
13
- import { botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
13
+ import { adminUserIdError, adminUserIdPattern, botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
14
14
  import { DiscordServerPicker } from "./settings-discord.js";
15
15
  import { startOperation } from "./settings-apply.js";
16
16
  import {
@@ -466,17 +466,18 @@ export function BotDialog({ id, ctx, onClose }) {
466
466
  */
467
467
  export function NewBotDialog({ ctx, onClose }) {
468
468
  const lease = useLease("new-bot");
469
- const [f, setF] = useState({ type: "discord", id: "", token: "", identity: null, guilds: [], group: "", general: "" });
469
+ const [f, setF] = useState({ type: "discord", id: "", token: "", identity: null, guilds: [], group: "", general: "", admin: "" });
470
470
  const [plan, setPlan] = useState(null);
471
471
  const [busy, setBusy] = useState(false);
472
472
  const [err, setErr] = useState("");
473
473
  // The form's revision (#1529 review): any change to the token, platform, id or group moves it; a Verify or a plan
474
474
  // that began before is dropped when it lands — never restoring an old token, naming it, or planning for it.
475
475
  const rev = useRef(0);
476
- const body = (x) => ({ platform: x.type, connection_only: true, ...(x.id.trim() ? { channel_id: x.id.trim() } : {}),
476
+ const body = (x) => ({ platform: x.type, connection_only: true, admin_user_id: x.admin.trim(), ...(x.id.trim() ? { channel_id: x.id.trim() } : {}),
477
477
  ...(x.group.trim() ? (x.type === "discord" ? { guild_id: x.group.trim() } : { group_id: x.group.trim() }) : {}),
478
478
  ...(x.type === "discord" && x.general.trim() ? { general_channel_id: x.general.trim() } : {}) });
479
479
  const planFor = async (x) => {
480
+ if (adminUserIdError(x.type, x.admin.trim())) { setPlan(null); return null; }
480
481
  const at = rev.current;
481
482
  const res = await api("/api/settings/quickstart/plan", { method: "POST", body: JSON.stringify(body(x)) }).catch(() => ({ ok: false, body: {} }));
482
483
  if (!lease.current() || rev.current !== at) return null;
@@ -501,6 +502,8 @@ export function NewBotDialog({ ctx, onClose }) {
501
502
  };
502
503
  const save = async () => {
503
504
  if (!f.identity || !f.identity.valid) { setErr(tn("wizardNeedVerify")); return; }
505
+ const adminError = adminUserIdError(f.type, f.admin.trim());
506
+ if (adminError) { setErr(adminError); return; }
504
507
  const x = f;
505
508
  setBusy(true);
506
509
  const p = await planFor(x);
@@ -514,7 +517,7 @@ export function NewBotDialog({ ctx, onClose }) {
514
517
  };
515
518
  // A change of platform or id is another connection: what was verified and planned no longer applies. The server or
516
519
  // group does not change the id or the token's name, so the plan (and its name under Advanced) stays.
517
- const set = (k) => (v) => { rev.current++; setF((x) => ({ ...x, [k]: v, ...(k === "type" ? { identity: null, guilds: [], group: "", general: "" } : {}) })); if (k !== "group" && k !== "general") setPlan(null); };
520
+ const set = (k) => (v) => { rev.current++; setF((x) => ({ ...x, [k]: v, ...(k === "type" ? { identity: null, guilds: [], group: "", general: "", admin: "" } : {}) })); if (k !== "group" && k !== "general") setPlan(null); };
518
521
  return html`<${Dialog} title=${tn("newBot")} onClose=${onClose} busy=${busy}
519
522
  actions=${html`<button type="button" class="btn" disabled=${busy} onClick=${onClose}>${tn("cancel")}</button>
520
523
  <button type="button" class="btn btn-primary" disabled=${busy || !f.identity || !f.identity.valid} onClick=${save}>${tn("save")}</button>`}>
@@ -528,6 +531,8 @@ export function NewBotDialog({ ctx, onClose }) {
528
531
  guild=${f.group} onGuild=${set("group")} channel=${f.general} onChannel=${set("general")} />`
529
532
  : html`<div class="field"><label for="nb-group">${tn("groupIdField")}</label>
530
533
  <input id="nb-group" type="text" value=${f.group} onInput=${(e) => set("group")(e.target.value.trim())} /></div>`}
534
+ <div class="field"><label for="nb-user">${tn("wizardAdminUser")}</label>
535
+ <input id="nb-user" type="text" required=${true} inputmode="numeric" pattern=${adminUserIdPattern(f.type)} value=${f.admin} onInput=${(e) => set("admin")(e.target.value.trim())} /></div>
531
536
  <${Drawer} title=${tn("advancedSection")}>
532
537
  <div class="field"><label for="nb-id">${tn("connectionIdOptional")}</label><input id="nb-id" type="text" placeholder=${plan ? plan.channel_id : f.type}
533
538
  value=${f.id} onInput=${(e) => set("id")(e.target.value)} onBlur=${() => { if (f.identity && f.identity.valid) planFor(f); }} /></div>
@@ -164,7 +164,9 @@ register("settings", {
164
164
  wizardWaitingStart: "Waiting for a message in the group…",
165
165
  wizardNoStartYet: "No group message yet — post one and press Detect again.",
166
166
  wizardGeneralChannel: "General channel id",
167
- wizardAdminUser: "Your user id (becomes the first allowed user)",
167
+ wizardAdminUser: "Your user ID (required; becomes the first allowed user)",
168
+ wizardNeedDiscordAdmin: "Enter your Discord user ID: 17–20 digits, without a leading zero.",
169
+ wizardNeedTelegramAdmin: "Enter your Telegram user ID: 1–20 positive digits, without a leading zero.",
168
170
  wizardNeedVerify: "Verify the token before continuing.",
169
171
  wizardPlanLoading: "Building the plan…",
170
172
  wizardWillWrite: "This will be written:",
@@ -535,7 +537,9 @@ register("settings", {
535
537
  wizardWaitingStart: "等待群組訊息中…",
536
538
  wizardNoStartYet: "還沒收到群組訊息 —— 發一則之後再按一次。",
537
539
  wizardGeneralChannel: "General 頻道 id",
538
- wizardAdminUser: "你的 user id(會成為第一個允許使用者)",
540
+ wizardAdminUser: "你的使用者 ID(必填;會成為第一個允許使用者)",
541
+ wizardNeedDiscordAdmin: "請輸入 Discord 使用者 ID:17–20 位數字,開頭不能是 0。",
542
+ wizardNeedTelegramAdmin: "請輸入 Telegram 使用者 ID:1–20 位正整數字,開頭不能是 0。",
539
543
  wizardNeedVerify: "請先驗證 token 再繼續。",
540
544
  wizardPlanLoading: "正在產生方案…",
541
545
  wizardWillWrite: "將寫入:",
@@ -10,6 +10,13 @@ import { api } from "./settings-confirm.js";
10
10
 
11
11
  const tn = (k, ...v) => t(`settings.${k}`, ...v);
12
12
 
13
+ // UI feedback only: the plan and commit independently enforce this on the server.
14
+ export const adminUserIdPattern = (platform) => platform === "discord" ? "[1-9][0-9]{16,19}" : "[1-9][0-9]{0,19}";
15
+ export function adminUserIdError(platform, value) {
16
+ return typeof value === "string" && new RegExp(`^${adminUserIdPattern(platform)}$`).test(value)
17
+ ? null : tn(platform === "discord" ? "wizardNeedDiscordAdmin" : "wizardNeedTelegramAdmin");
18
+ }
19
+
13
20
  /** Who this token is: { valid, username?, reason? } — one outbound check, the token sent once and not kept. */
14
21
  export async function verifyBotToken(platform, token) {
15
22
  const res = await api("/api/settings/quickstart/probe", { method: "POST", body: JSON.stringify({ action: "verify", platform, token }) })
@@ -10,7 +10,7 @@ import { api } from "./settings-confirm.js";
10
10
  import { startOperation } from "./settings-apply.js";
11
11
  import { BACKENDS, DEFAULT_SCHEMA, impactOf, toYaml } from "./settings-model.js";
12
12
  import { Drawer, Select } from "./settings-dialogs.js";
13
- import { botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
13
+ import { adminUserIdError, adminUserIdPattern, botHandle, TokenEnvNote, TokenField, verifyBotToken } from "./settings-token.js";
14
14
  import { DiscordServerPicker } from "./settings-discord.js";
15
15
 
16
16
  const tn = (k, ...v) => t(`settings.${k}`, ...v);
@@ -90,6 +90,8 @@ export function SetupWizard({ ctx, onClose }) {
90
90
  if (w.step === 2) { setW({ ...w, step: 3 }); return; }
91
91
  if (w.step === 3) {
92
92
  if (!w.identity || !w.identity.valid) { setErr(tn("wizardNeedVerify")); return; }
93
+ const adminError = adminUserIdError(w.platform, w.admin_user_id);
94
+ if (adminError) { setErr(adminError); return; }
93
95
  const at = rev.current;
94
96
  setBusy(tn("wizardPlanLoading"));
95
97
  const res = await post("/api/settings/quickstart/plan", input());
@@ -146,12 +148,12 @@ export function SetupWizard({ ctx, onClose }) {
146
148
  <${DiscordServerPicker} idPrefix="wz" token=${w.identity && w.identity.valid ? w.token : ""} bot=${w.identity && botHandle(w.identity.username)}
147
149
  invite=${w.identity && w.identity.invite} portal=${w.identity && w.identity.portal} guilds=${w.guilds} onGuilds=${(g) => setW((x) => ({ ...x, guilds: g }))}
148
150
  guild=${w.guild_id} onGuild=${set("guild_id")} channel=${w.general_channel_id} onChannel=${set("general_channel_id")} />
149
- <div class="field"><label for="wz-user">${tn("wizardAdminUser")}</label><input id="wz-user" type="text" placeholder=${tn("discordUserPlaceholder")} value=${w.admin_user_id} onInput=${(e) => set("admin_user_id")(e.target.value.trim())} /></div>`
151
+ <div class="field"><label for="wz-user">${tn("wizardAdminUser")}</label><input id="wz-user" type="text" required=${true} inputmode="numeric" pattern=${adminUserIdPattern(w.platform)} placeholder=${tn("discordUserPlaceholder")} value=${w.admin_user_id} onInput=${(e) => set("admin_user_id")(e.target.value.trim())} /></div>`
150
152
  : html`
151
153
  <div class="field"><label for="wz-group">${tn("groupIdField")}</label><input id="wz-group" type="text" placeholder="-1001234567890" value=${w.group_id} onInput=${(e) => set("group_id")(e.target.value.trim())} />
152
154
  <p class="note">${tn("wizardDetectHint")}</p></div>
153
155
  <div class="dlg-inline-actions"><button type="button" class="btn btn-sm" disabled=${!!busy} onClick=${detect}>${tn("wizardDetect")}</button></div>
154
- <div class="field"><label for="wz-user">${tn("wizardAdminUser")}</label><input id="wz-user" type="text" placeholder=${tn("telegramUserPlaceholder")} value=${w.admin_user_id} onInput=${(e) => set("admin_user_id")(e.target.value.trim())} /></div>`}`;
156
+ <div class="field"><label for="wz-user">${tn("wizardAdminUser")}</label><input id="wz-user" type="text" required=${true} inputmode="numeric" pattern=${adminUserIdPattern(w.platform)} placeholder=${tn("telegramUserPlaceholder")} value=${w.admin_user_id} onInput=${(e) => set("admin_user_id")(e.target.value.trim())} /></div>`}`;
155
157
  } else {
156
158
  body = !w.plan ? html`<p class="note">${tn("wizardPlanLoading")}</p>` : html`
157
159
  <p><strong>${tn("wizardWillWrite")}</strong></p>
@@ -1075,6 +1075,7 @@ strong.org-name { font-size: var(--fs-sm); color: var(--text); }
1075
1075
  .cmd-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); }
1076
1076
  .cmd-name { font-weight: var(--fw-semibold); }
1077
1077
  .cmd-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--font-ui); color: var(--text-2); }
1078
+ .cmd-phase { color: var(--text); font-weight: var(--fw-semibold); }
1078
1079
  .cmd-choices { display: flex; flex-wrap: wrap; gap: var(--s-1); max-height: 180px; overflow-y: auto; }
1079
1080
  .cmd-choices .btn.current { border-color: var(--accent); color: var(--accent); }
1080
1081
  .quick-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); margin-bottom: var(--s-2); font-size: var(--fs-xs); color: var(--text-2); }
@@ -317,6 +317,8 @@ function EditProfile({ it, onClose }) {
317
317
  const [f, setF] = useState({ display: it.display_name || "", role: it.role || "", desc: it.description || "" });
318
318
  const [busy, setBusy] = useState(false);
319
319
  const [msg, setMsg] = useState(null);
320
+ // #1630 follow-up: the uploaded picture is removed on Save (Cancel keeps it); a newly picked file wins over removing.
321
+ const [removePic, setRemovePic] = useState(false);
320
322
  const file = useRef(null);
321
323
  async function save() {
322
324
  if (busy) return;
@@ -326,6 +328,7 @@ function EditProfile({ it, onClose }) {
326
328
  const name = encodeURIComponent(it.instance_name);
327
329
  const draft = { display_name: f.display, role: f.role, description: f.desc };
328
330
  const picked = file.current && file.current.files && file.current.files[0];
331
+ const removing = removePic;
329
332
  try {
330
333
  const r = await fetch(`/api/profile/${name}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(draft) });
331
334
  if (!lease.current()) return;
@@ -334,6 +337,9 @@ function EditProfile({ it, onClose }) {
334
337
  if (picked) {
335
338
  const ar = await fetch(`/api/avatar/${name}`, { method: "POST", headers: { "Content-Type": picked.type }, body: picked });
336
339
  if (!ar.ok) { const e = await ar.json().catch(() => ({})); throw new Error(tn("avatarFailed", { error: e.error || ar.status })); }
340
+ } else if (removing) { // a newly picked file replaces it instead
341
+ const dr = await fetch(`/api/avatar/${name}`, { method: "DELETE" });
342
+ if (!dr.ok) { const e = await dr.json().catch(() => ({})); throw new Error(tn("avatarRemoveFailed", { error: e.error || dr.status })); }
337
343
  }
338
344
  if (!lease.current()) return;
339
345
  setBusy(false);
@@ -348,6 +354,8 @@ function EditProfile({ it, onClose }) {
348
354
  <label class="field"><span>${tn("role")}</span><input value=${f.role} onInput=${(e) => setF({ ...f, role: e.target.value })} /></label>
349
355
  <label class="field"><span>${tn("description")}</span><textarea rows="3" value=${f.desc} onInput=${(e) => setF({ ...f, desc: e.target.value })}></textarea></label>
350
356
  <label class="field"><span>${tn("avatar")}</span><input ref=${file} type="file" accept="image/png,image/jpeg,image/gif,image/webp" /></label>
357
+ ${it.has_avatar ? html`<div class="field-row"><button type="button" class="btn btn-sm" aria-pressed=${removePic ? "true" : "false"}
358
+ onClick=${() => setRemovePic(!removePic)}>${tn(removePic ? "keepPicture" : "removePicture")}</button>${removePic ? html`<span class="muted">${tn("removePictureNote")}</span>` : null}</div>` : null}
351
359
  </fieldset></form>
352
360
  ${msg ? html`<p class="err" role="alert">${msg}${msg === tn("signInToSave") ? html` <a class="link" href=${signInHref()}>${t("app.signIn")}</a>` : null}</p>` : null}
353
361
  </${Dialog}>`;
@@ -152,6 +152,10 @@ register("view", {
152
152
  "paneFailed": "⚠ Could not load pane for {name} (HTTP {status}).",
153
153
  "signInToSave": "Sign in to save — your session may have ended.",
154
154
  "avatarFailed": "Avatar upload failed: {error}",
155
+ "avatarRemoveFailed": "Could not remove the picture: {error}",
156
+ "removePicture": "Remove picture",
157
+ "keepPicture": "Keep picture",
158
+ "removePictureNote": "Removed when you save. The bot's avatar, the persona emoji or the first letter shows instead.",
155
159
  "saved": "Saved",
156
160
  "saveFailed": "Save failed: {error}",
157
161
  "signInToEdit": "Sign in to edit",
@@ -328,6 +332,10 @@ register("view", {
328
332
  "paneFailed": "⚠ 無法載入 {name} 的 pane(HTTP {status})。",
329
333
  "signInToSave": "請先登入才能儲存:你的 session 可能已結束。",
330
334
  "avatarFailed": "上傳頭像失敗:{error}",
335
+ "avatarRemoveFailed": "無法移除圖片:{error}",
336
+ "removePicture": "移除圖片",
337
+ "keepPicture": "保留圖片",
338
+ "removePictureNote": "儲存後移除,改顯示 Bot 的頭像、persona emoji 或名稱第一個字。",
331
339
  "saved": "已儲存",
332
340
  "saveFailed": "儲存失敗:{error}",
333
341
  "signInToEdit": "登入後才能編輯",
@@ -143,22 +143,42 @@ export interface ClaudeUsageScope {
143
143
  * always; beside profiles (`besideProfiles`), its statusline fallback leaves the profiles' instances out.
144
144
  */
145
145
  export declare function fetchClaudeUsage(storeHome?: string, scope?: ClaudeUsageScope): Promise<Omit<ProviderUsage, "id" | "name">>;
146
+ /**
147
+ * One quota bucket of `retrieveUserQuotaSummary` (agy's proto: QuotaSummaryBucket). `group` is not on the wire: it is
148
+ * the display name of the QuotaSummaryGroup the bucket came in, copied on by agyQuotaBuckets.
149
+ */
146
150
  interface AgyBucket {
147
151
  bucketId?: string;
148
152
  displayName?: string;
153
+ window?: string;
154
+ disabled?: boolean;
149
155
  remainingFraction?: number;
150
156
  resetTime?: string;
157
+ group?: string;
151
158
  }
159
+ interface AgyQuotaSummary {
160
+ groups?: {
161
+ displayName?: string;
162
+ buckets?: AgyBucket[];
163
+ }[];
164
+ buckets?: AgyBucket[];
165
+ }
166
+ /** The summary's buckets, each tagged with its group's name. The top-level list (deprecated in the proto) only counts
167
+ * when no group carries any bucket. */
168
+ export declare function agyQuotaBuckets(body: AgyQuotaSummary): AgyBucket[];
152
169
  /**
153
- * Pool the summary's buckets into at most four metrics.
170
+ * Pool the summary's buckets into one metric per pool and limit.
171
+ *
172
+ * Three payload shapes exist:
173
+ * - pooled ids (`gemini-5h`, `gemini-weekly`, `3p-5h`, `3p-weekly`), which map 1:1;
174
+ * - PER-MODEL buckets (`gemini-3.6-flash-high`, `claude-sonnet-4-6`, …), aggregated into a Gemini pool and an
175
+ * everything-else pool;
176
+ * - (#1898, agy 2026-10) named groups ("Gemini Models", "Claude and GPT Models") whose buckets are the group's
177
+ * five-hour and weekly limits. The group decides the pool; the bucket's window, id or name decides the limit.
154
178
  *
155
- * Two payload shapes exist. Older accounts report pooled ids (`gemini-5h`,
156
- * `gemini-weekly`, `3p-5h`, `3p-weekly`), which map 1:1. This machine's live
157
- * response instead lists PER-MODEL buckets (`gemini-3.6-flash-high`,
158
- * `claude-sonnet-4-6`, `gpt-oss-120b-medium`, …) — those aggregate into a
159
- * Gemini pool and an everything-else pool, keeping each pool's WORST remaining
160
- * fraction, because eleven per-model meters is noise and the binding constraint
161
- * is the one that runs out first.
179
+ * Every metric keeps the WORST remaining fraction of its buckets, because the binding constraint is the one that runs
180
+ * out first. Within one pool a bucket with a known limit is authoritative over per-model buckets; one pool never hides
181
+ * another. Disabled buckets (agy dims them, no number) are left out.
162
182
  */
163
183
  export declare function agyPoolMetrics(buckets: AgyBucket[]): UsageMetric[];
164
184
  interface AgyStoredToken {