diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-26 16:32:18 +0200 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-26 16:32:18 +0200 |
| commit | 185af91b4d525db6c192a050b53615772fb5fe11 (patch) | |
| tree | 6db92db4042dddcc5fee13d02943447ba9df5f2d /crates/dinggerd/assets/pi | |
| parent | 1eb0ac3705276c6f81f44487c7e99c24a302d4d5 (diff) | |
importance: mark an agent high, normal, or low
within a status, more important agents are listed first, and so jumped to
first; status stays first. a high agent shows a bold ! before its name, a
low agent's name is lightly dimmed with "low" on its second line, and it
makes no sound (it still notifies and waits in line).
in pi, /importance high|normal|low (completes the levels; none once one is
typed out, so Enter runs it). the extension saves it in the session as a
custom entry (dingger-importance), so /reload, restarts, /resume and
restore bring it back; /new starts at normal. it shows a level other than
normal in pi's footer, emits dingger:importance { level } for other
extensions, and sends it with every report. for other agents, `dingger
importance [level] [--pane]`; it refuses a pi that reports itself, whose
saved level would win.
the daemon stays the one writer of pane state: it writes
@dingger_importance (unset for normal), which goes with the agent and
survives a daemon restart. `dingger status` marks high and low.
tests: e2e step 21 (order, the option, no sound for low, the menu bar,
the command, the extension through a harness that now matches pi's
extension API), a DINGGER_SOUND seam so sounds are observable, and a
status-line check that read the counts once (flaky) now polls.
pi-live: /importance in real pi 1.4, its footer and session entry, and the
level after a restore; every pi there now uses the test's agent directory
(a restored pi read the user's). negative controls fail each part.
Diffstat (limited to 'crates/dinggerd/assets/pi')
| -rw-r--r-- | crates/dinggerd/assets/pi/pi-agent-state.ts | 72 |
1 files changed, 70 insertions, 2 deletions
diff --git a/crates/dinggerd/assets/pi/pi-agent-state.ts b/crates/dinggerd/assets/pi/pi-agent-state.ts index ff893e7..4c21afe 100644 --- a/crates/dinggerd/assets/pi/pi-agent-state.ts +++ b/crates/dinggerd/assets/pi/pi-agent-state.ts @@ -69,7 +69,34 @@ function updateSessionFile(ctx: any): void { } } -function report(state: AgentState, message?: string): Promise<void> { +// The agent's importance (/importance): it orders agents within a status +// in dingger's list and jump, and a low agent makes no sound. Saved in the +// session (a custom entry, never sent to the model), so it comes back with +// the conversation: on /resume, `pi --session`, and dingger's restore. +// Other extensions can follow it on the event bus: `dingger:importance`, +// `{ level }`, at session start and on every change. +type Importance = "low" | "normal" | "high"; +const importanceLevels: Importance[] = ["high", "normal", "low"]; +const importanceEntry = "dingger-importance"; +let importance: Importance = "normal"; + +function savedImportance(ctx: any): Importance { + try { + const entries = ctx?.sessionManager?.getEntries?.() ?? []; + for (let i = entries.length - 1; i >= 0; i--) { + const entry = entries[i]; + if (entry?.type === "custom" && entry.customType === importanceEntry) { + const level = entry.data?.level; + return importanceLevels.includes(level) ? level : "normal"; + } + } + } catch { + // An unreadable session: the default. + } + return "normal"; +} + +function report(state: AgentState | undefined, message?: string): Promise<void> { return sendRequest({ id: `${source}:${Date.now()}:${Math.random().toString(36).slice(2)}`, method: "pane.report_agent", @@ -77,8 +104,9 @@ function report(state: AgentState, message?: string): Promise<void> { pane_id: paneId!, source, agent: "pi", - state, + ...(state ? { state } : {}), message, + importance, seq: Date.now() * 1000, ...(sessionFile ? { agent_session_path: sessionFile } : {}), }, @@ -161,6 +189,43 @@ export default function (pi) { publishState(); }); + function showImportance(ctx: any) { + ctx?.ui?.setStatus?.("dingger-importance", importance === "normal" ? undefined : `importance: ${importance}`); + pi.events.emit("dingger:importance", { level: importance }); + } + + pi.registerCommand("importance", { + description: "Set this agent's importance in dingger: high, normal, or low", + // None once a level is typed out, so Enter runs the command at once. + getArgumentCompletions: (prefix: string) => + importanceLevels.includes(prefix.trim() as Importance) + ? null + : importanceLevels + .filter((level) => level.startsWith(prefix.trim())) + .map((level) => ({ value: level, label: level })), + handler: async (args: string, ctx: any) => { + const wanted = args.trim(); + if (!wanted) { + ctx.ui.notify(`importance: ${importance}`, "info"); + return; + } + if (!importanceLevels.includes(wanted as Importance)) { + ctx.ui.notify("importance is high, normal, or low", "error"); + return; + } + if (wanted !== importance) { + importance = wanted as Importance; + pi.appendEntry(importanceEntry, { level: importance }); + showImportance(ctx); + // Without a state: the state stays as last reported. + if (enabled()) { + void report(undefined); + } + } + ctx.ui.notify(`importance: ${importance}`, "info"); + }, + }); + pi.on("session_start", async (event, ctx) => { // TUI only: RPC/JSON/print modes are headless. if (ctx?.mode !== "tui") { @@ -169,6 +234,9 @@ export default function (pi) { // A reload can replace this extension mid-run without emitting another agent_start. agentActive = ctx?.isIdle?.() === false; updateSessionFile(ctx); + // Each session has its own: /new starts at normal, /resume brings its own back. + importance = savedImportance(ctx); + showImportance(ctx); publishState(true); }); |
