@oh-my-pi/pi-coding-agent 15.9.1 → 15.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -1
- package/dist/types/cli/dry-balance-cli.d.ts +104 -0
- package/dist/types/commands/dry-balance.d.ts +31 -0
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/models-config-schema.d.ts +3 -0
- package/dist/types/config/settings.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +1 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -3
- package/dist/types/hindsight/bank.d.ts +17 -9
- package/dist/types/hindsight/mental-models.d.ts +1 -1
- package/dist/types/hindsight/state.d.ts +9 -3
- package/dist/types/mcp/manager.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +3 -2
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/auth-storage.d.ts +2 -2
- package/dist/types/task/types.d.ts +2 -0
- package/dist/types/tools/index.d.ts +16 -0
- package/dist/types/tools/path-utils.d.ts +11 -0
- package/package.json +9 -9
- package/src/cli/dry-balance-cli.ts +823 -0
- package/src/cli-commands.ts +1 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/config/model-registry.ts +6 -0
- package/src/config/models-config-schema.ts +2 -0
- package/src/config/settings.ts +38 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +1 -0
- package/src/discovery/github.ts +37 -1
- package/src/discovery/helpers.ts +3 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +245 -25
- package/src/hindsight/backend.ts +184 -35
- package/src/hindsight/bank.ts +32 -22
- package/src/hindsight/mental-models.ts +1 -1
- package/src/hindsight/state.ts +21 -7
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/internal-urls/omp-protocol.ts +8 -2
- package/src/mcp/manager.ts +40 -21
- package/src/modes/components/transcript-container.ts +14 -3
- package/src/modes/components/tree-selector.ts +29 -2
- package/src/modes/controllers/input-controller.ts +8 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +27 -7
- package/src/prompts/agents/explore.md +1 -0
- package/src/prompts/agents/librarian.md +1 -0
- package/src/prompts/dry-balance-bench.md +8 -0
- package/src/sdk.ts +82 -9
- package/src/session/agent-session.ts +66 -7
- package/src/session/auth-storage.ts +4 -0
- package/src/task/executor.ts +6 -2
- package/src/task/index.ts +8 -7
- package/src/task/types.ts +2 -0
- package/src/tools/bash.ts +3 -4
- package/src/tools/index.ts +16 -0
- package/src/tools/job.ts +3 -3
- package/src/tools/memory-reflect.ts +2 -2
- package/src/tools/path-utils.ts +21 -0
- package/src/tools/search.ts +18 -1
- package/src/utils/file-mentions.ts +7 -107
- package/src/utils/title-generator.ts +58 -37
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.9.3] - 2026-06-05
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed `@`-mention auto-read injecting an unrelated, same-named file when a mention did not point at a real path — e.g. an npm scope like `@scope/`, a partial path, or a bare token. `generateFileMentionMessages` resolution previously fell back to prefix and repo-wide fuzzy matching (globbing the whole project on every such mention) and auto-read the single "best" guess. Resolution is now exact-only: a mention is auto-read only when it resolves to an existing file or directory; otherwise it is left as prose. The TUI `@`-selector already inserts the real, complete path before send, so post-send guessing was both unnecessary and the source of the wrong-file reads. Directories still resolve and are listed. Removes the per-mention `**/*` project scan.
|
|
10
|
+
|
|
11
|
+
## [15.9.2] - 2026-06-05
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added an encrypted local auth-broker snapshot cache for `discoverAuthStorage`, with `OMP_AUTH_BROKER_SNAPSHOT_TTL_MS` and `OMP_AUTH_BROKER_SNAPSHOT_CACHE`, so fresh cached broker credentials can boot without a blocking `/v1/snapshot` fetch and survive broker-down startup windows.
|
|
16
|
+
- Added `dry-balance` CLI command to perform a dry-run OAuth account balancing check across configurable random session IDs, with sample and concurrency options, JSON output, and success/failure summary reporting
|
|
17
|
+
- Added `--json` output mode and machine-readable result format to `omp dry-balance` for automated use
|
|
18
|
+
- Added `omitMaxOutputTokens` to `models.yml` model definitions and `modelOverrides`, so users can opt a model out of the on-the-wire `max_output_tokens` / `max_tokens` cap while keeping the catalog `maxTokens` for local budgeting. Intended for Ollama-style proxies whose upstream output limit OMP cannot discover. ([#1881](https://github.com/can1357/oh-my-pi/issues/1881))
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Fixed TTSR rule-violation injections leaking the absolute home directory to the model: the `ttsr-interrupt` / `ttsr-tool-reminder` blocks rendered the matched rule's `path` as its absolute on-disk path (e.g. `/Users/me/Projects/app/.omp/rules/no-any.md`). The path is now relativized to the session cwd when the rule lives in the project (`.omp/rules/no-any.md`), or `~`-relative when it lives under home, so no absolute path is fed into the agent's context outside the system prompt.
|
|
23
|
+
- Fixed `AsyncJobManager.instance()` being cleared while the owning top-level session was still live, which broke the `task` async path with "Async execution is enabled but no async job manager is available" until process restart. Any in-process secondary top-level `createAgentSession()` call (e.g. the Agent Control Center's create flow in `agent-dashboard.ts`) constructed a fresh `AsyncJobManager`, overwrote the singleton, and then cleared it on its own dispose. Secondary sessions now leave the live singleton untouched, and their dispose-time cleanup is scoped so it can no longer cancel the primary session's running bash/task jobs. `bash` / `task` / `job` tools and session job snapshots now resolve the manager through session-scoped async manager wiring rather than `AsyncJobManager.instance()`, so a secondary in-process top-level session cannot accidentally register background work on the owning session's manager or report the owning session's jobs; subagents still inherit the parent's manager via their scoped async manager. Startup failures after a top-level session installs its manager now clear and dispose that manager before the next session decides whether it can create its own ([#1923](https://github.com/can1357/oh-my-pi/issues/1923)).
|
|
24
|
+
- Fixed the `task` tool returning a hard `Async execution is enabled but no async job manager is available.` error when `async.enabled` was true but `AsyncJobManager.instance()` returned `undefined`, leaving `task` non-functional for the rest of the session. The tool now falls back to the existing synchronous execution path (which still runs subagents concurrently via `mapWithConcurrencyLimit`), and logs a warning so the missing-manager state stays diagnosable ([#1922](https://github.com/can1357/oh-my-pi/issues/1922)).
|
|
25
|
+
- Fixed Hindsight retain/recall/reflect calls staying pinned to the bank that was selected when the session started after the operator edited `hindsight.bankId`, `hindsight.bankIdPrefix`, or `hindsight.scoping` mid-session. The backend now subscribes to those settings via `onHindsightScopeChanged` and rebuilds the active `HindsightSessionState` against the recomputed scope, disposing the old state after flushing its queue so in-flight tool-initiated retains still land in the bank they were enqueued for. Also renamed `ensureBankMission` to `ensureBankExists` so a blank `bankMission` no longer skips bank creation entirely, and called it before mental-model bootstrap so `createMentalModel` is never the first POST against a missing bank. `AgentSession.dispose` now flushes the retain queue before clearing `#hindsightSessionState`, since the queue's identity guard would otherwise drop the spliced batch ([#1902](https://github.com/can1357/oh-my-pi/issues/1902)).
|
|
26
|
+
- Fixed `/tree` rendering a bare "No entries found" line on a fresh session where the only persisted entries are the `model_change` + `thinking_level_change` written by `sdk.ts` at startup — both are hidden by the tree-selector's default filter, so `#filteredNodes.length === 0` while `tree.length === 2` and the controller's `tree.length === 0` short-circuit never fired. The selector now splits the empty-state into three distinct shapes — truly empty tree, search query with no matches, and filter mode rejecting every entry — surfacing the cause and the recovery key (`Alt+A` to show all, `Backspace` to clear a stale search) so users on a fresh session can see immediately that the panel isn't broken ([#1909](https://github.com/can1357/oh-my-pi/issues/1909)).
|
|
27
|
+
- Fixed remote MCP OAuth refresh failures leaving stale credentials in `agent.db`: when the token endpoint returns a definitive failure (`invalid_grant`, `invalid_token`, `revoked`, plain 401/403 not classified as transient), `MCPManager#resolveAuthConfig` now drops the credential via `AuthStorage.remove(credentialId)` and skips re-attaching the dead `Authorization: Bearer …` header. Previously a revoked refresh token kept producing `401 invalid_token` on every MCP request and survived restarts, so users had to hand-clear the credential row to recover; the next connect now surfaces a clean auth error and `/mcp reauth <server>` (or `/mcp unauth`) recovers without restarting. Transient refresh failures (network/`fetch failed`/`ECONNREFUSED`) still fall back to the existing access token ([#1908](https://github.com/can1357/oh-my-pi/issues/1908)).
|
|
28
|
+
- Fixed `omp://docs` and `omp://docs/...` internal documentation URLs in the distributed package to resolve through the embedded documentation index instead of failing with `Documentation file not found` ([#1898](https://github.com/can1357/oh-my-pi/issues/1898)).
|
|
29
|
+
- Fixed the `github` discovery provider silently ignoring `.github/skills/<name>/SKILL.md`, GitHub's documented Agent Skills layout. The provider now registers a `skills` capability (priority 30, project-only) that scans `.github/skills/` non-recursively via `scanSkillsFromDir` with `requireDescription: true`, matching the Agent Skills spec and the sibling `native`/`omp-plugins` providers ([#1906](https://github.com/can1357/oh-my-pi/issues/1906)).
|
|
30
|
+
- Fixed inline images rendering as a wall of empty PUA box glyphs with laggy scrolling on Kitty-protocol terminals that do not honor Unicode placeholders (most notably WezTerm and tmux/screen passthrough to a non-Kitty outer terminal). The 15.9 placeholder rollout enabled the `U=1`/U+10EEEE grid for every Kitty-protocol path; it now defaults on only for `kitty` and `ghostty`, with `PI_NO_KITTY_PLACEHOLDERS=1` as a hard opt-out and `PI_KITTY_PLACEHOLDERS=1` as opt-in for terminals (e.g. wezterm nightlies) that have since added support ([#1877](https://github.com/can1357/oh-my-pi/issues/1877)).
|
|
31
|
+
- Fixed auto session-title generation failures being swallowed without an actionable diagnostic. Title generation now logs structured start, missing-model/API-key, provider-error, empty-result, and exception outcomes with the session id and resolved title model; the interactive auto-title caller also logs uncaught persistence/generation errors instead of dropping them. ([#1892](https://github.com/can1357/oh-my-pi/issues/1892))
|
|
32
|
+
- Fixed `TranscriptContainer` reporting the live block boundary to the TUI again, so ED3-risk foreground streaming can append newly sealed transcript blocks to native scrollback once while deferring only the active live block.
|
|
33
|
+
|
|
5
34
|
## [15.9.1] - 2026-06-04
|
|
6
35
|
|
|
7
36
|
### Added
|
|
@@ -20,7 +49,6 @@
|
|
|
20
49
|
### Fixed
|
|
21
50
|
|
|
22
51
|
- Fixed a streamed assistant message freezing at a partial prefix (e.g. only "Nat" of "Natives built, now…") on ED3-risk terminals (Ghostty/kitty/iTerm2/Alacritty), with the final text appearing only after a resize. `TranscriptContainer` freezes each non-live block by replaying its last live render, but render coalescing can finalize a block's content and append the next block within the same throttled frame — so the block was sealed at its stale mid-stream snapshot and never repainted until the next `thaw`. The block that was live on the previous render is now recomputed once on the live→frozen transition, sealing it at its final content.
|
|
23
|
-
|
|
24
52
|
- Fixed ACP/RPC stdio startup so protocol frames are no longer consumed as one-shot piped prompt input before the JSON-RPC transport starts.
|
|
25
53
|
- Fixed `omp completions` to await the completion script write before exiting.
|
|
26
54
|
- Fixed `AssistantMessageComponent` exposing its stable-prefix completion API again so streamed assistant messages remain unstable until explicitly completed.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Api, AssistantMessageEventStream, Context, Model, OAuthAccess, OAuthAccessResolution, SimpleStreamOptions } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { CanonicalModelVariant } from "../config/model-equivalence";
|
|
3
|
+
import { type CanonicalModelQueryOptions } from "../config/model-registry";
|
|
4
|
+
import { Settings } from "../config/settings";
|
|
5
|
+
export interface DryBalanceCommandArgs {
|
|
6
|
+
model?: string;
|
|
7
|
+
flags: {
|
|
8
|
+
model?: string;
|
|
9
|
+
count?: number;
|
|
10
|
+
concurrency?: number;
|
|
11
|
+
json?: boolean;
|
|
12
|
+
bench?: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface DryBalanceAuthOptions {
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
modelId?: string;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}
|
|
20
|
+
export interface DryBalanceAuthStorage {
|
|
21
|
+
getOAuthAccess(provider: string, sessionId?: string, options?: DryBalanceAuthOptions): Promise<OAuthAccess | undefined>;
|
|
22
|
+
getOAuthAccesses?(provider: string, options?: DryBalanceAuthOptions): Promise<OAuthAccessResolution[]>;
|
|
23
|
+
}
|
|
24
|
+
export interface DryBalanceModelRegistry {
|
|
25
|
+
authStorage: DryBalanceAuthStorage;
|
|
26
|
+
getAll(): Model<Api>[];
|
|
27
|
+
getAvailable(): Model<Api>[];
|
|
28
|
+
getApiKey(model: Model<Api>, sessionId?: string): Promise<string | undefined>;
|
|
29
|
+
getCanonicalVariants(canonicalId: string, options?: CanonicalModelQueryOptions): CanonicalModelVariant[];
|
|
30
|
+
resolveCanonicalModel?(canonicalId: string, options?: CanonicalModelQueryOptions): Model<Api> | undefined;
|
|
31
|
+
getCanonicalId?(model: Model<Api>): string | undefined;
|
|
32
|
+
}
|
|
33
|
+
export interface DryBalanceRuntime {
|
|
34
|
+
modelRegistry: DryBalanceModelRegistry;
|
|
35
|
+
settings?: Settings;
|
|
36
|
+
close?: () => void;
|
|
37
|
+
}
|
|
38
|
+
export interface DryBalanceAccountStat {
|
|
39
|
+
account: string;
|
|
40
|
+
count: number;
|
|
41
|
+
percent: number;
|
|
42
|
+
}
|
|
43
|
+
export interface DryBalanceFailureStat {
|
|
44
|
+
reason: string;
|
|
45
|
+
count: number;
|
|
46
|
+
percent: number;
|
|
47
|
+
}
|
|
48
|
+
export interface DryBalanceBenchSuccessResult {
|
|
49
|
+
ok: true;
|
|
50
|
+
account: string;
|
|
51
|
+
ttftMs: number;
|
|
52
|
+
durationMs: number;
|
|
53
|
+
outputTokens: number;
|
|
54
|
+
tokensPerSecond: number;
|
|
55
|
+
}
|
|
56
|
+
export interface DryBalanceBenchFailureResult {
|
|
57
|
+
ok: false;
|
|
58
|
+
account?: string;
|
|
59
|
+
error: string;
|
|
60
|
+
}
|
|
61
|
+
export type DryBalanceBenchResult = DryBalanceBenchSuccessResult | DryBalanceBenchFailureResult;
|
|
62
|
+
export interface DryBalanceBenchSummary {
|
|
63
|
+
total: number;
|
|
64
|
+
success: {
|
|
65
|
+
total: number;
|
|
66
|
+
averageTtftMs: number | null;
|
|
67
|
+
averageTokensPerSecond: number | null;
|
|
68
|
+
};
|
|
69
|
+
failure: {
|
|
70
|
+
total: number;
|
|
71
|
+
reasons: DryBalanceFailureStat[];
|
|
72
|
+
};
|
|
73
|
+
results: DryBalanceBenchResult[];
|
|
74
|
+
}
|
|
75
|
+
export interface DryBalanceSummary {
|
|
76
|
+
model: string;
|
|
77
|
+
provider: string;
|
|
78
|
+
samples: number;
|
|
79
|
+
concurrency: number;
|
|
80
|
+
success: {
|
|
81
|
+
total: number;
|
|
82
|
+
accounts: DryBalanceAccountStat[];
|
|
83
|
+
};
|
|
84
|
+
failure: {
|
|
85
|
+
total: number;
|
|
86
|
+
reasons: DryBalanceFailureStat[];
|
|
87
|
+
};
|
|
88
|
+
bench?: DryBalanceBenchSummary;
|
|
89
|
+
}
|
|
90
|
+
type DryBalanceStreamSimple = (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
|
|
91
|
+
export interface DryBalanceDependencies {
|
|
92
|
+
createRuntime?: () => Promise<DryBalanceRuntime>;
|
|
93
|
+
randomSessionId?: () => string;
|
|
94
|
+
writeStdout?: (text: string) => void;
|
|
95
|
+
writeStderr?: (text: string) => void;
|
|
96
|
+
setExitCode?: (code: number) => void;
|
|
97
|
+
streamSimple?: DryBalanceStreamSimple;
|
|
98
|
+
now?: () => number;
|
|
99
|
+
stdoutIsTTY?: boolean;
|
|
100
|
+
stderrIsTTY?: boolean;
|
|
101
|
+
}
|
|
102
|
+
export declare function formatDryBalanceText(summary: DryBalanceSummary): string;
|
|
103
|
+
export declare function runDryBalanceCommand(command: DryBalanceCommandArgs, deps?: DryBalanceDependencies): Promise<DryBalanceSummary>;
|
|
104
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Command } from "@oh-my-pi/pi-utils/cli";
|
|
2
|
+
export default class DryBalance extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
model: import("@oh-my-pi/pi-utils/cli").ArgDescriptor & {
|
|
6
|
+
description: string;
|
|
7
|
+
required: false;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
model: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"string"> & {
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
count: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"integer"> & {
|
|
15
|
+
description: string;
|
|
16
|
+
default: number;
|
|
17
|
+
};
|
|
18
|
+
concurrency: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"integer"> & {
|
|
19
|
+
description: string;
|
|
20
|
+
default: number;
|
|
21
|
+
};
|
|
22
|
+
json: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
bench: import("@oh-my-pi/pi-utils/cli").FlagDescriptor<"boolean"> & {
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
static examples: string[];
|
|
30
|
+
run(): Promise<void>;
|
|
31
|
+
}
|
|
@@ -104,6 +104,7 @@ export declare const ModelsConfigFile: ConfigFile<{
|
|
|
104
104
|
premiumMultiplier?: number | undefined;
|
|
105
105
|
contextWindow?: number | undefined;
|
|
106
106
|
maxTokens?: number | undefined;
|
|
107
|
+
omitMaxOutputTokens?: boolean | undefined;
|
|
107
108
|
headers?: Record<string, string> | undefined;
|
|
108
109
|
compat?: {
|
|
109
110
|
supportsStore?: boolean | undefined;
|
|
@@ -166,6 +167,7 @@ export declare const ModelsConfigFile: ConfigFile<{
|
|
|
166
167
|
premiumMultiplier?: number | undefined;
|
|
167
168
|
contextWindow?: number | undefined;
|
|
168
169
|
maxTokens?: number | undefined;
|
|
170
|
+
omitMaxOutputTokens?: boolean | undefined;
|
|
169
171
|
headers?: Record<string, string> | undefined;
|
|
170
172
|
compat?: {
|
|
171
173
|
supportsStore?: boolean | undefined;
|
|
@@ -109,6 +109,7 @@ export declare const ModelOverrideSchema: z.ZodObject<{
|
|
|
109
109
|
premiumMultiplier: z.ZodOptional<z.ZodNumber>;
|
|
110
110
|
contextWindow: z.ZodOptional<z.ZodNumber>;
|
|
111
111
|
maxTokens: z.ZodOptional<z.ZodNumber>;
|
|
112
|
+
omitMaxOutputTokens: z.ZodOptional<z.ZodBoolean>;
|
|
112
113
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
113
114
|
compat: z.ZodOptional<z.ZodObject<{
|
|
114
115
|
supportsStore: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -336,6 +337,7 @@ export declare const ModelsConfigSchema: z.ZodObject<{
|
|
|
336
337
|
premiumMultiplier: z.ZodOptional<z.ZodNumber>;
|
|
337
338
|
contextWindow: z.ZodOptional<z.ZodNumber>;
|
|
338
339
|
maxTokens: z.ZodOptional<z.ZodNumber>;
|
|
340
|
+
omitMaxOutputTokens: z.ZodOptional<z.ZodBoolean>;
|
|
339
341
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
340
342
|
compat: z.ZodOptional<z.ZodObject<{
|
|
341
343
|
supportsStore: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -449,6 +451,7 @@ export declare const ModelsConfigSchema: z.ZodObject<{
|
|
|
449
451
|
premiumMultiplier: z.ZodOptional<z.ZodNumber>;
|
|
450
452
|
contextWindow: z.ZodOptional<z.ZodNumber>;
|
|
451
453
|
maxTokens: z.ZodOptional<z.ZodNumber>;
|
|
454
|
+
omitMaxOutputTokens: z.ZodOptional<z.ZodBoolean>;
|
|
452
455
|
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
453
456
|
compat: z.ZodOptional<z.ZodObject<{
|
|
454
457
|
supportsStore: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -132,6 +132,17 @@ export declare class Settings {
|
|
|
132
132
|
* can register independently without overwriting each other.
|
|
133
133
|
*/
|
|
134
134
|
export declare function onAppendOnlyModeChanged(cb: (value: string) => void): () => void;
|
|
135
|
+
/**
|
|
136
|
+
* Subscribe to changes in the Hindsight bank-scoping settings. Lets the
|
|
137
|
+
* Hindsight backend rebuild the active `HindsightSessionState` when the
|
|
138
|
+
* operator switches `hindsight.bankId`, `hindsight.bankIdPrefix`, or
|
|
139
|
+
* `hindsight.scoping` mid-session so subsequent retain/recall calls land in
|
|
140
|
+
* the new bank instead of the one selected at session start.
|
|
141
|
+
*
|
|
142
|
+
* Returns an unsubscribe function. The callback receives no arguments — the
|
|
143
|
+
* caller is expected to re-read the relevant settings via `Settings.get`.
|
|
144
|
+
*/
|
|
145
|
+
export declare function onHindsightScopeChanged(cb: () => void): () => void;
|
|
135
146
|
export declare function isSettingsInitialized(): boolean;
|
|
136
147
|
/**
|
|
137
148
|
* Reset the global singleton for testing.
|
|
@@ -20,9 +20,8 @@ export declare function __computeBunfsPackageRoot(metaDir: string, pathImpl?: ty
|
|
|
20
20
|
* and `__dirname`-relative `readFileSync` asset loads (HTML/CSS bundled next to
|
|
21
21
|
* the entry) resolve exactly as they do under the original Pi runtime — no
|
|
22
22
|
* temp-directory mirroring and no asset copying. An `onLoad` hook scoped to the
|
|
23
|
-
* entry's
|
|
24
|
-
*
|
|
25
|
-
* resolves natively.
|
|
23
|
+
* entry's source graph rewrites only host-resolved compatibility imports in the
|
|
24
|
+
* extension's own source; everything else resolves natively.
|
|
26
25
|
*/
|
|
27
26
|
export declare function loadLegacyPiModule(resolvedPath: string): Promise<unknown>;
|
|
28
27
|
export declare function installLegacyPiSpecifierShim(): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bank ID derivation, project-tag scoping, and first-use
|
|
2
|
+
* Bank ID derivation, project-tag scoping, and first-use bank setup.
|
|
3
3
|
*
|
|
4
4
|
* Three scoping modes (`HindsightConfig.scoping`):
|
|
5
5
|
* - `global` — single shared bank, no per-project filter.
|
|
@@ -11,10 +11,13 @@
|
|
|
11
11
|
* The base bank id is `bankIdPrefix-bankId` (default `omp`). Per-project mode
|
|
12
12
|
* appends `-<project>`; tagged mode leaves the bank untouched and uses tags.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* banks we've already
|
|
16
|
-
* `createBank` call.
|
|
17
|
-
*
|
|
14
|
+
* Bank existence is idempotent at module level — a banksSet keeps track of
|
|
15
|
+
* banks we've already PUT so each session boundary doesn't fire a fresh
|
|
16
|
+
* `createBank` call. The PUT is idempotent server-side, so re-firing on a hot
|
|
17
|
+
* path would only burn round-trips. Failures are swallowed: missing the
|
|
18
|
+
* mission patch is an optimisation, but the bank ITSELF must exist before
|
|
19
|
+
* mental-model bootstrap or the first retain, otherwise the very first POST
|
|
20
|
+
* lands against a missing bank.
|
|
18
21
|
*/
|
|
19
22
|
import type { HindsightApi } from "./client";
|
|
20
23
|
import type { HindsightConfig } from "./config";
|
|
@@ -46,9 +49,14 @@ export declare function computeBankScope(config: HindsightConfig, directory: str
|
|
|
46
49
|
*/
|
|
47
50
|
export declare function deriveBankId(config: HindsightConfig, directory: string): string;
|
|
48
51
|
/**
|
|
49
|
-
* Ensure a bank
|
|
52
|
+
* Ensure a bank exists, and patch its reflect/retain mission on first use.
|
|
50
53
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* Idempotent: skips the PUT when the bank id is already in the supplied set.
|
|
55
|
+
* The mission body is optional — when `bankMission` is blank we still PUT to
|
|
56
|
+
* make sure the bank itself is created, so mental-model bootstrap and the
|
|
57
|
+
* first retain don't land against a non-existent bank.
|
|
58
|
+
*
|
|
59
|
+
* The set is capped; on overflow we drop the oldest half so it cannot grow
|
|
60
|
+
* unboundedly across long-lived processes.
|
|
53
61
|
*/
|
|
54
|
-
export declare function
|
|
62
|
+
export declare function ensureBankExists(client: HindsightApi, bankId: string, config: HindsightConfig, banksSet: Set<string>): Promise<void>;
|
|
@@ -70,7 +70,7 @@ export declare function resolveSeedsForScope(scope: BankScope, scoping: Hindsigh
|
|
|
70
70
|
* Idempotently create any seed mental models that don't already exist on the
|
|
71
71
|
* bank. Best-effort: a list/create failure does not throw — mental models are
|
|
72
72
|
* an optimization, not a precondition for retain/recall, and we mirror the
|
|
73
|
-
* swallow-on-failure pattern used by `
|
|
73
|
+
* swallow-on-failure pattern used by `ensureBankExists`.
|
|
74
74
|
*
|
|
75
75
|
* Existing models are NEVER modified. See module docstring.
|
|
76
76
|
*/
|
|
@@ -19,12 +19,12 @@ export interface HindsightSessionStateOptions {
|
|
|
19
19
|
recallTagsMatch?: "any" | "all" | "any_strict" | "all_strict";
|
|
20
20
|
config: HindsightConfig;
|
|
21
21
|
session: AgentSession;
|
|
22
|
-
|
|
22
|
+
banksSet: Set<string>;
|
|
23
23
|
lastRetainedTurn?: number;
|
|
24
24
|
hasRecalledForFirstTurn?: boolean;
|
|
25
25
|
/**
|
|
26
26
|
* When set, this entry is a subagent alias that reuses the parent's bank,
|
|
27
|
-
* scope, config, client, and
|
|
27
|
+
* scope, config, client, and banksSet. Aliases skip auto-recall and
|
|
28
28
|
* auto-retain — those run on the parent only — but the recall/retain/reflect
|
|
29
29
|
* tools resolve via the alias so they persist to the same bank as the parent.
|
|
30
30
|
*/
|
|
@@ -60,7 +60,7 @@ export declare class HindsightSessionState {
|
|
|
60
60
|
recallTagsMatch?: "any" | "all" | "any_strict" | "all_strict";
|
|
61
61
|
config: HindsightConfig;
|
|
62
62
|
session: AgentSession;
|
|
63
|
-
|
|
63
|
+
banksSet: Set<string>;
|
|
64
64
|
lastRetainedTurn: number;
|
|
65
65
|
hasRecalledForFirstTurn: boolean;
|
|
66
66
|
lastRecallSnippet?: string;
|
|
@@ -75,6 +75,12 @@ export declare class HindsightSessionState {
|
|
|
75
75
|
*/
|
|
76
76
|
mentalModelsLoadPromise?: Promise<void>;
|
|
77
77
|
unsubscribe?: () => void;
|
|
78
|
+
/**
|
|
79
|
+
* Releases the `onHindsightScopeChanged` subscription that drives live
|
|
80
|
+
* rebuilds when `hindsight.bankId` / `bankIdPrefix` / `scoping` change.
|
|
81
|
+
* Only set on primary states; aliases inherit the parent's subscription.
|
|
82
|
+
*/
|
|
83
|
+
unsubscribeScope?: () => void;
|
|
78
84
|
/** Alias states delegate persistence config to a primary parent state. */
|
|
79
85
|
aliasOf?: HindsightSessionState;
|
|
80
86
|
readonly retainQueue: HindsightRetainQueue;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type TSchema } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { SourceMeta } from "../capability/types";
|
|
3
3
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
4
4
|
import type { AuthStorage } from "../session/auth-storage";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Container } from "@oh-my-pi/pi-tui";
|
|
1
|
+
import { Container, type NativeScrollbackLiveRegion } from "@oh-my-pi/pi-tui";
|
|
2
2
|
/**
|
|
3
3
|
* Transcript container that freezes the rendered output of every block except
|
|
4
4
|
* the bottom-most (live) one on terminals where committed native scrollback is
|
|
@@ -21,10 +21,11 @@ import { Container } from "@oh-my-pi/pi-tui";
|
|
|
21
21
|
* and any drift reconciles safely. On terminals that can rebuild history this
|
|
22
22
|
* freezing is unnecessary, so it renders every block live for full fidelity.
|
|
23
23
|
*/
|
|
24
|
-
export declare class TranscriptContainer extends Container {
|
|
24
|
+
export declare class TranscriptContainer extends Container implements NativeScrollbackLiveRegion {
|
|
25
25
|
#private;
|
|
26
26
|
invalidate(): void;
|
|
27
27
|
clear(): void;
|
|
28
|
+
getNativeScrollbackLiveRegionStart(): number | undefined;
|
|
28
29
|
/**
|
|
29
30
|
* Retire all frozen snapshots so the next render reflects each block's current
|
|
30
31
|
* state. Call at reconciliation checkpoints (prompt submit) where the whole
|
|
@@ -201,6 +201,15 @@ export interface AgentSessionConfig {
|
|
|
201
201
|
* **MUST NOT** dispose it on their own teardown.
|
|
202
202
|
*/
|
|
203
203
|
ownedAsyncJobManager?: AsyncJobManager;
|
|
204
|
+
/**
|
|
205
|
+
* AsyncJobManager reachable by this session for scoped job actions.
|
|
206
|
+
*
|
|
207
|
+
* Top-level owners receive their own manager, subagents receive the inherited
|
|
208
|
+
* parent manager, and secondary in-process top-level sessions receive
|
|
209
|
+
* `undefined` so job snapshots and ACP drains cannot observe the primary's
|
|
210
|
+
* state.
|
|
211
|
+
*/
|
|
212
|
+
asyncJobManager?: AsyncJobManager;
|
|
204
213
|
/** Agent identity (registry id like "Main" or "Alice") used for IRC routing. */
|
|
205
214
|
agentId?: string;
|
|
206
215
|
/** Shared agent registry (for forwarding IRC observations to the main session UI). */
|
|
@@ -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, OAuthCredential, SerializedAuthStorage, StoredAuthCredential, } from "@oh-my-pi/pi-ai";
|
|
6
|
-
export { AuthBrokerClient, AuthStorage, REMOTE_REFRESH_SENTINEL, RemoteAuthCredentialStore, SqliteAuthCredentialStore, } from "@oh-my-pi/pi-ai";
|
|
5
|
+
export type { ApiKeyCredential, AuthCredential, AuthCredentialEntry, AuthCredentialStore, AuthStorageData, AuthStorageOptions, OAuthCredential, SerializedAuthStorage, SnapshotResponse, StoredAuthCredential, } from "@oh-my-pi/pi-ai";
|
|
6
|
+
export { AuthBrokerClient, AuthStorage, DEFAULT_SNAPSHOT_CACHE_TTL_MS, REMOTE_REFRESH_SENTINEL, RemoteAuthCredentialStore, readAuthBrokerSnapshotCache, SqliteAuthCredentialStore, writeAuthBrokerSnapshotCache, } from "@oh-my-pi/pi-ai";
|
|
@@ -149,6 +149,8 @@ export interface AgentDefinition {
|
|
|
149
149
|
output?: unknown;
|
|
150
150
|
blocking?: boolean;
|
|
151
151
|
autoloadSkills?: string[];
|
|
152
|
+
/** When `false`, the agent's `read` tool returns verbatim file content instead of structural summaries. */
|
|
153
|
+
readSummarize?: boolean;
|
|
152
154
|
source: AgentSource;
|
|
153
155
|
filePath?: string;
|
|
154
156
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
|
|
2
2
|
import type { AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
import type { ToolChoice } from "@oh-my-pi/pi-ai";
|
|
4
|
+
import type { AsyncJobManager } from "../async/job-manager";
|
|
4
5
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
5
6
|
import type { Settings } from "../config/settings";
|
|
6
7
|
import type { Skill } from "../extensibility/skills";
|
|
@@ -139,6 +140,21 @@ export interface ToolSession {
|
|
|
139
140
|
modelRegistry?: import("../config/model-registry").ModelRegistry;
|
|
140
141
|
/** Agent output manager for unique agent:// IDs across task invocations */
|
|
141
142
|
agentOutputManager?: AgentOutputManager;
|
|
143
|
+
/**
|
|
144
|
+
* Async job manager scoped to this session.
|
|
145
|
+
*
|
|
146
|
+
* - Top-level session that constructed one: its own manager.
|
|
147
|
+
* - Subagent (`parentTaskPrefix` set): the parent's manager, so background
|
|
148
|
+
* bash/task work and `onJobComplete` deliveries flow into the conversation
|
|
149
|
+
* that spawned it.
|
|
150
|
+
* - Secondary in-process top-level session that found a singleton already
|
|
151
|
+
* installed (issue #1923): `undefined`. Tools refuse async work rather
|
|
152
|
+
* than silently route completions into the owning session's `yieldQueue`.
|
|
153
|
+
*
|
|
154
|
+
* Tools MUST use this instead of `AsyncJobManager.instance()` so a secondary
|
|
155
|
+
* session never borrows the owning session's manager by accident.
|
|
156
|
+
*/
|
|
157
|
+
asyncJobManager?: AsyncJobManager;
|
|
142
158
|
/** MCP manager visible to subagents without relying on the process-global singleton. */
|
|
143
159
|
mcpManager?: MCPManager;
|
|
144
160
|
/** Local protocol root to propagate to nested subagents and eval-created agents. */
|
|
@@ -17,6 +17,17 @@ export declare function parseLineRangeChunk(sel: string): LineRange | null;
|
|
|
17
17
|
* downstream consumers can stream the file in a single forward pass per range.
|
|
18
18
|
*/
|
|
19
19
|
export declare function parseLineRanges(sel: string): [LineRange, ...LineRange[]] | null;
|
|
20
|
+
/**
|
|
21
|
+
* Extract the line-range component from a read-tool selector that may also
|
|
22
|
+
* carry a verbatim/index display mode (`raw`, `conflicts`) — alone or compounded
|
|
23
|
+
* with a range (`raw:50-100`, `50-100:raw`). Returns the parsed ranges when the
|
|
24
|
+
* selector names any, otherwise `undefined` (pure `raw`/`conflicts`/none).
|
|
25
|
+
*
|
|
26
|
+
* Used by content search, which honors line ranges as a match filter but has no
|
|
27
|
+
* use for verbatim/conflict display modes — so those selectors are accepted and
|
|
28
|
+
* treated as an unfiltered, whole-resource search rather than rejected.
|
|
29
|
+
*/
|
|
30
|
+
export declare function selectorLineRanges(sel: string | undefined): [LineRange, ...LineRange[]] | undefined;
|
|
20
31
|
/** Return `true` when `lineNumber` (1-indexed) falls in any of the supplied ranges. */
|
|
21
32
|
export declare function isLineInRanges(lineNumber: number, ranges: readonly LineRange[]): boolean;
|
|
22
33
|
export declare function splitPathAndSel(rawPath: string): {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "15.9.
|
|
4
|
+
"version": "15.9.3",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"@agentclientprotocol/sdk": "0.22.1",
|
|
48
48
|
"@babel/parser": "^7.29.7",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/hashline": "15.9.
|
|
51
|
-
"@oh-my-pi/omp-stats": "15.9.
|
|
52
|
-
"@oh-my-pi/pi-agent-core": "15.9.
|
|
53
|
-
"@oh-my-pi/pi-ai": "15.9.
|
|
54
|
-
"@oh-my-pi/pi-mnemopi": "15.9.
|
|
55
|
-
"@oh-my-pi/pi-natives": "15.9.
|
|
56
|
-
"@oh-my-pi/pi-tui": "15.9.
|
|
57
|
-
"@oh-my-pi/pi-utils": "15.9.
|
|
50
|
+
"@oh-my-pi/hashline": "15.9.3",
|
|
51
|
+
"@oh-my-pi/omp-stats": "15.9.3",
|
|
52
|
+
"@oh-my-pi/pi-agent-core": "15.9.3",
|
|
53
|
+
"@oh-my-pi/pi-ai": "15.9.3",
|
|
54
|
+
"@oh-my-pi/pi-mnemopi": "15.9.3",
|
|
55
|
+
"@oh-my-pi/pi-natives": "15.9.3",
|
|
56
|
+
"@oh-my-pi/pi-tui": "15.9.3",
|
|
57
|
+
"@oh-my-pi/pi-utils": "15.9.3",
|
|
58
58
|
"@opentelemetry/api": "^1.9.1",
|
|
59
59
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
60
60
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|