@oh-my-pi/pi-coding-agent 17.0.8 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -1
- package/README.md +1 -1
- package/dist/cli.js +5495 -5021
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +350 -50
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
- package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +14 -1
- package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
- package/dist/types/live/controller.d.ts +40 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +48 -0
- package/dist/types/live/visualizer.d.ts +33 -0
- package/dist/types/lsp/client.d.ts +18 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/manager.d.ts +6 -2
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/render.d.ts +2 -2
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/mcp/tool-bridge.d.ts +7 -3
- package/dist/types/mcp/types.d.ts +6 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/oauth-selector.d.ts +8 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/settings-defs.d.ts +8 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
- package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
- package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +314 -0
- package/dist/types/session/agent-session.d.ts +120 -750
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +49 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +22 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-loader.d.ts +6 -4
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +143 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/task/types.d.ts +14 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/hub/jobs.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/report-tool-issue.d.ts +24 -9
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +14 -0
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +95 -1
- package/package.json +13 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/scripts/legacy-pi-virtual-module.ts +1 -1
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/usage-cli.ts +11 -2
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/__tests__/model-registry.test.ts +147 -0
- package/src/config/model-registry.ts +50 -2
- package/src/config/model-resolver.ts +0 -1
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +250 -54
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +89 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +870 -311
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/registry-helpers.ts +40 -0
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/audio-worklet.txt +59 -0
- package/src/live/browser-runtime.txt +221 -0
- package/src/live/controller.ts +464 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +490 -0
- package/src/live/visualizer.ts +234 -0
- package/src/lsp/client.ts +52 -9
- package/src/lsp/clients/biome-client.ts +3 -4
- package/src/lsp/index.ts +44 -11
- package/src/main.ts +9 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/manager.ts +39 -8
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/render.ts +94 -35
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/mcp/tool-bridge.ts +107 -11
- package/src/mcp/types.ts +7 -0
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +27 -10
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/oauth-selector.ts +24 -7
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/settings-defs.ts +28 -3
- package/src/modes/components/settings-selector.ts +178 -6
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.test.ts +63 -2
- package/src/modes/components/tool-execution.ts +11 -2
- package/src/modes/controllers/command-controller.ts +70 -22
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/live-command-controller.ts +178 -0
- package/src/modes/controllers/mcp-command-controller.ts +80 -41
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +18 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +160 -38
- package/src/modes/rpc/rpc-client.ts +94 -3
- package/src/modes/rpc/rpc-frame.ts +164 -4
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +79 -7
- package/src/modes/rpc/rpc-types.ts +34 -2
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/model.ts +5 -7
- package/src/modes/setup-wizard/scenes/providers.ts +3 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
- package/src/modes/setup-wizard/scenes/theme.ts +13 -3
- package/src/modes/setup-wizard/scenes/types.ts +9 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +23 -10
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/goals/guided-goal-system.md +24 -3
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/task.md +12 -2
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +255 -139
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +334 -0
- package/src/session/agent-session.ts +1910 -12213
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +212 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +263 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.test.ts +224 -1
- package/src/session/session-context.ts +47 -5
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +10 -51
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +943 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +92 -11
- package/src/slash-commands/helpers/usage-report.ts +25 -4
- package/src/system-prompt.ts +41 -17
- package/src/task/executor.ts +288 -70
- package/src/task/index.ts +148 -111
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +4 -1
- package/src/task/types.ts +16 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +26 -0
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +67 -8
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +12 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +7 -1
- package/src/tools/report-tool-issue.ts +79 -28
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +67 -3
- package/src/tts/streaming-player.ts +62 -4
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/firecrawl.ts +46 -13
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +8 -1
package/src/commands/bench.ts
CHANGED
|
@@ -15,15 +15,24 @@ export default class Bench extends Command {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
static flags = {
|
|
18
|
-
runs: Flags.integer({ description: "Requests per model (results are averaged
|
|
19
|
-
"max-tokens": Flags.integer({ description: "Max output tokens per request
|
|
18
|
+
runs: Flags.integer({ description: "Requests per model (results are averaged; default: 10)" }),
|
|
19
|
+
"max-tokens": Flags.integer({ description: "Max output tokens per request (default: 512; cache mode: 64)" }),
|
|
20
20
|
prompt: Flags.string({ description: "Custom prompt text (default: bundled bench prompt)" }),
|
|
21
21
|
"service-tier": Flags.string({
|
|
22
22
|
description: "Service tier applied per model family (default: configured `tier.*` settings; `none` omits it)",
|
|
23
23
|
options: SERVICE_TIER_OPENAI_VALUES,
|
|
24
24
|
}),
|
|
25
25
|
json: Flags.boolean({ description: "Output JSON" }),
|
|
26
|
-
par: Flags.integer({ description: "Execute runs with N parallel queries/requests
|
|
26
|
+
par: Flags.integer({ description: "Execute runs with N parallel queries/requests (default: 4)" }),
|
|
27
|
+
cache: Flags.boolean({
|
|
28
|
+
description: "Run independent cold/warm prompt-cache pairs (not supported for openai-codex-responses)",
|
|
29
|
+
}),
|
|
30
|
+
"cache-prefix-file": Flags.string({ description: "Stable prompt prefix file for --cache" }),
|
|
31
|
+
"cache-prefix-bytes": Flags.integer({ description: "Stable prefix byte budget for --cache (default: 8192)" }),
|
|
32
|
+
"cache-pairs": Flags.integer({ description: "Cold/warm pairs per model for --cache (default: 1)" }),
|
|
33
|
+
"cache-concurrency": Flags.integer({
|
|
34
|
+
description: "Concurrent cache pairs for --cache; each pair remains sequential (default: 1)",
|
|
35
|
+
}),
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
static examples = [
|
|
@@ -31,7 +40,7 @@ export default class Bench extends Command {
|
|
|
31
40
|
"# Fuzzy selectors work\n omp bench opus sonnet",
|
|
32
41
|
"# Average over 3 runs each\n omp bench opus gpt-5.2 --runs 3",
|
|
33
42
|
"# Force priority serving tier\n omp bench openai-codex/gpt-5.5:low --runs 10 --service-tier priority",
|
|
34
|
-
"#
|
|
43
|
+
"# Measure one cold/warm prompt-cache pair\n omp bench openai/gpt-5.6 --cache --json",
|
|
35
44
|
];
|
|
36
45
|
|
|
37
46
|
async run(): Promise<void> {
|
|
@@ -45,6 +54,11 @@ export default class Bench extends Command {
|
|
|
45
54
|
serviceTier: flags["service-tier"],
|
|
46
55
|
json: flags.json,
|
|
47
56
|
par: flags.par,
|
|
57
|
+
cache: flags.cache,
|
|
58
|
+
cachePrefixFile: flags["cache-prefix-file"],
|
|
59
|
+
cachePrefixBytes: flags["cache-prefix-bytes"],
|
|
60
|
+
cachePairs: flags["cache-pairs"],
|
|
61
|
+
cacheConcurrency: flags["cache-concurrency"],
|
|
48
62
|
},
|
|
49
63
|
});
|
|
50
64
|
}
|
package/src/commands/launch.ts
CHANGED
|
@@ -83,6 +83,10 @@ export default class Index extends Command {
|
|
|
83
83
|
description: "Load an extra config.yml-style overlay for this run (repeatable)",
|
|
84
84
|
multiple: true,
|
|
85
85
|
}),
|
|
86
|
+
"add-dir": Flags.string({
|
|
87
|
+
description: "Add a workspace directory beyond the working directory (repeatable)",
|
|
88
|
+
multiple: true,
|
|
89
|
+
}),
|
|
86
90
|
print: Flags.boolean({
|
|
87
91
|
char: "p",
|
|
88
92
|
description: "Non-interactive mode: process prompt and exit",
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "bun:test";
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
6
|
+
import { ModelRegistry } from "../model-registry";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Stub AuthStorage that satisfies the surface used by ModelRegistry's
|
|
10
|
+
* constructor (#loadModels → clearConfigApiKeys, constructor →
|
|
11
|
+
* setFallbackResolver). The awaiter under test never reaches auth-gated code
|
|
12
|
+
* paths, so no real credential store is required.
|
|
13
|
+
*/
|
|
14
|
+
function createStubAuthStorage(): AuthStorage {
|
|
15
|
+
const stub = {
|
|
16
|
+
setFallbackResolver: () => {},
|
|
17
|
+
clearConfigApiKeys: () => {},
|
|
18
|
+
hasAuth: () => false,
|
|
19
|
+
};
|
|
20
|
+
return stub as unknown as AuthStorage;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("ModelRegistry.awaitBackgroundRefresh", () => {
|
|
24
|
+
let tmpDir: string;
|
|
25
|
+
let registry: ModelRegistry;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), "omp-reg-"));
|
|
29
|
+
// Construct with an explicit modelsPath inside the temp dir so the
|
|
30
|
+
// constructor's #loadModels read returns "not-found" rather than
|
|
31
|
+
// touching the host's ~/.omp/agent/models.yaml. isBunTestRuntime()
|
|
32
|
+
// auto-stubs #fetch in the constructor.
|
|
33
|
+
registry = new ModelRegistry(createStubAuthStorage(), path.join(tmpDir, "models.yaml"));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
vi.restoreAllMocks();
|
|
38
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("resolves immediately when no background refresh is in flight", async () => {
|
|
42
|
+
// No refreshInBackground() called → #backgroundRefresh is undefined.
|
|
43
|
+
// The awaiter must settle within a single microtask, never hanging.
|
|
44
|
+
let settled = false;
|
|
45
|
+
const p = registry.awaitBackgroundRefresh().then(() => {
|
|
46
|
+
settled = true;
|
|
47
|
+
});
|
|
48
|
+
await Promise.resolve();
|
|
49
|
+
await p;
|
|
50
|
+
expect(settled).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("blocks until the in-flight background refresh resolves, then resolves", async () => {
|
|
54
|
+
// Drive refreshInBackground with a controlled refresh() return value so
|
|
55
|
+
// #backgroundRefresh is captured but not yet settled.
|
|
56
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
57
|
+
const refreshSpy = vi.spyOn(registry, "refresh").mockReturnValue(promise);
|
|
58
|
+
|
|
59
|
+
registry.refreshInBackground();
|
|
60
|
+
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
61
|
+
|
|
62
|
+
let settled = false;
|
|
63
|
+
const awaitPromise = registry.awaitBackgroundRefresh().then(() => {
|
|
64
|
+
settled = true;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Yield to the microtask queue: the awaiter must still be pending.
|
|
68
|
+
for (let i = 0; i < 5; i++) await Promise.resolve();
|
|
69
|
+
expect(settled).toBe(false);
|
|
70
|
+
|
|
71
|
+
resolve();
|
|
72
|
+
|
|
73
|
+
await awaitPromise;
|
|
74
|
+
expect(settled).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("resolves even when the underlying refresh rejects (refreshInBackground swallows)", async () => {
|
|
78
|
+
// refreshInBackground wraps refresh() in .catch(...) so discovery errors
|
|
79
|
+
// never reach awaitBackgroundRefresh callers. The awaiter must resolve,
|
|
80
|
+
// not propagate the rejection.
|
|
81
|
+
const { promise, reject } = Promise.withResolvers<void>();
|
|
82
|
+
vi.spyOn(registry, "refresh").mockReturnValue(promise);
|
|
83
|
+
|
|
84
|
+
registry.refreshInBackground();
|
|
85
|
+
|
|
86
|
+
let settled = false;
|
|
87
|
+
const awaitPromise = registry.awaitBackgroundRefresh().then(() => {
|
|
88
|
+
settled = true;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
reject(new Error("synthetic discovery failure"));
|
|
92
|
+
|
|
93
|
+
await awaitPromise;
|
|
94
|
+
expect(settled).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("awaiter is a no-op after the in-flight refresh settles and clears #backgroundRefresh", async () => {
|
|
98
|
+
// Once refreshInBackground's promise resolves, #backgroundRefresh is
|
|
99
|
+
// cleared in the .finally. A subsequent awaitBackgroundRefresh must be
|
|
100
|
+
// an immediate no-op (microtask), not hang waiting for a stale promise
|
|
101
|
+
// or a second refresh that was never started.
|
|
102
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
103
|
+
vi.spyOn(registry, "refresh").mockReturnValue(promise);
|
|
104
|
+
|
|
105
|
+
registry.refreshInBackground();
|
|
106
|
+
resolve();
|
|
107
|
+
await registry.awaitBackgroundRefresh();
|
|
108
|
+
|
|
109
|
+
// Now #backgroundRefresh is cleared. A fresh await must resolve in a
|
|
110
|
+
// single microtask — measure by asserting it settles before a second
|
|
111
|
+
// microtask tick.
|
|
112
|
+
let settled = false;
|
|
113
|
+
const p = registry.awaitBackgroundRefresh().then(() => {
|
|
114
|
+
settled = true;
|
|
115
|
+
});
|
|
116
|
+
await Promise.resolve();
|
|
117
|
+
expect(settled).toBe(true);
|
|
118
|
+
await p;
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("refreshInBackground deduplicates: a second call while in-flight starts no new refresh", async () => {
|
|
122
|
+
// The guard `if (this.#backgroundRefresh) return` at the top of
|
|
123
|
+
// refreshInBackground prevents concurrent refreshes. A second call
|
|
124
|
+
// while the first is still pending must not invoke refresh() again.
|
|
125
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
126
|
+
const refreshSpy = vi.spyOn(registry, "refresh").mockReturnValue(promise);
|
|
127
|
+
|
|
128
|
+
registry.refreshInBackground();
|
|
129
|
+
registry.refreshInBackground();
|
|
130
|
+
registry.refreshInBackground();
|
|
131
|
+
|
|
132
|
+
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
133
|
+
|
|
134
|
+
resolve();
|
|
135
|
+
await registry.awaitBackgroundRefresh();
|
|
136
|
+
|
|
137
|
+
// After settle, #backgroundRefresh is cleared — a new call DOES start
|
|
138
|
+
// a fresh refresh.
|
|
139
|
+
const { promise: secondPromise, resolve: secondResolve } = Promise.withResolvers<void>();
|
|
140
|
+
refreshSpy.mockReturnValue(secondPromise);
|
|
141
|
+
registry.refreshInBackground();
|
|
142
|
+
expect(refreshSpy).toHaveBeenCalledTimes(2);
|
|
143
|
+
|
|
144
|
+
secondResolve();
|
|
145
|
+
await registry.awaitBackgroundRefresh();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -869,6 +869,28 @@ export class ModelRegistry {
|
|
|
869
869
|
this.#backgroundRefresh = refreshPromise;
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Wait for any in-flight background model discovery to settle.
|
|
874
|
+
*
|
|
875
|
+
* Background discovery started by {@link refreshInBackground} is
|
|
876
|
+
* fire-and-forget; RPC consumers (e.g. `get_available_models`,
|
|
877
|
+
* `set_model`) and deferred `--model` resolution that read the registry
|
|
878
|
+
* immediately after session creation can otherwise observe a partial
|
|
879
|
+
* catalog before discovery-backed providers have populated `#models`.
|
|
880
|
+
* Awaiting the tracked promise ensures the response reflects every
|
|
881
|
+
* configured provider once the initial background refresh resolves.
|
|
882
|
+
*
|
|
883
|
+
* No-op when no refresh is in flight (`#backgroundRefresh` cleared in the
|
|
884
|
+
* `finally` of `refreshInBackground` on completion). Resolves immediately
|
|
885
|
+
* in that case so already-warm sessions are unaffected. Discovery errors
|
|
886
|
+
* remain swallowed by `refreshInBackground`'s existing `.catch`.
|
|
887
|
+
*/
|
|
888
|
+
async awaitBackgroundRefresh(): Promise<void> {
|
|
889
|
+
if (this.#backgroundRefresh) {
|
|
890
|
+
await this.#backgroundRefresh;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
872
894
|
async refreshProvider(providerId: string, strategy: ModelRefreshStrategy = "online"): Promise<void> {
|
|
873
895
|
this.#reloadStaticModels();
|
|
874
896
|
for (const selector of this.#suppressedSelectors.keys()) {
|
|
@@ -2101,6 +2123,24 @@ export class ModelRegistry {
|
|
|
2101
2123
|
.map(provider => provider.provider);
|
|
2102
2124
|
}
|
|
2103
2125
|
|
|
2126
|
+
/**
|
|
2127
|
+
* Whether `providerId` is known to the registry: it has at least one live
|
|
2128
|
+
* model, or it is configured for dynamic discovery (models.yml `discovery:`
|
|
2129
|
+
* or a runtime extension provider) and is not disabled. Discovery-only
|
|
2130
|
+
* providers can hold zero models at startup — cached rows never persist
|
|
2131
|
+
* live auth headers (#5780), so a provider whose discovered models all
|
|
2132
|
+
* carry config headers (`authHeader: true`) only materializes models after
|
|
2133
|
+
* the online refresh completes.
|
|
2134
|
+
*/
|
|
2135
|
+
hasProvider(providerId: string): boolean {
|
|
2136
|
+
if (this.#models.some(model => model.provider === providerId)) return true;
|
|
2137
|
+
if (getDisabledProviderIdsFromSettings().has(providerId)) return false;
|
|
2138
|
+
return (
|
|
2139
|
+
this.#discoverableProviders.some(provider => provider.provider === providerId) ||
|
|
2140
|
+
this.#runtimeModelManagers.has(providerId)
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2104
2144
|
getProviderDiscoveryState(provider: string): ProviderDiscoveryState | undefined {
|
|
2105
2145
|
return this.#providerDiscoveryStates.get(provider);
|
|
2106
2146
|
}
|
|
@@ -2131,13 +2171,21 @@ export class ModelRegistry {
|
|
|
2131
2171
|
/**
|
|
2132
2172
|
* Get API key for a model.
|
|
2133
2173
|
*/
|
|
2134
|
-
async getApiKey(
|
|
2174
|
+
async getApiKey(
|
|
2175
|
+
model: Model<Api>,
|
|
2176
|
+
sessionId?: string,
|
|
2177
|
+
options?: { signal?: AbortSignal },
|
|
2178
|
+
): Promise<string | undefined> {
|
|
2135
2179
|
const commandKey = this.#resolveCommandBackedApiKey(model.provider);
|
|
2136
2180
|
if (commandKey.configured) return commandKey.value;
|
|
2137
2181
|
if (this.#keylessProviders.has(model.provider) && !this.authStorage.hasAuth(model.provider)) {
|
|
2138
2182
|
return kNoAuth;
|
|
2139
2183
|
}
|
|
2140
|
-
return this.authStorage.getApiKey(model.provider, sessionId, {
|
|
2184
|
+
return this.authStorage.getApiKey(model.provider, sessionId, {
|
|
2185
|
+
baseUrl: model.baseUrl,
|
|
2186
|
+
modelId: model.id,
|
|
2187
|
+
signal: options?.signal,
|
|
2188
|
+
});
|
|
2141
2189
|
}
|
|
2142
2190
|
|
|
2143
2191
|
/**
|
|
@@ -782,7 +782,6 @@ function parseModelPatternWithContext(
|
|
|
782
782
|
options?: { allowInvalidThinkingSelectorFallback?: boolean },
|
|
783
783
|
): ParsedModelResult {
|
|
784
784
|
// Exact match on the full pattern first (no fuzzy): a literal id that
|
|
785
|
-
// contains a colon (`coding-router:max`) wins over any suffix split.
|
|
786
785
|
const exactMatch = matchModel(pattern, availableModels, context, { exactOnly: true });
|
|
787
786
|
if (exactMatch) {
|
|
788
787
|
return { model: exactMatch, thinkingLevel: undefined, warning: undefined, explicitThinkingLevel: false };
|
|
@@ -73,8 +73,19 @@ export const OpenAICompatSchema = type({
|
|
|
73
73
|
"whenThinking?": OpenAICompatFieldsSchema,
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
const BedrockCompatSchema = type({
|
|
77
|
+
"promptCacheMode?": '"none" | "automatic" | "explicit"',
|
|
78
|
+
"supportsLongPromptCacheRetention?": "boolean",
|
|
79
|
+
"promptCacheMinimumTokens?": "number >= 0",
|
|
80
|
+
"promptCacheMaximumCheckpoints?": "number >= 0",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Provider-level overrides can target bundled models whose API is not repeated
|
|
84
|
+
// in models.yml, so preserve the sparse compat shape for each supported API.
|
|
85
|
+
const ApiCompatSchema = OpenAICompatSchema.and(BedrockCompatSchema);
|
|
86
|
+
|
|
76
87
|
const ApiSchema = type(
|
|
77
|
-
'"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
|
|
88
|
+
'"openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex"',
|
|
78
89
|
);
|
|
79
90
|
|
|
80
91
|
const EffortSchema = type('"minimal" | "low" | "medium" | "high" | "xhigh" | "max"');
|
|
@@ -173,7 +184,7 @@ const ModelDefinitionSchema = type({
|
|
|
173
184
|
"maxTokens?": "number",
|
|
174
185
|
"omitMaxOutputTokens?": "boolean",
|
|
175
186
|
"headers?": { "[string]": "string" },
|
|
176
|
-
"compat?":
|
|
187
|
+
"compat?": ApiCompatSchema,
|
|
177
188
|
"contextPromotionTarget?": "string",
|
|
178
189
|
"compactionModel?": "string",
|
|
179
190
|
"remoteCompaction?": RemoteCompactionSchema,
|
|
@@ -222,7 +233,7 @@ export const ModelOverrideSchema = type({
|
|
|
222
233
|
"maxTokens?": "number",
|
|
223
234
|
"omitMaxOutputTokens?": "boolean",
|
|
224
235
|
"headers?": { "[string]": "string" },
|
|
225
|
-
"compat?":
|
|
236
|
+
"compat?": ApiCompatSchema,
|
|
226
237
|
"contextPromotionTarget?": "string",
|
|
227
238
|
"compactionModel?": "string",
|
|
228
239
|
"remoteCompaction?": RemoteCompactionSchema,
|
|
@@ -263,7 +274,7 @@ const ProviderConfigSchema = type({
|
|
|
263
274
|
"apiKey?": "string",
|
|
264
275
|
"api?": ApiSchema,
|
|
265
276
|
"headers?": { "[string]": "string" },
|
|
266
|
-
"compat?":
|
|
277
|
+
"compat?": ApiCompatSchema,
|
|
267
278
|
"remoteCompaction?": RemoteCompactionSchema,
|
|
268
279
|
"authHeader?": "boolean",
|
|
269
280
|
"auth?": ProviderAuthSchema,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { setImageProviderOrder } from "../tools/image-gen";
|
|
2
2
|
import * as webSearch from "../web/search";
|
|
3
3
|
|
|
4
4
|
interface ProviderGlobalSettings {
|
|
5
|
+
get(path: "providers.webSearchOrder"): unknown;
|
|
5
6
|
get(path: "providers.webSearchExclude"): unknown;
|
|
6
|
-
get(path: "providers.
|
|
7
|
-
get(path: "providers.image"): unknown;
|
|
7
|
+
get(path: "providers.imageOrder"): unknown;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function applyProviderGlobalsFromSettings(settings: ProviderGlobalSettings): void {
|
|
@@ -13,13 +13,13 @@ export function applyProviderGlobalsFromSettings(settings: ProviderGlobalSetting
|
|
|
13
13
|
webSearch.setExcludedSearchProviders(excludedWebSearchProviders.filter(webSearch.isSearchProviderId));
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
webSearch.
|
|
16
|
+
const orderedWebSearchProviders = settings.get("providers.webSearchOrder");
|
|
17
|
+
if (Array.isArray(orderedWebSearchProviders)) {
|
|
18
|
+
webSearch.setSearchProviderOrder(orderedWebSearchProviders.filter(webSearch.isSearchProviderId));
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
|
|
21
|
+
const orderedImageProviders = settings.get("providers.imageOrder");
|
|
22
|
+
if (Array.isArray(orderedImageProviders)) {
|
|
23
|
+
setImageProviderOrder(orderedImageProviders.filter((entry): entry is string => typeof entry === "string"));
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ServiceTier, ServiceTierByFamily } from "@oh-my-pi/pi-ai";
|
|
1
|
+
import type { ServiceTier, ServiceTierByFamily, ServiceTierFamily } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { SubmenuOption } from "./settings-schema";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,6 +16,31 @@ export type ServiceTierOpenAISettingValue = (typeof SERVICE_TIER_OPENAI_VALUES)[
|
|
|
16
16
|
export type ServiceTierAnthropicSettingValue = (typeof SERVICE_TIER_ANTHROPIC_VALUES)[number];
|
|
17
17
|
export type ServiceTierGoogleSettingValue = (typeof SERVICE_TIER_GOOGLE_VALUES)[number];
|
|
18
18
|
|
|
19
|
+
/** Whether a runtime value names a provider family with an independent service-tier knob. */
|
|
20
|
+
export function isServiceTierFamily(value: unknown): value is ServiceTierFamily {
|
|
21
|
+
return value === "openai" || value === "anthropic" || value === "google";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Whether a runtime value is a supported service tier for one provider family. */
|
|
25
|
+
export function isServiceTierForFamily(family: string, tier: unknown): tier is ServiceTier {
|
|
26
|
+
if (typeof tier !== "string" || tier === "none") return false;
|
|
27
|
+
let values: readonly string[];
|
|
28
|
+
switch (family) {
|
|
29
|
+
case "openai":
|
|
30
|
+
values = SERVICE_TIER_OPENAI_VALUES;
|
|
31
|
+
break;
|
|
32
|
+
case "anthropic":
|
|
33
|
+
values = SERVICE_TIER_ANTHROPIC_VALUES;
|
|
34
|
+
break;
|
|
35
|
+
case "google":
|
|
36
|
+
values = SERVICE_TIER_GOOGLE_VALUES;
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return values.includes(tier);
|
|
42
|
+
}
|
|
43
|
+
|
|
19
44
|
/**
|
|
20
45
|
* Inherit-capable single value for the subagent/advisor tiers. The chosen tier
|
|
21
46
|
* is broadcast across families and applied to whichever family the spawned
|