@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.11
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 +95 -4
- package/dist/cli.js +23087 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +7 -219
- package/dist/types/config/model-resolver.d.ts +16 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +12 -7
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +25 -6
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +2 -1
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +69 -5
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +1 -1
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +231 -1019
- package/src/config/model-resolver.ts +113 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +18 -4
- package/src/config/settings.ts +37 -1
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/edit/diff.ts +47 -3
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +8 -2
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +2 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +40 -22
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +0 -2
- package/src/eval/py/kernel.ts +19 -0
- package/src/eval/py/runner.py +107 -3
- package/src/exec/bash-executor.ts +3 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/types.ts +3 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +8 -8
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +165 -17
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +2 -1
- package/src/mnemopi/backend.ts +25 -3
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +19 -13
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +1 -1
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +4 -2
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +258 -53
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +10 -3
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/interactive-mode.ts +3 -9
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +2 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +13 -24
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +1 -1
- package/src/sdk.ts +23 -10
- package/src/session/agent-session.ts +44 -10
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +23 -2
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/ssh/connection-manager.ts +27 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +61 -53
- package/src/task/index.ts +137 -60
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +2 -2
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +2 -1
- package/src/tiny/title-client.ts +26 -11
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +54 -13
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +3 -9
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +38 -8
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/file-mentions.ts +2 -1
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
|
@@ -31,10 +31,9 @@ Subagents have no conversation history. Every fact, file path, and direction the
|
|
|
31
31
|
|
|
32
32
|
<rules>
|
|
33
33
|
- **Maximize batch width.** Spawn the widest parallel set the work decomposes into. NEVER spawn a single-task batch for divisible work, or defer work that could have been concurrent.
|
|
34
|
-
-
|
|
35
|
-
- **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates and formatters. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.
|
|
34
|
+
- **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates, formatters, and project-wide build/test/lint. You run them once at the end across the union of changed files — avoids redundant runs and racing formatter passes.
|
|
36
35
|
- No globs, no "update all", no package-wide scope. Fan out.
|
|
37
|
-
-
|
|
36
|
+
- NEVER slow down or serialize because tasks might overlap on some files. Agents resolve collisions among themselves in real time.
|
|
38
37
|
- Pass large payloads via `local://<path>` URIs, not inline. {{#if contextEnabled}} (other than the context){{/if}}
|
|
39
38
|
{{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
|
|
40
39
|
- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
|
|
@@ -12,7 +12,7 @@ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, `n
|
|
|
12
12
|
|`start`|`task`|Mark in progress|
|
|
13
13
|
|`done`|`task` or `phase`|Mark completed|
|
|
14
14
|
|`drop`|`task` or `phase`|Mark abandoned|
|
|
15
|
-
|`rm`|`task` or `phase
|
|
15
|
+
|`rm`|`task` or `phase` (optional)|Remove task or phase's tasks; omit both to clear the entire list|
|
|
16
16
|
|`append`|`phase`, `items: string[]`|Append tasks to `phase`; lazily creates phase|
|
|
17
17
|
|`note`|`task`, `text`|Append a note to a task. Reminders for future-you only.|
|
|
18
18
|
|`view`|—|Read-only: echo the current list without modifying it|
|
package/src/sdk.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getOpenAICodexTransportDetails,
|
|
20
20
|
prewarmOpenAICodexResponses,
|
|
21
21
|
} from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
|
|
22
|
+
import { DEFAULT_MODEL_PER_PROVIDER } from "@oh-my-pi/pi-catalog/provider-models";
|
|
22
23
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
23
24
|
import {
|
|
24
25
|
$env,
|
|
@@ -41,7 +42,6 @@ import { createApiKeyResolver } from "./config/api-key-resolver";
|
|
|
41
42
|
import { shouldEnableAppendOnlyContext } from "./config/append-only-context-mode";
|
|
42
43
|
import { ModelRegistry } from "./config/model-registry";
|
|
43
44
|
import {
|
|
44
|
-
defaultModelPerProvider,
|
|
45
45
|
formatModelString,
|
|
46
46
|
getModelMatchPreferences,
|
|
47
47
|
parseModelPattern,
|
|
@@ -531,11 +531,18 @@ function resolveSnapshotTtlMs(): number {
|
|
|
531
531
|
* override to re-mint access tokens when needed.
|
|
532
532
|
*/
|
|
533
533
|
export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
|
|
534
|
-
const
|
|
534
|
+
const brokerConfigPromise = resolveAuthBrokerConfig();
|
|
535
|
+
const cachePath = getAuthBrokerSnapshotCachePath();
|
|
536
|
+
// Warm the encrypted snapshot cache into the page cache while the broker
|
|
537
|
+
// config resolves (it may shell out for a `!command` token). Decryption
|
|
538
|
+
// needs the resolved token, so the real cache read cannot start earlier.
|
|
539
|
+
void Bun.file(cachePath)
|
|
540
|
+
.arrayBuffer()
|
|
541
|
+
.catch(() => undefined);
|
|
542
|
+
const brokerConfig = await brokerConfigPromise;
|
|
535
543
|
if (brokerConfig) {
|
|
536
544
|
const client = new AuthBrokerClient({ url: brokerConfig.url, token: brokerConfig.token });
|
|
537
545
|
const ttlMs = resolveSnapshotTtlMs();
|
|
538
|
-
const cachePath = getAuthBrokerSnapshotCachePath();
|
|
539
546
|
const persist =
|
|
540
547
|
ttlMs > 0
|
|
541
548
|
? (snapshot: SnapshotResponse): void => {
|
|
@@ -1730,7 +1737,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1730
1737
|
// the winning provider (e.g. anthropic's claude-3-5-sonnet-20240620)
|
|
1731
1738
|
// instead of the intended provider default (claude-sonnet-4-6). Mirrors
|
|
1732
1739
|
// findInitialModel's precedence.
|
|
1733
|
-
for (const [provider, defaultId] of Object.entries(
|
|
1740
|
+
for (const [provider, defaultId] of Object.entries(DEFAULT_MODEL_PER_PROVIDER)) {
|
|
1734
1741
|
const preferred = fallbackCandidates.find(
|
|
1735
1742
|
candidate => candidate.provider === provider && candidate.id === defaultId,
|
|
1736
1743
|
);
|
|
@@ -2342,7 +2349,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2342
2349
|
}
|
|
2343
2350
|
|
|
2344
2351
|
if (model?.api === "openai-codex-responses") {
|
|
2345
|
-
|
|
2352
|
+
// `.api` equality doesn't narrow the generic; the guard makes this cast sound.
|
|
2353
|
+
const codexModel = model as Model<"openai-codex-responses">;
|
|
2346
2354
|
const codexTransport = getOpenAICodexTransportDetails(codexModel, {
|
|
2347
2355
|
sessionId: providerSessionId,
|
|
2348
2356
|
baseUrl: codexModel.baseUrl,
|
|
@@ -2373,12 +2381,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2373
2381
|
}
|
|
2374
2382
|
|
|
2375
2383
|
// Start LSP warmup in the background so startup does not block on language server initialization.
|
|
2376
|
-
//
|
|
2377
|
-
//
|
|
2378
|
-
//
|
|
2379
|
-
//
|
|
2384
|
+
// With `lsp.lazy` (the default) the warmup is skipped: recognized servers are still discovered and
|
|
2385
|
+
// surfaced in the UI as "available", but cold-start on first use — the lsp tool or an edit/write
|
|
2386
|
+
// touching a matching file type — through `getOrCreateClient`.
|
|
2387
|
+
// Print/script invocations (`hasUI=false`) skip it regardless: they don't render the warmup status
|
|
2388
|
+
// indicator AND typically finish before LSP servers would have stabilized — warming them just spends
|
|
2389
|
+
// CPU parsing big `initialize` responses concurrently with the LLM stream consumer, jittering
|
|
2390
|
+
// perceived latency.
|
|
2380
2391
|
let lspServers: CreateAgentSessionResult["lspServers"];
|
|
2381
|
-
if (enableLsp && options.hasUI && settings.get("lsp.
|
|
2392
|
+
if (enableLsp && options.hasUI && settings.get("lsp.lazy")) {
|
|
2393
|
+
lspServers = discoverStartupLspServers(cwd, "available");
|
|
2394
|
+
} else if (enableLsp && options.hasUI) {
|
|
2382
2395
|
lspServers = discoverStartupLspServers(cwd);
|
|
2383
2396
|
if (lspServers.length > 0) {
|
|
2384
2397
|
void (async () => {
|
|
@@ -79,14 +79,14 @@ import {
|
|
|
79
79
|
clearAnthropicFastModeFallback,
|
|
80
80
|
deriveClaudeDeviceId,
|
|
81
81
|
Effort,
|
|
82
|
-
getSupportedEfforts,
|
|
83
82
|
isContextOverflow,
|
|
84
83
|
isUsageLimitError,
|
|
85
|
-
modelsAreEqual,
|
|
86
84
|
parseRateLimitReason,
|
|
87
85
|
resolveServiceTier,
|
|
88
86
|
streamSimple,
|
|
89
87
|
} from "@oh-my-pi/pi-ai";
|
|
88
|
+
import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
89
|
+
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
90
90
|
import { countTokens, MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
|
|
91
91
|
import {
|
|
92
92
|
extractRetryHint,
|
|
@@ -105,7 +105,7 @@ import { classifyDifficulty } from "../auto-thinking/classifier";
|
|
|
105
105
|
import { reset as resetCapabilities } from "../capability";
|
|
106
106
|
import type { Rule } from "../capability/rule";
|
|
107
107
|
import { shouldEnableAppendOnlyContext } from "../config/append-only-context-mode";
|
|
108
|
-
import {
|
|
108
|
+
import type { ModelRegistry } from "../config/model-registry";
|
|
109
109
|
import {
|
|
110
110
|
extractExplicitThinkingSelector,
|
|
111
111
|
formatModelSelectorValue,
|
|
@@ -115,6 +115,7 @@ import {
|
|
|
115
115
|
type ResolvedModelRoleValue,
|
|
116
116
|
resolveModelRoleValue,
|
|
117
117
|
} from "../config/model-resolver";
|
|
118
|
+
import { MODEL_ROLE_IDS } from "../config/model-roles";
|
|
118
119
|
import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates";
|
|
119
120
|
import type { Settings, SkillsSettings } from "../config/settings";
|
|
120
121
|
import { onAppendOnlyModeChanged } from "../config/settings";
|
|
@@ -287,6 +288,20 @@ export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
|
|
|
287
288
|
export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
|
|
288
289
|
|
|
289
290
|
const EMPTY_STOP_MAX_RETRIES = 3;
|
|
291
|
+
const RETRY_BACKOFF_MAX_DELAY_MS = 8_000;
|
|
292
|
+
const RETRY_BACKOFF_JITTER_RATIO = 0.25;
|
|
293
|
+
|
|
294
|
+
function calculateRetryBackoffDelayMs(baseDelayMs: number, attempt: number): number {
|
|
295
|
+
const cappedDelayMs = Math.min(Math.max(0, baseDelayMs) * 2 ** Math.max(0, attempt - 1), RETRY_BACKOFF_MAX_DELAY_MS);
|
|
296
|
+
const jitter = 1 - Math.random() * RETRY_BACKOFF_JITTER_RATIO;
|
|
297
|
+
return cappedDelayMs * jitter;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Slack added past a sibling credential's block expiry before retrying, so
|
|
302
|
+
* the next getApiKey lands after the block has actually lapsed.
|
|
303
|
+
*/
|
|
304
|
+
const SIBLING_UNBLOCK_BUFFER_MS = 1_000;
|
|
290
305
|
const NON_WHITESPACE_RE = /\S/;
|
|
291
306
|
|
|
292
307
|
function hasNonWhitespace(value: string): boolean {
|
|
@@ -8307,13 +8322,16 @@ export class AgentSession {
|
|
|
8307
8322
|
|
|
8308
8323
|
const errorMessage = message.errorMessage || "Unknown error";
|
|
8309
8324
|
const parsedRetryAfterMs = this.#parseRetryAfterMsFromError(errorMessage);
|
|
8310
|
-
let delayMs = retrySettings.baseDelayMs
|
|
8325
|
+
let delayMs = calculateRetryBackoffDelayMs(retrySettings.baseDelayMs, this.#retryAttempt);
|
|
8311
8326
|
let switchedCredential = false;
|
|
8312
8327
|
let switchedModel = false;
|
|
8328
|
+
// Set when a usage-limit error pinned the wait to credential
|
|
8329
|
+
// availability — suppresses the generic retry-after bump below.
|
|
8330
|
+
let usageLimitWaitMs: number | undefined;
|
|
8313
8331
|
|
|
8314
8332
|
if (this.model && isUsageLimitError(errorMessage)) {
|
|
8315
8333
|
const retryAfterMs = parsedRetryAfterMs ?? calculateRateLimitBackoffMs(parseRateLimitReason(errorMessage));
|
|
8316
|
-
const
|
|
8334
|
+
const outcome = await this.#modelRegistry.authStorage.markUsageLimitReached(
|
|
8317
8335
|
this.model.provider,
|
|
8318
8336
|
this.sessionId,
|
|
8319
8337
|
{
|
|
@@ -8321,12 +8339,28 @@ export class AgentSession {
|
|
|
8321
8339
|
baseUrl: this.model.baseUrl,
|
|
8322
8340
|
},
|
|
8323
8341
|
);
|
|
8324
|
-
if (switched) {
|
|
8342
|
+
if (outcome.switched) {
|
|
8325
8343
|
switchedCredential = true;
|
|
8326
8344
|
delayMs = 0;
|
|
8327
|
-
} else
|
|
8328
|
-
// No
|
|
8329
|
-
|
|
8345
|
+
} else {
|
|
8346
|
+
// No sibling credential is usable right now. Wait for whichever
|
|
8347
|
+
// comes first: the provider's retry-after window for the current
|
|
8348
|
+
// account, or the earliest moment a temporarily blocked sibling
|
|
8349
|
+
// frees up (e.g. a 60s post-401 block or a 5-min usage-probe
|
|
8350
|
+
// block) — the next attempt's getApiKey re-ranks and picks it up.
|
|
8351
|
+
// Without this, one short-lived sibling block escalates a
|
|
8352
|
+
// recoverable situation into the provider's multi-hour wait and
|
|
8353
|
+
// trips the fail-fast cap below.
|
|
8354
|
+
usageLimitWaitMs = retryAfterMs;
|
|
8355
|
+
if (outcome.retryAtMs !== undefined) {
|
|
8356
|
+
const siblingWaitMs = Math.max(0, outcome.retryAtMs - Date.now()) + SIBLING_UNBLOCK_BUFFER_MS;
|
|
8357
|
+
if (siblingWaitMs < usageLimitWaitMs) {
|
|
8358
|
+
usageLimitWaitMs = siblingWaitMs;
|
|
8359
|
+
}
|
|
8360
|
+
}
|
|
8361
|
+
if (usageLimitWaitMs > delayMs) {
|
|
8362
|
+
delayMs = usageLimitWaitMs;
|
|
8363
|
+
}
|
|
8330
8364
|
}
|
|
8331
8365
|
}
|
|
8332
8366
|
|
|
@@ -8338,7 +8372,7 @@ export class AgentSession {
|
|
|
8338
8372
|
}
|
|
8339
8373
|
if (switchedModel) {
|
|
8340
8374
|
delayMs = 0;
|
|
8341
|
-
} else if (parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
|
|
8375
|
+
} else if (usageLimitWaitMs === undefined && parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
|
|
8342
8376
|
delayMs = parsedRetryAfterMs;
|
|
8343
8377
|
}
|
|
8344
8378
|
}
|
|
@@ -65,13 +65,42 @@ async function readConfigYaml(): Promise<ConfigSnapshot> {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Process-lifetime memo for {@link resolveAuthBrokerConfig}. Keyed on the env
|
|
70
|
+
* inputs (plus agent dir, which decides which config.yml is read) so tests
|
|
71
|
+
* that flip `OMP_AUTH_BROKER_*` between cases still observe the change, while
|
|
72
|
+
* repeated resolution within one CLI invocation (startup, subagent sessions)
|
|
73
|
+
* skips the config.yml read and any `!command` token resolution.
|
|
74
|
+
*/
|
|
75
|
+
let cachedConfigKey: string | null = null;
|
|
76
|
+
let cachedConfigPromise: Promise<AuthBrokerClientConfig | null> | null = null;
|
|
77
|
+
|
|
68
78
|
/**
|
|
69
79
|
* Read broker configuration. Returns null when the URL is missing
|
|
70
80
|
* (broker disabled — local store is used). Throws when URL is set but no
|
|
71
81
|
* token is available — the caller cannot fall back silently because the
|
|
72
82
|
* user explicitly asked to use the broker.
|
|
83
|
+
*
|
|
84
|
+
* Successful resolutions (including "no broker configured") are memoized for
|
|
85
|
+
* the process lifetime; failures are not, so a missing token can be fixed and
|
|
86
|
+
* retried. Concurrent callers share one in-flight resolution.
|
|
73
87
|
*/
|
|
74
|
-
export
|
|
88
|
+
export function resolveAuthBrokerConfig(): Promise<AuthBrokerClientConfig | null> {
|
|
89
|
+
const key = `${process.env.OMP_AUTH_BROKER_URL ?? ""}\u0000${process.env.OMP_AUTH_BROKER_TOKEN ?? ""}\u0000${getAgentDir()}`;
|
|
90
|
+
if (cachedConfigPromise && cachedConfigKey === key) return cachedConfigPromise;
|
|
91
|
+
const promise = resolveAuthBrokerConfigUncached();
|
|
92
|
+
cachedConfigKey = key;
|
|
93
|
+
cachedConfigPromise = promise;
|
|
94
|
+
promise.catch(() => {
|
|
95
|
+
if (cachedConfigPromise === promise) {
|
|
96
|
+
cachedConfigPromise = null;
|
|
97
|
+
cachedConfigKey = null;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return promise;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function resolveAuthBrokerConfigUncached(): Promise<AuthBrokerClientConfig | null> {
|
|
75
104
|
const envUrl = process.env.OMP_AUTH_BROKER_URL;
|
|
76
105
|
const envToken = process.env.OMP_AUTH_BROKER_TOKEN;
|
|
77
106
|
|
|
@@ -1516,10 +1516,10 @@ class NdjsonFileWriter {
|
|
|
1516
1516
|
}
|
|
1517
1517
|
}
|
|
1518
1518
|
|
|
1519
|
-
/** Get recent sessions for display in welcome screen */
|
|
1519
|
+
/** Get recent sessions for display in welcome screen (which reserves WELCOME_SESSION_SLOTS rows) */
|
|
1520
1520
|
export async function getRecentSessions(
|
|
1521
1521
|
sessionDir: string,
|
|
1522
|
-
limit =
|
|
1522
|
+
limit = 4,
|
|
1523
1523
|
storage: SessionStorage = new FileSessionStorage(),
|
|
1524
1524
|
): Promise<RecentSessionInfo[]> {
|
|
1525
1525
|
const sessions = await getSortedSessions(sessionDir, storage);
|
|
@@ -650,6 +650,7 @@ export class OutputSink {
|
|
|
650
650
|
#sawData = false;
|
|
651
651
|
#truncated = false;
|
|
652
652
|
#lastChunkTime = 0;
|
|
653
|
+
#pendingChunk = "";
|
|
653
654
|
|
|
654
655
|
// Per-line column cap streaming state (persists across `push` calls so a
|
|
655
656
|
// long line split across chunks still trips the same trigger).
|
|
@@ -701,14 +702,20 @@ export class OutputSink {
|
|
|
701
702
|
push(chunk: string): void {
|
|
702
703
|
chunk = sanitizeWithOptionalSixelPassthrough(chunk, sanitizeText);
|
|
703
704
|
|
|
704
|
-
// Throttled onChunk:
|
|
705
|
+
// Throttled onChunk: coalesce chunks arriving inside the throttle window
|
|
706
|
+
// and flush the buffered concatenation on the next eligible tick (plus a
|
|
707
|
+
// final flush in dump()) so the preview never has silent gaps.
|
|
705
708
|
// Live preview gets the raw (pre-cap) chunk so the TUI never lags behind
|
|
706
709
|
// what reached the sink — the column cap is for the persisted LLM view.
|
|
707
710
|
if (this.#onChunk) {
|
|
708
711
|
const now = Date.now();
|
|
709
712
|
if (now - this.#lastChunkTime >= this.#chunkThrottleMs) {
|
|
710
713
|
this.#lastChunkTime = now;
|
|
711
|
-
this.#
|
|
714
|
+
const merged = this.#pendingChunk + chunk;
|
|
715
|
+
this.#pendingChunk = "";
|
|
716
|
+
this.#onChunk(merged);
|
|
717
|
+
} else {
|
|
718
|
+
this.#pendingChunk += chunk;
|
|
712
719
|
}
|
|
713
720
|
}
|
|
714
721
|
|
|
@@ -880,6 +887,11 @@ export class OutputSink {
|
|
|
880
887
|
const sink = Bun.file(this.#artifactPath).writer();
|
|
881
888
|
this.#file = { path: this.#artifactPath, artifactId: this.#artifactId, sink };
|
|
882
889
|
|
|
890
|
+
// Head-retained bytes precede the rolling tail buffer in the capture.
|
|
891
|
+
if (this.#head.length > 0) {
|
|
892
|
+
sink.write(this.#head);
|
|
893
|
+
}
|
|
894
|
+
|
|
883
895
|
// Flush existing buffer to file BEFORE it gets trimmed further.
|
|
884
896
|
if (this.#buffer.length > 0) {
|
|
885
897
|
sink.write(this.#buffer);
|
|
@@ -946,10 +958,19 @@ export class OutputSink {
|
|
|
946
958
|
this.#columnEllipsisAdded = false;
|
|
947
959
|
this.#columnDroppedBytes = 0;
|
|
948
960
|
this.#columnTruncatedLines = 0;
|
|
961
|
+
this.#pendingChunk = "";
|
|
949
962
|
}
|
|
950
963
|
|
|
951
964
|
async dump(notice?: string): Promise<OutputSummary> {
|
|
952
965
|
const noticeLine = notice ? `[${notice}]\n` : "";
|
|
966
|
+
|
|
967
|
+
// Flush any chunk still held back by the throttle so the live preview
|
|
968
|
+
// ends with the complete stream.
|
|
969
|
+
if (this.#onChunk && this.#pendingChunk.length > 0) {
|
|
970
|
+
const pending = this.#pendingChunk;
|
|
971
|
+
this.#pendingChunk = "";
|
|
972
|
+
this.#onChunk(pending);
|
|
973
|
+
}
|
|
953
974
|
const totalLines = this.#sawData ? this.#totalLines + 1 : 0;
|
|
954
975
|
|
|
955
976
|
if (this.#file) await this.#file.sink.end();
|
|
@@ -30,6 +30,7 @@ import { createMarketplaceManager } from "./helpers/marketplace-manager";
|
|
|
30
30
|
import { handleMcpAcp } from "./helpers/mcp";
|
|
31
31
|
import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
|
|
32
32
|
import { handleSshAcp } from "./helpers/ssh";
|
|
33
|
+
import { launchStatsDashboard, parseStatsDashboardArgs } from "./helpers/stats-dashboard";
|
|
33
34
|
import { handleTodoAcp } from "./helpers/todo";
|
|
34
35
|
import { buildUsageReportText } from "./helpers/usage-report";
|
|
35
36
|
import { parseMarketplaceInstallArgs, parsePluginScopeArgs } from "./marketplace-install-parser";
|
|
@@ -542,6 +543,25 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
542
543
|
runtime.ctx.editor.setText("");
|
|
543
544
|
},
|
|
544
545
|
},
|
|
546
|
+
{
|
|
547
|
+
name: "stats",
|
|
548
|
+
description: "Launch the local stats dashboard",
|
|
549
|
+
inlineHint: "[--port <port>]",
|
|
550
|
+
allowArgs: true,
|
|
551
|
+
handle: async (command, runtime) => {
|
|
552
|
+
const parsed = parseStatsDashboardArgs(command.args);
|
|
553
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
554
|
+
|
|
555
|
+
await runtime.output("Syncing session files...");
|
|
556
|
+
try {
|
|
557
|
+
const result = await launchStatsDashboard(parsed);
|
|
558
|
+
await runtime.output(result.message);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
await runtime.output(`Stats dashboard failed: ${errorMessage(error)}`);
|
|
561
|
+
}
|
|
562
|
+
return commandConsumed();
|
|
563
|
+
},
|
|
564
|
+
},
|
|
545
565
|
{
|
|
546
566
|
name: "changelog",
|
|
547
567
|
description: "Show changelog entries",
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as stats from "@oh-my-pi/omp-stats";
|
|
2
|
+
import * as openUtils from "../../utils/open";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_STATS_DASHBOARD_PORT = 3847;
|
|
5
|
+
|
|
6
|
+
interface StatsDashboardServer {
|
|
7
|
+
port: number;
|
|
8
|
+
stop: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StatsDashboardArgs {
|
|
12
|
+
port: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface StatsDashboardLaunchResult {
|
|
16
|
+
url: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let activeStatsServer: StatsDashboardServer | undefined;
|
|
21
|
+
|
|
22
|
+
const STATS_DASHBOARD_USAGE = "Usage: /stats [--port <port>]";
|
|
23
|
+
|
|
24
|
+
function parsePort(value: string | undefined): number | string {
|
|
25
|
+
if (!value) return `Missing port. ${STATS_DASHBOARD_USAGE}`;
|
|
26
|
+
if (!/^\d+$/.test(value)) return `Invalid port: ${value}`;
|
|
27
|
+
const port = Number(value);
|
|
28
|
+
if (!Number.isInteger(port) || port < 0 || port > 65_535) return `Invalid port: ${value}`;
|
|
29
|
+
return port;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function parseStatsDashboardArgs(args: string): StatsDashboardArgs | { error: string } {
|
|
33
|
+
const tokens = args.split(/\s+/).filter(Boolean);
|
|
34
|
+
let port = DEFAULT_STATS_DASHBOARD_PORT;
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
37
|
+
const token = tokens[i];
|
|
38
|
+
if (token === "--port" || token === "-p") {
|
|
39
|
+
const parsed = parsePort(tokens[++i]);
|
|
40
|
+
if (typeof parsed === "string") return { error: parsed };
|
|
41
|
+
port = parsed;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (token.startsWith("--port=")) {
|
|
45
|
+
const parsed = parsePort(token.slice("--port=".length));
|
|
46
|
+
if (typeof parsed === "string") return { error: parsed };
|
|
47
|
+
port = parsed;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return { error: `Unknown option: ${token}. ${STATS_DASHBOARD_USAGE}` };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { port };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function launchStatsDashboard(args: StatsDashboardArgs): Promise<StatsDashboardLaunchResult> {
|
|
57
|
+
const { processed, files } = await stats.syncAllSessions();
|
|
58
|
+
const total = await stats.getTotalMessageCount();
|
|
59
|
+
let requestedPortIgnored = false;
|
|
60
|
+
|
|
61
|
+
if (!activeStatsServer) {
|
|
62
|
+
activeStatsServer = await stats.startServer(args.port);
|
|
63
|
+
} else if (args.port !== activeStatsServer.port) {
|
|
64
|
+
requestedPortIgnored = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const url = `http://localhost:${activeStatsServer.port}`;
|
|
68
|
+
openUtils.openPath(url);
|
|
69
|
+
|
|
70
|
+
const serverLine = requestedPortIgnored
|
|
71
|
+
? `Dashboard already running at: ${url} (requested port ${args.port} ignored)`
|
|
72
|
+
: `Dashboard available at: ${url}`;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
url,
|
|
76
|
+
message: `Synced ${processed} new entries from ${files} files (${total} total)\n${serverLine}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function stopStatsDashboard(): void {
|
|
81
|
+
if (!activeStatsServer) return;
|
|
82
|
+
activeStatsServer.stop();
|
|
83
|
+
activeStatsServer = undefined;
|
|
84
|
+
stats.closeDb();
|
|
85
|
+
}
|
|
@@ -355,6 +355,33 @@ export async function getHostInfoForHost(host: SSHConnectionTarget): Promise<SSH
|
|
|
355
355
|
return await loadHostInfoFromDisk(host);
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Synchronous, probe-free host info lookup for startup paths.
|
|
360
|
+
*
|
|
361
|
+
* Checks the in-memory cache, then falls back to a synchronous read of the
|
|
362
|
+
* persisted host-info cache file. Never opens a connection or probes the
|
|
363
|
+
* remote host — callers get `undefined` when nothing is cached yet.
|
|
364
|
+
*/
|
|
365
|
+
export function getCachedHostInfoSync(host: SSHConnectionTarget): SSHHostInfo | undefined {
|
|
366
|
+
const cached = hostInfoCache.get(host.name);
|
|
367
|
+
if (cached) {
|
|
368
|
+
const resolved = applyCompatOverride(host, cached);
|
|
369
|
+
if (resolved !== cached) hostInfoCache.set(host.name, resolved);
|
|
370
|
+
return resolved;
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
const parsed = parseHostInfo(JSON.parse(fs.readFileSync(getHostInfoPath(host.name), "utf-8")));
|
|
374
|
+
if (!parsed) return undefined;
|
|
375
|
+
const resolved = applyCompatOverride(host, parsed);
|
|
376
|
+
hostInfoCache.set(host.name, resolved);
|
|
377
|
+
return resolved;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
if (isEnoent(err)) return undefined;
|
|
380
|
+
logger.warn("Failed to load SSH host info", { host: host.name, error: String(err) });
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
358
385
|
export async function ensureHostInfo(host: SSHConnectionTarget): Promise<SSHHostInfo> {
|
|
359
386
|
const cached = hostInfoCache.get(host.name);
|
|
360
387
|
if (cached) {
|
package/src/task/commands.ts
CHANGED
|
@@ -120,7 +120,8 @@ export function getCommand(commands: WorkflowCommand[], name: string): WorkflowC
|
|
|
120
120
|
* Replaces $@ with the provided input.
|
|
121
121
|
*/
|
|
122
122
|
export function expandCommand(command: WorkflowCommand, input: string): string {
|
|
123
|
-
|
|
123
|
+
// Function replacement so `$`-patterns in user input ($$, $&, ...) stay literal.
|
|
124
|
+
return command.instructions.replace(/\$@/g, () => input);
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
/**
|
package/src/task/executor.ts
CHANGED
|
@@ -1285,59 +1285,67 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1285
1285
|
|
|
1286
1286
|
const { normalized: normalizedOutputSchema } = normalizeSchema(outputSchema);
|
|
1287
1287
|
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1288
|
+
const sessionPromise = createAgentSession({
|
|
1289
|
+
cwd: worktree ?? cwd,
|
|
1290
|
+
authStorage,
|
|
1291
|
+
modelRegistry,
|
|
1292
|
+
settings: subagentSettings,
|
|
1293
|
+
model,
|
|
1294
|
+
thinkingLevel: effectiveThinkingLevel,
|
|
1295
|
+
toolNames,
|
|
1296
|
+
outputSchema,
|
|
1297
|
+
requireYieldTool: true,
|
|
1298
|
+
contextFiles: options.contextFiles,
|
|
1299
|
+
skills: options.skills,
|
|
1300
|
+
promptTemplates: options.promptTemplates,
|
|
1301
|
+
workspaceTree: options.workspaceTree,
|
|
1302
|
+
rules: options.rules,
|
|
1303
|
+
preloadedExtensionPaths: options.preloadedExtensionPaths,
|
|
1304
|
+
preloadedCustomToolPaths: options.preloadedCustomToolPaths,
|
|
1305
|
+
systemPrompt: defaultPrompt => {
|
|
1306
|
+
const subagentPrompt = prompt.render(subagentSystemPromptTemplate, {
|
|
1307
|
+
agent: agent.systemPrompt,
|
|
1308
|
+
context: options.context?.trim() ?? "",
|
|
1309
|
+
planReference: options.planReference?.content ?? "",
|
|
1310
|
+
planReferencePath: options.planReference?.path ?? "",
|
|
1311
|
+
worktree: worktree ?? "",
|
|
1312
|
+
outputSchema: normalizedOutputSchema,
|
|
1313
|
+
contextFile: contextFileForPrompt,
|
|
1314
|
+
ircPeers: ircEnabled ? renderIrcPeerRoster(id) : "",
|
|
1315
|
+
ircSelfId: ircEnabled ? id : "",
|
|
1316
|
+
});
|
|
1317
|
+
return defaultPrompt.length === 0
|
|
1318
|
+
? [subagentPrompt]
|
|
1319
|
+
: [...defaultPrompt.slice(0, -1), subagentPrompt, defaultPrompt[defaultPrompt.length - 1]];
|
|
1320
|
+
},
|
|
1321
|
+
sessionManager,
|
|
1322
|
+
hasUI: false,
|
|
1323
|
+
spawns: spawnsEnv,
|
|
1324
|
+
taskDepth: childDepth,
|
|
1325
|
+
parentHindsightSessionState: options.parentHindsightSessionState,
|
|
1326
|
+
parentMnemopiSessionState: options.parentMnemopiSessionState,
|
|
1327
|
+
parentTaskPrefix: id,
|
|
1328
|
+
agentId: id,
|
|
1329
|
+
agentDisplayName: agent.name,
|
|
1330
|
+
enableLsp: lspEnabled,
|
|
1331
|
+
skipPythonPreflight,
|
|
1332
|
+
enableMCP,
|
|
1333
|
+
mcpManager: options.mcpManager,
|
|
1334
|
+
customTools: mcpProxyTools.length > 0 ? mcpProxyTools : undefined,
|
|
1335
|
+
localProtocolOptions: options.localProtocolOptions,
|
|
1336
|
+
telemetry: subagentTelemetry,
|
|
1337
|
+
parentEvalSessionId: options.parentEvalSessionId,
|
|
1338
|
+
});
|
|
1339
|
+
let session: AgentSession;
|
|
1340
|
+
try {
|
|
1341
|
+
({ session } = await awaitAbortable(sessionPromise));
|
|
1342
|
+
} catch (err) {
|
|
1343
|
+
// Abort raced session startup. The session may still resolve later
|
|
1344
|
+
// holding live LSP/MCP child processes — dispose it when it does so
|
|
1345
|
+
// a cancelled subagent cannot leak them.
|
|
1346
|
+
void sessionPromise.then(created => created.session.dispose()).catch(() => {});
|
|
1347
|
+
throw err;
|
|
1348
|
+
}
|
|
1341
1349
|
|
|
1342
1350
|
activeSession = session;
|
|
1343
1351
|
|