aboutsummaryrefslogtreecommitdiff
path: root/crates/dinggerd/assets/pi
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-10-26 16:32:18 +0200
committerIgor Soarez <igor@soarez.org>2026-10-26 16:32:18 +0200
commit185af91b4d525db6c192a050b53615772fb5fe11 (patch)
tree6db92db4042dddcc5fee13d02943447ba9df5f2d /crates/dinggerd/assets/pi
parent1eb0ac3705276c6f81f44487c7e99c24a302d4d5 (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.ts72
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);
});