@heidi-dang/oh-my-opencode 3.12.5 → 3.13.1
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/README.md +61 -367
- package/dist/agents/atlas/agent.d.ts +1 -1
- package/dist/agents/atlas/default.d.ts +1 -1
- package/dist/agents/atlas/gemini.d.ts +1 -1
- package/dist/agents/atlas/gpt.d.ts +1 -1
- package/dist/agents/atlas/prompt-section-builder.d.ts +1 -1
- package/dist/agents/builtin-agents/atlas-agent.d.ts +4 -1
- package/dist/agents/builtin-agents/available-skills.d.ts +1 -1
- package/dist/agents/builtin-agents/general-agents.d.ts +4 -1
- package/dist/agents/builtin-agents/hephaestus-agent.d.ts +5 -1
- package/dist/agents/builtin-agents/model-resolution.d.ts +2 -0
- package/dist/agents/builtin-agents/resolve-file-uri.test.d.ts +1 -0
- package/dist/agents/builtin-agents/sisyphus-agent.d.ts +4 -1
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/chat.d.ts +7 -0
- package/dist/agents/dynamic-agent-prompt-builder.test.d.ts +1 -0
- package/dist/agents/env-context.test.d.ts +1 -0
- package/dist/agents/hephaestus/agent.d.ts +19 -0
- package/dist/agents/hephaestus/agent.test.d.ts +1 -0
- package/dist/agents/hephaestus/gpt-5-3-codex.d.ts +21 -0
- package/dist/agents/hephaestus/gpt-5-4.d.ts +3 -0
- package/dist/agents/hephaestus/gpt.d.ts +3 -0
- package/dist/agents/hephaestus/index.d.ts +2 -0
- package/dist/agents/hephaestus.d.ts +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/metis.d.ts +1 -1
- package/dist/agents/momus.d.ts +1 -1
- package/dist/agents/momus.test.d.ts +1 -0
- package/dist/agents/prometheus-prompt.test.d.ts +1 -0
- package/dist/agents/prompts/agent-role.d.ts +2 -0
- package/dist/agents/prompts/anti-patterns.d.ts +2 -0
- package/dist/agents/prompts/base-system.d.ts +1 -0
- package/dist/agents/prompts/execution-rules.d.ts +2 -0
- package/dist/agents/prompts/hard-blocks.d.ts +2 -0
- package/dist/agents/prompts/index.d.ts +6 -0
- package/dist/agents/{dynamic-agent-prompt-builder.d.ts → prompts/orchestration.d.ts} +2 -23
- package/dist/agents/prompts/skill-context.d.ts +2 -0
- package/dist/agents/runtime/action-validator.d.ts +31 -0
- package/dist/agents/runtime/agent-logger.d.ts +15 -0
- package/dist/agents/runtime/loop-guard.d.ts +2 -0
- package/dist/agents/runtime/verify-action.d.ts +58 -0
- package/dist/agents/sisyphus/default.d.ts +9 -0
- package/dist/agents/sisyphus/gemini.d.ts +20 -0
- package/dist/agents/sisyphus/gpt-5-4.d.ts +16 -0
- package/dist/agents/sisyphus/index.d.ts +11 -0
- package/dist/agents/sisyphus-junior/index.test.d.ts +1 -0
- package/dist/agents/sisyphus.d.ts +1 -1
- package/dist/agents/tool-restrictions.test.d.ts +1 -0
- package/dist/agents/types.d.ts +24 -6
- package/dist/agents/types.test.d.ts +1 -0
- package/dist/agents/utils.test.d.ts +1 -0
- package/dist/cli/cli-installer.test.d.ts +1 -0
- package/dist/cli/config-manager/write-omo-config.test.d.ts +1 -0
- package/dist/cli/config-manager.test.d.ts +1 -0
- package/dist/cli/doctor/checks/config.test.d.ts +1 -0
- package/dist/cli/doctor/checks/dependencies.test.d.ts +1 -0
- package/dist/cli/doctor/checks/edit-atomicity.d.ts +5 -0
- package/dist/cli/doctor/checks/issue-resolution.d.ts +7 -0
- package/dist/cli/doctor/checks/model-resolution.test.d.ts +1 -0
- package/dist/cli/doctor/checks/plan-compiler.d.ts +8 -0
- package/dist/cli/doctor/checks/progress.d.ts +8 -0
- package/dist/cli/doctor/checks/run-state-watchdog.d.ts +2 -0
- package/dist/cli/doctor/checks/system-loaded-version.test.d.ts +1 -0
- package/dist/cli/doctor/checks/system.test.d.ts +1 -0
- package/dist/cli/doctor/checks/tool-contract.d.ts +8 -0
- package/dist/cli/doctor/checks/tool-metadata.d.ts +2 -0
- package/dist/cli/doctor/constants.d.ts +3 -0
- package/dist/cli/doctor/format-default.test.d.ts +1 -0
- package/dist/cli/doctor/formatter.test.d.ts +1 -0
- package/dist/cli/doctor/runner.test.d.ts +1 -0
- package/dist/cli/doctor/types.d.ts +3 -0
- package/dist/cli/index.js +2523 -221
- package/dist/cli/index.test.d.ts +1 -0
- package/dist/cli/install.test.d.ts +1 -0
- package/dist/cli/mcp-oauth/index.test.d.ts +1 -0
- package/dist/cli/mcp-oauth/login.test.d.ts +1 -0
- package/dist/cli/mcp-oauth/logout.test.d.ts +1 -0
- package/dist/cli/mcp-oauth/status.test.d.ts +1 -0
- package/dist/cli/model-fallback.test.d.ts +1 -0
- package/dist/cli/provider-model-id-transform.test.d.ts +1 -0
- package/dist/cli/run/completion-continuation.test.d.ts +1 -0
- package/dist/cli/run/completion-verbose-logging.test.d.ts +1 -0
- package/dist/cli/run/completion.test.d.ts +1 -0
- package/dist/cli/run/continuation-state-marker.test.d.ts +1 -0
- package/dist/cli/run/event-handlers.test.d.ts +1 -0
- package/dist/cli/run/events.test.d.ts +1 -0
- package/dist/cli/run/integration.test.d.ts +1 -0
- package/dist/cli/run/json-output.test.d.ts +1 -0
- package/dist/cli/run/message-part-delta.test.d.ts +1 -0
- package/dist/cli/run/on-complete-hook.test.d.ts +1 -0
- package/dist/cli/run/opencode-binary-resolver.test.d.ts +1 -0
- package/dist/cli/run/poll-for-completion.test.d.ts +1 -0
- package/dist/cli/run/runner.test.d.ts +1 -0
- package/dist/cli/run/server-connection.test.d.ts +1 -0
- package/dist/cli/run/session-resolver.test.d.ts +1 -0
- package/dist/cli/run/stdin-suppression.test.d.ts +1 -0
- package/dist/cli/run/timestamp-output.test.d.ts +1 -0
- package/dist/config/schema/agent-names.d.ts +6 -3
- package/dist/config/schema/agent-overrides.d.ts +150 -30
- package/dist/config/schema/background-task.test.d.ts +1 -0
- package/dist/config/schema/categories.d.ts +21 -3
- package/dist/config/schema/categories.test.d.ts +1 -0
- package/dist/config/schema/fallback-models.d.ts +11 -1
- package/dist/config/schema/hooks.d.ts +7 -0
- package/dist/config/schema/oh-my-opencode-config.d.ts +151 -29
- package/dist/config/schema.test.d.ts +1 -0
- package/dist/create-hooks.d.ts +11 -1
- package/dist/create-managers.d.ts +2 -0
- package/dist/create-tools.d.ts +1 -1
- package/dist/features/background-agent/compaction-aware-message-resolver.test.d.ts +1 -0
- package/dist/features/background-agent/concurrency.test.d.ts +1 -0
- package/dist/features/background-agent/error-classifier.test.d.ts +1 -0
- package/dist/features/background-agent/fallback-retry-handler.test.d.ts +1 -0
- package/dist/features/background-agent/manager.d.ts +13 -0
- package/dist/features/background-agent/manager.polling.test.d.ts +1 -0
- package/dist/features/background-agent/manager.test.d.ts +1 -0
- package/dist/features/background-agent/process-cleanup.test.d.ts +1 -0
- package/dist/features/background-agent/session-idle-event-handler.test.d.ts +1 -0
- package/dist/features/background-agent/spawner/parent-directory-resolver.test.d.ts +1 -0
- package/dist/features/background-agent/spawner.test.d.ts +1 -0
- package/dist/features/background-agent/task-history.test.d.ts +1 -0
- package/dist/features/background-agent/task-poller.test.d.ts +1 -0
- package/dist/features/background-agent/types.d.ts +9 -4
- package/dist/features/boulder-state/storage.test.d.ts +1 -0
- package/dist/features/builtin-commands/commands.test.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.test.d.ts +1 -0
- package/dist/features/builtin-skills/skills/git/git-commit.d.ts +2 -0
- package/dist/features/builtin-skills/skills/git/git-history.d.ts +1 -0
- package/dist/features/builtin-skills/skills/git/git-push-pr.d.ts +1 -0
- package/dist/features/builtin-skills/skills/git/git-rebase.d.ts +1 -0
- package/dist/features/builtin-skills/skills/git/git-shared.d.ts +2 -0
- package/dist/features/builtin-skills/skills.test.d.ts +1 -0
- package/dist/features/claude-code-mcp-loader/loader.test.d.ts +1 -0
- package/dist/features/claude-code-session-state/state.d.ts +4 -0
- package/dist/features/claude-code-session-state/state.test.d.ts +1 -0
- package/dist/features/claude-tasks/session-storage.test.d.ts +1 -0
- package/dist/features/claude-tasks/storage.test.d.ts +1 -0
- package/dist/features/claude-tasks/types.test.d.ts +1 -0
- package/dist/features/context-injector/collector.test.d.ts +1 -0
- package/dist/features/context-injector/injector.test.d.ts +1 -0
- package/dist/features/hook-message-injector/injector.d.ts +2 -0
- package/dist/features/hook-message-injector/injector.test.d.ts +1 -0
- package/dist/features/issue-resolution/state.d.ts +12 -0
- package/dist/features/issue-resolution/tests/integration.test.d.ts +1 -0
- package/dist/features/mcp-oauth/callback-server.test.d.ts +1 -0
- package/dist/features/mcp-oauth/dcr.test.d.ts +1 -0
- package/dist/features/mcp-oauth/discovery.test.d.ts +1 -0
- package/dist/features/mcp-oauth/provider.test.d.ts +1 -0
- package/dist/features/mcp-oauth/resource-indicator.test.d.ts +1 -0
- package/dist/features/mcp-oauth/schema.test.d.ts +1 -0
- package/dist/features/mcp-oauth/step-up.test.d.ts +1 -0
- package/dist/features/mcp-oauth/storage.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/agents-skills-global.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/async-loader.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/blocking.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/config-source-discovery.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/loader.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/merger.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/skill-content.test.d.ts +1 -0
- package/dist/features/pr-state/storage.d.ts +10 -0
- package/dist/features/run-continuation-state/storage.test.d.ts +1 -0
- package/dist/features/run-state-watchdog/index.d.ts +1 -0
- package/dist/features/run-state-watchdog/manager.d.ts +31 -0
- package/dist/features/run-state-watchdog/manager.test.d.ts +1 -0
- package/dist/features/run-state-watchdog/reconnect.test.d.ts +1 -0
- package/dist/features/skill-mcp-manager/env-cleaner.test.d.ts +1 -0
- package/dist/features/skill-mcp-manager/manager.test.d.ts +1 -0
- package/dist/features/task-toast-manager/manager.d.ts +9 -0
- package/dist/features/task-toast-manager/manager.test.d.ts +1 -0
- package/dist/features/task-toast-manager/types.d.ts +5 -0
- package/dist/features/tmux-subagent/action-executor.test.d.ts +1 -0
- package/dist/features/tmux-subagent/decision-engine.test.d.ts +1 -0
- package/dist/features/tmux-subagent/layout-config.test.d.ts +1 -0
- package/dist/features/tmux-subagent/manager.test.d.ts +1 -0
- package/dist/features/tmux-subagent/polling-manager.test.d.ts +1 -0
- package/dist/features/tool-metadata-store/index.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/executor.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/parser.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/recovery-deduplication.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/recovery-hook.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/storage.test.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/summarize-retry-strategy.test.d.ts +1 -0
- package/dist/hooks/anthropic-effort/index.test.d.ts +1 -0
- package/dist/hooks/atlas/index.test.d.ts +1 -0
- package/dist/hooks/auto-slash-command/constants.test.d.ts +1 -0
- package/dist/hooks/auto-slash-command/detector.test.d.ts +1 -0
- package/dist/hooks/auto-slash-command/executor.test.d.ts +1 -0
- package/dist/hooks/auto-slash-command/index.test.d.ts +1 -0
- package/dist/hooks/auto-update-checker/checker/pinned-version-updater.test.d.ts +1 -0
- package/dist/hooks/auto-update-checker/checker/plugin-entry.test.d.ts +1 -0
- package/dist/hooks/auto-update-checker/checker.test.d.ts +1 -0
- package/dist/hooks/auto-update-checker/hook/background-update-check.test.d.ts +1 -0
- package/dist/hooks/auto-update-checker/hook.test.d.ts +1 -0
- package/dist/hooks/auto-update-checker/index.test.d.ts +1 -0
- package/dist/hooks/category-skill-reminder/formatter.d.ts +1 -1
- package/dist/hooks/category-skill-reminder/hook.d.ts +1 -1
- package/dist/hooks/category-skill-reminder/index.test.d.ts +1 -0
- package/dist/hooks/claude-code-hooks/execute-http-hook.test.d.ts +1 -0
- package/dist/hooks/claude-code-hooks/stop.test.d.ts +1 -0
- package/dist/hooks/claude-code-hooks/transcript.test.d.ts +1 -0
- package/dist/hooks/claude-code-hooks/user-prompt-submit.test.d.ts +1 -0
- package/dist/hooks/comment-checker/cli.test.d.ts +1 -0
- package/dist/hooks/comment-checker/hook.apply-patch.test.d.ts +1 -0
- package/dist/hooks/comment-checker/pending-calls.test.d.ts +1 -0
- package/dist/hooks/compaction-context-injector/index.test.d.ts +1 -0
- package/dist/hooks/compaction-todo-preserver/index.test.d.ts +1 -0
- package/dist/hooks/context-window-monitor.test.d.ts +1 -0
- package/dist/hooks/delegate-task-retry/index.test.d.ts +1 -0
- package/dist/hooks/directory-agents-injector/injector.test.d.ts +1 -0
- package/dist/hooks/directory-readme-injector/injector.test.d.ts +1 -0
- package/dist/hooks/edit-error-recovery/hook.d.ts +2 -1
- package/dist/hooks/edit-error-recovery/index.test.d.ts +1 -0
- package/dist/hooks/edit-safeguard/hook.d.ts +2 -0
- package/dist/hooks/execution-journal/hook.d.ts +19 -0
- package/dist/hooks/execution-journal/index.d.ts +1 -0
- package/dist/hooks/hashline-read-enhancer/index.test.d.ts +1 -0
- package/dist/hooks/index.d.ts +11 -3
- package/dist/hooks/json-error-recovery/index.test.d.ts +1 -0
- package/dist/hooks/keyword-detector/constants.d.ts +1 -0
- package/dist/hooks/keyword-detector/detector.d.ts +1 -1
- package/dist/hooks/keyword-detector/index.test.d.ts +1 -0
- package/dist/hooks/keyword-detector/issue.d.ts +2 -0
- package/dist/hooks/model-fallback/hook.test.d.ts +1 -0
- package/dist/hooks/no-hephaestus-non-gpt/index.test.d.ts +1 -0
- package/dist/hooks/no-sisyphus-gpt/index.test.d.ts +1 -0
- package/dist/hooks/non-interactive-env/index.test.d.ts +1 -0
- package/dist/hooks/plan-enforcement/hook.d.ts +29 -0
- package/dist/hooks/plan-enforcement/hook.test.d.ts +1 -0
- package/dist/hooks/plan-enforcement/index.d.ts +1 -0
- package/dist/hooks/preemptive-compaction.test.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/index.test.d.ts +1 -0
- package/dist/hooks/question-label-truncator/index.test.d.ts +1 -0
- package/dist/hooks/ralph-loop/completion-handler.d.ts +14 -0
- package/dist/hooks/ralph-loop/completion-promise-detector.d.ts +1 -1
- package/dist/hooks/ralph-loop/completion-promise-detector.test.d.ts +1 -0
- package/dist/hooks/ralph-loop/constants.d.ts +1 -0
- package/dist/hooks/ralph-loop/index.test.d.ts +1 -0
- package/dist/hooks/ralph-loop/loop-state-controller.d.ts +2 -0
- package/dist/hooks/ralph-loop/ralph-loop-event-handler.d.ts +2 -0
- package/dist/hooks/ralph-loop/reset-strategy-race-condition.test.d.ts +1 -0
- package/dist/hooks/ralph-loop/session-event-handler.d.ts +12 -0
- package/dist/hooks/ralph-loop/types.d.ts +5 -1
- package/dist/hooks/ralph-loop/ulw-loop-verification.test.d.ts +1 -0
- package/dist/hooks/read-image-resizer/hook.test.d.ts +1 -0
- package/dist/hooks/read-image-resizer/image-dimensions.test.d.ts +1 -0
- package/dist/hooks/read-image-resizer/image-resizer.test.d.ts +1 -0
- package/dist/hooks/rules-injector/finder.test.d.ts +1 -0
- package/dist/hooks/rules-injector/injector.test.d.ts +1 -0
- package/dist/hooks/rules-injector/output-path.test.d.ts +1 -0
- package/dist/hooks/rules-injector/parser.test.d.ts +1 -0
- package/dist/hooks/run-state-watchdog/hook.d.ts +5 -0
- package/dist/hooks/run-state-watchdog/index.d.ts +1 -0
- package/dist/hooks/runtime-enforcement/hook.d.ts +10 -0
- package/dist/hooks/runtime-enforcement/hook.test.d.ts +1 -0
- package/dist/hooks/runtime-enforcement/index.d.ts +1 -0
- package/dist/hooks/runtime-enforcement/synthetic-injection.test.d.ts +1 -0
- package/dist/hooks/runtime-fallback/index.test.d.ts +1 -0
- package/dist/hooks/semantic-loop-guard/hook.d.ts +10 -0
- package/dist/hooks/semantic-loop-guard/hook.test.d.ts +1 -0
- package/dist/hooks/semantic-loop-guard/index.d.ts +1 -0
- package/dist/hooks/session-notification-input-needed.test.d.ts +1 -0
- package/dist/hooks/session-notification.test.d.ts +1 -0
- package/dist/hooks/session-recovery/detect-error-type.test.d.ts +1 -0
- package/dist/hooks/session-recovery/index.test.d.ts +1 -0
- package/dist/hooks/session-recovery/recover-empty-content-message-sdk.test.d.ts +1 -0
- package/dist/hooks/session-recovery/resume.test.d.ts +1 -0
- package/dist/hooks/session-recovery/storage/readers-from-sdk.test.d.ts +1 -0
- package/dist/hooks/start-work/index.test.d.ts +1 -0
- package/dist/hooks/start-work/parse-user-request.test.d.ts +1 -0
- package/dist/hooks/start-work/worktree-detector.test.d.ts +1 -0
- package/dist/hooks/stop-continuation-guard/index.test.d.ts +1 -0
- package/dist/hooks/task-reminder/index.test.d.ts +1 -0
- package/dist/hooks/task-resume-info/index.test.d.ts +1 -0
- package/dist/hooks/tasks-todowrite-disabler/index.test.d.ts +1 -0
- package/dist/hooks/think-mode/index.test.d.ts +1 -0
- package/dist/hooks/think-mode/switcher.test.d.ts +1 -0
- package/dist/hooks/todo-continuation-enforcer/continuation-injection.test.d.ts +1 -0
- package/dist/hooks/todo-continuation-enforcer/pending-question-detection.test.d.ts +1 -0
- package/dist/hooks/todo-continuation-enforcer/todo-continuation-enforcer.test.d.ts +1 -0
- package/dist/hooks/tool-contract/hook.d.ts +19 -0
- package/dist/hooks/tool-contract/hook.test.d.ts +1 -0
- package/dist/hooks/tool-contract/index.d.ts +1 -0
- package/dist/hooks/tool-output-truncator.test.d.ts +1 -0
- package/dist/hooks/unstable-agent-babysitter/index.test.d.ts +1 -0
- package/dist/hooks/write-existing-file-guard/index.test.d.ts +1 -0
- package/dist/hooks/xai-usage-patch/hook.d.ts +20 -0
- package/dist/hooks/xai-usage-patch/hook.test.d.ts +1 -0
- package/dist/index.compaction-model-agnostic.static.test.d.ts +1 -0
- package/dist/index.js +4990 -14470
- package/dist/index.test.d.ts +1 -0
- package/dist/mcp/index.test.d.ts +1 -0
- package/dist/mcp/websearch.test.d.ts +1 -0
- package/dist/oh-my-opencode.schema.json +622 -15
- package/dist/plugin/available-categories.d.ts +1 -1
- package/dist/plugin/chat-headers.test.d.ts +1 -0
- package/dist/plugin/chat-message.test.d.ts +1 -0
- package/dist/plugin/chat-params.test.d.ts +1 -0
- package/dist/plugin/event.model-fallback.test.d.ts +1 -0
- package/dist/plugin/event.test.d.ts +1 -0
- package/dist/plugin/hooks/create-core-hooks.d.ts +10 -0
- package/dist/plugin/hooks/create-session-hooks.d.ts +4 -1
- package/dist/plugin/hooks/create-skill-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +7 -1
- package/dist/plugin/recent-synthetic-idles.test.d.ts +1 -0
- package/dist/plugin/session-agent-resolver.test.d.ts +1 -0
- package/dist/plugin/session-status-normalizer.test.d.ts +1 -0
- package/dist/plugin/skill-context.d.ts +1 -1
- package/dist/plugin/tool-execute-before-session-notification.test.d.ts +1 -0
- package/dist/plugin/tool-execute-before.test.d.ts +1 -0
- package/dist/plugin/tool-normalization.test.d.ts +1 -0
- package/dist/plugin/tool-registry.d.ts +1 -1
- package/dist/plugin/truth-model-integration.test.d.ts +1 -0
- package/dist/plugin/ultrawork-db-model-override.test.d.ts +1 -0
- package/dist/plugin/ultrawork-model-override.test.d.ts +1 -0
- package/dist/plugin-config.test.d.ts +1 -0
- package/dist/plugin-handlers/agent-key-remapper.test.d.ts +1 -0
- package/dist/plugin-handlers/config-handler-formatter.test.d.ts +1 -0
- package/dist/plugin-handlers/config-handler.test.d.ts +1 -0
- package/dist/plugin-handlers/mcp-config-handler.test.d.ts +1 -0
- package/dist/plugin-handlers/plan-model-inheritance.test.d.ts +1 -0
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +3 -2
- package/dist/plugin-handlers/tool-config-handler.test.d.ts +1 -0
- package/dist/runtime/journal.d.ts +33 -0
- package/dist/runtime/plan-compiler.d.ts +20 -0
- package/dist/runtime/plan-compiler.test.d.ts +1 -0
- package/dist/runtime/startup-validation.d.ts +7 -0
- package/dist/runtime/state-ledger.d.ts +52 -0
- package/dist/runtime/tools/complete-task.d.ts +4 -0
- package/dist/runtime/tools/fs-safe.contract.test.d.ts +1 -0
- package/dist/runtime/tools/fs-safe.d.ts +1 -0
- package/dist/runtime/tools/gh-safe.d.ts +1 -0
- package/dist/runtime/tools/git-safe.d.ts +1 -0
- package/dist/runtime/tools/index.d.ts +8 -0
- package/dist/runtime/tools/plan.d.ts +3 -0
- package/dist/runtime/tools/query-ledger.d.ts +3 -0
- package/dist/runtime/tools/registry.d.ts +8 -0
- package/dist/runtime/tools/report-issue-verification.d.ts +1 -0
- package/dist/runtime/tools/verify.d.ts +1 -0
- package/dist/runtime/truth-model.test.d.ts +1 -0
- package/dist/runtime/vcs-detection.test.d.ts +1 -0
- package/dist/shared/active-task-storage.d.ts +15 -0
- package/dist/shared/agent-config-integration.test.d.ts +1 -0
- package/dist/shared/agent-display-names.test.d.ts +1 -0
- package/dist/shared/agent-variant.test.d.ts +1 -0
- package/dist/shared/claude-config-dir.test.d.ts +1 -0
- package/dist/shared/connected-providers-cache.test.d.ts +1 -0
- package/dist/shared/deep-merge.test.d.ts +1 -0
- package/dist/shared/dynamic-truncator.test.d.ts +1 -0
- package/dist/shared/external-plugin-detector.test.d.ts +1 -0
- package/dist/shared/file-utils.test.d.ts +1 -0
- package/dist/shared/first-message-variant.test.d.ts +1 -0
- package/dist/shared/frontmatter.test.d.ts +1 -0
- package/dist/shared/git-worktree/collect-git-diff-stats.test.d.ts +1 -0
- package/dist/shared/git-worktree/git-worktree.test.d.ts +1 -0
- package/dist/shared/git-worktree/parse-status-porcelain-line.test.d.ts +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/jsonc-parser.test.d.ts +1 -0
- package/dist/shared/merge-categories.test.d.ts +1 -0
- package/dist/shared/migration.test.d.ts +1 -0
- package/dist/shared/model-availability.d.ts +6 -0
- package/dist/shared/model-availability.test.d.ts +1 -0
- package/dist/shared/model-error-classifier.d.ts +5 -0
- package/dist/shared/model-error-classifier.test.d.ts +1 -0
- package/dist/shared/model-format-normalizer.test.d.ts +1 -0
- package/dist/shared/model-normalization.test.d.ts +1 -0
- package/dist/shared/model-requirements.d.ts +6 -0
- package/dist/shared/model-requirements.test.d.ts +1 -0
- package/dist/shared/model-resolution-acceptance.test.d.ts +1 -0
- package/dist/shared/model-resolution-pipeline.d.ts +5 -1
- package/dist/shared/model-resolution-pipeline.test.d.ts +1 -0
- package/dist/shared/model-resolution-tracker.d.ts +17 -0
- package/dist/shared/model-resolution-types.d.ts +7 -1
- package/dist/shared/model-resolver.d.ts +10 -3
- package/dist/shared/model-resolver.test.d.ts +1 -0
- package/dist/shared/model-suggestion-retry.test.d.ts +1 -0
- package/dist/shared/normalize-sdk-response.test.d.ts +1 -0
- package/dist/shared/opencode-config-dir.test.d.ts +1 -0
- package/dist/shared/opencode-http-api.test.d.ts +1 -0
- package/dist/shared/opencode-message-dir.test.d.ts +1 -0
- package/dist/shared/opencode-server-auth.test.d.ts +1 -0
- package/dist/shared/opencode-storage-detection.test.d.ts +1 -0
- package/dist/shared/opencode-version.test.d.ts +1 -0
- package/dist/shared/pattern-matcher.test.d.ts +1 -0
- package/dist/shared/permission-compat.test.d.ts +1 -0
- package/dist/shared/plugin-command-discovery.test.d.ts +1 -0
- package/dist/shared/port-utils.test.d.ts +1 -0
- package/dist/shared/prompt-tools.test.d.ts +1 -0
- package/dist/shared/read-permission-tracker.d.ts +15 -0
- package/dist/shared/safe-create-hook.test.d.ts +1 -0
- package/dist/shared/session-cursor.test.d.ts +1 -0
- package/dist/shared/session-directory-resolver.test.d.ts +1 -0
- package/dist/shared/session-model-state.test.d.ts +1 -0
- package/dist/shared/session-tools-store.test.d.ts +1 -0
- package/dist/shared/shell-env.test.d.ts +1 -0
- package/dist/shared/skill-path-resolver.test.d.ts +1 -0
- package/dist/shared/system-directive.test.d.ts +1 -0
- package/dist/shared/tmux/tmux-utils/layout.test.d.ts +1 -0
- package/dist/shared/tmux/tmux-utils.test.d.ts +1 -0
- package/dist/shared/tool-name.test.d.ts +1 -0
- package/dist/shared/truncate-description.test.d.ts +1 -0
- package/dist/shared/verify-task-completion.d.ts +4 -0
- package/dist/tools/background-task/create-background-output.blocking.test.d.ts +1 -0
- package/dist/tools/background-task/create-background-task.test.d.ts +1 -0
- package/dist/tools/background-task/tools.test.d.ts +1 -0
- package/dist/tools/call-omo-agent/background-agent-executor.test.d.ts +1 -0
- package/dist/tools/call-omo-agent/background-executor.test.d.ts +1 -0
- package/dist/tools/call-omo-agent/session-creator.test.d.ts +1 -0
- package/dist/tools/call-omo-agent/subagent-session-creator.test.d.ts +1 -0
- package/dist/tools/call-omo-agent/sync-executor.test.d.ts +1 -0
- package/dist/tools/call-omo-agent/tools.test.d.ts +1 -0
- package/dist/tools/delegate-task/background-continuation.test.d.ts +1 -0
- package/dist/tools/delegate-task/background-task.d.ts +1 -1
- package/dist/tools/delegate-task/background-task.test.d.ts +1 -0
- package/dist/tools/delegate-task/category-resolver.d.ts +1 -0
- package/dist/tools/delegate-task/category-resolver.test.d.ts +1 -0
- package/dist/tools/delegate-task/constants.d.ts +1 -1
- package/dist/tools/delegate-task/metadata-await.test.d.ts +1 -0
- package/dist/tools/delegate-task/metadata-model-unification.test.d.ts +1 -0
- package/dist/tools/delegate-task/model-selection.d.ts +2 -0
- package/dist/tools/delegate-task/subagent-resolver.d.ts +2 -1
- package/dist/tools/delegate-task/subagent-resolver.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-continuation.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-poll-timeout.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-prompt-sender.d.ts +1 -0
- package/dist/tools/delegate-task/sync-prompt-sender.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-result-fetcher.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-poller.d.ts +14 -0
- package/dist/tools/delegate-task/sync-session-poller.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-task-completion-verification.test.d.ts +1 -0
- package/dist/tools/delegate-task/sync-task.d.ts +1 -1
- package/dist/tools/delegate-task/sync-task.test.d.ts +1 -0
- package/dist/tools/delegate-task/timing.test.d.ts +1 -0
- package/dist/tools/delegate-task/token-limiter.test.d.ts +1 -0
- package/dist/tools/delegate-task/tools.test.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +1 -1
- package/dist/tools/delegate-task/unstable-agent-task.test.d.ts +1 -0
- package/dist/tools/delegate-task/unstable-agent-timeout.test.d.ts +1 -0
- package/dist/tools/glob/cli.test.d.ts +1 -0
- package/dist/tools/grep/downloader.test.d.ts +1 -0
- package/dist/tools/grep/result-formatter.test.d.ts +1 -0
- package/dist/tools/hashline-edit/diff-utils.test.d.ts +1 -0
- package/dist/tools/hashline-edit/edit-operations.test.d.ts +1 -0
- package/dist/tools/hashline-edit/edit-text-normalization.test.d.ts +1 -0
- package/dist/tools/hashline-edit/hash-computation.test.d.ts +1 -0
- package/dist/tools/hashline-edit/normalize-edits.test.d.ts +1 -0
- package/dist/tools/hashline-edit/tools.test.d.ts +1 -0
- package/dist/tools/hashline-edit/validation.test.d.ts +1 -0
- package/dist/tools/look-at/create-look-at-error-handling.test.d.ts +1 -0
- package/dist/tools/look-at/create-look-at-image-data.test.d.ts +1 -0
- package/dist/tools/look-at/create-look-at-model-passthrough.test.d.ts +1 -0
- package/dist/tools/look-at/create-look-at-sync-prompt.test.d.ts +1 -0
- package/dist/tools/look-at/create-look-at-unhandled-error.test.d.ts +1 -0
- package/dist/tools/look-at/image-converter.test.d.ts +1 -0
- package/dist/tools/look-at/mime-type-inference.test.d.ts +1 -0
- package/dist/tools/look-at/normalize-args.test.d.ts +1 -0
- package/dist/tools/look-at/session-poller.test.d.ts +1 -0
- package/dist/tools/look-at/tools.test.d.ts +7 -0
- package/dist/tools/look-at/validate-args.test.d.ts +1 -0
- package/dist/tools/lsp/client.test.d.ts +1 -0
- package/dist/tools/lsp/config.test.d.ts +1 -0
- package/dist/tools/lsp/lsp-client-wrapper.d.ts +3 -0
- package/dist/tools/lsp/lsp-process.test.d.ts +1 -0
- package/dist/tools/lsp/server-config-loader.test.d.ts +1 -0
- package/dist/tools/lsp/utils.test.d.ts +1 -0
- package/dist/tools/session-manager/storage.test.d.ts +1 -0
- package/dist/tools/session-manager/tools.test.d.ts +1 -0
- package/dist/tools/session-manager/utils.test.d.ts +1 -0
- package/dist/tools/skill/tools.test.agent-restriction.d.ts +1 -0
- package/dist/tools/skill/tools.test.d.ts +1 -0
- package/dist/tools/skill/tools.test.description.d.ts +1 -0
- package/dist/tools/skill/tools.test.mcp-schema.d.ts +1 -0
- package/dist/tools/skill/tools.test.ordering.d.ts +1 -0
- package/dist/tools/skill/tools.test.utils.d.ts +10 -0
- package/dist/tools/skill-mcp/builtin-mcp-hint.test.d.ts +1 -0
- package/dist/tools/skill-mcp/tools.test.d.ts +1 -0
- package/dist/tools/slashcommand/command-discovery.test.d.ts +1 -0
- package/dist/tools/slashcommand/command-output-formatter.test.d.ts +1 -0
- package/dist/tools/slashcommand/index.test.d.ts +1 -0
- package/dist/tools/task/task-create-input.test.d.ts +1 -0
- package/dist/tools/task/task-create.test.d.ts +1 -0
- package/dist/tools/task/task-delete-input.test.d.ts +1 -0
- package/dist/tools/task/task-get-input.test.d.ts +1 -0
- package/dist/tools/task/task-get.test.d.ts +1 -0
- package/dist/tools/task/task-list-input.test.d.ts +1 -0
- package/dist/tools/task/task-list.test.d.ts +1 -0
- package/dist/tools/task/task-schema-core.test.d.ts +1 -0
- package/dist/tools/task/task-schema-fields.test.d.ts +1 -0
- package/dist/tools/task/task-status.test.d.ts +1 -0
- package/dist/tools/task/task-update-input.test.d.ts +1 -0
- package/dist/tools/task/task-update.test.d.ts +1 -0
- package/dist/tools/task/todo-sync-all-fetch.test.d.ts +1 -0
- package/dist/tools/task/todo-sync-all-write.test.d.ts +1 -0
- package/dist/tools/task/todo-sync-sync.test.d.ts +1 -0
- package/dist/tools/task/todo-sync-update.test.d.ts +1 -0
- package/dist/tools/task/todo-sync.test.d.ts +1 -0
- package/dist/utils/context-trimmer.d.ts +21 -0
- package/dist/utils/safety-tool-result.d.ts +31 -0
- package/dist/utils/tool-contract-wrapper.d.ts +10 -0
- package/package.json +14 -13
package/dist/cli/index.js
CHANGED
|
@@ -6294,142 +6294,140 @@ var init_binary_downloader = __esm(() => {
|
|
|
6294
6294
|
});
|
|
6295
6295
|
|
|
6296
6296
|
// src/shared/model-requirements.ts
|
|
6297
|
-
var AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS;
|
|
6297
|
+
var DEFAULT_AGENT_MODEL_REQUIREMENTS, DEFAULT_CATEGORY_MODEL_REQUIREMENTS, AGENT_MODEL_REQUIREMENTS, CATEGORY_MODEL_REQUIREMENTS;
|
|
6298
6298
|
var init_model_requirements = __esm(() => {
|
|
6299
|
-
|
|
6299
|
+
DEFAULT_AGENT_MODEL_REQUIREMENTS = {
|
|
6300
6300
|
sisyphus: {
|
|
6301
6301
|
fallbackChain: [
|
|
6302
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6303
|
-
{ providers: ["zai-coding-plan", "opencode"], model: "glm-5" },
|
|
6302
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6304
6303
|
{ providers: ["opencode"], model: "big-pickle" }
|
|
6305
6304
|
],
|
|
6306
6305
|
requiresAnyModel: true
|
|
6307
6306
|
},
|
|
6308
6307
|
hephaestus: {
|
|
6309
6308
|
fallbackChain: [
|
|
6310
|
-
{ providers: ["openai", "
|
|
6311
|
-
{ providers: ["
|
|
6309
|
+
{ providers: ["openai", "opencode"], model: "gpt-4o", variant: "medium" },
|
|
6310
|
+
{ providers: ["anthropic", "opencode"], model: "claude-3-5-sonnet", variant: "max" }
|
|
6312
6311
|
],
|
|
6313
|
-
|
|
6312
|
+
requiresAnyModel: true
|
|
6314
6313
|
},
|
|
6315
6314
|
oracle: {
|
|
6316
6315
|
fallbackChain: [
|
|
6317
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6318
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6319
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6316
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o", variant: "high" },
|
|
6317
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" },
|
|
6318
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" }
|
|
6320
6319
|
]
|
|
6321
6320
|
},
|
|
6322
6321
|
librarian: {
|
|
6323
6322
|
fallbackChain: [
|
|
6324
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6325
|
-
{ providers: ["opencode"], model: "minimax-
|
|
6323
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-flash" },
|
|
6324
|
+
{ providers: ["opencode"], model: "minimax-text-01" },
|
|
6326
6325
|
{ providers: ["opencode"], model: "big-pickle" }
|
|
6327
6326
|
]
|
|
6328
6327
|
},
|
|
6329
6328
|
explore: {
|
|
6330
6329
|
fallbackChain: [
|
|
6331
|
-
{ providers: ["github-copilot"], model: "
|
|
6332
|
-
{ providers: ["opencode"], model: "minimax-
|
|
6333
|
-
{ providers: ["anthropic", "opencode"], model: "claude-
|
|
6334
|
-
{ providers: ["opencode"], model: "gpt-
|
|
6330
|
+
{ providers: ["github-copilot"], model: "gpt-4o-mini" },
|
|
6331
|
+
{ providers: ["opencode"], model: "minimax-text-01" },
|
|
6332
|
+
{ providers: ["anthropic", "opencode"], model: "claude-3-5-haiku" },
|
|
6333
|
+
{ providers: ["opencode"], model: "gpt-4o-mini" }
|
|
6335
6334
|
]
|
|
6336
6335
|
},
|
|
6337
6336
|
"multimodal-looker": {
|
|
6338
6337
|
fallbackChain: [
|
|
6339
|
-
{ providers: ["openai", "opencode"], model: "gpt-
|
|
6340
|
-
{ providers: ["
|
|
6341
|
-
{ providers: ["
|
|
6342
|
-
{ providers: ["zai-coding-plan"], model: "glm-4.6v" },
|
|
6343
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-5-nano" }
|
|
6338
|
+
{ providers: ["openai", "opencode"], model: "gpt-4o", variant: "medium" },
|
|
6339
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-flash" },
|
|
6340
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o-mini" }
|
|
6344
6341
|
]
|
|
6345
6342
|
},
|
|
6346
6343
|
prometheus: {
|
|
6347
6344
|
fallbackChain: [
|
|
6348
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6349
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6350
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6345
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6346
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o", variant: "high" },
|
|
6347
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro" }
|
|
6351
6348
|
]
|
|
6352
6349
|
},
|
|
6353
6350
|
metis: {
|
|
6354
6351
|
fallbackChain: [
|
|
6355
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6356
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6357
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6352
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6353
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o", variant: "high" },
|
|
6354
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" }
|
|
6358
6355
|
]
|
|
6359
6356
|
},
|
|
6360
6357
|
momus: {
|
|
6361
6358
|
fallbackChain: [
|
|
6362
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6363
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6364
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6359
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o", variant: "medium" },
|
|
6360
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6361
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" }
|
|
6365
6362
|
]
|
|
6366
6363
|
},
|
|
6367
6364
|
atlas: {
|
|
6368
6365
|
fallbackChain: [
|
|
6369
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6370
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6366
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet" },
|
|
6367
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o" }
|
|
6371
6368
|
]
|
|
6372
6369
|
}
|
|
6373
6370
|
};
|
|
6374
|
-
|
|
6371
|
+
DEFAULT_CATEGORY_MODEL_REQUIREMENTS = {
|
|
6375
6372
|
"visual-engineering": {
|
|
6376
6373
|
fallbackChain: [
|
|
6377
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6378
|
-
{ providers: ["
|
|
6379
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" }
|
|
6374
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" },
|
|
6375
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" }
|
|
6380
6376
|
]
|
|
6381
6377
|
},
|
|
6382
6378
|
ultrabrain: {
|
|
6383
6379
|
fallbackChain: [
|
|
6384
|
-
{ providers: ["openai", "opencode"], model: "
|
|
6385
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6386
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6380
|
+
{ providers: ["openai", "opencode"], model: "o3-mini", variant: "xhigh" },
|
|
6381
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" },
|
|
6382
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" }
|
|
6387
6383
|
]
|
|
6388
6384
|
},
|
|
6389
6385
|
deep: {
|
|
6390
6386
|
fallbackChain: [
|
|
6391
|
-
{ providers: ["openai", "opencode"], model: "
|
|
6392
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6393
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6387
|
+
{ providers: ["openai", "opencode"], model: "o3-mini", variant: "medium" },
|
|
6388
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6389
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" }
|
|
6394
6390
|
],
|
|
6395
|
-
requiresModel: "
|
|
6391
|
+
requiresModel: "o3-mini"
|
|
6396
6392
|
},
|
|
6397
6393
|
artistry: {
|
|
6398
6394
|
fallbackChain: [
|
|
6399
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6400
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6401
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6395
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro", variant: "high" },
|
|
6396
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6397
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o" }
|
|
6402
6398
|
],
|
|
6403
|
-
requiresModel: "gemini-
|
|
6399
|
+
requiresModel: "gemini-1.5-pro"
|
|
6404
6400
|
},
|
|
6405
6401
|
quick: {
|
|
6406
6402
|
fallbackChain: [
|
|
6407
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6408
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6409
|
-
{ providers: ["opencode"], model: "gpt-
|
|
6403
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-haiku" },
|
|
6404
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-flash" },
|
|
6405
|
+
{ providers: ["opencode"], model: "gpt-4o-mini" }
|
|
6410
6406
|
]
|
|
6411
6407
|
},
|
|
6412
6408
|
"unspecified-low": {
|
|
6413
6409
|
fallbackChain: [
|
|
6414
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6415
|
-
{ providers: ["openai", "opencode"], model: "gpt-
|
|
6416
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6410
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet" },
|
|
6411
|
+
{ providers: ["openai", "opencode"], model: "gpt-4o", variant: "medium" },
|
|
6412
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-flash" }
|
|
6417
6413
|
]
|
|
6418
6414
|
},
|
|
6419
6415
|
"unspecified-high": {
|
|
6420
6416
|
fallbackChain: [
|
|
6421
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6422
|
-
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-
|
|
6423
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6417
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet", variant: "max" },
|
|
6418
|
+
{ providers: ["openai", "github-copilot", "opencode"], model: "gpt-4o", variant: "high" },
|
|
6419
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-pro" }
|
|
6424
6420
|
]
|
|
6425
6421
|
},
|
|
6426
6422
|
writing: {
|
|
6427
6423
|
fallbackChain: [
|
|
6428
|
-
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-
|
|
6429
|
-
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-
|
|
6424
|
+
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-1.5-flash" },
|
|
6425
|
+
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-3-5-sonnet" }
|
|
6430
6426
|
]
|
|
6431
6427
|
}
|
|
6432
6428
|
};
|
|
6429
|
+
AGENT_MODEL_REQUIREMENTS = DEFAULT_AGENT_MODEL_REQUIREMENTS;
|
|
6430
|
+
CATEGORY_MODEL_REQUIREMENTS = DEFAULT_CATEGORY_MODEL_REQUIREMENTS;
|
|
6433
6431
|
});
|
|
6434
6432
|
|
|
6435
6433
|
// src/shared/agent-variant.ts
|
|
@@ -6447,8 +6445,15 @@ var init_system_directive = () => {};
|
|
|
6447
6445
|
|
|
6448
6446
|
// src/shared/agent-tool-restrictions.ts
|
|
6449
6447
|
var init_agent_tool_restrictions = () => {};
|
|
6448
|
+
|
|
6449
|
+
// src/shared/model-normalization.ts
|
|
6450
|
+
function normalizeModel(model) {
|
|
6451
|
+
const trimmed = model?.trim();
|
|
6452
|
+
return trimmed || undefined;
|
|
6453
|
+
}
|
|
6454
|
+
|
|
6450
6455
|
// src/shared/connected-providers-cache.ts
|
|
6451
|
-
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
6456
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, statSync } from "fs";
|
|
6452
6457
|
import { join as join4 } from "path";
|
|
6453
6458
|
function getCacheFilePath(filename) {
|
|
6454
6459
|
return join4(getOmoOpenCodeCacheDir(), filename);
|
|
@@ -6459,6 +6464,30 @@ function ensureCacheDir() {
|
|
|
6459
6464
|
mkdirSync(cacheDir, { recursive: true });
|
|
6460
6465
|
}
|
|
6461
6466
|
}
|
|
6467
|
+
function readConnectedProvidersCache() {
|
|
6468
|
+
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
6469
|
+
if (!existsSync3(cacheFile)) {
|
|
6470
|
+
memoryConnectedProviders = null;
|
|
6471
|
+
memoryConnectedProvidersMtime = 0;
|
|
6472
|
+
log("[connected-providers-cache] Cache file not found", { cacheFile });
|
|
6473
|
+
return null;
|
|
6474
|
+
}
|
|
6475
|
+
try {
|
|
6476
|
+
const mtime = statSync(cacheFile).mtimeMs;
|
|
6477
|
+
if (memoryConnectedProviders && memoryConnectedProvidersMtime === mtime) {
|
|
6478
|
+
return memoryConnectedProviders;
|
|
6479
|
+
}
|
|
6480
|
+
const content = readFileSync2(cacheFile, "utf-8");
|
|
6481
|
+
const data = JSON.parse(content);
|
|
6482
|
+
memoryConnectedProviders = data.connected;
|
|
6483
|
+
memoryConnectedProvidersMtime = mtime;
|
|
6484
|
+
log("[connected-providers-cache] Read cache", { count: data.connected.length, updatedAt: data.updatedAt });
|
|
6485
|
+
return data.connected;
|
|
6486
|
+
} catch (err) {
|
|
6487
|
+
log("[connected-providers-cache] Error reading cache", { error: String(err) });
|
|
6488
|
+
return null;
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6462
6491
|
function writeConnectedProvidersCache(connected) {
|
|
6463
6492
|
ensureCacheDir();
|
|
6464
6493
|
const cacheFile = getCacheFilePath(CONNECTED_PROVIDERS_CACHE_FILE);
|
|
@@ -6468,6 +6497,7 @@ function writeConnectedProvidersCache(connected) {
|
|
|
6468
6497
|
};
|
|
6469
6498
|
try {
|
|
6470
6499
|
writeFileSync2(cacheFile, JSON.stringify(data, null, 2));
|
|
6500
|
+
memoryConnectedProviders = null;
|
|
6471
6501
|
log("[connected-providers-cache] Cache written", { count: connected.length });
|
|
6472
6502
|
} catch (err) {
|
|
6473
6503
|
log("[connected-providers-cache] Error writing cache", { error: String(err) });
|
|
@@ -6486,6 +6516,7 @@ function writeProviderModelsCache(data) {
|
|
|
6486
6516
|
};
|
|
6487
6517
|
try {
|
|
6488
6518
|
writeFileSync2(cacheFile, JSON.stringify(cacheData, null, 2));
|
|
6519
|
+
memoryProviderModels = null;
|
|
6489
6520
|
log("[connected-providers-cache] Provider-models cache written", {
|
|
6490
6521
|
providerCount: Object.keys(data.models).length
|
|
6491
6522
|
});
|
|
@@ -6525,15 +6556,62 @@ async function updateConnectedProvidersCache(client) {
|
|
|
6525
6556
|
log("[connected-providers-cache] Error updating cache", { error: String(err) });
|
|
6526
6557
|
}
|
|
6527
6558
|
}
|
|
6528
|
-
var CONNECTED_PROVIDERS_CACHE_FILE = "connected-providers.json", PROVIDER_MODELS_CACHE_FILE = "provider-models.json";
|
|
6559
|
+
var CONNECTED_PROVIDERS_CACHE_FILE = "connected-providers.json", PROVIDER_MODELS_CACHE_FILE = "provider-models.json", memoryConnectedProviders = null, memoryConnectedProvidersMtime = 0, memoryProviderModels = null;
|
|
6529
6560
|
var init_connected_providers_cache = __esm(() => {
|
|
6530
6561
|
init_logger();
|
|
6531
6562
|
init_data_path();
|
|
6532
6563
|
});
|
|
6533
6564
|
|
|
6534
6565
|
// src/shared/model-availability.ts
|
|
6535
|
-
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
6566
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
6536
6567
|
import { join as join5 } from "path";
|
|
6568
|
+
function normalizeModelName(name) {
|
|
6569
|
+
return name.toLowerCase().replace(/claude-(opus|sonnet|haiku)-(\d+)[.-](\d+)/g, "claude-$1-$2.$3");
|
|
6570
|
+
}
|
|
6571
|
+
function fuzzyMatchModel(target, available, providers) {
|
|
6572
|
+
log("[fuzzyMatchModel] called", { target, availableCount: available.size, providers });
|
|
6573
|
+
if (available.size === 0) {
|
|
6574
|
+
log("[fuzzyMatchModel] empty available set");
|
|
6575
|
+
return null;
|
|
6576
|
+
}
|
|
6577
|
+
const targetNormalized = normalizeModelName(target);
|
|
6578
|
+
let candidates = Array.from(available);
|
|
6579
|
+
if (providers && providers.length > 0) {
|
|
6580
|
+
const providerSet = new Set(providers);
|
|
6581
|
+
candidates = candidates.filter((model) => {
|
|
6582
|
+
const [provider] = model.split("/");
|
|
6583
|
+
return providerSet.has(provider);
|
|
6584
|
+
});
|
|
6585
|
+
log("[fuzzyMatchModel] filtered by providers", { candidateCount: candidates.length, candidates: candidates.slice(0, 10) });
|
|
6586
|
+
}
|
|
6587
|
+
if (candidates.length === 0) {
|
|
6588
|
+
log("[fuzzyMatchModel] no candidates after filter");
|
|
6589
|
+
return null;
|
|
6590
|
+
}
|
|
6591
|
+
const matches = candidates.filter((model) => normalizeModelName(model).includes(targetNormalized));
|
|
6592
|
+
log("[fuzzyMatchModel] substring matches", { targetNormalized, matchCount: matches.length, matches });
|
|
6593
|
+
if (matches.length === 0) {
|
|
6594
|
+
log("[fuzzyMatchModel] WARNING: no match found", { target, availableCount: available.size, providers });
|
|
6595
|
+
return null;
|
|
6596
|
+
}
|
|
6597
|
+
const exactMatch = matches.find((model) => normalizeModelName(model) === targetNormalized);
|
|
6598
|
+
if (exactMatch) {
|
|
6599
|
+
log("[fuzzyMatchModel] exact match found", { exactMatch });
|
|
6600
|
+
return exactMatch;
|
|
6601
|
+
}
|
|
6602
|
+
const exactModelIdMatches = matches.filter((model) => {
|
|
6603
|
+
const modelId = model.split("/").slice(1).join("/");
|
|
6604
|
+
return normalizeModelName(modelId) === targetNormalized;
|
|
6605
|
+
});
|
|
6606
|
+
if (exactModelIdMatches.length > 0) {
|
|
6607
|
+
const result2 = exactModelIdMatches.reduce((shortest, current) => current.length < shortest.length ? current : shortest);
|
|
6608
|
+
log("[fuzzyMatchModel] exact model ID match found", { result: result2, candidateCount: exactModelIdMatches.length });
|
|
6609
|
+
return result2;
|
|
6610
|
+
}
|
|
6611
|
+
const result = matches.reduce((shortest, current) => current.length < shortest.length ? current : shortest);
|
|
6612
|
+
log("[fuzzyMatchModel] shortest match", { result });
|
|
6613
|
+
return result;
|
|
6614
|
+
}
|
|
6537
6615
|
function isModelCacheAvailable() {
|
|
6538
6616
|
if (hasProviderModelsCache()) {
|
|
6539
6617
|
return true;
|
|
@@ -6559,6 +6637,113 @@ function transformModelForProvider(provider, model) {
|
|
|
6559
6637
|
}
|
|
6560
6638
|
|
|
6561
6639
|
// src/shared/model-resolution-pipeline.ts
|
|
6640
|
+
function resolveModelPipeline(request) {
|
|
6641
|
+
const attempted = [];
|
|
6642
|
+
const { intent, constraints, policy } = request;
|
|
6643
|
+
const availableModels = constraints.availableModels;
|
|
6644
|
+
const fallbackChain = policy?.fallbackChain;
|
|
6645
|
+
const systemDefaultModel = policy?.systemDefaultModel;
|
|
6646
|
+
const tryResolve = (model, provenance = "override") => {
|
|
6647
|
+
const normalized = normalizeModel(model);
|
|
6648
|
+
if (!normalized)
|
|
6649
|
+
return;
|
|
6650
|
+
attempted.push(normalized);
|
|
6651
|
+
if (availableModels.size > 0) {
|
|
6652
|
+
const parts = normalized.split("/");
|
|
6653
|
+
const providerHint = parts.length >= 2 ? [parts[0]] : undefined;
|
|
6654
|
+
const match = fuzzyMatchModel(normalized, availableModels, providerHint);
|
|
6655
|
+
if (match) {
|
|
6656
|
+
log(`[MODEL-RESOLUTION] Resolved via ${provenance}`, { requested: normalized, resolved: match });
|
|
6657
|
+
return { model: match, provenance, attempted };
|
|
6658
|
+
}
|
|
6659
|
+
log(`[MODEL-RESOLUTION] Requested model not available in current providers`, { requested: normalized, provenance });
|
|
6660
|
+
return;
|
|
6661
|
+
} else {
|
|
6662
|
+
log(`[MODEL-RESOLUTION] Resolved via ${provenance} (no availability cache)`, { model: normalized });
|
|
6663
|
+
return { model: normalized, provenance, attempted };
|
|
6664
|
+
}
|
|
6665
|
+
};
|
|
6666
|
+
const uiResult = tryResolve(intent?.uiSelectedModel);
|
|
6667
|
+
if (uiResult)
|
|
6668
|
+
return uiResult;
|
|
6669
|
+
const userResult = tryResolve(intent?.userModel);
|
|
6670
|
+
if (userResult)
|
|
6671
|
+
return userResult;
|
|
6672
|
+
const userFallbackResult = tryResolve(intent?.userFallbackModel, "user-fallback");
|
|
6673
|
+
if (userFallbackResult)
|
|
6674
|
+
return userFallbackResult;
|
|
6675
|
+
const sessionResult = tryResolve(intent?.sessionModel);
|
|
6676
|
+
if (sessionResult)
|
|
6677
|
+
return sessionResult;
|
|
6678
|
+
const categoryResult = tryResolve(intent?.categoryDefaultModel, "category-default");
|
|
6679
|
+
if (categoryResult)
|
|
6680
|
+
return categoryResult;
|
|
6681
|
+
const categoryFallbackResult = tryResolve(intent?.categoryFallbackModel, "category-default");
|
|
6682
|
+
if (categoryFallbackResult)
|
|
6683
|
+
return categoryFallbackResult;
|
|
6684
|
+
const userFallbackModels = intent?.userFallbackModels;
|
|
6685
|
+
if (userFallbackModels && userFallbackModels.length > 0) {
|
|
6686
|
+
for (const model of userFallbackModels) {
|
|
6687
|
+
const res = tryResolve(model, "user-fallback");
|
|
6688
|
+
if (res)
|
|
6689
|
+
return res;
|
|
6690
|
+
}
|
|
6691
|
+
}
|
|
6692
|
+
if (fallbackChain && fallbackChain.length > 0) {
|
|
6693
|
+
if (availableModels.size === 0) {
|
|
6694
|
+
const connectedProviders = constraints.connectedProviders ?? readConnectedProvidersCache();
|
|
6695
|
+
const connectedSet = connectedProviders ? new Set(connectedProviders) : null;
|
|
6696
|
+
if (connectedSet !== null) {
|
|
6697
|
+
for (const entry of fallbackChain) {
|
|
6698
|
+
for (const provider of entry.providers) {
|
|
6699
|
+
if (connectedSet.has(provider)) {
|
|
6700
|
+
const transformedModelId = transformModelForProvider(provider, entry.model);
|
|
6701
|
+
const model = `${provider}/${transformedModelId}`;
|
|
6702
|
+
log("[MODEL-RESOLUTION] Resolved via provider-specific fallback chain (no cache)", {
|
|
6703
|
+
provider,
|
|
6704
|
+
model: transformedModelId
|
|
6705
|
+
});
|
|
6706
|
+
return {
|
|
6707
|
+
model,
|
|
6708
|
+
provenance: "provider-fallback",
|
|
6709
|
+
variant: entry.variant,
|
|
6710
|
+
attempted
|
|
6711
|
+
};
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
} else {
|
|
6717
|
+
for (const entry of fallbackChain) {
|
|
6718
|
+
for (const provider of entry.providers) {
|
|
6719
|
+
const fullModel = `${provider}/${entry.model}`;
|
|
6720
|
+
const match = fuzzyMatchModel(fullModel, availableModels, [provider]);
|
|
6721
|
+
if (match) {
|
|
6722
|
+
log("[MODEL-RESOLUTION] Resolved via provider-specific fallback chain", {
|
|
6723
|
+
provider,
|
|
6724
|
+
model: entry.model,
|
|
6725
|
+
match
|
|
6726
|
+
});
|
|
6727
|
+
return {
|
|
6728
|
+
model: match,
|
|
6729
|
+
provenance: "provider-fallback",
|
|
6730
|
+
variant: entry.variant,
|
|
6731
|
+
attempted
|
|
6732
|
+
};
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
const systemDefaultResult = tryResolve(systemDefaultModel, "system-default");
|
|
6739
|
+
if (systemDefaultResult)
|
|
6740
|
+
return systemDefaultResult;
|
|
6741
|
+
const systemDefaultFallbackResult = tryResolve(policy?.systemDefaultFallbackModel, "system-default");
|
|
6742
|
+
if (systemDefaultFallbackResult)
|
|
6743
|
+
return systemDefaultFallbackResult;
|
|
6744
|
+
log("[MODEL-RESOLUTION] FAILED to resolve any supported model", { attempted });
|
|
6745
|
+
return;
|
|
6746
|
+
}
|
|
6562
6747
|
var init_model_resolution_pipeline = __esm(() => {
|
|
6563
6748
|
init_logger();
|
|
6564
6749
|
init_connected_providers_cache();
|
|
@@ -6570,6 +6755,39 @@ var init_model_resolver = __esm(() => {
|
|
|
6570
6755
|
init_model_resolution_pipeline();
|
|
6571
6756
|
});
|
|
6572
6757
|
|
|
6758
|
+
// src/shared/model-resolution-tracker.ts
|
|
6759
|
+
class ModelResolutionTracker {
|
|
6760
|
+
lastResolved = new Map;
|
|
6761
|
+
resolve(contextKey, request) {
|
|
6762
|
+
const result = resolveModelPipeline(request);
|
|
6763
|
+
if (!result)
|
|
6764
|
+
return;
|
|
6765
|
+
const previousVisibleModel = this.lastResolved.get(contextKey);
|
|
6766
|
+
const currentModel = result.model;
|
|
6767
|
+
if (currentModel !== previousVisibleModel) {
|
|
6768
|
+
this.lastResolved.set(contextKey, currentModel);
|
|
6769
|
+
if (true) {
|
|
6770
|
+
console.log(`[MODEL-RESOLUTION] [${contextKey}] Resolved to: ${currentModel} (via ${result.provenance})`);
|
|
6771
|
+
}
|
|
6772
|
+
log(`Model resolution changed for ${contextKey}`, {
|
|
6773
|
+
model: currentModel,
|
|
6774
|
+
provenance: result.provenance,
|
|
6775
|
+
previous: previousVisibleModel
|
|
6776
|
+
});
|
|
6777
|
+
}
|
|
6778
|
+
return result;
|
|
6779
|
+
}
|
|
6780
|
+
clear(contextKey) {
|
|
6781
|
+
this.lastResolved.delete(contextKey);
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
var modelResolutionTracker;
|
|
6785
|
+
var init_model_resolution_tracker = __esm(() => {
|
|
6786
|
+
init_model_resolution_pipeline();
|
|
6787
|
+
init_logger();
|
|
6788
|
+
modelResolutionTracker = new ModelResolutionTracker;
|
|
6789
|
+
});
|
|
6790
|
+
|
|
6573
6791
|
// src/shared/fallback-model-availability.ts
|
|
6574
6792
|
var init_fallback_model_availability = __esm(() => {
|
|
6575
6793
|
init_connected_providers_cache();
|
|
@@ -6877,11 +7095,91 @@ var init_session_category_registry = __esm(() => {
|
|
|
6877
7095
|
sessionCategoryMap = new Map;
|
|
6878
7096
|
});
|
|
6879
7097
|
|
|
7098
|
+
// src/shared/verify-task-completion.ts
|
|
7099
|
+
async function verifyTaskCompletionState(client, sessionID, options) {
|
|
7100
|
+
try {
|
|
7101
|
+
const response = await client.session.messages({
|
|
7102
|
+
path: { id: sessionID }
|
|
7103
|
+
});
|
|
7104
|
+
if (response === null || response === undefined) {
|
|
7105
|
+
throw new Error("SDK returned null or undefined session messages");
|
|
7106
|
+
}
|
|
7107
|
+
const messages = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
7108
|
+
let lastCompleteTaskIndex = -1;
|
|
7109
|
+
let lastCompleteTaskPart = null;
|
|
7110
|
+
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
7111
|
+
const msg = messages[i2];
|
|
7112
|
+
if (msg.info?.role === "assistant" || msg.info?.role === "tool") {
|
|
7113
|
+
const parts = msg.parts ?? [];
|
|
7114
|
+
for (const p of parts) {
|
|
7115
|
+
if (p.type === "tool" && (p.toolName === "complete_task" || p.name === "complete_task" || p.tool === "complete_task")) {
|
|
7116
|
+
lastCompleteTaskIndex = i2;
|
|
7117
|
+
lastCompleteTaskPart = p;
|
|
7118
|
+
break;
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
if (lastCompleteTaskIndex !== -1)
|
|
7123
|
+
break;
|
|
7124
|
+
}
|
|
7125
|
+
if (lastCompleteTaskIndex !== -1 && lastCompleteTaskPart) {
|
|
7126
|
+
let hasErrorResult = false;
|
|
7127
|
+
let foundResult = false;
|
|
7128
|
+
if (lastCompleteTaskPart.state && (lastCompleteTaskPart.state.status === "completed" || lastCompleteTaskPart.state.status === "error")) {
|
|
7129
|
+
foundResult = true;
|
|
7130
|
+
const output = typeof lastCompleteTaskPart.state.output === "string" ? lastCompleteTaskPart.state.output : JSON.stringify(lastCompleteTaskPart.state.output || "");
|
|
7131
|
+
if (output.includes("[ERROR] TASK COMPLETION REJECTED") || output.includes("[ERROR] STRICT ISSUE RESOLUTION MODE") || output.includes("explicitly failed") || lastCompleteTaskPart.state.status === "error") {
|
|
7132
|
+
hasErrorResult = true;
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
if (!foundResult) {
|
|
7136
|
+
for (let i2 = lastCompleteTaskIndex + 1;i2 < messages.length; i2++) {
|
|
7137
|
+
const msg = messages[i2];
|
|
7138
|
+
if (msg.info?.role === "user" || msg.info?.role === "tool") {
|
|
7139
|
+
const parts = msg.parts ?? [];
|
|
7140
|
+
for (const p of parts) {
|
|
7141
|
+
if (p.type === "tool_result" || p.type === "tool") {
|
|
7142
|
+
foundResult = true;
|
|
7143
|
+
const content = typeof p.content === "string" ? p.content : typeof p.state?.output === "string" ? p.state.output : JSON.stringify(p.content || p.state?.output || "");
|
|
7144
|
+
if (content.includes("[ERROR] TASK COMPLETION REJECTED") || content.includes("[ERROR] STRICT ISSUE RESOLUTION MODE") || content.includes("explicitly failed") || p.state?.status === "error") {
|
|
7145
|
+
hasErrorResult = true;
|
|
7146
|
+
}
|
|
7147
|
+
} else if (p.type === "text" && typeof p.text === "string" && p.text.includes("[tool result]")) {
|
|
7148
|
+
if (p.text.includes("[ERROR] TASK COMPLETION REJECTED") || p.text.includes("[ERROR] STRICT ISSUE RESOLUTION MODE") || p.text.includes("explicitly failed")) {
|
|
7149
|
+
foundResult = true;
|
|
7150
|
+
hasErrorResult = true;
|
|
7151
|
+
}
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
if (foundResult && hasErrorResult) {
|
|
7158
|
+
log("[verifyTaskCompletionState] Rejected completion because the last complete_task call failed:", sessionID);
|
|
7159
|
+
return false;
|
|
7160
|
+
}
|
|
7161
|
+
}
|
|
7162
|
+
if (options?.requireCompleteTask && lastCompleteTaskIndex === -1) {
|
|
7163
|
+
log("[verifyTaskCompletionState] complete_task was required but not found, fail-closed:", sessionID);
|
|
7164
|
+
return false;
|
|
7165
|
+
}
|
|
7166
|
+
return true;
|
|
7167
|
+
} catch (error) {
|
|
7168
|
+
log("[verifyTaskCompletionState] Error verifying task completion state, fail-closed:", error);
|
|
7169
|
+
return false;
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7172
|
+
var init_verify_task_completion = __esm(() => {
|
|
7173
|
+
init_logger();
|
|
7174
|
+
});
|
|
7175
|
+
|
|
6880
7176
|
// src/shared/index.ts
|
|
6881
7177
|
var init_shared = __esm(() => {
|
|
6882
7178
|
init_model_resolver();
|
|
6883
7179
|
init_model_resolution_pipeline();
|
|
7180
|
+
init_model_resolution_tracker();
|
|
6884
7181
|
init_session_category_registry();
|
|
7182
|
+
init_verify_task_completion();
|
|
6885
7183
|
init_frontmatter();
|
|
6886
7184
|
init_command_executor();
|
|
6887
7185
|
init_file_reference_resolver();
|
|
@@ -7056,13 +7354,13 @@ var init_opencode_config_format = __esm(() => {
|
|
|
7056
7354
|
});
|
|
7057
7355
|
|
|
7058
7356
|
// src/cli/config-manager/parse-opencode-config-file.ts
|
|
7059
|
-
import { readFileSync as readFileSync4, statSync } from "fs";
|
|
7357
|
+
import { readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
7060
7358
|
function isEmptyOrWhitespace(content) {
|
|
7061
7359
|
return content.trim().length === 0;
|
|
7062
7360
|
}
|
|
7063
7361
|
function parseOpenCodeConfigFileWithError(path3) {
|
|
7064
7362
|
try {
|
|
7065
|
-
const stat =
|
|
7363
|
+
const stat = statSync3(path3);
|
|
7066
7364
|
if (stat.size === 0) {
|
|
7067
7365
|
return { config: null, error: `Config file is empty: ${path3}. Delete it or add valid JSON content.` };
|
|
7068
7366
|
}
|
|
@@ -7183,9 +7481,10 @@ var init_model_fallback_requirements = __esm(() => {
|
|
|
7183
7481
|
},
|
|
7184
7482
|
hephaestus: {
|
|
7185
7483
|
fallbackChain: [
|
|
7186
|
-
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" }
|
|
7484
|
+
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
|
|
7485
|
+
{ providers: ["opencode"], model: "claude-opus-4-6", variant: "max" }
|
|
7187
7486
|
],
|
|
7188
|
-
|
|
7487
|
+
requiresAnyModel: true
|
|
7189
7488
|
},
|
|
7190
7489
|
oracle: {
|
|
7191
7490
|
fallbackChain: [
|
|
@@ -7270,8 +7569,7 @@ var init_model_fallback_requirements = __esm(() => {
|
|
|
7270
7569
|
{ providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
|
|
7271
7570
|
{ providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
|
|
7272
7571
|
{ providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" }
|
|
7273
|
-
]
|
|
7274
|
-
requiresModel: "gpt-5.3-codex"
|
|
7572
|
+
]
|
|
7275
7573
|
},
|
|
7276
7574
|
artistry: {
|
|
7277
7575
|
fallbackChain: [
|
|
@@ -7385,7 +7683,7 @@ function generateModelConfig(config) {
|
|
|
7385
7683
|
if (!hasAnyProvider) {
|
|
7386
7684
|
return {
|
|
7387
7685
|
$schema: SCHEMA_URL,
|
|
7388
|
-
agents: Object.fromEntries(Object.entries(CLI_AGENT_MODEL_REQUIREMENTS).filter(([
|
|
7686
|
+
agents: Object.fromEntries(Object.entries(CLI_AGENT_MODEL_REQUIREMENTS).filter(([_, req]) => !req.requiresAnyModel).map(([role]) => [role, { model: ULTIMATE_FALLBACK }])),
|
|
7389
7687
|
categories: Object.fromEntries(Object.keys(CLI_CATEGORY_MODEL_REQUIREMENTS).map((cat) => [cat, { model: ULTIMATE_FALLBACK }]))
|
|
7390
7688
|
};
|
|
7391
7689
|
}
|
|
@@ -7430,7 +7728,7 @@ function generateModelConfig(config) {
|
|
|
7430
7728
|
if (resolved) {
|
|
7431
7729
|
const variant = resolved.variant ?? req.variant;
|
|
7432
7730
|
agents[role] = variant ? { model: resolved.model, variant } : { model: resolved.model };
|
|
7433
|
-
} else {
|
|
7731
|
+
} else if (!req.requiresAnyModel) {
|
|
7434
7732
|
agents[role] = { model: ULTIMATE_FALLBACK };
|
|
7435
7733
|
}
|
|
7436
7734
|
}
|
|
@@ -7488,7 +7786,7 @@ function deepMergeRecord(target, source) {
|
|
|
7488
7786
|
}
|
|
7489
7787
|
|
|
7490
7788
|
// src/cli/config-manager/write-omo-config.ts
|
|
7491
|
-
import { existsSync as existsSync7, readFileSync as readFileSync6, statSync as
|
|
7789
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
7492
7790
|
function isEmptyOrWhitespace2(content) {
|
|
7493
7791
|
return content.trim().length === 0;
|
|
7494
7792
|
}
|
|
@@ -7507,7 +7805,7 @@ function writeOmoConfig(installConfig) {
|
|
|
7507
7805
|
const newConfig = generateOmoConfig(installConfig);
|
|
7508
7806
|
if (existsSync7(omoConfigPath)) {
|
|
7509
7807
|
try {
|
|
7510
|
-
const stat =
|
|
7808
|
+
const stat = statSync4(omoConfigPath);
|
|
7511
7809
|
const content = readFileSync6(omoConfigPath, "utf-8");
|
|
7512
7810
|
if (stat.size === 0 || isEmptyOrWhitespace2(content)) {
|
|
7513
7811
|
writeFileSync4(omoConfigPath, JSON.stringify(newConfig, null, 2) + `
|
|
@@ -8633,7 +8931,7 @@ var {
|
|
|
8633
8931
|
// package.json
|
|
8634
8932
|
var package_default = {
|
|
8635
8933
|
name: "@heidi-dang/oh-my-opencode",
|
|
8636
|
-
version: "3.
|
|
8934
|
+
version: "3.13.1",
|
|
8637
8935
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
8638
8936
|
main: "dist/index.js",
|
|
8639
8937
|
types: "dist/index.d.ts",
|
|
@@ -8709,20 +9007,21 @@ var package_default = {
|
|
|
8709
9007
|
typescript: "^5.7.3"
|
|
8710
9008
|
},
|
|
8711
9009
|
optionalDependencies: {
|
|
8712
|
-
"@heidi-dang/oh-my-opencode-darwin-arm64": "3.
|
|
8713
|
-
"@heidi-dang/oh-my-opencode-darwin-x64": "3.
|
|
8714
|
-
"@heidi-dang/oh-my-opencode-darwin-x64-baseline": "3.
|
|
8715
|
-
"@heidi-dang/oh-my-opencode-linux-arm64": "3.
|
|
8716
|
-
"@heidi-dang/oh-my-opencode-linux-arm64-musl": "3.
|
|
8717
|
-
"@heidi-dang/oh-my-opencode-linux-x64": "3.
|
|
8718
|
-
"@heidi-dang/oh-my-opencode-linux-x64-baseline": "3.
|
|
8719
|
-
"@heidi-dang/oh-my-opencode-linux-x64-musl": "3.
|
|
8720
|
-
"@heidi-dang/oh-my-opencode-linux-x64-musl-baseline": "3.
|
|
8721
|
-
"@heidi-dang/oh-my-opencode-windows-x64": "3.
|
|
8722
|
-
"@heidi-dang/oh-my-opencode-windows-x64-baseline": "3.
|
|
9010
|
+
"@heidi-dang/oh-my-opencode-darwin-arm64": "3.13.1",
|
|
9011
|
+
"@heidi-dang/oh-my-opencode-darwin-x64": "3.13.1",
|
|
9012
|
+
"@heidi-dang/oh-my-opencode-darwin-x64-baseline": "3.13.1",
|
|
9013
|
+
"@heidi-dang/oh-my-opencode-linux-arm64": "3.13.1",
|
|
9014
|
+
"@heidi-dang/oh-my-opencode-linux-arm64-musl": "3.13.1",
|
|
9015
|
+
"@heidi-dang/oh-my-opencode-linux-x64": "3.13.1",
|
|
9016
|
+
"@heidi-dang/oh-my-opencode-linux-x64-baseline": "3.13.1",
|
|
9017
|
+
"@heidi-dang/oh-my-opencode-linux-x64-musl": "3.13.1",
|
|
9018
|
+
"@heidi-dang/oh-my-opencode-linux-x64-musl-baseline": "3.13.1",
|
|
9019
|
+
"@heidi-dang/oh-my-opencode-windows-x64": "3.13.1",
|
|
9020
|
+
"@heidi-dang/oh-my-opencode-windows-x64-baseline": "3.13.1"
|
|
8723
9021
|
},
|
|
8724
9022
|
overrides: {
|
|
8725
|
-
"@opencode-ai/sdk": "^1.2.17"
|
|
9023
|
+
"@opencode-ai/sdk": "^1.2.17",
|
|
9024
|
+
zod: "^4.1.8"
|
|
8726
9025
|
},
|
|
8727
9026
|
trustedDependencies: [
|
|
8728
9027
|
"@ast-grep/cli",
|
|
@@ -9754,39 +10053,274 @@ function loadDefaultConfig() {
|
|
|
9754
10053
|
`;
|
|
9755
10054
|
}
|
|
9756
10055
|
var EMBEDDED_DEFAULT_CONFIG = {
|
|
9757
|
-
$schema: "https://raw.githubusercontent.com/
|
|
10056
|
+
$schema: "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
|
|
10057
|
+
new_task_system_enabled: true,
|
|
10058
|
+
default_run_agent: "sisyphus",
|
|
10059
|
+
hashline_edit: true,
|
|
10060
|
+
model_fallback: true,
|
|
10061
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10062
|
+
disabled_skills: [],
|
|
10063
|
+
disabled_hooks: [],
|
|
10064
|
+
disabled_tools: [],
|
|
10065
|
+
disabled_commands: [],
|
|
10066
|
+
experimental: {
|
|
10067
|
+
aggressive_truncation: true,
|
|
10068
|
+
auto_resume: true,
|
|
10069
|
+
preemptive_compaction: true,
|
|
10070
|
+
truncate_all_tool_outputs: true,
|
|
10071
|
+
task_system: true,
|
|
10072
|
+
safe_hook_creation: true,
|
|
10073
|
+
disable_omo_env: false,
|
|
10074
|
+
hashline_edit: true,
|
|
10075
|
+
model_fallback_title: true,
|
|
10076
|
+
dynamic_context_pruning: {
|
|
10077
|
+
enabled: true,
|
|
10078
|
+
notification: "minimal",
|
|
10079
|
+
turn_protection: {
|
|
10080
|
+
enabled: true,
|
|
10081
|
+
turns: 3
|
|
10082
|
+
},
|
|
10083
|
+
protected_tools: [
|
|
10084
|
+
"task",
|
|
10085
|
+
"todowrite",
|
|
10086
|
+
"todoread",
|
|
10087
|
+
"lsp_rename",
|
|
10088
|
+
"session_read",
|
|
10089
|
+
"session_write",
|
|
10090
|
+
"session_search"
|
|
10091
|
+
],
|
|
10092
|
+
strategies: {
|
|
10093
|
+
deduplication: {
|
|
10094
|
+
enabled: true
|
|
10095
|
+
},
|
|
10096
|
+
supersede_writes: {
|
|
10097
|
+
enabled: true,
|
|
10098
|
+
aggressive: false
|
|
10099
|
+
},
|
|
10100
|
+
purge_errors: {
|
|
10101
|
+
enabled: true,
|
|
10102
|
+
turns: 5
|
|
10103
|
+
}
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10106
|
+
},
|
|
10107
|
+
runtime_fallback: {
|
|
10108
|
+
enabled: true,
|
|
10109
|
+
max_fallback_attempts: 2,
|
|
10110
|
+
cooldown_seconds: 8,
|
|
10111
|
+
timeout_seconds: 45,
|
|
10112
|
+
notify_on_fallback: true
|
|
10113
|
+
},
|
|
10114
|
+
background_task: {
|
|
10115
|
+
defaultConcurrency: 2,
|
|
10116
|
+
providerConcurrency: {
|
|
10117
|
+
xai: 2,
|
|
10118
|
+
"github-copilot": 2,
|
|
10119
|
+
"opencode-go": 1
|
|
10120
|
+
},
|
|
10121
|
+
modelConcurrency: {
|
|
10122
|
+
"xai/grok-4-1-fast": 2,
|
|
10123
|
+
"xai/grok-4-1-fast-non-reasoning": 2,
|
|
10124
|
+
"github-copilot/gpt-5-mini": 2,
|
|
10125
|
+
"opencode-go/minimax-m2.5": 1
|
|
10126
|
+
},
|
|
10127
|
+
staleTimeoutMs: 120000,
|
|
10128
|
+
messageStalenessTimeoutMs: 120000,
|
|
10129
|
+
syncPollTimeoutMs: 120000
|
|
10130
|
+
},
|
|
10131
|
+
babysitting: {
|
|
10132
|
+
timeout_ms: 120000
|
|
10133
|
+
},
|
|
10134
|
+
browser_automation_engine: {
|
|
10135
|
+
provider: "playwright"
|
|
10136
|
+
},
|
|
10137
|
+
sisyphus_agent: {
|
|
10138
|
+
disabled: false,
|
|
10139
|
+
default_builder_enabled: true,
|
|
10140
|
+
planner_enabled: true,
|
|
10141
|
+
replace_plan: false
|
|
10142
|
+
},
|
|
9758
10143
|
agents: {
|
|
9759
10144
|
sisyphus: {
|
|
9760
10145
|
model: "xai/grok-4-1-fast",
|
|
9761
|
-
|
|
10146
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10147
|
+
reasoningEffort: "medium",
|
|
10148
|
+
textVerbosity: "medium",
|
|
10149
|
+
temperature: 0.2,
|
|
10150
|
+
top_p: 0.95,
|
|
10151
|
+
maxTokens: 120000,
|
|
10152
|
+
thinking: {
|
|
10153
|
+
type: "enabled",
|
|
10154
|
+
budgetTokens: 24000
|
|
10155
|
+
},
|
|
10156
|
+
mode: "primary",
|
|
10157
|
+
description: "Main orchestrator. Strong planning, routing, and repo-wide coordination."
|
|
10158
|
+
},
|
|
10159
|
+
plan: {
|
|
10160
|
+
model: "xai/grok-4-1-fast",
|
|
10161
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10162
|
+
reasoningEffort: "high",
|
|
10163
|
+
textVerbosity: "medium",
|
|
10164
|
+
temperature: 0.1,
|
|
10165
|
+
top_p: 0.9,
|
|
10166
|
+
maxTokens: 64000,
|
|
10167
|
+
thinking: {
|
|
10168
|
+
type: "enabled",
|
|
10169
|
+
budgetTokens: 32000
|
|
10170
|
+
},
|
|
10171
|
+
mode: "all",
|
|
10172
|
+
description: "Deterministic planner. Keep conservative and low-noise."
|
|
10173
|
+
},
|
|
10174
|
+
build: {
|
|
10175
|
+
model: "xai/grok-4-1-fast-non-reasoning",
|
|
10176
|
+
fallback_models: [
|
|
10177
|
+
"github-copilot/gpt-5-mini",
|
|
10178
|
+
"opencode-go/minimax-m2.5"
|
|
10179
|
+
],
|
|
10180
|
+
reasoningEffort: "low",
|
|
10181
|
+
textVerbosity: "low",
|
|
10182
|
+
temperature: 0.1,
|
|
10183
|
+
top_p: 0.9,
|
|
10184
|
+
maxTokens: 64000,
|
|
10185
|
+
thinking: {
|
|
10186
|
+
type: "disabled"
|
|
10187
|
+
},
|
|
10188
|
+
mode: "all",
|
|
10189
|
+
description: "Fast code execution/build lane."
|
|
10190
|
+
},
|
|
10191
|
+
hephaestus: {
|
|
10192
|
+
model: "xai/grok-4-1-fast-non-reasoning",
|
|
10193
|
+
fallback_models: [
|
|
10194
|
+
"github-copilot/gpt-5-mini",
|
|
10195
|
+
"opencode-go/minimax-m2.5"
|
|
10196
|
+
],
|
|
10197
|
+
reasoningEffort: "low",
|
|
10198
|
+
textVerbosity: "low",
|
|
10199
|
+
temperature: 0.1,
|
|
10200
|
+
top_p: 0.9,
|
|
10201
|
+
maxTokens: 64000,
|
|
10202
|
+
thinking: {
|
|
10203
|
+
type: "disabled"
|
|
10204
|
+
},
|
|
10205
|
+
mode: "subagent",
|
|
10206
|
+
allow_non_gpt_model: true,
|
|
10207
|
+
description: "Deep worker optimized for fast implementation, not long reasoning."
|
|
9762
10208
|
},
|
|
9763
|
-
librarian: { model: "xai/grok-4-1-fast-non-reasoning" },
|
|
9764
|
-
explore: { model: "xai/grok-4-1-fast-non-reasoning" },
|
|
9765
|
-
oracle: { model: "xai/grok-4-1-fast", variant: "high" },
|
|
9766
10209
|
prometheus: {
|
|
9767
|
-
|
|
10210
|
+
model: "xai/grok-4-1-fast",
|
|
10211
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10212
|
+
reasoningEffort: "high",
|
|
10213
|
+
textVerbosity: "medium",
|
|
10214
|
+
temperature: 0.2,
|
|
10215
|
+
top_p: 0.95,
|
|
10216
|
+
maxTokens: 96000,
|
|
10217
|
+
thinking: {
|
|
10218
|
+
type: "enabled",
|
|
10219
|
+
budgetTokens: 28000
|
|
10220
|
+
},
|
|
10221
|
+
mode: "subagent",
|
|
10222
|
+
description: "Research/planning/strategy agent."
|
|
10223
|
+
},
|
|
10224
|
+
atlas: {
|
|
10225
|
+
model: "xai/grok-4-1-fast",
|
|
10226
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10227
|
+
reasoningEffort: "medium",
|
|
10228
|
+
textVerbosity: "medium",
|
|
10229
|
+
temperature: 0.2,
|
|
10230
|
+
top_p: 0.95,
|
|
10231
|
+
maxTokens: 96000,
|
|
10232
|
+
thinking: {
|
|
10233
|
+
type: "enabled",
|
|
10234
|
+
budgetTokens: 20000
|
|
10235
|
+
},
|
|
10236
|
+
mode: "subagent",
|
|
10237
|
+
description: "Repository analysis and architecture navigation."
|
|
10238
|
+
},
|
|
10239
|
+
explore: {
|
|
10240
|
+
model: "xai/grok-4-1-fast",
|
|
10241
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10242
|
+
reasoningEffort: "medium",
|
|
10243
|
+
textVerbosity: "medium",
|
|
10244
|
+
temperature: 0.3,
|
|
10245
|
+
top_p: 0.95,
|
|
10246
|
+
maxTokens: 64000,
|
|
10247
|
+
thinking: {
|
|
10248
|
+
type: "enabled",
|
|
10249
|
+
budgetTokens: 16000
|
|
10250
|
+
},
|
|
10251
|
+
mode: "subagent",
|
|
10252
|
+
description: "Codebase exploration and discovery."
|
|
10253
|
+
},
|
|
10254
|
+
librarian: {
|
|
10255
|
+
model: "github-copilot/gpt-5-mini",
|
|
10256
|
+
fallback_model: "xai/grok-4-1-fast",
|
|
10257
|
+
reasoningEffort: "low",
|
|
10258
|
+
textVerbosity: "low",
|
|
10259
|
+
temperature: 0.1,
|
|
10260
|
+
top_p: 0.9,
|
|
10261
|
+
maxTokens: 32000,
|
|
10262
|
+
thinking: {
|
|
10263
|
+
type: "disabled"
|
|
10264
|
+
},
|
|
10265
|
+
mode: "subagent",
|
|
10266
|
+
description: "Cheap summarizer and retrieval helper."
|
|
10267
|
+
},
|
|
10268
|
+
"multimodal-looker": {
|
|
10269
|
+
model: "xai/grok-4-1-fast",
|
|
10270
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10271
|
+
reasoningEffort: "medium",
|
|
10272
|
+
textVerbosity: "medium",
|
|
10273
|
+
temperature: 0.2,
|
|
10274
|
+
top_p: 0.95,
|
|
10275
|
+
maxTokens: 48000,
|
|
10276
|
+
thinking: {
|
|
10277
|
+
type: "enabled",
|
|
10278
|
+
budgetTokens: 12000
|
|
10279
|
+
},
|
|
10280
|
+
mode: "subagent",
|
|
10281
|
+
description: "Image/screenshot-oriented inspection."
|
|
9768
10282
|
}
|
|
9769
10283
|
},
|
|
9770
10284
|
categories: {
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
"
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
openai: 1,
|
|
9784
|
-
anthropic: 1
|
|
10285
|
+
"fast-build": {
|
|
10286
|
+
description: "High-speed implementation path",
|
|
10287
|
+
model: "xai/grok-4-1-fast-non-reasoning",
|
|
10288
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10289
|
+
temperature: 0.1,
|
|
10290
|
+
top_p: 0.9,
|
|
10291
|
+
thinking: {
|
|
10292
|
+
type: "disabled"
|
|
10293
|
+
},
|
|
10294
|
+
reasoningEffort: "low",
|
|
10295
|
+
textVerbosity: "low",
|
|
10296
|
+
max_prompt_tokens: 120000
|
|
9785
10297
|
},
|
|
9786
|
-
|
|
9787
|
-
"
|
|
9788
|
-
"xai/grok-4-1-fast"
|
|
9789
|
-
"
|
|
10298
|
+
"deep-plan": {
|
|
10299
|
+
description: "High-quality planning path",
|
|
10300
|
+
model: "xai/grok-4-1-fast",
|
|
10301
|
+
fallback_model: "github-copilot/gpt-5-mini",
|
|
10302
|
+
temperature: 0.1,
|
|
10303
|
+
top_p: 0.9,
|
|
10304
|
+
thinking: {
|
|
10305
|
+
type: "enabled",
|
|
10306
|
+
budgetTokens: 24000
|
|
10307
|
+
},
|
|
10308
|
+
reasoningEffort: "high",
|
|
10309
|
+
textVerbosity: "medium",
|
|
10310
|
+
max_prompt_tokens: 160000
|
|
10311
|
+
},
|
|
10312
|
+
"cheap-helper": {
|
|
10313
|
+
description: "Low-cost utility path",
|
|
10314
|
+
model: "github-copilot/gpt-5-mini",
|
|
10315
|
+
fallback_model: "xai/grok-4-1-fast-non-reasoning",
|
|
10316
|
+
temperature: 0.1,
|
|
10317
|
+
top_p: 0.9,
|
|
10318
|
+
thinking: {
|
|
10319
|
+
type: "disabled"
|
|
10320
|
+
},
|
|
10321
|
+
reasoningEffort: "low",
|
|
10322
|
+
textVerbosity: "low",
|
|
10323
|
+
max_prompt_tokens: 48000
|
|
9790
10324
|
}
|
|
9791
10325
|
}
|
|
9792
10326
|
};
|
|
@@ -9978,7 +10512,11 @@ function logEventVerbose(ctx, payload) {
|
|
|
9978
10512
|
case "session.error": {
|
|
9979
10513
|
const errorProps = props;
|
|
9980
10514
|
const errorMsg = serializeError(errorProps?.error);
|
|
9981
|
-
|
|
10515
|
+
if (errorMsg.includes("[Semantic Loop Guard]")) {
|
|
10516
|
+
console.error(import_picocolors6.default.green(`${sessionTag} ${errorMsg}`));
|
|
10517
|
+
} else {
|
|
10518
|
+
console.error(import_picocolors6.default.red(`${sessionTag} SESSION.ERROR: ${errorMsg}`));
|
|
10519
|
+
}
|
|
9982
10520
|
break;
|
|
9983
10521
|
}
|
|
9984
10522
|
default:
|
|
@@ -10257,10 +10795,17 @@ function handleSessionError(ctx, payload, state) {
|
|
|
10257
10795
|
return;
|
|
10258
10796
|
const props = payload.properties;
|
|
10259
10797
|
if (getSessionId(props) === ctx.sessionID) {
|
|
10798
|
+
const errorName = props?.error?.name;
|
|
10260
10799
|
state.mainSessionError = true;
|
|
10261
|
-
|
|
10262
|
-
|
|
10800
|
+
if (errorName === "AbortError" || errorName === "MessageAbortedError") {
|
|
10801
|
+
state.lastError = "Task aborted.";
|
|
10802
|
+
console.error(import_picocolors8.default.yellow(`
|
|
10803
|
+
[session.error] ${state.lastError}`));
|
|
10804
|
+
} else {
|
|
10805
|
+
state.lastError = serializeError(props?.error);
|
|
10806
|
+
console.error(import_picocolors8.default.red(`
|
|
10263
10807
|
[session.error] ${state.lastError}`));
|
|
10808
|
+
}
|
|
10264
10809
|
}
|
|
10265
10810
|
}
|
|
10266
10811
|
function handleMessagePartUpdated(ctx, payload, state) {
|
|
@@ -24069,7 +24614,8 @@ var BuiltinAgentNameSchema = exports_external.enum([
|
|
|
24069
24614
|
"multimodal-looker",
|
|
24070
24615
|
"metis",
|
|
24071
24616
|
"momus",
|
|
24072
|
-
"atlas"
|
|
24617
|
+
"atlas",
|
|
24618
|
+
"chat"
|
|
24073
24619
|
]);
|
|
24074
24620
|
var BuiltinSkillNameSchema = exports_external.enum([
|
|
24075
24621
|
"playwright",
|
|
@@ -24092,10 +24638,19 @@ var OverridableAgentNameSchema = exports_external.enum([
|
|
|
24092
24638
|
"librarian",
|
|
24093
24639
|
"explore",
|
|
24094
24640
|
"multimodal-looker",
|
|
24095
|
-
"atlas"
|
|
24641
|
+
"atlas",
|
|
24642
|
+
"chat"
|
|
24096
24643
|
]);
|
|
24097
24644
|
// src/config/schema/fallback-models.ts
|
|
24098
|
-
var
|
|
24645
|
+
var FallbackEntrySchema = exports_external.object({
|
|
24646
|
+
providers: exports_external.array(exports_external.string()).optional(),
|
|
24647
|
+
model: exports_external.string(),
|
|
24648
|
+
variant: exports_external.string().optional()
|
|
24649
|
+
});
|
|
24650
|
+
var FallbackModelsSchema = exports_external.union([
|
|
24651
|
+
exports_external.string(),
|
|
24652
|
+
exports_external.array(exports_external.union([exports_external.string(), FallbackEntrySchema]))
|
|
24653
|
+
]);
|
|
24099
24654
|
|
|
24100
24655
|
// src/config/schema/internal/permission.ts
|
|
24101
24656
|
var PermissionValueSchema = exports_external.enum(["ask", "allow", "deny"]);
|
|
@@ -24115,6 +24670,7 @@ var AgentPermissionSchema = exports_external.object({
|
|
|
24115
24670
|
// src/config/schema/agent-overrides.ts
|
|
24116
24671
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
24117
24672
|
model: exports_external.string().optional(),
|
|
24673
|
+
fallback_model: exports_external.string().optional(),
|
|
24118
24674
|
fallback_models: FallbackModelsSchema.optional(),
|
|
24119
24675
|
variant: exports_external.string().optional(),
|
|
24120
24676
|
category: exports_external.string().optional(),
|
|
@@ -24137,6 +24693,9 @@ var AgentOverrideConfigSchema = exports_external.object({
|
|
|
24137
24693
|
reasoningEffort: exports_external.enum(["low", "medium", "high", "xhigh"]).optional(),
|
|
24138
24694
|
textVerbosity: exports_external.enum(["low", "medium", "high"]).optional(),
|
|
24139
24695
|
providerOptions: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
|
|
24696
|
+
requires_model: exports_external.string().optional(),
|
|
24697
|
+
requires_any_model: exports_external.boolean().optional(),
|
|
24698
|
+
requires_provider: exports_external.array(exports_external.string()).optional(),
|
|
24140
24699
|
ultrawork: exports_external.object({
|
|
24141
24700
|
model: exports_external.string().optional(),
|
|
24142
24701
|
variant: exports_external.string().optional()
|
|
@@ -24191,6 +24750,7 @@ var BrowserAutomationConfigSchema = exports_external.object({
|
|
|
24191
24750
|
var CategoryConfigSchema = exports_external.object({
|
|
24192
24751
|
description: exports_external.string().optional(),
|
|
24193
24752
|
model: exports_external.string().optional(),
|
|
24753
|
+
fallback_model: exports_external.string().optional(),
|
|
24194
24754
|
fallback_models: FallbackModelsSchema.optional(),
|
|
24195
24755
|
variant: exports_external.string().optional(),
|
|
24196
24756
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
@@ -24206,7 +24766,11 @@ var CategoryConfigSchema = exports_external.object({
|
|
|
24206
24766
|
prompt_append: exports_external.string().optional(),
|
|
24207
24767
|
max_prompt_tokens: exports_external.number().int().positive().optional(),
|
|
24208
24768
|
is_unstable_agent: exports_external.boolean().optional(),
|
|
24209
|
-
disable: exports_external.boolean().optional()
|
|
24769
|
+
disable: exports_external.boolean().optional(),
|
|
24770
|
+
requires_model: exports_external.string().optional(),
|
|
24771
|
+
requires_any_model: exports_external.boolean().optional(),
|
|
24772
|
+
requires_provider: exports_external.array(exports_external.string()).optional(),
|
|
24773
|
+
background_task: exports_external.never().optional().describe("Mis-nested background_task config - move to root level")
|
|
24210
24774
|
});
|
|
24211
24775
|
var BuiltinCategoryNameSchema = exports_external.enum([
|
|
24212
24776
|
"visual-engineering",
|
|
@@ -24341,7 +24905,14 @@ var HookNameSchema = exports_external.enum([
|
|
|
24341
24905
|
"write-existing-file-guard",
|
|
24342
24906
|
"anthropic-effort",
|
|
24343
24907
|
"hashline-read-enhancer",
|
|
24344
|
-
"read-image-resizer"
|
|
24908
|
+
"read-image-resizer",
|
|
24909
|
+
"execution-journal",
|
|
24910
|
+
"tool-contract",
|
|
24911
|
+
"runtime-enforcement",
|
|
24912
|
+
"plan-enforcement",
|
|
24913
|
+
"semantic-loop-guard",
|
|
24914
|
+
"edit-safeguard",
|
|
24915
|
+
"xai-usage-patch"
|
|
24345
24916
|
]);
|
|
24346
24917
|
// src/config/schema/notification.ts
|
|
24347
24918
|
var NotificationConfigSchema = exports_external.object({
|
|
@@ -24460,6 +25031,7 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
24460
25031
|
disabled_tools: exports_external.array(exports_external.string()).optional(),
|
|
24461
25032
|
hashline_edit: exports_external.boolean().optional(),
|
|
24462
25033
|
model_fallback: exports_external.boolean().optional(),
|
|
25034
|
+
fallback_model: exports_external.string().optional(),
|
|
24463
25035
|
agents: AgentOverridesSchema.optional(),
|
|
24464
25036
|
categories: CategoriesConfigSchema.optional(),
|
|
24465
25037
|
claude_code: ClaudeCodeConfigSchema.optional(),
|
|
@@ -26502,16 +27074,22 @@ function readState(directory, customPath) {
|
|
|
26502
27074
|
const str3 = String(val ?? "");
|
|
26503
27075
|
return str3.replace(/^["']|["']$/g, "");
|
|
26504
27076
|
};
|
|
27077
|
+
const ultrawork = data.ultrawork === true || data.ultrawork === "true" ? true : undefined;
|
|
27078
|
+
const maxIterations = data.max_iterations === undefined || data.max_iterations === "" ? ultrawork ? undefined : DEFAULT_MAX_ITERATIONS : Number(data.max_iterations) || DEFAULT_MAX_ITERATIONS;
|
|
26505
27079
|
return {
|
|
26506
27080
|
active: isActive,
|
|
26507
27081
|
iteration: iterationNum,
|
|
26508
|
-
max_iterations:
|
|
27082
|
+
max_iterations: maxIterations,
|
|
26509
27083
|
message_count_at_start: typeof data.message_count_at_start === "number" ? data.message_count_at_start : typeof data.message_count_at_start === "string" && data.message_count_at_start.trim() !== "" ? Number(data.message_count_at_start) : undefined,
|
|
26510
27084
|
completion_promise: stripQuotes(data.completion_promise) || DEFAULT_COMPLETION_PROMISE,
|
|
27085
|
+
initial_completion_promise: data.initial_completion_promise ? stripQuotes(data.initial_completion_promise) : undefined,
|
|
27086
|
+
verification_attempt_id: data.verification_attempt_id ? stripQuotes(data.verification_attempt_id) : undefined,
|
|
27087
|
+
verification_session_id: data.verification_session_id ? stripQuotes(data.verification_session_id) : undefined,
|
|
26511
27088
|
started_at: stripQuotes(data.started_at) || new Date().toISOString(),
|
|
26512
27089
|
prompt: body.trim(),
|
|
26513
27090
|
session_id: data.session_id ? stripQuotes(data.session_id) : undefined,
|
|
26514
|
-
ultrawork
|
|
27091
|
+
ultrawork,
|
|
27092
|
+
verification_pending: data.verification_pending === true || data.verification_pending === "true" ? true : undefined,
|
|
26515
27093
|
strategy: data.strategy === "reset" || data.strategy === "continue" ? data.strategy : undefined
|
|
26516
27094
|
};
|
|
26517
27095
|
} catch {
|
|
@@ -26568,6 +27146,11 @@ async function checkCompletionConditions(ctx) {
|
|
|
26568
27146
|
if (!areContinuationHooksIdle(ctx, continuationState)) {
|
|
26569
27147
|
return false;
|
|
26570
27148
|
}
|
|
27149
|
+
const isLegitimatelyComplete = await verifyTaskCompletionState(ctx.client, ctx.sessionID);
|
|
27150
|
+
if (!isLegitimatelyComplete) {
|
|
27151
|
+
logWaiting(ctx, "failed verification constraints or complete_task explicitly rejected");
|
|
27152
|
+
return false;
|
|
27153
|
+
}
|
|
26571
27154
|
return true;
|
|
26572
27155
|
} catch (err) {
|
|
26573
27156
|
console.error(import_picocolors14.default.red(`[completion] API error: ${err}`));
|
|
@@ -27164,7 +27747,10 @@ var CHECK_IDS = {
|
|
|
27164
27747
|
TOOLS: "tools",
|
|
27165
27748
|
MODELS: "models",
|
|
27166
27749
|
FORK: "fork",
|
|
27167
|
-
DEFAULT_CONFIG: "default-config"
|
|
27750
|
+
DEFAULT_CONFIG: "default-config",
|
|
27751
|
+
PLAN_COMPILER: "plan-compiler",
|
|
27752
|
+
PROGRESS: "progress",
|
|
27753
|
+
EDIT_ATOMICITY: "edit-atomicity"
|
|
27168
27754
|
};
|
|
27169
27755
|
var CHECK_NAMES = {
|
|
27170
27756
|
[CHECK_IDS.SYSTEM]: "System",
|
|
@@ -27172,7 +27758,10 @@ var CHECK_NAMES = {
|
|
|
27172
27758
|
[CHECK_IDS.TOOLS]: "Tools",
|
|
27173
27759
|
[CHECK_IDS.MODELS]: "Models",
|
|
27174
27760
|
[CHECK_IDS.FORK]: "Fork Integrity",
|
|
27175
|
-
[CHECK_IDS.DEFAULT_CONFIG]: "Default Config"
|
|
27761
|
+
[CHECK_IDS.DEFAULT_CONFIG]: "Default Config",
|
|
27762
|
+
[CHECK_IDS.PLAN_COMPILER]: "Plan Compiler",
|
|
27763
|
+
[CHECK_IDS.PROGRESS]: "Subagent Progress",
|
|
27764
|
+
[CHECK_IDS.EDIT_ATOMICITY]: "Edit Atomicity & Validation"
|
|
27176
27765
|
};
|
|
27177
27766
|
var EXIT_CODES = {
|
|
27178
27767
|
SUCCESS: 0,
|
|
@@ -27654,19 +28243,19 @@ function buildModelResolutionDetails(options) {
|
|
|
27654
28243
|
details.push("");
|
|
27655
28244
|
details.push("Agents:");
|
|
27656
28245
|
for (const agent of options.info.agents) {
|
|
27657
|
-
const marker = agent.userOverride ? "\u25CF" : "\u25CB";
|
|
28246
|
+
const marker = agent.userOverride ? "\u25CF" : agent.requirement.fallbackModel ? "\u25D3" : "\u25CB";
|
|
27658
28247
|
const display = formatModelWithVariant(agent.effectiveModel, getEffectiveVariant(agent.name, agent.requirement, options.config));
|
|
27659
28248
|
details.push(` ${marker} ${agent.name}: ${display}`);
|
|
27660
28249
|
}
|
|
27661
28250
|
details.push("");
|
|
27662
28251
|
details.push("Categories:");
|
|
27663
28252
|
for (const category of options.info.categories) {
|
|
27664
|
-
const marker = category.userOverride ? "\u25CF" : "\u25CB";
|
|
28253
|
+
const marker = category.userOverride ? "\u25CF" : category.requirement.fallbackModel ? "\u25D3" : "\u25CB";
|
|
27665
28254
|
const display = formatModelWithVariant(category.effectiveModel, getCategoryEffectiveVariant(category.name, category.requirement, options.config));
|
|
27666
28255
|
details.push(` ${marker} ${category.name}: ${display}`);
|
|
27667
28256
|
}
|
|
27668
28257
|
details.push("");
|
|
27669
|
-
details.push("\u25CF = user override, \u25CB = provider fallback");
|
|
28258
|
+
details.push("\u25CF = user override, \u25D3 = config fallback (fallback_model), \u25CB = provider fallback");
|
|
27670
28259
|
return details;
|
|
27671
28260
|
}
|
|
27672
28261
|
|
|
@@ -27678,6 +28267,9 @@ function getEffectiveModel(requirement, userOverride) {
|
|
|
27678
28267
|
if (userOverride) {
|
|
27679
28268
|
return userOverride;
|
|
27680
28269
|
}
|
|
28270
|
+
if (requirement.fallbackModel) {
|
|
28271
|
+
return requirement.fallbackModel;
|
|
28272
|
+
}
|
|
27681
28273
|
const firstEntry = requirement.fallbackChain[0];
|
|
27682
28274
|
if (!firstEntry) {
|
|
27683
28275
|
return "unknown";
|
|
@@ -27688,6 +28280,9 @@ function buildEffectiveResolution(requirement, userOverride) {
|
|
|
27688
28280
|
if (userOverride) {
|
|
27689
28281
|
return `User override: ${userOverride}`;
|
|
27690
28282
|
}
|
|
28283
|
+
if (requirement.fallbackModel) {
|
|
28284
|
+
return `Config fallback: ${requirement.fallbackModel}`;
|
|
28285
|
+
}
|
|
27691
28286
|
const firstEntry = requirement.fallbackChain[0];
|
|
27692
28287
|
if (!firstEntry) {
|
|
27693
28288
|
return "No fallback chain defined";
|
|
@@ -28415,6 +29010,142 @@ async function checkFork() {
|
|
|
28415
29010
|
};
|
|
28416
29011
|
}
|
|
28417
29012
|
|
|
29013
|
+
// src/runtime/plan-compiler.ts
|
|
29014
|
+
class PlanCompiler {
|
|
29015
|
+
static instance;
|
|
29016
|
+
sessionStates = new Map;
|
|
29017
|
+
constructor() {}
|
|
29018
|
+
static getInstance() {
|
|
29019
|
+
if (!PlanCompiler.instance) {
|
|
29020
|
+
PlanCompiler.instance = new PlanCompiler;
|
|
29021
|
+
}
|
|
29022
|
+
return PlanCompiler.instance;
|
|
29023
|
+
}
|
|
29024
|
+
submit(sessionID, steps) {
|
|
29025
|
+
const taskID = Math.random().toString(36).substring(7);
|
|
29026
|
+
const graph = this.injectVerificationDependencies(steps.map((n) => ({ ...n, status: "pending" })));
|
|
29027
|
+
this.sessionStates.set(sessionID, {
|
|
29028
|
+
graph,
|
|
29029
|
+
currentStepIndex: 0,
|
|
29030
|
+
taskID
|
|
29031
|
+
});
|
|
29032
|
+
return taskID;
|
|
29033
|
+
}
|
|
29034
|
+
getActiveStep(sessionID) {
|
|
29035
|
+
const state = this.sessionStates.get(sessionID);
|
|
29036
|
+
if (!state)
|
|
29037
|
+
return null;
|
|
29038
|
+
if (state.currentStepIndex >= 0 && state.currentStepIndex < state.graph.length) {
|
|
29039
|
+
return state.graph[state.currentStepIndex];
|
|
29040
|
+
}
|
|
29041
|
+
return null;
|
|
29042
|
+
}
|
|
29043
|
+
markStepComplete(sessionID, id) {
|
|
29044
|
+
const state = this.sessionStates.get(sessionID);
|
|
29045
|
+
if (!state)
|
|
29046
|
+
return;
|
|
29047
|
+
const node = state.graph.find((n) => n.id === id);
|
|
29048
|
+
if (node) {
|
|
29049
|
+
node.status = "completed";
|
|
29050
|
+
state.currentStepIndex++;
|
|
29051
|
+
if (state.currentStepIndex >= state.graph.length) {
|
|
29052
|
+
this.clear(sessionID);
|
|
29053
|
+
}
|
|
29054
|
+
}
|
|
29055
|
+
}
|
|
29056
|
+
injectForcedReplan(sessionID, reason) {
|
|
29057
|
+
const state = this.sessionStates.get(sessionID);
|
|
29058
|
+
if (!state)
|
|
29059
|
+
return;
|
|
29060
|
+
const active = this.getActiveStep(sessionID);
|
|
29061
|
+
if (active) {
|
|
29062
|
+
active.status = "failed";
|
|
29063
|
+
}
|
|
29064
|
+
this.submit(sessionID, [
|
|
29065
|
+
{
|
|
29066
|
+
id: "forced_replan_" + Date.now(),
|
|
29067
|
+
action: "submit_plan",
|
|
29068
|
+
dependencies: []
|
|
29069
|
+
}
|
|
29070
|
+
]);
|
|
29071
|
+
}
|
|
29072
|
+
clear(sessionID) {
|
|
29073
|
+
this.sessionStates.delete(sessionID);
|
|
29074
|
+
}
|
|
29075
|
+
resetAll() {
|
|
29076
|
+
this.sessionStates.clear();
|
|
29077
|
+
}
|
|
29078
|
+
injectVerificationDependencies(nodes) {
|
|
29079
|
+
const compiled = [];
|
|
29080
|
+
for (const node of nodes) {
|
|
29081
|
+
compiled.push(node);
|
|
29082
|
+
if (node.action.includes("commit") || node.action.includes("push") || node.action.includes("fix")) {
|
|
29083
|
+
compiled.push({
|
|
29084
|
+
id: `${node.id}_verify`,
|
|
29085
|
+
action: "verify_action",
|
|
29086
|
+
dependencies: [node.id],
|
|
29087
|
+
status: "pending"
|
|
29088
|
+
});
|
|
29089
|
+
}
|
|
29090
|
+
}
|
|
29091
|
+
return compiled;
|
|
29092
|
+
}
|
|
29093
|
+
}
|
|
29094
|
+
var compiler = PlanCompiler.getInstance();
|
|
29095
|
+
|
|
29096
|
+
// src/cli/doctor/checks/plan-compiler.ts
|
|
29097
|
+
async function checkPlanCompiler() {
|
|
29098
|
+
const findings = [];
|
|
29099
|
+
let passed = true;
|
|
29100
|
+
try {
|
|
29101
|
+
const sessionA = "doctor-session-a-" + Date.now();
|
|
29102
|
+
const sessionB = "doctor-session-b-" + Date.now();
|
|
29103
|
+
compiler.submit(sessionA, [
|
|
29104
|
+
{ id: "step1", action: "exclusive-action-a", dependencies: [] }
|
|
29105
|
+
]);
|
|
29106
|
+
const activeA = compiler.getActiveStep(sessionA);
|
|
29107
|
+
const activeB = compiler.getActiveStep(sessionB);
|
|
29108
|
+
if (activeA?.action !== "exclusive-action-a") {
|
|
29109
|
+
passed = false;
|
|
29110
|
+
findings.push(`${SYMBOLS3.cross} Session A plan not correctly stored.`);
|
|
29111
|
+
}
|
|
29112
|
+
if (activeB !== null) {
|
|
29113
|
+
passed = false;
|
|
29114
|
+
findings.push(`${SYMBOLS3.cross} Session B is polluted by Session A's plan. Cross-session leakage detected!`);
|
|
29115
|
+
} else {
|
|
29116
|
+
findings.push(`${SYMBOLS3.check} Session isolation verified.`);
|
|
29117
|
+
}
|
|
29118
|
+
compiler.clear(sessionA);
|
|
29119
|
+
if (compiler.getActiveStep(sessionA) !== null) {
|
|
29120
|
+
passed = false;
|
|
29121
|
+
findings.push(`${SYMBOLS3.cross} Manual unlock failed to clear session state.`);
|
|
29122
|
+
} else {
|
|
29123
|
+
findings.push(`${SYMBOLS3.check} Manual unlock (clear) verified.`);
|
|
29124
|
+
}
|
|
29125
|
+
const taskID1 = compiler.submit(sessionA, [{ id: "s1", action: "a1", dependencies: [] }]);
|
|
29126
|
+
const taskID2 = compiler.submit(sessionA, [{ id: "s2", action: "a2", dependencies: [] }]);
|
|
29127
|
+
if (taskID1 === taskID2) {
|
|
29128
|
+
findings.push(`${SYMBOLS3.warn} Task IDs are not unique across subsequent submits in the same session.`);
|
|
29129
|
+
} else {
|
|
29130
|
+
findings.push(`${SYMBOLS3.check} Task ID rotation verified.`);
|
|
29131
|
+
}
|
|
29132
|
+
} catch (error48) {
|
|
29133
|
+
passed = false;
|
|
29134
|
+
findings.push(`${SYMBOLS3.cross} Plan Compiler check crashed: ${error48.message}`);
|
|
29135
|
+
}
|
|
29136
|
+
return {
|
|
29137
|
+
name: "Plan Compiler",
|
|
29138
|
+
status: passed ? "pass" : "fail",
|
|
29139
|
+
message: passed ? "Plan Compiler is healthy and correctly isolating sessions." : "Plan Compiler has integrity issues (isolation or recovery failure).",
|
|
29140
|
+
details: findings,
|
|
29141
|
+
issues: passed ? [] : [{
|
|
29142
|
+
title: "Plan Compiler integrity failure",
|
|
29143
|
+
description: "Deterministic plan enforcement or session isolation is broken.",
|
|
29144
|
+
severity: "error"
|
|
29145
|
+
}]
|
|
29146
|
+
};
|
|
29147
|
+
}
|
|
29148
|
+
|
|
28418
29149
|
// src/cli/doctor/checks/default-config.ts
|
|
28419
29150
|
import { existsSync as existsSync28, readFileSync as readFileSync28 } from "fs";
|
|
28420
29151
|
import * as path10 from "path";
|
|
@@ -28528,22 +29259,1534 @@ async function checkDefaultConfig() {
|
|
|
28528
29259
|
issues
|
|
28529
29260
|
};
|
|
28530
29261
|
}
|
|
28531
|
-
|
|
28532
|
-
|
|
28533
|
-
|
|
28534
|
-
|
|
28535
|
-
|
|
28536
|
-
|
|
28537
|
-
|
|
28538
|
-
|
|
28539
|
-
|
|
28540
|
-
|
|
28541
|
-
|
|
28542
|
-
|
|
28543
|
-
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
|
|
29262
|
+
|
|
29263
|
+
// src/shared/active-task-storage.ts
|
|
29264
|
+
init_opencode_config_dir();
|
|
29265
|
+
init_logger();
|
|
29266
|
+
import { join as join28 } from "path";
|
|
29267
|
+
import { writeFileSync as writeFileSync11, readFileSync as readFileSync29, existsSync as existsSync29, mkdirSync as mkdirSync7 } from "fs";
|
|
29268
|
+
var STORAGE_FILENAME = "active_background_tasks.json";
|
|
29269
|
+
function getStoragePath() {
|
|
29270
|
+
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
29271
|
+
if (!existsSync29(configDir)) {
|
|
29272
|
+
mkdirSync7(configDir, { recursive: true });
|
|
29273
|
+
}
|
|
29274
|
+
return join28(configDir, STORAGE_FILENAME);
|
|
29275
|
+
}
|
|
29276
|
+
function saveActiveTasks(tasks) {
|
|
29277
|
+
try {
|
|
29278
|
+
const path11 = getStoragePath();
|
|
29279
|
+
writeFileSync11(path11, JSON.stringify(tasks, null, 2), "utf-8");
|
|
29280
|
+
} catch (err) {
|
|
29281
|
+
log("[active-task-storage] Failed to save active tasks:", err);
|
|
29282
|
+
}
|
|
29283
|
+
}
|
|
29284
|
+
function readActiveTasks() {
|
|
29285
|
+
try {
|
|
29286
|
+
const path11 = getStoragePath();
|
|
29287
|
+
if (!existsSync29(path11))
|
|
29288
|
+
return [];
|
|
29289
|
+
const content = readFileSync29(path11, "utf-8");
|
|
29290
|
+
return JSON.parse(content);
|
|
29291
|
+
} catch (err) {
|
|
29292
|
+
log("[active-task-storage] Failed to read active tasks:", err);
|
|
29293
|
+
return [];
|
|
29294
|
+
}
|
|
29295
|
+
}
|
|
29296
|
+
|
|
29297
|
+
// src/cli/doctor/checks/progress.ts
|
|
29298
|
+
async function checkProgress() {
|
|
29299
|
+
const findings = [];
|
|
29300
|
+
let passed = true;
|
|
29301
|
+
try {
|
|
29302
|
+
const testTask = {
|
|
29303
|
+
id: "doctor-test-task-" + Date.now(),
|
|
29304
|
+
sessionID: "test-session",
|
|
29305
|
+
description: "Doctor progress check",
|
|
29306
|
+
agent: "sisyphus",
|
|
29307
|
+
status: "running",
|
|
29308
|
+
startedAt: new Date().toISOString(),
|
|
29309
|
+
progress: {
|
|
29310
|
+
phase: "Testing",
|
|
29311
|
+
percent: 50,
|
|
29312
|
+
message: "Halfway there"
|
|
29313
|
+
}
|
|
29314
|
+
};
|
|
29315
|
+
const originalTasks = readActiveTasks();
|
|
29316
|
+
saveActiveTasks([...originalTasks, testTask]);
|
|
29317
|
+
const updatedTasks = readActiveTasks();
|
|
29318
|
+
const found = updatedTasks.find((t) => t.id === testTask.id);
|
|
29319
|
+
if (!found) {
|
|
29320
|
+
passed = false;
|
|
29321
|
+
findings.push(`${SYMBOLS3.cross} Failed to persist active task.`);
|
|
29322
|
+
} else if (found.progress?.percent !== 50) {
|
|
29323
|
+
passed = false;
|
|
29324
|
+
findings.push(`${SYMBOLS3.cross} Persisted progress data is incorrect.`);
|
|
29325
|
+
} else {
|
|
29326
|
+
findings.push(`${SYMBOLS3.check} Progress storage persistence verified.`);
|
|
29327
|
+
}
|
|
29328
|
+
saveActiveTasks(originalTasks);
|
|
29329
|
+
} catch (error48) {
|
|
29330
|
+
passed = false;
|
|
29331
|
+
findings.push(`${SYMBOLS3.cross} Progress check crashed: ${error48.message}`);
|
|
29332
|
+
}
|
|
29333
|
+
return {
|
|
29334
|
+
name: "Subagent Progress",
|
|
29335
|
+
status: passed ? "pass" : "fail",
|
|
29336
|
+
message: passed ? "Subagent progress tracking is healthy." : "Subagent progress tracking has issues.",
|
|
29337
|
+
issues: passed ? [] : [{
|
|
29338
|
+
title: "Progress tracking failure",
|
|
29339
|
+
description: "The system failed to persist or retrieve background task progress.",
|
|
29340
|
+
severity: "error"
|
|
29341
|
+
}]
|
|
29342
|
+
};
|
|
29343
|
+
}
|
|
29344
|
+
|
|
29345
|
+
// src/runtime/tools/fs-safe.ts
|
|
29346
|
+
import * as fs11 from "fs";
|
|
29347
|
+
import * as path11 from "path";
|
|
29348
|
+
|
|
29349
|
+
// node_modules/@opencode-ai/plugin/dist/tool.js
|
|
29350
|
+
function tool(input) {
|
|
29351
|
+
return input;
|
|
29352
|
+
}
|
|
29353
|
+
tool.schema = exports_external;
|
|
29354
|
+
// src/utils/safety-tool-result.ts
|
|
29355
|
+
function createSuccessResult(params) {
|
|
29356
|
+
const result = {
|
|
29357
|
+
success: true,
|
|
29358
|
+
verified: params.verified,
|
|
29359
|
+
changedState: params.changedState,
|
|
29360
|
+
message: params.message
|
|
29361
|
+
};
|
|
29362
|
+
if (params.changedState) {
|
|
29363
|
+
if (!params.stateChange) {
|
|
29364
|
+
throw new Error("[Safety Helper] 'stateChange' metadata is required when 'changedState' is true.");
|
|
29365
|
+
}
|
|
29366
|
+
result.stateChange = params.stateChange;
|
|
29367
|
+
}
|
|
29368
|
+
return result;
|
|
29369
|
+
}
|
|
29370
|
+
function createFailureResult(message) {
|
|
29371
|
+
return {
|
|
29372
|
+
success: false,
|
|
29373
|
+
verified: false,
|
|
29374
|
+
changedState: false,
|
|
29375
|
+
message
|
|
29376
|
+
};
|
|
29377
|
+
}
|
|
29378
|
+
|
|
29379
|
+
// src/features/tool-metadata-store/store.ts
|
|
29380
|
+
var pendingStore = new Map;
|
|
29381
|
+
var STALE_TIMEOUT_MS = 15 * 60 * 1000;
|
|
29382
|
+
function makeKey(sessionID, callID) {
|
|
29383
|
+
return `${sessionID}:${callID}`;
|
|
29384
|
+
}
|
|
29385
|
+
function cleanupStaleEntries() {
|
|
29386
|
+
const now = Date.now();
|
|
29387
|
+
for (const [key, entry] of pendingStore) {
|
|
29388
|
+
if (now - entry.storedAt > STALE_TIMEOUT_MS) {
|
|
29389
|
+
pendingStore.delete(key);
|
|
29390
|
+
}
|
|
29391
|
+
}
|
|
29392
|
+
}
|
|
29393
|
+
function storeToolMetadata(sessionID, callID, data) {
|
|
29394
|
+
cleanupStaleEntries();
|
|
29395
|
+
pendingStore.set(makeKey(sessionID, callID), { ...data, storedAt: Date.now() });
|
|
29396
|
+
}
|
|
29397
|
+
function consumeToolMetadata(sessionID, callID) {
|
|
29398
|
+
const key = makeKey(sessionID, callID);
|
|
29399
|
+
const stored = pendingStore.get(key);
|
|
29400
|
+
if (stored) {
|
|
29401
|
+
pendingStore.delete(key);
|
|
29402
|
+
const { storedAt: _3, ...data } = stored;
|
|
29403
|
+
return data;
|
|
29404
|
+
}
|
|
29405
|
+
return;
|
|
29406
|
+
}
|
|
29407
|
+
function clearPendingStore() {
|
|
29408
|
+
pendingStore.clear();
|
|
29409
|
+
}
|
|
29410
|
+
// src/utils/tool-contract-wrapper.ts
|
|
29411
|
+
function withToolContract(toolName, executeFn) {
|
|
29412
|
+
const safetyCriticalTools = ["git_safe", "fs_safe", "verify_action", "submit_plan", "mark_step_complete", "unlock_plan", "query_ledger", "complete_task", "report_issue_verification"];
|
|
29413
|
+
return async (args, context) => {
|
|
29414
|
+
let metadataCalled = false;
|
|
29415
|
+
let lastMetadata = null;
|
|
29416
|
+
const originalMetadata = context.metadata.bind(context);
|
|
29417
|
+
context.metadata = (data) => {
|
|
29418
|
+
metadataCalled = true;
|
|
29419
|
+
let flattened = data;
|
|
29420
|
+
if (data && typeof data === "object") {
|
|
29421
|
+
if (data.metadata && typeof data.metadata === "object") {
|
|
29422
|
+
const { metadata, ...rest } = data;
|
|
29423
|
+
flattened = { ...rest, ...metadata };
|
|
29424
|
+
}
|
|
29425
|
+
}
|
|
29426
|
+
lastMetadata = flattened;
|
|
29427
|
+
if (context.callID) {
|
|
29428
|
+
const pendingData = {
|
|
29429
|
+
title: flattened.title || toolName,
|
|
29430
|
+
metadata: flattened
|
|
29431
|
+
};
|
|
29432
|
+
storeToolMetadata(context.sessionID, context.callID, pendingData);
|
|
29433
|
+
}
|
|
29434
|
+
return originalMetadata(flattened);
|
|
29435
|
+
};
|
|
29436
|
+
let executionMessage;
|
|
29437
|
+
try {
|
|
29438
|
+
executionMessage = await executeFn(args, context);
|
|
29439
|
+
if (!metadataCalled) {
|
|
29440
|
+
const result = createFailureResult(`[Tool Contract Violation] Tool ${toolName} finished without calling context.metadata().`);
|
|
29441
|
+
const meta3 = {
|
|
29442
|
+
title: `${toolName} protocol error`,
|
|
29443
|
+
...result,
|
|
29444
|
+
success: false,
|
|
29445
|
+
verified: false
|
|
29446
|
+
};
|
|
29447
|
+
context.metadata(meta3);
|
|
29448
|
+
return result.message;
|
|
29449
|
+
}
|
|
29450
|
+
} catch (error48) {
|
|
29451
|
+
const result = createFailureResult(`Exception in ${toolName}: ${error48.message}`);
|
|
29452
|
+
const meta3 = {
|
|
29453
|
+
title: `${toolName} failed`,
|
|
29454
|
+
...result,
|
|
29455
|
+
success: false,
|
|
29456
|
+
verified: false
|
|
29457
|
+
};
|
|
29458
|
+
context.metadata(meta3);
|
|
29459
|
+
return result.message || error48.message;
|
|
29460
|
+
}
|
|
29461
|
+
return executionMessage;
|
|
29462
|
+
};
|
|
29463
|
+
}
|
|
29464
|
+
|
|
29465
|
+
// src/runtime/tools/fs-safe.ts
|
|
29466
|
+
function createFsSafeTool() {
|
|
29467
|
+
return tool({
|
|
29468
|
+
description: "Safe, structured execution of filesystem writes. Returns verifiable status.",
|
|
29469
|
+
args: {
|
|
29470
|
+
operation: exports_external.enum(["write", "delete", "mkdir"]).describe("The filesystem operation to perform."),
|
|
29471
|
+
filePath: exports_external.string().describe("Absolute or relative path to the file/directory."),
|
|
29472
|
+
content: exports_external.string().optional().describe("File content (only used if operation is 'write').")
|
|
29473
|
+
},
|
|
29474
|
+
execute: withToolContract("fs_safe", async (args, context) => {
|
|
29475
|
+
try {
|
|
29476
|
+
const { operation, filePath, content } = args;
|
|
29477
|
+
const fullPath = path11.resolve(context.directory || process.cwd(), filePath);
|
|
29478
|
+
let changedState = false;
|
|
29479
|
+
let stateChangePayload = null;
|
|
29480
|
+
if (operation === "write") {
|
|
29481
|
+
const dir = path11.dirname(fullPath);
|
|
29482
|
+
if (!fs11.existsSync(dir)) {
|
|
29483
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
29484
|
+
}
|
|
29485
|
+
fs11.writeFileSync(fullPath, content || "", "utf8");
|
|
29486
|
+
changedState = true;
|
|
29487
|
+
stateChangePayload = { type: "file.write", key: filePath, details: { fullPath } };
|
|
29488
|
+
} else if (operation === "delete") {
|
|
29489
|
+
if (fs11.existsSync(fullPath)) {
|
|
29490
|
+
const stats = fs11.statSync(fullPath);
|
|
29491
|
+
if (stats.isDirectory()) {
|
|
29492
|
+
fs11.rmSync(fullPath, { recursive: true, force: true });
|
|
29493
|
+
} else {
|
|
29494
|
+
fs11.unlinkSync(fullPath);
|
|
29495
|
+
}
|
|
29496
|
+
changedState = true;
|
|
29497
|
+
stateChangePayload = { type: "file.delete", key: filePath, details: { fullPath } };
|
|
29498
|
+
}
|
|
29499
|
+
} else if (operation === "mkdir") {
|
|
29500
|
+
if (!fs11.existsSync(fullPath)) {
|
|
29501
|
+
fs11.mkdirSync(fullPath, { recursive: true });
|
|
29502
|
+
changedState = true;
|
|
29503
|
+
stateChangePayload = { type: "command.execute", key: `mkdir ${filePath}`, details: { fullPath } };
|
|
29504
|
+
}
|
|
29505
|
+
}
|
|
29506
|
+
const result = createSuccessResult({
|
|
29507
|
+
verified: true,
|
|
29508
|
+
changedState,
|
|
29509
|
+
stateChange: stateChangePayload || undefined
|
|
29510
|
+
});
|
|
29511
|
+
context.metadata({
|
|
29512
|
+
title: `fs ${operation}`,
|
|
29513
|
+
...result
|
|
29514
|
+
});
|
|
29515
|
+
return `Successfully executed ${operation} on ${filePath}`;
|
|
29516
|
+
} catch (err) {
|
|
29517
|
+
const result = createFailureResult(`Failed: ${err.message}`);
|
|
29518
|
+
context.metadata({
|
|
29519
|
+
title: `fs ${args.operation} error`,
|
|
29520
|
+
...result
|
|
29521
|
+
});
|
|
29522
|
+
return result.message;
|
|
29523
|
+
}
|
|
29524
|
+
})
|
|
29525
|
+
});
|
|
29526
|
+
}
|
|
29527
|
+
|
|
29528
|
+
// src/runtime/tools/git-safe.ts
|
|
29529
|
+
var {spawn: spawn2 } = globalThis.Bun;
|
|
29530
|
+
function createGitSafeTool() {
|
|
29531
|
+
return tool({
|
|
29532
|
+
description: "Safe, structured execution of git commands. Returns verifiable status.",
|
|
29533
|
+
args: {
|
|
29534
|
+
command: exports_external.string().describe(`The git command without the 'git ' prefix (e.g. 'commit -m "msg"', 'push origin main')`)
|
|
29535
|
+
},
|
|
29536
|
+
execute: withToolContract("git_safe", async (args, context) => {
|
|
29537
|
+
let commandArgs = [];
|
|
29538
|
+
try {
|
|
29539
|
+
commandArgs = args.command.match(/([^\\"]\S*|".+?")\s*/g)?.map((s) => s.trim().replace(/^"(.*)"$/, "$1")) || [];
|
|
29540
|
+
if (commandArgs.length === 0) {
|
|
29541
|
+
const result2 = createFailureResult("No command provided");
|
|
29542
|
+
context.metadata({ title: "Git Exec Error", ...result2 });
|
|
29543
|
+
return result2.message;
|
|
29544
|
+
}
|
|
29545
|
+
const proc = spawn2(["git", ...commandArgs], {
|
|
29546
|
+
cwd: context.directory || process.cwd(),
|
|
29547
|
+
stdout: "pipe",
|
|
29548
|
+
stderr: "pipe"
|
|
29549
|
+
});
|
|
29550
|
+
const stdoutText = await new Response(proc.stdout).text();
|
|
29551
|
+
const stderrText = await new Response(proc.stderr).text();
|
|
29552
|
+
const exitCode = await proc.exited;
|
|
29553
|
+
const success2 = exitCode === 0;
|
|
29554
|
+
const isPush = commandArgs[0] === "push";
|
|
29555
|
+
const isCommit = commandArgs[0] === "commit";
|
|
29556
|
+
const isStateChanging = ["commit", "push", "checkout", "branch", "rebase", "merge", "reset", "revert", "clean", "rm", "add"].includes(commandArgs[0]);
|
|
29557
|
+
const changedState = success2 && isStateChanging;
|
|
29558
|
+
let stateChangePayload = null;
|
|
29559
|
+
if (changedState) {
|
|
29560
|
+
if (isPush)
|
|
29561
|
+
stateChangePayload = { type: "git.push", key: "origin", details: { exitCode } };
|
|
29562
|
+
else if (isCommit)
|
|
29563
|
+
stateChangePayload = { type: "git.commit", key: "HEAD", details: { exitCode } };
|
|
29564
|
+
else
|
|
29565
|
+
stateChangePayload = { type: "command.execute", key: `git ${commandArgs[0]}`, details: { exitCode } };
|
|
29566
|
+
}
|
|
29567
|
+
const result = createSuccessResult({
|
|
29568
|
+
verified: !isStateChanging,
|
|
29569
|
+
changedState,
|
|
29570
|
+
stateChange: stateChangePayload || undefined
|
|
29571
|
+
});
|
|
29572
|
+
context.metadata({
|
|
29573
|
+
title: `git ${commandArgs[0]}`,
|
|
29574
|
+
...result
|
|
29575
|
+
});
|
|
29576
|
+
return `Exit Code: ${exitCode}
|
|
29577
|
+
|
|
29578
|
+
STDOUT:
|
|
29579
|
+
${stdoutText}
|
|
29580
|
+
|
|
29581
|
+
STDERR:
|
|
29582
|
+
${stderrText}`;
|
|
29583
|
+
} catch (e2) {
|
|
29584
|
+
const result = createFailureResult(`JSON parse error on args or execution failed: ${e2.message}`);
|
|
29585
|
+
context.metadata({ title: "Git Exec Error", ...result });
|
|
29586
|
+
return result.message;
|
|
29587
|
+
}
|
|
29588
|
+
})
|
|
29589
|
+
});
|
|
29590
|
+
}
|
|
29591
|
+
|
|
29592
|
+
// src/runtime/tools/plan.ts
|
|
29593
|
+
function createSubmitPlanTool() {
|
|
29594
|
+
return tool({
|
|
29595
|
+
description: "Submits a deterministic execution plan. The Runtime Compiler will take over and guide you strictly through the resulting dependency graph.",
|
|
29596
|
+
args: {
|
|
29597
|
+
steps: exports_external.array(exports_external.object({
|
|
29598
|
+
id: exports_external.string().describe("Unique step ID (e.g. 'step1')"),
|
|
29599
|
+
action: exports_external.string().describe("The high-level action (e.g. 'run_tests', 'fix_linter', 'commit')"),
|
|
29600
|
+
dependencies: exports_external.array(exports_external.string()).describe("IDs of steps that must complete before this one")
|
|
29601
|
+
})).describe("The execution DAG (Directed Acyclic Graph) of operations")
|
|
29602
|
+
},
|
|
29603
|
+
execute: withToolContract("submit_plan", async (args, toolContext) => {
|
|
29604
|
+
const taskID = compiler.submit(toolContext.sessionID, args.steps);
|
|
29605
|
+
const result = createSuccessResult({
|
|
29606
|
+
verified: true,
|
|
29607
|
+
changedState: false,
|
|
29608
|
+
metadata: { planLength: args.steps.length, taskID }
|
|
29609
|
+
});
|
|
29610
|
+
toolContext.metadata({
|
|
29611
|
+
title: "Plan Submitted",
|
|
29612
|
+
...result
|
|
29613
|
+
});
|
|
29614
|
+
const active = compiler.getActiveStep(toolContext.sessionID);
|
|
29615
|
+
return `Plan successfully compiled into an executable graph (including implicit verification nodes).
|
|
29616
|
+
|
|
29617
|
+
CURRENT FORCED STEP: ${active?.action} (ID: ${active?.id}).
|
|
29618
|
+
Do not execute any other tools until this step is complete.`;
|
|
29619
|
+
})
|
|
29620
|
+
});
|
|
29621
|
+
}
|
|
29622
|
+
function createMarkStepCompleteTool() {
|
|
29623
|
+
return tool({
|
|
29624
|
+
description: "Marks the current forced execution step as complete and retrieves the next step.",
|
|
29625
|
+
args: {
|
|
29626
|
+
id: exports_external.string().describe("The ID of the step that was completed")
|
|
29627
|
+
},
|
|
29628
|
+
execute: withToolContract("mark_step_complete", async (args, toolContext) => {
|
|
29629
|
+
compiler.markStepComplete(toolContext.sessionID, args.id);
|
|
29630
|
+
const result = createSuccessResult({
|
|
29631
|
+
verified: true,
|
|
29632
|
+
changedState: false,
|
|
29633
|
+
metadata: { stepId: args.id }
|
|
29634
|
+
});
|
|
29635
|
+
toolContext.metadata({
|
|
29636
|
+
title: `Step ${args.id} Complete`,
|
|
29637
|
+
...result
|
|
29638
|
+
});
|
|
29639
|
+
const active = compiler.getActiveStep(toolContext.sessionID);
|
|
29640
|
+
if (!active) {
|
|
29641
|
+
return `Step ${args.id} marked complete. The plan graph is now fully exhausted. You may report final success to the user.`;
|
|
29642
|
+
}
|
|
29643
|
+
return `Step ${args.id} marked complete.
|
|
29644
|
+
|
|
29645
|
+
NEXT FORCED STEP: ${active.action} (ID: ${active.id}).`;
|
|
29646
|
+
})
|
|
29647
|
+
});
|
|
29648
|
+
}
|
|
29649
|
+
function createUnlockPlanTool() {
|
|
29650
|
+
return tool({
|
|
29651
|
+
description: "Manually unlocks the deterministic execution plan, clearing any active steps for the current session.",
|
|
29652
|
+
args: {},
|
|
29653
|
+
execute: withToolContract("unlock_plan", async (_3, toolContext) => {
|
|
29654
|
+
compiler.clear(toolContext.sessionID);
|
|
29655
|
+
const result = createSuccessResult({
|
|
29656
|
+
verified: true,
|
|
29657
|
+
changedState: false
|
|
29658
|
+
});
|
|
29659
|
+
toolContext.metadata({
|
|
29660
|
+
title: "Plan Unlocked",
|
|
29661
|
+
...result
|
|
29662
|
+
});
|
|
29663
|
+
return "The deterministic execution plan has been cleared for this session. You are now in freestyle mode.";
|
|
29664
|
+
})
|
|
29665
|
+
});
|
|
29666
|
+
}
|
|
29667
|
+
|
|
29668
|
+
// src/runtime/tools/verify.ts
|
|
29669
|
+
var {spawn: spawn3 } = globalThis.Bun;
|
|
29670
|
+
|
|
29671
|
+
// src/agents/runtime/verify-action.ts
|
|
29672
|
+
var VERIFICATION_COMMANDS = {
|
|
29673
|
+
gitPush: {
|
|
29674
|
+
name: "Git Push Verification",
|
|
29675
|
+
command: "git rev-list --count origin/${BRANCH}..HEAD",
|
|
29676
|
+
successCondition: 'output === "0"',
|
|
29677
|
+
failureMessage: "Push failed \u2014 unpushed commits remain"
|
|
29678
|
+
},
|
|
29679
|
+
gitCommit: {
|
|
29680
|
+
name: "Git Commit Verification",
|
|
29681
|
+
command: 'git log -1 --format="%H"',
|
|
29682
|
+
successCondition: "output !== previousHash",
|
|
29683
|
+
failureMessage: "Commit failed \u2014 hash unchanged from before work"
|
|
29684
|
+
},
|
|
29685
|
+
prCreated: {
|
|
29686
|
+
name: "PR Creation Verification",
|
|
29687
|
+
command: 'gh pr view --json url --jq ".url"',
|
|
29688
|
+
successCondition: 'output starts with "https://"',
|
|
29689
|
+
failureMessage: "PR not found \u2014 do not fabricate URL"
|
|
29690
|
+
},
|
|
29691
|
+
cleanWorkDir: {
|
|
29692
|
+
name: "Clean Working Directory",
|
|
29693
|
+
command: "git status --porcelain",
|
|
29694
|
+
successCondition: "output is empty",
|
|
29695
|
+
failureMessage: "Uncommitted changes exist \u2014 cannot proceed"
|
|
29696
|
+
},
|
|
29697
|
+
branchExists: {
|
|
29698
|
+
name: "Branch Existence",
|
|
29699
|
+
command: "git rev-parse --verify ${BRANCH}",
|
|
29700
|
+
successCondition: "exit code === 0",
|
|
29701
|
+
failureMessage: "Branch does not exist"
|
|
29702
|
+
},
|
|
29703
|
+
commandExit: {
|
|
29704
|
+
name: "Command Exit Code",
|
|
29705
|
+
command: undefined,
|
|
29706
|
+
successCondition: "exit code === 0",
|
|
29707
|
+
failureMessage: "Command failed with non-zero exit code"
|
|
29708
|
+
},
|
|
29709
|
+
fileWrite: {
|
|
29710
|
+
name: "File Write Verification",
|
|
29711
|
+
command: undefined,
|
|
29712
|
+
successCondition: "write tool returned success",
|
|
29713
|
+
failureMessage: "File write failed \u2014 check tool output"
|
|
29714
|
+
}
|
|
29715
|
+
};
|
|
29716
|
+
|
|
29717
|
+
// src/runtime/tools/verify.ts
|
|
29718
|
+
function createVerifyTool() {
|
|
29719
|
+
return tool({
|
|
29720
|
+
description: "Centrally verifies system state changes. Required before claiming success.",
|
|
29721
|
+
args: {
|
|
29722
|
+
action: exports_external.enum(Object.keys(VERIFICATION_COMMANDS)).describe("The predefined verification action to perform."),
|
|
29723
|
+
context: exports_external.record(exports_external.string(), exports_external.any()).optional().describe("Context variables needed for the command (e.g., { BRANCH: 'feature-1' })")
|
|
29724
|
+
},
|
|
29725
|
+
execute: withToolContract("verify_action", async (args, toolContext) => {
|
|
29726
|
+
const actionKey = args.action;
|
|
29727
|
+
const config2 = VERIFICATION_COMMANDS[actionKey];
|
|
29728
|
+
if (!config2 || !config2.command) {
|
|
29729
|
+
const result2 = createFailureResult(`Unknown verification action or missing command: ${args.action}`);
|
|
29730
|
+
toolContext.metadata({ title: "Verify Error", ...result2 });
|
|
29731
|
+
return result2.message;
|
|
29732
|
+
}
|
|
29733
|
+
let cmdString = config2.command;
|
|
29734
|
+
if (args.context) {
|
|
29735
|
+
for (const [key, value] of Object.entries(args.context)) {
|
|
29736
|
+
cmdString = cmdString.replace(`\${${key}}`, String(value));
|
|
29737
|
+
}
|
|
29738
|
+
}
|
|
29739
|
+
const commandArgs = cmdString.split(" ");
|
|
29740
|
+
const proc = spawn3(commandArgs, {
|
|
29741
|
+
cwd: toolContext.directory || process.cwd(),
|
|
29742
|
+
stdout: "pipe",
|
|
29743
|
+
stderr: "pipe"
|
|
29744
|
+
});
|
|
29745
|
+
const stdoutText = await new Response(proc.stdout).text();
|
|
29746
|
+
const exitCode = await proc.exited;
|
|
29747
|
+
let isSuccess = false;
|
|
29748
|
+
if (config2.successCondition === 'output === "0"') {
|
|
29749
|
+
isSuccess = stdoutText.trim() === "0";
|
|
29750
|
+
} else if (config2.successCondition === "exit code === 0") {
|
|
29751
|
+
isSuccess = exitCode === 0;
|
|
29752
|
+
} else if (config2.successCondition === 'output starts with "https://"') {
|
|
29753
|
+
isSuccess = stdoutText.trim().startsWith("https://");
|
|
29754
|
+
} else if (config2.successCondition === "output is empty") {
|
|
29755
|
+
isSuccess = stdoutText.trim() === "";
|
|
29756
|
+
} else {
|
|
29757
|
+
isSuccess = exitCode === 0;
|
|
29758
|
+
}
|
|
29759
|
+
const result = createSuccessResult({
|
|
29760
|
+
verified: isSuccess,
|
|
29761
|
+
changedState: false,
|
|
29762
|
+
message: isSuccess ? undefined : config2.failureMessage
|
|
29763
|
+
});
|
|
29764
|
+
toolContext.metadata({
|
|
29765
|
+
title: `Verify: ${config2.name}`,
|
|
29766
|
+
...result
|
|
29767
|
+
});
|
|
29768
|
+
return isSuccess ? `Verification SUCCESS. (Output: ${stdoutText.trim()})` : `Verification FAILED. ${config2.failureMessage} (Output: ${stdoutText.trim()})`;
|
|
29769
|
+
})
|
|
29770
|
+
});
|
|
29771
|
+
}
|
|
29772
|
+
|
|
29773
|
+
// src/runtime/state-ledger.ts
|
|
29774
|
+
class StateLedger {
|
|
29775
|
+
static instance;
|
|
29776
|
+
entries = [];
|
|
29777
|
+
lastFlowStartTime = 0;
|
|
29778
|
+
constructor() {}
|
|
29779
|
+
static getInstance() {
|
|
29780
|
+
if (!StateLedger.instance) {
|
|
29781
|
+
StateLedger.instance = new StateLedger;
|
|
29782
|
+
}
|
|
29783
|
+
return StateLedger.instance;
|
|
29784
|
+
}
|
|
29785
|
+
startNewFlow() {
|
|
29786
|
+
this.lastFlowStartTime = Date.now();
|
|
29787
|
+
}
|
|
29788
|
+
record(type2, key, success2, verified, changedState, stdout, metadata, sessionID) {
|
|
29789
|
+
this.entries.push({
|
|
29790
|
+
type: type2,
|
|
29791
|
+
timestamp: Date.now(),
|
|
29792
|
+
key,
|
|
29793
|
+
success: success2,
|
|
29794
|
+
verified,
|
|
29795
|
+
changedState,
|
|
29796
|
+
stdout,
|
|
29797
|
+
sessionID,
|
|
29798
|
+
metadata
|
|
29799
|
+
});
|
|
29800
|
+
}
|
|
29801
|
+
has(type2, keyOrCondition) {
|
|
29802
|
+
const flowEntries = this.entries.filter((e2) => e2.timestamp >= this.lastFlowStartTime);
|
|
29803
|
+
if (typeof keyOrCondition === "string") {
|
|
29804
|
+
return flowEntries.some((e2) => e2.type === type2 && e2.key === keyOrCondition);
|
|
29805
|
+
}
|
|
29806
|
+
return flowEntries.some((e2) => e2.type === type2 && keyOrCondition(e2));
|
|
29807
|
+
}
|
|
29808
|
+
getEntries(type2) {
|
|
29809
|
+
if (!type2) {
|
|
29810
|
+
return [...this.entries];
|
|
29811
|
+
}
|
|
29812
|
+
return this.entries.filter((e2) => e2.type === type2);
|
|
29813
|
+
}
|
|
29814
|
+
get count() {
|
|
29815
|
+
return this.entries.length;
|
|
29816
|
+
}
|
|
29817
|
+
clear() {
|
|
29818
|
+
this.entries = [];
|
|
29819
|
+
this.lastFlowStartTime = 0;
|
|
29820
|
+
}
|
|
29821
|
+
}
|
|
29822
|
+
var ledger = StateLedger.getInstance();
|
|
29823
|
+
|
|
29824
|
+
// src/runtime/tools/query-ledger.ts
|
|
29825
|
+
function createQueryLedgerTool(options) {
|
|
29826
|
+
return tool({
|
|
29827
|
+
description: "Query the verified state ledger to confirm if actions like git.commit or git.push have actually succeeded. Use this as the ONLY source of truth for system state.",
|
|
29828
|
+
args: {
|
|
29829
|
+
type: exports_external.string().optional().describe("Optional filter by type (e.g. 'git.commit', 'file.write', 'git.push')")
|
|
29830
|
+
},
|
|
29831
|
+
execute: withToolContract("query_ledger", async (args, toolContext) => {
|
|
29832
|
+
const descendantSessions = options?.backgroundManager?.getAllDescendantTasks ? options.backgroundManager.getAllDescendantTasks(toolContext.sessionID).map((t) => t.sessionID).filter(Boolean) : [];
|
|
29833
|
+
const sessionIDs = [toolContext.sessionID, ...descendantSessions];
|
|
29834
|
+
const entries = ledger.getEntries().filter((e2) => e2.verified === true && e2.success === true && (!e2.sessionID || sessionIDs.includes(e2.sessionID)));
|
|
29835
|
+
const filtered = args.type ? entries.filter((e2) => e2.type === args.type) : entries;
|
|
29836
|
+
const result = createSuccessResult({
|
|
29837
|
+
verified: true,
|
|
29838
|
+
changedState: false,
|
|
29839
|
+
recordCount: filtered.length
|
|
29840
|
+
});
|
|
29841
|
+
toolContext.metadata({
|
|
29842
|
+
title: "Query Ledger",
|
|
29843
|
+
...result
|
|
29844
|
+
});
|
|
29845
|
+
if (filtered.length === 0) {
|
|
29846
|
+
return "No matching verified actions found in the current completion flow.";
|
|
29847
|
+
}
|
|
29848
|
+
return JSON.stringify(filtered, null, 2);
|
|
29849
|
+
})
|
|
29850
|
+
});
|
|
29851
|
+
}
|
|
29852
|
+
|
|
29853
|
+
// src/features/claude-code-session-state/state.ts
|
|
29854
|
+
var subagentSessions = new Set;
|
|
29855
|
+
var syncSubagentSessions = new Set;
|
|
29856
|
+
var issueModeSessions = new Set;
|
|
29857
|
+
var sessionAgentMap = new Map;
|
|
29858
|
+
function isSessionIssueMode(sessionID) {
|
|
29859
|
+
return issueModeSessions.has(sessionID);
|
|
29860
|
+
}
|
|
29861
|
+
// src/features/issue-resolution/state.ts
|
|
29862
|
+
init_data_path();
|
|
29863
|
+
import { join as join29 } from "path";
|
|
29864
|
+
import { existsSync as existsSync31, readFileSync as readFileSync30, writeFileSync as writeFileSync13, mkdirSync as mkdirSync9 } from "fs";
|
|
29865
|
+
var DATA_DIR = join29(getDataDir(), "oh-my-opencode");
|
|
29866
|
+
var ISSUE_STATE_FILE = join29(DATA_DIR, "issue-verification-state.json");
|
|
29867
|
+
function ensureDataDir() {
|
|
29868
|
+
if (!existsSync31(DATA_DIR)) {
|
|
29869
|
+
mkdirSync9(DATA_DIR, { recursive: true });
|
|
29870
|
+
}
|
|
29871
|
+
}
|
|
29872
|
+
function loadFromDisk() {
|
|
29873
|
+
try {
|
|
29874
|
+
if (existsSync31(ISSUE_STATE_FILE)) {
|
|
29875
|
+
const data = JSON.parse(readFileSync30(ISSUE_STATE_FILE, "utf8"));
|
|
29876
|
+
return new Map(Object.entries(data));
|
|
29877
|
+
}
|
|
29878
|
+
} catch (e2) {}
|
|
29879
|
+
return new Map;
|
|
29880
|
+
}
|
|
29881
|
+
function saveToDisk(states) {
|
|
29882
|
+
ensureDataDir();
|
|
29883
|
+
const data = Object.fromEntries(states);
|
|
29884
|
+
writeFileSync13(ISSUE_STATE_FILE, JSON.stringify(data, null, 2));
|
|
29885
|
+
}
|
|
29886
|
+
var issueStates = loadFromDisk();
|
|
29887
|
+
function getIssueState(sessionID) {
|
|
29888
|
+
if (!issueStates.has(sessionID)) {
|
|
29889
|
+
issueStates.set(sessionID, {
|
|
29890
|
+
reproduced: false,
|
|
29891
|
+
fixApplied: false,
|
|
29892
|
+
reproAfterPassed: false,
|
|
29893
|
+
failureModeChecksPassed: false
|
|
29894
|
+
});
|
|
29895
|
+
saveToDisk(issueStates);
|
|
29896
|
+
}
|
|
29897
|
+
return issueStates.get(sessionID);
|
|
29898
|
+
}
|
|
29899
|
+
function updateIssueState(sessionID, partialState) {
|
|
29900
|
+
const currentState = getIssueState(sessionID);
|
|
29901
|
+
const newState = { ...currentState, ...partialState };
|
|
29902
|
+
issueStates.set(sessionID, newState);
|
|
29903
|
+
saveToDisk(issueStates);
|
|
29904
|
+
return newState;
|
|
29905
|
+
}
|
|
29906
|
+
|
|
29907
|
+
// src/runtime/tools/complete-task.ts
|
|
29908
|
+
function createCompleteTaskTool(options) {
|
|
29909
|
+
return tool({
|
|
29910
|
+
description: "Signal that the task is complete. The runtime will compose the final verified state report. DO NOT output your own summary, just call this tool.",
|
|
29911
|
+
args: {
|
|
29912
|
+
message: exports_external.string().describe("Optional short note about what was done. Do not include PR URLs or commit hashes here.")
|
|
29913
|
+
},
|
|
29914
|
+
execute: withToolContract("complete_task", async (args, toolContext) => {
|
|
29915
|
+
const client3 = options?.client;
|
|
29916
|
+
const sessionID = toolContext.sessionID;
|
|
29917
|
+
if (client3) {
|
|
29918
|
+
try {
|
|
29919
|
+
const todosRes = await client3.session.todo({
|
|
29920
|
+
path: { id: sessionID }
|
|
29921
|
+
});
|
|
29922
|
+
const todos = normalizeSDKResponse(todosRes, []);
|
|
29923
|
+
const incompleteTodos = todos.filter((t) => t.status !== "completed" && t.status !== "cancelled" && t.status !== "blocked" && t.status !== "deleted");
|
|
29924
|
+
if (incompleteTodos.length > 0) {
|
|
29925
|
+
const failMsg = `[ERROR] TASK COMPLETION REJECTED.
|
|
29926
|
+
|
|
29927
|
+
You have ${incompleteTodos.length} incomplete TODOs remaining in the UI tracker. You CANNOT mark the task as complete until all TODOs are explicitly marked as completed or cancelled using the TodoWrite tool (or TaskUpdate if experimental tasks are enabled).
|
|
29928
|
+
|
|
29929
|
+
Please review your TODOs, complete them, and then retry calling complete_task.`;
|
|
29930
|
+
const result2 = createFailureResult(failMsg);
|
|
29931
|
+
const meta3 = { title: "Task Completion Rejected", ...result2 };
|
|
29932
|
+
toolContext.metadata(meta3);
|
|
29933
|
+
return failMsg;
|
|
29934
|
+
}
|
|
29935
|
+
} catch (e2) {}
|
|
29936
|
+
}
|
|
29937
|
+
if (isSessionIssueMode(sessionID)) {
|
|
29938
|
+
const issueState = getIssueState(sessionID);
|
|
29939
|
+
if (!issueState.reproduced || !issueState.fixApplied || !issueState.reproAfterPassed) {
|
|
29940
|
+
const failMsg = `[ERROR] STRICT ISSUE RESOLUTION MODE ACTIVE.
|
|
29941
|
+
|
|
29942
|
+
You cannot mark this task as complete until you have explicitly verified the fix.
|
|
29943
|
+
|
|
29944
|
+
Current Verification State:
|
|
29945
|
+
- Reproduced: ${issueState.reproduced}
|
|
29946
|
+
- Fix Applied: ${issueState.fixApplied}
|
|
29947
|
+
- Repro After Fix Passed: ${issueState.reproAfterPassed}
|
|
29948
|
+
|
|
29949
|
+
You MUST use the 'report_issue_verification' tool to truthfully log your progress as you perform each step. If you only performed static reasoning without live verification, your state is incomplete.`;
|
|
29950
|
+
const result2 = createFailureResult(failMsg);
|
|
29951
|
+
toolContext.metadata({ title: "Task Completion Rejected", ...result2 });
|
|
29952
|
+
return failMsg;
|
|
29953
|
+
}
|
|
29954
|
+
}
|
|
29955
|
+
const descendantSessions = options?.backgroundManager?.getAllDescendantTasks ? options.backgroundManager.getAllDescendantTasks(toolContext.sessionID).map((t) => t.sessionID).filter(Boolean) : [];
|
|
29956
|
+
const sessionIDs = [toolContext.sessionID, ...descendantSessions];
|
|
29957
|
+
const entries = ledger.getEntries().filter((e2) => e2.verified === true && e2.success === true && (!e2.sessionID || sessionIDs.includes(e2.sessionID)));
|
|
29958
|
+
let report = `TASK COMPLETE.
|
|
29959
|
+
|
|
29960
|
+
Runtime Verified Actions (Current Flow):
|
|
29961
|
+
`;
|
|
29962
|
+
if (entries.length === 0) {
|
|
29963
|
+
report += `- No state changes recorded in this session.
|
|
29964
|
+
`;
|
|
29965
|
+
} else {
|
|
29966
|
+
for (const entry of entries) {
|
|
29967
|
+
report += `- [${entry.type}] ${entry.key}
|
|
29968
|
+
`;
|
|
29969
|
+
}
|
|
29970
|
+
}
|
|
29971
|
+
if (args.message) {
|
|
29972
|
+
report += `
|
|
29973
|
+
Agent Note: ${args.message}
|
|
29974
|
+
`;
|
|
29975
|
+
}
|
|
29976
|
+
const result = createSuccessResult({
|
|
29977
|
+
verified: true,
|
|
29978
|
+
changedState: false,
|
|
29979
|
+
message: args.message
|
|
29980
|
+
});
|
|
29981
|
+
toolContext.metadata({
|
|
29982
|
+
title: "Task Completed",
|
|
29983
|
+
...result,
|
|
29984
|
+
sessionID: toolContext.sessionID,
|
|
29985
|
+
entries: entries.length
|
|
29986
|
+
});
|
|
29987
|
+
return `[RUNTIME AUTHORIZATION]
|
|
29988
|
+
|
|
29989
|
+
${report}
|
|
29990
|
+
|
|
29991
|
+
You may now conclude your response using EXACTLY this report as your final output.`;
|
|
29992
|
+
})
|
|
29993
|
+
});
|
|
29994
|
+
}
|
|
29995
|
+
|
|
29996
|
+
// src/cli/doctor/checks/tool-contract.ts
|
|
29997
|
+
async function checkToolContract() {
|
|
29998
|
+
const issues = [];
|
|
29999
|
+
const toolsToTest = [
|
|
30000
|
+
{ name: "fs_safe", factory: createFsSafeTool, args: { operation: "read", filePath: "non-existent-test-file" } },
|
|
30001
|
+
{ name: "git_safe", factory: createGitSafeTool, args: { command: "status" } },
|
|
30002
|
+
{ name: "submit_plan", factory: createSubmitPlanTool, args: { steps: [] } },
|
|
30003
|
+
{ name: "mark_step_complete", factory: createMarkStepCompleteTool, args: { id: "step1" } },
|
|
30004
|
+
{ name: "unlock_plan", factory: createUnlockPlanTool, args: {} },
|
|
30005
|
+
{ name: "verify_action", factory: createVerifyTool, args: { action: "LS_FILES" } },
|
|
30006
|
+
{ name: "query_ledger", factory: createQueryLedgerTool, args: {} },
|
|
30007
|
+
{ name: "complete_task", factory: createCompleteTaskTool, args: { message: "test completion" } }
|
|
30008
|
+
];
|
|
30009
|
+
for (const toolSpec of toolsToTest) {
|
|
30010
|
+
try {
|
|
30011
|
+
const tool3 = toolSpec.factory();
|
|
30012
|
+
const sessionID = `doctor-test-${Date.now()}`;
|
|
30013
|
+
const callID = `call-${toolSpec.name}`;
|
|
30014
|
+
clearPendingStore();
|
|
30015
|
+
const mockContext = {
|
|
30016
|
+
sessionID,
|
|
30017
|
+
callID,
|
|
30018
|
+
directory: process.cwd(),
|
|
30019
|
+
metadata: () => {},
|
|
30020
|
+
client: {}
|
|
30021
|
+
};
|
|
30022
|
+
await tool3.execute(toolSpec.args, mockContext).catch(() => {});
|
|
30023
|
+
const stored = consumeToolMetadata(sessionID, callID);
|
|
30024
|
+
if (!stored || !stored.metadata) {
|
|
30025
|
+
issues.push({
|
|
30026
|
+
title: `Tool Contract Violation: ${toolSpec.name}`,
|
|
30027
|
+
description: `Tool did not call storeToolMetadata or returned no metadata.`,
|
|
30028
|
+
severity: "error",
|
|
30029
|
+
affects: [toolSpec.name]
|
|
30030
|
+
});
|
|
30031
|
+
continue;
|
|
30032
|
+
}
|
|
30033
|
+
const meta3 = stored.metadata;
|
|
30034
|
+
if (typeof meta3.success !== "boolean" || typeof meta3.verified !== "boolean") {
|
|
30035
|
+
issues.push({
|
|
30036
|
+
title: `Tool Contract Violation: ${toolSpec.name}`,
|
|
30037
|
+
description: `Tool metadata missing 'success' or 'verified' booleans. Found: ${JSON.stringify(meta3)}`,
|
|
30038
|
+
severity: "error",
|
|
30039
|
+
affects: [toolSpec.name]
|
|
30040
|
+
});
|
|
30041
|
+
}
|
|
30042
|
+
} catch (e2) {
|
|
30043
|
+
issues.push({
|
|
30044
|
+
title: `Doctor internal error testing ${toolSpec.name}`,
|
|
30045
|
+
description: e2.message,
|
|
30046
|
+
severity: "warning"
|
|
30047
|
+
});
|
|
30048
|
+
}
|
|
30049
|
+
}
|
|
30050
|
+
return {
|
|
30051
|
+
name: "Tool Contract Compliance",
|
|
30052
|
+
status: issues.length === 0 ? "pass" : "fail",
|
|
30053
|
+
message: issues.length === 0 ? "All safety tools comply with the result contract." : `${issues.length} tool contract violation(s) detected.`,
|
|
30054
|
+
details: toolsToTest.map((t) => `${t.name}: tested`),
|
|
30055
|
+
issues
|
|
30056
|
+
};
|
|
30057
|
+
}
|
|
30058
|
+
|
|
30059
|
+
// src/cli/doctor/checks/edit-atomicity.ts
|
|
30060
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync15, unlinkSync as unlinkSync3, existsSync as existsSync33 } from "fs";
|
|
30061
|
+
import { join as join30 } from "path";
|
|
30062
|
+
|
|
30063
|
+
// src/hooks/edit-safeguard/hook.ts
|
|
30064
|
+
init_shared();
|
|
30065
|
+
import { existsSync as existsSync32, readFileSync as readFileSync31, writeFileSync as writeFileSync14 } from "fs";
|
|
30066
|
+
import { spawnSync } from "child_process";
|
|
30067
|
+
|
|
30068
|
+
// src/shared/read-permission-tracker.ts
|
|
30069
|
+
init_logger();
|
|
30070
|
+
|
|
30071
|
+
class ReadPermissionTracker {
|
|
30072
|
+
readFiles = new Map;
|
|
30073
|
+
recordRead(sessionID, filePath) {
|
|
30074
|
+
if (!this.readFiles.has(sessionID)) {
|
|
30075
|
+
this.readFiles.set(sessionID, new Set);
|
|
30076
|
+
}
|
|
30077
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
30078
|
+
this.readFiles.get(sessionID).add(normalizedPath);
|
|
30079
|
+
log(`[ReadTracker] Recorded read for ${normalizedPath} in session ${sessionID}`);
|
|
30080
|
+
}
|
|
30081
|
+
hasRead(sessionID, filePath) {
|
|
30082
|
+
const sessionReads = this.readFiles.get(sessionID);
|
|
30083
|
+
if (!sessionReads)
|
|
30084
|
+
return false;
|
|
30085
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
30086
|
+
return sessionReads.has(normalizedPath);
|
|
30087
|
+
}
|
|
30088
|
+
clearSession(sessionID) {
|
|
30089
|
+
this.readFiles.delete(sessionID);
|
|
30090
|
+
}
|
|
30091
|
+
normalizePath(filePath) {
|
|
30092
|
+
return filePath.trim().replace(/\/+$/, "");
|
|
30093
|
+
}
|
|
30094
|
+
}
|
|
30095
|
+
var readTracker = new ReadPermissionTracker;
|
|
30096
|
+
|
|
30097
|
+
// src/hooks/edit-safeguard/hook.ts
|
|
30098
|
+
var FAILURE_PATTERNS = [
|
|
30099
|
+
"oldstring not found",
|
|
30100
|
+
"oldstring found multiple times",
|
|
30101
|
+
"oldstring and newstring must be different",
|
|
30102
|
+
"verification failed",
|
|
30103
|
+
"failed to find expected lines"
|
|
30104
|
+
];
|
|
30105
|
+
function createEditSafeguardHook(ctx) {
|
|
30106
|
+
const backups = new Map;
|
|
30107
|
+
function getFilePath(toolName, args) {
|
|
30108
|
+
if (toolName === "edit")
|
|
30109
|
+
return args.filePath;
|
|
30110
|
+
if (toolName === "apply_patch")
|
|
30111
|
+
return args.path || args.filePath;
|
|
30112
|
+
return;
|
|
30113
|
+
}
|
|
30114
|
+
return {
|
|
30115
|
+
"tool.execute.before": async (input, output) => {
|
|
30116
|
+
const toolName = input.tool?.toLowerCase();
|
|
30117
|
+
const isEditTool = toolName === "edit" || toolName === "apply_patch";
|
|
30118
|
+
if (!isEditTool)
|
|
30119
|
+
return;
|
|
30120
|
+
const filePath = getFilePath(toolName, output.args);
|
|
30121
|
+
if (!filePath)
|
|
30122
|
+
return;
|
|
30123
|
+
if (!readTracker.hasRead(input.sessionID, filePath) && existsSync32(filePath)) {
|
|
30124
|
+
log("[edit-safeguard] REJECTED: Read-before-write violation", { filePath, sessionID: input.sessionID });
|
|
30125
|
+
throw new Error(`[Edit Discipline Violation] Path REJECTED: ${filePath}
|
|
30126
|
+
` + `You attempted to edit or patch a file that you have not read in the current session.
|
|
30127
|
+
` + `The rule is path-specific: you MUST read the exact target file path before attempting to edit or overwrite it.
|
|
30128
|
+
` + `Reading a different file or a grep snippet does not count.`);
|
|
30129
|
+
}
|
|
30130
|
+
try {
|
|
30131
|
+
if (existsSync32(filePath)) {
|
|
30132
|
+
const content = readFileSync31(filePath, "utf-8");
|
|
30133
|
+
backups.set(`${input.sessionID}:${input.callID}:${filePath}`, content);
|
|
30134
|
+
}
|
|
30135
|
+
} catch (err) {
|
|
30136
|
+
log("[edit-safeguard] Failed to create backup", { filePath, error: String(err) });
|
|
30137
|
+
}
|
|
30138
|
+
},
|
|
30139
|
+
"tool.execute.after": async (input, output) => {
|
|
30140
|
+
const toolName = input.tool?.toLowerCase();
|
|
30141
|
+
const isEditTool = toolName === "edit" || toolName === "apply_patch";
|
|
30142
|
+
if (!isEditTool)
|
|
30143
|
+
return;
|
|
30144
|
+
const filePath = getFilePath(toolName, output.metadata?.args || {});
|
|
30145
|
+
if (!filePath)
|
|
30146
|
+
return;
|
|
30147
|
+
const backupKey = `${input.sessionID}:${input.callID}:${filePath}`;
|
|
30148
|
+
const originalContent = backups.get(backupKey);
|
|
30149
|
+
if (originalContent === undefined)
|
|
30150
|
+
return;
|
|
30151
|
+
const resultOutput = (output.output ?? "").toLowerCase();
|
|
30152
|
+
const hasFailurePattern = FAILURE_PATTERNS.some((p2) => resultOutput.includes(p2));
|
|
30153
|
+
let currentContent = "";
|
|
30154
|
+
try {
|
|
30155
|
+
currentContent = readFileSync31(filePath, "utf-8");
|
|
30156
|
+
} catch (err) {
|
|
30157
|
+
if (hasFailurePattern) {
|
|
30158
|
+
writeFileSync14(filePath, originalContent);
|
|
30159
|
+
log("[edit-safeguard] Restored deleted file after failed edit", { filePath });
|
|
30160
|
+
}
|
|
30161
|
+
return;
|
|
30162
|
+
}
|
|
30163
|
+
const contentChanged = currentContent !== originalContent;
|
|
30164
|
+
if (hasFailurePattern && contentChanged) {
|
|
30165
|
+
writeFileSync14(filePath, originalContent);
|
|
30166
|
+
log("[edit-safeguard] Reverted partial mutation after " + resultOutput, { filePath });
|
|
30167
|
+
output.output += `
|
|
30168
|
+
|
|
30169
|
+
[SAFEGUARD] Reverted partial mutation to prevent corruption.`;
|
|
30170
|
+
}
|
|
30171
|
+
if (hasFailurePattern) {
|
|
30172
|
+
output.output += `
|
|
30173
|
+
|
|
30174
|
+
**ACTION REQUIRED**: The edit or patch verification failed. This usually means the file content has drifted or the match block is incorrect.
|
|
30175
|
+
` + `You MUST run 'read_file' (or equivalent) on '${filePath}' to synchronize your context with the real file contents, and then regenerate your edit/patch from the exact lines found in the file.`;
|
|
30176
|
+
return;
|
|
30177
|
+
}
|
|
30178
|
+
if (!hasFailurePattern && contentChanged) {
|
|
30179
|
+
if (filePath.endsWith(".py")) {
|
|
30180
|
+
const isValid = validatePythonSyntax(filePath);
|
|
30181
|
+
if (!isValid) {
|
|
30182
|
+
writeFileSync14(filePath, originalContent);
|
|
30183
|
+
log("[edit-safeguard] Reverted edit due to Python syntax error", { filePath });
|
|
30184
|
+
throw new Error(`[edit-safeguard] Syntax validation failed for ${filePath}. Edit reverted to prevent corruption.`);
|
|
30185
|
+
}
|
|
30186
|
+
}
|
|
30187
|
+
}
|
|
30188
|
+
backups.delete(backupKey);
|
|
30189
|
+
}
|
|
30190
|
+
};
|
|
30191
|
+
}
|
|
30192
|
+
function validatePythonSyntax(filePath) {
|
|
30193
|
+
try {
|
|
30194
|
+
const result = spawnSync("python3", ["-m", "py_compile", filePath]);
|
|
30195
|
+
return result.status === 0;
|
|
30196
|
+
} catch (err) {
|
|
30197
|
+
log("[edit-safeguard] Syntax validation execution failed", { error: String(err) });
|
|
30198
|
+
return true;
|
|
30199
|
+
}
|
|
30200
|
+
}
|
|
30201
|
+
|
|
30202
|
+
// src/cli/doctor/checks/edit-atomicity.ts
|
|
30203
|
+
var checkEditAtomicity = {
|
|
30204
|
+
id: "EDIT_ATOMICITY",
|
|
30205
|
+
name: "Edit Atomicity & Validation",
|
|
30206
|
+
async check() {
|
|
30207
|
+
const testFile = join30(process.cwd(), "test_edit_atomicity.py");
|
|
30208
|
+
const originalContent = `def main():
|
|
30209
|
+
print("hello")
|
|
30210
|
+
`;
|
|
30211
|
+
try {
|
|
30212
|
+
writeFileSync15(testFile, originalContent);
|
|
30213
|
+
const hook = createEditSafeguardHook({ directory: process.cwd() });
|
|
30214
|
+
const sessionID = "test-session";
|
|
30215
|
+
const callID = "test-call";
|
|
30216
|
+
await hook["tool.execute.before"]?.({ tool: "edit", sessionID, callID }, { args: { filePath: testFile } });
|
|
30217
|
+
writeFileSync15(testFile, `def main():
|
|
30218
|
+
print("corrupted"
|
|
30219
|
+
`);
|
|
30220
|
+
const output = {
|
|
30221
|
+
title: "Edit",
|
|
30222
|
+
output: "Error: oldString not found",
|
|
30223
|
+
metadata: { args: { filePath: testFile } }
|
|
30224
|
+
};
|
|
30225
|
+
await hook["tool.execute.after"]?.({ tool: "edit", sessionID, callID, args: { filePath: testFile } }, output);
|
|
30226
|
+
const contentAfterFail = readFileSync32(testFile, "utf-8");
|
|
30227
|
+
if (contentAfterFail !== originalContent) {
|
|
30228
|
+
return {
|
|
30229
|
+
status: "fail",
|
|
30230
|
+
name: this.name,
|
|
30231
|
+
message: "Edit is NOT atomic. Partial mutation was not reverted after error.",
|
|
30232
|
+
issues: [{
|
|
30233
|
+
title: "Partial Mutation",
|
|
30234
|
+
description: "File content changed despite tool reporting failure.",
|
|
30235
|
+
severity: "error"
|
|
30236
|
+
}]
|
|
30237
|
+
};
|
|
30238
|
+
}
|
|
30239
|
+
await hook["tool.execute.before"]?.({ tool: "edit", sessionID, callID: "call2" }, { args: { filePath: testFile } });
|
|
30240
|
+
writeFileSync15(testFile, `def main():
|
|
30241
|
+
print("invalid")
|
|
30242
|
+
x = [`);
|
|
30243
|
+
const outputSuccess = {
|
|
30244
|
+
title: "Edit",
|
|
30245
|
+
output: "Successfully replaced string",
|
|
30246
|
+
metadata: { args: { filePath: testFile } }
|
|
30247
|
+
};
|
|
30248
|
+
try {
|
|
30249
|
+
await hook["tool.execute.after"]?.({ tool: "edit", sessionID, callID: "call2", args: { filePath: testFile } }, outputSuccess);
|
|
30250
|
+
return {
|
|
30251
|
+
status: "fail",
|
|
30252
|
+
name: this.name,
|
|
30253
|
+
message: "Syntax validation failed to block invalid Python code.",
|
|
30254
|
+
issues: [{
|
|
30255
|
+
title: "Validation Bypass",
|
|
30256
|
+
description: "Edit tool allowed syntactically invalid Python code.",
|
|
30257
|
+
severity: "error"
|
|
30258
|
+
}]
|
|
30259
|
+
};
|
|
30260
|
+
} catch (err) {
|
|
30261
|
+
if (!String(err).includes("Syntax validation failed")) {
|
|
30262
|
+
return {
|
|
30263
|
+
status: "fail",
|
|
30264
|
+
name: this.name,
|
|
30265
|
+
message: `Unexpected error during syntax validation: ${err}`,
|
|
30266
|
+
issues: [{
|
|
30267
|
+
title: "Unexpected Error",
|
|
30268
|
+
description: String(err),
|
|
30269
|
+
severity: "error"
|
|
30270
|
+
}]
|
|
30271
|
+
};
|
|
30272
|
+
}
|
|
30273
|
+
}
|
|
30274
|
+
const contentAfterSyntaxFail = readFileSync32(testFile, "utf-8");
|
|
30275
|
+
if (contentAfterSyntaxFail !== originalContent) {
|
|
30276
|
+
return {
|
|
30277
|
+
status: "fail",
|
|
30278
|
+
name: this.name,
|
|
30279
|
+
message: "File was not reverted after syntax validation failure.",
|
|
30280
|
+
issues: [{
|
|
30281
|
+
title: "Revert Failed",
|
|
30282
|
+
description: "File was not restored to its original state after syntax error.",
|
|
30283
|
+
severity: "error"
|
|
30284
|
+
}]
|
|
30285
|
+
};
|
|
30286
|
+
}
|
|
30287
|
+
return {
|
|
30288
|
+
status: "pass",
|
|
30289
|
+
name: this.name,
|
|
30290
|
+
message: "Edit atomicity and syntax validation are working correctly.",
|
|
30291
|
+
issues: []
|
|
30292
|
+
};
|
|
30293
|
+
} catch (err) {
|
|
30294
|
+
return {
|
|
30295
|
+
status: "fail",
|
|
30296
|
+
name: this.name,
|
|
30297
|
+
message: `Check failed with error: ${err}`,
|
|
30298
|
+
issues: [{
|
|
30299
|
+
title: "Check Error",
|
|
30300
|
+
description: String(err),
|
|
30301
|
+
severity: "error"
|
|
30302
|
+
}]
|
|
30303
|
+
};
|
|
30304
|
+
} finally {
|
|
30305
|
+
if (existsSync33(testFile))
|
|
30306
|
+
unlinkSync3(testFile);
|
|
30307
|
+
}
|
|
30308
|
+
}
|
|
30309
|
+
};
|
|
30310
|
+
|
|
30311
|
+
// src/cli/doctor/checks/issue-resolution.ts
|
|
30312
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync11, writeFileSync as writeFileSync17, unlinkSync as unlinkSync4 } from "fs";
|
|
30313
|
+
import { join as join32 } from "path";
|
|
30314
|
+
|
|
30315
|
+
// src/runtime/tools/report-issue-verification.ts
|
|
30316
|
+
function createReportIssueVerificationTool() {
|
|
30317
|
+
return tool({
|
|
30318
|
+
name: "report_issue_verification",
|
|
30319
|
+
description: "Report progress on issue verification. Use this tool to permanently record that you have reproduced the issue, applied a fix, and verified the fix. Required before completing any issue-resolution task. Fields are additive, so you can report them one at a time as you proceed.",
|
|
30320
|
+
args: {
|
|
30321
|
+
reproduced: exports_external.boolean().optional().describe("Set to true once you have successfully reproduced the issue live."),
|
|
30322
|
+
errorSignatureBefore: exports_external.string().optional().describe("The exact error message, traceback, or symptom before the fix."),
|
|
30323
|
+
fixApplied: exports_external.boolean().optional().describe("Set to true once you have applied a fix to the codebase."),
|
|
30324
|
+
reproAfterPassed: exports_external.boolean().optional().describe("True if you re-ran the reproduction steps after the fix, and the issue is gone."),
|
|
30325
|
+
failureModeChecksPassed: exports_external.boolean().optional().describe("Set to true if you checked nearby/related failure modes and they pass.")
|
|
30326
|
+
},
|
|
30327
|
+
execute: withToolContract("report_issue_verification", async (args, toolContext) => {
|
|
30328
|
+
const sessionID = toolContext.sessionID;
|
|
30329
|
+
const newState = updateIssueState(sessionID, args);
|
|
30330
|
+
const result = createSuccessResult({
|
|
30331
|
+
verified: true,
|
|
30332
|
+
changedState: true,
|
|
30333
|
+
stateChange: newState,
|
|
30334
|
+
message: `Verification state updated.`
|
|
30335
|
+
});
|
|
30336
|
+
toolContext.metadata({
|
|
30337
|
+
title: "Issue Verification Update",
|
|
30338
|
+
...result,
|
|
30339
|
+
sessionID,
|
|
30340
|
+
state: newState
|
|
30341
|
+
});
|
|
30342
|
+
let response = `[VERIFICATION LOGGED]
|
|
30343
|
+
|
|
30344
|
+
Current Verification State:
|
|
30345
|
+
- Reproduced: ${newState.reproduced}
|
|
30346
|
+
- Error Signature: ${newState.errorSignatureBefore ?? "None"}
|
|
30347
|
+
- Fix Applied: ${newState.fixApplied}
|
|
30348
|
+
- Repro After Fix Passed: ${newState.reproAfterPassed}
|
|
30349
|
+
- Nearby Checks Passed: ${newState.failureModeChecksPassed}
|
|
30350
|
+
|
|
30351
|
+
`;
|
|
30352
|
+
if (isSessionIssueMode(sessionID)) {
|
|
30353
|
+
response += `Note: You are in Strict Issue Resolution Mode. 'reproduced', 'fixApplied', and 'reproAfterPassed' MUST be true to complete this task.`;
|
|
30354
|
+
}
|
|
30355
|
+
return response;
|
|
30356
|
+
})
|
|
30357
|
+
});
|
|
30358
|
+
}
|
|
30359
|
+
|
|
30360
|
+
// src/runtime/tools/gh-safe.ts
|
|
30361
|
+
var {spawn: spawn4 } = globalThis.Bun;
|
|
30362
|
+
|
|
30363
|
+
// src/features/pr-state/storage.ts
|
|
30364
|
+
import { existsSync as existsSync34, readFileSync as readFileSync33, writeFileSync as writeFileSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
30365
|
+
import { join as join31 } from "path";
|
|
30366
|
+
import { homedir as homedir9 } from "os";
|
|
30367
|
+
var STORAGE_DIR = join31(homedir9(), ".local", "share", "oh-my-opencode");
|
|
30368
|
+
var STORAGE_FILE = join31(STORAGE_DIR, "pr-state.json");
|
|
30369
|
+
var state2 = {};
|
|
30370
|
+
function loadPRState() {
|
|
30371
|
+
try {
|
|
30372
|
+
if (existsSync34(STORAGE_FILE)) {
|
|
30373
|
+
state2 = JSON.parse(readFileSync33(STORAGE_FILE, "utf-8"));
|
|
30374
|
+
}
|
|
30375
|
+
} catch (e2) {
|
|
30376
|
+
state2 = {};
|
|
30377
|
+
}
|
|
30378
|
+
}
|
|
30379
|
+
function savePRState() {
|
|
30380
|
+
try {
|
|
30381
|
+
if (!existsSync34(STORAGE_DIR)) {
|
|
30382
|
+
mkdirSync10(STORAGE_DIR, { recursive: true });
|
|
30383
|
+
}
|
|
30384
|
+
writeFileSync16(STORAGE_FILE, JSON.stringify(state2, null, 2));
|
|
30385
|
+
} catch (e2) {}
|
|
30386
|
+
}
|
|
30387
|
+
function updatePRState(sessionID, pr) {
|
|
30388
|
+
const current = state2[sessionID] || { updatedAt: new Date().toISOString() };
|
|
30389
|
+
state2[sessionID] = {
|
|
30390
|
+
...current,
|
|
30391
|
+
...pr,
|
|
30392
|
+
updatedAt: new Date().toISOString()
|
|
30393
|
+
};
|
|
30394
|
+
savePRState();
|
|
30395
|
+
}
|
|
30396
|
+
loadPRState();
|
|
30397
|
+
|
|
30398
|
+
// src/runtime/tools/gh-safe.ts
|
|
30399
|
+
function createGhSafeTool() {
|
|
30400
|
+
return tool({
|
|
30401
|
+
description: "Safe, structured execution of GitHub CLI (gh) commands. Returns verifiable status.",
|
|
30402
|
+
args: {
|
|
30403
|
+
command: exports_external.string().describe(`The gh command without the 'gh ' prefix (e.g. 'pr create --title "..." --body "..."')`)
|
|
30404
|
+
},
|
|
30405
|
+
execute: withToolContract("gh_safe", async (args, context) => {
|
|
30406
|
+
let commandArgs = [];
|
|
30407
|
+
try {
|
|
30408
|
+
commandArgs = args.command.match(/([^\\"]\S*|".+?")\s*/g)?.map((s) => s.trim().replace(/^"(.*)"$/, "$1")) || [];
|
|
30409
|
+
if (commandArgs.length === 0) {
|
|
30410
|
+
const result2 = createFailureResult("No command provided");
|
|
30411
|
+
context.metadata({ title: "GH Exec Error", ...result2 });
|
|
30412
|
+
return result2.message;
|
|
30413
|
+
}
|
|
30414
|
+
const proc = spawn4(["gh", ...commandArgs], {
|
|
30415
|
+
cwd: context.directory || process.cwd(),
|
|
30416
|
+
stdout: "pipe",
|
|
30417
|
+
stderr: "pipe"
|
|
30418
|
+
});
|
|
30419
|
+
const stdoutText = await new Response(proc.stdout).text();
|
|
30420
|
+
const stderrText = await new Response(proc.stderr).text();
|
|
30421
|
+
const exitCode = await proc.exited;
|
|
30422
|
+
const success2 = exitCode === 0;
|
|
30423
|
+
let prURL = "";
|
|
30424
|
+
if (success2 && commandArgs[0] === "pr" && commandArgs[1] === "create") {
|
|
30425
|
+
const match = stdoutText.match(/https:\/\/github\.com\/[^\/]+\/[^\/]+\/pull\/\d+/);
|
|
30426
|
+
if (match) {
|
|
30427
|
+
prURL = match[0];
|
|
30428
|
+
const prNumber = parseInt(prURL.split("/").pop() || "0");
|
|
30429
|
+
updatePRState(context.sessionID, {
|
|
30430
|
+
url: prURL,
|
|
30431
|
+
number: prNumber,
|
|
30432
|
+
status: "open"
|
|
30433
|
+
});
|
|
30434
|
+
}
|
|
30435
|
+
}
|
|
30436
|
+
if (success2 && commandArgs[0] === "pr" && commandArgs[1] === "merge") {
|
|
30437
|
+
updatePRState(context.sessionID, {
|
|
30438
|
+
status: "merged"
|
|
30439
|
+
});
|
|
30440
|
+
}
|
|
30441
|
+
const isStateChanging = ["pr", "issue", "branch", "repo"].includes(commandArgs[0]);
|
|
30442
|
+
const result = createSuccessResult({
|
|
30443
|
+
verified: !isStateChanging,
|
|
30444
|
+
changedState: success2 && isStateChanging,
|
|
30445
|
+
stateChange: prURL ? { type: "git.pr", key: prURL, details: { url: prURL } } : undefined
|
|
30446
|
+
});
|
|
30447
|
+
context.metadata({
|
|
30448
|
+
title: `gh ${commandArgs[0]} ${commandArgs[1] || ""}`,
|
|
30449
|
+
...result
|
|
30450
|
+
});
|
|
30451
|
+
return `Exit Code: ${exitCode}
|
|
30452
|
+
|
|
30453
|
+
STDOUT:
|
|
30454
|
+
${stdoutText}
|
|
30455
|
+
|
|
30456
|
+
STDERR:
|
|
30457
|
+
${stderrText}`;
|
|
30458
|
+
} catch (e2) {
|
|
30459
|
+
const result = createFailureResult(`Execution failed: ${e2.message}`);
|
|
30460
|
+
context.metadata({ title: "GH Exec Error", ...result });
|
|
30461
|
+
return result.message;
|
|
30462
|
+
}
|
|
30463
|
+
})
|
|
30464
|
+
});
|
|
30465
|
+
}
|
|
30466
|
+
|
|
30467
|
+
// src/runtime/tools/registry.ts
|
|
30468
|
+
var DETERMINISTIC_TOOLS = {
|
|
30469
|
+
git_safe: createGitSafeTool,
|
|
30470
|
+
fs_safe: createFsSafeTool,
|
|
30471
|
+
verify_action: createVerifyTool,
|
|
30472
|
+
submit_plan: createSubmitPlanTool,
|
|
30473
|
+
mark_step_complete: createMarkStepCompleteTool,
|
|
30474
|
+
unlock_plan: createUnlockPlanTool,
|
|
30475
|
+
query_ledger: createQueryLedgerTool,
|
|
30476
|
+
complete_task: createCompleteTaskTool,
|
|
30477
|
+
report_issue_verification: createReportIssueVerificationTool,
|
|
30478
|
+
gh_safe: createGhSafeTool
|
|
30479
|
+
};
|
|
30480
|
+
function getToolFromRegistry(name) {
|
|
30481
|
+
const toolFactory = DETERMINISTIC_TOOLS[name];
|
|
30482
|
+
if (!toolFactory) {
|
|
30483
|
+
throw new Error(`[Tool Registry] Unsupported or unauthorized tool requested: ${name}`);
|
|
30484
|
+
}
|
|
30485
|
+
return toolFactory();
|
|
30486
|
+
}
|
|
30487
|
+
|
|
30488
|
+
// src/cli/doctor/checks/issue-resolution.ts
|
|
30489
|
+
init_data_path();
|
|
30490
|
+
async function checkIssueResolutionWorkflow() {
|
|
30491
|
+
const issues = [];
|
|
30492
|
+
const requiredTools = ["report_issue_verification", "complete_task", "gh_safe", "query_ledger"];
|
|
30493
|
+
for (const toolName of requiredTools) {
|
|
30494
|
+
if (!DETERMINISTIC_TOOLS[toolName]) {
|
|
30495
|
+
issues.push({
|
|
30496
|
+
severity: "error",
|
|
30497
|
+
title: "Missing Tool",
|
|
30498
|
+
description: `${toolName} tool is missing from deterministic registry`,
|
|
30499
|
+
fix: `Ensure ${toolName} is registered in src/runtime/tools/registry.ts`
|
|
30500
|
+
});
|
|
30501
|
+
}
|
|
30502
|
+
}
|
|
30503
|
+
try {
|
|
30504
|
+
const dataDir = join32(getDataDir(), "oh-my-opencode");
|
|
30505
|
+
const testFile = join32(dataDir, ".doctor-persistence-test");
|
|
30506
|
+
if (!existsSync35(dataDir)) {
|
|
30507
|
+
mkdirSync11(dataDir, { recursive: true });
|
|
30508
|
+
}
|
|
30509
|
+
writeFileSync17(testFile, "test");
|
|
30510
|
+
unlinkSync4(testFile);
|
|
30511
|
+
} catch (e2) {
|
|
30512
|
+
issues.push({
|
|
30513
|
+
severity: "warning",
|
|
30514
|
+
title: "Persistence Issue",
|
|
30515
|
+
description: `Cannot write to OMO data directory: ${e2.message}`,
|
|
30516
|
+
fix: "Check permissions for your local share directory"
|
|
30517
|
+
});
|
|
30518
|
+
}
|
|
30519
|
+
try {
|
|
30520
|
+
if (DETERMINISTIC_TOOLS["complete_task"]) {
|
|
30521
|
+
const tool3 = DETERMINISTIC_TOOLS["complete_task"]();
|
|
30522
|
+
if (!tool3 || typeof tool3.execute !== "function") {
|
|
30523
|
+
issues.push({
|
|
30524
|
+
severity: "error",
|
|
30525
|
+
title: "Invalid Tool Implementation",
|
|
30526
|
+
description: "complete_task tool has missing or invalid execute function",
|
|
30527
|
+
fix: "Check src/runtime/tools/complete-task.ts"
|
|
30528
|
+
});
|
|
30529
|
+
}
|
|
30530
|
+
}
|
|
30531
|
+
} catch (e2) {
|
|
30532
|
+
issues.push({
|
|
30533
|
+
severity: "error",
|
|
30534
|
+
title: "Tool Initialization Error",
|
|
30535
|
+
description: `Error initializing complete_task for check: ${e2.message}`,
|
|
30536
|
+
fix: "Fix syntax or import errors in complete-task.ts"
|
|
30537
|
+
});
|
|
30538
|
+
}
|
|
30539
|
+
return {
|
|
30540
|
+
name: "Issue Resolution Workflow",
|
|
30541
|
+
message: issues.length === 0 ? "Workflow is intact and persistent" : "Workflow check found issues",
|
|
30542
|
+
status: issues.length === 0 ? "pass" : issues.some((i2) => i2.severity === "error") ? "fail" : "warn",
|
|
30543
|
+
issues
|
|
30544
|
+
};
|
|
30545
|
+
}
|
|
30546
|
+
|
|
30547
|
+
// src/cli/doctor/checks/tool-metadata.ts
|
|
30548
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
30549
|
+
var CRITICAL_TOOLS = ["complete_task", "fs_safe", "verify_action", "git_safe", "report_issue_verification"];
|
|
30550
|
+
var checkToolMetadataContract = {
|
|
30551
|
+
id: "tool-metadata-contract",
|
|
30552
|
+
name: "Tool Metadata Contract",
|
|
30553
|
+
critical: true,
|
|
30554
|
+
check: async () => {
|
|
30555
|
+
const issues = [];
|
|
30556
|
+
let hasError = false;
|
|
30557
|
+
const mockContext = {
|
|
30558
|
+
sessionID: "doctor-test",
|
|
30559
|
+
callID: "call-test",
|
|
30560
|
+
metadata: (meta3) => {
|
|
30561
|
+
if (typeof meta3.success !== "boolean" || typeof meta3.verified !== "boolean") {
|
|
30562
|
+
hasError = true;
|
|
30563
|
+
issues.push({
|
|
30564
|
+
severity: "error",
|
|
30565
|
+
title: "Malformed Metadata",
|
|
30566
|
+
description: `Tool did not return boolean 'success' and 'verified' fields in context.metadata(). Found: ${JSON.stringify(meta3)}`,
|
|
30567
|
+
fix: "Wrap the tool with withToolContract."
|
|
30568
|
+
});
|
|
30569
|
+
}
|
|
30570
|
+
}
|
|
30571
|
+
};
|
|
30572
|
+
for (const toolName of CRITICAL_TOOLS) {
|
|
30573
|
+
try {
|
|
30574
|
+
const toolSpec = getToolFromRegistry(toolName);
|
|
30575
|
+
if (!toolSpec) {
|
|
30576
|
+
issues.push({
|
|
30577
|
+
severity: "warning",
|
|
30578
|
+
title: "Unregistered Tool",
|
|
30579
|
+
description: `Critical tool '${toolName}' is not registered.`,
|
|
30580
|
+
fix: "Check src/runtime/tools/registry.ts."
|
|
30581
|
+
});
|
|
30582
|
+
continue;
|
|
30583
|
+
}
|
|
30584
|
+
await toolSpec.execute({ invalid_arg: "should_fail" }, mockContext);
|
|
30585
|
+
} catch (err) {
|
|
30586
|
+
issues.push({
|
|
30587
|
+
severity: "error",
|
|
30588
|
+
title: "Unhandled Tool Exception",
|
|
30589
|
+
description: `Tool '${toolName}' threw an unhandled exception instead of returning a safe failure result. Error: ${err.message}`,
|
|
30590
|
+
fix: "Wrap the tool's execute block with withToolContract to ensure safe fallbacks."
|
|
30591
|
+
});
|
|
30592
|
+
hasError = true;
|
|
30593
|
+
}
|
|
30594
|
+
}
|
|
30595
|
+
if (hasError) {
|
|
30596
|
+
return {
|
|
30597
|
+
name: "Tool Metadata Contract",
|
|
30598
|
+
status: "fail",
|
|
30599
|
+
message: import_picocolors19.default.red("Critical tools are violating the metadata contract on error paths."),
|
|
30600
|
+
issues
|
|
30601
|
+
};
|
|
30602
|
+
}
|
|
30603
|
+
return {
|
|
30604
|
+
name: "Tool Metadata Contract",
|
|
30605
|
+
status: "pass",
|
|
30606
|
+
message: import_picocolors19.default.green("All critical tools conform to the structured metadata contract."),
|
|
30607
|
+
issues
|
|
30608
|
+
};
|
|
30609
|
+
}
|
|
30610
|
+
};
|
|
30611
|
+
|
|
30612
|
+
// src/cli/doctor/checks/run-state-watchdog.ts
|
|
30613
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
30614
|
+
|
|
30615
|
+
// src/features/run-state-watchdog/manager.ts
|
|
30616
|
+
init_logger();
|
|
30617
|
+
|
|
30618
|
+
class RunStateWatchdogManager {
|
|
30619
|
+
client;
|
|
30620
|
+
activeSessions = new Map;
|
|
30621
|
+
pollingIntervalMs;
|
|
30622
|
+
stallThresholdMs;
|
|
30623
|
+
timer = null;
|
|
30624
|
+
constructor(client3, opts) {
|
|
30625
|
+
this.client = client3;
|
|
30626
|
+
this.pollingIntervalMs = opts?.pollingIntervalMs ?? 5000;
|
|
30627
|
+
this.stallThresholdMs = opts?.stallThresholdMs ?? 15000;
|
|
30628
|
+
}
|
|
30629
|
+
start() {
|
|
30630
|
+
if (this.timer)
|
|
30631
|
+
return;
|
|
30632
|
+
this.timer = setInterval(() => this.checkStalledRuns(), this.pollingIntervalMs);
|
|
30633
|
+
}
|
|
30634
|
+
stop() {
|
|
30635
|
+
if (this.timer) {
|
|
30636
|
+
clearInterval(this.timer);
|
|
30637
|
+
this.timer = null;
|
|
30638
|
+
}
|
|
30639
|
+
}
|
|
30640
|
+
updateState(sessionID, state3) {
|
|
30641
|
+
const ctx = this.getOrCreate(sessionID);
|
|
30642
|
+
ctx.currentState = state3;
|
|
30643
|
+
ctx.lastActivityAt = Date.now();
|
|
30644
|
+
}
|
|
30645
|
+
recordActivity(sessionID, type2) {
|
|
30646
|
+
const ctx = this.getOrCreate(sessionID);
|
|
30647
|
+
const now = Date.now();
|
|
30648
|
+
ctx.lastActivityAt = now;
|
|
30649
|
+
if (type2 === "text") {
|
|
30650
|
+
ctx.lastTextFragmentAt = now;
|
|
30651
|
+
} else if (type2 === "tool") {
|
|
30652
|
+
ctx.lastToolCallAt = now;
|
|
30653
|
+
}
|
|
30654
|
+
}
|
|
30655
|
+
updateTodos(sessionID, count) {
|
|
30656
|
+
const ctx = this.getOrCreate(sessionID);
|
|
30657
|
+
ctx.openTodos = count;
|
|
30658
|
+
}
|
|
30659
|
+
getContext(sessionID) {
|
|
30660
|
+
return this.activeSessions.get(sessionID);
|
|
30661
|
+
}
|
|
30662
|
+
getOrCreate(sessionID) {
|
|
30663
|
+
let ctx = this.activeSessions.get(sessionID);
|
|
30664
|
+
if (!ctx) {
|
|
30665
|
+
ctx = {
|
|
30666
|
+
sessionID,
|
|
30667
|
+
currentState: "idle",
|
|
30668
|
+
lastActivityAt: Date.now(),
|
|
30669
|
+
lastTextFragmentAt: Date.now(),
|
|
30670
|
+
lastToolCallAt: Date.now(),
|
|
30671
|
+
openTodos: 0
|
|
30672
|
+
};
|
|
30673
|
+
this.activeSessions.set(sessionID, ctx);
|
|
30674
|
+
}
|
|
30675
|
+
return ctx;
|
|
30676
|
+
}
|
|
30677
|
+
async checkStalledRuns() {
|
|
30678
|
+
const now = Date.now();
|
|
30679
|
+
for (const [sessionID, ctx] of this.activeSessions.entries()) {
|
|
30680
|
+
if (ctx.currentState !== "running" && ctx.currentState !== "waiting")
|
|
30681
|
+
continue;
|
|
30682
|
+
const timeSinceLastActivity = now - ctx.lastActivityAt;
|
|
30683
|
+
const timeSinceText = now - ctx.lastTextFragmentAt;
|
|
30684
|
+
const timeSinceTool = now - ctx.lastToolCallAt;
|
|
30685
|
+
if (timeSinceText > this.stallThresholdMs && timeSinceTool > this.stallThresholdMs) {
|
|
30686
|
+
log(`[RunStateWatchdog] Detected stalled run for session ${sessionID}.`, {
|
|
30687
|
+
timeSinceText,
|
|
30688
|
+
timeSinceTool,
|
|
30689
|
+
openTodos: ctx.openTodos
|
|
30690
|
+
});
|
|
30691
|
+
log(`[RunStateWatchdog] TERMINATING stalled session ${sessionID}.`);
|
|
30692
|
+
this.client.session.abort({
|
|
30693
|
+
path: { id: sessionID }
|
|
30694
|
+
}).catch((err) => {
|
|
30695
|
+
log(`[RunStateWatchdog] Failed to abort stalled session ${sessionID}`, { error: String(err) });
|
|
30696
|
+
});
|
|
30697
|
+
const tuiClient = this.client;
|
|
30698
|
+
if (tuiClient.tui?.showToast) {
|
|
30699
|
+
tuiClient.tui.showToast({
|
|
30700
|
+
body: {
|
|
30701
|
+
title: "Task Aborted",
|
|
30702
|
+
message: "Session terminated due to inactivity / stall detection.",
|
|
30703
|
+
variant: "error",
|
|
30704
|
+
duration: 5000
|
|
30705
|
+
}
|
|
30706
|
+
}).catch(() => {});
|
|
30707
|
+
}
|
|
30708
|
+
this.activeSessions.delete(sessionID);
|
|
30709
|
+
}
|
|
30710
|
+
}
|
|
30711
|
+
}
|
|
30712
|
+
}
|
|
30713
|
+
|
|
30714
|
+
// src/cli/doctor/checks/run-state-watchdog.ts
|
|
30715
|
+
var checkRunStateWatchdog = {
|
|
30716
|
+
id: "run-state-watchdog",
|
|
30717
|
+
name: "Run State Watchdog",
|
|
30718
|
+
critical: true,
|
|
30719
|
+
check: async () => {
|
|
30720
|
+
const issues = [];
|
|
30721
|
+
let hasError = false;
|
|
30722
|
+
let toastCalls = 0;
|
|
30723
|
+
const mockClient = {
|
|
30724
|
+
tui: {
|
|
30725
|
+
showToast: async () => {
|
|
30726
|
+
toastCalls++;
|
|
30727
|
+
}
|
|
30728
|
+
}
|
|
30729
|
+
};
|
|
30730
|
+
const manager = new RunStateWatchdogManager(mockClient, { pollingIntervalMs: 5, stallThresholdMs: 20 });
|
|
30731
|
+
manager.updateState("sess-doctor", "running");
|
|
30732
|
+
manager.recordActivity("sess-doctor", "text");
|
|
30733
|
+
await new Promise((r2) => setTimeout(r2, 25));
|
|
30734
|
+
await manager.checkStalledRuns();
|
|
30735
|
+
if (toastCalls === 0) {
|
|
30736
|
+
hasError = true;
|
|
30737
|
+
issues.push({
|
|
30738
|
+
severity: "error",
|
|
30739
|
+
title: "Watchdog Stall Detection Failed",
|
|
30740
|
+
description: "Watchdog did not emit a toast when a session with status 'running' had no recent text/tool activity.",
|
|
30741
|
+
fix: "Verify RunStateWatchdogManager polling thresholds and state storage."
|
|
30742
|
+
});
|
|
30743
|
+
}
|
|
30744
|
+
toastCalls = 0;
|
|
30745
|
+
manager.updateState("sess-doctor", "idle");
|
|
30746
|
+
await new Promise((r2) => setTimeout(r2, 25));
|
|
30747
|
+
await manager.checkStalledRuns();
|
|
30748
|
+
if (toastCalls > 0) {
|
|
30749
|
+
hasError = true;
|
|
30750
|
+
issues.push({
|
|
30751
|
+
severity: "warning",
|
|
30752
|
+
title: "Watchdog False Positive",
|
|
30753
|
+
description: "Watchdog emitted a stall toast for an 'idle' session.",
|
|
30754
|
+
fix: "Watchdog should ignore idle or non-active sessions."
|
|
30755
|
+
});
|
|
30756
|
+
}
|
|
30757
|
+
manager.stop();
|
|
30758
|
+
if (hasError) {
|
|
30759
|
+
return {
|
|
30760
|
+
name: "Run State Watchdog",
|
|
30761
|
+
status: "fail",
|
|
30762
|
+
message: import_picocolors20.default.red("Run State Watchdog failed coverage checks."),
|
|
30763
|
+
issues
|
|
30764
|
+
};
|
|
30765
|
+
}
|
|
30766
|
+
return {
|
|
30767
|
+
name: "Run State Watchdog",
|
|
30768
|
+
status: "pass",
|
|
30769
|
+
message: import_picocolors20.default.green("Watchdog detects and surfaces stalled runs correctly."),
|
|
30770
|
+
issues
|
|
30771
|
+
};
|
|
30772
|
+
}
|
|
30773
|
+
};
|
|
30774
|
+
// src/cli/doctor/checks/index.ts
|
|
30775
|
+
function getAllCheckDefinitions() {
|
|
30776
|
+
return [
|
|
30777
|
+
{
|
|
30778
|
+
id: CHECK_IDS.SYSTEM,
|
|
30779
|
+
name: CHECK_NAMES[CHECK_IDS.SYSTEM],
|
|
30780
|
+
check: checkSystem,
|
|
30781
|
+
critical: true
|
|
30782
|
+
},
|
|
30783
|
+
{
|
|
30784
|
+
id: CHECK_IDS.CONFIG,
|
|
30785
|
+
name: CHECK_NAMES[CHECK_IDS.CONFIG],
|
|
30786
|
+
check: checkConfig
|
|
30787
|
+
},
|
|
30788
|
+
{
|
|
30789
|
+
id: CHECK_IDS.TOOLS,
|
|
28547
30790
|
name: CHECK_NAMES[CHECK_IDS.TOOLS],
|
|
28548
30791
|
check: checkTools
|
|
28549
30792
|
},
|
|
@@ -28561,15 +30804,50 @@ function getAllCheckDefinitions() {
|
|
|
28561
30804
|
id: CHECK_IDS.DEFAULT_CONFIG,
|
|
28562
30805
|
name: CHECK_NAMES[CHECK_IDS.DEFAULT_CONFIG],
|
|
28563
30806
|
check: checkDefaultConfig
|
|
30807
|
+
},
|
|
30808
|
+
{
|
|
30809
|
+
id: CHECK_IDS.PLAN_COMPILER,
|
|
30810
|
+
name: CHECK_NAMES[CHECK_IDS.PLAN_COMPILER],
|
|
30811
|
+
check: checkPlanCompiler
|
|
30812
|
+
},
|
|
30813
|
+
{
|
|
30814
|
+
id: CHECK_IDS.PROGRESS,
|
|
30815
|
+
name: CHECK_NAMES[CHECK_IDS.PROGRESS],
|
|
30816
|
+
check: checkProgress
|
|
30817
|
+
},
|
|
30818
|
+
{
|
|
30819
|
+
id: CHECK_IDS.EDIT_ATOMICITY,
|
|
30820
|
+
name: CHECK_NAMES[CHECK_IDS.EDIT_ATOMICITY],
|
|
30821
|
+
check: checkEditAtomicity.check
|
|
30822
|
+
},
|
|
30823
|
+
{
|
|
30824
|
+
id: "issue-resolution",
|
|
30825
|
+
name: "Issue Resolution Workflow",
|
|
30826
|
+
check: checkIssueResolutionWorkflow
|
|
30827
|
+
},
|
|
30828
|
+
{
|
|
30829
|
+
id: checkToolMetadataContract.id,
|
|
30830
|
+
name: checkToolMetadataContract.name,
|
|
30831
|
+
check: checkToolMetadataContract.check
|
|
30832
|
+
},
|
|
30833
|
+
{
|
|
30834
|
+
id: checkRunStateWatchdog.id,
|
|
30835
|
+
name: checkRunStateWatchdog.name,
|
|
30836
|
+
check: checkRunStateWatchdog.check
|
|
30837
|
+
},
|
|
30838
|
+
{
|
|
30839
|
+
id: "TOOL_CONTRACT",
|
|
30840
|
+
name: "Tool Contract Compliance",
|
|
30841
|
+
check: checkToolContract
|
|
28564
30842
|
}
|
|
28565
30843
|
];
|
|
28566
30844
|
}
|
|
28567
30845
|
|
|
28568
30846
|
// src/cli/doctor/format-default.ts
|
|
28569
|
-
var
|
|
30847
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
28570
30848
|
|
|
28571
30849
|
// src/cli/doctor/format-shared.ts
|
|
28572
|
-
var
|
|
30850
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
28573
30851
|
function formatStatusSymbol(status) {
|
|
28574
30852
|
const colorFn = STATUS_COLORS[status];
|
|
28575
30853
|
switch (status) {
|
|
@@ -28584,23 +30862,23 @@ function formatStatusSymbol(status) {
|
|
|
28584
30862
|
}
|
|
28585
30863
|
}
|
|
28586
30864
|
function formatStatusMark(available) {
|
|
28587
|
-
return available ?
|
|
30865
|
+
return available ? import_picocolors21.default.green(SYMBOLS3.check) : import_picocolors21.default.red(SYMBOLS3.cross);
|
|
28588
30866
|
}
|
|
28589
30867
|
function formatHeader() {
|
|
28590
30868
|
return `
|
|
28591
|
-
${
|
|
30869
|
+
${import_picocolors21.default.bgMagenta(import_picocolors21.default.white(" oMoMoMoMo Doctor "))}
|
|
28592
30870
|
`;
|
|
28593
30871
|
}
|
|
28594
30872
|
function formatIssue(issue2, index) {
|
|
28595
30873
|
const lines = [];
|
|
28596
|
-
const severityColor = issue2.severity === "error" ?
|
|
30874
|
+
const severityColor = issue2.severity === "error" ? import_picocolors21.default.red : import_picocolors21.default.yellow;
|
|
28597
30875
|
lines.push(`${index}. ${severityColor(issue2.title)}`);
|
|
28598
|
-
lines.push(` ${
|
|
30876
|
+
lines.push(` ${import_picocolors21.default.dim(issue2.description)}`);
|
|
28599
30877
|
if (issue2.fix) {
|
|
28600
|
-
lines.push(` ${
|
|
30878
|
+
lines.push(` ${import_picocolors21.default.cyan("Fix:")} ${import_picocolors21.default.dim(issue2.fix)}`);
|
|
28601
30879
|
}
|
|
28602
30880
|
if (issue2.affects && issue2.affects.length > 0) {
|
|
28603
|
-
lines.push(` ${
|
|
30881
|
+
lines.push(` ${import_picocolors21.default.cyan("Affects:")} ${import_picocolors21.default.dim(issue2.affects.join(", "))}`);
|
|
28604
30882
|
}
|
|
28605
30883
|
return lines.join(`
|
|
28606
30884
|
`);
|
|
@@ -28614,12 +30892,12 @@ function formatDefault(result) {
|
|
|
28614
30892
|
if (allIssues.length === 0) {
|
|
28615
30893
|
const opencodeVer = result.systemInfo.opencodeVersion ?? "unknown";
|
|
28616
30894
|
const pluginVer = result.systemInfo.pluginVersion ?? "unknown";
|
|
28617
|
-
lines.push(` ${
|
|
30895
|
+
lines.push(` ${import_picocolors22.default.green(SYMBOLS3.check)} ${import_picocolors22.default.green(`System OK (opencode ${opencodeVer} \xB7 oh-my-opencode ${pluginVer})`)}`);
|
|
28618
30896
|
} else {
|
|
28619
30897
|
const issueCount = allIssues.filter((i2) => i2.severity === "error").length;
|
|
28620
30898
|
const warnCount = allIssues.filter((i2) => i2.severity === "warning").length;
|
|
28621
30899
|
const totalStr = `${issueCount + warnCount} ${issueCount + warnCount === 1 ? "issue" : "issues"}`;
|
|
28622
|
-
lines.push(` ${
|
|
30900
|
+
lines.push(` ${import_picocolors22.default.yellow(SYMBOLS3.warn)} ${totalStr} found:
|
|
28623
30901
|
`);
|
|
28624
30902
|
allIssues.forEach((issue2, index) => {
|
|
28625
30903
|
lines.push(formatIssue(issue2, index + 1));
|
|
@@ -28631,7 +30909,7 @@ function formatDefault(result) {
|
|
|
28631
30909
|
}
|
|
28632
30910
|
|
|
28633
30911
|
// src/cli/doctor/format-status.ts
|
|
28634
|
-
var
|
|
30912
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
28635
30913
|
function formatStatus(result) {
|
|
28636
30914
|
const lines = [];
|
|
28637
30915
|
lines.push(formatHeader());
|
|
@@ -28642,7 +30920,7 @@ function formatStatus(result) {
|
|
|
28642
30920
|
const bunVer = systemInfo.bunVersion ?? "unknown";
|
|
28643
30921
|
lines.push(` ${padding}System ${opencodeVer} \xB7 ${pluginVer} \xB7 Bun ${bunVer}`);
|
|
28644
30922
|
const configPath = systemInfo.configPath ?? "unknown";
|
|
28645
|
-
const configStatus = systemInfo.configValid ?
|
|
30923
|
+
const configStatus = systemInfo.configValid ? import_picocolors23.default.green("(valid)") : import_picocolors23.default.red("(invalid)");
|
|
28646
30924
|
lines.push(` ${padding}Config ${configPath} ${configStatus}`);
|
|
28647
30925
|
const lspText = `LSP ${tools.lspInstalled}/${tools.lspTotal}`;
|
|
28648
30926
|
const astGrepMark = formatStatusMark(tools.astGrepCli);
|
|
@@ -28654,18 +30932,40 @@ function formatStatus(result) {
|
|
|
28654
30932
|
const builtinText = builtinCount > 0 ? tools.mcpBuiltin.join(" \xB7 ") : "none";
|
|
28655
30933
|
const userText = userCount > 0 ? `+ ${userCount} user` : "";
|
|
28656
30934
|
lines.push(` ${padding}MCPs ${builtinText} ${userText}`);
|
|
30935
|
+
if (result.activeTasks && result.activeTasks.length > 0) {
|
|
30936
|
+
const activeTasks = result.activeTasks;
|
|
30937
|
+
lines.push("");
|
|
30938
|
+
lines.push(` ${padding}Active Tasks (${activeTasks.length}):`);
|
|
30939
|
+
for (const task of activeTasks) {
|
|
30940
|
+
const { agent, description, progress } = task;
|
|
30941
|
+
const bar = renderProgressBar(progress?.percent);
|
|
30942
|
+
const phase = progress?.phase ? ` [${progress.phase}]` : "";
|
|
30943
|
+
const message = progress?.message ? ` (${progress.message})` : "";
|
|
30944
|
+
lines.push(` ${import_picocolors23.default.blue("\xB7")} ${import_picocolors23.default.bold(agent)}: ${description}${phase}${message}`);
|
|
30945
|
+
lines.push(` ${bar}`);
|
|
30946
|
+
}
|
|
30947
|
+
}
|
|
28657
30948
|
return lines.join(`
|
|
28658
30949
|
`);
|
|
28659
30950
|
}
|
|
30951
|
+
function renderProgressBar(percent) {
|
|
30952
|
+
if (percent === undefined)
|
|
30953
|
+
return import_picocolors23.default.dim("[\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591\u2591]");
|
|
30954
|
+
const totalBlocks = 10;
|
|
30955
|
+
const filledBlocks = Math.round(percent / 100 * totalBlocks);
|
|
30956
|
+
const emptyBlocks = totalBlocks - filledBlocks;
|
|
30957
|
+
const bar = import_picocolors23.default.cyan("\u2588".repeat(filledBlocks)) + import_picocolors23.default.dim("\u2591".repeat(emptyBlocks));
|
|
30958
|
+
return `[${bar}] ${import_picocolors23.default.bold(percent + "%")}`;
|
|
30959
|
+
}
|
|
28660
30960
|
|
|
28661
30961
|
// src/cli/doctor/format-verbose.ts
|
|
28662
|
-
var
|
|
30962
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
28663
30963
|
function formatVerbose(result) {
|
|
28664
30964
|
const lines = [];
|
|
28665
30965
|
lines.push(formatHeader());
|
|
28666
30966
|
const { systemInfo, tools, results, summary } = result;
|
|
28667
|
-
lines.push(`${
|
|
28668
|
-
lines.push(`${
|
|
30967
|
+
lines.push(`${import_picocolors24.default.bold("System Information")}`);
|
|
30968
|
+
lines.push(`${import_picocolors24.default.dim("\u2500".repeat(40))}`);
|
|
28669
30969
|
lines.push(` ${formatStatusSymbol("pass")} opencode ${systemInfo.opencodeVersion ?? "unknown"}`);
|
|
28670
30970
|
lines.push(` ${formatStatusSymbol("pass")} oh-my-opencode ${systemInfo.pluginVersion ?? "unknown"}`);
|
|
28671
30971
|
if (systemInfo.loadedVersion) {
|
|
@@ -28676,33 +30976,33 @@ function formatVerbose(result) {
|
|
|
28676
30976
|
}
|
|
28677
30977
|
lines.push(` ${formatStatusSymbol("pass")} path ${systemInfo.opencodePath ?? "unknown"}`);
|
|
28678
30978
|
if (systemInfo.isLocalDev) {
|
|
28679
|
-
lines.push(` ${
|
|
30979
|
+
lines.push(` ${import_picocolors24.default.yellow("*")} ${import_picocolors24.default.dim("(local development mode)")}`);
|
|
28680
30980
|
}
|
|
28681
30981
|
lines.push("");
|
|
28682
|
-
lines.push(`${
|
|
28683
|
-
lines.push(`${
|
|
28684
|
-
const configStatus = systemInfo.configValid ?
|
|
30982
|
+
lines.push(`${import_picocolors24.default.bold("Configuration")}`);
|
|
30983
|
+
lines.push(`${import_picocolors24.default.dim("\u2500".repeat(40))}`);
|
|
30984
|
+
const configStatus = systemInfo.configValid ? import_picocolors24.default.green("valid") : import_picocolors24.default.red("invalid");
|
|
28685
30985
|
lines.push(` ${formatStatusSymbol(systemInfo.configValid ? "pass" : "fail")} ${systemInfo.configPath ?? "unknown"} (${configStatus})`);
|
|
28686
30986
|
lines.push("");
|
|
28687
|
-
lines.push(`${
|
|
28688
|
-
lines.push(`${
|
|
30987
|
+
lines.push(`${import_picocolors24.default.bold("Tools")}`);
|
|
30988
|
+
lines.push(`${import_picocolors24.default.dim("\u2500".repeat(40))}`);
|
|
28689
30989
|
lines.push(` ${formatStatusSymbol("pass")} LSP ${tools.lspInstalled}/${tools.lspTotal} installed`);
|
|
28690
30990
|
lines.push(` ${formatStatusSymbol(tools.astGrepCli ? "pass" : "fail")} ast-grep CLI ${tools.astGrepCli ? "installed" : "not found"}`);
|
|
28691
30991
|
lines.push(` ${formatStatusSymbol(tools.astGrepNapi ? "pass" : "fail")} ast-grep napi ${tools.astGrepNapi ? "installed" : "not found"}`);
|
|
28692
30992
|
lines.push(` ${formatStatusSymbol(tools.commentChecker ? "pass" : "fail")} comment-checker ${tools.commentChecker ? "installed" : "not found"}`);
|
|
28693
30993
|
lines.push(` ${formatStatusSymbol(tools.ghCli.installed && tools.ghCli.authenticated ? "pass" : "fail")} gh CLI ${tools.ghCli.installed ? "installed" : "not found"}${tools.ghCli.authenticated && tools.ghCli.username ? ` (${tools.ghCli.username})` : ""}`);
|
|
28694
30994
|
lines.push("");
|
|
28695
|
-
lines.push(`${
|
|
28696
|
-
lines.push(`${
|
|
30995
|
+
lines.push(`${import_picocolors24.default.bold("MCPs")}`);
|
|
30996
|
+
lines.push(`${import_picocolors24.default.dim("\u2500".repeat(40))}`);
|
|
28697
30997
|
if (tools.mcpBuiltin.length === 0) {
|
|
28698
|
-
lines.push(` ${
|
|
30998
|
+
lines.push(` ${import_picocolors24.default.dim("No built-in MCPs")}`);
|
|
28699
30999
|
} else {
|
|
28700
31000
|
for (const mcp of tools.mcpBuiltin) {
|
|
28701
31001
|
lines.push(` ${formatStatusSymbol("pass")} ${mcp}`);
|
|
28702
31002
|
}
|
|
28703
31003
|
}
|
|
28704
31004
|
if (tools.mcpUser.length > 0) {
|
|
28705
|
-
lines.push(` ${
|
|
31005
|
+
lines.push(` ${import_picocolors24.default.cyan("+")} ${tools.mcpUser.length} user MCP(s):`);
|
|
28706
31006
|
for (const mcp of tools.mcpUser) {
|
|
28707
31007
|
lines.push(` ${formatStatusSymbol("pass")} ${mcp}`);
|
|
28708
31008
|
}
|
|
@@ -28710,20 +31010,20 @@ function formatVerbose(result) {
|
|
|
28710
31010
|
lines.push("");
|
|
28711
31011
|
const allIssues = results.flatMap((r2) => r2.issues);
|
|
28712
31012
|
if (allIssues.length > 0) {
|
|
28713
|
-
lines.push(`${
|
|
28714
|
-
lines.push(`${
|
|
31013
|
+
lines.push(`${import_picocolors24.default.bold("Issues")}`);
|
|
31014
|
+
lines.push(`${import_picocolors24.default.dim("\u2500".repeat(40))}`);
|
|
28715
31015
|
allIssues.forEach((issue2, index) => {
|
|
28716
31016
|
lines.push(formatIssue(issue2, index + 1));
|
|
28717
31017
|
lines.push("");
|
|
28718
31018
|
});
|
|
28719
31019
|
}
|
|
28720
|
-
lines.push(`${
|
|
28721
|
-
lines.push(`${
|
|
28722
|
-
const passText = summary.passed > 0 ?
|
|
28723
|
-
const failText = summary.failed > 0 ?
|
|
28724
|
-
const warnText = summary.warnings > 0 ?
|
|
31020
|
+
lines.push(`${import_picocolors24.default.bold("Summary")}`);
|
|
31021
|
+
lines.push(`${import_picocolors24.default.dim("\u2500".repeat(40))}`);
|
|
31022
|
+
const passText = summary.passed > 0 ? import_picocolors24.default.green(`${summary.passed} passed`) : `${summary.passed} passed`;
|
|
31023
|
+
const failText = summary.failed > 0 ? import_picocolors24.default.red(`${summary.failed} failed`) : `${summary.failed} failed`;
|
|
31024
|
+
const warnText = summary.warnings > 0 ? import_picocolors24.default.yellow(`${summary.warnings} warnings`) : `${summary.warnings} warnings`;
|
|
28725
31025
|
lines.push(` ${passText}, ${failText}, ${warnText}`);
|
|
28726
|
-
lines.push(` ${
|
|
31026
|
+
lines.push(` ${import_picocolors24.default.dim(`Total: ${summary.total} checks in ${summary.duration}ms`)}`);
|
|
28727
31027
|
return lines.join(`
|
|
28728
31028
|
`);
|
|
28729
31029
|
}
|
|
@@ -28776,10 +31076,11 @@ function determineExitCode(results) {
|
|
|
28776
31076
|
async function runDoctor(options) {
|
|
28777
31077
|
const start = performance.now();
|
|
28778
31078
|
const allChecks = getAllCheckDefinitions();
|
|
28779
|
-
const [results, systemInfo, tools] = await Promise.all([
|
|
31079
|
+
const [results, systemInfo, tools, activeTasks] = await Promise.all([
|
|
28780
31080
|
Promise.all(allChecks.map(runCheck)),
|
|
28781
31081
|
gatherSystemInfo(),
|
|
28782
|
-
gatherToolsSummary()
|
|
31082
|
+
gatherToolsSummary(),
|
|
31083
|
+
Promise.resolve(readActiveTasks())
|
|
28783
31084
|
]);
|
|
28784
31085
|
const duration3 = performance.now() - start;
|
|
28785
31086
|
const summary = calculateSummary(results, duration3);
|
|
@@ -28789,6 +31090,7 @@ async function runDoctor(options) {
|
|
|
28789
31090
|
systemInfo,
|
|
28790
31091
|
tools,
|
|
28791
31092
|
summary,
|
|
31093
|
+
activeTasks,
|
|
28792
31094
|
exitCode
|
|
28793
31095
|
};
|
|
28794
31096
|
if (options.json) {
|
|
@@ -28807,11 +31109,11 @@ async function doctor(options = { mode: "default" }) {
|
|
|
28807
31109
|
|
|
28808
31110
|
// src/features/mcp-oauth/storage.ts
|
|
28809
31111
|
init_shared();
|
|
28810
|
-
import { chmodSync, existsSync as
|
|
28811
|
-
import { dirname as
|
|
31112
|
+
import { chmodSync, existsSync as existsSync36, mkdirSync as mkdirSync12, readFileSync as readFileSync34, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "fs";
|
|
31113
|
+
import { dirname as dirname11, join as join33 } from "path";
|
|
28812
31114
|
var STORAGE_FILE_NAME = "mcp-oauth.json";
|
|
28813
31115
|
function getMcpOauthStoragePath() {
|
|
28814
|
-
return
|
|
31116
|
+
return join33(getOpenCodeConfigDir({ binary: "opencode" }), STORAGE_FILE_NAME);
|
|
28815
31117
|
}
|
|
28816
31118
|
function normalizeHost(serverHost) {
|
|
28817
31119
|
let host = serverHost.trim();
|
|
@@ -28848,11 +31150,11 @@ function buildKey(serverHost, resource) {
|
|
|
28848
31150
|
}
|
|
28849
31151
|
function readStore() {
|
|
28850
31152
|
const filePath = getMcpOauthStoragePath();
|
|
28851
|
-
if (!
|
|
31153
|
+
if (!existsSync36(filePath)) {
|
|
28852
31154
|
return null;
|
|
28853
31155
|
}
|
|
28854
31156
|
try {
|
|
28855
|
-
const content =
|
|
31157
|
+
const content = readFileSync34(filePath, "utf-8");
|
|
28856
31158
|
return JSON.parse(content);
|
|
28857
31159
|
} catch {
|
|
28858
31160
|
return null;
|
|
@@ -28861,11 +31163,11 @@ function readStore() {
|
|
|
28861
31163
|
function writeStore(store2) {
|
|
28862
31164
|
const filePath = getMcpOauthStoragePath();
|
|
28863
31165
|
try {
|
|
28864
|
-
const dir =
|
|
28865
|
-
if (!
|
|
28866
|
-
|
|
31166
|
+
const dir = dirname11(filePath);
|
|
31167
|
+
if (!existsSync36(dir)) {
|
|
31168
|
+
mkdirSync12(dir, { recursive: true });
|
|
28867
31169
|
}
|
|
28868
|
-
|
|
31170
|
+
writeFileSync18(filePath, JSON.stringify(store2, null, 2), { encoding: "utf-8", mode: 384 });
|
|
28869
31171
|
chmodSync(filePath, 384);
|
|
28870
31172
|
return true;
|
|
28871
31173
|
} catch {
|
|
@@ -28897,8 +31199,8 @@ function deleteToken(serverHost, resource) {
|
|
|
28897
31199
|
if (Object.keys(store2).length === 0) {
|
|
28898
31200
|
try {
|
|
28899
31201
|
const filePath = getMcpOauthStoragePath();
|
|
28900
|
-
if (
|
|
28901
|
-
|
|
31202
|
+
if (existsSync36(filePath)) {
|
|
31203
|
+
unlinkSync5(filePath);
|
|
28902
31204
|
}
|
|
28903
31205
|
return true;
|
|
28904
31206
|
} catch {
|
|
@@ -29077,7 +31379,7 @@ async function findAvailablePort2(startPort = DEFAULT_PORT) {
|
|
|
29077
31379
|
}
|
|
29078
31380
|
|
|
29079
31381
|
// src/features/mcp-oauth/oauth-authorization-flow.ts
|
|
29080
|
-
import { spawn as
|
|
31382
|
+
import { spawn as spawn5 } from "child_process";
|
|
29081
31383
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
29082
31384
|
import { createServer } from "http";
|
|
29083
31385
|
function generateCodeVerifier() {
|
|
@@ -29104,13 +31406,13 @@ function buildAuthorizationUrl(authorizationEndpoint, options) {
|
|
|
29104
31406
|
}
|
|
29105
31407
|
var CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
29106
31408
|
function startCallbackServer(port) {
|
|
29107
|
-
return new Promise((
|
|
31409
|
+
return new Promise((resolve3, reject) => {
|
|
29108
31410
|
let timeoutId;
|
|
29109
31411
|
const server2 = createServer((request, response) => {
|
|
29110
31412
|
clearTimeout(timeoutId);
|
|
29111
31413
|
const requestUrl = new URL(request.url ?? "/", `http://localhost:${port}`);
|
|
29112
31414
|
const code = requestUrl.searchParams.get("code");
|
|
29113
|
-
const
|
|
31415
|
+
const state3 = requestUrl.searchParams.get("state");
|
|
29114
31416
|
const error48 = requestUrl.searchParams.get("error");
|
|
29115
31417
|
if (error48) {
|
|
29116
31418
|
const errorDescription = requestUrl.searchParams.get("error_description") ?? error48;
|
|
@@ -29120,7 +31422,7 @@ function startCallbackServer(port) {
|
|
|
29120
31422
|
reject(new Error(`OAuth authorization error: ${errorDescription}`));
|
|
29121
31423
|
return;
|
|
29122
31424
|
}
|
|
29123
|
-
if (!code || !
|
|
31425
|
+
if (!code || !state3) {
|
|
29124
31426
|
response.writeHead(400, { "content-type": "text/html" });
|
|
29125
31427
|
response.end("<html><body><h1>Missing code or state</h1></body></html>");
|
|
29126
31428
|
server2.close();
|
|
@@ -29130,7 +31432,7 @@ function startCallbackServer(port) {
|
|
|
29130
31432
|
response.writeHead(200, { "content-type": "text/html" });
|
|
29131
31433
|
response.end("<html><body><h1>Authorization successful. You can close this tab.</h1></body></html>");
|
|
29132
31434
|
server2.close();
|
|
29133
|
-
|
|
31435
|
+
resolve3({ code, state: state3 });
|
|
29134
31436
|
});
|
|
29135
31437
|
timeoutId = setTimeout(() => {
|
|
29136
31438
|
server2.close();
|
|
@@ -29158,7 +31460,7 @@ function openBrowser(url2) {
|
|
|
29158
31460
|
args = [url2];
|
|
29159
31461
|
}
|
|
29160
31462
|
try {
|
|
29161
|
-
const child =
|
|
31463
|
+
const child = spawn5(command, args, { stdio: "ignore", detached: true });
|
|
29162
31464
|
child.on("error", () => {});
|
|
29163
31465
|
child.unref();
|
|
29164
31466
|
} catch {}
|
|
@@ -29166,19 +31468,19 @@ function openBrowser(url2) {
|
|
|
29166
31468
|
async function runAuthorizationCodeRedirect(options) {
|
|
29167
31469
|
const verifier = generateCodeVerifier();
|
|
29168
31470
|
const challenge = generateCodeChallenge(verifier);
|
|
29169
|
-
const
|
|
31471
|
+
const state3 = randomBytes2(16).toString("hex");
|
|
29170
31472
|
const authorizationUrl = buildAuthorizationUrl(options.authorizationEndpoint, {
|
|
29171
31473
|
clientId: options.clientId,
|
|
29172
31474
|
redirectUri: options.redirectUri,
|
|
29173
31475
|
codeChallenge: challenge,
|
|
29174
|
-
state,
|
|
31476
|
+
state: state3,
|
|
29175
31477
|
scopes: options.scopes,
|
|
29176
31478
|
resource: options.resource
|
|
29177
31479
|
});
|
|
29178
31480
|
const callbackPromise = startCallbackServer(options.callbackPort);
|
|
29179
31481
|
openBrowser(authorizationUrl);
|
|
29180
31482
|
const result = await callbackPromise;
|
|
29181
|
-
if (result.state !==
|
|
31483
|
+
if (result.state !== state3) {
|
|
29182
31484
|
throw new Error("OAuth state mismatch");
|
|
29183
31485
|
}
|
|
29184
31486
|
return { code: result.code, verifier };
|
|
@@ -29428,26 +31730,26 @@ function createMcpOAuthCommand() {
|
|
|
29428
31730
|
}
|
|
29429
31731
|
|
|
29430
31732
|
// src/cli/master-login/index.ts
|
|
29431
|
-
var
|
|
29432
|
-
import { writeFileSync as
|
|
29433
|
-
import { join as
|
|
31733
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
31734
|
+
import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync13, existsSync as existsSync37 } from "fs";
|
|
31735
|
+
import { join as join34, dirname as dirname12 } from "path";
|
|
29434
31736
|
import * as os5 from "os";
|
|
29435
31737
|
import { createRequire as createRequire2 } from "module";
|
|
29436
31738
|
async function resolvePlaywright() {
|
|
29437
31739
|
try {
|
|
29438
31740
|
return await import("playwright");
|
|
29439
31741
|
} catch (e2) {}
|
|
29440
|
-
const binaryDir =
|
|
31742
|
+
const binaryDir = dirname12(process.execPath);
|
|
29441
31743
|
let currentDir = binaryDir;
|
|
29442
31744
|
for (let i2 = 0;i2 < 6; i2++) {
|
|
29443
|
-
const potentialPath =
|
|
29444
|
-
if (
|
|
31745
|
+
const potentialPath = join34(currentDir, "node_modules", "playwright");
|
|
31746
|
+
if (existsSync37(potentialPath)) {
|
|
29445
31747
|
try {
|
|
29446
|
-
const require2 = createRequire2(
|
|
31748
|
+
const require2 = createRequire2(join34(currentDir, "index.js"));
|
|
29447
31749
|
return require2("playwright");
|
|
29448
31750
|
} catch (e2) {}
|
|
29449
31751
|
}
|
|
29450
|
-
const parent =
|
|
31752
|
+
const parent = dirname12(currentDir);
|
|
29451
31753
|
if (parent === currentDir)
|
|
29452
31754
|
break;
|
|
29453
31755
|
currentDir = parent;
|
|
@@ -29455,26 +31757,26 @@ async function resolvePlaywright() {
|
|
|
29455
31757
|
throw new Error("Cannot find package 'playwright'. Please ensure it's installed via 'npm install -g @heidi-dang/oh-my-opencode'.");
|
|
29456
31758
|
}
|
|
29457
31759
|
async function masterLogin(options) {
|
|
29458
|
-
const configPath =
|
|
29459
|
-
if (
|
|
29460
|
-
console.log(`${
|
|
29461
|
-
console.log(
|
|
31760
|
+
const configPath = join34(os5.homedir(), ".ygka_config.json");
|
|
31761
|
+
if (existsSync37(configPath) && !options.force) {
|
|
31762
|
+
console.log(`${import_picocolors25.default.yellow("[exists]")} ${import_picocolors25.default.dim(configPath)}`);
|
|
31763
|
+
console.log(import_picocolors25.default.dim("YGKA config already exists. Use --force to overwrite."));
|
|
29462
31764
|
return 0;
|
|
29463
31765
|
}
|
|
29464
31766
|
let playwrightModule;
|
|
29465
31767
|
try {
|
|
29466
31768
|
playwrightModule = await resolvePlaywright();
|
|
29467
31769
|
} catch (err) {
|
|
29468
|
-
console.error(
|
|
31770
|
+
console.error(import_picocolors25.default.red(`
|
|
29469
31771
|
[error] Playwright not found.`));
|
|
29470
|
-
console.log(
|
|
29471
|
-
console.log(
|
|
29472
|
-
console.log(
|
|
31772
|
+
console.log(import_picocolors25.default.dim("This command requires Playwright to automate the login process."));
|
|
31773
|
+
console.log(import_picocolors25.default.dim("Please try running: npm install -g @heidi-dang/oh-my-opencode"));
|
|
31774
|
+
console.log(import_picocolors25.default.dim(`Diagnostic: looked for 'playwright' in ${dirname12(process.execPath)} and parents.`));
|
|
29473
31775
|
return 1;
|
|
29474
31776
|
}
|
|
29475
|
-
console.log(
|
|
29476
|
-
console.log(
|
|
29477
|
-
console.log(
|
|
31777
|
+
console.log(import_picocolors25.default.cyan("\uD83D\uDE80 Launching browser for ChatGPT login..."));
|
|
31778
|
+
console.log(import_picocolors25.default.dim("Please log in to chat.openai.com in the window that appears."));
|
|
31779
|
+
console.log(import_picocolors25.default.dim("Once logged in, return here. I will automatically detect the session."));
|
|
29478
31780
|
const { chromium } = playwrightModule;
|
|
29479
31781
|
const browser = await chromium.launch({ headless: false });
|
|
29480
31782
|
const context = await browser.newContext();
|
|
@@ -29492,18 +31794,18 @@ async function masterLogin(options) {
|
|
|
29492
31794
|
sessionToken = sessionCookie.value;
|
|
29493
31795
|
break;
|
|
29494
31796
|
}
|
|
29495
|
-
process.stdout.write(`\r${
|
|
31797
|
+
process.stdout.write(`\r${import_picocolors25.default.blue(spinner[spinnerIdx])} Waiting for login... `);
|
|
29496
31798
|
spinnerIdx = (spinnerIdx + 1) % spinner.length;
|
|
29497
|
-
await new Promise((
|
|
31799
|
+
await new Promise((resolve3) => setTimeout(resolve3, 500));
|
|
29498
31800
|
}
|
|
29499
31801
|
process.stdout.write("\r");
|
|
29500
31802
|
if (!sessionToken) {
|
|
29501
|
-
console.error(
|
|
31803
|
+
console.error(import_picocolors25.default.red(`
|
|
29502
31804
|
[error] Login timed out or session token not found.`));
|
|
29503
31805
|
await browser.close();
|
|
29504
31806
|
return 1;
|
|
29505
31807
|
}
|
|
29506
|
-
console.log(
|
|
31808
|
+
console.log(import_picocolors25.default.green(`
|
|
29507
31809
|
[ok] Session token captured successfully!`));
|
|
29508
31810
|
let accessToken;
|
|
29509
31811
|
try {
|
|
@@ -29524,12 +31826,12 @@ async function masterLogin(options) {
|
|
|
29524
31826
|
browser_name: "playwright-internal"
|
|
29525
31827
|
};
|
|
29526
31828
|
try {
|
|
29527
|
-
|
|
29528
|
-
|
|
29529
|
-
console.log(`${
|
|
31829
|
+
mkdirSync13(dirname12(configPath), { recursive: true });
|
|
31830
|
+
writeFileSync19(configPath, JSON.stringify(config2, null, 2), "utf-8");
|
|
31831
|
+
console.log(`${import_picocolors25.default.green("[ok]")} Saved YGKA config to ${import_picocolors25.default.white(configPath)}`);
|
|
29530
31832
|
return 0;
|
|
29531
31833
|
} catch (err) {
|
|
29532
|
-
console.error(
|
|
31834
|
+
console.error(import_picocolors25.default.red(`[error] Failed to write config: ${err instanceof Error ? err.message : String(err)}`));
|
|
29533
31835
|
return 1;
|
|
29534
31836
|
}
|
|
29535
31837
|
}
|