saeeol 1.2.4 → 1.2.7
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/bin/saeeol.cjs +2 -1
- package/package.json +15 -12
- package/src/ltm/config.ts +15 -15
- package/src/ltm/events.ts +1 -1
- package/src/ltm/index.ts +1 -1
- package/src/ltm/pipeline.ts +23 -23
- package/src/ltm/scheduler.ts +91 -17
- package/src/ltm/store.ts +9 -7
- package/src/ltm/types.ts +15 -15
- package/src/provider/local/embedder.ts +24 -24
- package/src/provider/models-snapshot.d.ts +2 -0
- package/src/provider/models-snapshot.js +3 -0
- package/src/session/message/message-errors.ts +1 -1
- package/src/session/message/message-parts.ts +1 -1
- package/src/session/message/message-transform.ts +1 -1
- package/src/session/message/message-types.ts +1 -1
- package/src/tool/core/tool.ts +1 -1
- package/AGENTS.md +0 -72
- package/BUN_SHELL_MIGRATION_PLAN.md +0 -136
- package/Dockerfile +0 -18
- package/assets/saeeol.ico +0 -0
- package/bin/saeeol +0 -187
- package/bunfig.toml +0 -7
- package/database.db +0 -0
- package/drizzle.config.ts +0 -10
- package/git +0 -0
- package/migration/20260127222353_familiar_lady_ursula/migration.sql +0 -90
- package/migration/20260127222353_familiar_lady_ursula/snapshot.json +0 -796
- package/migration/20260211171708_add_project_commands/migration.sql +0 -1
- package/migration/20260211171708_add_project_commands/snapshot.json +0 -806
- package/migration/20260213144116_wakeful_the_professor/migration.sql +0 -11
- package/migration/20260213144116_wakeful_the_professor/snapshot.json +0 -897
- package/migration/20260225215848_workspace/migration.sql +0 -7
- package/migration/20260225215848_workspace/snapshot.json +0 -959
- package/migration/20260227213759_add_session_workspace_id/migration.sql +0 -2
- package/migration/20260227213759_add_session_workspace_id/snapshot.json +0 -983
- package/migration/20260228203230_blue_harpoon/migration.sql +0 -17
- package/migration/20260228203230_blue_harpoon/snapshot.json +0 -1102
- package/migration/20260303231226_add_workspace_fields/migration.sql +0 -5
- package/migration/20260303231226_add_workspace_fields/snapshot.json +0 -1013
- package/migration/20260309230000_move_org_to_state/migration.sql +0 -3
- package/migration/20260309230000_move_org_to_state/snapshot.json +0 -1156
- package/migration/20260312043431_session_message_cursor/migration.sql +0 -4
- package/migration/20260312043431_session_message_cursor/snapshot.json +0 -1168
- package/migration/20260323234822_events/migration.sql +0 -13
- package/migration/20260323234822_events/snapshot.json +0 -1271
- package/migration/20260410174513_workspace-name/migration.sql +0 -16
- package/migration/20260410174513_workspace-name/snapshot.json +0 -1271
- package/migration/20260413175956_chief_energizer/migration.sql +0 -13
- package/migration/20260413175956_chief_energizer/snapshot.json +0 -1399
- package/migration/20260423070820_add_icon_url_override/migration.sql +0 -2
- package/migration/20260423070820_add_icon_url_override/snapshot.json +0 -1409
- package/migration/20260428004200_add_session_path/migration.sql +0 -1
- package/migration/20260428004200_add_session_path/snapshot.json +0 -1419
- package/npm/bin/saeeol +0 -42
- package/npm/package.json +0 -39
- package/npm/postinstall.js +0 -162
- package/parsers-config.ts +0 -289
- package/script/build.ts +0 -393
- package/script/check-migrations.ts +0 -16
- package/script/fix-node-pty.ts +0 -34
- package/script/generate.ts +0 -23
- package/script/postinstall.mjs +0 -189
- package/script/publish.ts +0 -200
- package/script/run-workspace-server +0 -106
- package/script/schema.ts +0 -63
- package/script/test-runner.ts +0 -420
- package/script/time.ts +0 -6
- package/script/trace-imports.ts +0 -153
- package/script/upgrade-opentui.ts +0 -64
- package/scripts/diff-sdk-types.sh +0 -52
- package/specs/effect/facades.md +0 -221
- package/specs/effect/http-api.md +0 -401
- package/specs/effect/instance-context.md +0 -309
- package/specs/effect/loose-ends.md +0 -34
- package/specs/effect/migration.md +0 -299
- package/specs/effect/routes.md +0 -64
- package/specs/effect/schema.md +0 -399
- package/specs/effect/server-package.md +0 -668
- package/specs/effect/tools.md +0 -90
- package/specs/tui-plugins.md +0 -433
- package/specs/v2/api.ts +0 -67
- package/specs/v2/keymappings.md +0 -10
- package/specs/v2/message-shape.md +0 -136
- package/src/tool/apply_patch.txt +0 -33
- package/src/tool/bash.txt +0 -119
- package/src/tool/edit.txt +0 -10
- package/src/tool/glob.txt +0 -6
- package/src/tool/grep.txt +0 -8
- package/src/tool/lsp.txt +0 -24
- package/src/tool/plan-enter.txt +0 -14
- package/src/tool/plan-exit.txt +0 -13
- package/src/tool/question.txt +0 -11
- package/src/tool/read.txt +0 -14
- package/src/tool/recall.txt +0 -12
- package/src/tool/skill.txt +0 -5
- package/src/tool/task.txt +0 -57
- package/src/tool/todowrite.txt +0 -167
- package/src/tool/warpgrep.txt +0 -10
- package/src/tool/webfetch.txt +0 -13
- package/src/tool/websearch.txt +0 -14
- package/src/tool/write.txt +0 -8
- package/sst-env.d.ts +0 -10
- package/test/AGENTS.md +0 -133
- package/test/account/repo.test.ts +0 -352
- package/test/account/service.test.ts +0 -456
- package/test/acp/agent-interface.test.ts +0 -51
- package/test/acp/event-subscription.test.ts +0 -725
- package/test/agent/agent.test.ts +0 -890
- package/test/auth/auth.test.ts +0 -86
- package/test/bun/registry.test.ts +0 -75
- package/test/bus/bus-effect.test.ts +0 -161
- package/test/bus/bus-integration.test.ts +0 -87
- package/test/bus/bus.test.ts +0 -219
- package/test/cli/account.test.ts +0 -26
- package/test/cli/auto-mode.test.ts +0 -75
- package/test/cli/bin-saeeol.test.ts +0 -8
- package/test/cli/cmd/tui/prompt-part.test.ts +0 -47
- package/test/cli/cmd/tui/prompt-traits.test.ts +0 -38
- package/test/cli/cmd/tui/sync.test.tsx +0 -159
- package/test/cli/error.test.ts +0 -18
- package/test/cli/github-action.test.ts +0 -198
- package/test/cli/github-remote.test.ts +0 -85
- package/test/cli/import.test.ts +0 -97
- package/test/cli/install-artifact.test.ts +0 -72
- package/test/cli/plugin-auth-picker.test.ts +0 -120
- package/test/cli/pr.test.ts +0 -59
- package/test/cli/tui/editor-context-zed.test.ts +0 -356
- package/test/cli/tui/editor-context.test.tsx +0 -228
- package/test/cli/tui/keybind-plugin.test.ts +0 -90
- package/test/cli/tui/markdown.test.ts +0 -161
- package/test/cli/tui/plugin-add.test.ts +0 -111
- package/test/cli/tui/plugin-install.test.ts +0 -87
- package/test/cli/tui/plugin-lifecycle.test.ts +0 -224
- package/test/cli/tui/plugin-loader-entrypoint.test.ts +0 -484
- package/test/cli/tui/plugin-loader-pure.test.ts +0 -71
- package/test/cli/tui/plugin-loader.test.ts +0 -816
- package/test/cli/tui/plugin-toggle.test.ts +0 -157
- package/test/cli/tui/revert-diff.test.ts +0 -35
- package/test/cli/tui/slot-replace.test.tsx +0 -47
- package/test/cli/tui/theme-store.test.ts +0 -54
- package/test/cli/tui/thread.test.ts +0 -28
- package/test/cli/tui/transcript.test.ts +0 -426
- package/test/cli/tui/usage.test.ts +0 -60
- package/test/cli/tui/use-event.test.tsx +0 -175
- package/test/config/agent-color.test.ts +0 -67
- package/test/config/config.test.ts +0 -2544
- package/test/config/fixtures/empty-frontmatter.md +0 -4
- package/test/config/fixtures/frontmatter.md +0 -28
- package/test/config/fixtures/markdown-header.md +0 -11
- package/test/config/fixtures/no-frontmatter.md +0 -1
- package/test/config/fixtures/weird-model-id.md +0 -13
- package/test/config/lsp.test.ts +0 -87
- package/test/config/markdown.test.ts +0 -228
- package/test/config/plugin.test.ts +0 -0
- package/test/config/tui.test.ts +0 -624
- package/test/control-plane/adapters.test.ts +0 -71
- package/test/control-plane/workspace.test.ts +0 -1526
- package/test/effect/app-runtime-logger.test.ts +0 -98
- package/test/effect/config-service.test.ts +0 -65
- package/test/effect/instance-state.test.ts +0 -394
- package/test/effect/run-service.test.ts +0 -89
- package/test/effect/runner.test.ts +0 -523
- package/test/fake/provider.ts +0 -82
- package/test/file/fsmonitor.test.ts +0 -68
- package/test/file/ignore.test.ts +0 -10
- package/test/file/index.test.ts +0 -954
- package/test/file/path-traversal.test.ts +0 -205
- package/test/file/ripgrep.test.ts +0 -226
- package/test/file/watcher.test.ts +0 -249
- package/test/filesystem/filesystem.test.ts +0 -319
- package/test/fixture/db.ts +0 -11
- package/test/fixture/fixture.test.ts +0 -26
- package/test/fixture/fixture.ts +0 -175
- package/test/fixture/flock-worker.ts +0 -72
- package/test/fixture/log-init-worker.ts +0 -62
- package/test/fixture/lsp/fake-lsp-server.js +0 -249
- package/test/fixture/plug-worker.ts +0 -93
- package/test/fixture/plugin-meta-worker.ts +0 -19
- package/test/fixture/skills/agents-sdk/SKILL.md +0 -152
- package/test/fixture/skills/cloudflare/SKILL.md +0 -211
- package/test/fixture/skills/index.json +0 -6
- package/test/fixture/tui-plugin.ts +0 -323
- package/test/fixture/tui-runtime.ts +0 -31
- package/test/format/format.test.ts +0 -272
- package/test/git/git.test.ts +0 -128
- package/test/ide/ide.test.ts +0 -82
- package/test/installation/installation.test.ts +0 -168
- package/test/keybind.test.ts +0 -421
- package/test/lib/effect.ts +0 -53
- package/test/lib/filesystem.ts +0 -10
- package/test/lib/llm-server.ts +0 -778
- package/test/lib/websocket.ts +0 -46
- package/test/lsp/client.test.ts +0 -482
- package/test/lsp/index.test.ts +0 -160
- package/test/lsp/launch.test.ts +0 -22
- package/test/lsp/lifecycle.test.ts +0 -184
- package/test/mcp/headers.test.ts +0 -178
- package/test/mcp/lifecycle.test.ts +0 -787
- package/test/mcp/oauth-auto-connect.test.ts +0 -311
- package/test/mcp/oauth-browser.test.ts +0 -276
- package/test/mcp/oauth-callback.test.ts +0 -34
- package/test/memory/abort-leak-webfetch.ts +0 -49
- package/test/memory/abort-leak.test.ts +0 -128
- package/test/patch/patch.test.ts +0 -348
- package/test/permission/arity.test.ts +0 -33
- package/test/permission/next.test.ts +0 -1227
- package/test/permission/next.toConfig.test.ts +0 -110
- package/test/permission-task.test.ts +0 -326
- package/test/plugin/auth-override.test.ts +0 -79
- package/test/plugin/cloudflare.test.ts +0 -68
- package/test/plugin/codex.test.ts +0 -123
- package/test/plugin/github-copilot-models.test.ts +0 -261
- package/test/plugin/install-concurrency.test.ts +0 -140
- package/test/plugin/install.test.ts +0 -570
- package/test/plugin/loader-shared.test.ts +0 -1169
- package/test/plugin/meta.test.ts +0 -137
- package/test/plugin/shared.test.ts +0 -88
- package/test/plugin/trigger.test.ts +0 -102
- package/test/plugin/workspace-adapter.test.ts +0 -109
- package/test/preload.ts +0 -77
- package/test/project/instance.test.ts +0 -276
- package/test/project/migrate-global.test.ts +0 -152
- package/test/project/project.test.ts +0 -600
- package/test/project/vcs.test.ts +0 -286
- package/test/project/worktree-remove.test.ts +0 -126
- package/test/project/worktree.test.ts +0 -223
- package/test/provider/amazon-bedrock.test.ts +0 -462
- package/test/provider/copilot/convert-to-copilot-messages.test.ts +0 -523
- package/test/provider/copilot/copilot-chat-model.test.ts +0 -592
- package/test/provider/gitlab-duo.test.ts +0 -413
- package/test/provider/local.test.ts +0 -208
- package/test/provider/models.test.ts +0 -261
- package/test/provider/provider-category.test.ts +0 -190
- package/test/provider/provider.test.ts +0 -2758
- package/test/provider/transform.test.ts +0 -3681
- package/test/pty/pty-output-isolation.test.ts +0 -147
- package/test/pty/pty-session.test.ts +0 -102
- package/test/pty/pty-shell.test.ts +0 -104
- package/test/question/question.test.ts +0 -490
- package/test/saeeol/agent-global-config-dirs.test.ts +0 -24
- package/test/saeeol/agent-manager-tool.test.ts +0 -71
- package/test/saeeol/agent-permission-overrides.test.ts +0 -75
- package/test/saeeol/agent-skill-permissions.test.ts +0 -37
- package/test/saeeol/ask-agent-permissions.test.ts +0 -303
- package/test/saeeol/bash-hierarchy.test.ts +0 -64
- package/test/saeeol/bash-permission-metadata.test.ts +0 -66
- package/test/saeeol/bash-security-extended.test.ts +0 -243
- package/test/saeeol/bedrock-claude-empty-content.test.ts +0 -138
- package/test/saeeol/boxes-integration.test.ts +0 -415
- package/test/saeeol/builtin-skills.test.ts +0 -75
- package/test/saeeol/cleanup.ts +0 -28
- package/test/saeeol/cli/dev-setup.test.ts +0 -74
- package/test/saeeol/cli/roll-call.test.ts +0 -161
- package/test/saeeol/cli-run-auto-helper.test.ts +0 -58
- package/test/saeeol/codex-auth-refresh.test.ts +0 -124
- package/test/saeeol/commit-message/generate.test.ts +0 -188
- package/test/saeeol/commit-message/git-context.test.ts +0 -303
- package/test/saeeol/commit-message-windows.test.ts +0 -38
- package/test/saeeol/compaction-payload-recovery.test.ts +0 -406
- package/test/saeeol/compaction-preservation-audit.test.ts +0 -122
- package/test/saeeol/compaction-skip-guard.test.ts +0 -224
- package/test/saeeol/compaction-smart-select.test.ts +0 -100
- package/test/saeeol/config/config.test.ts +0 -166
- package/test/saeeol/config/indexing-default-plugin.test.ts +0 -82
- package/test/saeeol/config/opentelemetry-default.test.ts +0 -29
- package/test/saeeol/config-gitignore.test.ts +0 -70
- package/test/saeeol/config-injector.test.ts +0 -305
- package/test/saeeol/config-resilience.test.ts +0 -234
- package/test/saeeol/config-validation.test.ts +0 -183
- package/test/saeeol/cost-propagation.test.ts +0 -94
- package/test/saeeol/cost-tracker-extended.test.ts +0 -141
- package/test/saeeol/cost-tracker.test.ts +0 -64
- package/test/saeeol/custom-provider-delete.test.ts +0 -149
- package/test/saeeol/diff-full.test.ts +0 -226
- package/test/saeeol/edit-permission-filediff.test.ts +0 -223
- package/test/saeeol/encoding.test.ts +0 -364
- package/test/saeeol/enhance-prompt.test.ts +0 -61
- package/test/saeeol/ensure-plan-dir.test.ts +0 -32
- package/test/saeeol/errors.test.ts +0 -144
- package/test/saeeol/external-directory-boundary.test.ts +0 -96
- package/test/saeeol/gateway-headers.test.ts +0 -88
- package/test/saeeol/help.test.ts +0 -191
- package/test/saeeol/ignore-migrator.test.ts +0 -308
- package/test/saeeol/indexing-auth.test.ts +0 -45
- package/test/saeeol/indexing-feature.test.ts +0 -44
- package/test/saeeol/indexing-label.test.ts +0 -70
- package/test/saeeol/indexing-startup.test.ts +0 -381
- package/test/saeeol/indexing-worktree.test.ts +0 -73
- package/test/saeeol/instruction.test.ts +0 -136
- package/test/saeeol/lancedb-runtime.test.ts +0 -116
- package/test/saeeol/loader-auth.test.ts +0 -168
- package/test/saeeol/local-model.test.ts +0 -621
- package/test/saeeol/logo.test.ts +0 -31
- package/test/saeeol/lsp-typescript-lightweight.test.ts +0 -89
- package/test/saeeol/mcp-branding.test.ts +0 -33
- package/test/saeeol/mcp-docker-rm.test.ts +0 -32
- package/test/saeeol/mcp-migrator.test.ts +0 -736
- package/test/saeeol/mcp-oauth-callback.test.ts +0 -33
- package/test/saeeol/memory-io.test.ts +0 -198
- package/test/saeeol/memory-paths.test.ts +0 -87
- package/test/saeeol/memory-security.test.ts +0 -166
- package/test/saeeol/model-cache-org.test.ts +0 -164
- package/test/saeeol/model-info-panel-utils.test.ts +0 -52
- package/test/saeeol/model-info-panel.types.test.ts +0 -7
- package/test/saeeol/models-401-fallback.test.ts +0 -52
- package/test/saeeol/modes-migrator.test.ts +0 -320
- package/test/saeeol/nvidia-headers.test.ts +0 -74
- package/test/saeeol/patch-jsonc.test.ts +0 -73
- package/test/saeeol/patch.test.ts +0 -172
- package/test/saeeol/paths.test.ts +0 -265
- package/test/saeeol/permission/config-paths.test.ts +0 -174
- package/test/saeeol/permission/env-read.test.ts +0 -149
- package/test/saeeol/permission/external-directory-allow.test.ts +0 -327
- package/test/saeeol/permission/next.always-rules.test.ts +0 -882
- package/test/saeeol/permission/next.reply-http.test.ts +0 -205
- package/test/saeeol/permission/next.reply-routing.test.ts +0 -184
- package/test/saeeol/plan-exit-detection.test.ts +0 -494
- package/test/saeeol/plan-followup.test.ts +0 -1376
- package/test/saeeol/project-config-update.test.ts +0 -120
- package/test/saeeol/project-id.test.ts +0 -455
- package/test/saeeol/provider-cost.test.ts +0 -171
- package/test/saeeol/provider-list-failed-state.test.ts +0 -100
- package/test/saeeol/question-dismiss-all.test.ts +0 -174
- package/test/saeeol/read-directory.test.ts +0 -116
- package/test/saeeol/rules-migrator.test.ts +0 -257
- package/test/saeeol/run-auto.test.ts +0 -176
- package/test/saeeol/run-network.test.ts +0 -224
- package/test/saeeol/semantic-search.test.ts +0 -186
- package/test/saeeol/server/permission-allow-everything.test.ts +0 -125
- package/test/saeeol/session/instruction-substitution.test.ts +0 -72
- package/test/saeeol/session/platform-attribution.test.ts +0 -118
- package/test/saeeol/session/session.test.ts +0 -105
- package/test/saeeol/session-compaction-cap.test.ts +0 -399
- package/test/saeeol/session-compaction-chunks.test.ts +0 -501
- package/test/saeeol/session-compaction-safety.test.ts +0 -481
- package/test/saeeol/session-fork-remap.test.ts +0 -251
- package/test/saeeol/session-import-service.test.ts +0 -114
- package/test/saeeol/session-list.test.ts +0 -47
- package/test/saeeol/session-message-metadata.test.ts +0 -128
- package/test/saeeol/session-overflow.test.ts +0 -78
- package/test/saeeol/session-processor-empty-tool-calls.test.ts +0 -571
- package/test/saeeol/session-processor-network-offline.test.ts +0 -204
- package/test/saeeol/session-processor-retry-limit.test.ts +0 -238
- package/test/saeeol/session-processor-review-telemetry.test.ts +0 -82
- package/test/saeeol/session-prompt-compaction-safety.test.ts +0 -517
- package/test/saeeol/session-prompt-queue.test.ts +0 -815
- package/test/saeeol/sessions/inflight-cache.test.ts +0 -157
- package/test/saeeol/sessions/ingest-queue.test.ts +0 -402
- package/test/saeeol/sessions/remote-protocol.test.ts +0 -258
- package/test/saeeol/sessions/remote-sender.test.ts +0 -1036
- package/test/saeeol/sessions/remote-ws.test.ts +0 -367
- package/test/saeeol/sessions/sessions-enable-remote.test.disable +0 -181
- package/test/saeeol/slot-prop-reactivity.test.ts +0 -142
- package/test/saeeol/snapshot-cache.test.ts +0 -84
- package/test/saeeol/snapshot-freeze-repro.test.ts +0 -100
- package/test/saeeol/snapshot-track-timeout.test.ts +0 -519
- package/test/saeeol/stats-subagent-cost.test.ts +0 -123
- package/test/saeeol/suggestion/auto-dismiss.test.ts +0 -65
- package/test/saeeol/suggestion/suggestion.test.ts +0 -145
- package/test/saeeol/suggestion/tool.test.ts +0 -298
- package/test/saeeol/summary-file-diff.test.ts +0 -28
- package/test/saeeol/system-prompt.test.ts +0 -142
- package/test/saeeol/task-nesting.test.ts +0 -193
- package/test/saeeol/telemetry/feedback.test.ts +0 -8
- package/test/saeeol/todo-view.test.ts +0 -57
- package/test/saeeol/tool-encoding.test.ts +0 -455
- package/test/saeeol/tool-registry-indexing-import-failure.test.ts +0 -49
- package/test/saeeol/tool-registry-indexing.test.ts +0 -236
- package/test/saeeol/tool-registry-semantic-import-failure.test.ts +0 -55
- package/test/saeeol/tool-task-model.test.ts +0 -352
- package/test/saeeol/transform-opus-4.7.test.ts +0 -89
- package/test/saeeol/tui-diff.test.ts +0 -91
- package/test/saeeol/tui-sync.test.ts +0 -80
- package/test/saeeol/util/url.test.ts +0 -141
- package/test/saeeol/workflows-migrator.test.ts +0 -261
- package/test/saeeol/worktree-diff-summary.test.ts +0 -64
- package/test/saeeol/worktree-diff.test.ts +0 -223
- package/test/saeeol/worktree-remove-lock.test.ts +0 -82
- package/test/server/AGENTS.md +0 -15
- package/test/server/contract.test.ts +0 -249
- package/test/server/experimental-session-list.test.ts +0 -157
- package/test/server/global-session-list.test.ts +0 -155
- package/test/server/httpapi-authorization.test.ts +0 -103
- package/test/server/httpapi-bridge.test.ts +0 -440
- package/test/server/httpapi-config.test.ts +0 -67
- package/test/server/httpapi-cors.test.ts +0 -89
- package/test/server/httpapi-event.test.ts +0 -57
- package/test/server/httpapi-experimental.test.ts +0 -219
- package/test/server/httpapi-file.test.ts +0 -79
- package/test/server/httpapi-instance-context.test.ts +0 -237
- package/test/server/httpapi-instance.legacy.test.ts +0 -140
- package/test/server/httpapi-instance.test.ts +0 -83
- package/test/server/httpapi-json-parity.test.ts +0 -263
- package/test/server/httpapi-mcp-oauth.test.ts +0 -76
- package/test/server/httpapi-mcp.test.ts +0 -189
- package/test/server/httpapi-provider.test.ts +0 -153
- package/test/server/httpapi-pty-websocket.test.ts +0 -16
- package/test/server/httpapi-pty.test.ts +0 -175
- package/test/server/httpapi-raw-route-auth.test.ts +0 -89
- package/test/server/httpapi-sdk.test.ts +0 -679
- package/test/server/httpapi-session.test.ts +0 -464
- package/test/server/httpapi-sync.test.ts +0 -130
- package/test/server/httpapi-tui.test.ts +0 -121
- package/test/server/httpapi-workspace-routing.test.ts +0 -471
- package/test/server/httpapi-workspace.test.ts +0 -427
- package/test/server/project-init-git.test.ts +0 -113
- package/test/server/proxy-util.test.ts +0 -113
- package/test/server/session-actions.test.ts +0 -49
- package/test/server/session-list.test.ts +0 -238
- package/test/server/session-messages.test.ts +0 -167
- package/test/server/session-select.test.ts +0 -100
- package/test/server/trace-attributes.test.ts +0 -76
- package/test/server/workspace-proxy.test.ts +0 -165
- package/test/server/workspace-routing.test.ts +0 -85
- package/test/session/compaction.test.ts +0 -2420
- package/test/session/instruction.test.ts +0 -247
- package/test/session/llm.test.ts +0 -1273
- package/test/session/message-v2.test.ts +0 -1291
- package/test/session/messages-pagination.test.ts +0 -1173
- package/test/session/network.test.ts +0 -249
- package/test/session/processor-effect.test.ts +0 -847
- package/test/session/prompt.test.ts +0 -2131
- package/test/session/retry.test.ts +0 -340
- package/test/session/revert-compact.test.ts +0 -639
- package/test/session/schema-decoding.test.ts +0 -311
- package/test/session/session-entry-stepper.test.ts +0 -917
- package/test/session/session-schema.test.ts +0 -76
- package/test/session/snapshot-tool-race.test.ts +0 -257
- package/test/session/structured-output-integration.test.ts +0 -265
- package/test/session/structured-output.test.ts +0 -381
- package/test/session/system.test.ts +0 -73
- package/test/share/share-next.test.ts +0 -333
- package/test/shell/shell.test.ts +0 -99
- package/test/skill/discovery.test.ts +0 -116
- package/test/skill/skill.test.ts +0 -393
- package/test/snapshot/snapshot.test.ts +0 -1531
- package/test/storage/db.test.ts +0 -23
- package/test/storage/json-migration.test.ts +0 -832
- package/test/storage/storage.test.ts +0 -293
- package/test/suggestion/suggestion.test.ts +0 -1
- package/test/sync/index.test.ts +0 -256
- package/test/tool/__snapshots__/parameters.test.ts.snap +0 -500
- package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
- package/test/tool/apply_patch.test.ts +0 -614
- package/test/tool/bash.test.ts +0 -1225
- package/test/tool/diagnostics-filter.test.ts +0 -55
- package/test/tool/edit.test.ts +0 -754
- package/test/tool/external-directory.test.ts +0 -169
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +0 -65179
- package/test/tool/glob.test.ts +0 -107
- package/test/tool/grep.test.ts +0 -114
- package/test/tool/lsp.test.ts +0 -187
- package/test/tool/parameters.test.ts +0 -243
- package/test/tool/question.test.ts +0 -129
- package/test/tool/read.test.ts +0 -500
- package/test/tool/recall.test.ts +0 -151
- package/test/tool/registry.test.ts +0 -203
- package/test/tool/skill.test.ts +0 -135
- package/test/tool/suggest.test.ts +0 -1
- package/test/tool/task.test.ts +0 -612
- package/test/tool/tool-define.test.ts +0 -99
- package/test/tool/truncation.test.ts +0 -260
- package/test/tool/webfetch.test.ts +0 -103
- package/test/tool/write.test.ts +0 -291
- package/test/util/data-url.test.ts +0 -14
- package/test/util/effect-zod.test.ts +0 -754
- package/test/util/error.test.ts +0 -38
- package/test/util/filesystem.test.ts +0 -656
- package/test/util/format.test.ts +0 -59
- package/test/util/glob.test.ts +0 -164
- package/test/util/iife.test.ts +0 -36
- package/test/util/lazy.test.ts +0 -50
- package/test/util/lock.test.ts +0 -72
- package/test/util/log.test.ts +0 -86
- package/test/util/module.test.ts +0 -59
- package/test/util/process.test.ts +0 -128
- package/test/util/timeout.test.ts +0 -21
- package/test/util/which.test.ts +0 -100
- package/test/util/wildcard.test.ts +0 -90
- package/test/workspace/workspace-restore.test.ts +0 -296
- package/tsconfig.json +0 -19
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// Verifies `saeeol stats` does not double-count subagent cost while still
|
|
2
|
-
// including child-session messages, tokens, tools, and model usage. The task
|
|
3
|
-
// tool propagates each child session's total cost up to the parent's
|
|
4
|
-
// tool-wrapper assistant message (#6321).
|
|
5
|
-
|
|
6
|
-
import { afterEach, describe, expect, test } from "bun:test"
|
|
7
|
-
import { aggregateSessionStats } from "../../src/cli/cmd/stats"
|
|
8
|
-
import { MessageV2 } from "../../src/session/message-v2"
|
|
9
|
-
import { Instance } from "../../src/project/instance"
|
|
10
|
-
import { ProviderID, ModelID } from "../../src/provider/schema"
|
|
11
|
-
import { Session } from "../../src/session/session"
|
|
12
|
-
import { MessageID, PartID } from "../../src/session/schema"
|
|
13
|
-
import * as Log from "@saeeol/core/util/log"
|
|
14
|
-
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
|
15
|
-
|
|
16
|
-
Log.init({ print: false })
|
|
17
|
-
|
|
18
|
-
afterEach(async () => {
|
|
19
|
-
await disposeAllInstances()
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
const ref = {
|
|
23
|
-
providerID: ProviderID.make("test"),
|
|
24
|
-
modelID: ModelID.make("test-model"),
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function assistant(sessionID: string, parentID: string, cost: number): MessageV2.Assistant {
|
|
28
|
-
return {
|
|
29
|
-
id: MessageID.ascending(),
|
|
30
|
-
role: "assistant",
|
|
31
|
-
parentID: parentID as any,
|
|
32
|
-
sessionID: sessionID as any,
|
|
33
|
-
mode: "build",
|
|
34
|
-
agent: "build",
|
|
35
|
-
cost,
|
|
36
|
-
path: { cwd: "/tmp", root: "/tmp" },
|
|
37
|
-
tokens: { input: 10, output: 5, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
38
|
-
modelID: ref.modelID,
|
|
39
|
-
providerID: ref.providerID,
|
|
40
|
-
time: { created: Date.now() },
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function step(sessionID: string, messageID: string, cost: number) {
|
|
45
|
-
await Session.updatePart({
|
|
46
|
-
id: PartID.ascending(),
|
|
47
|
-
messageID: messageID as any,
|
|
48
|
-
sessionID: sessionID as any,
|
|
49
|
-
type: "step-finish",
|
|
50
|
-
reason: "stop",
|
|
51
|
-
cost,
|
|
52
|
-
tokens: { total: 15, input: 10, output: 5, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function tool(sessionID: string, messageID: string) {
|
|
57
|
-
const time = Date.now()
|
|
58
|
-
await Session.updatePart({
|
|
59
|
-
id: PartID.ascending(),
|
|
60
|
-
messageID: messageID as any,
|
|
61
|
-
sessionID: sessionID as any,
|
|
62
|
-
type: "tool",
|
|
63
|
-
callID: "call_1",
|
|
64
|
-
tool: "bash",
|
|
65
|
-
state: {
|
|
66
|
-
status: "completed",
|
|
67
|
-
input: {},
|
|
68
|
-
output: "ok",
|
|
69
|
-
title: "bash",
|
|
70
|
-
metadata: {},
|
|
71
|
-
time: { start: time, end: time },
|
|
72
|
-
},
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
describe("stats subagent cost", () => {
|
|
77
|
-
test("counts child usage without double-counting propagated cost", async () => {
|
|
78
|
-
await using tmp = await tmpdir({ git: true })
|
|
79
|
-
await Instance.provide({
|
|
80
|
-
directory: tmp.path,
|
|
81
|
-
fn: async () => {
|
|
82
|
-
const parent = await Session.create({ title: "root" })
|
|
83
|
-
const child = await Session.create({ parentID: parent.id, title: "subagent" })
|
|
84
|
-
|
|
85
|
-
const userMsg = await Session.updateMessage({
|
|
86
|
-
id: MessageID.ascending(),
|
|
87
|
-
role: "user",
|
|
88
|
-
sessionID: parent.id,
|
|
89
|
-
agent: "build",
|
|
90
|
-
model: ref,
|
|
91
|
-
time: { created: Date.now() },
|
|
92
|
-
} as any)
|
|
93
|
-
const parentMsg = await Session.updateMessage(assistant(parent.id, userMsg.id, 1.5))
|
|
94
|
-
await step(parent.id, parentMsg.id, 1)
|
|
95
|
-
|
|
96
|
-
const childUser = await Session.updateMessage({
|
|
97
|
-
id: MessageID.ascending(),
|
|
98
|
-
role: "user",
|
|
99
|
-
sessionID: child.id,
|
|
100
|
-
agent: "general",
|
|
101
|
-
model: ref,
|
|
102
|
-
time: { created: Date.now() },
|
|
103
|
-
} as any)
|
|
104
|
-
const childMsg = await Session.updateMessage(assistant(child.id, childUser.id, 0.5))
|
|
105
|
-
await step(child.id, childMsg.id, 0.5)
|
|
106
|
-
await tool(child.id, childMsg.id)
|
|
107
|
-
|
|
108
|
-
const stats = await aggregateSessionStats()
|
|
109
|
-
const model = stats.modelUsage["test/test-model"]!
|
|
110
|
-
expect(stats.totalCost).toBeCloseTo(1.5, 6)
|
|
111
|
-
expect(stats.totalSessions).toBe(2)
|
|
112
|
-
expect(stats.totalMessages).toBe(4)
|
|
113
|
-
expect(stats.totalTokens.input).toBe(20)
|
|
114
|
-
expect(stats.totalTokens.output).toBe(10)
|
|
115
|
-
expect(stats.toolUsage.bash).toBe(1)
|
|
116
|
-
expect(model.messages).toBe(2)
|
|
117
|
-
expect(model.tokens.input).toBe(20)
|
|
118
|
-
expect(model.tokens.output).toBe(10)
|
|
119
|
-
expect(model.cost).toBeCloseTo(1.5, 6)
|
|
120
|
-
},
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
})
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import { Effect } from "effect"
|
|
3
|
-
import { SaeeolSessionPromptQueue } from "../../../src/overlay/session/prompt-queue"
|
|
4
|
-
import { Suggestion } from "../../../src/overlay/suggestion"
|
|
5
|
-
import { Instance } from "../../../src/project/instance"
|
|
6
|
-
import { MessageID, SessionID } from "../../../src/session/schema"
|
|
7
|
-
import { tmpdir } from "../../fixture/fixture"
|
|
8
|
-
|
|
9
|
-
describe("Suggestion.show auto-dismiss on queued followup", () => {
|
|
10
|
-
test("show rejects immediately when a followup is queued on the session", async () => {
|
|
11
|
-
// A tool that calls Suggestion.show after a queued prompt has arrived would
|
|
12
|
-
// otherwise block the turn on user input. Verify the pre-emptive
|
|
13
|
-
// hasFollowup check rejects with DismissedError before any pending entry
|
|
14
|
-
// is registered or a Shown event is published.
|
|
15
|
-
await using tmp = await tmpdir({ git: true })
|
|
16
|
-
await Instance.provide({
|
|
17
|
-
directory: tmp.path,
|
|
18
|
-
fn: async () => {
|
|
19
|
-
const sessionID = SessionID.make("ses_auto_show")
|
|
20
|
-
const started = Promise.withResolvers<void>()
|
|
21
|
-
const release = Promise.withResolvers<void>()
|
|
22
|
-
|
|
23
|
-
// Slot 1 stays running so activeSince is pinned to its seq.
|
|
24
|
-
const first = Effect.runPromise(
|
|
25
|
-
SaeeolSessionPromptQueue.enqueue(
|
|
26
|
-
sessionID,
|
|
27
|
-
MessageID.make("message_show_1"),
|
|
28
|
-
Effect.gen(function* () {
|
|
29
|
-
started.resolve()
|
|
30
|
-
yield* Effect.promise(() => release.promise)
|
|
31
|
-
return "first" as const
|
|
32
|
-
}),
|
|
33
|
-
Effect.succeed("first-cancelled" as const),
|
|
34
|
-
),
|
|
35
|
-
)
|
|
36
|
-
await started.promise
|
|
37
|
-
|
|
38
|
-
// Slot 2 arrives while slot 1 is active — latest > activeSince.
|
|
39
|
-
const second = Effect.runPromise(
|
|
40
|
-
SaeeolSessionPromptQueue.enqueue(
|
|
41
|
-
sessionID,
|
|
42
|
-
MessageID.make("message_show_2"),
|
|
43
|
-
Effect.succeed("second" as const),
|
|
44
|
-
Effect.succeed("second-cancelled" as const),
|
|
45
|
-
),
|
|
46
|
-
)
|
|
47
|
-
await Bun.sleep(10)
|
|
48
|
-
expect(SaeeolSessionPromptQueue.hasFollowup(sessionID)).toBe(true)
|
|
49
|
-
|
|
50
|
-
await expect(
|
|
51
|
-
Suggestion.show({
|
|
52
|
-
sessionID,
|
|
53
|
-
text: "Run review?",
|
|
54
|
-
actions: [{ label: "Review", prompt: "/local-review-uncommitted" }],
|
|
55
|
-
}),
|
|
56
|
-
).rejects.toBeInstanceOf(Suggestion.DismissedError)
|
|
57
|
-
expect(await Suggestion.list()).toEqual([])
|
|
58
|
-
|
|
59
|
-
release.resolve()
|
|
60
|
-
expect(await first).toBe("first")
|
|
61
|
-
expect(await second).toBe("second")
|
|
62
|
-
},
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
})
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test"
|
|
2
|
-
import { Instance } from "../../../src/project/instance"
|
|
3
|
-
import { Suggestion } from "../../../src/overlay/suggestion"
|
|
4
|
-
import { tmpdir } from "../../fixture/fixture"
|
|
5
|
-
|
|
6
|
-
describe("suggestion", () => {
|
|
7
|
-
test("show adds pending request with blocking flag", async () => {
|
|
8
|
-
await using tmp = await tmpdir({ git: true })
|
|
9
|
-
await Instance.provide({
|
|
10
|
-
directory: tmp.path,
|
|
11
|
-
fn: async () => {
|
|
12
|
-
const pending = Suggestion.show({
|
|
13
|
-
sessionID: "ses_test",
|
|
14
|
-
text: "Run review?",
|
|
15
|
-
blocking: false,
|
|
16
|
-
actions: [{ label: "Start", description: "Run it", prompt: "/local-review-uncommitted" }],
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const list = await Suggestion.list()
|
|
20
|
-
expect(list).toHaveLength(1)
|
|
21
|
-
expect(list[0]?.blocking).toBe(false)
|
|
22
|
-
expect(list[0]?.text).toBe("Run review?")
|
|
23
|
-
|
|
24
|
-
await Suggestion.dismiss(list[0]!.id)
|
|
25
|
-
await expect(pending).rejects.toBeInstanceOf(Suggestion.DismissedError)
|
|
26
|
-
},
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test("accept resolves selected action and removes pending request", async () => {
|
|
31
|
-
await using tmp = await tmpdir({ git: true })
|
|
32
|
-
await Instance.provide({
|
|
33
|
-
directory: tmp.path,
|
|
34
|
-
fn: async () => {
|
|
35
|
-
const ask = Suggestion.show({
|
|
36
|
-
sessionID: "ses_test",
|
|
37
|
-
text: "Next step?",
|
|
38
|
-
actions: [
|
|
39
|
-
{ label: "Review", description: "Start review", prompt: "/local-review-uncommitted" },
|
|
40
|
-
{ label: "Test", description: "Run tests", prompt: "Run the relevant tests now." },
|
|
41
|
-
],
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const list = await Suggestion.list()
|
|
45
|
-
await Suggestion.accept({ requestID: list[0]!.id, index: 1 })
|
|
46
|
-
|
|
47
|
-
await expect(ask).resolves.toEqual({
|
|
48
|
-
label: "Test",
|
|
49
|
-
description: "Run tests",
|
|
50
|
-
prompt: "Run the relevant tests now.",
|
|
51
|
-
})
|
|
52
|
-
await expect(Suggestion.list()).resolves.toEqual([])
|
|
53
|
-
},
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
test("dismiss rejects pending request and removes it", async () => {
|
|
58
|
-
await using tmp = await tmpdir({ git: true })
|
|
59
|
-
await Instance.provide({
|
|
60
|
-
directory: tmp.path,
|
|
61
|
-
fn: async () => {
|
|
62
|
-
const ask = Suggestion.show({
|
|
63
|
-
sessionID: "ses_test",
|
|
64
|
-
text: "Review changes?",
|
|
65
|
-
actions: [{ label: "Start", prompt: "/local-review-uncommitted" }],
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const list = await Suggestion.list()
|
|
69
|
-
await Suggestion.dismiss(list[0]!.id)
|
|
70
|
-
|
|
71
|
-
await expect(ask).rejects.toBeInstanceOf(Suggestion.DismissedError)
|
|
72
|
-
await expect(Suggestion.list()).resolves.toEqual([])
|
|
73
|
-
},
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
test("dismissAll clears all pending suggestions for the target session", async () => {
|
|
78
|
-
await using tmp = await tmpdir({ git: true })
|
|
79
|
-
await Instance.provide({
|
|
80
|
-
directory: tmp.path,
|
|
81
|
-
fn: async () => {
|
|
82
|
-
// Two suggestions for session A
|
|
83
|
-
const a1 = Suggestion.show({
|
|
84
|
-
sessionID: "ses_a",
|
|
85
|
-
text: "Review?",
|
|
86
|
-
actions: [{ label: "Go", prompt: "/review" }],
|
|
87
|
-
})
|
|
88
|
-
const a2 = Suggestion.show({
|
|
89
|
-
sessionID: "ses_a",
|
|
90
|
-
text: "Test?",
|
|
91
|
-
actions: [{ label: "Run", prompt: "/test" }],
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
// One suggestion for session B
|
|
95
|
-
const b1 = Suggestion.show({
|
|
96
|
-
sessionID: "ses_b",
|
|
97
|
-
text: "Deploy?",
|
|
98
|
-
actions: [{ label: "Ship", prompt: "/deploy" }],
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
expect(await Suggestion.list()).toHaveLength(3)
|
|
102
|
-
|
|
103
|
-
// Track whether B's promise settles
|
|
104
|
-
let settled = false
|
|
105
|
-
b1.then(() => {
|
|
106
|
-
settled = true
|
|
107
|
-
}).catch(() => {
|
|
108
|
-
settled = true
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
// Dismiss all for session A only
|
|
112
|
-
await Suggestion.dismissAll("ses_a")
|
|
113
|
-
|
|
114
|
-
// Both A promises should reject
|
|
115
|
-
await expect(a1).rejects.toBeInstanceOf(Suggestion.DismissedError)
|
|
116
|
-
await expect(a2).rejects.toBeInstanceOf(Suggestion.DismissedError)
|
|
117
|
-
|
|
118
|
-
// Flush microtasks to see if B settled
|
|
119
|
-
await new Promise((r) => setTimeout(r, 10))
|
|
120
|
-
expect(settled).toBe(false)
|
|
121
|
-
|
|
122
|
-
// Only B's suggestion remains
|
|
123
|
-
const remaining = await Suggestion.list()
|
|
124
|
-
expect(remaining).toHaveLength(1)
|
|
125
|
-
expect(remaining[0]?.sessionID).toBe("ses_b")
|
|
126
|
-
|
|
127
|
-
// Clean up B
|
|
128
|
-
await Suggestion.dismiss(remaining[0]!.id)
|
|
129
|
-
await expect(b1).rejects.toBeInstanceOf(Suggestion.DismissedError)
|
|
130
|
-
},
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test("dismissAll is a no-op when no suggestions exist", async () => {
|
|
135
|
-
await using tmp = await tmpdir({ git: true })
|
|
136
|
-
await Instance.provide({
|
|
137
|
-
directory: tmp.path,
|
|
138
|
-
fn: async () => {
|
|
139
|
-
// Should not throw
|
|
140
|
-
await Suggestion.dismissAll("ses_nonexistent")
|
|
141
|
-
expect(await Suggestion.list()).toEqual([])
|
|
142
|
-
},
|
|
143
|
-
})
|
|
144
|
-
})
|
|
145
|
-
})
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test, spyOn } from "bun:test"
|
|
2
|
-
import { Effect, Layer, ManagedRuntime } from "effect"
|
|
3
|
-
import { Command } from "../../../src/command"
|
|
4
|
-
import { Suggestion } from "../../../src/overlay/suggestion"
|
|
5
|
-
import { SuggestTool } from "../../../src/overlay/suggestion/tool"
|
|
6
|
-
import { Tool } from "../../../src/tool/tool"
|
|
7
|
-
import { Truncate } from "../../../src/tool/truncate"
|
|
8
|
-
import { Agent } from "../../../src/agent/agent"
|
|
9
|
-
import { SessionStatus } from "../../../src/session/status"
|
|
10
|
-
|
|
11
|
-
const toolRuntime = ManagedRuntime.make(Layer.mergeAll(Truncate.defaultLayer, Agent.defaultLayer))
|
|
12
|
-
|
|
13
|
-
async function initTool() {
|
|
14
|
-
return toolRuntime.runPromise(
|
|
15
|
-
Effect.gen(function* () {
|
|
16
|
-
const info = yield* SuggestTool
|
|
17
|
-
return yield* Tool.init(info)
|
|
18
|
-
}),
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const ctx = {
|
|
23
|
-
sessionID: "ses_test",
|
|
24
|
-
messageID: "msg_assistant",
|
|
25
|
-
callID: "call_suggest",
|
|
26
|
-
agent: "code",
|
|
27
|
-
abort: AbortSignal.any([]),
|
|
28
|
-
messages: [
|
|
29
|
-
{
|
|
30
|
-
info: {
|
|
31
|
-
id: "msg_user",
|
|
32
|
-
role: "user",
|
|
33
|
-
sessionID: "ses_test",
|
|
34
|
-
time: { created: 1 },
|
|
35
|
-
agent: "code",
|
|
36
|
-
model: { providerID: "openai", modelID: "gpt-4" },
|
|
37
|
-
},
|
|
38
|
-
parts: [],
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
metadata: () => {},
|
|
42
|
-
ask: () => Effect.void,
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
describe("tool.suggest", () => {
|
|
46
|
-
let show: ReturnType<typeof spyOn>
|
|
47
|
-
let cmdGet: ReturnType<typeof spyOn>
|
|
48
|
-
let statusSet: ReturnType<typeof spyOn>
|
|
49
|
-
|
|
50
|
-
beforeEach(() => {
|
|
51
|
-
show = spyOn(Suggestion, "show")
|
|
52
|
-
cmdGet = spyOn(Command, "get")
|
|
53
|
-
statusSet = spyOn(SessionStatus, "set").mockResolvedValue(undefined as any)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
afterEach(() => {
|
|
57
|
-
show.mockRestore()
|
|
58
|
-
cmdGet.mockRestore()
|
|
59
|
-
statusSet.mockRestore()
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test("returns dismissal result when suggestion is dismissed", async () => {
|
|
63
|
-
const tool = await initTool()
|
|
64
|
-
show.mockRejectedValueOnce(new Suggestion.DismissedError())
|
|
65
|
-
|
|
66
|
-
const result = await toolRuntime.runPromise(
|
|
67
|
-
tool.execute(
|
|
68
|
-
{
|
|
69
|
-
suggest: "Run review?",
|
|
70
|
-
actions: [{ label: "Start", prompt: "/local-review-uncommitted" }],
|
|
71
|
-
},
|
|
72
|
-
ctx as any,
|
|
73
|
-
),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
expect(result.title).toBe("Suggestion dismissed")
|
|
77
|
-
expect(result.output).toBe("User dismissed the suggestion.")
|
|
78
|
-
expect(result.metadata.dismissed).toBe(true)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
test("resolves command template for slash-command action prompt", async () => {
|
|
82
|
-
const tool = await initTool()
|
|
83
|
-
show.mockResolvedValueOnce({
|
|
84
|
-
label: "Start review",
|
|
85
|
-
description: "Run a local review now",
|
|
86
|
-
prompt: "/local-review-uncommitted",
|
|
87
|
-
})
|
|
88
|
-
cmdGet.mockResolvedValueOnce({
|
|
89
|
-
name: "local-review-uncommitted",
|
|
90
|
-
description: "local review (uncommitted changes)",
|
|
91
|
-
template: Promise.resolve("Review these uncommitted changes:\n\n## Files Changed\n..."),
|
|
92
|
-
hints: [],
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const result = await toolRuntime.runPromise(
|
|
96
|
-
tool.execute(
|
|
97
|
-
{
|
|
98
|
-
suggest: "Run review?",
|
|
99
|
-
actions: [{ label: "Start review", prompt: "/local-review-uncommitted" }],
|
|
100
|
-
},
|
|
101
|
-
ctx as any,
|
|
102
|
-
),
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
expect(result.title).toBe("User accepted: Start review")
|
|
106
|
-
expect(result.output).toContain("Review these uncommitted changes:")
|
|
107
|
-
expect(result.output).toContain("Carry out the following request now")
|
|
108
|
-
expect(result.metadata.dismissed).toBe(false)
|
|
109
|
-
expect(result.metadata.accepted).toEqual({
|
|
110
|
-
label: "Start review",
|
|
111
|
-
description: "Run a local review now",
|
|
112
|
-
prompt: "/local-review-uncommitted",
|
|
113
|
-
})
|
|
114
|
-
expect(cmdGet).toHaveBeenCalledWith("local-review-uncommitted")
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
test("returns plain-text prompt directly for non-command actions", async () => {
|
|
118
|
-
const tool = await initTool()
|
|
119
|
-
show.mockResolvedValueOnce({
|
|
120
|
-
label: "Run tests",
|
|
121
|
-
prompt: "Run the test suite and fix any failures",
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const result = await toolRuntime.runPromise(
|
|
125
|
-
tool.execute(
|
|
126
|
-
{
|
|
127
|
-
suggest: "Tests might need running",
|
|
128
|
-
actions: [{ label: "Run tests", prompt: "Run the test suite and fix any failures" }],
|
|
129
|
-
},
|
|
130
|
-
ctx as any,
|
|
131
|
-
),
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
expect(result.title).toBe("User accepted: Run tests")
|
|
135
|
-
expect(result.output).toContain("Run the test suite and fix any failures")
|
|
136
|
-
expect(result.output).toContain("Carry out the following request now")
|
|
137
|
-
expect(result.metadata.dismissed).toBe(false)
|
|
138
|
-
expect(cmdGet).not.toHaveBeenCalled()
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
test("falls back to raw prompt when command is not found", async () => {
|
|
142
|
-
const tool = await initTool()
|
|
143
|
-
show.mockResolvedValueOnce({
|
|
144
|
-
label: "Unknown cmd",
|
|
145
|
-
prompt: "/nonexistent-command",
|
|
146
|
-
})
|
|
147
|
-
cmdGet.mockResolvedValueOnce(undefined)
|
|
148
|
-
|
|
149
|
-
const result = await toolRuntime.runPromise(
|
|
150
|
-
tool.execute(
|
|
151
|
-
{
|
|
152
|
-
suggest: "Try this?",
|
|
153
|
-
actions: [{ label: "Unknown cmd", prompt: "/nonexistent-command" }],
|
|
154
|
-
},
|
|
155
|
-
ctx as any,
|
|
156
|
-
),
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
expect(result.title).toBe("User accepted: Unknown cmd")
|
|
160
|
-
expect(result.output).toContain("/nonexistent-command")
|
|
161
|
-
expect(result.metadata.dismissed).toBe(false)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
test("falls back to raw prompt when template resolution fails", async () => {
|
|
165
|
-
const tool = await initTool()
|
|
166
|
-
show.mockResolvedValueOnce({
|
|
167
|
-
label: "Start review",
|
|
168
|
-
prompt: "/local-review-uncommitted",
|
|
169
|
-
})
|
|
170
|
-
cmdGet.mockResolvedValueOnce({
|
|
171
|
-
name: "local-review-uncommitted",
|
|
172
|
-
description: "local review (uncommitted changes)",
|
|
173
|
-
template: Promise.reject(new Error("git not found")),
|
|
174
|
-
hints: [],
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
const result = await toolRuntime.runPromise(
|
|
178
|
-
tool.execute(
|
|
179
|
-
{
|
|
180
|
-
suggest: "Run review?",
|
|
181
|
-
actions: [{ label: "Start review", prompt: "/local-review-uncommitted" }],
|
|
182
|
-
},
|
|
183
|
-
ctx as any,
|
|
184
|
-
),
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
expect(result.title).toBe("User accepted: Start review")
|
|
188
|
-
expect(result.output).toContain("/local-review-uncommitted")
|
|
189
|
-
expect(result.metadata.dismissed).toBe(false)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
// The suggest tool must emit non-blocking suggestions so the main CLI input
|
|
193
|
-
// stays focused and submittable while the picker is visible (matches the
|
|
194
|
-
// VS Code extension). Blocking suggestions hide the main prompt entirely.
|
|
195
|
-
test("emits non-blocking suggestions so the main input stays active", async () => {
|
|
196
|
-
const tool = await initTool()
|
|
197
|
-
show.mockRejectedValueOnce(new Suggestion.DismissedError())
|
|
198
|
-
|
|
199
|
-
await toolRuntime.runPromise(
|
|
200
|
-
tool.execute(
|
|
201
|
-
{
|
|
202
|
-
suggest: "Run review?",
|
|
203
|
-
actions: [{ label: "Start", prompt: "/local-review-uncommitted" }],
|
|
204
|
-
},
|
|
205
|
-
ctx as any,
|
|
206
|
-
),
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
expect(show).toHaveBeenCalledWith(
|
|
210
|
-
expect.objectContaining({
|
|
211
|
-
sessionID: ctx.sessionID,
|
|
212
|
-
blocking: false,
|
|
213
|
-
}),
|
|
214
|
-
)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
// Regression for https://github.com/SAEEOL/saeeol/pull/9199: while the
|
|
218
|
-
// suggest tool is blocked on user input the session status must be flipped
|
|
219
|
-
// to idle so a session left with an open suggestion (e.g. VS Code closed
|
|
220
|
-
// mid-prompt) does not appear stuck as busy.
|
|
221
|
-
test("marks session idle while waiting for user response", async () => {
|
|
222
|
-
const tool = await initTool()
|
|
223
|
-
let resolveShow: (action: Suggestion.Action) => void = () => {}
|
|
224
|
-
show.mockReturnValueOnce(
|
|
225
|
-
new Promise<Suggestion.Action>((resolve) => {
|
|
226
|
-
resolveShow = resolve
|
|
227
|
-
}),
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
const pending = toolRuntime.runPromise(
|
|
231
|
-
tool.execute(
|
|
232
|
-
{
|
|
233
|
-
suggest: "Run review?",
|
|
234
|
-
actions: [{ label: "Start", prompt: "do it" }],
|
|
235
|
-
},
|
|
236
|
-
ctx as any,
|
|
237
|
-
),
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
// Wait for the tool to reach the await on the suggestion promise so the
|
|
241
|
-
// idle status call has been issued.
|
|
242
|
-
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
243
|
-
|
|
244
|
-
expect(statusSet).toHaveBeenCalledWith(ctx.sessionID, { type: "idle" })
|
|
245
|
-
expect(statusSet).not.toHaveBeenCalledWith(ctx.sessionID, { type: "busy" })
|
|
246
|
-
|
|
247
|
-
resolveShow({ label: "Start", prompt: "do it" })
|
|
248
|
-
await pending
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
// Regression for https://github.com/SAEEOL/saeeol/pull/9199: once the
|
|
252
|
-
// user accepts a suggestion the session must be flipped back to busy
|
|
253
|
-
// immediately so there is no idle flash while the follow-up response is
|
|
254
|
-
// generated.
|
|
255
|
-
test("restores busy status after accept, in order (idle then busy)", async () => {
|
|
256
|
-
const tool = await initTool()
|
|
257
|
-
show.mockResolvedValueOnce({ label: "Go", prompt: "go" })
|
|
258
|
-
|
|
259
|
-
await toolRuntime.runPromise(
|
|
260
|
-
tool.execute(
|
|
261
|
-
{
|
|
262
|
-
suggest: "Go?",
|
|
263
|
-
actions: [{ label: "Go", prompt: "go" }],
|
|
264
|
-
},
|
|
265
|
-
ctx as any,
|
|
266
|
-
),
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
const statuses = statusSet.mock.calls
|
|
270
|
-
.filter((call: unknown[]) => call[0] === ctx.sessionID)
|
|
271
|
-
.map((call: unknown[]) => (call[1] as { type: string }).type)
|
|
272
|
-
expect(statuses).toEqual(["idle", "busy"])
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
// Regression for https://github.com/SAEEOL/saeeol/pull/9199: a dismissed
|
|
276
|
-
// suggestion leaves the session idle — the runLoop will restore busy on the
|
|
277
|
-
// next iteration, so the tool must not flip busy itself when the user
|
|
278
|
-
// walked away.
|
|
279
|
-
test("leaves session idle when suggestion is dismissed", async () => {
|
|
280
|
-
const tool = await initTool()
|
|
281
|
-
show.mockRejectedValueOnce(new Suggestion.DismissedError())
|
|
282
|
-
|
|
283
|
-
await toolRuntime.runPromise(
|
|
284
|
-
tool.execute(
|
|
285
|
-
{
|
|
286
|
-
suggest: "Go?",
|
|
287
|
-
actions: [{ label: "Go", prompt: "go" }],
|
|
288
|
-
},
|
|
289
|
-
ctx as any,
|
|
290
|
-
),
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
const statuses = statusSet.mock.calls
|
|
294
|
-
.filter((call: unknown[]) => call[0] === ctx.sessionID)
|
|
295
|
-
.map((call: unknown[]) => (call[1] as { type: string }).type)
|
|
296
|
-
expect(statuses).toEqual(["idle"])
|
|
297
|
-
})
|
|
298
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { test, expect } from "bun:test"
|
|
2
|
-
import { Snapshot } from "../../src/snapshot"
|
|
3
|
-
|
|
4
|
-
test("SummaryFileDiff does not contain the `patch` field", () => {
|
|
5
|
-
const keys = Object.keys(Snapshot.SummaryFileDiff.fields)
|
|
6
|
-
expect(keys).not.toContain("patch")
|
|
7
|
-
expect(keys.sort()).toEqual(["additions", "deletions", "file", "status"])
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
test("SummaryFileDiff parse strips `patch` when present on input", () => {
|
|
11
|
-
const full = {
|
|
12
|
-
file: "a.txt",
|
|
13
|
-
patch: "@@ -1 +1 @@\n-old\n+new\n",
|
|
14
|
-
additions: 1,
|
|
15
|
-
deletions: 1,
|
|
16
|
-
status: "modified" as const,
|
|
17
|
-
}
|
|
18
|
-
const parsed = Snapshot.SummaryFileDiff.zod.parse(full)
|
|
19
|
-
expect(parsed).not.toHaveProperty("patch")
|
|
20
|
-
expect(parsed).toEqual({ file: "a.txt", additions: 1, deletions: 1, status: "modified" })
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test("SummaryFileDiff differs from FileDiff by exactly `patch`", () => {
|
|
24
|
-
const full = new Set(Object.keys(Snapshot.FileDiff.fields))
|
|
25
|
-
const summary = new Set(Object.keys(Snapshot.SummaryFileDiff.fields))
|
|
26
|
-
expect([...full].filter((k) => !summary.has(k))).toEqual(["patch"])
|
|
27
|
-
expect([...summary].filter((k) => !full.has(k))).toEqual([])
|
|
28
|
-
})
|