@oh-my-pi/pi-coding-agent 15.11.3 → 15.11.6

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 (135) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/cli.js +692 -607
  3. package/dist/types/cli/usage-cli.d.ts +10 -1
  4. package/dist/types/commands/usage.d.ts +9 -0
  5. package/dist/types/config/api-key-resolver.d.ts +9 -3
  6. package/dist/types/config/keybindings.d.ts +1 -1
  7. package/dist/types/config/model-discovery.d.ts +6 -4
  8. package/dist/types/config/model-registry.d.ts +7 -4
  9. package/dist/types/config/settings-schema.d.ts +508 -155
  10. package/dist/types/export/html/template.generated.d.ts +1 -1
  11. package/dist/types/mnemopi/config.d.ts +3 -1
  12. package/dist/types/modes/components/reset-usage-selector.d.ts +12 -0
  13. package/dist/types/modes/components/session-selector.d.ts +1 -1
  14. package/dist/types/modes/components/settings-defs.d.ts +9 -2
  15. package/dist/types/modes/components/settings-selector.d.ts +9 -4
  16. package/dist/types/modes/components/tool-execution.d.ts +26 -1
  17. package/dist/types/modes/components/transcript-container.d.ts +12 -0
  18. package/dist/types/modes/controllers/input-controller.d.ts +9 -1
  19. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  20. package/dist/types/modes/interactive-mode.d.ts +10 -0
  21. package/dist/types/modes/session-observer-registry.d.ts +2 -0
  22. package/dist/types/modes/theme/theme.d.ts +23 -3
  23. package/dist/types/modes/types.d.ts +2 -0
  24. package/dist/types/modes/utils/context-usage.d.ts +6 -1
  25. package/dist/types/session/agent-session.d.ts +28 -8
  26. package/dist/types/session/auth-storage.d.ts +1 -1
  27. package/dist/types/session/codex-auto-reset.d.ts +107 -0
  28. package/dist/types/session/snapcompact-inline.d.ts +129 -0
  29. package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +14 -0
  30. package/dist/types/slash-commands/helpers/reset-usage.d.ts +27 -0
  31. package/dist/types/system-prompt.d.ts +3 -1
  32. package/dist/types/task/render.d.ts +17 -6
  33. package/dist/types/tools/gh.d.ts +3 -0
  34. package/dist/types/tools/render-utils.d.ts +8 -16
  35. package/dist/types/tools/todo.d.ts +0 -11
  36. package/dist/types/utils/session-color.d.ts +15 -3
  37. package/dist/types/web/kagi.d.ts +1 -2
  38. package/dist/types/web/search/providers/codex.d.ts +1 -1
  39. package/dist/types/web/search/providers/gemini.d.ts +9 -6
  40. package/package.json +11 -11
  41. package/src/auto-thinking/classifier.ts +1 -5
  42. package/src/cli/usage-cli.ts +187 -16
  43. package/src/commands/usage.ts +8 -0
  44. package/src/commit/model-selection.ts +3 -6
  45. package/src/config/api-key-resolver.ts +10 -3
  46. package/src/config/keybindings.ts +1 -1
  47. package/src/config/model-discovery.ts +60 -46
  48. package/src/config/model-registry.ts +21 -8
  49. package/src/config/model-resolver.ts +57 -3
  50. package/src/config/settings-schema.ts +654 -153
  51. package/src/config/settings.ts +9 -0
  52. package/src/eval/completion-bridge.ts +1 -5
  53. package/src/export/html/template.generated.ts +1 -1
  54. package/src/export/html/template.js +13 -6
  55. package/src/internal-urls/docs-index.generated.ts +6 -6
  56. package/src/internal-urls/issue-pr-protocol.ts +10 -4
  57. package/src/memories/index.ts +2 -10
  58. package/src/mnemopi/backend.ts +30 -8
  59. package/src/mnemopi/config.ts +6 -1
  60. package/src/mnemopi/state.ts +6 -0
  61. package/src/modes/components/extensions/inspector-panel.ts +6 -2
  62. package/src/modes/components/plan-review-overlay.ts +15 -17
  63. package/src/modes/components/plugin-settings.ts +22 -5
  64. package/src/modes/components/reset-usage-selector.ts +161 -0
  65. package/src/modes/components/session-selector.ts +8 -2
  66. package/src/modes/components/settings-defs.ts +19 -4
  67. package/src/modes/components/settings-selector.ts +510 -95
  68. package/src/modes/components/status-line/component.ts +3 -1
  69. package/src/modes/components/status-line/segments.ts +3 -1
  70. package/src/modes/components/tool-execution.ts +87 -12
  71. package/src/modes/components/transcript-container.ts +49 -1
  72. package/src/modes/components/tree-selector.ts +16 -6
  73. package/src/modes/controllers/command-controller.ts +61 -8
  74. package/src/modes/controllers/event-controller.ts +1 -0
  75. package/src/modes/controllers/input-controller.ts +68 -6
  76. package/src/modes/controllers/selector-controller.ts +149 -61
  77. package/src/modes/interactive-mode.ts +63 -2
  78. package/src/modes/rpc/rpc-mode.ts +2 -1
  79. package/src/modes/session-observer-registry.ts +61 -3
  80. package/src/modes/shared.ts +2 -0
  81. package/src/modes/theme/theme.ts +102 -9
  82. package/src/modes/types.ts +2 -0
  83. package/src/modes/utils/context-usage.ts +78 -2
  84. package/src/modes/utils/hotkeys-markdown.ts +1 -1
  85. package/src/modes/utils/ui-helpers.ts +9 -5
  86. package/src/prompts/system/personalities/default.md +26 -0
  87. package/src/prompts/system/personalities/friendly.md +17 -0
  88. package/src/prompts/system/personalities/pragmatic.md +15 -0
  89. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  90. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  91. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  92. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  93. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  94. package/src/prompts/system/system-prompt.md +5 -22
  95. package/src/prompts/tools/browser.md +33 -43
  96. package/src/prompts/tools/eval.md +27 -50
  97. package/src/prompts/tools/irc.md +29 -31
  98. package/src/prompts/tools/read.md +31 -37
  99. package/src/prompts/tools/task.md +3 -3
  100. package/src/prompts/tools/todo.md +1 -2
  101. package/src/sdk.ts +23 -1
  102. package/src/session/agent-session.ts +221 -29
  103. package/src/session/auth-storage.ts +4 -0
  104. package/src/session/codex-auto-reset.ts +190 -0
  105. package/src/session/session-dump-format.ts +8 -1
  106. package/src/session/session-manager.ts +5 -5
  107. package/src/session/snapcompact-inline.ts +524 -0
  108. package/src/slash-commands/builtin-registry.ts +145 -8
  109. package/src/slash-commands/helpers/active-oauth-account.ts +44 -0
  110. package/src/slash-commands/helpers/context-report.ts +28 -1
  111. package/src/slash-commands/helpers/reset-usage.ts +66 -0
  112. package/src/slash-commands/helpers/usage-report.ts +36 -3
  113. package/src/system-prompt.ts +15 -1
  114. package/src/task/index.ts +30 -7
  115. package/src/task/render.ts +57 -32
  116. package/src/tool-discovery/tool-index.ts +2 -0
  117. package/src/tools/bash.ts +10 -3
  118. package/src/tools/eval-render.ts +13 -8
  119. package/src/tools/gh.ts +39 -1
  120. package/src/tools/image-gen.ts +114 -78
  121. package/src/tools/inspect-image.ts +1 -5
  122. package/src/tools/job.ts +25 -5
  123. package/src/tools/read.ts +1 -57
  124. package/src/tools/render-utils.ts +29 -31
  125. package/src/tools/ssh.ts +3 -3
  126. package/src/tools/todo.ts +8 -128
  127. package/src/tools/tts.ts +40 -20
  128. package/src/utils/clipboard.ts +56 -4
  129. package/src/utils/commit-message-generator.ts +1 -5
  130. package/src/utils/session-color.ts +83 -9
  131. package/src/utils/title-generator.ts +1 -1
  132. package/src/web/kagi.ts +26 -27
  133. package/src/web/search/providers/codex.ts +42 -40
  134. package/src/web/search/providers/gemini.ts +42 -22
  135. package/src/web/search/providers/perplexity.ts +22 -10
@@ -2,7 +2,7 @@ import type { MnemopiOptions } from "@oh-my-pi/pi-mnemopi";
2
2
  import type { Settings } from "../config/settings";
3
3
  export type MnemopiLlmMode = "none" | "smol" | "remote";
4
4
  export type MnemopiScoping = "global" | "per-project" | "per-project-tagged";
5
- export type MnemopiProviderOptions = Pick<MnemopiOptions, "noEmbeddings" | "embeddingModel" | "embeddingApiUrl" | "embeddingApiKey" | "llm">;
5
+ export type MnemopiProviderOptions = Pick<MnemopiOptions, "noEmbeddings" | "embeddingModel" | "embeddingApiUrl" | "embeddingApiKey" | "llm" | "debug">;
6
6
  export interface MnemopiBackendConfig {
7
7
  dbPath: string;
8
8
  baseBank?: string;
@@ -13,6 +13,8 @@ export interface MnemopiBackendConfig {
13
13
  scoping?: MnemopiScoping;
14
14
  autoRecall: boolean;
15
15
  autoRetain: boolean;
16
+ polyphonicRecall: boolean;
17
+ enhancedRecall: boolean;
16
18
  retainEveryNTurns: number;
17
19
  recallLimit: number;
18
20
  recallContextTurns: number;
@@ -0,0 +1,12 @@
1
+ import { Container } from "@oh-my-pi/pi-tui";
2
+ import type { ResetUsageAccount } from "../../slash-commands/helpers/reset-usage";
3
+ /**
4
+ * Account picker for `/usage reset`. Lists Codex accounts with their saved
5
+ * rate-limit reset counts; selecting one redeems a reset. Because a reset is a
6
+ * scarce, irreversible credit, Enter requires a second press to confirm.
7
+ */
8
+ export declare class ResetUsageSelectorComponent extends Container {
9
+ #private;
10
+ constructor(accounts: ResetUsageAccount[], onSelect: (account: ResetUsageAccount) => void, onCancel: () => void);
11
+ handleInput(keyData: string): void;
12
+ }
@@ -8,7 +8,7 @@ export type SessionHistoryMatcher = (query: string) => string[];
8
8
  * Resume search narrows a recency-sorted list: once every query token appears
9
9
  * as a literal substring, newer sessions should beat a slightly better fuzzy
10
10
  * position match. Pure fuzzy/acronym matches still sort by fuzzy score after
11
- * literal matches.
11
+ * literal matches, but weak pure fuzzy tokens are dropped as noise.
12
12
  */
13
13
  export declare function rankSessionSearchMatches(allSessions: SessionInfo[], query: string): SessionInfo[];
14
14
  /**
@@ -4,7 +4,8 @@
4
4
  * settings selector.
5
5
  *
6
6
  * To add a new setting to the UI: declare it in `settings-schema.ts`
7
- * with a `ui` block. If it needs a submenu, include `options: [...]`
7
+ * with a `ui` block carrying `tab` and `group` (the group must be listed
8
+ * in `TAB_GROUPS[tab]`). If it needs a submenu, include `options: [...]`
8
9
  * (or `options: "runtime"` for runtime-injected lists like themes).
9
10
  */
10
11
  import { type SettingPath, type SettingTab, type SubmenuOption } from "../../config/settings-schema";
@@ -14,6 +15,8 @@ interface BaseSettingDef {
14
15
  label: string;
15
16
  description: string;
16
17
  tab: SettingTab;
18
+ /** Section within the tab; items are ordered by TAB_GROUPS[tab] and rendered under a heading row. */
19
+ group?: string;
17
20
  /**
18
21
  * Optional visibility predicate. When supplied and returning false, the
19
22
  * setting is hidden from the UI. Applies to every variant — booleans,
@@ -41,7 +44,11 @@ export interface TextInputSettingDef extends BaseSettingDef {
41
44
  export type SettingDef = BooleanSettingDef | EnumSettingDef | SubmenuSettingDef | TextInputSettingDef;
42
45
  /** Get all setting definitions with UI */
43
46
  export declare function getAllSettingDefs(): SettingDef[];
44
- /** Get settings for a specific tab */
47
+ /**
48
+ * Get settings for a specific tab, ordered by the tab's group layout
49
+ * (TAB_GROUPS). Ungrouped settings sort first; within a group, schema
50
+ * declaration order is preserved.
51
+ */
45
52
  export declare function getSettingsForTab(tab: SettingTab): SettingDef[];
46
53
  /** Get a setting definition by path */
47
54
  export declare function getSettingDef(path: SettingPath): SettingDef | undefined;
@@ -1,9 +1,8 @@
1
1
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Effort } from "@oh-my-pi/pi-ai";
3
- import { Container, SettingsList } from "@oh-my-pi/pi-tui";
3
+ import { type Component } from "@oh-my-pi/pi-tui";
4
4
  import { type SettingPath } from "../../config/settings";
5
5
  import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../config/settings-schema";
6
- import { PluginSettingsComponent } from "./plugin-settings";
7
6
  /**
8
7
  * Dynamic context for settings that need runtime data.
9
8
  * Some settings (like thinking level) are managed by the session, not Settings.
@@ -45,11 +44,17 @@ export interface SettingsCallbacks {
45
44
  * Main tabbed settings selector component.
46
45
  * Uses declarative settings definitions from settings-defs.ts.
47
46
  */
48
- export declare class SettingsSelectorComponent extends Container {
47
+ export declare class SettingsSelectorComponent implements Component {
49
48
  #private;
50
49
  private readonly context;
51
50
  private readonly callbacks;
52
51
  constructor(context: SettingsRuntimeContext, callbacks: SettingsCallbacks);
53
- getFocusComponent(): SettingsList | PluginSettingsComponent;
52
+ invalidate(): void;
53
+ /**
54
+ * Fullscreen frame: title border, tab row, divider, optional search banner,
55
+ * the active content sized to fill the terminal, the appearance preview,
56
+ * then a footer hint pinned above the bottom border.
57
+ */
58
+ render(width: number): readonly string[];
54
59
  handleInput(data: string): void;
55
60
  }
@@ -1,11 +1,22 @@
1
1
  import type { SnapshotStore } from "@oh-my-pi/hashline";
2
2
  import type { AgentTool } from "@oh-my-pi/pi-agent-core";
3
- import { Container, type TUI } from "@oh-my-pi/pi-tui";
3
+ import { type Component, Container, type TUI } from "@oh-my-pi/pi-tui";
4
+ /**
5
+ * Transcript-side probe telling a block whether it is still inside the live
6
+ * (repaintable) region. Implemented by `TranscriptContainer`; injected rather
7
+ * than imported so the component stays decoupled from the transcript.
8
+ */
9
+ export interface TranscriptLiveRegionProbe {
10
+ isBlockInLiveRegion(component: Component): boolean;
11
+ }
4
12
  export interface ToolExecutionOptions {
5
13
  snapshots?: SnapshotStore;
6
14
  showImages?: boolean;
7
15
  editFuzzyThreshold?: number;
8
16
  editAllowFuzzy?: boolean;
17
+ /** Live-region probe used to settle detached task progress once the block
18
+ * leaves the repaintable transcript region. */
19
+ liveRegion?: TranscriptLiveRegionProbe;
9
20
  }
10
21
  export interface ToolExecutionHandle {
11
22
  updateArgs(args: any, toolCallId?: string): void;
@@ -61,6 +72,20 @@ export declare class ToolExecutionComponent extends Container {
61
72
  * past, or an explicit {@link seal} flips it to `true`.
62
73
  */
63
74
  isTranscriptBlockFinalized(): boolean;
75
+ /**
76
+ * Whether this still-live block's settled rows may enter native scrollback
77
+ * (see `FinalizableBlock.isTranscriptBlockCommitStable`). A pending
78
+ * collapsed preview is provisional: the tail-window streaming views
79
+ * (edit/bash/eval caps) are re-anchored top-first by the result render, so
80
+ * promoting their visually static head — e.g. an edit preview idling on
81
+ * its last frame while the apply + LSP pass runs — would strand a stale
82
+ * copy of the call box above the final block the moment the result lands.
83
+ * Expanded pending blocks stream top-anchored append-shaped content whose
84
+ * rows the result render preserves byte-stable (the over-tall write/eval
85
+ * scrollback contract), so they stay commit-eligible. Displaceable waiting
86
+ * polls are removed wholesale by the next poll and must never commit.
87
+ */
88
+ isTranscriptBlockCommitStable(): boolean;
64
89
  /**
65
90
  * Mark the tool terminal even though no result arrived (the turn aborted or
66
91
  * abandoned it) and stop animating, so it can freeze and stops pinning the
@@ -40,6 +40,18 @@ export declare class TranscriptContainer extends Container implements NativeScro
40
40
  * only repair by recommitting everything below it — duplication.
41
41
  */
42
42
  isWithinLiveRegion(component: Component): boolean;
43
+ /**
44
+ * Whether `component` is inside the live (repaintable) region exactly as
45
+ * {@link render} computes it: at/after the first still-mutating block, or
46
+ * the transcript tail when every block has finalized. Unlike
47
+ * {@link isWithinLiveRegion} (strictly below a still-mutating block, i.e.
48
+ * guaranteed-uncommitted), this also counts the trailing block that anchors
49
+ * the live region. Self-animating finalized blocks (a detached task's
50
+ * shimmering progress rows) poll this to stop animating — and settle on
51
+ * static bytes — the moment they sit above the seam, where their rows
52
+ * become commit-eligible native-scrollback history.
53
+ */
54
+ isBlockInLiveRegion(component: Component): boolean;
43
55
  render(width: number): readonly string[];
44
56
  }
45
57
  /**
@@ -1,9 +1,17 @@
1
1
  import type { AutocompleteProvider, SlashCommand } from "@oh-my-pi/pi-tui";
2
2
  import type { InteractiveModeContext } from "../../modes/types";
3
+ import { readImageFromClipboard, readTextFromClipboard } from "../../utils/clipboard";
3
4
  export declare class InputController {
4
5
  #private;
5
6
  private ctx;
6
- constructor(ctx: InteractiveModeContext);
7
+ /** Injectable clipboard reads so tests can drive paste flows without a real clipboard. */
8
+ private clipboard;
9
+ constructor(ctx: InteractiveModeContext,
10
+ /** Injectable clipboard reads so tests can drive paste flows without a real clipboard. */
11
+ clipboard?: {
12
+ readImage: typeof readImageFromClipboard;
13
+ readText: typeof readTextFromClipboard;
14
+ });
7
15
  setupKeyHandlers(): void;
8
16
  setupEditorSubmitHandler(): void;
9
17
  handleCtrlC(): void;
@@ -41,6 +41,7 @@ export declare class SelectorController {
41
41
  handleResumeSession(sessionPath: string): Promise<void>;
42
42
  handleSessionDeleteCommand(): Promise<void>;
43
43
  showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
44
+ showResetUsageSelector(): Promise<void>;
44
45
  showDebugSelector(): Promise<void>;
45
46
  showAgentHub(observers: SessionObserverRegistry): void;
46
47
  }
@@ -27,6 +27,7 @@ import type { ToolExecutionHandle } from "./components/tool-execution";
27
27
  import { TranscriptContainer } from "./components/transcript-container";
28
28
  import { type LoopLimitRuntime } from "./loop-limit";
29
29
  import { OAuthManualInputManager } from "./oauth-manual-input";
30
+ import type { ObservableSession } from "./session-observer-registry";
30
31
  import type { Theme } from "./theme/theme";
31
32
  import type { CompactionQueuedMessage, InteractiveModeContext, InteractiveModeInitOptions, InteractiveSelectorDialogOptions, SubmittedUserInput, TodoItem, TodoPhase } from "./types";
32
33
  /** Options for creating an InteractiveMode instance (for future API use) */
@@ -42,6 +43,13 @@ export interface InteractiveModeOptions {
42
43
  /** Additional initial messages to queue */
43
44
  initialMessages?: string[];
44
45
  }
46
+ /**
47
+ * Build the anchored subagent HUD block: a bold accent "Subagents" header plus
48
+ * one hooked row per running agent in the same `Id: description` shape the
49
+ * inline task rows use (muted task preview when no description was given).
50
+ * Returns an empty array when nothing is running so the container can clear.
51
+ */
52
+ export declare function renderSubagentHudLines(sessions: ObservableSession[], columns: number): string[];
45
53
  export declare class InteractiveMode implements InteractiveModeContext {
46
54
  #private;
47
55
  session: AgentSession;
@@ -56,6 +64,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
56
64
  pendingMessagesContainer: Container;
57
65
  statusContainer: Container;
58
66
  todoContainer: Container;
67
+ subagentContainer: Container;
59
68
  btwContainer: Container;
60
69
  omfgContainer: Container;
61
70
  errorBannerContainer: Container;
@@ -265,6 +274,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
265
274
  handleResumeSession(sessionPath: string): Promise<void>;
266
275
  handleSessionDeleteCommand(): Promise<void>;
267
276
  showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
277
+ showResetUsageSelector(): Promise<void>;
268
278
  showProviderSetup(): Promise<void>;
269
279
  showHookConfirm(title: string, message: string): Promise<boolean>;
270
280
  handleCtrlC(): void;
@@ -8,6 +8,8 @@ export interface ObservableSession {
8
8
  description?: string;
9
9
  status: "active" | "completed" | "failed" | "aborted";
10
10
  sessionFile?: string;
11
+ parentToolCallId?: string;
12
+ index?: number;
11
13
  lastUpdate: number;
12
14
  /** Latest progress snapshot from the subagent executor */
13
15
  progress?: AgentProgress;
@@ -1,12 +1,12 @@
1
1
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Effort } from "@oh-my-pi/pi-ai";
3
- import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
3
+ import type { EditorTheme, MarkdownTheme, SelectListTheme, SettingsListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
4
4
  export { getLanguageFromPath } from "../../utils/lang-from-path";
5
5
  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.job" | "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";
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.job" | "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.files" | "tab.shell" | "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 */
@@ -33,6 +33,26 @@ export declare class Theme {
33
33
  * dark themes so accents stay vivid. Pass straight to `getSessionAccentHex`.
34
34
  */
35
35
  get accentSurfaceLuminance(): number | undefined;
36
+ /**
37
+ * Get the resolved CSS hex string for a foreground theme color.
38
+ */
39
+ getColorHex(color: ThemeColor): string;
40
+ /**
41
+ * Get all foreground and background theme colors as CSS hex strings.
42
+ * Skips colors resolved to the default terminal color (unstyled).
43
+ */
44
+ getAllThemeColorHexes(): string[];
45
+ /**
46
+ * Get the most visually dominant theme colors as CSS hex strings — accent,
47
+ * border, success, error, warning, heading, link, diff markers, etc.
48
+ * These are the colors the session accent could visually clash with.
49
+ * Skips colors resolved to the default terminal color (unstyled).
50
+ */
51
+ getMajorThemeColorHexes(): string[];
52
+ /**
53
+ * Get the resolved CSS hex string for the theme's accent color.
54
+ */
55
+ getAccentColorHex(): string;
36
56
  fg(color: ThemeColor, text: string): string;
37
57
  bg(color: ThemeBg, text: string): string;
38
58
  bold(text: string): string;
@@ -305,4 +325,4 @@ export declare function getSymbolTheme(): SymbolTheme;
305
325
  export declare function getMarkdownTheme(): MarkdownTheme;
306
326
  export declare function getSelectListTheme(): SelectListTheme;
307
327
  export declare function getEditorTheme(): EditorTheme;
308
- export declare function getSettingsListTheme(): import("@oh-my-pi/pi-tui").SettingsListTheme;
328
+ export declare function getSettingsListTheme(): SettingsListTheme;
@@ -66,6 +66,7 @@ export interface InteractiveModeContext {
66
66
  pendingMessagesContainer: Container;
67
67
  statusContainer: Container;
68
68
  todoContainer: Container;
69
+ subagentContainer: Container;
69
70
  btwContainer: Container;
70
71
  omfgContainer: Container;
71
72
  errorBannerContainer: Container;
@@ -264,6 +265,7 @@ export interface InteractiveModeContext {
264
265
  handleResumeSession(sessionPath: string): Promise<void>;
265
266
  handleSessionDeleteCommand(): Promise<void>;
266
267
  showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
268
+ showResetUsageSelector(): Promise<void>;
267
269
  showProviderSetup(): Promise<void>;
268
270
  showHookConfirm(title: string, message: string): Promise<boolean>;
269
271
  showDebugSelector(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import type { Model } from "@oh-my-pi/pi-ai";
2
2
  import type { Skill } from "../../extensibility/skills";
3
3
  import type { AgentSession } from "../../session/agent-session";
4
+ import { type SnapcompactSavingsEstimate } from "../../session/snapcompact-inline";
4
5
  import type { Tool } from "../../tools";
5
6
  import type { theme as Theme } from "../theme/theme";
6
7
  type CategoryId = "systemPrompt" | "systemContext" | "systemTools" | "skills" | "messages";
@@ -18,6 +19,8 @@ export interface ContextBreakdown {
18
19
  usedTokens: number;
19
20
  autoCompactBufferTokens: number;
20
21
  freeTokens: number;
22
+ /** Estimated snapcompact wire savings; set when requested and a snapcompact.* setting is enabled. */
23
+ snapcompact?: SnapcompactSavingsEstimate;
21
24
  }
22
25
  export declare function estimateSkillsTokens(skills: readonly Skill[]): number;
23
26
  export declare function estimateToolSchemaTokens(tools: ReadonlyArray<Pick<Tool, "name" | "description" | "parameters">>): number;
@@ -38,7 +41,9 @@ export declare function computeNonMessageTokens(session: AgentSession): number;
38
41
  * Compute a breakdown of estimated context usage by category for the active
39
42
  * session and model.
40
43
  */
41
- export declare function computeContextBreakdown(session: AgentSession): ContextBreakdown;
44
+ export declare function computeContextBreakdown(session: AgentSession, options?: {
45
+ snapcompactSavings?: boolean;
46
+ }): ContextBreakdown;
42
47
  /**
43
48
  * Render a colorful context-usage panel as ANSI text. Output is a series of
44
49
  * lines pairing the grid (left) with the legend (right).
@@ -15,7 +15,7 @@
15
15
  import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
16
16
  import { type Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
17
17
  import { type CompactionResult, type ShakeConfig } from "@oh-my-pi/pi-agent-core/compaction";
18
- import type { AssistantMessage, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@oh-my-pi/pi-ai";
18
+ import type { AssistantMessage, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ResetCreditAccountStatus, ResetCreditRedeemOutcome, ResetCreditTarget, ServiceTier, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@oh-my-pi/pi-ai";
19
19
  import { Effort } from "@oh-my-pi/pi-ai";
20
20
  import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async";
21
21
  import type { Rule } from "../capability/rule";
@@ -300,6 +300,11 @@ export interface FreshSessionResult {
300
300
  sessionId: string;
301
301
  closedProviderSessions: number;
302
302
  }
303
+ /** Entry returned by {@link AgentSession.clearQueue} / {@link AgentSession.popLastQueuedMessage}. */
304
+ export type RestoredQueuedMessage = {
305
+ text: string;
306
+ images?: ImageContent[];
307
+ };
303
308
  export declare class AgentSession {
304
309
  #private;
305
310
  readonly agent: Agent;
@@ -610,12 +615,14 @@ export declare class AgentSession {
610
615
  deliverAs?: "steer" | "followUp";
611
616
  }): Promise<void>;
612
617
  /**
613
- * Clear queued messages and return them.
614
- * Useful for restoring to editor when user aborts.
618
+ * Clear queued messages and return them (text plus any attached images).
619
+ * Useful for restoring to editor when user aborts. The internal entry
620
+ * arrays are handed out as-is — a `tag` (if any) is inert once the record
621
+ * leaves the queue.
615
622
  */
616
623
  clearQueue(): {
617
- steering: string[];
618
- followUp: string[];
624
+ steering: RestoredQueuedMessage[];
625
+ followUp: RestoredQueuedMessage[];
619
626
  };
620
627
  /** Number of pending messages (includes steering, follow-up, and next-turn messages) */
621
628
  get queuedMessageCount(): number;
@@ -630,10 +637,10 @@ export declare class AgentSession {
630
637
  /**
631
638
  * Pop the last queued message (steering first, then follow-up).
632
639
  * Used by dequeue keybinding to restore messages to editor one at a time.
633
- * Returns the popped entry's `.text`; the tag (if any) dies with the
634
- * record — no orphan state can outlive the queue entry.
640
+ * Returns the popped entry's text and images; the tag (if any) dies with
641
+ * the record — no orphan state can outlive the queue entry.
635
642
  */
636
- popLastQueuedMessage(): string | undefined;
643
+ popLastQueuedMessage(): RestoredQueuedMessage | undefined;
637
644
  get skillsSettings(): SkillsSettings | undefined;
638
645
  /** Skills loaded by SDK (empty if --no-skills or skills: [] was passed) */
639
646
  get skills(): readonly Skill[];
@@ -1045,6 +1052,19 @@ export declare class AgentSession {
1045
1052
  contextWindow?: number;
1046
1053
  }): ContextUsage | undefined;
1047
1054
  fetchUsageReports(signal?: AbortSignal): Promise<UsageReport[] | null>;
1055
+ /**
1056
+ * Redeem one saved Codex rate-limit reset for a specific account, injecting
1057
+ * the provider base URL like {@link AgentSession.fetchUsageReports}. Powers
1058
+ * the `/usage reset` command and auto-redeem. Never throws for business
1059
+ * outcomes — inspect the returned `code`.
1060
+ */
1061
+ redeemResetCredit(target: ResetCreditTarget, signal?: AbortSignal): Promise<ResetCreditRedeemOutcome>;
1062
+ /**
1063
+ * List saved Codex rate-limit resets per stored account, fetched live from
1064
+ * the dedicated credits endpoint (bypasses the usage cache). Powers the
1065
+ * `/usage reset` account selector.
1066
+ */
1067
+ listResetCredits(signal?: AbortSignal): Promise<ResetCreditAccountStatus[]>;
1048
1068
  /**
1049
1069
  * Export session to HTML.
1050
1070
  * @param outputPath Optional output path (defaults to session directory)
@@ -2,5 +2,5 @@
2
2
  * Re-exports from @oh-my-pi/pi-ai.
3
3
  * All credential storage types and the AuthStorage class now live in the ai package.
4
4
  */
5
- export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialStore, AuthStorageData, AuthStorageOptions, CredentialOrigin, CredentialOriginKind, OAuthCredential, SerializedAuthStorage, SnapshotResponse, StoredAuthCredential, } from "@oh-my-pi/pi-ai";
5
+ export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialStore, AuthStorageData, AuthStorageOptions, CredentialOrigin, CredentialOriginKind, OAuthAccountIdentity, OAuthCredential, ResetCreditAccountStatus, ResetCreditRedeemOutcome, ResetCreditTarget, SerializedAuthStorage, SnapshotResponse, StoredAuthCredential, } from "@oh-my-pi/pi-ai";
6
6
  export { AuthBrokerClient, AuthStorage, DEFAULT_SNAPSHOT_CACHE_TTL_MS, REMOTE_REFRESH_SENTINEL, RemoteAuthCredentialStore, readAuthBrokerSnapshotCache, SqliteAuthCredentialStore, writeAuthBrokerSnapshotCache, } from "@oh-my-pi/pi-ai";
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Pure decision predicate for auto-redeeming a saved OpenAI Codex rate-limit
3
+ * reset, plus the process-wide coordinator that serializes attempts.
4
+ *
5
+ * WHY THIS IS REACTIVE-ONLY (never proactive):
6
+ * The only trustworthy "blocked right now" signal is a live 429 /
7
+ * `usage_limit_reached` from a request authenticated as the session's active
8
+ * Codex credential. The session hook calls this predicate from the usage-limit
9
+ * branch of the retry pipeline, *after* free remedies (sibling-account switch)
10
+ * fail and *before* model fallback. A proactive surface (the status-line usage
11
+ * poll) cannot be used: at `used_percent < 100` the account is not actually
12
+ * limited, so redeeming would be a credit-wasting no-op; at exactly 100 the
13
+ * user may be idle, so the freshly-reset weekly window would tick away with
14
+ * nobody working. Saved resets are a scarce, ~monthly, effectively
15
+ * irreversible resource — every gate here is biased to precision over recall:
16
+ * we would rather miss a redeem than waste a credit.
17
+ *
18
+ * THE DECISION-2 TRAP (status MUST NOT be used to find the blocker):
19
+ * `openai-codex.ts` applies the top-level `rate_limit.limit_reached` flag to
20
+ * BOTH the primary (5h) and secondary (weekly) `buildUsageLimit` calls, so when
21
+ * an account is blocked, *both* limit entries carry `status: "exhausted"`
22
+ * regardless of which window is actually at 100%. Only `amount.usedFraction`
23
+ * disambiguates which window is the real blocker. This module therefore keys
24
+ * eligibility off exact limit ids (`openai-codex:primary` /
25
+ * `openai-codex:secondary`) and `usedFraction`, never off `status`.
26
+ *
27
+ * ANTI-WASTE GATES (in evaluation order): the policy must be OFF unless opted
28
+ * in; the active model must be Codex (not Spark — a Spark block lives on a
29
+ * separate meter and it is unknown whether a credit even resets it); a fresh
30
+ * usage report for the active account must confirm `limitReached`; the WEEKLY
31
+ * (secondary) window must be genuinely exhausted — a 5h-only block self-heals
32
+ * within the hour, so a credit spent there buys nothing; the natural reset must be far
33
+ * enough away to justify spending a ~30-day credit yet within one plausible
34
+ * window length; a credit must be verifiably available above the reserve; and
35
+ * the same block episode must not have been attempted already (debounce +
36
+ * per-account cooldown). All of this is pure — no fetches, no IO. The only
37
+ * stateful piece is the {@link CodexAutoRedeemCoordinator} container, whose
38
+ * read-only views are passed in so the predicate itself stays deterministic.
39
+ */
40
+ import type { OAuthAccountIdentity, ResetCreditTarget, UsageReport } from "@oh-my-pi/pi-ai";
41
+ /** Weekly window counts as exhausted at `usedFraction >= 0.999` (used_percent >= 99.9). */
42
+ export declare const WEEKLY_EXHAUSTED_MIN_FRACTION = 0.999;
43
+ /** A weekly reset can never be more than one window length (7d) away; +1h slack for skew. */
44
+ export declare const MAX_PLAUSIBLE_REMAINING_MS: number;
45
+ /** Report must be no older than the 5-min usage cache TTL plus slack. */
46
+ export declare const REPORT_FRESHNESS_MS: number;
47
+ /** Per-account cooldown that catches blockKey drift across a minute boundary. */
48
+ export declare const ATTEMPT_COOLDOWN_MS = 60000;
49
+ /** Minute bucket for blockKey, absorbing `reset_after_seconds`-derived jitter. */
50
+ export declare const DEBOUNCE_BUCKET_MS = 60000;
51
+ export type CodexAutoRedeemSkipReason = "disabled" | "wrong-provider" | "spark-model" | "no-identity" | "no-report" | "stale-report" | "not-limit-reached" | "weekly-not-exhausted" | "no-reset-time" | "reset-too-soon" | "reset-implausible" | "credits-unknown" | "reserve" | "already-attempted" | "cooldown";
52
+ export interface CodexAutoRedeemInput {
53
+ nowMs: number;
54
+ /** `this.model.provider`. */
55
+ provider: string;
56
+ /** `this.model.id`. */
57
+ modelId: string;
58
+ settings: {
59
+ autoRedeem: boolean;
60
+ minBlockedMinutes: number;
61
+ keepCredits: number;
62
+ };
63
+ /** `getOAuthAccountIdentity("openai-codex", sessionId)`, captured at hook entry before any await. */
64
+ identity: OAuthAccountIdentity | undefined;
65
+ /** `session.fetchUsageReports()` (≤5-min cache). */
66
+ reports: UsageReport[] | null;
67
+ attemptedBlockKeys: ReadonlySet<string>;
68
+ lastAttemptAtByAccount: ReadonlyMap<string, number>;
69
+ }
70
+ export type CodexAutoRedeemDecision = {
71
+ redeem: true;
72
+ target: ResetCreditTarget;
73
+ accountKey: string;
74
+ blockKey: string;
75
+ weeklyResetAtMs: number;
76
+ remainingMs: number;
77
+ availableCount: number;
78
+ } | {
79
+ redeem: false;
80
+ reason: CodexAutoRedeemSkipReason;
81
+ };
82
+ /**
83
+ * Decide whether to auto-redeem a saved Codex reset for the active account.
84
+ *
85
+ * Pure: every gate below is a function of the snapshot inputs only. Order
86
+ * matters — cheapest / most-decisive gates first so the common "not eligible"
87
+ * paths short-circuit before any account/report matching.
88
+ */
89
+ export declare function evaluateCodexAutoRedeem(input: CodexAutoRedeemInput): CodexAutoRedeemDecision;
90
+ /**
91
+ * Process-wide (NOT per-session) coordinator state. Parallel subagent sessions
92
+ * share the same Codex accounts and must not race a double-spend, so this is a
93
+ * single shared container, not a per-session field.
94
+ *
95
+ * - `attemptedBlockKeys`: one attempt EVER per block episode, regardless of
96
+ * outcome — recorded before calling the consume so exceptions can't re-enter.
97
+ * - `lastAttemptAtByAccount`: per-account cooldown timestamps (epoch ms),
98
+ * catching blockKey drift across a minute boundary.
99
+ * - `inFlightByAccount`: serializes per account — a second session for the same
100
+ * account adopts the in-flight promise instead of starting a second consume.
101
+ */
102
+ export interface CodexAutoRedeemCoordinator {
103
+ attemptedBlockKeys: Set<string>;
104
+ lastAttemptAtByAccount: Map<string, number>;
105
+ inFlightByAccount: Map<string, Promise<boolean>>;
106
+ }
107
+ export declare const defaultCodexAutoRedeemCoordinator: CodexAutoRedeemCoordinator;