opencode-repos 0.3.0 → 0.3.2
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/index.ts +25 -11
- package/package.json +27 -27
- package/src/__tests__/git.test.ts +33 -2
- package/src/git.ts +32 -5
- package/.sisyphus/boulder.json +0 -8
- package/.sisyphus/notepads/opencode-repos/decisions.md +0 -15
- package/.sisyphus/notepads/opencode-repos/learnings.md +0 -384
- package/.sisyphus/plans/opencode-repos.md +0 -987
- package/.tmux-sessionizer +0 -8
- package/TODO.md +0 -3
- package/oh-my-opencode/.github/FUNDING.yml +0 -15
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/bug_report.yml +0 -129
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/feature_request.yml +0 -100
- package/oh-my-opencode/.github/ISSUE_TEMPLATE/general.yml +0 -83
- package/oh-my-opencode/.github/assets/google.jpg +0 -0
- package/oh-my-opencode/.github/assets/hero.jpg +0 -0
- package/oh-my-opencode/.github/assets/indent.jpg +0 -0
- package/oh-my-opencode/.github/assets/microsoft.jpg +0 -0
- package/oh-my-opencode/.github/assets/omo.png +0 -0
- package/oh-my-opencode/.github/assets/orchestrator-atlas.png +0 -0
- package/oh-my-opencode/.github/assets/sisyphus.png +0 -0
- package/oh-my-opencode/.github/assets/sisyphuslabs.png +0 -0
- package/oh-my-opencode/.github/pull_request_template.md +0 -34
- package/oh-my-opencode/.github/workflows/ci.yml +0 -138
- package/oh-my-opencode/.github/workflows/cla.yml +0 -41
- package/oh-my-opencode/.github/workflows/lint-workflows.yml +0 -22
- package/oh-my-opencode/.github/workflows/publish.yml +0 -165
- package/oh-my-opencode/.github/workflows/sisyphus-agent.yml +0 -500
- package/oh-my-opencode/.opencode/background-tasks.json +0 -27
- package/oh-my-opencode/.opencode/command/get-unpublished-changes.md +0 -84
- package/oh-my-opencode/.opencode/command/omomomo.md +0 -37
- package/oh-my-opencode/.opencode/command/publish.md +0 -257
- package/oh-my-opencode/AGENTS.md +0 -179
- package/oh-my-opencode/CLA.md +0 -58
- package/oh-my-opencode/CONTRIBUTING.md +0 -268
- package/oh-my-opencode/LICENSE.md +0 -82
- package/oh-my-opencode/README.ja.md +0 -370
- package/oh-my-opencode/README.md +0 -376
- package/oh-my-opencode/README.zh-cn.md +0 -380
- package/oh-my-opencode/assets/oh-my-opencode.schema.json +0 -2171
- package/oh-my-opencode/bin/oh-my-opencode.js +0 -80
- package/oh-my-opencode/bin/platform.js +0 -38
- package/oh-my-opencode/bin/platform.test.ts +0 -148
- package/oh-my-opencode/bun.lock +0 -314
- package/oh-my-opencode/bunfig.toml +0 -2
- package/oh-my-opencode/docs/category-skill-guide.md +0 -200
- package/oh-my-opencode/docs/cli-guide.md +0 -272
- package/oh-my-opencode/docs/configurations.md +0 -654
- package/oh-my-opencode/docs/features.md +0 -550
- package/oh-my-opencode/docs/guide/installation.md +0 -288
- package/oh-my-opencode/docs/guide/overview.md +0 -97
- package/oh-my-opencode/docs/guide/understanding-orchestration-system.md +0 -445
- package/oh-my-opencode/docs/orchestration-guide.md +0 -152
- package/oh-my-opencode/docs/ultrawork-manifesto.md +0 -197
- package/oh-my-opencode/package.json +0 -89
- package/oh-my-opencode/packages/darwin-arm64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/darwin-arm64/package.json +0 -22
- package/oh-my-opencode/packages/darwin-x64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/darwin-x64/package.json +0 -22
- package/oh-my-opencode/packages/linux-arm64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-arm64/package.json +0 -25
- package/oh-my-opencode/packages/linux-arm64-musl/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-arm64-musl/package.json +0 -25
- package/oh-my-opencode/packages/linux-x64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-x64/package.json +0 -25
- package/oh-my-opencode/packages/linux-x64-musl/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/linux-x64-musl/package.json +0 -25
- package/oh-my-opencode/packages/windows-x64/bin/.gitkeep +0 -0
- package/oh-my-opencode/packages/windows-x64/package.json +0 -22
- package/oh-my-opencode/postinstall.mjs +0 -43
- package/oh-my-opencode/script/build-binaries.ts +0 -103
- package/oh-my-opencode/script/build-schema.ts +0 -28
- package/oh-my-opencode/script/generate-changelog.ts +0 -92
- package/oh-my-opencode/script/publish.ts +0 -344
- package/oh-my-opencode/signatures/cla.json +0 -676
- package/oh-my-opencode/src/agents/AGENTS.md +0 -67
- package/oh-my-opencode/src/agents/atlas.ts +0 -1383
- package/oh-my-opencode/src/agents/dynamic-agent-prompt-builder.ts +0 -400
- package/oh-my-opencode/src/agents/explore.ts +0 -122
- package/oh-my-opencode/src/agents/index.ts +0 -13
- package/oh-my-opencode/src/agents/librarian.ts +0 -326
- package/oh-my-opencode/src/agents/metis.ts +0 -315
- package/oh-my-opencode/src/agents/momus.test.ts +0 -57
- package/oh-my-opencode/src/agents/momus.ts +0 -444
- package/oh-my-opencode/src/agents/multimodal-looker.ts +0 -56
- package/oh-my-opencode/src/agents/oracle.ts +0 -122
- package/oh-my-opencode/src/agents/prometheus-prompt.test.ts +0 -22
- package/oh-my-opencode/src/agents/prometheus-prompt.ts +0 -1196
- package/oh-my-opencode/src/agents/sisyphus-junior.test.ts +0 -232
- package/oh-my-opencode/src/agents/sisyphus-junior.ts +0 -134
- package/oh-my-opencode/src/agents/sisyphus.ts +0 -633
- package/oh-my-opencode/src/agents/types.ts +0 -80
- package/oh-my-opencode/src/agents/utils.test.ts +0 -311
- package/oh-my-opencode/src/agents/utils.ts +0 -240
- package/oh-my-opencode/src/cli/AGENTS.md +0 -91
- package/oh-my-opencode/src/cli/config-manager.test.ts +0 -364
- package/oh-my-opencode/src/cli/config-manager.ts +0 -641
- package/oh-my-opencode/src/cli/doctor/checks/auth.test.ts +0 -114
- package/oh-my-opencode/src/cli/doctor/checks/auth.ts +0 -115
- package/oh-my-opencode/src/cli/doctor/checks/config.test.ts +0 -103
- package/oh-my-opencode/src/cli/doctor/checks/config.ts +0 -123
- package/oh-my-opencode/src/cli/doctor/checks/dependencies.test.ts +0 -152
- package/oh-my-opencode/src/cli/doctor/checks/dependencies.ts +0 -163
- package/oh-my-opencode/src/cli/doctor/checks/gh.test.ts +0 -151
- package/oh-my-opencode/src/cli/doctor/checks/gh.ts +0 -171
- package/oh-my-opencode/src/cli/doctor/checks/index.ts +0 -34
- package/oh-my-opencode/src/cli/doctor/checks/lsp.test.ts +0 -134
- package/oh-my-opencode/src/cli/doctor/checks/lsp.ts +0 -77
- package/oh-my-opencode/src/cli/doctor/checks/mcp.test.ts +0 -115
- package/oh-my-opencode/src/cli/doctor/checks/mcp.ts +0 -128
- package/oh-my-opencode/src/cli/doctor/checks/opencode.test.ts +0 -227
- package/oh-my-opencode/src/cli/doctor/checks/opencode.ts +0 -178
- package/oh-my-opencode/src/cli/doctor/checks/plugin.test.ts +0 -109
- package/oh-my-opencode/src/cli/doctor/checks/plugin.ts +0 -124
- package/oh-my-opencode/src/cli/doctor/checks/version.test.ts +0 -148
- package/oh-my-opencode/src/cli/doctor/checks/version.ts +0 -135
- package/oh-my-opencode/src/cli/doctor/constants.ts +0 -72
- package/oh-my-opencode/src/cli/doctor/formatter.test.ts +0 -218
- package/oh-my-opencode/src/cli/doctor/formatter.ts +0 -140
- package/oh-my-opencode/src/cli/doctor/index.ts +0 -11
- package/oh-my-opencode/src/cli/doctor/runner.test.ts +0 -153
- package/oh-my-opencode/src/cli/doctor/runner.ts +0 -132
- package/oh-my-opencode/src/cli/doctor/types.ts +0 -113
- package/oh-my-opencode/src/cli/get-local-version/formatter.ts +0 -66
- package/oh-my-opencode/src/cli/get-local-version/index.ts +0 -106
- package/oh-my-opencode/src/cli/get-local-version/types.ts +0 -14
- package/oh-my-opencode/src/cli/index.ts +0 -153
- package/oh-my-opencode/src/cli/install.ts +0 -523
- package/oh-my-opencode/src/cli/model-fallback.ts +0 -246
- package/oh-my-opencode/src/cli/run/completion.test.ts +0 -170
- package/oh-my-opencode/src/cli/run/completion.ts +0 -79
- package/oh-my-opencode/src/cli/run/events.test.ts +0 -155
- package/oh-my-opencode/src/cli/run/events.ts +0 -325
- package/oh-my-opencode/src/cli/run/index.ts +0 -2
- package/oh-my-opencode/src/cli/run/runner.ts +0 -159
- package/oh-my-opencode/src/cli/run/types.ts +0 -76
- package/oh-my-opencode/src/cli/types.ts +0 -40
- package/oh-my-opencode/src/config/index.ts +0 -26
- package/oh-my-opencode/src/config/schema.test.ts +0 -444
- package/oh-my-opencode/src/config/schema.ts +0 -339
- package/oh-my-opencode/src/features/AGENTS.md +0 -77
- package/oh-my-opencode/src/features/background-agent/concurrency.test.ts +0 -418
- package/oh-my-opencode/src/features/background-agent/concurrency.ts +0 -137
- package/oh-my-opencode/src/features/background-agent/index.ts +0 -3
- package/oh-my-opencode/src/features/background-agent/manager.test.ts +0 -1928
- package/oh-my-opencode/src/features/background-agent/manager.ts +0 -1335
- package/oh-my-opencode/src/features/background-agent/types.ts +0 -66
- package/oh-my-opencode/src/features/boulder-state/constants.ts +0 -13
- package/oh-my-opencode/src/features/boulder-state/index.ts +0 -3
- package/oh-my-opencode/src/features/boulder-state/storage.test.ts +0 -250
- package/oh-my-opencode/src/features/boulder-state/storage.ts +0 -150
- package/oh-my-opencode/src/features/boulder-state/types.ts +0 -26
- package/oh-my-opencode/src/features/builtin-commands/commands.ts +0 -89
- package/oh-my-opencode/src/features/builtin-commands/index.ts +0 -2
- package/oh-my-opencode/src/features/builtin-commands/templates/init-deep.ts +0 -300
- package/oh-my-opencode/src/features/builtin-commands/templates/ralph-loop.ts +0 -38
- package/oh-my-opencode/src/features/builtin-commands/templates/refactor.ts +0 -619
- package/oh-my-opencode/src/features/builtin-commands/templates/start-work.ts +0 -72
- package/oh-my-opencode/src/features/builtin-commands/types.ts +0 -9
- package/oh-my-opencode/src/features/builtin-skills/frontend-ui-ux/SKILL.md +0 -78
- package/oh-my-opencode/src/features/builtin-skills/git-master/SKILL.md +0 -1105
- package/oh-my-opencode/src/features/builtin-skills/index.ts +0 -2
- package/oh-my-opencode/src/features/builtin-skills/skills.ts +0 -1203
- package/oh-my-opencode/src/features/builtin-skills/types.ts +0 -16
- package/oh-my-opencode/src/features/claude-code-agent-loader/index.ts +0 -2
- package/oh-my-opencode/src/features/claude-code-agent-loader/loader.ts +0 -90
- package/oh-my-opencode/src/features/claude-code-agent-loader/types.ts +0 -17
- package/oh-my-opencode/src/features/claude-code-command-loader/index.ts +0 -2
- package/oh-my-opencode/src/features/claude-code-command-loader/loader.ts +0 -144
- package/oh-my-opencode/src/features/claude-code-command-loader/types.ts +0 -46
- package/oh-my-opencode/src/features/claude-code-mcp-loader/env-expander.ts +0 -27
- package/oh-my-opencode/src/features/claude-code-mcp-loader/index.ts +0 -11
- package/oh-my-opencode/src/features/claude-code-mcp-loader/loader.test.ts +0 -162
- package/oh-my-opencode/src/features/claude-code-mcp-loader/loader.ts +0 -113
- package/oh-my-opencode/src/features/claude-code-mcp-loader/transformer.ts +0 -53
- package/oh-my-opencode/src/features/claude-code-mcp-loader/types.ts +0 -42
- package/oh-my-opencode/src/features/claude-code-plugin-loader/index.ts +0 -3
- package/oh-my-opencode/src/features/claude-code-plugin-loader/loader.ts +0 -486
- package/oh-my-opencode/src/features/claude-code-plugin-loader/types.ts +0 -210
- package/oh-my-opencode/src/features/claude-code-session-state/index.ts +0 -1
- package/oh-my-opencode/src/features/claude-code-session-state/state.test.ts +0 -126
- package/oh-my-opencode/src/features/claude-code-session-state/state.ts +0 -37
- package/oh-my-opencode/src/features/context-injector/collector.test.ts +0 -330
- package/oh-my-opencode/src/features/context-injector/collector.ts +0 -85
- package/oh-my-opencode/src/features/context-injector/index.ts +0 -14
- package/oh-my-opencode/src/features/context-injector/injector.test.ts +0 -122
- package/oh-my-opencode/src/features/context-injector/injector.ts +0 -167
- package/oh-my-opencode/src/features/context-injector/types.ts +0 -91
- package/oh-my-opencode/src/features/hook-message-injector/constants.ts +0 -6
- package/oh-my-opencode/src/features/hook-message-injector/index.ts +0 -4
- package/oh-my-opencode/src/features/hook-message-injector/injector.ts +0 -195
- package/oh-my-opencode/src/features/hook-message-injector/types.ts +0 -47
- package/oh-my-opencode/src/features/opencode-skill-loader/async-loader.test.ts +0 -448
- package/oh-my-opencode/src/features/opencode-skill-loader/async-loader.ts +0 -180
- package/oh-my-opencode/src/features/opencode-skill-loader/blocking.test.ts +0 -210
- package/oh-my-opencode/src/features/opencode-skill-loader/blocking.ts +0 -62
- package/oh-my-opencode/src/features/opencode-skill-loader/discover-worker.ts +0 -59
- package/oh-my-opencode/src/features/opencode-skill-loader/index.ts +0 -4
- package/oh-my-opencode/src/features/opencode-skill-loader/loader.test.ts +0 -273
- package/oh-my-opencode/src/features/opencode-skill-loader/loader.ts +0 -259
- package/oh-my-opencode/src/features/opencode-skill-loader/merger.ts +0 -267
- package/oh-my-opencode/src/features/opencode-skill-loader/skill-content.test.ts +0 -267
- package/oh-my-opencode/src/features/opencode-skill-loader/skill-content.ts +0 -206
- package/oh-my-opencode/src/features/opencode-skill-loader/types.ts +0 -38
- package/oh-my-opencode/src/features/skill-mcp-manager/env-cleaner.test.ts +0 -201
- package/oh-my-opencode/src/features/skill-mcp-manager/env-cleaner.ts +0 -27
- package/oh-my-opencode/src/features/skill-mcp-manager/index.ts +0 -2
- package/oh-my-opencode/src/features/skill-mcp-manager/manager.test.ts +0 -611
- package/oh-my-opencode/src/features/skill-mcp-manager/manager.ts +0 -520
- package/oh-my-opencode/src/features/skill-mcp-manager/types.ts +0 -14
- package/oh-my-opencode/src/features/task-toast-manager/index.ts +0 -2
- package/oh-my-opencode/src/features/task-toast-manager/manager.test.ts +0 -249
- package/oh-my-opencode/src/features/task-toast-manager/manager.ts +0 -215
- package/oh-my-opencode/src/features/task-toast-manager/types.ts +0 -24
- package/oh-my-opencode/src/hooks/AGENTS.md +0 -73
- package/oh-my-opencode/src/hooks/agent-usage-reminder/constants.ts +0 -54
- package/oh-my-opencode/src/hooks/agent-usage-reminder/index.ts +0 -109
- package/oh-my-opencode/src/hooks/agent-usage-reminder/storage.ts +0 -42
- package/oh-my-opencode/src/hooks/agent-usage-reminder/types.ts +0 -6
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/executor.test.ts +0 -307
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/executor.ts +0 -485
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/index.ts +0 -151
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/parser.ts +0 -201
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts +0 -33
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.ts +0 -184
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-types.ts +0 -44
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/storage.test.ts +0 -77
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/storage.ts +0 -250
- package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/types.ts +0 -42
- package/oh-my-opencode/src/hooks/atlas/index.test.ts +0 -953
- package/oh-my-opencode/src/hooks/atlas/index.ts +0 -771
- package/oh-my-opencode/src/hooks/auto-slash-command/constants.ts +0 -12
- package/oh-my-opencode/src/hooks/auto-slash-command/detector.test.ts +0 -296
- package/oh-my-opencode/src/hooks/auto-slash-command/detector.ts +0 -65
- package/oh-my-opencode/src/hooks/auto-slash-command/executor.ts +0 -205
- package/oh-my-opencode/src/hooks/auto-slash-command/index.test.ts +0 -254
- package/oh-my-opencode/src/hooks/auto-slash-command/index.ts +0 -89
- package/oh-my-opencode/src/hooks/auto-slash-command/types.ts +0 -23
- package/oh-my-opencode/src/hooks/auto-update-checker/cache.ts +0 -93
- package/oh-my-opencode/src/hooks/auto-update-checker/checker.test.ts +0 -24
- package/oh-my-opencode/src/hooks/auto-update-checker/checker.ts +0 -284
- package/oh-my-opencode/src/hooks/auto-update-checker/constants.ts +0 -64
- package/oh-my-opencode/src/hooks/auto-update-checker/index.test.ts +0 -254
- package/oh-my-opencode/src/hooks/auto-update-checker/index.ts +0 -260
- package/oh-my-opencode/src/hooks/auto-update-checker/types.ts +0 -29
- package/oh-my-opencode/src/hooks/background-compaction/index.ts +0 -87
- package/oh-my-opencode/src/hooks/background-notification/index.ts +0 -28
- package/oh-my-opencode/src/hooks/background-notification/types.ts +0 -5
- package/oh-my-opencode/src/hooks/claude-code-hooks/AGENTS.md +0 -70
- package/oh-my-opencode/src/hooks/claude-code-hooks/config-loader.ts +0 -107
- package/oh-my-opencode/src/hooks/claude-code-hooks/config.ts +0 -103
- package/oh-my-opencode/src/hooks/claude-code-hooks/index.ts +0 -401
- package/oh-my-opencode/src/hooks/claude-code-hooks/plugin-config.ts +0 -12
- package/oh-my-opencode/src/hooks/claude-code-hooks/post-tool-use.ts +0 -199
- package/oh-my-opencode/src/hooks/claude-code-hooks/pre-compact.ts +0 -109
- package/oh-my-opencode/src/hooks/claude-code-hooks/pre-tool-use.ts +0 -172
- package/oh-my-opencode/src/hooks/claude-code-hooks/stop.ts +0 -118
- package/oh-my-opencode/src/hooks/claude-code-hooks/todo.ts +0 -76
- package/oh-my-opencode/src/hooks/claude-code-hooks/tool-input-cache.ts +0 -47
- package/oh-my-opencode/src/hooks/claude-code-hooks/transcript.ts +0 -252
- package/oh-my-opencode/src/hooks/claude-code-hooks/types.ts +0 -204
- package/oh-my-opencode/src/hooks/claude-code-hooks/user-prompt-submit.ts +0 -117
- package/oh-my-opencode/src/hooks/comment-checker/cli.test.ts +0 -68
- package/oh-my-opencode/src/hooks/comment-checker/cli.ts +0 -221
- package/oh-my-opencode/src/hooks/comment-checker/downloader.ts +0 -196
- package/oh-my-opencode/src/hooks/comment-checker/index.ts +0 -171
- package/oh-my-opencode/src/hooks/comment-checker/types.ts +0 -33
- package/oh-my-opencode/src/hooks/compaction-context-injector/index.ts +0 -61
- package/oh-my-opencode/src/hooks/context-window-monitor.ts +0 -99
- package/oh-my-opencode/src/hooks/delegate-task-retry/index.test.ts +0 -119
- package/oh-my-opencode/src/hooks/delegate-task-retry/index.ts +0 -136
- package/oh-my-opencode/src/hooks/directory-agents-injector/constants.ts +0 -9
- package/oh-my-opencode/src/hooks/directory-agents-injector/index.ts +0 -182
- package/oh-my-opencode/src/hooks/directory-agents-injector/storage.ts +0 -48
- package/oh-my-opencode/src/hooks/directory-agents-injector/types.ts +0 -5
- package/oh-my-opencode/src/hooks/directory-readme-injector/constants.ts +0 -9
- package/oh-my-opencode/src/hooks/directory-readme-injector/index.ts +0 -177
- package/oh-my-opencode/src/hooks/directory-readme-injector/storage.ts +0 -48
- package/oh-my-opencode/src/hooks/directory-readme-injector/types.ts +0 -5
- package/oh-my-opencode/src/hooks/edit-error-recovery/index.test.ts +0 -126
- package/oh-my-opencode/src/hooks/edit-error-recovery/index.ts +0 -57
- package/oh-my-opencode/src/hooks/empty-task-response-detector.ts +0 -27
- package/oh-my-opencode/src/hooks/index.ts +0 -32
- package/oh-my-opencode/src/hooks/interactive-bash-session/constants.ts +0 -15
- package/oh-my-opencode/src/hooks/interactive-bash-session/index.ts +0 -262
- package/oh-my-opencode/src/hooks/interactive-bash-session/storage.ts +0 -59
- package/oh-my-opencode/src/hooks/interactive-bash-session/types.ts +0 -11
- package/oh-my-opencode/src/hooks/keyword-detector/constants.ts +0 -300
- package/oh-my-opencode/src/hooks/keyword-detector/detector.ts +0 -52
- package/oh-my-opencode/src/hooks/keyword-detector/index.test.ts +0 -529
- package/oh-my-opencode/src/hooks/keyword-detector/index.ts +0 -100
- package/oh-my-opencode/src/hooks/keyword-detector/types.ts +0 -4
- package/oh-my-opencode/src/hooks/non-interactive-env/constants.ts +0 -70
- package/oh-my-opencode/src/hooks/non-interactive-env/detector.ts +0 -19
- package/oh-my-opencode/src/hooks/non-interactive-env/index.test.ts +0 -323
- package/oh-my-opencode/src/hooks/non-interactive-env/index.ts +0 -63
- package/oh-my-opencode/src/hooks/non-interactive-env/types.ts +0 -3
- package/oh-my-opencode/src/hooks/prometheus-md-only/constants.ts +0 -32
- package/oh-my-opencode/src/hooks/prometheus-md-only/index.test.ts +0 -488
- package/oh-my-opencode/src/hooks/prometheus-md-only/index.ts +0 -136
- package/oh-my-opencode/src/hooks/ralph-loop/constants.ts +0 -5
- package/oh-my-opencode/src/hooks/ralph-loop/index.test.ts +0 -835
- package/oh-my-opencode/src/hooks/ralph-loop/index.ts +0 -417
- package/oh-my-opencode/src/hooks/ralph-loop/storage.ts +0 -115
- package/oh-my-opencode/src/hooks/ralph-loop/types.ts +0 -19
- package/oh-my-opencode/src/hooks/rules-injector/constants.ts +0 -30
- package/oh-my-opencode/src/hooks/rules-injector/finder.test.ts +0 -381
- package/oh-my-opencode/src/hooks/rules-injector/finder.ts +0 -263
- package/oh-my-opencode/src/hooks/rules-injector/index.ts +0 -223
- package/oh-my-opencode/src/hooks/rules-injector/matcher.ts +0 -63
- package/oh-my-opencode/src/hooks/rules-injector/parser.test.ts +0 -226
- package/oh-my-opencode/src/hooks/rules-injector/parser.ts +0 -211
- package/oh-my-opencode/src/hooks/rules-injector/storage.ts +0 -59
- package/oh-my-opencode/src/hooks/rules-injector/types.ts +0 -57
- package/oh-my-opencode/src/hooks/session-notification-utils.ts +0 -140
- package/oh-my-opencode/src/hooks/session-notification.test.ts +0 -361
- package/oh-my-opencode/src/hooks/session-notification.ts +0 -330
- package/oh-my-opencode/src/hooks/session-recovery/constants.ts +0 -10
- package/oh-my-opencode/src/hooks/session-recovery/index.test.ts +0 -223
- package/oh-my-opencode/src/hooks/session-recovery/index.ts +0 -435
- package/oh-my-opencode/src/hooks/session-recovery/storage.ts +0 -390
- package/oh-my-opencode/src/hooks/session-recovery/types.ts +0 -98
- package/oh-my-opencode/src/hooks/start-work/index.test.ts +0 -402
- package/oh-my-opencode/src/hooks/start-work/index.ts +0 -242
- package/oh-my-opencode/src/hooks/task-resume-info/index.ts +0 -36
- package/oh-my-opencode/src/hooks/think-mode/detector.ts +0 -57
- package/oh-my-opencode/src/hooks/think-mode/index.test.ts +0 -353
- package/oh-my-opencode/src/hooks/think-mode/index.ts +0 -89
- package/oh-my-opencode/src/hooks/think-mode/switcher.test.ts +0 -461
- package/oh-my-opencode/src/hooks/think-mode/switcher.ts +0 -222
- package/oh-my-opencode/src/hooks/think-mode/types.ts +0 -21
- package/oh-my-opencode/src/hooks/thinking-block-validator/index.ts +0 -171
- package/oh-my-opencode/src/hooks/todo-continuation-enforcer.test.ts +0 -876
- package/oh-my-opencode/src/hooks/todo-continuation-enforcer.ts +0 -480
- package/oh-my-opencode/src/hooks/tool-output-truncator.test.ts +0 -168
- package/oh-my-opencode/src/hooks/tool-output-truncator.ts +0 -61
- package/oh-my-opencode/src/index.ts +0 -589
- package/oh-my-opencode/src/mcp/AGENTS.md +0 -70
- package/oh-my-opencode/src/mcp/context7.ts +0 -6
- package/oh-my-opencode/src/mcp/grep-app.ts +0 -6
- package/oh-my-opencode/src/mcp/index.test.ts +0 -86
- package/oh-my-opencode/src/mcp/index.ts +0 -32
- package/oh-my-opencode/src/mcp/types.ts +0 -9
- package/oh-my-opencode/src/mcp/websearch.ts +0 -10
- package/oh-my-opencode/src/plugin-config.test.ts +0 -119
- package/oh-my-opencode/src/plugin-config.ts +0 -135
- package/oh-my-opencode/src/plugin-handlers/config-handler.test.ts +0 -103
- package/oh-my-opencode/src/plugin-handlers/config-handler.ts +0 -399
- package/oh-my-opencode/src/plugin-handlers/index.ts +0 -1
- package/oh-my-opencode/src/plugin-state.ts +0 -30
- package/oh-my-opencode/src/shared/AGENTS.md +0 -63
- package/oh-my-opencode/src/shared/agent-tool-restrictions.ts +0 -44
- package/oh-my-opencode/src/shared/agent-variant.test.ts +0 -83
- package/oh-my-opencode/src/shared/agent-variant.ts +0 -40
- package/oh-my-opencode/src/shared/claude-config-dir.test.ts +0 -60
- package/oh-my-opencode/src/shared/claude-config-dir.ts +0 -11
- package/oh-my-opencode/src/shared/command-executor.ts +0 -225
- package/oh-my-opencode/src/shared/config-errors.ts +0 -18
- package/oh-my-opencode/src/shared/config-path.ts +0 -47
- package/oh-my-opencode/src/shared/data-path.ts +0 -22
- package/oh-my-opencode/src/shared/deep-merge.test.ts +0 -336
- package/oh-my-opencode/src/shared/deep-merge.ts +0 -53
- package/oh-my-opencode/src/shared/dynamic-truncator.ts +0 -193
- package/oh-my-opencode/src/shared/external-plugin-detector.test.ts +0 -133
- package/oh-my-opencode/src/shared/external-plugin-detector.ts +0 -132
- package/oh-my-opencode/src/shared/file-reference-resolver.ts +0 -85
- package/oh-my-opencode/src/shared/file-utils.ts +0 -40
- package/oh-my-opencode/src/shared/first-message-variant.test.ts +0 -32
- package/oh-my-opencode/src/shared/first-message-variant.ts +0 -28
- package/oh-my-opencode/src/shared/frontmatter.test.ts +0 -262
- package/oh-my-opencode/src/shared/frontmatter.ts +0 -31
- package/oh-my-opencode/src/shared/hook-disabled.ts +0 -22
- package/oh-my-opencode/src/shared/index.ts +0 -29
- package/oh-my-opencode/src/shared/jsonc-parser.test.ts +0 -266
- package/oh-my-opencode/src/shared/jsonc-parser.ts +0 -66
- package/oh-my-opencode/src/shared/logger.ts +0 -20
- package/oh-my-opencode/src/shared/migration.test.ts +0 -602
- package/oh-my-opencode/src/shared/migration.ts +0 -191
- package/oh-my-opencode/src/shared/model-resolver.test.ts +0 -101
- package/oh-my-opencode/src/shared/model-resolver.ts +0 -35
- package/oh-my-opencode/src/shared/model-sanitizer.ts +0 -12
- package/oh-my-opencode/src/shared/opencode-config-dir.test.ts +0 -318
- package/oh-my-opencode/src/shared/opencode-config-dir.ts +0 -142
- package/oh-my-opencode/src/shared/opencode-version.test.ts +0 -223
- package/oh-my-opencode/src/shared/opencode-version.ts +0 -72
- package/oh-my-opencode/src/shared/pattern-matcher.ts +0 -29
- package/oh-my-opencode/src/shared/permission-compat.test.ts +0 -134
- package/oh-my-opencode/src/shared/permission-compat.ts +0 -77
- package/oh-my-opencode/src/shared/session-cursor.test.ts +0 -66
- package/oh-my-opencode/src/shared/session-cursor.ts +0 -85
- package/oh-my-opencode/src/shared/shell-env.test.ts +0 -278
- package/oh-my-opencode/src/shared/shell-env.ts +0 -111
- package/oh-my-opencode/src/shared/snake-case.ts +0 -49
- package/oh-my-opencode/src/shared/system-directive.ts +0 -40
- package/oh-my-opencode/src/shared/tool-name.ts +0 -26
- package/oh-my-opencode/src/shared/zip-extractor.ts +0 -83
- package/oh-my-opencode/src/tools/AGENTS.md +0 -74
- package/oh-my-opencode/src/tools/ast-grep/cli.ts +0 -230
- package/oh-my-opencode/src/tools/ast-grep/constants.ts +0 -261
- package/oh-my-opencode/src/tools/ast-grep/downloader.ts +0 -128
- package/oh-my-opencode/src/tools/ast-grep/index.ts +0 -13
- package/oh-my-opencode/src/tools/ast-grep/tools.ts +0 -112
- package/oh-my-opencode/src/tools/ast-grep/types.ts +0 -61
- package/oh-my-opencode/src/tools/ast-grep/utils.ts +0 -102
- package/oh-my-opencode/src/tools/background-task/constants.ts +0 -7
- package/oh-my-opencode/src/tools/background-task/index.ts +0 -7
- package/oh-my-opencode/src/tools/background-task/tools.ts +0 -479
- package/oh-my-opencode/src/tools/background-task/types.ts +0 -16
- package/oh-my-opencode/src/tools/call-omo-agent/constants.ts +0 -7
- package/oh-my-opencode/src/tools/call-omo-agent/index.ts +0 -3
- package/oh-my-opencode/src/tools/call-omo-agent/tools.ts +0 -338
- package/oh-my-opencode/src/tools/call-omo-agent/types.ts +0 -27
- package/oh-my-opencode/src/tools/delegate-task/constants.ts +0 -205
- package/oh-my-opencode/src/tools/delegate-task/index.ts +0 -3
- package/oh-my-opencode/src/tools/delegate-task/tools.test.ts +0 -1575
- package/oh-my-opencode/src/tools/delegate-task/tools.ts +0 -885
- package/oh-my-opencode/src/tools/delegate-task/types.ts +0 -9
- package/oh-my-opencode/src/tools/glob/cli.test.ts +0 -158
- package/oh-my-opencode/src/tools/glob/cli.ts +0 -191
- package/oh-my-opencode/src/tools/glob/constants.ts +0 -12
- package/oh-my-opencode/src/tools/glob/index.ts +0 -3
- package/oh-my-opencode/src/tools/glob/tools.ts +0 -41
- package/oh-my-opencode/src/tools/glob/types.ts +0 -22
- package/oh-my-opencode/src/tools/glob/utils.ts +0 -26
- package/oh-my-opencode/src/tools/grep/cli.ts +0 -229
- package/oh-my-opencode/src/tools/grep/constants.ts +0 -127
- package/oh-my-opencode/src/tools/grep/downloader.test.ts +0 -103
- package/oh-my-opencode/src/tools/grep/downloader.ts +0 -145
- package/oh-my-opencode/src/tools/grep/index.ts +0 -3
- package/oh-my-opencode/src/tools/grep/tools.ts +0 -40
- package/oh-my-opencode/src/tools/grep/types.ts +0 -39
- package/oh-my-opencode/src/tools/grep/utils.ts +0 -53
- package/oh-my-opencode/src/tools/index.ts +0 -72
- package/oh-my-opencode/src/tools/interactive-bash/constants.ts +0 -18
- package/oh-my-opencode/src/tools/interactive-bash/index.ts +0 -4
- package/oh-my-opencode/src/tools/interactive-bash/tools.ts +0 -126
- package/oh-my-opencode/src/tools/interactive-bash/utils.ts +0 -71
- package/oh-my-opencode/src/tools/look-at/constants.ts +0 -3
- package/oh-my-opencode/src/tools/look-at/index.ts +0 -3
- package/oh-my-opencode/src/tools/look-at/tools.test.ts +0 -73
- package/oh-my-opencode/src/tools/look-at/tools.ts +0 -173
- package/oh-my-opencode/src/tools/look-at/types.ts +0 -4
- package/oh-my-opencode/src/tools/lsp/client.ts +0 -596
- package/oh-my-opencode/src/tools/lsp/config.test.ts +0 -130
- package/oh-my-opencode/src/tools/lsp/config.ts +0 -285
- package/oh-my-opencode/src/tools/lsp/constants.ts +0 -390
- package/oh-my-opencode/src/tools/lsp/index.ts +0 -7
- package/oh-my-opencode/src/tools/lsp/tools.ts +0 -261
- package/oh-my-opencode/src/tools/lsp/types.ts +0 -124
- package/oh-my-opencode/src/tools/lsp/utils.ts +0 -406
- package/oh-my-opencode/src/tools/session-manager/constants.ts +0 -97
- package/oh-my-opencode/src/tools/session-manager/index.ts +0 -3
- package/oh-my-opencode/src/tools/session-manager/storage.test.ts +0 -315
- package/oh-my-opencode/src/tools/session-manager/storage.ts +0 -238
- package/oh-my-opencode/src/tools/session-manager/tools.test.ts +0 -124
- package/oh-my-opencode/src/tools/session-manager/tools.ts +0 -146
- package/oh-my-opencode/src/tools/session-manager/types.ts +0 -99
- package/oh-my-opencode/src/tools/session-manager/utils.test.ts +0 -160
- package/oh-my-opencode/src/tools/session-manager/utils.ts +0 -199
- package/oh-my-opencode/src/tools/skill/constants.ts +0 -8
- package/oh-my-opencode/src/tools/skill/index.ts +0 -3
- package/oh-my-opencode/src/tools/skill/tools.test.ts +0 -239
- package/oh-my-opencode/src/tools/skill/tools.ts +0 -200
- package/oh-my-opencode/src/tools/skill/types.ts +0 -31
- package/oh-my-opencode/src/tools/skill-mcp/constants.ts +0 -3
- package/oh-my-opencode/src/tools/skill-mcp/index.ts +0 -3
- package/oh-my-opencode/src/tools/skill-mcp/tools.test.ts +0 -215
- package/oh-my-opencode/src/tools/skill-mcp/tools.ts +0 -172
- package/oh-my-opencode/src/tools/skill-mcp/types.ts +0 -8
- package/oh-my-opencode/src/tools/slashcommand/index.ts +0 -2
- package/oh-my-opencode/src/tools/slashcommand/tools.ts +0 -252
- package/oh-my-opencode/src/tools/slashcommand/types.ts +0 -28
- package/oh-my-opencode/test-setup.ts +0 -6
- package/oh-my-opencode/tsconfig.json +0 -20
|
@@ -1,876 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
|
2
|
-
|
|
3
|
-
import type { BackgroundManager } from "../features/background-agent"
|
|
4
|
-
import { setMainSession, subagentSessions, _resetForTesting } from "../features/claude-code-session-state"
|
|
5
|
-
import { createTodoContinuationEnforcer } from "./todo-continuation-enforcer"
|
|
6
|
-
|
|
7
|
-
describe("todo-continuation-enforcer", () => {
|
|
8
|
-
let promptCalls: Array<{ sessionID: string; agent?: string; model?: { providerID?: string; modelID?: string }; text: string }>
|
|
9
|
-
let toastCalls: Array<{ title: string; message: string }>
|
|
10
|
-
|
|
11
|
-
interface MockMessage {
|
|
12
|
-
info: {
|
|
13
|
-
id: string
|
|
14
|
-
role: "user" | "assistant"
|
|
15
|
-
error?: { name: string; data?: { message: string } }
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let mockMessages: MockMessage[] = []
|
|
20
|
-
|
|
21
|
-
function createMockPluginInput() {
|
|
22
|
-
return {
|
|
23
|
-
client: {
|
|
24
|
-
session: {
|
|
25
|
-
todo: async () => ({ data: [
|
|
26
|
-
{ id: "1", content: "Task 1", status: "pending", priority: "high" },
|
|
27
|
-
{ id: "2", content: "Task 2", status: "completed", priority: "medium" },
|
|
28
|
-
]}),
|
|
29
|
-
messages: async () => ({ data: mockMessages }),
|
|
30
|
-
prompt: async (opts: any) => {
|
|
31
|
-
promptCalls.push({
|
|
32
|
-
sessionID: opts.path.id,
|
|
33
|
-
agent: opts.body.agent,
|
|
34
|
-
model: opts.body.model,
|
|
35
|
-
text: opts.body.parts[0].text,
|
|
36
|
-
})
|
|
37
|
-
return {}
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
tui: {
|
|
41
|
-
showToast: async (opts: any) => {
|
|
42
|
-
toastCalls.push({
|
|
43
|
-
title: opts.body.title,
|
|
44
|
-
message: opts.body.message,
|
|
45
|
-
})
|
|
46
|
-
return {}
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
directory: "/tmp/test",
|
|
51
|
-
} as any
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function createMockBackgroundManager(runningTasks: boolean = false): BackgroundManager {
|
|
55
|
-
return {
|
|
56
|
-
getTasksByParentSession: () => runningTasks
|
|
57
|
-
? [{ status: "running" }]
|
|
58
|
-
: [],
|
|
59
|
-
} as any
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
_resetForTesting()
|
|
64
|
-
promptCalls = []
|
|
65
|
-
toastCalls = []
|
|
66
|
-
mockMessages = []
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
afterEach(() => {
|
|
70
|
-
_resetForTesting()
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test("should inject continuation when idle with incomplete todos", async () => {
|
|
74
|
-
// #given - main session with incomplete todos
|
|
75
|
-
const sessionID = "main-123"
|
|
76
|
-
setMainSession(sessionID)
|
|
77
|
-
|
|
78
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {
|
|
79
|
-
backgroundManager: createMockBackgroundManager(false),
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
// #when - session goes idle
|
|
83
|
-
await hook.handler({
|
|
84
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
// #then - countdown toast shown
|
|
88
|
-
await new Promise(r => setTimeout(r, 100))
|
|
89
|
-
expect(toastCalls.length).toBeGreaterThanOrEqual(1)
|
|
90
|
-
expect(toastCalls[0].title).toBe("Todo Continuation")
|
|
91
|
-
|
|
92
|
-
// #then - after countdown, continuation injected
|
|
93
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
94
|
-
expect(promptCalls.length).toBe(1)
|
|
95
|
-
expect(promptCalls[0].text).toContain("TODO CONTINUATION")
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
test("should not inject when all todos are complete", async () => {
|
|
99
|
-
// #given - session with all todos complete
|
|
100
|
-
const sessionID = "main-456"
|
|
101
|
-
setMainSession(sessionID)
|
|
102
|
-
|
|
103
|
-
const mockInput = createMockPluginInput()
|
|
104
|
-
mockInput.client.session.todo = async () => ({ data: [
|
|
105
|
-
{ id: "1", content: "Task 1", status: "completed", priority: "high" },
|
|
106
|
-
]})
|
|
107
|
-
|
|
108
|
-
const hook = createTodoContinuationEnforcer(mockInput, {})
|
|
109
|
-
|
|
110
|
-
// #when - session goes idle
|
|
111
|
-
await hook.handler({
|
|
112
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
116
|
-
|
|
117
|
-
// #then - no continuation injected
|
|
118
|
-
expect(promptCalls).toHaveLength(0)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
test("should not inject when background tasks are running", async () => {
|
|
122
|
-
// #given - session with running background tasks
|
|
123
|
-
const sessionID = "main-789"
|
|
124
|
-
setMainSession(sessionID)
|
|
125
|
-
|
|
126
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {
|
|
127
|
-
backgroundManager: createMockBackgroundManager(true),
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
// #when - session goes idle
|
|
131
|
-
await hook.handler({
|
|
132
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
136
|
-
|
|
137
|
-
// #then - no continuation injected
|
|
138
|
-
expect(promptCalls).toHaveLength(0)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
test("should not inject for non-main session", async () => {
|
|
142
|
-
// #given - main session set, different session goes idle
|
|
143
|
-
setMainSession("main-session")
|
|
144
|
-
const otherSession = "other-session"
|
|
145
|
-
|
|
146
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
147
|
-
|
|
148
|
-
// #when - non-main session goes idle
|
|
149
|
-
await hook.handler({
|
|
150
|
-
event: { type: "session.idle", properties: { sessionID: otherSession } },
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
154
|
-
|
|
155
|
-
// #then - no continuation injected
|
|
156
|
-
expect(promptCalls).toHaveLength(0)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test("should inject for background task session (subagent)", async () => {
|
|
160
|
-
// #given - main session set, background task session registered
|
|
161
|
-
setMainSession("main-session")
|
|
162
|
-
const bgTaskSession = "bg-task-session"
|
|
163
|
-
subagentSessions.add(bgTaskSession)
|
|
164
|
-
|
|
165
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
166
|
-
|
|
167
|
-
// #when - background task session goes idle
|
|
168
|
-
await hook.handler({
|
|
169
|
-
event: { type: "session.idle", properties: { sessionID: bgTaskSession } },
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
// #then - continuation injected for background task session
|
|
173
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
174
|
-
expect(promptCalls.length).toBe(1)
|
|
175
|
-
expect(promptCalls[0].sessionID).toBe(bgTaskSession)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
test("should cancel countdown on user message after grace period", async () => {
|
|
181
|
-
// #given - session starting countdown
|
|
182
|
-
const sessionID = "main-cancel"
|
|
183
|
-
setMainSession(sessionID)
|
|
184
|
-
|
|
185
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
186
|
-
|
|
187
|
-
// #when - session goes idle
|
|
188
|
-
await hook.handler({
|
|
189
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// #when - wait past grace period (500ms), then user sends message
|
|
193
|
-
await new Promise(r => setTimeout(r, 600))
|
|
194
|
-
await hook.handler({
|
|
195
|
-
event: {
|
|
196
|
-
type: "message.updated",
|
|
197
|
-
properties: { info: { sessionID, role: "user" } }
|
|
198
|
-
},
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
// #then - wait past countdown time and verify no injection (countdown was cancelled)
|
|
202
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
203
|
-
expect(promptCalls).toHaveLength(0)
|
|
204
|
-
})
|
|
205
|
-
|
|
206
|
-
test("should ignore user message within grace period", async () => {
|
|
207
|
-
// #given - session starting countdown
|
|
208
|
-
const sessionID = "main-grace"
|
|
209
|
-
setMainSession(sessionID)
|
|
210
|
-
|
|
211
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
212
|
-
|
|
213
|
-
// #when - session goes idle
|
|
214
|
-
await hook.handler({
|
|
215
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
// #when - user message arrives within grace period (immediately)
|
|
219
|
-
await hook.handler({
|
|
220
|
-
event: {
|
|
221
|
-
type: "message.updated",
|
|
222
|
-
properties: { info: { sessionID, role: "user" } }
|
|
223
|
-
},
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
// #then - countdown should continue (message was ignored)
|
|
227
|
-
// wait past 2s countdown and verify injection happens
|
|
228
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
229
|
-
expect(promptCalls).toHaveLength(1)
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
test("should cancel countdown on assistant activity", async () => {
|
|
233
|
-
// #given - session starting countdown
|
|
234
|
-
const sessionID = "main-assistant"
|
|
235
|
-
setMainSession(sessionID)
|
|
236
|
-
|
|
237
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
238
|
-
|
|
239
|
-
// #when - session goes idle
|
|
240
|
-
await hook.handler({
|
|
241
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
// #when - assistant starts responding
|
|
245
|
-
await new Promise(r => setTimeout(r, 500))
|
|
246
|
-
await hook.handler({
|
|
247
|
-
event: {
|
|
248
|
-
type: "message.part.updated",
|
|
249
|
-
properties: { info: { sessionID, role: "assistant" } }
|
|
250
|
-
},
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
254
|
-
|
|
255
|
-
// #then - no continuation injected (cancelled)
|
|
256
|
-
expect(promptCalls).toHaveLength(0)
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
test("should cancel countdown on tool execution", async () => {
|
|
260
|
-
// #given - session starting countdown
|
|
261
|
-
const sessionID = "main-tool"
|
|
262
|
-
setMainSession(sessionID)
|
|
263
|
-
|
|
264
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
265
|
-
|
|
266
|
-
// #when - session goes idle
|
|
267
|
-
await hook.handler({
|
|
268
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
// #when - tool starts executing
|
|
272
|
-
await new Promise(r => setTimeout(r, 500))
|
|
273
|
-
await hook.handler({
|
|
274
|
-
event: { type: "tool.execute.before", properties: { sessionID } },
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
278
|
-
|
|
279
|
-
// #then - no continuation injected (cancelled)
|
|
280
|
-
expect(promptCalls).toHaveLength(0)
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
test("should skip injection during recovery mode", async () => {
|
|
284
|
-
// #given - session in recovery mode
|
|
285
|
-
const sessionID = "main-recovery"
|
|
286
|
-
setMainSession(sessionID)
|
|
287
|
-
|
|
288
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
289
|
-
|
|
290
|
-
// #when - mark as recovering
|
|
291
|
-
hook.markRecovering(sessionID)
|
|
292
|
-
|
|
293
|
-
// #when - session goes idle
|
|
294
|
-
await hook.handler({
|
|
295
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
299
|
-
|
|
300
|
-
// #then - no continuation injected
|
|
301
|
-
expect(promptCalls).toHaveLength(0)
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
test("should inject after recovery complete", async () => {
|
|
305
|
-
// #given - session was in recovery, now complete
|
|
306
|
-
const sessionID = "main-recovery-done"
|
|
307
|
-
setMainSession(sessionID)
|
|
308
|
-
|
|
309
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
310
|
-
|
|
311
|
-
// #when - mark as recovering then complete
|
|
312
|
-
hook.markRecovering(sessionID)
|
|
313
|
-
hook.markRecoveryComplete(sessionID)
|
|
314
|
-
|
|
315
|
-
// #when - session goes idle
|
|
316
|
-
await hook.handler({
|
|
317
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
321
|
-
|
|
322
|
-
// #then - continuation injected
|
|
323
|
-
expect(promptCalls.length).toBe(1)
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
test("should cleanup on session deleted", async () => {
|
|
327
|
-
// #given - session starting countdown
|
|
328
|
-
const sessionID = "main-delete"
|
|
329
|
-
setMainSession(sessionID)
|
|
330
|
-
|
|
331
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
332
|
-
|
|
333
|
-
// #when - session goes idle
|
|
334
|
-
await hook.handler({
|
|
335
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
// #when - session is deleted during countdown
|
|
339
|
-
await new Promise(r => setTimeout(r, 500))
|
|
340
|
-
await hook.handler({
|
|
341
|
-
event: { type: "session.deleted", properties: { info: { id: sessionID } } },
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
345
|
-
|
|
346
|
-
// #then - no continuation injected (cleaned up)
|
|
347
|
-
expect(promptCalls).toHaveLength(0)
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
test("should accept skipAgents option without error", async () => {
|
|
351
|
-
// #given - session with skipAgents configured for Prometheus
|
|
352
|
-
const sessionID = "main-prometheus-option"
|
|
353
|
-
setMainSession(sessionID)
|
|
354
|
-
|
|
355
|
-
// #when - create hook with skipAgents option (should not throw)
|
|
356
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {
|
|
357
|
-
skipAgents: ["Prometheus (Planner)", "custom-agent"],
|
|
358
|
-
})
|
|
359
|
-
|
|
360
|
-
// #then - handler works without error
|
|
361
|
-
await hook.handler({
|
|
362
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
363
|
-
})
|
|
364
|
-
|
|
365
|
-
await new Promise(r => setTimeout(r, 100))
|
|
366
|
-
expect(toastCalls.length).toBeGreaterThanOrEqual(1)
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
test("should show countdown toast updates", async () => {
|
|
370
|
-
// #given - session with incomplete todos
|
|
371
|
-
const sessionID = "main-toast"
|
|
372
|
-
setMainSession(sessionID)
|
|
373
|
-
|
|
374
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
375
|
-
|
|
376
|
-
// #when - session goes idle
|
|
377
|
-
await hook.handler({
|
|
378
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
// #then - multiple toast updates during countdown (2s countdown = 2 toasts: "2s" and "1s")
|
|
382
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
383
|
-
expect(toastCalls.length).toBeGreaterThanOrEqual(2)
|
|
384
|
-
expect(toastCalls[0].message).toContain("2s")
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
test("should not have 10s throttle between injections", async () => {
|
|
388
|
-
// #given - new hook instance (no prior state)
|
|
389
|
-
const sessionID = "main-no-throttle"
|
|
390
|
-
setMainSession(sessionID)
|
|
391
|
-
|
|
392
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
393
|
-
|
|
394
|
-
// #when - first idle cycle completes
|
|
395
|
-
await hook.handler({
|
|
396
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
397
|
-
})
|
|
398
|
-
await new Promise(r => setTimeout(r, 3500))
|
|
399
|
-
|
|
400
|
-
// #then - first injection happened
|
|
401
|
-
expect(promptCalls.length).toBe(1)
|
|
402
|
-
|
|
403
|
-
// #when - immediately trigger second idle (no 10s wait needed)
|
|
404
|
-
await hook.handler({
|
|
405
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
406
|
-
})
|
|
407
|
-
await new Promise(r => setTimeout(r, 3500))
|
|
408
|
-
|
|
409
|
-
// #then - second injection also happened (no throttle blocking)
|
|
410
|
-
expect(promptCalls.length).toBe(2)
|
|
411
|
-
}, { timeout: 15000 })
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
test("should NOT skip for non-abort errors even if immediately before idle", async () => {
|
|
420
|
-
// #given - session with incomplete todos
|
|
421
|
-
const sessionID = "main-noabort-error"
|
|
422
|
-
setMainSession(sessionID)
|
|
423
|
-
|
|
424
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
425
|
-
|
|
426
|
-
// #when - non-abort error occurs (e.g., network error, API error)
|
|
427
|
-
await hook.handler({
|
|
428
|
-
event: {
|
|
429
|
-
type: "session.error",
|
|
430
|
-
properties: {
|
|
431
|
-
sessionID,
|
|
432
|
-
error: { name: "NetworkError", message: "Connection failed" }
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
})
|
|
436
|
-
|
|
437
|
-
// #when - session goes idle immediately after
|
|
438
|
-
await hook.handler({
|
|
439
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
443
|
-
|
|
444
|
-
// #then - continuation injected (non-abort errors don't block)
|
|
445
|
-
expect(promptCalls.length).toBe(1)
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
// ============================================================
|
|
453
|
-
// API-BASED ABORT DETECTION TESTS
|
|
454
|
-
// These tests verify that abort is detected by checking
|
|
455
|
-
// the last assistant message's error field via session.messages API
|
|
456
|
-
// ============================================================
|
|
457
|
-
|
|
458
|
-
test("should skip injection when last assistant message has MessageAbortedError", async () => {
|
|
459
|
-
// #given - session where last assistant message was aborted
|
|
460
|
-
const sessionID = "main-api-abort"
|
|
461
|
-
setMainSession(sessionID)
|
|
462
|
-
|
|
463
|
-
mockMessages = [
|
|
464
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
465
|
-
{ info: { id: "msg-2", role: "assistant", error: { name: "MessageAbortedError", data: { message: "The operation was aborted" } } } },
|
|
466
|
-
]
|
|
467
|
-
|
|
468
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
469
|
-
|
|
470
|
-
// #when - session goes idle
|
|
471
|
-
await hook.handler({
|
|
472
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
476
|
-
|
|
477
|
-
// #then - no continuation (last message was aborted)
|
|
478
|
-
expect(promptCalls).toHaveLength(0)
|
|
479
|
-
})
|
|
480
|
-
|
|
481
|
-
test("should inject when last assistant message has no error", async () => {
|
|
482
|
-
// #given - session where last assistant message completed normally
|
|
483
|
-
const sessionID = "main-api-no-error"
|
|
484
|
-
setMainSession(sessionID)
|
|
485
|
-
|
|
486
|
-
mockMessages = [
|
|
487
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
488
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
489
|
-
]
|
|
490
|
-
|
|
491
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
492
|
-
|
|
493
|
-
// #when - session goes idle
|
|
494
|
-
await hook.handler({
|
|
495
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
499
|
-
|
|
500
|
-
// #then - continuation injected (no abort)
|
|
501
|
-
expect(promptCalls.length).toBe(1)
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
test("should inject when last message is from user (not assistant)", async () => {
|
|
505
|
-
// #given - session where last message is from user
|
|
506
|
-
const sessionID = "main-api-user-last"
|
|
507
|
-
setMainSession(sessionID)
|
|
508
|
-
|
|
509
|
-
mockMessages = [
|
|
510
|
-
{ info: { id: "msg-1", role: "assistant" } },
|
|
511
|
-
{ info: { id: "msg-2", role: "user" } },
|
|
512
|
-
]
|
|
513
|
-
|
|
514
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
515
|
-
|
|
516
|
-
// #when - session goes idle
|
|
517
|
-
await hook.handler({
|
|
518
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
522
|
-
|
|
523
|
-
// #then - continuation injected (last message is user, not aborted assistant)
|
|
524
|
-
expect(promptCalls.length).toBe(1)
|
|
525
|
-
})
|
|
526
|
-
|
|
527
|
-
test("should skip when last assistant message has any abort-like error", async () => {
|
|
528
|
-
// #given - session where last assistant message has AbortError (DOMException style)
|
|
529
|
-
const sessionID = "main-api-abort-dom"
|
|
530
|
-
setMainSession(sessionID)
|
|
531
|
-
|
|
532
|
-
mockMessages = [
|
|
533
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
534
|
-
{ info: { id: "msg-2", role: "assistant", error: { name: "AbortError" } } },
|
|
535
|
-
]
|
|
536
|
-
|
|
537
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
538
|
-
|
|
539
|
-
// #when - session goes idle
|
|
540
|
-
await hook.handler({
|
|
541
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
542
|
-
})
|
|
543
|
-
|
|
544
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
545
|
-
|
|
546
|
-
// #then - no continuation (abort error detected)
|
|
547
|
-
expect(promptCalls).toHaveLength(0)
|
|
548
|
-
})
|
|
549
|
-
|
|
550
|
-
test("should skip injection when abort detected via session.error event (event-based, primary)", async () => {
|
|
551
|
-
// #given - session with incomplete todos
|
|
552
|
-
const sessionID = "main-event-abort"
|
|
553
|
-
setMainSession(sessionID)
|
|
554
|
-
mockMessages = [
|
|
555
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
556
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
557
|
-
]
|
|
558
|
-
|
|
559
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
560
|
-
|
|
561
|
-
// #when - abort error event fires
|
|
562
|
-
await hook.handler({
|
|
563
|
-
event: {
|
|
564
|
-
type: "session.error",
|
|
565
|
-
properties: { sessionID, error: { name: "MessageAbortedError" } },
|
|
566
|
-
},
|
|
567
|
-
})
|
|
568
|
-
|
|
569
|
-
// #when - session goes idle immediately after
|
|
570
|
-
await hook.handler({
|
|
571
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
572
|
-
})
|
|
573
|
-
|
|
574
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
575
|
-
|
|
576
|
-
// #then - no continuation (abort detected via event)
|
|
577
|
-
expect(promptCalls).toHaveLength(0)
|
|
578
|
-
})
|
|
579
|
-
|
|
580
|
-
test("should skip injection when AbortError detected via session.error event", async () => {
|
|
581
|
-
// #given - session with incomplete todos
|
|
582
|
-
const sessionID = "main-event-abort-dom"
|
|
583
|
-
setMainSession(sessionID)
|
|
584
|
-
mockMessages = [
|
|
585
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
586
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
587
|
-
]
|
|
588
|
-
|
|
589
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
590
|
-
|
|
591
|
-
// #when - AbortError event fires
|
|
592
|
-
await hook.handler({
|
|
593
|
-
event: {
|
|
594
|
-
type: "session.error",
|
|
595
|
-
properties: { sessionID, error: { name: "AbortError" } },
|
|
596
|
-
},
|
|
597
|
-
})
|
|
598
|
-
|
|
599
|
-
// #when - session goes idle
|
|
600
|
-
await hook.handler({
|
|
601
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
605
|
-
|
|
606
|
-
// #then - no continuation (abort detected via event)
|
|
607
|
-
expect(promptCalls).toHaveLength(0)
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
test("should inject when abort flag is stale (>3s old)", async () => {
|
|
611
|
-
// #given - session with incomplete todos and old abort timestamp
|
|
612
|
-
const sessionID = "main-stale-abort"
|
|
613
|
-
setMainSession(sessionID)
|
|
614
|
-
mockMessages = [
|
|
615
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
616
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
617
|
-
]
|
|
618
|
-
|
|
619
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
620
|
-
|
|
621
|
-
// #when - abort error fires
|
|
622
|
-
await hook.handler({
|
|
623
|
-
event: {
|
|
624
|
-
type: "session.error",
|
|
625
|
-
properties: { sessionID, error: { name: "MessageAbortedError" } },
|
|
626
|
-
},
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
// #when - wait >3s then idle fires
|
|
630
|
-
await new Promise(r => setTimeout(r, 3100))
|
|
631
|
-
|
|
632
|
-
await hook.handler({
|
|
633
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
634
|
-
})
|
|
635
|
-
|
|
636
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
637
|
-
|
|
638
|
-
// #then - continuation injected (abort flag is stale)
|
|
639
|
-
expect(promptCalls.length).toBeGreaterThan(0)
|
|
640
|
-
}, 10000)
|
|
641
|
-
|
|
642
|
-
test("should clear abort flag on user message activity", async () => {
|
|
643
|
-
// #given - session with abort detected
|
|
644
|
-
const sessionID = "main-clear-on-user"
|
|
645
|
-
setMainSession(sessionID)
|
|
646
|
-
mockMessages = [
|
|
647
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
648
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
649
|
-
]
|
|
650
|
-
|
|
651
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
652
|
-
|
|
653
|
-
// #when - abort error fires
|
|
654
|
-
await hook.handler({
|
|
655
|
-
event: {
|
|
656
|
-
type: "session.error",
|
|
657
|
-
properties: { sessionID, error: { name: "MessageAbortedError" } },
|
|
658
|
-
},
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
// #when - user sends new message (clears abort flag)
|
|
662
|
-
await new Promise(r => setTimeout(r, 600))
|
|
663
|
-
await hook.handler({
|
|
664
|
-
event: {
|
|
665
|
-
type: "message.updated",
|
|
666
|
-
properties: { info: { sessionID, role: "user" } },
|
|
667
|
-
},
|
|
668
|
-
})
|
|
669
|
-
|
|
670
|
-
// #when - session goes idle
|
|
671
|
-
await hook.handler({
|
|
672
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
673
|
-
})
|
|
674
|
-
|
|
675
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
676
|
-
|
|
677
|
-
// #then - continuation injected (abort flag was cleared by user activity)
|
|
678
|
-
expect(promptCalls.length).toBeGreaterThan(0)
|
|
679
|
-
})
|
|
680
|
-
|
|
681
|
-
test("should clear abort flag on assistant message activity", async () => {
|
|
682
|
-
// #given - session with abort detected
|
|
683
|
-
const sessionID = "main-clear-on-assistant"
|
|
684
|
-
setMainSession(sessionID)
|
|
685
|
-
mockMessages = [
|
|
686
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
687
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
688
|
-
]
|
|
689
|
-
|
|
690
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
691
|
-
|
|
692
|
-
// #when - abort error fires
|
|
693
|
-
await hook.handler({
|
|
694
|
-
event: {
|
|
695
|
-
type: "session.error",
|
|
696
|
-
properties: { sessionID, error: { name: "MessageAbortedError" } },
|
|
697
|
-
},
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
// #when - assistant starts responding (clears abort flag)
|
|
701
|
-
await hook.handler({
|
|
702
|
-
event: {
|
|
703
|
-
type: "message.updated",
|
|
704
|
-
properties: { info: { sessionID, role: "assistant" } },
|
|
705
|
-
},
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
// #when - session goes idle
|
|
709
|
-
await hook.handler({
|
|
710
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
711
|
-
})
|
|
712
|
-
|
|
713
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
714
|
-
|
|
715
|
-
// #then - continuation injected (abort flag was cleared by assistant activity)
|
|
716
|
-
expect(promptCalls.length).toBeGreaterThan(0)
|
|
717
|
-
})
|
|
718
|
-
|
|
719
|
-
test("should clear abort flag on tool execution", async () => {
|
|
720
|
-
// #given - session with abort detected
|
|
721
|
-
const sessionID = "main-clear-on-tool"
|
|
722
|
-
setMainSession(sessionID)
|
|
723
|
-
mockMessages = [
|
|
724
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
725
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
726
|
-
]
|
|
727
|
-
|
|
728
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
729
|
-
|
|
730
|
-
// #when - abort error fires
|
|
731
|
-
await hook.handler({
|
|
732
|
-
event: {
|
|
733
|
-
type: "session.error",
|
|
734
|
-
properties: { sessionID, error: { name: "MessageAbortedError" } },
|
|
735
|
-
},
|
|
736
|
-
})
|
|
737
|
-
|
|
738
|
-
// #when - tool executes (clears abort flag)
|
|
739
|
-
await hook.handler({
|
|
740
|
-
event: {
|
|
741
|
-
type: "tool.execute.before",
|
|
742
|
-
properties: { sessionID },
|
|
743
|
-
},
|
|
744
|
-
})
|
|
745
|
-
|
|
746
|
-
// #when - session goes idle
|
|
747
|
-
await hook.handler({
|
|
748
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
749
|
-
})
|
|
750
|
-
|
|
751
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
752
|
-
|
|
753
|
-
// #then - continuation injected (abort flag was cleared by tool execution)
|
|
754
|
-
expect(promptCalls.length).toBeGreaterThan(0)
|
|
755
|
-
})
|
|
756
|
-
|
|
757
|
-
test("should use event-based detection even when API indicates no abort (event wins)", async () => {
|
|
758
|
-
// #given - session with abort event but API shows no error
|
|
759
|
-
const sessionID = "main-event-wins"
|
|
760
|
-
setMainSession(sessionID)
|
|
761
|
-
mockMessages = [
|
|
762
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
763
|
-
{ info: { id: "msg-2", role: "assistant" } },
|
|
764
|
-
]
|
|
765
|
-
|
|
766
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
767
|
-
|
|
768
|
-
// #when - abort error event fires (but API doesn't have it yet)
|
|
769
|
-
await hook.handler({
|
|
770
|
-
event: {
|
|
771
|
-
type: "session.error",
|
|
772
|
-
properties: { sessionID, error: { name: "MessageAbortedError" } },
|
|
773
|
-
},
|
|
774
|
-
})
|
|
775
|
-
|
|
776
|
-
// #when - session goes idle
|
|
777
|
-
await hook.handler({
|
|
778
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
779
|
-
})
|
|
780
|
-
|
|
781
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
782
|
-
|
|
783
|
-
// #then - no continuation (event-based detection wins over API)
|
|
784
|
-
expect(promptCalls).toHaveLength(0)
|
|
785
|
-
})
|
|
786
|
-
|
|
787
|
-
test("should use API fallback when event is missed but API shows abort", async () => {
|
|
788
|
-
// #given - session where event was missed but API shows abort
|
|
789
|
-
const sessionID = "main-api-fallback"
|
|
790
|
-
setMainSession(sessionID)
|
|
791
|
-
mockMessages = [
|
|
792
|
-
{ info: { id: "msg-1", role: "user" } },
|
|
793
|
-
{ info: { id: "msg-2", role: "assistant", error: { name: "MessageAbortedError" } } },
|
|
794
|
-
]
|
|
795
|
-
|
|
796
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
|
797
|
-
|
|
798
|
-
// #when - session goes idle without prior session.error event
|
|
799
|
-
await hook.handler({
|
|
800
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
801
|
-
})
|
|
802
|
-
|
|
803
|
-
await new Promise(r => setTimeout(r, 3000))
|
|
804
|
-
|
|
805
|
-
// #then - no continuation (API fallback detected the abort)
|
|
806
|
-
expect(promptCalls).toHaveLength(0)
|
|
807
|
-
})
|
|
808
|
-
|
|
809
|
-
test("should pass model property in prompt call (undefined when no message context)", async () => {
|
|
810
|
-
// #given - session with incomplete todos, no prior message context available
|
|
811
|
-
const sessionID = "main-model-preserve"
|
|
812
|
-
setMainSession(sessionID)
|
|
813
|
-
|
|
814
|
-
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {
|
|
815
|
-
backgroundManager: createMockBackgroundManager(false),
|
|
816
|
-
})
|
|
817
|
-
|
|
818
|
-
// #when - session goes idle and continuation is injected
|
|
819
|
-
await hook.handler({
|
|
820
|
-
event: { type: "session.idle", properties: { sessionID } },
|
|
821
|
-
})
|
|
822
|
-
|
|
823
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
824
|
-
|
|
825
|
-
// #then - prompt call made, model is undefined when no context (expected behavior)
|
|
826
|
-
expect(promptCalls.length).toBe(1)
|
|
827
|
-
expect(promptCalls[0].text).toContain("TODO CONTINUATION")
|
|
828
|
-
expect("model" in promptCalls[0]).toBe(true)
|
|
829
|
-
})
|
|
830
|
-
|
|
831
|
-
test("should extract model from assistant message with flat modelID/providerID", async () => {
|
|
832
|
-
// #given - session with assistant message that has flat modelID/providerID (OpenCode API format)
|
|
833
|
-
const sessionID = "main-assistant-model"
|
|
834
|
-
setMainSession(sessionID)
|
|
835
|
-
|
|
836
|
-
// OpenCode returns assistant messages with flat modelID/providerID, not nested model object
|
|
837
|
-
const mockMessagesWithAssistant = [
|
|
838
|
-
{ info: { id: "msg-1", role: "user", agent: "Sisyphus", model: { providerID: "openai", modelID: "gpt-5.2" } } },
|
|
839
|
-
{ info: { id: "msg-2", role: "assistant", agent: "Sisyphus", modelID: "gpt-5.2", providerID: "openai" } },
|
|
840
|
-
]
|
|
841
|
-
|
|
842
|
-
const mockInput = {
|
|
843
|
-
client: {
|
|
844
|
-
session: {
|
|
845
|
-
todo: async () => ({
|
|
846
|
-
data: [{ id: "1", content: "Task 1", status: "pending", priority: "high" }],
|
|
847
|
-
}),
|
|
848
|
-
messages: async () => ({ data: mockMessagesWithAssistant }),
|
|
849
|
-
prompt: async (opts: any) => {
|
|
850
|
-
promptCalls.push({
|
|
851
|
-
sessionID: opts.path.id,
|
|
852
|
-
agent: opts.body.agent,
|
|
853
|
-
model: opts.body.model,
|
|
854
|
-
text: opts.body.parts[0].text,
|
|
855
|
-
})
|
|
856
|
-
return {}
|
|
857
|
-
},
|
|
858
|
-
},
|
|
859
|
-
tui: { showToast: async () => ({}) },
|
|
860
|
-
},
|
|
861
|
-
directory: "/tmp/test",
|
|
862
|
-
} as any
|
|
863
|
-
|
|
864
|
-
const hook = createTodoContinuationEnforcer(mockInput, {
|
|
865
|
-
backgroundManager: createMockBackgroundManager(false),
|
|
866
|
-
})
|
|
867
|
-
|
|
868
|
-
// #when - session goes idle
|
|
869
|
-
await hook.handler({ event: { type: "session.idle", properties: { sessionID } } })
|
|
870
|
-
await new Promise(r => setTimeout(r, 2500))
|
|
871
|
-
|
|
872
|
-
// #then - model should be extracted from assistant message's flat modelID/providerID
|
|
873
|
-
expect(promptCalls.length).toBe(1)
|
|
874
|
-
expect(promptCalls[0].model).toEqual({ providerID: "openai", modelID: "gpt-5.2" })
|
|
875
|
-
})
|
|
876
|
-
})
|