@songsid/agend 2.1.0-beta.23 → 2.1.0-beta.4

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 (84) hide show
  1. package/README.md +0 -1
  2. package/README.zh-TW.md +1 -2
  3. package/dist/access-path.js +3 -3
  4. package/dist/access-path.js.map +1 -1
  5. package/dist/agent-endpoint.d.ts +0 -8
  6. package/dist/agent-endpoint.js +8 -36
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/backend/antigravity.js +2 -3
  9. package/dist/backend/antigravity.js.map +1 -1
  10. package/dist/backend/factory.js +1 -5
  11. package/dist/backend/factory.js.map +1 -1
  12. package/dist/backend/kiro.js +2 -3
  13. package/dist/backend/kiro.js.map +1 -1
  14. package/dist/backend/types.d.ts +2 -12
  15. package/dist/backend/types.js +0 -1
  16. package/dist/backend/types.js.map +1 -1
  17. package/dist/channel/adapters/discord.d.ts +0 -20
  18. package/dist/channel/adapters/discord.js +11 -69
  19. package/dist/channel/adapters/discord.js.map +1 -1
  20. package/dist/channel/adapters/telegram.js +1 -4
  21. package/dist/channel/adapters/telegram.js.map +1 -1
  22. package/dist/classic-channel-manager.d.ts +1 -6
  23. package/dist/classic-channel-manager.js +3 -26
  24. package/dist/classic-channel-manager.js.map +1 -1
  25. package/dist/cli.js +4 -48
  26. package/dist/cli.js.map +1 -1
  27. package/dist/config-validator.js +1 -50
  28. package/dist/config-validator.js.map +1 -1
  29. package/dist/config.d.ts +1 -3
  30. package/dist/config.js +1 -13
  31. package/dist/config.js.map +1 -1
  32. package/dist/context-guardian.d.ts +0 -2
  33. package/dist/context-guardian.js +2 -10
  34. package/dist/context-guardian.js.map +1 -1
  35. package/dist/daemon-entry.js +1 -2
  36. package/dist/daemon-entry.js.map +1 -1
  37. package/dist/daemon.d.ts +1 -25
  38. package/dist/daemon.js +30 -156
  39. package/dist/daemon.js.map +1 -1
  40. package/dist/fleet-context.d.ts +0 -1
  41. package/dist/fleet-manager.d.ts +5 -30
  42. package/dist/fleet-manager.js +106 -312
  43. package/dist/fleet-manager.js.map +1 -1
  44. package/dist/general-knowledge/skills/fleet-config/SKILL.md +1 -1
  45. package/dist/general-knowledge/skills/model-discovery/SKILL.md +0 -1
  46. package/dist/instance-lifecycle.d.ts +1 -2
  47. package/dist/instance-lifecycle.js +4 -15
  48. package/dist/instance-lifecycle.js.map +1 -1
  49. package/dist/locale.js +0 -2
  50. package/dist/locale.js.map +1 -1
  51. package/dist/logger.d.ts +0 -6
  52. package/dist/logger.js +9 -28
  53. package/dist/logger.js.map +1 -1
  54. package/dist/outbound-handlers.d.ts +0 -3
  55. package/dist/outbound-handlers.js +0 -25
  56. package/dist/outbound-handlers.js.map +1 -1
  57. package/dist/outbound-schemas.d.ts +1 -2
  58. package/dist/outbound-schemas.js +1 -1
  59. package/dist/outbound-schemas.js.map +1 -1
  60. package/dist/settings-api.d.ts +2 -17
  61. package/dist/settings-api.js +8 -105
  62. package/dist/settings-api.js.map +1 -1
  63. package/dist/setup-wizard.js +0 -8
  64. package/dist/setup-wizard.js.map +1 -1
  65. package/dist/statusline-watcher.d.ts +1 -1
  66. package/dist/statusline-watcher.js +2 -3
  67. package/dist/statusline-watcher.js.map +1 -1
  68. package/dist/tmux-manager.d.ts +1 -1
  69. package/dist/tmux-manager.js.map +1 -1
  70. package/dist/topic-commands.d.ts +1 -14
  71. package/dist/topic-commands.js +23 -89
  72. package/dist/topic-commands.js.map +1 -1
  73. package/dist/transcript-monitor.js +0 -2
  74. package/dist/transcript-monitor.js.map +1 -1
  75. package/dist/types.d.ts +1 -16
  76. package/dist/ui/dashboard.html +2 -2
  77. package/dist/ui/settings.html +40 -251
  78. package/dist/web-api.d.ts +0 -1
  79. package/dist/web-api.js +15 -24
  80. package/dist/web-api.js.map +1 -1
  81. package/package.json +1 -2
  82. package/dist/backend/grok.d.ts +0 -51
  83. package/dist/backend/grok.js +0 -280
  84. package/dist/backend/grok.js.map +0 -1
@@ -8,7 +8,7 @@
8
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
9
  :root {
10
10
  --bg:#ffffff; --card:#f9fafb; --border:#e5e7eb; --fg:#111827; --muted:#6b7280;
11
- --accent:#2563eb; --accent-hover:#1d4ed8; --green:#059669; --red:#dc2626; --yellow:#b45309;
11
+ --accent:#2563eb; --accent-hover:#1d4ed8; --green:#059669; --red:#dc2626;
12
12
  --radius:8px; --radius-sm:6px;
13
13
  }
14
14
  body { background: var(--bg); color: var(--fg); font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: 14px; line-height: 1.5; padding-bottom: 60px; }
@@ -32,7 +32,7 @@
32
32
  .item .del-btn { visibility: hidden; }
33
33
  .item:hover .del-btn { visibility: visible; }
34
34
  .dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; background: #9ca3af; }
35
- .dot.running { background: var(--green); } .dot.paused { background:#f59e0b; } .dot.crashed { background: var(--red); } .dot.stopped { background: #9ca3af; }
35
+ .dot.running { background: var(--green); } .dot.crashed { background: var(--red); } .dot.stopped { background: #9ca3af; }
36
36
  .name { font-weight: 600; }
37
37
  .tag { font-size: 12px; color: var(--muted); background: #eef2ff; color: #3730a3; padding: 1px 7px; border-radius: 10px; }
38
38
  .tag.persona { background: #fdf2f8; color: #9d174d; }
@@ -49,7 +49,7 @@
49
49
  button.danger:hover { background: #fef2f2; }
50
50
  button.sm { padding: 4px 9px; font-size: 12px; }
51
51
  label { display: block; font-size: 12px; font-weight: 500; color: var(--muted); margin: 10px 0 4px; }
52
- input[type=text], input[type=number], select { width: 100%; font-family: inherit; font-size: 13px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; color: var(--fg); }
52
+ input[type=text], select { width: 100%; font-family: inherit; font-size: 13px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: #fff; color: var(--fg); }
53
53
  input:focus, select:focus { outline: 2px solid #bfdbfe; border-color: var(--accent); }
54
54
  .edit { padding: 14px 16px; background: #fff; border-top: 1px solid var(--border); }
55
55
  .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@@ -63,19 +63,6 @@
63
63
  .msg { font-size: 12px; margin-left: 4px; }
64
64
  .msg .e { color: var(--red); } .msg .w { color: #b45309; } .msg .s { color: var(--green); }
65
65
  .hint { font-size: 12px; color: var(--muted); margin-top: 6px; }
66
- .impact { display:inline-block; font-size:11px; color:var(--muted); margin-left:6px; font-weight:400; }
67
- .feedback { min-height:17px; font-size:12px; margin-top:3px; }
68
- .feedback.error { color:var(--red); } .feedback.warning { color:var(--yellow); }
69
- input.invalid, select.invalid { border-color:var(--red); background:#fef2f2; }
70
- button:disabled { opacity:.5; cursor:not-allowed; }
71
- .settings-section { border-bottom:1px solid var(--border); background:#fff; }
72
- .settings-section:last-child { border-bottom:none; }
73
- .settings-section > summary { cursor:pointer; list-style:none; padding:12px 16px; font-weight:650; background:#f9fafb; }
74
- .settings-section > summary::-webkit-details-marker { display:none; }
75
- .settings-section > summary::before { content:"▶"; display:inline-block; width:18px; color:var(--muted); font-size:10px; }
76
- .settings-section[open] > summary::before { content:"▼"; }
77
- .section-body { padding:0 0 2px; }
78
- .inherit-row { display:flex; gap:8px; align-items:center; margin-top:6px; color:var(--muted); font-size:12px; }
79
66
  .adv-body { display: none; padding: 14px 16px; }
80
67
  .adv-body.open { display: block; }
81
68
  .toolbar { display: flex; gap: 6px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
@@ -97,16 +84,6 @@
97
84
  .help-panel li { font-size: 13px; margin: 3px 0; }
98
85
  .help-panel .close { position: absolute; top: 14px; right: 16px; cursor: pointer; color: var(--muted); font-size: 22px; line-height: 1; background: none; border: none; }
99
86
  .help-panel .tip { margin-top: 16px; padding: 10px 12px; background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 13px; }
100
- @media (max-width: 640px) {
101
- header { padding:10px 12px; flex-wrap:wrap; } header h1 { font-size:16px; }
102
- main { padding:12px 10px; } section { margin-bottom:18px; }
103
- .grid2 { grid-template-columns:1fr; gap:0; }
104
- .item { padding:9px 10px; } .item-row { flex-wrap:wrap; }
105
- .item-row .spacer { flex-basis:100%; height:0; }
106
- .sub { margin-left:0; } .field { padding:10px 12px; }
107
- #agentSearch { width:130px !important; margin-right:0 !important; }
108
- button { min-height:36px; } button.sm { min-height:32px; }
109
- }
110
87
  </style>
111
88
  </head>
112
89
  <body>
@@ -164,7 +141,7 @@
164
141
  const $ = (id) => document.getElementById(id);
165
142
  const el = (tag, attrs = {}, ...kids) => { const e = document.createElement(tag); for (const k in attrs) { if (k === "class") e.className = attrs[k]; else if (k === "html") e.innerHTML = attrs[k]; else if (k.startsWith("on")) e.addEventListener(k.slice(2), attrs[k]); else e.setAttribute(k, attrs[k]); } for (const c of kids) if (c != null) e.append(c); return e; };
166
143
  const esc = (s) => String(s).replace(/[&<>]/g, c => ({ "&":"&amp;","<":"&lt;",">":"&gt;" }[c]));
167
- const BACKENDS = ["claude-code", "kiro-cli", "codex", "opencode", "gemini-cli", "antigravity", { value: "grok", label: "grok (experimental)" }];
144
+ const BACKENDS = ["claude-code", "kiro-cli", "codex", "opencode", "gemini-cli", "antigravity"];
168
145
 
169
146
  // ── i18n ──
170
147
  const I18N = {
@@ -175,8 +152,6 @@
175
152
  edit: "Edit", close: "Close", save: "Save", cancel: "Cancel", create: "Create", start: "Start", stop: "Stop",
176
153
  backend: "Backend", model: "Model", workingDir: "Working directory", channelBinding: "Channel binding",
177
154
  description: "Description", systemPrompt: "System prompt", tags: "Tags", generalTopicField: "General topic (fleet dispatcher)",
178
- runtimeResources: "Runtime & Resources", autoPause: "Auto-pause after", minutes: "minutes", inheritDefault: "Inherit from defaults",
179
- detection: "Detection", startup: "Startup", backendSection: "Backend", advancedSection: "Advanced & Access", hangEnabled: "Hang detector enabled", hangTimeout: "Stuck timeout", agentMode: "Agent mode", toolSet: "Tool set", logLevel: "Log level", concurrency: "Startup concurrency", staggerDelay: "Stagger delay", lightweight: "Lightweight mode", modelFailover: "Fallback model", displayName: "Display name", pause: "Pause", wake: "Wake", accessMode: "Access mode", affected: "Affected instances",
180
155
  language: "Language (bot replies)", defaultBackend: "Default backend", defaultModel: "Default model", allowedUsers: "Allowed users (primary bot)",
181
156
  classicAdmins: "ClassicBot admins", allowedGuilds: "Allowed guilds", type: "Type", adapterId: "Adapter id",
182
157
  tokenEnv: "Bot token env var", groupId: "Group / guild id", nameField: "Name",
@@ -193,8 +168,6 @@
193
168
  edit: "編輯", close: "關閉", save: "儲存", cancel: "取消", create: "建立", start: "啟動", stop: "停止",
194
169
  backend: "後端", model: "模型", workingDir: "工作目錄", channelBinding: "頻道綁定",
195
170
  description: "描述", systemPrompt: "System prompt", tags: "標籤", generalTopicField: "General topic(fleet 分派器)",
196
- runtimeResources: "執行環境與資源", autoPause: "閒置後自動暫停", minutes: "分鐘", inheritDefault: "沿用全域預設值",
197
- detection: "偵測", startup: "啟動", backendSection: "後端", advancedSection: "進階與存取", hangEnabled: "啟用卡住偵測", hangTimeout: "卡住逾時", agentMode: "Agent 模式", toolSet: "工具集", logLevel: "Log 等級", concurrency: "啟動併發數", staggerDelay: "錯開延遲", lightweight: "輕量模式", modelFailover: "備援模型", displayName: "顯示名稱", pause: "暫停", wake: "喚醒", accessMode: "存取模式", affected: "受影響 instances",
198
171
  language: "語言(bot 回覆)", defaultBackend: "預設後端", defaultModel: "預設模型", allowedUsers: "允許使用者(主 bot)",
199
172
  classicAdmins: "ClassicBot 管理員", allowedGuilds: "允許的伺服器", type: "類型", adapterId: "Adapter id",
200
173
  tokenEnv: "Bot token 環境變數", groupId: "群組 / guild id", nameField: "名稱",
@@ -278,14 +251,6 @@
278
251
  else if (!html) html = `<span class="e">✗ ${esc((b && b.error) || ("HTTP " + res.status))}</span>`;
279
252
  elm.innerHTML = html;
280
253
  }
281
- function showBanner(text, error = false) {
282
- const b = $("banner");
283
- b.style.display = "block";
284
- b.style.background = error ? "#fef2f2" : "#ecfdf5";
285
- b.style.color = error ? "var(--red)" : "var(--green)";
286
- b.textContent = text;
287
- setTimeout(() => { b.style.display = "none"; }, 6000);
288
- }
289
254
 
290
255
  // ── State ──
291
256
  const state = { fleet: {}, classic: {}, classicStatus: {}, live: {}, fleetUp: false, advFmt: "yaml", advEdit: false, collapsedGroups: new Set(), version: "", newAgentOpen: false, newBotOpen: false };
@@ -303,26 +268,7 @@
303
268
  box.append(chips, el("div", { class: "chip-add" }, inp, el("button", { class: "sm", onclick: add }, "+ Add")));
304
269
  redraw(); return box;
305
270
  }
306
- function select(value, options) { const s = el("select"); for (const o of options) { const val = typeof o === "string" ? o : o.value; const label = typeof o === "string" ? o : o.label; const opt = el("option", {}, label); opt.value = val; if (val === value) opt.selected = true; s.append(opt); } return s; }
307
- const impact = (kind) => el("span", { class: "impact" }, kind === "now" ? "⚡ immediate" : kind === "fleet" ? "🔄🔄 restart fleet" : "🔄 restart instance");
308
- const hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj || {}, key);
309
- function overrideControl(inst, key, input, inheritedValue, impactKind = "instance") {
310
- const inherited = !hasOwn(inst, key);
311
- const toggle = el("input", { type: "checkbox" }); toggle.checked = inherited;
312
- input.disabled = inherited;
313
- if (inherited && inheritedValue !== undefined) {
314
- if (input.type === "checkbox") input.checked = !!inheritedValue;
315
- else input.value = inheritedValue;
316
- }
317
- toggle.onchange = () => { input.disabled = toggle.checked; };
318
- return { input, toggle, row: el("div", {}, input, el("label", { class: "inherit-row" }, toggle, el("span", {}, t("inheritDefault")), impact(impactKind))) };
319
- }
320
- function setValidation(input, feedback, error, warning) {
321
- input.classList.toggle("invalid", !!error);
322
- feedback.className = "feedback" + (error ? " error" : warning ? " warning" : "");
323
- feedback.textContent = error ? `✗ ${error}` : warning ? `⚠ ${warning}` : "";
324
- return !error;
325
- }
271
+ function select(value, options) { const s = el("select"); for (const o of options) { const opt = el("option", {}, o); opt.value = o; if (o === value) opt.selected = true; s.append(opt); } return s; }
326
272
 
327
273
  // ── My Agents ──
328
274
  let editing = null;
@@ -335,13 +281,12 @@
335
281
  }
336
282
  // Display name: profile display_name → else strip the -t<topicId> suffix.
337
283
  const shortName = (name, inst) => (inst && inst.display_name) || name.replace(/-t\d+$/, "");
338
- const STATUS_TEXT = { running: "🟢 Running", paused: "⏸ Paused", crashed: "🔴 Crashed", stopped: "⚪ Stopped" };
339
- const STATUS_ORDER = { running: 0, paused: 1, crashed: 2, stopped: 3 };
284
+ const STATUS_TEXT = { running: "🟢 Running", crashed: "🔴 Crashed", stopped: "⚪ Stopped" };
285
+ const STATUS_ORDER = { running: 0, crashed: 1, stopped: 2 };
340
286
  const groupOf = (inst) => (inst.tags && inst.tags[0]) || "Other";
341
287
  function compactRow(name, inst) {
342
288
  const st = state.live[name]?.status || "stopped";
343
289
  const running = st === "running";
344
- const paused = st === "paused";
345
290
  const short = shortName(name, inst);
346
291
  const chTag = agentChannelLabel(inst);
347
292
  const item = el("div", { class: "item" });
@@ -354,9 +299,7 @@
354
299
  chTag ? el("span", { class: "tag" + (chTag.includes("persona") ? " persona" : chTag.includes("telegram") ? " tg" : "") }, chTag) : null,
355
300
  el("span", { class: "spacer" }),
356
301
  el("button", { class: "sm ghost", onclick: () => { editing = editing === name ? null : name; renderAgents(); } }, editing === name ? t("close") : t("edit")),
357
- running ? el("button", { class: "sm", onclick: () => pauseWakeAgent(name, "pause") }, t("pause")) : null,
358
- paused ? el("button", { class: "sm", onclick: () => pauseWakeAgent(name, "wake") }, t("wake")) : null,
359
- !paused ? el("button", { class: "sm", onclick: () => toggleAgent(name, running) }, running ? t("stop") : t("start")) : null,
302
+ el("button", { class: "sm", onclick: () => toggleAgent(name, running) }, running ? t("stop") : t("start")),
360
303
  el("button", { class: "sm danger del-btn", title: t("close"), onclick: () => delAgent(name) }, "🗑️"),
361
304
  ));
362
305
  if (editing === name) item.append(agentEditForm(name, inst));
@@ -390,7 +333,6 @@
390
333
  }
391
334
  function agentEditForm(name, inst) {
392
335
  const box = el("div", { class: "edit" });
393
- const defaults = state.fleet.defaults || {};
394
336
  const fBackend = select(inst.backend || state.fleet.defaults?.backend || "claude-code", BACKENDS);
395
337
  const fModel = el("input", { type: "text", value: inst.model || "", placeholder: "(inherit default)" });
396
338
  const fWd = el("input", { type: "text", value: inst.working_directory || "" });
@@ -399,77 +341,29 @@
399
341
  const fSys = el("input", { type: "text", value: inst.systemPrompt || "", placeholder: "e.g. file:prompts/dev.md" });
400
342
  const tags = [...(inst.tags || [])];
401
343
  const fGeneral = el("input", { type: "checkbox" }); fGeneral.checked = !!inst.general_topic;
402
- const auto = overrideControl(inst, "auto_pause_after", el("input", { type: "number", min: "0", step: "1", value: inst.auto_pause_after ?? defaults.auto_pause_after ?? 0, placeholder: "0 = disabled" }), defaults.auto_pause_after ?? 0);
403
- const hangRaw = inst.hang_detector || {};
404
- const hangHolder = hasOwn(hangRaw, "timeout_minutes") ? { value: hangRaw.timeout_minutes } : {};
405
- const hang = overrideControl(hangHolder, "value", el("input", { type: "number", min: "0.1", step: "0.5", value: hangRaw.timeout_minutes ?? defaults.hang_detector?.timeout_minutes ?? 15 }), defaults.hang_detector?.timeout_minutes ?? 15);
406
- const agentMode = overrideControl(inst, "agent_mode", select(inst.agent_mode || defaults.agent_mode || "mcp", ["mcp", "cli"]), defaults.agent_mode || "mcp");
407
- const toolSet = overrideControl(inst, "tool_set", select(inst.tool_set || defaults.tool_set || "full", ["full", "standard", "minimal"]), defaults.tool_set || "full");
408
- const logLevel = overrideControl(inst, "log_level", select(inst.log_level || defaults.log_level || "info", ["trace", "debug", "info", "warn", "error"]), defaults.log_level || "info");
409
- const fLightweight = el("input", { type: "checkbox" }); fLightweight.checked = inst.lightweight ?? defaults.lightweight ?? false;
410
- const lightweight = overrideControl(inst, "lightweight", fLightweight, defaults.lightweight ?? false);
411
- const fallbackValue = Array.isArray(inst.model_failover) ? (inst.model_failover[0] || "") : (defaults.model_failover?.[0] || "");
412
- const failover = overrideControl(inst, "model_failover", el("input", { type: "text", value: fallbackValue, placeholder: "e.g. sonnet" }), defaults.model_failover?.[0] || "");
413
- const displayName = overrideControl(inst, "display_name", el("input", { type: "text", value: inst.display_name ?? defaults.display_name ?? "", placeholder: name }), defaults.display_name || "");
414
- const autoFeedback = el("div", { class: "feedback" });
415
- const hangFeedback = el("div", { class: "feedback" });
416
- const failoverFeedback = el("div", { class: "feedback" });
417
344
  const msg = el("span", { class: "msg" });
418
- const saveBtn = el("button", { class: "primary sm" }, t("save"));
419
- const validate = () => {
420
- const autoValue = Number(auto.input.value), hangValue = Number(hang.input.value);
421
- const validAuto = setValidation(auto.input, autoFeedback, !auto.toggle.checked && (!Number.isFinite(autoValue) || autoValue < 0) ? "Must be 0 or greater" : "");
422
- const validHang = setValidation(hang.input, hangFeedback, !hang.toggle.checked && (!Number.isFinite(hangValue) || hangValue <= 0) ? "Must be greater than 0" : "");
423
- setValidation(failover.input, failoverFeedback, "", !failover.toggle.checked && !failover.input.value.trim() ? "Empty list disables failover" : "");
424
- saveBtn.disabled = !(validAuto && validHang);
425
- return !saveBtn.disabled;
426
- };
427
- for (const ctl of [auto, hang, agentMode, toolSet, logLevel, lightweight, failover, displayName]) {
428
- ctl.input.addEventListener("input", validate); ctl.input.addEventListener("change", validate); ctl.toggle.addEventListener("change", validate);
429
- }
430
- saveBtn.onclick = async () => {
431
- if (!validate()) return;
432
- const patch = {
433
- backend: fBackend.value, model: fModel.value || undefined,
434
- working_directory: fWd.value, channel_id: fChan.value || undefined,
435
- description: fDesc.value || undefined, systemPrompt: fSys.value || undefined,
436
- tags: tags.length ? tags : undefined, general_topic: fGeneral.checked,
437
- auto_pause_after: auto.toggle.checked ? null : Number(auto.input.value),
438
- hang_detector: { timeout_minutes: hang.toggle.checked ? null : Number(hang.input.value) },
439
- agent_mode: agentMode.toggle.checked ? null : agentMode.input.value,
440
- tool_set: toolSet.toggle.checked ? null : toolSet.input.value,
441
- log_level: logLevel.toggle.checked ? null : logLevel.input.value,
442
- lightweight: lightweight.toggle.checked ? null : lightweight.input.checked,
443
- model_failover: failover.toggle.checked ? null : (failover.input.value.trim() ? [failover.input.value.trim()] : []),
444
- display_name: displayName.toggle.checked ? null : displayName.input.value.trim(),
445
- };
446
- const res = await api(`/api/settings/fleet/instances/${encodeURIComponent(name)}`, { method: "PATCH", body: JSON.stringify(patch) });
447
- resultMsg(msg, res); if (res.ok) { await reload(); showBanner(`✓ ${t("affected")}: ${name} — 🔄 restart instance`); }
448
- };
449
345
  box.append(
450
346
  el("div", { class: "sub", style: "margin:0 0 8px" }, "ID: " + name),
451
- el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("backend"), impact("instance")), fBackend), el("div", {}, el("label", {}, t("model"), impact("instance")), fModel)),
452
- el("label", {}, t("workingDir"), impact("instance")), fWd,
453
- el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("channelBinding"), impact("fleet")), fChan), el("div", {}, el("label", {}, t("description"), impact("now")), fDesc)),
454
- el("label", {}, t("systemPrompt"), impact("instance")), fSys,
455
- el("label", {}, t("tags"), impact("now")), chipList(tags, null, "tag"),
456
- el("label", { style: "display:flex;align-items:center;gap:8px;cursor:pointer" }, fGeneral, el("span", {}, t("generalTopicField")), impact("fleet")),
457
- el("div", { class: "sub", style: "margin:14px 0 6px;font-weight:600" }, t("runtimeResources")),
458
- el("div", { class: "grid2" },
459
- el("div", {}, el("label", {}, `${t("autoPause")} (${t("minutes")})`), auto.row, autoFeedback),
460
- el("div", {}, el("label", {}, `${t("hangTimeout")} (${t("minutes")})`), hang.row, hangFeedback),
461
- el("div", {}, el("label", {}, t("agentMode")), agentMode.row),
462
- el("div", {}, el("label", {}, t("toolSet")), toolSet.row),
463
- el("div", {}, el("label", {}, t("logLevel")), logLevel.row),
464
- el("div", {}, el("label", {}, t("lightweight")), lightweight.row),
465
- el("div", {}, el("label", {}, t("modelFailover")), failover.row, failoverFeedback),
466
- el("div", {}, el("label", {}, t("displayName")), displayName.row)),
347
+ el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("backend")), fBackend), el("div", {}, el("label", {}, t("model")), fModel)),
348
+ el("label", {}, t("workingDir")), fWd,
349
+ el("div", { class: "grid2" }, el("div", {}, el("label", {}, t("channelBinding")), fChan), el("div", {}, el("label", {}, t("description")), fDesc)),
350
+ el("label", {}, t("systemPrompt")), fSys,
351
+ el("label", {}, t("tags")), chipList(tags, null, "tag"),
352
+ el("label", { style: "display:flex;align-items:center;gap:8px;cursor:pointer" }, fGeneral, el("span", {}, t("generalTopicField"))),
467
353
  el("div", { class: "actions" },
468
- saveBtn,
354
+ el("button", { class: "primary sm", onclick: async () => {
355
+ const patch = {
356
+ backend: fBackend.value, model: fModel.value || undefined,
357
+ working_directory: fWd.value, channel_id: fChan.value || undefined,
358
+ description: fDesc.value || undefined, systemPrompt: fSys.value || undefined,
359
+ tags: tags.length ? tags : undefined, general_topic: fGeneral.checked,
360
+ };
361
+ const res = await api(`/api/settings/fleet/instances/${encodeURIComponent(name)}`, { method: "PATCH", body: JSON.stringify(patch) });
362
+ resultMsg(msg, res); if (res.ok) await reload();
363
+ } }, t("save")),
469
364
  el("button", { class: "sm ghost", onclick: () => { editing = null; renderAgents(); } }, t("cancel")),
470
365
  msg),
471
366
  );
472
- validate();
473
367
  return box;
474
368
  }
475
369
  async function toggleAgent(name, running) {
@@ -477,11 +371,6 @@
477
371
  else await api(`/api/instance/${encodeURIComponent(name)}/start`, { method: "POST" });
478
372
  setTimeout(reloadLive, 800);
479
373
  }
480
- async function pauseWakeAgent(name, action) {
481
- const res = await api(`/api/settings/instances/${encodeURIComponent(name)}/${action}`, { method: "POST" });
482
- if (!res.ok) alert(res.body?.error || `${action} failed`);
483
- await reloadLive();
484
- }
485
374
  async function delAgent(name) {
486
375
  if (!confirm(`Delete agent "${name}"? (config removed; running daemon stops on next Reload)`)) return;
487
376
  const res = await api(`/api/settings/fleet/instances/${encodeURIComponent(name)}`, { method: "DELETE" });
@@ -510,7 +399,6 @@
510
399
  }
511
400
 
512
401
  // ── Bots ──
513
- let editingBot = null;
514
402
  function renderBots() {
515
403
  const host = $("bots"); host.innerHTML = ""; const chs = channels();
516
404
  if (!chs.length) { host.className = "card empty"; host.textContent = t("noBots"); return; }
@@ -524,41 +412,12 @@
524
412
  el("span", { class: "sub", style: "margin:0" }, ch.bot_token_env || "(no token env)"),
525
413
  el("span", { class: "spacer" }),
526
414
  el("span", { style: "color:var(--green);font-size:13px" }, state.fleetUp ? t("online") : "—"),
527
- el("button", { class: "sm ghost", onclick: () => { editingBot = editingBot === i ? null : i; renderBots(); } }, editingBot === i ? t("close") : t("edit")),
528
415
  el("button", { class: "sm danger", onclick: () => delBot(i) }, "🗑️"),
529
416
  ));
530
- if (editingBot === i) item.append(botEditForm(ch, i));
531
417
  host.append(item);
532
418
  });
533
419
  }
534
420
 
535
- function botEditForm(ch, i) {
536
- ch.access = ch.access || { mode: "locked", allowed_users: [] };
537
- ch.access.allowed_users = ch.access.allowed_users || [];
538
- const box = el("div", { class: "edit" });
539
- const fMode = select(ch.access.mode || "locked", ["open", "locked", "pairing"]);
540
- const users = [...ch.access.allowed_users];
541
- const msg = el("span", { class: "msg" });
542
- box.append(
543
- el("div", { class: "grid2" },
544
- el("div", {}, el("label", {}, t("accessMode"), impact("fleet")), fMode),
545
- el("div", {}, el("label", {}, t("allowedUsers"), impact("fleet")), chipList(users, null, "user id"))),
546
- el("div", { class: "actions" },
547
- el("button", { class: "primary sm", onclick: async () => {
548
- const chs = channels();
549
- chs[i] = { ...chs[i], access: { ...chs[i].access, mode: fMode.value, allowed_users: users } };
550
- const res = await saveChannels(msg);
551
- if (res.ok) {
552
- const affected = Object.keys(state.fleet.instances || {});
553
- editingBot = null; await reload();
554
- showBanner(`✓ ${t("affected")}: ${affected.length ? affected.join(", ") : chLabel(i) + " bot"} — 🔄🔄 restart fleet`);
555
- }
556
- } }, t("save")),
557
- el("button", { class: "sm ghost", onclick: () => { editingBot = null; renderBots(); } }, t("cancel")), msg),
558
- );
559
- return box;
560
- }
561
-
562
421
  // ── ClassicBot Channels (read-only; created dynamically via /start /stop) ──
563
422
  function renderClassic() {
564
423
  const host = $("classicList"); if (!host) return; host.innerHTML = "";
@@ -627,8 +486,6 @@
627
486
  state.fleet.defaults = state.fleet.defaults || {};
628
487
  state.classic.defaults = state.classic.defaults || {};
629
488
  const d = state.fleet.defaults, c = state.classic.defaults;
630
- const hang = d.hang_detector || {};
631
- const startup = d.startup || {};
632
489
  const primary = channels()[0];
633
490
  if (primary) primary.access = primary.access || { mode: "locked", allowed_users: [] };
634
491
  const allowedUsers = primary ? (primary.access.allowed_users = primary.access.allowed_users || []) : [];
@@ -636,93 +493,26 @@
636
493
 
637
494
  const fBackend = select(d.backend || "claude-code", BACKENDS);
638
495
  const fModel = el("input", { type: "text", value: d.model || "", placeholder: "e.g. claude-opus-4-8" });
639
- const fAgentMode = select(d.agent_mode || "mcp", ["mcp", "cli"]);
640
- const fToolSet = select(d.tool_set || "full", ["full", "standard", "minimal"]);
641
- const fLogLevel = select(d.log_level || "info", ["trace", "debug", "info", "warn", "error"]);
642
496
  const fLocale = el("select");
643
- const fAutoPause = el("input", { type: "number", min: "0", step: "1", value: d.auto_pause_after ?? 0, placeholder: "0 = disabled" });
644
- const fHangEnabled = el("input", { type: "checkbox" }); fHangEnabled.checked = hang.enabled !== false;
645
- const fHangTimeout = el("input", { type: "number", min: "0.1", step: "0.5", value: hang.timeout_minutes ?? 15 });
646
- const fConcurrency = el("input", { type: "number", min: "1", max: "20", step: "1", value: startup.concurrency ?? 10 });
647
- const fStagger = el("input", { type: "number", min: "0", max: "30000", step: "100", value: startup.stagger_delay_ms ?? 500 });
648
497
  [["en", "English"], ["zh-TW", "繁體中文 (zh-TW)"]].forEach(([v, lbl]) => { const o = el("option", {}, lbl); o.value = v; if ((d.locale || "") === v) o.selected = true; fLocale.append(o); });
649
498
  const msg = el("span", { class: "msg" });
650
- const autoFeedback = el("div", { class: "feedback" });
651
- const hangFeedback = el("div", { class: "feedback" });
652
- const concurrencyFeedback = el("div", { class: "feedback" });
653
- const staggerFeedback = el("div", { class: "feedback" });
654
- const saveBtn = el("button", { class: "primary" }, t("save"));
655
- const validate = () => {
656
- const auto = Number(fAutoPause.value), timeout = Number(fHangTimeout.value);
657
- const concurrency = Number(fConcurrency.value), stagger = Number(fStagger.value);
658
- const validAuto = setValidation(fAutoPause, autoFeedback, !Number.isFinite(auto) || auto < 0 ? "Must be 0 or greater" : "");
659
- const validHang = setValidation(fHangTimeout, hangFeedback, !Number.isFinite(timeout) || timeout <= 0 ? "Must be greater than 0" : "");
660
- const validConcurrency = setValidation(fConcurrency, concurrencyFeedback, !Number.isInteger(concurrency) || concurrency < 1 || concurrency > 20 ? "Must be an integer from 1 to 20" : "", concurrency > 4 ? "May exceed CPU capacity on smaller hosts" : "");
661
- const validStagger = setValidation(fStagger, staggerFeedback, !Number.isFinite(stagger) || stagger < 0 || stagger > 30000 ? "Must be between 0 and 30000" : "", stagger < 100 ? "Very low values can cause startup spikes" : "");
662
- saveBtn.disabled = !(validAuto && validHang && validConcurrency && validStagger);
663
- return !saveBtn.disabled;
664
- };
665
- for (const input of [fAutoPause, fHangTimeout, fConcurrency, fStagger]) {
666
- input.addEventListener("input", validate); input.addEventListener("change", validate);
667
- }
668
- saveBtn.onclick = async () => {
669
- if (!validate()) return;
670
- const defaultsPatch = {
671
- backend: fBackend.value,
672
- model: fModel.value || undefined,
673
- locale: fLocale.value,
674
- auto_pause_after: Number(fAutoPause.value),
675
- agent_mode: fAgentMode.value,
676
- tool_set: fToolSet.value,
677
- log_level: fLogLevel.value,
678
- hang_detector: { enabled: fHangEnabled.checked, timeout_minutes: Number(fHangTimeout.value) },
679
- startup: { concurrency: Number(fConcurrency.value), stagger_delay_ms: Number(fStagger.value) },
680
- };
681
- const r1 = await api("/api/settings/fleet/defaults", { method: "PUT", body: JSON.stringify(defaultsPatch) });
682
- let r2 = { ok: true, body: {} };
683
- if (primary) r2 = await saveChannels();
684
- const r3 = await api("/api/settings/classic/defaults", { method: "PUT", body: JSON.stringify({ backend: c.backend, admin_users: c.admin_users, allowed_guilds: c.allowed_guilds }) });
685
- const bad = [r1, r2, r3].find(r => !r.ok);
686
- resultMsg(msg, bad || { ok: true, body: { warnings: [...(r1.body?.warnings || []), ...(r3.body?.warnings || [])] } });
687
- if (!bad) {
688
- const affected = Object.keys(state.fleet.instances || {});
689
- await reload();
690
- showBanner(`✓ ${t("affected")}: ${affected.length ? affected.join(", ") : "none"} — 🔄 restart instances; startup changes 🔄🔄 restart fleet`);
691
- }
692
- };
693
-
694
- const section = (title, open, ...children) => {
695
- const details = el("details", { class: "settings-section" }); details.open = open;
696
- details.append(el("summary", {}, title), el("div", { class: "section-body" }, ...children));
697
- return details;
698
- };
699
499
  host.append(
700
- section(t("runtimeResources"), true,
701
- el("div", { class: "grid2" },
702
- el("div", { class: "field" }, el("label", {}, `${t("autoPause")} (${t("minutes")})`, impact("instance")), fAutoPause, autoFeedback),
703
- el("div", { class: "field" }, el("label", {}, t("toolSet"), impact("instance")), fToolSet),
704
- el("div", { class: "field" }, el("label", {}, t("logLevel"), impact("instance")), fLogLevel))),
705
- section(t("backendSection"), true,
706
- el("div", { class: "grid2" },
707
- el("div", { class: "field" }, el("label", {}, t("defaultBackend"), impact("instance")), fBackend),
708
- el("div", { class: "field" }, el("label", {}, t("defaultModel"), impact("instance")), fModel),
709
- el("div", { class: "field" }, el("label", {}, t("agentMode"), impact("instance")), fAgentMode))),
710
- section(t("detection"), false,
711
- el("div", { class: "grid2" },
712
- el("div", { class: "field" }, el("label", { style: "display:flex;align-items:center;gap:8px" }, fHangEnabled, el("span", {}, t("hangEnabled")), impact("instance"))),
713
- el("div", { class: "field" }, el("label", {}, `${t("hangTimeout")} (${t("minutes")})`, impact("instance")), fHangTimeout, hangFeedback))),
714
- section(t("startup"), false,
715
- el("div", { class: "grid2" },
716
- el("div", { class: "field" }, el("label", {}, t("concurrency"), impact("fleet")), fConcurrency, concurrencyFeedback),
717
- el("div", { class: "field" }, el("label", {}, `${t("staggerDelay")} (ms)`, impact("fleet")), fStagger, staggerFeedback))),
718
- section(t("advancedSection"), false,
719
- el("div", { class: "field" }, el("label", {}, t("language"), impact("now")), fLocale),
720
- el("div", { class: "field" }, el("label", {}, t("allowedUsers"), impact("fleet")), chipList(allowedUsers, null, "user id")),
721
- el("div", { class: "field" }, el("label", {}, t("classicAdmins"), impact("fleet")), chipList(c.admin_users, null, "user id")),
722
- el("div", { class: "field" }, el("label", {}, t("allowedGuilds"), impact("fleet")), chipList(c.allowed_guilds, null, "guild id"))),
723
- el("div", { class: "field" }, el("div", { class: "actions" }, saveBtn, msg)),
500
+ el("div", { class: "field" }, el("label", {}, t("language")), fLocale),
501
+ el("div", { class: "field" }, el("label", {}, t("defaultBackend")), fBackend),
502
+ el("div", { class: "field" }, el("label", {}, t("defaultModel")), fModel),
503
+ el("div", { class: "field" }, el("label", {}, t("allowedUsers")), chipList(allowedUsers, null, "user id")),
504
+ el("div", { class: "field" }, el("label", {}, t("classicAdmins")), chipList(c.admin_users, null, "user id")),
505
+ el("div", { class: "field" }, el("label", {}, t("allowedGuilds")), chipList(c.allowed_guilds, null, "guild id")),
506
+ el("div", { class: "field" }, el("div", { class: "actions" }, el("button", { class: "primary", onclick: async () => {
507
+ d.backend = fBackend.value; d.model = fModel.value || undefined; d.locale = fLocale.value;
508
+ const r1 = await api("/api/settings/fleet/defaults", { method: "PUT", body: JSON.stringify({ backend: d.backend, model: d.model, locale: d.locale }) });
509
+ let r2 = { ok: true, body: {} };
510
+ if (primary) r2 = await saveChannels();
511
+ const r3 = await api("/api/settings/classic/defaults", { method: "PUT", body: JSON.stringify({ backend: c.backend, admin_users: c.admin_users, allowed_guilds: c.allowed_guilds }) });
512
+ const bad = [r1, r2, r3].find(r => !r.ok);
513
+ resultMsg(msg, bad || { ok: true, body: { warnings: [...(r1.body?.warnings || []), ...(r3.body?.warnings || [])] } });
514
+ } }, t("save")), msg)),
724
515
  );
725
- validate();
726
516
  }
727
517
 
728
518
  // ── What's New ──
@@ -799,8 +589,7 @@
799
589
  renderAgents(); renderBots(); renderClassic();
800
590
  }
801
591
  async function reload() {
802
- // Forms edit the user-authored config, not defaults-expanded runtime values.
803
- const f = await api("/api/settings/fleet/raw");
592
+ const f = await api("/api/settings/fleet");
804
593
  if (!f.ok) { $("banner").style.display = "block"; $("banner").style.background = "#fef2f2"; $("banner").style.color = "var(--red)"; $("banner").textContent = "Failed to load config — check the token in the URL."; return; }
805
594
  state.fleet = f.body || {};
806
595
  const c = await api("/api/settings/classic"); state.classic = c.body || {};
package/dist/web-api.d.ts CHANGED
@@ -61,7 +61,6 @@ export interface WebApiContext {
61
61
  };
62
62
  getInstanceDir(name: string): string;
63
63
  getInstanceStatus(name: string): "running" | "paused" | "stopped" | "crashed";
64
- deliverToInstance(instanceName: string, payload: Record<string, unknown>): Promise<void>;
65
64
  getUiStatus(): unknown;
66
65
  emitSseEvent(event: string, data: unknown): void;
67
66
  startInstance(name: string, config: unknown, topicMode: boolean): Promise<void>;
package/dist/web-api.js CHANGED
@@ -41,7 +41,7 @@ const ConfigUpdateSchema = z.object({
41
41
  access: z.record(z.string(), z.unknown()).optional(),
42
42
  }).strict().optional(),
43
43
  defaults: z.object({
44
- backend: z.enum(["claude-code", "gemini-cli", "codex", "opencode", "kiro-cli", "antigravity", "grok"]).optional(),
44
+ backend: z.enum(["claude-code", "gemini-cli", "codex", "opencode", "kiro-cli", "antigravity"]).optional(),
45
45
  model: z.string().max(128).optional(),
46
46
  }).strict().optional(),
47
47
  project_roots: z.array(z.string().min(1).max(1024)).max(64).optional(),
@@ -165,8 +165,6 @@ export function handleWebRequest(req, res, url, ctx) {
165
165
  { name: "gemini-cli", binary: "gemini" },
166
166
  { name: "opencode", binary: "opencode" },
167
167
  { name: "kiro-cli", binary: "kiro-cli" },
168
- { name: "antigravity", binary: "agy" },
169
- { name: "grok", binary: "grok" },
170
168
  ];
171
169
  const backends = BACKENDS.map(b => {
172
170
  let installed = false;
@@ -594,7 +592,7 @@ function handleSendMessage(req, res, ctx) {
594
592
  }
595
593
  body += chunk.toString();
596
594
  });
597
- req.on("end", async () => {
595
+ req.on("end", () => {
598
596
  try {
599
597
  const raw = JSON.parse(body);
600
598
  const parsed = SendMessageSchema.safeParse(raw);
@@ -616,26 +614,19 @@ function handleSendMessage(req, res, ctx) {
616
614
  // a separate reply path for that case.
617
615
  const groupId = ctx.fleetConfig?.channel?.group_id;
618
616
  const topicId = ctx.fleetConfig?.instances[instance]?.topic_id;
619
- try {
620
- await ctx.deliverToInstance(instance, {
621
- type: "fleet_inbound",
622
- content: message,
623
- targetSession: instance,
624
- meta: {
625
- chat_id: groupId ? String(groupId) : "",
626
- message_id: `web-${Date.now()}`,
627
- user: "web-user", user_id: "web-user",
628
- ts,
629
- thread_id: topicId != null ? String(topicId) : "",
630
- source: "web",
631
- },
632
- });
633
- }
634
- catch (err) {
635
- ctx.logger.error({ err, instance }, "Web message delivery failed");
636
- json(res, 503, { error: "Instance delivery failed" });
637
- return;
638
- }
617
+ ipc.send({
618
+ type: "fleet_inbound",
619
+ content: message,
620
+ targetSession: instance,
621
+ meta: {
622
+ chat_id: groupId ? String(groupId) : "",
623
+ message_id: `web-${Date.now()}`,
624
+ user: "web-user", user_id: "web-user",
625
+ ts,
626
+ thread_id: topicId != null ? String(topicId) : "",
627
+ source: "web",
628
+ },
629
+ });
639
630
  ctx.lastInboundUser.set(instance, "web-user");
640
631
  ctx.eventLog?.logActivity("message", "web-user", message.slice(0, 200), instance);
641
632
  ctx.emitSseEvent("message", { instance, sender: "web-user", text: message, ts });