@oh-my-pi/pi-coding-agent 15.10.12 → 15.11.0

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 (125) hide show
  1. package/CHANGELOG.md +60 -3
  2. package/dist/cli.js +841 -803
  3. package/dist/types/async/index.d.ts +0 -1
  4. package/dist/types/cli/gallery-fixtures/types.d.ts +5 -0
  5. package/dist/types/config/keybindings.d.ts +6 -1
  6. package/dist/types/config/settings-schema.d.ts +56 -33
  7. package/dist/types/export/html/template.generated.d.ts +1 -1
  8. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  9. package/dist/types/extensibility/shared-events.d.ts +2 -2
  10. package/dist/types/internal-urls/history-protocol.d.ts +14 -0
  11. package/dist/types/internal-urls/index.d.ts +1 -0
  12. package/dist/types/internal-urls/types.d.ts +1 -1
  13. package/dist/types/irc/bus.d.ts +66 -0
  14. package/dist/types/modes/components/agent-hub.d.ts +30 -0
  15. package/dist/types/modes/components/compaction-summary-message.d.ts +10 -4
  16. package/dist/types/modes/components/custom-editor.d.ts +2 -0
  17. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  18. package/dist/types/modes/components/ttsr-notification.d.ts +5 -1
  19. package/dist/types/modes/components/welcome.d.ts +3 -9
  20. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  21. package/dist/types/modes/interactive-mode.d.ts +3 -2
  22. package/dist/types/modes/theme/theme.d.ts +2 -1
  23. package/dist/types/modes/types.d.ts +3 -2
  24. package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
  25. package/dist/types/registry/agent-lifecycle.d.ts +51 -0
  26. package/dist/types/registry/agent-registry.d.ts +16 -5
  27. package/dist/types/session/agent-session.d.ts +35 -30
  28. package/dist/types/session/messages.d.ts +2 -4
  29. package/dist/types/session/session-history-format.d.ts +12 -0
  30. package/dist/types/session/session-manager.d.ts +21 -3
  31. package/dist/types/session/streaming-output.d.ts +23 -0
  32. package/dist/types/task/executor.d.ts +11 -2
  33. package/dist/types/task/index.d.ts +11 -4
  34. package/dist/types/task/output-manager.d.ts +0 -7
  35. package/dist/types/task/repair-args.d.ts +8 -7
  36. package/dist/types/task/types.d.ts +55 -51
  37. package/dist/types/tools/browser/tab-worker.d.ts +3 -1
  38. package/dist/types/tools/find.d.ts +0 -11
  39. package/dist/types/tools/grouped-file-output.d.ts +0 -49
  40. package/dist/types/tools/index.d.ts +1 -3
  41. package/dist/types/tools/irc.d.ts +76 -38
  42. package/dist/types/tools/job.d.ts +7 -1
  43. package/examples/extensions/with-deps/package.json +1 -0
  44. package/package.json +11 -10
  45. package/scripts/bundle-dist.ts +28 -19
  46. package/src/async/index.ts +0 -1
  47. package/src/cli/gallery-cli.ts +1 -1
  48. package/src/cli/gallery-fixtures/agentic.ts +230 -115
  49. package/src/cli/gallery-fixtures/types.ts +5 -0
  50. package/src/cli.ts +20 -6
  51. package/src/commit/agentic/tools/analyze-file.ts +38 -19
  52. package/src/config/keybindings.ts +6 -1
  53. package/src/config/settings-schema.ts +56 -40
  54. package/src/config/settings.ts +7 -0
  55. package/src/eval/__tests__/agent-bridge.test.ts +5 -3
  56. package/src/eval/agent-bridge.ts +3 -16
  57. package/src/eval/js/shared/prelude.txt +1 -1
  58. package/src/eval/py/prelude.py +5 -6
  59. package/src/export/html/template.generated.ts +1 -1
  60. package/src/export/html/template.js +38 -13
  61. package/src/extensibility/custom-tools/types.ts +2 -2
  62. package/src/extensibility/shared-events.ts +2 -2
  63. package/src/internal-urls/docs-index.generated.ts +8 -8
  64. package/src/internal-urls/history-protocol.ts +113 -0
  65. package/src/internal-urls/index.ts +1 -0
  66. package/src/internal-urls/router.ts +3 -1
  67. package/src/internal-urls/types.ts +1 -1
  68. package/src/irc/bus.ts +292 -0
  69. package/src/main.ts +8 -60
  70. package/src/modes/components/{session-observer-overlay.ts → agent-hub.ts} +586 -367
  71. package/src/modes/components/compaction-summary-message.ts +68 -32
  72. package/src/modes/components/custom-editor.ts +10 -0
  73. package/src/modes/components/tool-execution.ts +31 -1
  74. package/src/modes/components/ttsr-notification.ts +72 -30
  75. package/src/modes/components/welcome.ts +9 -33
  76. package/src/modes/controllers/event-controller.ts +65 -0
  77. package/src/modes/controllers/extension-ui-controller.ts +8 -8
  78. package/src/modes/controllers/input-controller.ts +18 -2
  79. package/src/modes/controllers/selector-controller.ts +21 -17
  80. package/src/modes/interactive-mode.ts +8 -13
  81. package/src/modes/theme/theme.ts +18 -5
  82. package/src/modes/types.ts +3 -5
  83. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  84. package/src/modes/utils/ui-helpers.ts +51 -49
  85. package/src/prompts/system/irc-incoming.md +3 -4
  86. package/src/prompts/system/orchestrate-notice.md +2 -2
  87. package/src/prompts/system/subagent-system-prompt.md +0 -5
  88. package/src/prompts/system/system-prompt.md +1 -0
  89. package/src/prompts/system/workflow-notice.md +2 -2
  90. package/src/prompts/tools/eval.md +3 -3
  91. package/src/prompts/tools/irc.md +29 -19
  92. package/src/prompts/tools/read.md +2 -2
  93. package/src/prompts/tools/task-summary.md +5 -16
  94. package/src/prompts/tools/task.md +38 -29
  95. package/src/registry/agent-lifecycle.ts +218 -0
  96. package/src/registry/agent-registry.ts +16 -5
  97. package/src/sdk.ts +29 -9
  98. package/src/session/agent-session.ts +243 -237
  99. package/src/session/messages.ts +11 -78
  100. package/src/session/session-history-format.ts +246 -0
  101. package/src/session/session-manager.ts +59 -5
  102. package/src/session/streaming-output.ts +60 -0
  103. package/src/task/executor.ts +855 -466
  104. package/src/task/index.ts +718 -794
  105. package/src/task/output-manager.ts +0 -11
  106. package/src/task/render.ts +133 -63
  107. package/src/task/repair-args.ts +21 -9
  108. package/src/task/types.ts +73 -66
  109. package/src/tools/ask.ts +4 -2
  110. package/src/tools/bash.ts +15 -5
  111. package/src/tools/browser/tab-worker.ts +26 -7
  112. package/src/tools/browser.ts +28 -1
  113. package/src/tools/find.ts +2 -27
  114. package/src/tools/grouped-file-output.ts +1 -118
  115. package/src/tools/index.ts +4 -12
  116. package/src/tools/irc.ts +596 -171
  117. package/src/tools/job.ts +41 -7
  118. package/src/tools/read.ts +57 -1
  119. package/src/tools/renderers.ts +2 -0
  120. package/src/tools/resolve.ts +4 -1
  121. package/dist/types/async/support.d.ts +0 -2
  122. package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
  123. package/dist/types/task/simple-mode.d.ts +0 -8
  124. package/src/async/support.ts +0 -5
  125. package/src/task/simple-mode.ts +0 -27
@@ -88,10 +88,10 @@ export type CustomToolSessionEvent = {
88
88
  } | {
89
89
  reason: "auto_compaction_start";
90
90
  trigger: "threshold" | "overflow" | "idle" | "incomplete";
91
- action: "context-full" | "handoff" | "shake";
91
+ action: "context-full" | "handoff" | "shake" | "snapcompact";
92
92
  } | {
93
93
  reason: "auto_compaction_end";
94
- action: "context-full" | "handoff" | "shake";
94
+ action: "context-full" | "handoff" | "shake" | "snapcompact";
95
95
  result: CompactionResult | undefined;
96
96
  aborted: boolean;
97
97
  willRetry: boolean;
@@ -160,12 +160,12 @@ export interface TurnEndEvent {
160
160
  export interface AutoCompactionStartEvent {
161
161
  type: "auto_compaction_start";
162
162
  reason: "threshold" | "overflow" | "idle" | "incomplete";
163
- action: "context-full" | "handoff" | "shake";
163
+ action: "context-full" | "handoff" | "shake" | "snapcompact";
164
164
  }
165
165
  /** Fired when auto-compaction ends */
166
166
  export interface AutoCompactionEndEvent {
167
167
  type: "auto_compaction_end";
168
- action: "context-full" | "handoff" | "shake";
168
+ action: "context-full" | "handoff" | "shake" | "snapcompact";
169
169
  result: CompactionResult | undefined;
170
170
  aborted: boolean;
171
171
  willRetry: boolean;
@@ -0,0 +1,14 @@
1
+ import type { InternalResource, InternalUrl, ProtocolHandler, UrlCompletion } from "./types";
2
+ /**
3
+ * Handler for history:// URLs.
4
+ *
5
+ * Resolves agent ids against the global AgentRegistry, serving transcripts
6
+ * for both live and parked agents.
7
+ */
8
+ export declare class HistoryProtocolHandler implements ProtocolHandler {
9
+ #private;
10
+ readonly scheme = "history";
11
+ readonly immutable = false;
12
+ resolve(url: InternalUrl): Promise<InternalResource>;
13
+ complete(): Promise<UrlCompletion[]>;
14
+ }
@@ -9,6 +9,7 @@
9
9
  */
10
10
  export * from "./agent-protocol";
11
11
  export * from "./artifact-protocol";
12
+ export * from "./history-protocol";
12
13
  export * from "./issue-pr-protocol";
13
14
  export * from "./json-query";
14
15
  export * from "./local-protocol";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Types for the internal URL routing system.
3
3
  *
4
- * Internal URLs (`agent://`, `artifact://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`) are resolved by tools like read,
4
+ * Internal URLs (`agent://`, `artifact://`, `history://`, `issue://`, `local://`, `mcp://`, `memory://`, `omp://`, `pr://`, `rule://`, `skill://`, and `vault://`) are resolved by tools like read,
5
5
  * providing access to agent outputs and server resources without exposing filesystem paths.
6
6
  */
7
7
  import type { LocalProtocolOptions } from "./local-protocol";
@@ -0,0 +1,66 @@
1
+ /**
2
+ * IrcBus - Process-global mailbox bus for agent-to-agent messaging.
3
+ *
4
+ * Replaces the old auto-reply model: a `send` never blocks on the recipient
5
+ * generating anything. Delivery resolves the recipient via the global
6
+ * AgentRegistry — parked agents are revived through the
7
+ * AgentLifecycleManager, idle agents are woken with a real turn, and busy
8
+ * agents receive the message as a non-interrupting aside at the next step
9
+ * boundary (see AgentSession.deliverIrcMessage). Replies are real turns by
10
+ * the recipient, observed via `wait`.
11
+ */
12
+ import { AgentLifecycleManager } from "../registry/agent-lifecycle";
13
+ import { AgentRegistry } from "../registry/agent-registry";
14
+ export interface IrcMessage {
15
+ id: string;
16
+ /** Sender agent id. */
17
+ from: string;
18
+ /** Recipient agent id (resolved; "all" is expanded by the tool, not stored). */
19
+ to: string;
20
+ body: string;
21
+ ts: number;
22
+ /** Message id being answered. */
23
+ replyTo?: string;
24
+ }
25
+ export interface IrcDeliveryReceipt {
26
+ to: string;
27
+ outcome: "injected" | "woken" | "revived" | "failed";
28
+ error?: string;
29
+ }
30
+ export declare class IrcBus {
31
+ #private;
32
+ static global(): IrcBus;
33
+ /** Reset the global bus. Test-only. */
34
+ static resetGlobalForTests(): void;
35
+ constructor(registry?: AgentRegistry, lifecycle?: AgentLifecycleManager);
36
+ /**
37
+ * Fire-and-forget delivery. Never blocks on the recipient generating
38
+ * anything: the receipt reports how the message reached the recipient
39
+ * (waiter/aside = "injected", idle wake = "woken", park revival =
40
+ * "revived"), not what they did with it.
41
+ *
42
+ * Mailbox semantics: a successfully delivered message never lingers in
43
+ * the recipient's mailbox — injection/wake puts the full body into their
44
+ * context, so buffering it too would double-deliver via a later
45
+ * `wait`/`inbox` and inflate unread counts. Only a failed live hand-off
46
+ * is buffered for the recipient to drain later.
47
+ */
48
+ send(msg: Omit<IrcMessage, "id" | "ts">): Promise<IrcDeliveryReceipt>;
49
+ /**
50
+ * Block until a message for `agentId` (optionally from `filter.from`)
51
+ * arrives; consume + return it. Null on timeout (`timeoutMs <= 0` waits
52
+ * forever). Rejects when `signal` aborts. By default, already-buffered
53
+ * mail satisfies the wait before parking a future waiter; callers that
54
+ * need a strictly future reply can disable that drain.
55
+ */
56
+ wait(agentId: string, filter: {
57
+ from?: string;
58
+ }, timeoutMs: number, signal?: AbortSignal, options?: {
59
+ drainPending?: boolean;
60
+ }): Promise<IrcMessage | null>;
61
+ /** Drain (or peek) pending messages for `agentId`. */
62
+ inbox(agentId: string, opts?: {
63
+ peek?: boolean;
64
+ }): IrcMessage[];
65
+ unreadCount(agentId: string): number;
66
+ }
@@ -0,0 +1,30 @@
1
+ import { Container } from "@oh-my-pi/pi-tui";
2
+ import type { KeyId } from "../../config/keybindings";
3
+ import { IrcBus } from "../../irc/bus";
4
+ import { AgentLifecycleManager } from "../../registry/agent-lifecycle";
5
+ import { AgentRegistry } from "../../registry/agent-registry";
6
+ import type { SessionObserverRegistry } from "../session-observer-registry";
7
+ export interface AgentHubDeps {
8
+ /** Progress/status snapshot source (task lifecycle + progress channels). */
9
+ observers: SessionObserverRegistry;
10
+ /** Keys that toggle the hub closed from inside (app.agents.hub + app.session.observe). */
11
+ hubKeys: KeyId[];
12
+ onDone: () => void;
13
+ requestRender: () => void;
14
+ /** Injectable for tests; defaults to the process-global registry. */
15
+ registry?: AgentRegistry;
16
+ /** Injectable for tests; defaults to the process-global lifecycle manager. */
17
+ lifecycle?: AgentLifecycleManager;
18
+ /** Injectable for tests; defaults to the process-global bus. */
19
+ irc?: IrcBus;
20
+ }
21
+ export declare class AgentHubOverlayComponent extends Container {
22
+ #private;
23
+ constructor(deps: AgentHubDeps);
24
+ /** Tear down every subscription and timer. Called by the overlay owner on close. */
25
+ dispose(): void;
26
+ render(width: number): readonly string[];
27
+ handleInput(keyData: string): void;
28
+ /** Open the chat view for an agent id (public for table Enter and tests). */
29
+ openChat(id: string): void;
30
+ }
@@ -1,13 +1,19 @@
1
- import { Box } from "@oh-my-pi/pi-tui";
1
+ import { type Component } from "@oh-my-pi/pi-tui";
2
2
  import type { CompactionSummaryMessage } from "../../session/messages";
3
3
  /**
4
- * Component that renders a compaction message with collapsed/expanded state.
5
- * Uses same background color as hook messages for visual consistency.
4
+ * Compaction point in the transcript, rendered as a slim horizontal divider:
5
+ *
6
+ * ──────── 📷 compacted · ctrl+o ────────
7
+ *
8
+ * The conversation above the divider stays visible (display transcript keeps
9
+ * full history); only the LLM context was reset. Expanding (ctrl+o) reveals
10
+ * the compaction summary below the divider.
6
11
  */
7
- export declare class CompactionSummaryMessageComponent extends Box {
12
+ export declare class CompactionSummaryMessageComponent implements Component {
8
13
  #private;
9
14
  private readonly message;
10
15
  constructor(message: CompactionSummaryMessage);
11
16
  setExpanded(expanded: boolean): void;
12
17
  invalidate(): void;
18
+ render(width: number): readonly string[];
13
19
  }
@@ -42,6 +42,8 @@ export declare class CustomEditor extends Editor {
42
42
  onDequeue?: () => void;
43
43
  /** Called when Caps Lock is pressed. */
44
44
  onCapsLock?: () => void;
45
+ /** Called when left-arrow is pressed while the editor is empty (cursor necessarily at start). */
46
+ onLeftAtStart?: () => void;
45
47
  setActionKeys(action: ConfigurableEditorAction, keys: KeyId[]): void;
46
48
  /**
47
49
  * Register a custom key handler. Extensions use this for shortcuts.
@@ -67,6 +67,14 @@ export declare class ToolExecutionComponent extends Container {
67
67
  * transcript live region.
68
68
  */
69
69
  seal(): void;
70
+ /**
71
+ * Whether this block is a waiting `job` poll (every watched job still
72
+ * running) that has not been sealed. Such a block never finalized, so none
73
+ * of its rows entered native scrollback (the ticking spinner keeps the
74
+ * stable-prefix ratchet at zero) and the whole block can be removed when a
75
+ * follow-up `job` call supersedes it.
76
+ */
77
+ isDisplaceableBlock(): boolean;
70
78
  /**
71
79
  * Stop spinner animation and cleanup resources.
72
80
  */
@@ -3,11 +3,15 @@ import type { Rule } from "../../capability/rule";
3
3
  /**
4
4
  * Component that renders a TTSR (Time Traveling Stream Rules) notification.
5
5
  * Shows when a rule violation is detected and the stream is being rewound.
6
+ * One block can carry several rules: a single event may match multiple rules,
7
+ * and consecutive notifications merge into the previous block via
8
+ * {@link addRules} while it is still the live transcript tail.
6
9
  */
7
10
  export declare class TtsrNotificationComponent extends Container {
8
11
  #private;
9
- private readonly rules;
10
12
  constructor(rules: Rule[]);
13
+ /** Merge additional rules into this block (deduped by rule name). */
14
+ addRules(rules: Rule[]): void;
11
15
  setExpanded(expanded: boolean): void;
12
16
  isExpanded(): boolean;
13
17
  }
@@ -1,7 +1,7 @@
1
1
  import { type Component } from "@oh-my-pi/pi-tui";
2
2
  /**
3
- * Fixed number of session rows in the welcome box so its height doesn't shift
4
- * between the pre-TUI splash (loading placeholder) and the loaded state.
3
+ * Fixed number of session rows in the welcome box so its height stays stable
4
+ * across recent-session updates.
5
5
  */
6
6
  export declare const WELCOME_SESSION_SLOTS = 4;
7
7
  /**
@@ -29,14 +29,8 @@ export declare class WelcomeComponent implements Component {
29
29
  private providerName;
30
30
  private recentSessions;
31
31
  private lspServers;
32
- constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[] | null, lspServers?: LspServerInfo[]);
32
+ constructor(version: string, modelName: string, providerName: string, recentSessions?: RecentSession[], lspServers?: LspServerInfo[]);
33
33
  invalidate(): void;
34
- /**
35
- * Freeze the logo on the intro animation's first frame. The pre-TUI startup
36
- * splash uses this so the in-TUI intro — which starts at that exact frame —
37
- * picks up seamlessly from the splash's static box.
38
- */
39
- holdIntroFirstFrame(): void;
40
34
  /**
41
35
  * Play a one-shot intro that sweeps the gradient through every phase
42
36
  * before settling on the resting frame. Safe to call multiple times —
@@ -42,5 +42,5 @@ export declare class SelectorController {
42
42
  handleSessionDeleteCommand(): Promise<void>;
43
43
  showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
44
44
  showDebugSelector(): Promise<void>;
45
- showSessionObserver(registry: SessionObserverRegistry): void;
45
+ showAgentHub(observers: SessionObserverRegistry): void;
46
46
  }
@@ -100,6 +100,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
100
100
  locallySubmittedUserSignatures: Set<string>;
101
101
  lastSigintTime: number;
102
102
  lastEscapeTime: number;
103
+ lastLeftTapTime: number;
103
104
  shutdownRequested: boolean;
104
105
  hookSelector: HookSelectorComponent | undefined;
105
106
  hookInput: HookInputComponent | undefined;
@@ -210,7 +211,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
210
211
  updateFooter?: boolean;
211
212
  populateHistory?: boolean;
212
213
  }): void;
213
- renderInitialMessages(prebuiltContext?: SessionContext, options?: {
214
+ renderInitialMessages(options?: {
214
215
  preserveExistingChat?: boolean;
215
216
  clearTerminalHistory?: boolean;
216
217
  }): void;
@@ -238,7 +239,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
238
239
  handleMemoryCommand(text: string): Promise<void>;
239
240
  handleSTTToggle(): Promise<void>;
240
241
  showDebugSelector(): Promise<void>;
241
- showSessionObserver(): void;
242
+ showAgentHub(): void;
242
243
  resetObserverRegistry(): void;
243
244
  handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void>;
244
245
  handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void>;
@@ -6,7 +6,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
6
6
  /**
7
7
  * All available symbol keys organized by category.
8
8
  */
9
- export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal";
9
+ export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "icon.camera" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal" | "tool.irc";
10
10
  export type SpinnerType = "status" | "activity";
11
11
  export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
12
12
  /** Check if a string is a valid ThemeColor value */
@@ -170,6 +170,7 @@ export declare class Theme {
170
170
  extensionContextFile: string;
171
171
  extensionInstruction: string;
172
172
  mic: string;
173
+ camera: string;
173
174
  };
174
175
  get thinking(): {
175
176
  minimal: string;
@@ -117,6 +117,7 @@ export interface InteractiveModeContext {
117
117
  locallySubmittedUserSignatures: Set<string>;
118
118
  lastSigintTime: number;
119
119
  lastEscapeTime: number;
120
+ lastLeftTapTime: number;
120
121
  shutdownRequested: boolean;
121
122
  hookSelector: HookSelectorComponent | undefined;
122
123
  hookInput: HookInputComponent | undefined;
@@ -204,7 +205,7 @@ export interface InteractiveModeContext {
204
205
  updateFooter?: boolean;
205
206
  populateHistory?: boolean;
206
207
  }): void;
207
- renderInitialMessages(prebuiltContext?: SessionContext, options?: {
208
+ renderInitialMessages(options?: {
208
209
  preserveExistingChat?: boolean;
209
210
  clearTerminalHistory?: boolean;
210
211
  }): void;
@@ -266,7 +267,7 @@ export interface InteractiveModeContext {
266
267
  showProviderSetup(): Promise<void>;
267
268
  showHookConfirm(title: string, message: string): Promise<boolean>;
268
269
  showDebugSelector(): Promise<void>;
269
- showSessionObserver(): void;
270
+ showAgentHub(): void;
270
271
  resetObserverRegistry(): void;
271
272
  handleCtrlC(): void;
272
273
  handleCtrlD(): void;
@@ -36,7 +36,7 @@ export declare class UiHelpers {
36
36
  updateFooter?: boolean;
37
37
  populateHistory?: boolean;
38
38
  }): void;
39
- renderInitialMessages(prebuiltContext?: SessionContext, options?: RenderInitialMessagesOptions): void;
39
+ renderInitialMessages(options?: RenderInitialMessagesOptions): void;
40
40
  clearEditor(): void;
41
41
  showError(errorMessage: string): void;
42
42
  showWarning(warningMessage: string): void;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * AgentLifecycleManager - Owns the idle → parked → revived lifecycle of
3
+ * adopted subagents.
4
+ *
5
+ * The task executor hands a finished agent over via {@link AgentLifecycleManager.adopt};
6
+ * from then on the manager arms a TTL timer whenever the agent goes `idle`,
7
+ * parks it on expiry (disposes the live session, keeps the AgentRef +
8
+ * sessionFile), and revives it on demand through
9
+ * {@link AgentLifecycleManager.ensureLive}. Only this manager flips
10
+ * `parked` ↔ `idle`.
11
+ */
12
+ import type { AgentSession } from "../session/agent-session";
13
+ import { AgentRegistry } from "./agent-registry";
14
+ export type AgentReviver = () => Promise<AgentSession>;
15
+ export interface AdoptOptions {
16
+ /** TTL before an idle agent is parked. <= 0 disables parking. */
17
+ idleTtlMs: number;
18
+ /** Recreates a live AgentSession from the ref's sessionFile. Absent => not resumable after park (e.g. isolated runs). */
19
+ revive?: AgentReviver;
20
+ }
21
+ export declare class AgentLifecycleManager {
22
+ #private;
23
+ static global(): AgentLifecycleManager;
24
+ /** Reset the global manager. Test-only. */
25
+ static resetGlobalForTests(): void;
26
+ constructor(registry?: AgentRegistry);
27
+ /**
28
+ * Take ownership of a finished subagent. Caller has already set registry
29
+ * status to "idle". Arms the TTL timer (idleTtlMs <= 0 adopts without one).
30
+ */
31
+ adopt(id: string, opts: AdoptOptions): void;
32
+ /** True if the id is adopted (parked or live). */
33
+ has(id: string): boolean;
34
+ /** True while {@link park} is disposing this agent's session (lets dispose hooks distinguish park from teardown). */
35
+ isParking(id: string): boolean;
36
+ /**
37
+ * Dispose the live session, detach it from the registry, and mark the
38
+ * agent `parked`. No-op unless the id is adopted and live.
39
+ */
40
+ park(id: string): Promise<void>;
41
+ /**
42
+ * Return the live session, reviving from the sessionFile if parked.
43
+ * Throws a plain Error if the id is unknown or parked without a reviver.
44
+ * Concurrent calls share one in-flight revive.
45
+ */
46
+ ensureLive(id: string): Promise<AgentSession>;
47
+ /** Hard removal: dispose if live, unregister from registry, drop timers. */
48
+ release(id: string): Promise<void>;
49
+ /** Teardown everything (process exit / main session dispose). */
50
+ dispose(): Promise<void>;
51
+ }
@@ -1,13 +1,23 @@
1
1
  /**
2
- * AgentRegistry - Process-global registry of live AgentSession instances.
2
+ * AgentRegistry - Process-global registry of agents (the main session plus
3
+ * every subagent), keyed by stable id.
3
4
  *
4
- * Tracks every alive agent (the main session plus every subagent) so the
5
- * `irc` tool can address peers by id. Sessions are registered explicitly at
6
- * creation and removed when the owner releases them.
5
+ * Tracks each agent's status and (when live) its AgentSession so peers can be
6
+ * addressed by id (`irc`, `task resume`, `history://`). Sessions are
7
+ * registered explicitly at creation; finished agents stay registered as
8
+ * `idle` (live) or `parked` (session disposed, ref + sessionFile retained for
9
+ * revival) and are only removed on explicit release/teardown.
7
10
  */
8
11
  import type { AgentSession } from "../session/agent-session";
9
12
  export declare const MAIN_AGENT_ID = "Main";
10
- export type AgentStatus = "running" | "idle" | "completed" | "aborted";
13
+ /**
14
+ * - `running`: a turn is in flight.
15
+ * - `idle`: live AgentSession in memory, awaiting work. Finished agents are
16
+ * `idle`, not removed.
17
+ * - `parked`: session disposed; AgentRef + sessionFile retained, revivable.
18
+ * - `aborted`: hard-killed, terminal.
19
+ */
20
+ export type AgentStatus = "running" | "idle" | "parked" | "aborted";
11
21
  export type AgentKind = "main" | "sub";
12
22
  export interface AgentRef {
13
23
  id: string;
@@ -15,6 +25,7 @@ export interface AgentRef {
15
25
  kind: AgentKind;
16
26
  parentId?: string;
17
27
  status: AgentStatus;
28
+ /** Null exactly when parked/aborted. */
18
29
  session: AgentSession | null;
19
30
  sessionFile: string | null;
20
31
  createdAt: number;
@@ -36,9 +36,9 @@ import { type FileSlashCommand } from "../extensibility/slash-commands";
36
36
  import { GoalRuntime } from "../goals/runtime";
37
37
  import type { Goal, GoalModeState } from "../goals/state";
38
38
  import type { HindsightSessionState } from "../hindsight/state";
39
+ import type { IrcMessage } from "../irc/bus";
39
40
  import { type MnemopiSessionState } from "../mnemopi/state";
40
41
  import type { PlanModeState } from "../plan-mode/state";
41
- import { type AgentRegistry } from "../registry/agent-registry";
42
42
  import { type SecretObfuscator } from "../secrets/obfuscator";
43
43
  import { type ConfiguredThinkingLevel } from "../thinking";
44
44
  import { type DiscoverableTool, type DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
@@ -54,10 +54,10 @@ import { YieldQueue } from "./yield-queue";
54
54
  export type AgentSessionEvent = AgentEvent | {
55
55
  type: "auto_compaction_start";
56
56
  reason: "threshold" | "overflow" | "idle" | "incomplete";
57
- action: "context-full" | "handoff" | "shake";
57
+ action: "context-full" | "handoff" | "shake" | "snapcompact";
58
58
  } | {
59
59
  type: "auto_compaction_end";
60
- action: "context-full" | "handoff" | "shake";
60
+ action: "context-full" | "handoff" | "shake" | "snapcompact";
61
61
  result: CompactionResult | undefined;
62
62
  aborted: boolean;
63
63
  willRetry: boolean;
@@ -214,8 +214,6 @@ export interface AgentSessionConfig {
214
214
  asyncJobManager?: AsyncJobManager;
215
215
  /** Agent identity (registry id like "Main" or "Alice") used for IRC routing. */
216
216
  agentId?: string;
217
- /** Shared agent registry (for forwarding IRC observations to the main session UI). */
218
- agentRegistry?: AgentRegistry;
219
217
  /**
220
218
  * Override the provider-facing session ID for all API requests from this session.
221
219
  * When absent, `sessionManager.getSessionId()` is used. Needed when benchmark or
@@ -380,6 +378,16 @@ export declare class AgentSession {
380
378
  * Multiple listeners can be added. Returns unsubscribe function for this listener.
381
379
  */
382
380
  subscribe(listener: AgentSessionEventListener): () => void;
381
+ /**
382
+ * Synchronously mark the session as disposing so new work is rejected
383
+ * immediately: Python/eval starts throw, queued asides are dropped, and the
384
+ * aside provider is detached. Idempotent; `dispose()` runs it first.
385
+ *
386
+ * Wrappers that await other teardown before delegating to `dispose()` MUST
387
+ * call this before their first await — otherwise work started in that async
388
+ * gap slips past the disposal guards.
389
+ */
390
+ beginDispose(): void;
383
391
  /**
384
392
  * Remove all listeners, flush pending writes, and disconnect from agent.
385
393
  * Call this when completely done with the session.
@@ -483,6 +491,13 @@ export declare class AgentSession {
483
491
  /** All messages including custom types like BashExecutionMessage */
484
492
  get messages(): AgentMessage[];
485
493
  buildDisplaySessionContext(): SessionContext;
494
+ /**
495
+ * Full-history transcript for TUI display: every path entry in
496
+ * chronological order with compactions rendered inline at the point they
497
+ * fired (instead of replacing prior history). Display-only — NEVER feed
498
+ * the result to `agent.replaceMessages` or a provider.
499
+ */
500
+ buildTranscriptSessionContext(): SessionContext;
486
501
  /** Convert session messages using the same pre-LLM pipeline as the active session. */
487
502
  convertMessagesToLlm(messages: AgentMessage[], signal?: AbortSignal): Promise<Message[]>;
488
503
  /** Apply session-level stream hooks to a direct side request. */
@@ -910,27 +925,23 @@ export declare class AgentSession {
910
925
  /** Whether there are pending Python messages waiting to be flushed */
911
926
  get hasPendingPythonMessages(): boolean;
912
927
  /**
913
- * Generate an ephemeral reply to a background message (e.g. an IRC ping from
914
- * another agent) using this session's current model + system prompt + history.
928
+ * Deliver an IRC message into this session (recipient side; called by the
929
+ * IrcBus). Emits the `irc_message` session event for UI cards and injects
930
+ * the rendered message into the model's context as an `irc:incoming`
931
+ * custom message:
915
932
  *
916
- * The incoming message is queued for injection into the recipient's persisted
917
- * history immediately so timeouts/abort still preserve delivery. The reply is
918
- * computed via a side-channel `streamSimple` call (analogous to `/btw`) so it
919
- * never blocks on the recipient's in-flight tool calls. When a reply is
920
- * generated, it is queued separately. Injection happens immediately when the
921
- * session is idle, otherwise it is deferred until streaming ends.
922
- */
923
- respondAsBackground(args: {
924
- from: string;
925
- message: string;
926
- awaitReply?: boolean;
927
- signal?: AbortSignal;
928
- }): Promise<{
929
- replyText: string | null;
930
- }>;
933
+ * - mid-turn queued on the aside channel and folded in at the next step
934
+ * boundary (non-interrupting, like async-result deliveries) "injected";
935
+ * - idle → starts a real turn with the message so the recipient wakes
936
+ * "woken".
937
+ *
938
+ * Never blocks on the recipient's turn: the wake turn is fire-and-forget.
939
+ */
940
+ deliverIrcMessage(msg: IrcMessage): Promise<"injected" | "woken">;
931
941
  /**
932
942
  * Emit an IRC relay observation event on this session for UI rendering only.
933
- * Does not persist the record to history. Public so other sessions can forward.
943
+ * Does not persist the record to history. Called by the IrcBus to surface
944
+ * agent↔agent traffic on the main session.
934
945
  */
935
946
  emitIrcRelayObservation(record: CustomMessage): void;
936
947
  /**
@@ -939,7 +950,7 @@ export declare class AgentSession {
939
950
  * does not block on, or interfere with, any in-flight main turn. The
940
951
  * session's history and persisted state are NOT modified by this call.
941
952
  *
942
- * Used by `respondAsBackground` (IRC) and `BtwController` (`/btw`) to share
953
+ * Used by `BtwController` (`/btw`) and `OmfgController` (`/omfg`) to share
943
954
  * the snapshot + stream pipeline. The snapshot includes any in-flight
944
955
  * streaming assistant text so the model sees the half-finished response
945
956
  * rather than missing context.
@@ -1045,12 +1056,6 @@ export declare class AgentSession {
1045
1056
  * Includes user messages, assistant text, thinking blocks, tool calls, and tool results.
1046
1057
  */
1047
1058
  formatSessionAsText(): string;
1048
- /**
1049
- * Format the conversation as compact context for subagents.
1050
- * Includes only user messages and assistant text responses.
1051
- * Excludes: system prompt, tool definitions, tool calls/results, thinking blocks.
1052
- */
1053
- formatCompactContext(): string;
1054
1059
  /**
1055
1060
  * Check if extensions have handlers for a specific event type.
1056
1061
  */
@@ -7,7 +7,7 @@
7
7
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
8
8
  import { type BranchSummaryMessage, type CompactionSummaryMessage } from "@oh-my-pi/pi-agent-core/compaction/messages";
9
9
  import type { AssistantMessage, ImageContent, Message, MessageAttribution, TextContent } from "@oh-my-pi/pi-ai";
10
- export { type BranchSummaryMessage, type CompactionSummaryMessage, createBranchSummaryMessage, createCompactionSummaryMessage, } from "@oh-my-pi/pi-agent-core/compaction/messages";
10
+ export { type BranchSummaryMessage, type CompactionSummaryMessage, createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage, } from "@oh-my-pi/pi-agent-core/compaction/messages";
11
11
  import type { OutputMeta } from "../tools/output-meta";
12
12
  export declare const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
13
13
  export declare const LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE = "lsp-late-diagnostic";
@@ -33,7 +33,7 @@ export interface SkillPromptDetails {
33
33
  *
34
34
  * Consumers: `AgentSession.#handleAgentEvent` (stamper) writes this value;
35
35
  * `EventController.#handleMessageEnd`, `AssistantMessageComponent`,
36
- * `ui-helpers.addMessageToChat` (renderers), `SessionObserverOverlay
36
+ * `ui-helpers.addMessageToChat` (renderers), `AgentHubOverlayComponent
37
37
  * #buildTranscriptLines`, `runPrintMode`, and `AcpAgent#replayAssistantMessage`
38
38
  * (fallback error emission) read it via `isSilentAbort`. */
39
39
  export declare const SILENT_ABORT_MARKER = "__omp.silent_abort__";
@@ -175,8 +175,6 @@ export declare function bashExecutionToText(msg: BashExecutionMessage): string;
175
175
  */
176
176
  export declare function pythonExecutionToText(msg: PythonExecutionMessage): string;
177
177
  export declare function sanitizeRehydratedOpenAIResponsesAssistantMessage(message: AssistantMessage): AssistantMessage;
178
- /** Convert CustomMessageEntry to AgentMessage format */
179
- export declare function createCustomMessage(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details: unknown | undefined, timestamp: string, attribution?: MessageAttribution): CustomMessage;
180
178
  /**
181
179
  * Transform AgentMessages (including custom types) to LLM-compatible Messages.
182
180
  *
@@ -0,0 +1,12 @@
1
+ export interface HistoryFormatOptions {
2
+ /** Optional H1 prepended to the transcript. */
3
+ title?: string;
4
+ }
5
+ /**
6
+ * Format a session's message array as a concise markdown transcript.
7
+ *
8
+ * `messages` is the session's in-memory message array (or the read-only
9
+ * equivalent loaded from a session file) — the same shapes
10
+ * `session-dump-format.ts` consumes.
11
+ */
12
+ export declare function formatSessionHistoryMarkdown(messages: unknown[], opts?: HistoryFormatOptions): string;