@songsid/agend 2.3.0-beta.24 → 2.3.0-beta.25

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.
@@ -28,7 +28,7 @@ import {
28
28
  ACCESS_MODES, AGENT_MODES, DEFAULT_SCHEMA, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS, VISIBILITY_MODES, batchImpact,
29
29
  changedFields, channelId, channelsOf, chLabel, connectionState, effectiveSummary, fleetModel, fromYaml, fullModelRequests, groupAgents,
30
30
  hasOwn, impactOf, localeOptions, defaultsImpact, requestImpact, nonNegative, positive, replyGuardSupported, shortName, STATUS_ORDER, toYaml,
31
- visibilityDefault, worstImpact,
31
+ toolSetDefault, visibilityDefault, worstImpact,
32
32
  } from "./settings-model.js";
33
33
  import {
34
34
  AgentDialog, BackendSelect, BotDialog, ChipList, ClassicDialog, Drawer, Feedback, Impact, Label, ModelSelect, NewBotDialog, Override,
@@ -676,7 +676,7 @@ function General({ ctx }) {
676
676
  const original = useMemo(() => ({ c: structuredClone(c) }), []);
677
677
  const [f, setF] = useState(() => ({
678
678
  backend: d.backend || "claude-code", model: d.model || "", locale: d.locale || "", auto: String(d.auto_pause_after ?? 0),
679
- agent_mode: d.agent_mode || "mcp", tool_set: d.tool_set || "full", tool_progress: d.tool_progress ?? "off",
679
+ agent_mode: d.agent_mode || "mcp", tool_set: TOOL_SETS.includes(d.tool_set) ? d.tool_set : "", tool_progress: d.tool_progress ?? "off",
680
680
  visibility: visibilityDefault(d), reply: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
681
681
  hangOn: hang.enabled !== false, hang: String(hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES),
682
682
  concurrency: String(startup.concurrency ?? 10), stagger: String(startup.stagger_delay_ms ?? 500),
@@ -699,13 +699,13 @@ function General({ ctx }) {
699
699
  if (!ready) return;
700
700
  const defaultsPatch = changedFields({
701
701
  backend: f.backend, model: f.model || undefined, locale: f.locale || null, auto_pause_after: num(f.auto),
702
- agent_mode: f.agent_mode, tool_set: f.tool_set, tool_progress: f.tool_progress, cross_instance_visibility: f.visibility,
702
+ agent_mode: f.agent_mode, tool_set: f.tool_set || null, tool_progress: f.tool_progress, cross_instance_visibility: f.visibility,
703
703
  reply_completion_guard: f.reply, log_level: f.log_level,
704
704
  hang_detector: { enabled: f.hangOn, timeout_minutes: num(f.hang) },
705
705
  startup: { concurrency: num(f.concurrency), stagger_delay_ms: num(f.stagger) },
706
706
  }, {
707
707
  backend: d.backend || "claude-code", model: d.model || undefined, locale: d.locale || null, auto_pause_after: d.auto_pause_after ?? 0,
708
- agent_mode: d.agent_mode || "mcp", tool_set: d.tool_set || "full", tool_progress: d.tool_progress ?? "off",
708
+ agent_mode: d.agent_mode || "mcp", tool_set: TOOL_SETS.includes(d.tool_set) ? d.tool_set : null, tool_progress: d.tool_progress ?? "off",
709
709
  cross_instance_visibility: visibilityDefault(d), reply_completion_guard: d.reply_completion_guard ?? true, log_level: d.log_level || "info",
710
710
  hang_detector: { enabled: hang.enabled !== false, timeout_minutes: hang.timeout_minutes ?? HANG_TIMEOUT_DEFAULT_MINUTES },
711
711
  startup: { concurrency: startup.concurrency ?? 10, stagger_delay_ms: startup.stagger_delay_ms ?? 500 },
@@ -771,7 +771,8 @@ function General({ ctx }) {
771
771
  </section>
772
772
  <${Drawer} title=${tn("advancedSection")}>
773
773
  <section class="card"><h3>${tn("runtimeResources")}</h3><div class="grid2">
774
- <div class="field">${L(tn("toolSet"), "defaults.tool_set", "g-ts")}<${Select} id="g-ts" value=${f.tool_set} onChange=${set("tool_set")} options=${TOOL_SETS} /></div>
774
+ <div class="field">${L(tn("toolSet"), "defaults.tool_set", "g-ts")}<${Select} id="g-ts" value=${f.tool_set} onChange=${set("tool_set")}
775
+ options=${[{ value: "", label: tn("toolSetUnset", toolSetDefault({}, {}), toolSetDefault({}, { general_topic: true })) }, ...TOOL_SETS]} /></div>
775
776
  <div class="field">${L(tn("logLevel"), "defaults.log_level", "g-ll")}<${Select} id="g-ll" value=${f.log_level} onChange=${set("log_level")} options=${LOG_LEVELS} /></div>
776
777
  <div class="field">${L(tn("agentMode"), "defaults.agent_mode", "g-am")}<${Select} id="g-am" value=${f.agent_mode} onChange=${set("agent_mode")} options=${AGENT_MODES} /></div>
777
778
  </div></section>
@@ -20,7 +20,7 @@ import {
20
20
  ACCESS_MODES, AGENT_MODES, CH_TYPES, HANG_TIMEOUT_DEFAULT_MINUTES, LOG_LEVELS, TOOL_PROGRESS, TOOL_SETS,
21
21
  VISIBILITY_MODES, accessConfirmations, agentForm, agentPatch, backendOptions, changedFields, channelId, channelsOf,
22
22
  chLabel, hasOwn, impactOf, newConnectionImpact, nonNegative, nonNegativeInteger, positive, replyGuardSupported, sameValue, visibilityDefault,
23
- classicWebEchoEffective, worstImpact,
23
+ classicWebEchoEffective, toolSetDefault, worstImpact,
24
24
  } from "./settings-model.js";
25
25
  /** #2342: a field by id, for recordEdit before a pick or a reset writes it. */
26
26
  const byId = (id) => (typeof document !== "undefined" ? document.getElementById(id) : null);
@@ -230,7 +230,7 @@ function EmojiPicker({ cat, keyName, platform, channel, onPick, onClose }) {
230
230
  export function AgentDialog({ name, inst, ctx, onClose }) {
231
231
  const { fleet, schema } = ctx;
232
232
  const defaults = fleet.defaults || {};
233
- const [f, setF] = useState(() => agentForm(inst, defaults));
233
+ const [f, setF] = useState(() => agentForm(inst, defaults, name));
234
234
  const [emojis, setEmojis] = useState({ value: inst.status_emojis || null, baseline: inst.status_emojis || null });
235
235
  // #1302: the baseline is what the picker shows before anyone touches it.
236
236
  const visibilityBaseline = useMemo(() => (hasOwn(inst, "cross_instance_visibility") ? (VISIBILITY_MODES.includes(inst.cross_instance_visibility) ? inst.cross_instance_visibility : VISIBILITY_MODES[0]) : null), []);
@@ -278,14 +278,14 @@ export function AgentDialog({ name, inst, ctx, onClose }) {
278
278
  <input id="ag-hang" type="number" min="0.1" step="0.5" class=${errors.hang ? "invalid" : ""} disabled=${f.inherit.hang}
279
279
  value=${shown("hang", f.hang, String((defaults.hang_detector && defaults.hang_detector.timeout_minutes) ?? HANG_TIMEOUT_DEFAULT_MINUTES))} onInput=${(e) => set("hang")(e.target.value)} /></${Override}>
280
280
  <${Feedback} error=${errors.hang} /></div>
281
- ${[["agent_mode", "agentMode", AGENT_MODES, defaults.agent_mode || "mcp"], ["tool_set", "toolSet", TOOL_SETS, defaults.tool_set || "full"],
281
+ ${[["agent_mode", "agentMode", AGENT_MODES, defaults.agent_mode || "mcp"], ["tool_set", "toolSet", TOOL_SETS, toolSetDefault(defaults, inst, name)],
282
282
  ["tool_progress", "toolProgress", TOOL_PROGRESS, defaults.tool_progress ?? "off"],
283
283
  ["cross_instance_visibility", "crossInstanceVisibility", VISIBILITY_MODES, visibilityDefault(defaults)],
284
284
  ["log_level", "logLevel", LOG_LEVELS, defaults.log_level || "info"]].map(([k, label, opts, dflt]) => html`<div key=${k} class="field">
285
285
  ${L(tn(label), `instance.${k}`, `ag-${k}`)}
286
286
  <${Override} inherit=${f.inherit[k]} onInherit=${setInherit(k)}>
287
287
  <${Select} id=${`ag-${k}`} value=${shown(k, f[k], dflt)} disabled=${f.inherit[k]} onChange=${set(k)}
288
- options=${opts.includes(f[k]) || k === "cross_instance_visibility" ? opts : [f[k], ...opts]} /></${Override}>
288
+ options=${opts.includes(shown(k, f[k], dflt)) || k === "cross_instance_visibility" ? opts : [shown(k, f[k], dflt), ...opts]} /></${Override}>
289
289
  ${k === "cross_instance_visibility" ? html`<p class="note">${tn("crossInstanceVisibilityHint")}</p>` : null}</div>`)}
290
290
  <div class="field">${L(tn("replyCompletionGuard"), "instance.reply_completion_guard")}
291
291
  <${Override} inherit=${f.inherit.reply_completion_guard} onInherit=${setInherit("reply_completion_guard")}>
@@ -9,7 +9,22 @@ export const CH_TYPES = ["discord", "telegram"];
9
9
  export const LOCALES = [["en", "English"], ["zh-TW", "繁體中文 (zh-TW)"]];
10
10
  export const VISIBILITY_MODES = ["full", "summary", "hidden"];
11
11
  export const LOG_LEVELS = ["trace", "debug", "info", "warn", "error"];
12
- export const TOOL_SETS = ["full", "standard", "minimal"];
12
+ /**
13
+ * The tool profiles a person may choose (src/tool-permissions.ts; the validator's list). `general` is not one: AgEnD
14
+ * gives it to a General that sets none.
15
+ */
16
+ export const TOOL_SETS = ["worker", "coordinator", "standard", "minimal", "full"];
17
+ /**
18
+ * The profile an agent runs under while it sets none (resolveToolSet, on the config as loaded: the defaults merged
19
+ * under the agent's own): the fleet default's, else `general` for a General — its own `general_topic`, else the
20
+ * default's (#2407 review: `defaults.general_topic: true` makes every agent without its own a General), or the instance
21
+ * named general — else `worker`; never `full`, which the page used to show.
22
+ */
23
+ export function toolSetDefault(defaults, inst, name = "") {
24
+ if (TOOL_SETS.includes(defaults && defaults.tool_set)) return defaults.tool_set;
25
+ const general = hasOwn(inst, "general_topic") ? inst.general_topic === true : !!defaults && defaults.general_topic === true;
26
+ return general || name === "general" ? "general" : "worker";
27
+ }
13
28
  export const TOOL_PROGRESS = ["off", "standard", "verbose"];
14
29
  export const AGENT_MODES = ["mcp", "cli"];
15
30
  export const ACCESS_MODES = ["locked", "open", "pairing"];
@@ -166,7 +181,7 @@ export function agentPatch(form, inst, defaults, visibilityBaseline, statusEmoji
166
181
  }
167
182
 
168
183
  /** The agent form's starting values (an override shows the inherited value while "inherit" is on). */
169
- export function agentForm(inst, defaults) {
184
+ export function agentForm(inst, defaults, name = "") {
170
185
  const hang = inst.hang_detector || {};
171
186
  const inherit = (key) => !hasOwn(inst, key);
172
187
  return {
@@ -177,7 +192,9 @@ export function agentForm(inst, defaults) {
177
192
  auto_pause_after: String(inst.auto_pause_after ?? defaults.auto_pause_after ?? 0),
178
193
  hang: String(hang.timeout_minutes ?? (defaults.hang_detector && defaults.hang_detector.timeout_minutes) ?? HANG_TIMEOUT_DEFAULT_MINUTES),
179
194
  agent_mode: inst.agent_mode || defaults.agent_mode || "mcp",
180
- tool_set: inst.tool_set || defaults.tool_set || "full",
195
+ // What the picker offers when the agent sets its own: a profile a person may choose (a General's inherited
196
+ // `general` is shown while inheriting, but not offered to write back).
197
+ tool_set: TOOL_SETS.includes(inst.tool_set) ? inst.tool_set : TOOL_SETS.includes(toolSetDefault(defaults, inst, name)) ? toolSetDefault(defaults, inst, name) : "worker",
181
198
  tool_progress: inst.tool_progress ?? defaults.tool_progress ?? "off",
182
199
  // #1302: a value the fleet does not know (a typo) is read as unset — the picker shows "full", as the old page did,
183
200
  // and the baseline is that same "full", so an unrelated edit never writes the typo back (the server refuses it).
@@ -86,6 +86,7 @@ register("settings", {
86
86
  hangTimeout: "Stuck timeout",
87
87
  agentMode: "Agent mode",
88
88
  toolSet: "Tool set",
89
+ toolSetUnset: "Not set ({0}; a General: {1})",
89
90
  toolProgress: "Tool progress",
90
91
  crossInstanceVisibility: "Bot-to-bot messages in topics",
91
92
  crossInstanceVisibilityHint: "full: the whole message · summary: one line · hidden: nothing. Messages are always delivered; the Mirror Topic still shows them.",
@@ -677,6 +678,7 @@ register("settings", {
677
678
  hangTimeout: "卡住逾時",
678
679
  agentMode: "Agent 模式",
679
680
  toolSet: "工具集",
681
+ toolSetUnset: "未設定({0};General 為 {1})",
680
682
  toolProgress: "工具進度",
681
683
  crossInstanceVisibility: "Agent 之間的訊息在主題中",
682
684
  crossInstanceVisibilityHint: "full:完整訊息 · summary:一行摘要 · hidden:不顯示。訊息一律照常送達,Mirror Topic 也照常顯示。",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@songsid/agend",
3
- "version": "2.3.0-beta.24",
3
+ "version": "2.3.0-beta.25",
4
4
  "description": "Multi-agent fleet daemon — run coding CLIs (Claude, Muse, Codex, OpenCode) from Discord or Telegram",
5
5
  "type": "module",
6
6
  "bin": {