killeros 2.1.29 → 2.1.31

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.
package/Killeros.ts CHANGED
@@ -1,53 +1,53 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { registerRequestActivity } from "./killeros/activity.ts";
3
- import { registerAutoCompaction } from "./killeros/auto-compaction.ts";
4
- import {
5
- createSlashCommandResolver,
6
- registerAliases,
7
- registerSlashAutocomplete,
8
- } from "./killeros/commands.ts";
9
- import { registerFooter } from "./killeros/footer.ts";
10
- import { registerGoalInterface } from "./killeros/goal-interface.ts";
11
- import { registerGoalRuntime } from "./killeros/goal-runtime.ts";
12
- import { registerGoalSettlement } from "./killeros/goal-settlement.ts";
13
- import { registerHandoff } from "./killeros/handoff.ts";
14
- import { registerLifecycleHooks } from "./killeros/hooks.ts";
15
- import {
16
- registerCompletionNotifications,
17
- type CompletionNotificationDependencies,
18
- } from "./killeros/notifications.ts";
19
- import { registerCodexFastMode } from "./killeros/codex-fast.ts";
20
- import { registerPersonalInstructions } from "./killeros/personal-instructions.ts";
21
- import { registerQuestionTool } from "./killeros/question.ts";
22
- import { createGoalRuntime } from "./killeros/runtime.ts";
23
- import { registerShellUi } from "./killeros/shell-ui.ts";
24
- import { registerWorkedFor } from "./killeros/worked-for.ts";
25
-
26
- export { contextPercentRemaining, formatCost, formatContextProgress } from "./killeros/footer.ts";
27
- export { executeHook } from "./killeros/hooks.ts";
28
- export interface KillerosOptions {
29
- completionNotifications?: CompletionNotificationDependencies;
30
- /** Output-token budget for /handoff summaries; invalid values fall back to killeros.json, then the default. */
31
- handoffMaxTokens?: number;
32
- }
33
-
34
- export default function Killeros(pi: ExtensionAPI, options: KillerosOptions = {}): void {
35
- const goalRuntime = createGoalRuntime();
36
- const commandResolver = createSlashCommandResolver(pi);
37
- registerShellUi(pi, commandResolver);
38
- registerGoalInterface(pi, goalRuntime);
39
- registerGoalRuntime(pi, goalRuntime);
40
- registerPersonalInstructions(pi);
41
- registerQuestionTool(pi);
42
- registerAliases(pi);
43
- registerHandoff(pi, goalRuntime, options.handoffMaxTokens);
44
- registerSlashAutocomplete(pi, commandResolver);
45
- registerFooter(pi, goalRuntime);
46
- registerCodexFastMode(pi);
47
- registerLifecycleHooks(pi);
48
- registerWorkedFor(pi);
49
- const goalCompaction = registerGoalSettlement(pi, goalRuntime);
50
- registerAutoCompaction(pi, { goal: goalCompaction });
51
- registerRequestActivity(pi);
52
- registerCompletionNotifications(pi, options.completionNotifications);
53
- }
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { registerRequestActivity } from "./killeros/activity.ts";
3
+ import { registerAutoCompaction } from "./killeros/auto-compaction.ts";
4
+ import {
5
+ createSlashCommandResolver,
6
+ registerAliases,
7
+ registerSlashAutocomplete,
8
+ } from "./killeros/commands.ts";
9
+ import { registerFooter } from "./killeros/footer.ts";
10
+ import { registerGoalInterface } from "./killeros/goal-interface.ts";
11
+ import { registerGoalRuntime } from "./killeros/goal-runtime.ts";
12
+ import { registerGoalSettlement } from "./killeros/goal-settlement.ts";
13
+ import { registerHandoff } from "./killeros/handoff.ts";
14
+ import { registerLifecycleHooks } from "./killeros/hooks.ts";
15
+ import {
16
+ registerCompletionNotifications,
17
+ type CompletionNotificationDependencies,
18
+ } from "./killeros/notifications.ts";
19
+ import { registerCodexFastMode } from "./killeros/codex-fast.ts";
20
+ import { registerPersonalInstructions } from "./killeros/personal-instructions.ts";
21
+ import { registerQuestionTool } from "./killeros/question.ts";
22
+ import { createGoalRuntime } from "./killeros/runtime.ts";
23
+ import { registerShellUi } from "./killeros/shell-ui.ts";
24
+ import { registerWorkedFor } from "./killeros/worked-for.ts";
25
+
26
+ export { contextPercentRemaining, formatCost, formatContextProgress } from "./killeros/footer.ts";
27
+ export { executeHook } from "./killeros/hooks.ts";
28
+ export interface KillerosOptions {
29
+ completionNotifications?: CompletionNotificationDependencies;
30
+ /** Output-token budget for /handoff summaries; invalid values fall back to killeros.json, then the default. */
31
+ handoffMaxTokens?: number;
32
+ }
33
+
34
+ export default function Killeros(pi: ExtensionAPI, options: KillerosOptions = {}): void {
35
+ const goalRuntime = createGoalRuntime();
36
+ const commandResolver = createSlashCommandResolver(pi);
37
+ registerShellUi(pi, commandResolver);
38
+ registerGoalInterface(pi, goalRuntime);
39
+ registerGoalRuntime(pi, goalRuntime);
40
+ registerPersonalInstructions(pi);
41
+ registerQuestionTool(pi);
42
+ registerAliases(pi);
43
+ registerHandoff(pi, goalRuntime, options.handoffMaxTokens);
44
+ registerSlashAutocomplete(pi, commandResolver);
45
+ registerFooter(pi, goalRuntime);
46
+ registerCodexFastMode(pi);
47
+ registerLifecycleHooks(pi);
48
+ registerWorkedFor(pi);
49
+ const goalCompaction = registerGoalSettlement(pi, goalRuntime);
50
+ registerAutoCompaction(pi, { goal: goalCompaction });
51
+ registerRequestActivity(pi);
52
+ registerCompletionNotifications(pi, options.completionNotifications);
53
+ }
package/README.md CHANGED
@@ -1,112 +1,112 @@
1
- # KillerOS
2
-
3
- A TypeScript extension for the [Pi coding agent](https://github.com/earendil-works/pi) that replaces the stock TUI and adds long-running goals, reasoning controls, and workflow commands.
4
-
5
- ## What you get
6
-
7
- - A custom TUI: startup masthead with versions, model, working directory, and Git branch; a dark theme with coral accents; a multiline editor with slash-command completion; a footer that tracks model, context, and goal state; settled task receipts with duration and token usage.
8
- - `/goal`: set an objective and Pi keeps working toward it across turns, compaction, reloads, and branch navigation. Each turn must record `continue` with evidence and one next action, `complete`, or the existing blocker decision; otherwise the goal pauses. New goals pause after 20 turns; `/goal resume` grants another 20.
9
- - `/codex-fast`: toggles the `priority` service tier on Codex requests or reports its status.
10
- - `/handoff`: starts a fresh linked session carrying visible continuation context.
11
- - `/auto-compact`: reports, enables, disables, or tunes automatic context compaction.
12
- - A `question` tool with single-select and multi-select modes.
13
- - Lifecycle hooks (`tool_call`, `tool_result`, `agent_settled`) from `.pi/killeros-hooks.json`, plus `AGENTS.local.md` loading for trusted projects.
14
- - Optional completion sounds for settled requests.
15
-
16
-
17
- ## Requirements
18
-
19
- - Node.js 22.19.0+
20
- - Pi 0.85.0 or later within the 0.x release line
21
- - An interactive TUI session for the custom header, editor, footer, and `question`
22
-
23
- ## Install
24
-
25
- ```bash
26
- pi install npm:killeros
27
- ```
28
-
29
- Or from GitHub:
30
-
31
- ```bash
32
- pi install git:github.com/KyrosHendrix/pi-KillerOS
33
- ```
34
-
35
- Pin a release by appending its tag, for example `@v2.1.29`. Add `-l` to install only for the current project. Restart Pi after installing.
36
-
37
- ## Commands
38
-
39
- ```text
40
- /goal View the current goal
41
- /goal <objective> Set an objective
42
- /goal pause Stop automatic continuation
43
- /goal resume Resume automatic continuation
44
- /goal clear Remove the current goal
45
- /codex-fast [status] Toggle or report Codex fast mode
46
- /auto-compact [status|on|off|<percent>]
47
- Inspect or change automatic compaction
48
- /notification Configure the completion sound
49
- /handoff [focus] Fresh session with continuation context
50
- /clear New session after confirmation
51
- /exit Quit Pi gracefully
52
- ```
53
-
54
- ## Behavior by mode
55
-
56
- | Mode | What works |
57
- | --- | --- |
58
- | TUI | Everything |
59
- | RPC | Goals, proactive compaction; no TUI components, sounds, title indicator |
60
- | Print/JSON | No interactive questions, `/goal`, or proactive compaction |
61
-
62
- ## Configuration
63
-
64
- The packaged `killeros` theme activates on TUI start. Compaction triggers by default at 15% tokens remaining, stored in global `killeros.json`:
65
-
66
- ```json
67
- {
68
- "autoCompaction": {
69
- "enabled": true,
70
- "percentRemaining": 15
71
- },
72
- "handoffMaxTokens": 8192
73
- }
74
- ```
75
-
76
- Use `/auto-compact status` to inspect the effective KillerOS preference, `/auto-compact on` or `/auto-compact off` to toggle it, and `/auto-compact <percent>` to set an integer threshold from 0 through 100. A threshold of 0 does not disable Pi's own token reserve.
77
-
78
- `handoffMaxTokens` caps the `/handoff` summary output at 8192 tokens by default; raise it when long sessions truncate the summary.
79
-
80
- State proof in the objective so the agent can verify it with its normal tools:
81
-
82
- ```text
83
- /goal Reduce p95 checkout latency below 120 ms, verified by the checkout benchmark, while keeping the correctness suite green
84
- ```
85
-
86
- A direct quoted file target binds silent file proof:
87
-
88
- ```text
89
- /goal Fix `killeros/footer.ts`, verified by npm test
90
- ```
91
-
92
- KillerOS captures the file baseline at goal start and only completes when the file is created or changed. A normal response never continues a goal by itself: the agent must record `continue`, `complete`, or a blocker decision through `killeros_goal_update`. Repeated continuation reports and unavailable goal tools pause the goal. New goals pause after 20 turns without warning. An explicit `/goal resume` on an exhausted goal grants another 20 turns; compaction recovery never grants turns. Restored goals keep their persisted limit.
93
-
94
- Completion sounds are off by default; change with `/notification` in TUI mode. The tab-title indicator requires a Nerd Font.
95
-
96
- ## Development
97
-
98
- Strict TypeScript throughout. Tests run on Node's built-in test runner:
99
-
100
- ```bash
101
- npm ci && npm run check && npm test
102
- ```
103
-
104
- Releases go through CI on `main`; do not push version tags manually.
105
-
106
- ## Security
107
-
108
- Pi extensions run with your user permissions. Review the source before installing globally. Hook commands run only for projects Pi marks as trusted; check `.pi/killeros-hooks.json` before enabling project trust. KillerOS accepts that configuration only as a regular, non-linked file no larger than 64 KiB in the project's real `.pi` directory.
109
-
110
- ## License
111
-
112
- [MIT](LICENSE) © 2026 KyrosHendrix
1
+ # KillerOS
2
+
3
+ A TypeScript extension for the [Pi coding agent](https://github.com/earendil-works/pi) that replaces the stock TUI and adds long-running goals, reasoning controls, and workflow commands.
4
+
5
+ ## What you get
6
+
7
+ - A custom TUI: startup masthead with versions, model, working directory, and Git branch; a dark theme with coral accents; a multiline editor with slash-command completion; a footer that tracks model, context, and goal state; settled task receipts with duration and token usage.
8
+ - `/goal`: set an objective and Pi keeps working toward it across turns, compaction, reloads, and branch navigation. Each turn must record `continue` with evidence and one next action, `complete`, or the existing blocker decision; otherwise the goal pauses. New goals pause after 20 turns; `/goal resume` grants another 20.
9
+ - `/codex-fast`: toggles the `priority` service tier on Codex requests or reports its status.
10
+ - `/handoff`: starts a fresh linked session carrying visible continuation context.
11
+ - `/auto-compact`: reports, enables, disables, or tunes automatic context compaction.
12
+ - A `question` tool with single-select and multi-select modes.
13
+ - Lifecycle hooks (`tool_call`, `tool_result`, `agent_settled`) from `.pi/killeros-hooks.json`, plus `AGENTS.local.md` loading for trusted projects.
14
+ - Optional completion sounds for settled requests.
15
+
16
+
17
+ ## Requirements
18
+
19
+ - Node.js 22.19.0+
20
+ - Pi 0.85.0 or later within the 0.x release line
21
+ - An interactive TUI session for the custom header, editor, footer, and `question`
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pi install npm:killeros
27
+ ```
28
+
29
+ Or from GitHub:
30
+
31
+ ```bash
32
+ pi install git:github.com/KyrosHendrix/pi-KillerOS
33
+ ```
34
+
35
+ Pin a release by appending its tag, for example `@v2.1.30`. Add `-l` to install only for the current project. Restart Pi after installing.
36
+
37
+ ## Commands
38
+
39
+ ```text
40
+ /goal View the current goal
41
+ /goal <objective> Set an objective
42
+ /goal pause Stop automatic continuation
43
+ /goal resume Resume automatic continuation
44
+ /goal clear Remove the current goal
45
+ /codex-fast [status] Toggle or report Codex fast mode
46
+ /auto-compact [status|on|off|<percent>]
47
+ Inspect or change automatic compaction
48
+ /notification Configure the completion sound
49
+ /handoff [focus] Fresh session with continuation context
50
+ /clear New session after confirmation
51
+ /exit Quit Pi gracefully
52
+ ```
53
+
54
+ ## Behavior by mode
55
+
56
+ | Mode | What works |
57
+ | --- | --- |
58
+ | TUI | Everything |
59
+ | RPC | Goals, proactive compaction; no TUI components, sounds, title indicator |
60
+ | Print/JSON | No interactive questions, `/goal`, or proactive compaction |
61
+
62
+ ## Configuration
63
+
64
+ The packaged `killeros` theme activates on TUI start. Compaction triggers by default at 15% tokens remaining, stored in global `killeros.json`:
65
+
66
+ ```json
67
+ {
68
+ "autoCompaction": {
69
+ "enabled": true,
70
+ "percentRemaining": 15
71
+ },
72
+ "handoffMaxTokens": 8192
73
+ }
74
+ ```
75
+
76
+ Use `/auto-compact status` to inspect the effective KillerOS preference, `/auto-compact on` or `/auto-compact off` to toggle it, and `/auto-compact <percent>` to set an integer threshold from 0 through 100. A threshold of 0 does not disable Pi's own token reserve.
77
+
78
+ `handoffMaxTokens` caps the `/handoff` summary output at 8192 tokens by default; raise it when long sessions truncate the summary.
79
+
80
+ State proof in the objective so the agent can verify it with its normal tools:
81
+
82
+ ```text
83
+ /goal Reduce p95 checkout latency below 120 ms, verified by the checkout benchmark, while keeping the correctness suite green
84
+ ```
85
+
86
+ A direct quoted file target binds silent file proof:
87
+
88
+ ```text
89
+ /goal Fix `killeros/footer.ts`, verified by npm test
90
+ ```
91
+
92
+ KillerOS captures the file baseline at goal start and only completes when the file is created or changed. A normal response never continues a goal by itself: the agent must record `continue`, `complete`, or a blocker decision through `killeros_goal_update`. Repeated continuation reports and unavailable goal tools pause the goal. New goals pause after 20 turns without warning. An explicit `/goal resume` on an exhausted goal grants another 20 turns; compaction recovery never grants turns. Restored goals keep their persisted limit.
93
+
94
+ Completion sounds are off by default; change with `/notification` in TUI mode. The tab-title indicator requires a Nerd Font.
95
+
96
+ ## Development
97
+
98
+ Strict TypeScript throughout. Tests run on Node's built-in test runner:
99
+
100
+ ```bash
101
+ npm ci && npm run check && npm test
102
+ ```
103
+
104
+ Releases go through CI on `main`; do not push version tags manually.
105
+
106
+ ## Security
107
+
108
+ Pi extensions run with your user permissions. Review the source before installing globally. Hook commands run only for projects Pi marks as trusted; check `.pi/killeros-hooks.json` before enabling project trust. KillerOS accepts that configuration only as a regular, non-linked file no larger than 64 KiB in the project's real `.pi` directory.
109
+
110
+ ## License
111
+
112
+ [MIT](LICENSE) © 2026 KyrosHendrix
@@ -1,110 +1,110 @@
1
- import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
2
- import { truncateToWidth } from "@earendil-works/pi-tui";
3
- import { modelDisplayName } from "./display.ts";
4
-
5
- export type ActivityMessage =
6
- | { kind: "prompt" }
7
- | { kind: "tool"; toolName: string }
8
- | { kind: "tool-result"; failed: boolean }
9
- | { kind: "responding" };
10
-
11
- function safeToolName(toolName: string): string {
12
- const normalized = toolName.replace(/[\u0000-\u001F\u007F]+/gu, " ").replace(/\s+/gu, " ").trim();
13
- return truncateToWidth(normalized || "tool", 32, "…");
14
- }
15
-
16
- export function formatActivityMessage(message: ActivityMessage, theme: Theme, modelId?: string): string {
17
- let verb: string;
18
- let detail: string;
19
-
20
- switch (message.kind) {
21
- case "prompt":
22
- verb = "Mapping…";
23
- detail = "understanding request";
24
- break;
25
- case "tool-result":
26
- verb = message.failed ? "Recovering…" : "Reviewing…";
27
- detail = message.failed ? "tool failed" : "reading the result";
28
- break;
29
- case "responding":
30
- verb = "Responding…";
31
- detail = "assembling the answer";
32
- break;
33
- case "tool":
34
- switch (message.toolName.trim().toLowerCase()) {
35
- case "read":
36
- case "grep":
37
- case "find":
38
- case "ls":
39
- verb = "Inspecting…";
40
- detail = "reading relevant code";
41
- break;
42
- case "edit":
43
- case "write":
44
- verb = "Changing…";
45
- detail = "editing";
46
- break;
47
- case "bash":
48
- verb = "Running…";
49
- detail = "command";
50
- break;
51
- default:
52
- verb = "Working…";
53
- detail = `using ${safeToolName(message.toolName)}`;
54
- }
55
- break;
56
- default: {
57
- const exhaustive: never = message;
58
- return exhaustive;
59
- }
60
- }
61
-
62
- const suffix = modelDisplayName({ id: modelId }) || "unknown model";
63
- return `${theme.fg("accent", verb)} ${theme.fg("dim", `(${theme.bold("esc")} to interrupt · ${detail})`)}${theme.fg("dim", ` · ${suffix}`)}`;
64
- }
65
-
66
- export function registerRequestActivity(pi: ExtensionAPI): void {
67
- let active = false;
68
- let activeModel: string | undefined;
69
-
70
- const render = (message: ActivityMessage, ctx: ExtensionContext): void => {
71
- ctx.ui.setWorkingMessage(formatActivityMessage(message, ctx.ui.theme, activeModel));
72
- };
73
-
74
- const clear = (ctx?: ExtensionContext): void => {
75
- active = false;
76
- activeModel = undefined;
77
- if (ctx?.mode === "tui") {
78
- ctx.ui.setWorkingMessage();
79
- }
80
- };
81
-
82
- pi.on("agent_start", (_event, ctx) => {
83
- if (ctx.mode !== "tui") return;
84
- if (!active) activeModel = ctx.model?.id;
85
- active = true;
86
- render({ kind: "prompt" }, ctx);
87
- });
88
-
89
- pi.on("tool_execution_start", (event, ctx) => {
90
- if (ctx.mode !== "tui" || !active) return;
91
- render({ kind: "tool", toolName: event.toolName }, ctx);
92
- });
93
-
94
- pi.on("tool_execution_end", (event, ctx) => {
95
- if (ctx.mode !== "tui" || !active) return;
96
- render({ kind: "tool-result", failed: event.isError }, ctx);
97
- });
98
-
99
- pi.on("message_update", (event, ctx) => {
100
- if (ctx.mode !== "tui" || !active || event.assistantMessageEvent.type !== "text_start") return;
101
- render({ kind: "responding" }, ctx);
102
- });
103
-
104
- pi.on("agent_settled", (_event, ctx) => {
105
- if (ctx.mode !== "tui" || !active || !ctx.isIdle?.() || ctx.hasPendingMessages?.()) return;
106
- clear(ctx);
107
- });
108
-
109
- pi.on("session_shutdown", (_event, ctx) => clear(ctx));
110
- }
1
+ import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
2
+ import { truncateToWidth } from "@earendil-works/pi-tui";
3
+ import { modelDisplayName } from "./display.ts";
4
+
5
+ export type ActivityMessage =
6
+ | { kind: "prompt" }
7
+ | { kind: "tool"; toolName: string }
8
+ | { kind: "tool-result"; failed: boolean }
9
+ | { kind: "responding" };
10
+
11
+ function safeToolName(toolName: string): string {
12
+ const normalized = toolName.replace(/[\u0000-\u001F\u007F]+/gu, " ").replace(/\s+/gu, " ").trim();
13
+ return truncateToWidth(normalized || "tool", 32, "…");
14
+ }
15
+
16
+ export function formatActivityMessage(message: ActivityMessage, theme: Theme, modelId?: string): string {
17
+ let verb: string;
18
+ let detail: string;
19
+
20
+ switch (message.kind) {
21
+ case "prompt":
22
+ verb = "Mapping…";
23
+ detail = "understanding request";
24
+ break;
25
+ case "tool-result":
26
+ verb = message.failed ? "Recovering…" : "Reviewing…";
27
+ detail = message.failed ? "tool failed" : "reading the result";
28
+ break;
29
+ case "responding":
30
+ verb = "Responding…";
31
+ detail = "assembling the answer";
32
+ break;
33
+ case "tool":
34
+ switch (message.toolName.trim().toLowerCase()) {
35
+ case "read":
36
+ case "grep":
37
+ case "find":
38
+ case "ls":
39
+ verb = "Inspecting…";
40
+ detail = "reading relevant code";
41
+ break;
42
+ case "edit":
43
+ case "write":
44
+ verb = "Changing…";
45
+ detail = "editing";
46
+ break;
47
+ case "bash":
48
+ verb = "Running…";
49
+ detail = "command";
50
+ break;
51
+ default:
52
+ verb = "Working…";
53
+ detail = `using ${safeToolName(message.toolName)}`;
54
+ }
55
+ break;
56
+ default: {
57
+ const exhaustive: never = message;
58
+ return exhaustive;
59
+ }
60
+ }
61
+
62
+ const suffix = modelDisplayName({ id: modelId }) || "unknown model";
63
+ return `${theme.fg("accent", verb)} ${theme.fg("dim", `(${theme.bold("esc")} to interrupt · ${detail})`)}${theme.fg("dim", ` · ${suffix}`)}`;
64
+ }
65
+
66
+ export function registerRequestActivity(pi: ExtensionAPI): void {
67
+ let active = false;
68
+ let activeModel: string | undefined;
69
+
70
+ const render = (message: ActivityMessage, ctx: ExtensionContext): void => {
71
+ ctx.ui.setWorkingMessage(formatActivityMessage(message, ctx.ui.theme, activeModel));
72
+ };
73
+
74
+ const clear = (ctx?: ExtensionContext): void => {
75
+ active = false;
76
+ activeModel = undefined;
77
+ if (ctx?.mode === "tui") {
78
+ ctx.ui.setWorkingMessage();
79
+ }
80
+ };
81
+
82
+ pi.on("agent_start", (_event, ctx) => {
83
+ if (ctx.mode !== "tui") return;
84
+ if (!active) activeModel = ctx.model?.id;
85
+ active = true;
86
+ render({ kind: "prompt" }, ctx);
87
+ });
88
+
89
+ pi.on("tool_execution_start", (event, ctx) => {
90
+ if (ctx.mode !== "tui" || !active) return;
91
+ render({ kind: "tool", toolName: event.toolName }, ctx);
92
+ });
93
+
94
+ pi.on("tool_execution_end", (event, ctx) => {
95
+ if (ctx.mode !== "tui" || !active) return;
96
+ render({ kind: "tool-result", failed: event.isError }, ctx);
97
+ });
98
+
99
+ pi.on("message_update", (event, ctx) => {
100
+ if (ctx.mode !== "tui" || !active || event.assistantMessageEvent.type !== "text_start") return;
101
+ render({ kind: "responding" }, ctx);
102
+ });
103
+
104
+ pi.on("agent_settled", (_event, ctx) => {
105
+ if (ctx.mode !== "tui" || !active || !ctx.isIdle?.() || ctx.hasPendingMessages?.()) return;
106
+ clear(ctx);
107
+ });
108
+
109
+ pi.on("session_shutdown", (_event, ctx) => clear(ctx));
110
+ }