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

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 (121) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/cli.js +5349 -5328
  3. package/dist/types/cli/args.d.ts +1 -0
  4. package/dist/types/cli-commands.d.ts +12 -0
  5. package/dist/types/commands/launch.d.ts +4 -0
  6. package/dist/types/config/api-key-resolver.d.ts +3 -0
  7. package/dist/types/config/model-registry.d.ts +1 -0
  8. package/dist/types/config/model-resolver.d.ts +18 -0
  9. package/dist/types/config/settings-schema.d.ts +29 -1
  10. package/dist/types/config/settings.d.ts +7 -0
  11. package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
  12. package/dist/types/eval/py/executor.d.ts +5 -0
  13. package/dist/types/eval/py/kernel.d.ts +6 -1
  14. package/dist/types/eval/py/runtime.d.ts +9 -0
  15. package/dist/types/exec/bash-executor.d.ts +2 -0
  16. package/dist/types/extensibility/extensions/runner.d.ts +3 -2
  17. package/dist/types/extensibility/extensions/types.d.ts +3 -0
  18. package/dist/types/memory-backend/index.d.ts +1 -0
  19. package/dist/types/memory-backend/runtime.d.ts +4 -0
  20. package/dist/types/memory-backend/types.d.ts +66 -1
  21. package/dist/types/modes/index.d.ts +3 -3
  22. package/dist/types/modes/interactive-mode.d.ts +7 -2
  23. package/dist/types/modes/oauth-manual-input.d.ts +7 -0
  24. package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
  25. package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
  26. package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
  27. package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
  28. package/dist/types/modes/setup-wizard/index.d.ts +5 -1
  29. package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
  30. package/dist/types/modes/types.d.ts +2 -0
  31. package/dist/types/secrets/index.d.ts +1 -1
  32. package/dist/types/secrets/obfuscator.d.ts +8 -2
  33. package/dist/types/session/agent-session.d.ts +14 -2
  34. package/dist/types/session/streaming-output.d.ts +23 -0
  35. package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
  36. package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
  37. package/dist/types/slash-commands/types.d.ts +1 -1
  38. package/dist/types/system-prompt.d.ts +2 -0
  39. package/dist/types/task/executor.d.ts +1 -0
  40. package/dist/types/task/index.d.ts +2 -2
  41. package/dist/types/task/types.d.ts +8 -0
  42. package/dist/types/thinking.d.ts +4 -0
  43. package/dist/types/tiny/title-client.d.ts +11 -0
  44. package/dist/types/tiny/title-protocol.d.ts +1 -0
  45. package/dist/types/tools/index.d.ts +6 -0
  46. package/dist/types/utils/git.d.ts +15 -2
  47. package/dist/types/utils/title-generator.d.ts +3 -2
  48. package/package.json +10 -10
  49. package/src/auto-thinking/classifier.ts +1 -0
  50. package/src/cli/args.ts +3 -0
  51. package/src/cli-commands.ts +29 -0
  52. package/src/cli.ts +8 -9
  53. package/src/commands/launch.ts +4 -0
  54. package/src/commit/model-selection.ts +3 -2
  55. package/src/config/api-key-resolver.ts +8 -6
  56. package/src/config/model-registry.ts +97 -30
  57. package/src/config/model-resolver.ts +60 -0
  58. package/src/config/settings-schema.ts +43 -15
  59. package/src/config/settings.ts +61 -3
  60. package/src/edit/hashline/execute.ts +39 -2
  61. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  62. package/src/eval/completion-bridge.ts +1 -0
  63. package/src/eval/py/executor.ts +29 -7
  64. package/src/eval/py/index.ts +6 -1
  65. package/src/eval/py/kernel.ts +31 -11
  66. package/src/eval/py/runtime.ts +37 -0
  67. package/src/exec/bash-executor.ts +82 -3
  68. package/src/extensibility/extensions/get-commands-handler.ts +2 -1
  69. package/src/extensibility/extensions/runner.ts +6 -1
  70. package/src/extensibility/extensions/types.ts +3 -0
  71. package/src/hindsight/bank.ts +17 -2
  72. package/src/internal-urls/docs-index.generated.ts +3 -3
  73. package/src/main.ts +18 -6
  74. package/src/memories/index.ts +2 -0
  75. package/src/memory-backend/index.ts +1 -0
  76. package/src/memory-backend/local-backend.ts +9 -0
  77. package/src/memory-backend/off-backend.ts +9 -0
  78. package/src/memory-backend/runtime.ts +66 -0
  79. package/src/memory-backend/types.ts +81 -1
  80. package/src/mnemopi/backend.ts +151 -4
  81. package/src/modes/acp/acp-agent.ts +119 -11
  82. package/src/modes/components/assistant-message.ts +19 -21
  83. package/src/modes/components/footer.ts +3 -1
  84. package/src/modes/components/status-line/component.ts +118 -34
  85. package/src/modes/controllers/command-controller.ts +1 -1
  86. package/src/modes/controllers/input-controller.ts +1 -0
  87. package/src/modes/controllers/mcp-command-controller.ts +38 -3
  88. package/src/modes/index.ts +3 -21
  89. package/src/modes/interactive-mode.ts +39 -9
  90. package/src/modes/oauth-manual-input.ts +30 -3
  91. package/src/modes/rpc/rpc-client.ts +154 -3
  92. package/src/modes/rpc/rpc-mode.ts +97 -12
  93. package/src/modes/rpc/rpc-subagents.ts +265 -0
  94. package/src/modes/rpc/rpc-types.ts +81 -1
  95. package/src/modes/setup-wizard/index.ts +12 -2
  96. package/src/modes/setup-wizard/lazy.ts +16 -0
  97. package/src/modes/types.ts +2 -0
  98. package/src/sdk.ts +8 -1
  99. package/src/secrets/index.ts +8 -1
  100. package/src/secrets/obfuscator.ts +39 -18
  101. package/src/session/agent-session.ts +179 -54
  102. package/src/session/streaming-output.ts +166 -10
  103. package/src/slash-commands/acp-builtins.ts +24 -0
  104. package/src/slash-commands/builtin-registry.ts +20 -0
  105. package/src/slash-commands/types.ts +1 -1
  106. package/src/system-prompt.ts +14 -0
  107. package/src/task/executor.ts +13 -12
  108. package/src/task/index.ts +9 -8
  109. package/src/task/render.ts +18 -3
  110. package/src/task/types.ts +9 -0
  111. package/src/thinking.ts +7 -0
  112. package/src/tiny/title-client.ts +34 -5
  113. package/src/tiny/title-protocol.ts +1 -1
  114. package/src/tiny/worker.ts +6 -4
  115. package/src/tools/bash.ts +46 -5
  116. package/src/tools/image-gen.ts +11 -4
  117. package/src/tools/index.ts +13 -1
  118. package/src/tools/inspect-image.ts +1 -0
  119. package/src/utils/commit-message-generator.ts +1 -0
  120. package/src/utils/git.ts +267 -13
  121. package/src/utils/title-generator.ts +24 -5
@@ -8,6 +8,7 @@ export interface Args {
8
8
  allowHome?: boolean;
9
9
  provider?: string;
10
10
  model?: string;
11
+ config?: string[];
11
12
  smol?: string;
12
13
  slow?: string;
13
14
  plan?: string;
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import type { CommandEntry } from "@oh-my-pi/pi-utils/cli";
12
12
  export declare const commands: CommandEntry[];
13
+ export declare function reservedTopLevelWordMessage(first: string | undefined, argc?: number): string | undefined;
13
14
  /**
14
15
  * Return true when `first` matches a registered subcommand name or alias.
15
16
  *
@@ -17,3 +18,14 @@ export declare const commands: CommandEntry[];
17
18
  * runner skips ahead to the default `launch` command.
18
19
  */
19
20
  export declare function isSubcommand(first: string | undefined): boolean;
21
+ export type ResolvedCliArgv = {
22
+ argv: string[];
23
+ } | {
24
+ error: string;
25
+ };
26
+ /**
27
+ * Decide what the CLI runner should do with raw argv: reject bare reserved
28
+ * management words, pass help/version through untouched, and route everything
29
+ * that is not a known subcommand to `launch`.
30
+ */
31
+ export declare function resolveCliArgv(argv: string[]): ResolvedCliArgv;
@@ -47,6 +47,10 @@ export default class Index extends Command {
47
47
  description: string;
48
48
  options: string[];
49
49
  };
50
+ config: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"string"> & {
51
+ description: string;
52
+ multiple: true;
53
+ };
50
54
  print: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
51
55
  char: string;
52
56
  description: string;
@@ -4,6 +4,8 @@ export interface ApiKeyResolverOptions {
4
4
  sessionId?: string;
5
5
  /** Provider base URL hint forwarded to the auth-storage cascade. */
6
6
  baseUrl?: string;
7
+ /** Provider model id forwarded to model-scoped usage ranking/backoff. */
8
+ modelId?: string;
7
9
  }
8
10
  /**
9
11
  * Minimal slice of `ModelRegistry` the resolver needs. Typed structurally so
@@ -13,6 +15,7 @@ export interface ApiKeyResolverOptions {
13
15
  export interface ApiKeyResolverRegistry {
14
16
  getApiKeyForProvider(provider: string, sessionId?: string, options?: {
15
17
  baseUrl?: string;
18
+ modelId?: string;
16
19
  forceRefresh?: boolean;
17
20
  signal?: AbortSignal;
18
21
  }): Promise<string | undefined>;
@@ -148,6 +148,7 @@ export declare class ModelRegistry {
148
148
  */
149
149
  getApiKeyForProvider(provider: string, sessionId?: string, options?: {
150
150
  baseUrl?: string;
151
+ modelId?: string;
151
152
  forceRefresh?: boolean;
152
153
  signal?: AbortSignal;
153
154
  }): Promise<string | undefined>;
@@ -174,6 +174,24 @@ export declare function resolveModelScope(patterns: string[], modelRegistry: Pic
174
174
  * rather than falling back to the global default (see issue #1022).
175
175
  */
176
176
  export declare function resolveAllowedModels(modelRegistry: Pick<ModelRegistry, "getAvailable" | "getCanonicalVariants">, settings: Settings | undefined, preferences?: ModelMatchPreferences): Promise<Model<Api>[]>;
177
+ /**
178
+ * Synchronous subset of {@link resolveAllowedModels} for contexts where async is unavailable
179
+ * (e.g. `getAvailableModels()` which is called from the ACP model-list advertisement, RPC
180
+ * `get_available_models`, and the `/model` slash command). Uses the same effective
181
+ * `enabledModels` scope semantics as startup resolution:
182
+ *
183
+ * - Glob selectors match `provider/modelId` and bare model id
184
+ * - Exact canonical ids expand to all available concrete variants
185
+ * - Exact `provider/modelId`, bare ids, provider-scoped fuzzy, and substring selectors
186
+ * resolve through the shared model-pattern matcher
187
+ * - Optional `:thinkingLevel` suffixes are stripped only when valid
188
+ *
189
+ * When no pattern resolves to any model (misconfiguration / typo) an empty list is returned,
190
+ * consistent with the empty-list contract of {@link resolveAllowedModels}. Callers that render
191
+ * a UI picker should treat an empty list as "hide the picker entry", matching how the SDK
192
+ * surfaces the same misconfiguration during session initialization.
193
+ */
194
+ export declare function filterAvailableModelsByEnabledPatterns(available: Model<Api>[], patterns: readonly string[], registry: Pick<ModelRegistry, "getCanonicalVariants">): Model<Api>[];
177
195
  export interface ResolveCliModelResult {
178
196
  model: Model<Api> | undefined;
179
197
  selector?: string;
@@ -62,7 +62,7 @@ export type AnyUiMetadata = UiBase & {
62
62
  };
63
63
  interface BooleanDef {
64
64
  type: "boolean";
65
- default: boolean;
65
+ default: boolean | undefined;
66
66
  ui?: UiBoolean;
67
67
  }
68
68
  interface StringDef {
@@ -2392,6 +2392,20 @@ export declare const SETTINGS_SCHEMA: {
2392
2392
  readonly type: "number";
2393
2393
  readonly default: number;
2394
2394
  };
2395
+ readonly "shellMinimizer.sourceOutlineLevel": {
2396
+ readonly type: "enum";
2397
+ readonly values: readonly ["default", "aggressive"];
2398
+ readonly default: "default";
2399
+ readonly ui: {
2400
+ readonly tab: "editing";
2401
+ readonly label: "Shell Minimizer Source Outline";
2402
+ readonly description: "Source outline mode for cat/read of source files: default or aggressive";
2403
+ };
2404
+ };
2405
+ readonly "shellMinimizer.legacyFilters": {
2406
+ readonly type: "boolean";
2407
+ readonly default: undefined;
2408
+ };
2395
2409
  readonly "eval.py": {
2396
2410
  readonly type: "boolean";
2397
2411
  readonly default: true;
@@ -2420,6 +2434,15 @@ export declare const SETTINGS_SCHEMA: {
2420
2434
  readonly description: "Whether to keep IPython kernel alive across calls";
2421
2435
  };
2422
2436
  };
2437
+ readonly "python.interpreter": {
2438
+ readonly type: "string";
2439
+ readonly default: "";
2440
+ readonly ui: {
2441
+ readonly tab: "editing";
2442
+ readonly label: "Python Interpreter";
2443
+ readonly description: "Optional path to an exact Python executable. When set, automatic Python runtime discovery is skipped.";
2444
+ };
2445
+ };
2423
2446
  readonly "tools.approval": {
2424
2447
  readonly type: "record";
2425
2448
  readonly default: {};
@@ -3896,6 +3919,9 @@ export type SettingPath = keyof Schema;
3896
3919
  /** Infer the value type for a setting path */
3897
3920
  export type SettingValue<P extends SettingPath> = Schema[P] extends {
3898
3921
  type: "boolean";
3922
+ default: undefined;
3923
+ } ? boolean | undefined : Schema[P] extends {
3924
+ type: "boolean";
3899
3925
  } ? boolean : Schema[P] extends {
3900
3926
  type: "string";
3901
3927
  } ? string | undefined : Schema[P] extends {
@@ -4051,6 +4077,8 @@ export interface ShellMinimizerSettings {
4051
4077
  only: string[];
4052
4078
  except: string[];
4053
4079
  maxCaptureBytes: number;
4080
+ sourceOutlineLevel: "default" | "aggressive";
4081
+ legacyFilters: boolean | undefined;
4054
4082
  }
4055
4083
  /** Map group prefix -> typed settings interface */
4056
4084
  export interface GroupTypeMap {
@@ -30,6 +30,8 @@ export interface SettingsOptions {
30
30
  inMemory?: boolean;
31
31
  /** Initial overrides */
32
32
  overrides?: Partial<Record<SettingPath, unknown>>;
33
+ /** Extra config.yml-style overlays loaded after global/project settings */
34
+ configFiles?: string[];
33
35
  }
34
36
  export declare class Settings {
35
37
  #private;
@@ -54,6 +56,11 @@ export declare class Settings {
54
56
  * Returns the merged value from global + project + overrides, or the default.
55
57
  */
56
58
  get<P extends SettingPath>(path: P): SettingValue<P>;
59
+ /**
60
+ * Whether `path` has an explicitly configured value (global config, project
61
+ * config, or runtime override) rather than falling back to the schema default.
62
+ */
63
+ isConfigured(path: SettingPath): boolean;
57
64
  /**
58
65
  * Set a setting value (sync).
59
66
  * Updates global settings and queues a background save.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Per-session guard against subagents looping on byte-identical no-op edits.
3
+ *
4
+ * A hashline patch can apply cleanly yet produce no change when the body rows
5
+ * are already byte-identical to the targeted lines. {@link executeHashlineSingle}
6
+ * surfaces a soft hint ("re-read the file before issuing another edit"), but in
7
+ * the wild some models ignore the hint and keep re-issuing the same bytes
8
+ * (issue #2081 captured 182 such repeats in 205 calls before the user aborted).
9
+ *
10
+ * This module tracks consecutive byte-identical no-op edits per canonical file
11
+ * path within a single session. Once the same payload no-ops {@link NOOP_HARD_LIMIT}
12
+ * times in a row the caller is expected to escalate from a soft text result to
13
+ * a thrown {@link ToolError} so the agent loop sees a tool *failure* — empirically
14
+ * far more effective at breaking the cycle than the soft hint alone.
15
+ *
16
+ * A successful (non-noop) commit for a path resets that path's counter; a
17
+ * different payload on the same path also resets it because the body hash
18
+ * changed, which is a sign of model progress and deserves another soft hint.
19
+ */
20
+ interface NoopLoopEntry {
21
+ /** Hash of the most recent input that no-op'd on this canonical path. */
22
+ hash: string;
23
+ /** Consecutive no-op count for the same `hash` on this path. */
24
+ count: number;
25
+ }
26
+ /** Cross-session-safe state slot held on the `ToolSession`. */
27
+ export interface NoopLoopGuard {
28
+ entries: Map<string, NoopLoopEntry>;
29
+ }
30
+ /**
31
+ * After this many consecutive byte-identical no-op edits on the same path,
32
+ * {@link recordNoopEdit} returns `escalate: true`. Picked deliberately small
33
+ * so the soft hint still fires once or twice before we escalate — the model
34
+ * deserves a chance to recover, but a tight bound is what actually breaks
35
+ * loops in practice.
36
+ */
37
+ export declare const NOOP_HARD_LIMIT = 3;
38
+ interface NoopLoopGuardOwner {
39
+ noopLoopGuard?: NoopLoopGuard;
40
+ }
41
+ /** Lazily create the per-session guard, mirroring `getFileSnapshotStore`. */
42
+ export declare function getNoopLoopGuard(session: NoopLoopGuardOwner): NoopLoopGuard;
43
+ /** Result of recording one no-op against the guard. */
44
+ export interface NoopRecordResult {
45
+ /** Consecutive identical no-op count, including the current one. */
46
+ count: number;
47
+ /** True once `count >= NOOP_HARD_LIMIT` and the caller MUST escalate. */
48
+ escalate: boolean;
49
+ }
50
+ /**
51
+ * Record a no-op edit for `canonicalPath` keyed by `inputHash` (a stable hash
52
+ * of the raw patch input bytes). Returns the running consecutive-no-op count
53
+ * and whether the caller should escalate from a soft text result to a thrown
54
+ * error.
55
+ *
56
+ * `inputHash` is intentionally derived from the raw model-authored bytes
57
+ * rather than from file content: when the model emits a different payload
58
+ * (even whitespace-only) that's progress and earns a fresh soft hint, but
59
+ * re-issuing the same bytes after being warned is what we want to break.
60
+ */
61
+ export declare function recordNoopEdit(session: NoopLoopGuardOwner, canonicalPath: string, inputHash: string): NoopRecordResult;
62
+ /**
63
+ * Clear the no-op counter for `canonicalPath`. Call after a non-noop commit
64
+ * for the same path so a future no-op starts fresh from the soft hint.
65
+ */
66
+ export declare function resetNoopEdit(session: NoopLoopGuardOwner, canonicalPath: string): void;
67
+ /**
68
+ * Stable hash of the raw patch input. Bun's `Bun.hash` is xxHash64 — fast,
69
+ * non-cryptographic, more than adequate for "is this the same payload?".
70
+ */
71
+ export declare function hashPatchInput(input: string): string;
72
+ export {};
@@ -25,6 +25,11 @@ export interface PythonExecutorOptions {
25
25
  kernelOwnerId?: string;
26
26
  /** Kernel mode (session reuse vs per-call) */
27
27
  kernelMode?: PythonKernelMode;
28
+ /**
29
+ * Explicit interpreter path (`python.interpreter` resolved from the
30
+ * session's settings). Skips automatic runtime discovery when set.
31
+ */
32
+ interpreter?: string;
28
33
  /** Restart the kernel before executing */
29
34
  reset?: boolean;
30
35
  /** Session file path for accessing task outputs */
@@ -45,6 +45,11 @@ interface KernelLifecycleOptions {
45
45
  interface KernelStartOptions extends KernelLifecycleOptions {
46
46
  cwd: string;
47
47
  env?: Record<string, string | undefined>;
48
+ /**
49
+ * Explicit interpreter path (`python.interpreter` from the session's
50
+ * settings). When set, runtime discovery is skipped entirely.
51
+ */
52
+ interpreter?: string;
48
53
  }
49
54
  interface KernelShutdownOptions {
50
55
  signal?: AbortSignal;
@@ -57,7 +62,7 @@ export interface PythonKernelAvailability {
57
62
  /** The probed-working runtime, when one was found. */
58
63
  runtime?: PythonRuntime;
59
64
  }
60
- export declare function checkPythonKernelAvailability(cwd: string): Promise<PythonKernelAvailability>;
65
+ export declare function checkPythonKernelAvailability(cwd: string, interpreter?: string): Promise<PythonKernelAvailability>;
61
66
  export declare class PythonKernel {
62
67
  #private;
63
68
  readonly id: string;
@@ -15,6 +15,15 @@ export declare function filterEnv(env: Record<string, string | undefined>): Reco
15
15
  * Detect virtual environment path from VIRTUAL_ENV or common locations.
16
16
  */
17
17
  export declare function resolveVenvPath(cwd: string): string | undefined;
18
+ /**
19
+ * Resolve an explicitly configured interpreter (`python.interpreter`) into a
20
+ * runtime, bypassing discovery. Does not probe or validate the executable —
21
+ * callers must check it actually runs. `~` expands to the home directory and
22
+ * relative paths resolve against `cwd`. When the interpreter sits inside a
23
+ * virtualenv (a `pyvenv.cfg` above its bin dir), the venv activation env is
24
+ * applied so subprocesses and `pip` resolve consistently.
25
+ */
26
+ export declare function resolveExplicitPythonRuntime(interpreter: string, cwd: string, baseEnv: Record<string, string | undefined>): PythonRuntime;
18
27
  /**
19
28
  * Enumerate candidate Python runtimes in priority order: an active/project venv,
20
29
  * the managed `~/.omp/python-env`, then the system interpreter on PATH. Every
@@ -10,6 +10,8 @@ export interface BashExecutorOptions {
10
10
  sessionKey?: string;
11
11
  /** Additional environment variables to inject */
12
12
  env?: Record<string, string>;
13
+ /** Run through the configured user shell instead of brush parsing directly. */
14
+ useUserShell?: boolean;
13
15
  /** Artifact path/id for full output storage */
14
16
  artifactPath?: string;
15
17
  artifactId?: string;
@@ -5,6 +5,7 @@ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
5
5
  import type { CredentialDisabledEvent, ImageContent, Model, ProviderResponseMetadata } from "@oh-my-pi/pi-ai";
6
6
  import type { KeyId } from "@oh-my-pi/pi-tui";
7
7
  import type { ModelRegistry } from "../../config/model-registry";
8
+ import type { MemoryRuntimeContext } from "../../memory-backend";
8
9
  import type { SessionManager } from "../../session/session-manager";
9
10
  import type { AfterProviderResponseEvent, AssistantThinkingRenderer, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, ContextEvent, Extension, ExtensionActions, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFlag, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, InputEvent, InputEventResult, MessageRenderer, RegisteredCommand, RegisteredTool, ResourcesDiscoverEvent, SessionBeforeBranchResult, SessionBeforeCompactResult, SessionBeforeSwitchResult, SessionBeforeTreeResult, SessionCompactingResult, ToolCallEvent, ToolCallEventResult, ToolResultEvent, ToolResultEventResult, UserBashEvent, UserBashEventResult, UserPythonEvent, UserPythonEventResult } from "./types";
10
11
  /** Combined result from all before_agent_start handlers */
@@ -61,7 +62,7 @@ export declare class ExtensionRunner {
61
62
  private readonly cwd;
62
63
  private readonly sessionManager;
63
64
  private readonly modelRegistry;
64
- constructor(extensions: Extension[], runtime: ExtensionRuntime, cwd: string, sessionManager: SessionManager, modelRegistry: ModelRegistry);
65
+ constructor(extensions: Extension[], runtime: ExtensionRuntime, cwd: string, sessionManager: SessionManager, modelRegistry: ModelRegistry, getMemory?: () => MemoryRuntimeContext | undefined);
65
66
  initialize(actions: ExtensionActions, contextActions: ExtensionContextActions, commandContextActions?: ExtensionCommandContextActions, uiContext?: ExtensionUIContext): void;
66
67
  /**
67
68
  * Forward a `credential_disabled` event from `AuthStorage` to extension handlers.
@@ -99,7 +100,7 @@ export declare class ExtensionRunner {
99
100
  hasHandlers(eventType: string): boolean;
100
101
  getMessageRenderer(customType: string): MessageRenderer | undefined;
101
102
  getAssistantThinkingRenderers(): AssistantThinkingRenderer[];
102
- getRegisteredCommands(reserved?: Set<string>): RegisteredCommand[];
103
+ getRegisteredCommands(reserved?: ReadonlySet<string>): RegisteredCommand[];
103
104
  getCommandDiagnostics(): Array<{
104
105
  type: string;
105
106
  message: string;
@@ -21,6 +21,7 @@ import type { EditToolDetails } from "../../edit";
21
21
  import type { PythonResult } from "../../eval/py/executor";
22
22
  import type { BashResult } from "../../exec/bash-executor";
23
23
  import type { ExecOptions, ExecResult } from "../../exec/exec";
24
+ import type { MemoryRuntimeContext } from "../../memory-backend";
24
25
  import type { CustomEditor } from "../../modes/components/custom-editor";
25
26
  import type { Theme } from "../../modes/theme/theme";
26
27
  import type { CustomMessage } from "../../session/messages";
@@ -199,6 +200,8 @@ export interface ExtensionContext {
199
200
  shutdown(): void;
200
201
  /** Get the current effective system prompt. */
201
202
  getSystemPrompt(): string[];
203
+ /** Structured memory runtime for status/search/save across the configured backend. */
204
+ memory?: MemoryRuntimeContext;
202
205
  }
203
206
  /**
204
207
  * Extended context for command handlers.
@@ -3,4 +3,5 @@ export type { MnemopiMemoryEditOperation, MnemopiMemoryEditOptions, MnemopiMemor
3
3
  export * from "./local-backend";
4
4
  export * from "./off-backend";
5
5
  export * from "./resolve";
6
+ export * from "./runtime";
6
7
  export * from "./types";
@@ -0,0 +1,4 @@
1
+ import type { AgentSession } from "../session/agent-session";
2
+ import type { MemoryBackendOperationContext, MemoryRuntimeContext } from "./types";
3
+ export declare function createMemoryRuntimeContext(context: MemoryBackendOperationContext): MemoryRuntimeContext;
4
+ export declare function createSessionMemoryRuntimeContext(session: AgentSession, agentDir: string, cwd: string): MemoryRuntimeContext;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Memory backend abstraction.
3
3
  *
4
- * Backends are mutually exclusive — `await resolveMemoryBackend(settings)` resolves
4
+ * Backends are mutually exclusive — `await resolveMemoryBackend(settings)` returns
5
5
  * exactly one. Implementations MUST be self-contained: they own the per-session
6
6
  * state they create in `start()` and tear it down on `clear()`.
7
7
  */
@@ -12,6 +12,65 @@ import type { HindsightSessionState } from "../hindsight/state";
12
12
  import type { MnemopiSessionState } from "../mnemopi/state";
13
13
  import type { AgentSession } from "../session/agent-session";
14
14
  export type MemoryBackendId = "off" | "local" | "hindsight" | "mnemopi";
15
+ export interface MemoryBackendStatus {
16
+ backend: MemoryBackendId;
17
+ active: boolean;
18
+ writable: boolean;
19
+ searchable: boolean;
20
+ scope?: string;
21
+ retainBank?: string;
22
+ recallBanks?: string[];
23
+ workingCount?: number;
24
+ episodicCount?: number;
25
+ tripleCount?: number;
26
+ lastMemory?: string;
27
+ lastRecall?: boolean;
28
+ database?: string;
29
+ message?: string;
30
+ error?: string;
31
+ }
32
+ export interface MemoryBackendSearchOptions {
33
+ limit?: number;
34
+ /** Best-effort abort signal. Backends may only observe it before/after an underlying recall call. */
35
+ signal?: AbortSignal;
36
+ }
37
+ export interface MemoryBackendSearchItem {
38
+ id?: string;
39
+ content: string;
40
+ source?: string;
41
+ timestamp?: string;
42
+ score?: number;
43
+ }
44
+ export interface MemoryBackendSearchResult {
45
+ backend: MemoryBackendId;
46
+ query: string;
47
+ count: number;
48
+ items: MemoryBackendSearchItem[];
49
+ message?: string;
50
+ }
51
+ export interface MemoryBackendSaveInput {
52
+ content: string;
53
+ context?: string;
54
+ source?: string;
55
+ importance?: number;
56
+ }
57
+ export interface MemoryBackendSaveResult {
58
+ backend: MemoryBackendId;
59
+ stored: number;
60
+ ids?: string[];
61
+ queued?: boolean;
62
+ message?: string;
63
+ }
64
+ export interface MemoryBackendOperationContext {
65
+ agentDir: string;
66
+ cwd: string;
67
+ session?: AgentSession;
68
+ }
69
+ export interface MemoryRuntimeContext {
70
+ status(): Promise<MemoryBackendStatus>;
71
+ search(query: string, options?: MemoryBackendSearchOptions): Promise<MemoryBackendSearchResult>;
72
+ save(input: string | MemoryBackendSaveInput): Promise<MemoryBackendSaveResult>;
73
+ }
15
74
  export interface MemoryBackendStartOptions {
16
75
  session: AgentSession;
17
76
  settings: Settings;
@@ -40,6 +99,12 @@ export interface MemoryBackend {
40
99
  clear(agentDir: string, cwd: string, session?: AgentSession): Promise<void>;
41
100
  /** Force consolidation/retain to happen now (slash `/memory enqueue`). */
42
101
  enqueue(agentDir: string, cwd: string, session?: AgentSession): Promise<void>;
102
+ /** Structured state for UI, slash commands, and extensions. */
103
+ status?(context: MemoryBackendOperationContext): Promise<MemoryBackendStatus>;
104
+ /** Explicit user-facing semantic/lexical search. */
105
+ search?(context: MemoryBackendOperationContext, query: string, options?: MemoryBackendSearchOptions): Promise<MemoryBackendSearchResult>;
106
+ /** Explicit user-facing save operation. */
107
+ save?(context: MemoryBackendOperationContext, input: MemoryBackendSaveInput): Promise<MemoryBackendSaveResult>;
43
108
  /** Render backend-specific memory statistics as markdown (`/memory stats`). */
44
109
  stats?(agentDir: string, cwd: string, session?: AgentSession): Promise<string | undefined>;
45
110
  /** Render backend-specific memory diagnostics as markdown (`/memory diagnose`). */
@@ -5,6 +5,6 @@
5
5
  * barrel does not pull print, RPC server, or ACP server mode into the normal
6
6
  * TUI graph.
7
7
  */
8
- export { InteractiveMode, type InteractiveModeOptions } from "./interactive-mode";
9
- export { defineRpcClientTool, type ModelInfo, RpcClient, type RpcClientCustomTool, type RpcClientOptions, type RpcClientToolContext, type RpcClientToolResult, type RpcEventListener, } from "./rpc/rpc-client";
10
- export type { RpcCommand, RpcHostToolCallRequest, RpcHostToolCancelRequest, RpcHostToolDefinition, RpcHostToolResult, RpcHostToolUpdate, RpcResponse, RpcSessionState, } from "./rpc/rpc-types";
8
+ export * from "./interactive-mode";
9
+ export * from "./rpc/rpc-client";
10
+ export * from "./rpc/rpc-types";
@@ -7,6 +7,7 @@ import { KeybindingsManager } from "../config/keybindings";
7
7
  import { Settings } from "../config/settings";
8
8
  import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectItem, ExtensionWidgetContent, ExtensionWidgetOptions } from "../extensibility/extensions";
9
9
  import type { CompactOptions } from "../extensibility/extensions/types";
10
+ import type { MCPManager } from "../mcp";
10
11
  import { type PlanApprovalDetails } from "../plan-mode/approved-plan";
11
12
  import type { AgentSession } from "../session/agent-session";
12
13
  import { HistoryStorage } from "../session/history-storage";
@@ -49,6 +50,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
49
50
  keybindings: KeybindingsManager;
50
51
  agent: Agent;
51
52
  historyStorage?: HistoryStorage;
53
+ titleSystemPrompt?: string;
52
54
  ui: TUI;
53
55
  chatContainer: TranscriptContainer;
54
56
  pendingMessagesContainer: Container;
@@ -108,10 +110,12 @@ export declare class InteractiveMode implements InteractiveModeContext {
108
110
  skillCommands: Map<string, string>;
109
111
  oauthManualInput: OAuthManualInputManager;
110
112
  readonly lspServers: LspStartupServerInfo[] | undefined;
111
- mcpManager?: import("../mcp").MCPManager;
112
- constructor(session: AgentSession, version: string, changelogMarkdown?: string | undefined, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, lspServers?: LspStartupServerInfo[] | undefined, mcpManager?: import("../mcp").MCPManager, eventBus?: EventBus);
113
+ mcpManager?: MCPManager;
114
+ constructor(session: AgentSession, version: string, changelogMarkdown?: string | undefined, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, lspServers?: LspStartupServerInfo[] | undefined, mcpManager?: MCPManager, eventBus?: EventBus, titleSystemPrompt?: string);
113
115
  playWelcomeIntro(): void;
114
116
  init(options?: InteractiveModeInitOptions): Promise<void>;
117
+ /** Reload the title-generation system prompt override for the provided working directory. */
118
+ refreshTitleSystemPrompt(cwd?: string): Promise<void>;
115
119
  /** Reload slash commands and autocomplete for the provided working directory. */
116
120
  refreshSlashCommandState(cwd?: string): Promise<void>;
117
121
  /**
@@ -260,6 +264,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
260
264
  handleResumeSession(sessionPath: string): Promise<void>;
261
265
  handleSessionDeleteCommand(): Promise<void>;
262
266
  showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
267
+ showProviderSetup(): Promise<void>;
263
268
  showHookConfirm(title: string, message: string): Promise<boolean>;
264
269
  handleCtrlC(): void;
265
270
  handleCtrlD(): void;
@@ -1,8 +1,15 @@
1
+ type ClaimedInput = {
2
+ promise: Promise<string>;
3
+ clear: (reason?: string) => void;
4
+ };
1
5
  export declare class OAuthManualInputManager {
2
6
  #private;
3
7
  waitForInput(providerId: string): Promise<string>;
8
+ tryWaitForInput(providerId: string): Promise<string> | undefined;
9
+ tryClaimInput(providerId: string): ClaimedInput | undefined;
4
10
  submit(input: string): boolean;
5
11
  clear(reason?: string): void;
6
12
  hasPending(): boolean;
7
13
  get pendingProviderId(): string | undefined;
8
14
  }
15
+ export {};
@@ -7,8 +7,8 @@ import type { AgentEvent, AgentMessage, AgentToolResult, ThinkingLevel } from "@
7
7
  import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
8
8
  import type { ImageContent, Model } from "@oh-my-pi/pi-ai";
9
9
  import type { BashResult } from "../../exec/bash-executor";
10
- import type { SessionStats } from "../../session/agent-session";
11
- import type { RpcHandoffResult, RpcHostToolDefinition, RpcSessionState } from "./rpc-types";
10
+ import type { AgentSessionEvent, SessionStats } from "../../session/agent-session";
11
+ import type { RpcHandoffResult, RpcHostToolDefinition, RpcSessionState, RpcSubagentEventFrame, RpcSubagentLifecycleFrame, RpcSubagentMessagesResult, RpcSubagentProgressFrame, RpcSubagentSnapshot, RpcSubagentSubscriptionLevel } from "./rpc-types";
12
12
  export interface RpcClientOptions {
13
13
  /** Path to the CLI entry point (default: searches for dist/cli.js) */
14
14
  cliPath?: string;
@@ -29,6 +29,10 @@ export interface RpcClientOptions {
29
29
  }
30
30
  export type ModelInfo = Pick<Model, "provider" | "id" | "contextWindow" | "reasoning" | "thinking">;
31
31
  export type RpcEventListener = (event: AgentEvent) => void;
32
+ export type RpcSessionEventListener = (event: AgentSessionEvent) => void;
33
+ export type RpcSubagentLifecycleListener = (payload: RpcSubagentLifecycleFrame["payload"]) => void;
34
+ export type RpcSubagentProgressListener = (payload: RpcSubagentProgressFrame["payload"]) => void;
35
+ export type RpcSubagentEventListener = (payload: RpcSubagentEventFrame["payload"]) => void;
32
36
  export interface RpcClientToolContext<TDetails = unknown> {
33
37
  toolCallId: string;
34
38
  signal: AbortSignal;
@@ -60,6 +64,22 @@ export declare class RpcClient {
60
64
  * Subscribe to agent events.
61
65
  */
62
66
  onEvent(listener: RpcEventListener): () => void;
67
+ /**
68
+ * Subscribe to all top-level session events, including non-core session state events.
69
+ */
70
+ onSessionEvent(listener: RpcSessionEventListener): () => void;
71
+ /**
72
+ * Subscribe to subagent lifecycle frames after setSubagentSubscription("progress" | "events").
73
+ */
74
+ onSubagentLifecycle(listener: RpcSubagentLifecycleListener): () => void;
75
+ /**
76
+ * Subscribe to aggregated subagent progress frames after setSubagentSubscription("progress" | "events").
77
+ */
78
+ onSubagentProgress(listener: RpcSubagentProgressListener): () => void;
79
+ /**
80
+ * Subscribe to raw subagent session events. Call setSubagentSubscription(\"events\") to enable them server-side.
81
+ */
82
+ onSubagentEvent(listener: RpcSubagentEventListener): () => void;
63
83
  /**
64
84
  * Get collected stderr output (useful for debugging).
65
85
  */
@@ -98,6 +118,23 @@ export declare class RpcClient {
98
118
  * Get current session state.
99
119
  */
100
120
  getState(): Promise<RpcSessionState>;
121
+ /**
122
+ * Configure subagent frames emitted by the RPC server. Servers default to "off".
123
+ * "progress" emits lifecycle/progress frames; "events" additionally emits raw subagent session events.
124
+ */
125
+ setSubagentSubscription(level: RpcSubagentSubscriptionLevel): Promise<RpcSubagentSubscriptionLevel>;
126
+ /**
127
+ * Return the RPC server's current subagent snapshot.
128
+ */
129
+ getSubagents(): Promise<RpcSubagentSnapshot[]>;
130
+ /**
131
+ * Read persisted transcript entries for a tracked subagent session.
132
+ */
133
+ getSubagentMessages(selector: {
134
+ subagentId?: string;
135
+ sessionFile?: string;
136
+ fromByte?: number;
137
+ }): Promise<RpcSubagentMessagesResult>;
101
138
  /**
102
139
  * Set model by provider and ID.
103
140
  */
@@ -1,12 +1,41 @@
1
1
  import { type ExtensionUIContext, type ExtensionUIDialogOptions } from "../../extensibility/extensions";
2
2
  import type { AgentSession } from "../../session/agent-session";
3
- import type { RpcExtensionUIRequest, RpcExtensionUIResponse, RpcHostToolCallRequest, RpcHostToolCancelRequest, RpcHostUriCancelRequest, RpcHostUriRequest, RpcResponse } from "./rpc-types";
3
+ import type { EventBus } from "../../utils/event-bus";
4
+ import { RpcSubagentRegistry } from "./rpc-subagents";
5
+ import type { RpcCommand, RpcExtensionUIRequest, RpcExtensionUIResponse, RpcHostToolCallRequest, RpcHostToolCancelRequest, RpcHostUriCancelRequest, RpcHostUriRequest, RpcResponse } from "./rpc-types";
4
6
  export type * from "./rpc-types";
5
7
  export type PendingExtensionRequest = {
6
8
  resolve: (response: RpcExtensionUIResponse) => void;
7
9
  reject: (error: Error) => void;
8
10
  };
9
11
  type RpcOutput = (obj: RpcResponse | RpcExtensionUIRequest | RpcHostToolCallRequest | RpcHostToolCancelRequest | RpcHostUriRequest | RpcHostUriCancelRequest | object) => void;
12
+ export type RpcSessionChangeCommand = Extract<RpcCommand, {
13
+ type: "new_session";
14
+ } | {
15
+ type: "switch_session";
16
+ } | {
17
+ type: "branch";
18
+ }>;
19
+ export type RpcSessionChangeResult = {
20
+ type: "new_session";
21
+ data: {
22
+ cancelled: boolean;
23
+ };
24
+ } | {
25
+ type: "switch_session";
26
+ data: {
27
+ cancelled: boolean;
28
+ };
29
+ } | {
30
+ type: "branch";
31
+ data: {
32
+ text: string;
33
+ cancelled: boolean;
34
+ };
35
+ };
36
+ export type RpcSessionChangeSession = Pick<AgentSession, "newSession" | "switchSession" | "branch">;
37
+ export type RpcSubagentResetRegistry = Pick<RpcSubagentRegistry, "clear">;
38
+ export declare function handleRpcSessionChange(session: RpcSessionChangeSession, command: RpcSessionChangeCommand, subagentRegistry?: RpcSubagentResetRegistry): Promise<RpcSessionChangeResult>;
10
39
  export declare function requestRpcEditor(pendingRequests: Map<string, PendingExtensionRequest>, output: RpcOutput, title: string, prefill?: string, dialogOptions?: ExtensionUIDialogOptions, editorOptions?: {
11
40
  promptStyle?: boolean;
12
41
  }): Promise<string | undefined>;
@@ -14,4 +43,4 @@ export declare function requestRpcEditor(pendingRequests: Map<string, PendingExt
14
43
  * Run in RPC mode.
15
44
  * Listens for JSON commands on stdin, outputs events and responses on stdout.
16
45
  */
17
- export declare function runRpcMode(session: AgentSession, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void): Promise<never>;
46
+ export declare function runRpcMode(session: AgentSession, setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void, eventBus?: EventBus): Promise<never>;