@selesai/code 0.13.15 → 0.13.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +2 -14
- package/dist/bun/cli.d.ts +3 -1
- package/dist/bun/cli.js +3 -9
- package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
- package/dist/bun/sandbox-env-setup.d.ts +1 -0
- package/dist/bun/sandbox-env-setup.js +3 -0
- package/dist/cli/auth-command.d.ts +23 -0
- package/dist/cli/auth-command.js +102 -0
- package/dist/cli/config-selector.js +2 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.js +11 -0
- package/dist/cli.js +2 -14
- package/dist/config.d.ts +3 -2
- package/dist/config.js +9 -4
- package/dist/core/agent-session-runtime.js +15 -7
- package/dist/core/agent-session.d.ts +2 -6
- package/dist/core/agent-session.js +15 -4
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -1
- package/dist/core/extensions/loader.js +3 -0
- package/dist/core/extensions/types.d.ts +16 -2
- package/dist/core/http-dispatcher.js +2 -0
- package/dist/core/keybindings.d.ts +5 -0
- package/dist/core/keybindings.js +4 -0
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/model-config.d.ts +15 -0
- package/dist/core/model-config.js +3 -0
- package/dist/core/model-runtime.d.ts +1 -0
- package/dist/core/model-runtime.js +5 -2
- package/dist/core/provider-composer.js +8 -2
- package/dist/core/session-export.d.ts +3 -0
- package/dist/core/session-export.js +31 -0
- package/dist/core/session-manager.d.ts +3 -2
- package/dist/core/session-manager.js +40 -13
- package/dist/core/skills.d.ts +1 -1
- package/dist/core/skills.js +4 -2
- package/dist/core/system-prompt.js +10 -10
- package/dist/core/tools/bash.js +3 -117
- package/dist/core/tools/edit.d.ts +1 -12
- package/dist/core/tools/edit.js +5 -163
- package/dist/core/tools/find.js +4 -55
- package/dist/core/tools/grep.js +4 -60
- package/dist/core/tools/ls.js +4 -49
- package/dist/core/tools/read.d.ts +4 -0
- package/dist/core/tools/read.js +10 -122
- package/dist/core/tools/renderers/bash.d.ts +11 -0
- package/dist/core/tools/renderers/bash.js +129 -0
- package/dist/core/tools/renderers/edit.d.ts +22 -0
- package/dist/core/tools/renderers/edit.js +166 -0
- package/dist/core/tools/renderers/find.d.ts +9 -0
- package/dist/core/tools/renderers/find.js +63 -0
- package/dist/core/tools/renderers/grep.d.ts +9 -0
- package/dist/core/tools/renderers/grep.js +68 -0
- package/dist/core/tools/renderers/index.d.ts +27 -0
- package/dist/core/tools/renderers/index.js +46 -0
- package/dist/core/tools/renderers/ls.d.ts +9 -0
- package/dist/core/tools/renderers/ls.js +57 -0
- package/dist/core/tools/renderers/read.d.ts +10 -0
- package/dist/core/tools/renderers/read.js +128 -0
- package/dist/core/tools/renderers/write.d.ts +9 -0
- package/dist/core/tools/renderers/write.js +152 -0
- package/dist/core/tools/write.js +5 -146
- package/dist/extensions/capability-gateway/index.ts +12 -1
- package/dist/extensions/capability-gateway/integration.test.ts +23 -0
- package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
- package/dist/extensions/pi-hermes-memory/README.md +16 -12
- package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
- package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
- package/dist/extensions/pi-hermes-memory/package.json +4 -4
- package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
- package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
- package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
- package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
- package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
- package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
- package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
- package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
- package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
- package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
- package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
- package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
- package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
- package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
- package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
- package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
- package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
- package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
- package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
- package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
- package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
- package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
- package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
- package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
- package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
- package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
- package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
- package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
- package/dist/extensions/pi-intercom/README.md +16 -2
- package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
- package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
- package/dist/extensions/pi-intercom/index.ts +1 -1
- package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
- package/dist/extensions/pi-intercom/package.json +1 -1
- package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
- package/dist/extensions/pi-intercom/project-agent.ts +1 -6
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
- package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
- package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
- package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
- package/dist/extensions/pi-subagents/README.md +3 -3
- package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
- package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
- package/dist/extensions/pi-subagents/agents/scout.md +1 -1
- package/dist/extensions/pi-subagents/docs/agents.md +32 -16
- package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
- package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
- package/dist/extensions/pi-subagents/docs/missions.md +2 -0
- package/dist/extensions/pi-subagents/docs/models.md +58 -1
- package/dist/extensions/pi-subagents/docs/observability.md +50 -12
- package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
- package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
- package/dist/extensions/pi-subagents/package-lock.json +747 -177
- package/dist/extensions/pi-subagents/package.json +6 -4
- package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
- package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
- package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
- package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
- package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
- package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
- package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
- package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
- package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
- package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
- package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
- package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
- package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
- package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
- package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
- package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
- package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
- package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
- package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
- package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
- package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
- package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
- package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
- package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
- package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
- package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
- package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
- package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
- package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
- package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
- package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
- package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
- package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
- package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
- package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
- package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
- package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
- package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
- package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
- package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
- package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
- package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
- package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
- package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
- package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
- package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
- package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
- package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
- package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
- package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
- package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
- package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
- package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
- package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
- package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
- package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
- package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
- package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
- package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
- package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
- package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
- package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
- package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
- package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
- package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
- package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
- package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
- package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
- package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
- package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
- package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
- package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
- package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
- package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
- package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
- package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
- package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
- package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
- package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
- package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
- package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
- package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
- package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
- package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
- package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
- package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
- package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
- package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
- package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
- package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
- package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
- package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
- package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
- package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
- package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
- package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
- package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
- package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
- package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
- package/dist/extensions/pi-web-agent/src/types.ts +1 -1
- package/dist/extensions/pi-zentui/README.md +17 -0
- package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
- package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
- package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
- package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
- package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
- package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
- package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
- package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
- package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
- package/dist/extensions/pi-zentui/package-lock.json +277 -517
- package/dist/extensions/pi-zentui/package.json +7 -4
- package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
- package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
- package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
- package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
- package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
- package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
- package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
- package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
- package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
- package/dist/index.d.ts +1 -1
- package/dist/main.js +4 -1
- package/dist/modes/interactive/chat-viewport.d.ts +19 -0
- package/dist/modes/interactive/chat-viewport.js +27 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
- package/dist/modes/interactive/components/assistant-message.js +30 -13
- package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
- package/dist/modes/interactive/components/custom-editor.js +39 -1
- package/dist/modes/interactive/components/first-time-setup.js +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
- package/dist/modes/interactive/components/markdown-transform.js +18 -0
- package/dist/modes/interactive/components/model-selector.js +19 -17
- package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
- package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -20
- package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
- package/dist/modes/interactive/components/settings-submenu.js +164 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
- package/dist/modes/interactive/components/status-indicator.js +10 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.js +75 -18
- package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
- package/dist/modes/interactive/components/tool-execution.js +35 -33
- package/dist/modes/interactive/components/trust-selector.js +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts +13 -15
- package/dist/modes/interactive/interactive-mode.js +144 -80
- package/dist/modes/interactive/session-share.d.ts +15 -0
- package/dist/modes/interactive/session-share.js +183 -0
- package/dist/modes/interactive/theme/dark.json +2 -1
- package/dist/modes/interactive/theme/light.json +2 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +5 -0
- package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
- package/dist/modes/interactive/theme/theme-json.js +129 -0
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +14 -5
- package/dist/modes/interactive/theme/theme.js +18 -119
- package/dist/modes/interactive/tui-renderer.d.ts +20 -0
- package/dist/modes/interactive/tui-renderer.js +65 -0
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/management-http.d.ts +24 -0
- package/dist/utils/management-http.js +53 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.js +8 -0
- package/dist/utils/tools-manager.d.ts +1 -0
- package/dist/utils/tools-manager.js +43 -21
- package/docs/docs.json +0 -4
- package/package.json +4 -4
- package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
- package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
- package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
- package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
- package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
- package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
- package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
- package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
- package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
- package/dist/skills/implanger/SKILL.md +0 -70
- package/dist/skills/output/SKILL.md +0 -49
- package/dist/skills/planger/SKILL.md +0 -167
- package/dist/skills/workflow/SKILL.md +0 -95
- package/docs/workflows.md +0 -18
- /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
|
@@ -2,6 +2,30 @@ const FTS5_OPERATOR_PATTERN = /\b(OR|AND|NOT|NEAR)\b/;
|
|
|
2
2
|
const FTS5_TOKEN_PATTERN = /"([^"]*)"|(\S+)/g;
|
|
3
3
|
const NATURAL_LANGUAGE_CONNECTORS = new Set(['and', 'or', 'not', 'near']);
|
|
4
4
|
|
|
5
|
+
// Common English stop words that add noise to FTS5 searches. They are silently
|
|
6
|
+
// dropped from natural-language queries so common filler words cannot dominate
|
|
7
|
+
// the FTS5 ranking or produce misleading "AND"-style misses. The list is
|
|
8
|
+
// deliberately conservative: only high-frequency function words are dropped.
|
|
9
|
+
// Words that frequently carry the actual search intent (can, need, off, like,
|
|
10
|
+
// get) stay, so "can the app start" still searches for "can".
|
|
11
|
+
const STOP_WORDS = new Set([
|
|
12
|
+
'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
13
|
+
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should',
|
|
14
|
+
'may', 'might', 'shall', 'dare', 'ought', 'used',
|
|
15
|
+
'i', 'me', 'my', 'mine', 'we', 'our', 'ours', 'you', 'your', 'yours',
|
|
16
|
+
'he', 'him', 'his', 'she', 'her', 'hers', 'it', 'its', 'they', 'them',
|
|
17
|
+
'their', 'theirs', 'that', 'this', 'these', 'those', 'what', 'which',
|
|
18
|
+
'who', 'whom', 'whose', 'when', 'where', 'why', 'how', 'all', 'each',
|
|
19
|
+
'every', 'both', 'few', 'more', 'most', 'other', 'some', 'such', 'no',
|
|
20
|
+
'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's',
|
|
21
|
+
't', 'just', 'don', 'now', 'about', 'above', 'after', 'again', 'against',
|
|
22
|
+
'am', 'at', 'before', 'behind', 'below', 'between', 'by', 'during',
|
|
23
|
+
'from', 'further', 'into', 'over', 'per', 'through',
|
|
24
|
+
'throughout', 'till', 'to', 'under', 'until', 'up', 'upon', 'via', 'with',
|
|
25
|
+
'within', 'without', 'as', 'if', 'or', 'but', 'and', 'out', 'of',
|
|
26
|
+
'for', 'in', 'on', 'any', 'got', 'here', 'there',
|
|
27
|
+
]);
|
|
28
|
+
|
|
5
29
|
export function hasExplicitFts5Operator(query: string): boolean {
|
|
6
30
|
return FTS5_OPERATOR_PATTERN.test(query.trim());
|
|
7
31
|
}
|
|
@@ -15,6 +39,10 @@ function collectNaturalLanguageTerms(query: string): string[] {
|
|
|
15
39
|
if (phrase === undefined && term && NATURAL_LANGUAGE_CONNECTORS.has(term.toLowerCase())) {
|
|
16
40
|
continue;
|
|
17
41
|
}
|
|
42
|
+
// Skip high-frequency stop words so they cannot dominate FTS5 ranking.
|
|
43
|
+
if (phrase === undefined && term && STOP_WORDS.has(term.toLowerCase())) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
18
46
|
|
|
19
47
|
const rawValue = phrase ?? term ?? '';
|
|
20
48
|
if (rawValue.length > 0) terms.push(rawValue);
|
|
@@ -67,6 +95,30 @@ function quoteTerms(terms: string[], separator: string): string {
|
|
|
67
95
|
return terms.map((term) => `"${term.replace(/"/g, '""')}"`).join(separator);
|
|
68
96
|
}
|
|
69
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Collect the raw terms of a query for literal (LIKE) fallback searches.
|
|
100
|
+
* Unlike normalizeFts5Query this does NOT drop stop words — a literal
|
|
101
|
+
* substring fallback is the last resort, and the original terms are what the
|
|
102
|
+
* user typed. Natural-language connectors are skipped because they never
|
|
103
|
+
* carry search intent.
|
|
104
|
+
*/
|
|
105
|
+
export function collectLikeTerms(query: string): string[] {
|
|
106
|
+
const terms: string[] = [];
|
|
107
|
+
|
|
108
|
+
for (const match of query.matchAll(FTS5_TOKEN_PATTERN)) {
|
|
109
|
+
const phrase = match[1];
|
|
110
|
+
const term = match[2];
|
|
111
|
+
if (phrase === undefined && term && NATURAL_LANGUAGE_CONNECTORS.has(term.toLowerCase())) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const rawValue = phrase ?? term ?? '';
|
|
116
|
+
if (rawValue.length > 0) terms.push(rawValue);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return terms;
|
|
120
|
+
}
|
|
121
|
+
|
|
70
122
|
/**
|
|
71
123
|
* Normalize a query as natural language even when it contains uppercase
|
|
72
124
|
* operator words. Queries like "DO NOT USE FIND /" are passed through as raw
|
|
@@ -97,5 +149,10 @@ export function buildNaturalLanguageFallbackQuery(query: string): string | null
|
|
|
97
149
|
export function isFts5QueryError(err: unknown): boolean {
|
|
98
150
|
if (!(err instanceof Error)) return false;
|
|
99
151
|
const msg = err.message.toLowerCase();
|
|
100
|
-
|
|
152
|
+
// NOTE: "malformed" and "sql logic error" are intentionally NOT matched here.
|
|
153
|
+
// They are corruption/recovery signals used by DatabaseManager (#186); swallowing
|
|
154
|
+
// them would suppress database quarantine/rebuild. Only genuine FTS5 query/index
|
|
155
|
+
// errors are considered recoverable search-path failures.
|
|
156
|
+
return msg.includes('fts5') ||
|
|
157
|
+
msg.includes('unterminated string');
|
|
101
158
|
}
|
|
@@ -40,6 +40,7 @@ import { canonicalMarkdownIdentity, withMarkdownMutationLock } from "./markdown-
|
|
|
40
40
|
|
|
41
41
|
const MAX_EXTERNAL_WRITE_RETRIES = 2;
|
|
42
42
|
const RECOVERY_ACTIVE_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
43
|
+
const RECOVERY_SNAPSHOT_MIN_INTERVAL_MS = 60 * 60 * 1000;
|
|
43
44
|
const RECOVERY_MAX_COUNT = 32;
|
|
44
45
|
const RECOVERY_MAX_BYTES = 64 * 1024 * 1024;
|
|
45
46
|
const RETIRED_RECOVERY_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
@@ -119,6 +120,9 @@ export class MemoryStore {
|
|
|
119
120
|
if (target === "failure") return this.config.memoryCharLimit * 2; // Failures get more space
|
|
120
121
|
return target === "user" ? this.config.userCharLimit : this.config.memoryCharLimit;
|
|
121
122
|
}
|
|
123
|
+
private get capEnforced(): boolean {
|
|
124
|
+
return this.config.memoryMode !== "policy-only";
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
private charCount(target: "memory" | "user" | "failure"): number {
|
|
124
128
|
const entries = this.entriesFor(target);
|
|
@@ -190,10 +194,11 @@ export class MemoryStore {
|
|
|
190
194
|
toolState?: string;
|
|
191
195
|
correctedTo?: string;
|
|
192
196
|
project?: string;
|
|
197
|
+
signal?: AbortSignal;
|
|
193
198
|
}): Promise<MemoryResult> {
|
|
194
199
|
const failureText = this.buildFailureMemoryText(content, options);
|
|
195
200
|
return this.addWithConsolidation(
|
|
196
|
-
"failure", failureText,
|
|
201
|
+
"failure", failureText, options.signal, 1, "Failure memory saved: " + options.category, options.project,
|
|
197
202
|
);
|
|
198
203
|
}
|
|
199
204
|
|
|
@@ -244,7 +249,7 @@ export class MemoryStore {
|
|
|
244
249
|
const encoded = this.encodeEntry(content, today, today, project);
|
|
245
250
|
|
|
246
251
|
const newTotal = [...entries, encoded].join(ENTRY_DELIMITER).length;
|
|
247
|
-
if (newTotal > limit) {
|
|
252
|
+
if (this.capEnforced && newTotal > limit) {
|
|
248
253
|
this.overflowSince[target] ??= Date.now();
|
|
249
254
|
const strategy = this.memoryOverflowStrategy();
|
|
250
255
|
|
|
@@ -276,6 +281,7 @@ export class MemoryStore {
|
|
|
276
281
|
const result = await this.runTargetMutation(
|
|
277
282
|
target,
|
|
278
283
|
(markMutation) => this._add(target, content, signal, addedMessage, project, markMutation),
|
|
284
|
+
signal,
|
|
279
285
|
);
|
|
280
286
|
if (
|
|
281
287
|
result.success
|
|
@@ -374,7 +380,7 @@ export class MemoryStore {
|
|
|
374
380
|
async applyMutationPlan(
|
|
375
381
|
target: "memory" | "user" | "failure",
|
|
376
382
|
operations: MemoryMutationOperation[],
|
|
377
|
-
options: { requireShrink?: boolean } = {},
|
|
383
|
+
options: { requireShrink?: boolean; signal?: AbortSignal } = {},
|
|
378
384
|
): Promise<MemoryResult> {
|
|
379
385
|
return this.runTargetMutation(target, async (markMutation) => {
|
|
380
386
|
await this.syncTargetFromDiskIfChanged(target);
|
|
@@ -440,7 +446,7 @@ export class MemoryStore {
|
|
|
440
446
|
|
|
441
447
|
const originalTotal = originalEntries.join(ENTRY_DELIMITER).length;
|
|
442
448
|
const plannedTotal = plannedEntries.join(ENTRY_DELIMITER).length;
|
|
443
|
-
if (plannedTotal > this.charLimit(target)) {
|
|
449
|
+
if (this.capEnforced && plannedTotal > this.charLimit(target)) {
|
|
444
450
|
return {
|
|
445
451
|
success: false,
|
|
446
452
|
error: `Memory mutation plan would put memory at ${plannedTotal}/${this.charLimit(target)} chars.`,
|
|
@@ -457,13 +463,14 @@ export class MemoryStore {
|
|
|
457
463
|
await this.saveToDisk(target);
|
|
458
464
|
markMutation();
|
|
459
465
|
return this.successResponse(target, `Applied ${operations.length} memory operations atomically.`);
|
|
460
|
-
});
|
|
466
|
+
}, options.signal);
|
|
461
467
|
}
|
|
462
468
|
|
|
463
|
-
async replace(target: "memory" | "user" | "failure", oldText: string, newContent: string): Promise<MemoryResult> {
|
|
469
|
+
async replace(target: "memory" | "user" | "failure", oldText: string, newContent: string, signal?: AbortSignal): Promise<MemoryResult> {
|
|
464
470
|
return this.runTargetMutation(
|
|
465
471
|
target,
|
|
466
472
|
(markMutation) => this.replaceUnlocked(target, oldText, newContent, markMutation),
|
|
473
|
+
signal,
|
|
467
474
|
);
|
|
468
475
|
}
|
|
469
476
|
|
|
@@ -506,7 +513,7 @@ export class MemoryStore {
|
|
|
506
513
|
|
|
507
514
|
const newTotal = testEntries.join(ENTRY_DELIMITER).length;
|
|
508
515
|
|
|
509
|
-
if (newTotal > this.charLimit(target)) {
|
|
516
|
+
if (this.capEnforced && newTotal > this.charLimit(target)) {
|
|
510
517
|
return {
|
|
511
518
|
success: false,
|
|
512
519
|
error: `Replacement would put memory at ${newTotal}/${this.charLimit(target)} chars. Shorten or remove other entries first.`,
|
|
@@ -520,10 +527,11 @@ export class MemoryStore {
|
|
|
520
527
|
return this.successResponse(target, "Entry replaced.");
|
|
521
528
|
}
|
|
522
529
|
|
|
523
|
-
async remove(target: "memory" | "user" | "failure", oldText: string): Promise<MemoryResult> {
|
|
530
|
+
async remove(target: "memory" | "user" | "failure", oldText: string, signal?: AbortSignal): Promise<MemoryResult> {
|
|
524
531
|
return this.runTargetMutation(
|
|
525
532
|
target,
|
|
526
533
|
(markMutation) => this.removeUnlocked(target, oldText, markMutation),
|
|
534
|
+
signal,
|
|
527
535
|
);
|
|
528
536
|
}
|
|
529
537
|
|
|
@@ -839,10 +847,14 @@ export class MemoryStore {
|
|
|
839
847
|
private async runTargetMutation(
|
|
840
848
|
target: "memory" | "user" | "failure",
|
|
841
849
|
mutation: (markMutation: () => void) => Promise<MemoryResult>,
|
|
850
|
+
signal?: AbortSignal,
|
|
842
851
|
): Promise<MemoryResult> {
|
|
843
852
|
const storagePath = await this.resolveStoragePath(target);
|
|
844
853
|
return withMarkdownMutationLock(storagePath, async () => {
|
|
845
854
|
for (let attempt = 0; ; attempt++) {
|
|
855
|
+
if (signal?.aborted) {
|
|
856
|
+
return { success: false, error: "Memory mutation cancelled." };
|
|
857
|
+
}
|
|
846
858
|
let mutated = false;
|
|
847
859
|
try {
|
|
848
860
|
const result = await mutation(() => {
|
|
@@ -914,6 +926,15 @@ export class MemoryStore {
|
|
|
914
926
|
}
|
|
915
927
|
throw error;
|
|
916
928
|
}
|
|
929
|
+
} else if (await this.shouldReuseRecoverySnapshot(filePath)) {
|
|
930
|
+
try {
|
|
931
|
+
await fs.rename(tmpPath, filePath);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
934
|
+
throw new ExternalMemoryWriteConflict();
|
|
935
|
+
}
|
|
936
|
+
throw error;
|
|
937
|
+
}
|
|
917
938
|
} else {
|
|
918
939
|
const recoveryPath = this.recoveryPathFor(filePath);
|
|
919
940
|
const publishedIdentity = await this.fileIdentity(tmpPath);
|
|
@@ -1048,6 +1069,37 @@ export class MemoryStore {
|
|
|
1048
1069
|
);
|
|
1049
1070
|
}
|
|
1050
1071
|
|
|
1072
|
+
private async shouldReuseRecoverySnapshot(filePath: string): Promise<boolean> {
|
|
1073
|
+
const directory = path.dirname(filePath);
|
|
1074
|
+
const escapedName = path.basename(filePath).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1075
|
+
const uuidPattern = "[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
|
|
1076
|
+
const recoveryPattern = new RegExp(`^\\.${escapedName}\\.recovery-\\d+-${uuidPattern}$`, "i");
|
|
1077
|
+
try {
|
|
1078
|
+
const names = await fs.readdir(directory);
|
|
1079
|
+
const mtimes = await Promise.all(
|
|
1080
|
+
names
|
|
1081
|
+
.filter((name) => recoveryPattern.test(name))
|
|
1082
|
+
.map(async (name) => {
|
|
1083
|
+
try {
|
|
1084
|
+
const state = await fs.lstat(path.join(directory, name));
|
|
1085
|
+
return state.isFile() ? state.mtimeMs : null;
|
|
1086
|
+
} catch {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
}),
|
|
1090
|
+
);
|
|
1091
|
+
let newestMtimeMs: number | null = null;
|
|
1092
|
+
for (const mtimeMs of mtimes) {
|
|
1093
|
+
if (mtimeMs !== null && (newestMtimeMs === null || mtimeMs > newestMtimeMs)) {
|
|
1094
|
+
newestMtimeMs = mtimeMs;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return newestMtimeMs !== null && Date.now() - newestMtimeMs < RECOVERY_SNAPSHOT_MIN_INTERVAL_MS;
|
|
1098
|
+
} catch {
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1051
1103
|
private retiredRecoveryPathFor(filePath: string): string {
|
|
1052
1104
|
return path.join(
|
|
1053
1105
|
path.dirname(filePath),
|
|
@@ -25,6 +25,8 @@ export interface BulkIndexResult {
|
|
|
25
25
|
messagesIndexed: number;
|
|
26
26
|
errors: string[];
|
|
27
27
|
reachedLimit?: boolean;
|
|
28
|
+
/** Session files skipped because their mtime is outside the retention window. */
|
|
29
|
+
expiredSkipped?: number;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
interface SessionFileMetadata {
|
|
@@ -36,6 +38,25 @@ interface SessionFileMetadata {
|
|
|
36
38
|
export interface IncrementalIndexOptions {
|
|
37
39
|
projectDir?: string;
|
|
38
40
|
maxFilesToIndex?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Optional retention cutoff (ms epoch). JSONL session files whose mtime is
|
|
43
|
+
* strictly older than this cutoff are considered outside the retained window
|
|
44
|
+
* and are skipped entirely (not queued for indexing or counted as changed).
|
|
45
|
+
* This keeps incremental backfill aligned with session retention pruning:
|
|
46
|
+
* sessions pruned by pruneOldSessions() are never re-indexed on a later
|
|
47
|
+
* startup. Omit/0 to index every file (backwards-compatible default).
|
|
48
|
+
*/
|
|
49
|
+
retentionCutoffMs?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* True when a session JSONL file's last-modified time falls within the
|
|
54
|
+
* retention window (mtime >= cutoff). Files older than the cutoff are treated
|
|
55
|
+
* as expired and are ineligible for backfill/indexing so that pruned sessions
|
|
56
|
+
* are not re-surfaced.
|
|
57
|
+
*/
|
|
58
|
+
function isWithinRetention(mtimeMs: number, retentionCutoffMs: number | undefined): boolean {
|
|
59
|
+
return !retentionCutoffMs || mtimeMs >= retentionCutoffMs;
|
|
39
60
|
}
|
|
40
61
|
|
|
41
62
|
export function truncateMessageContent(
|
|
@@ -339,21 +360,39 @@ function indexSessionFile(dbManager: DatabaseManager, file: string, result: Bulk
|
|
|
339
360
|
|
|
340
361
|
/**
|
|
341
362
|
* Index all sessions from disk.
|
|
363
|
+
* With retentionCutoffMs > 0, files whose mtime falls outside the window are
|
|
364
|
+
* skipped (counted in expiredSkipped) so a manual reindex honors the same
|
|
365
|
+
* retention policy the auto pruning enforces, instead of re-adding expired
|
|
366
|
+
* sessions that pruning just deleted.
|
|
342
367
|
*
|
|
343
368
|
* @param dbManager — Database manager instance
|
|
344
369
|
* @param sessionsDir — Path to ~/.selesai/agent/sessions/
|
|
345
370
|
* @param projectDir — Optional: specific project directory to index
|
|
371
|
+
* @param retentionCutoffMs — Optional: epoch ms; files modified before it are skipped
|
|
346
372
|
* @returns Bulk index result
|
|
347
373
|
*/
|
|
348
374
|
export function indexAllSessions(
|
|
349
375
|
dbManager: DatabaseManager,
|
|
350
376
|
sessionsDir: string,
|
|
351
|
-
projectDir?: string
|
|
377
|
+
projectDir?: string,
|
|
378
|
+
retentionCutoffMs = 0,
|
|
352
379
|
): BulkIndexResult {
|
|
353
380
|
const files = getSessionFiles(sessionsDir, projectDir);
|
|
354
381
|
const result = emptyBulkIndexResult();
|
|
382
|
+
let expiredSkipped = 0;
|
|
355
383
|
|
|
356
384
|
for (const file of files) {
|
|
385
|
+
if (retentionCutoffMs > 0) {
|
|
386
|
+
try {
|
|
387
|
+
if (!isWithinRetention(getSessionFileMetadata(file).mtimeMs, retentionCutoffMs)) {
|
|
388
|
+
expiredSkipped++;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
// Unreadable metadata: let indexSessionFile report the real error.
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
357
396
|
try {
|
|
358
397
|
indexSessionFile(dbManager, file, result);
|
|
359
398
|
} catch (err) {
|
|
@@ -361,6 +400,10 @@ export function indexAllSessions(
|
|
|
361
400
|
}
|
|
362
401
|
}
|
|
363
402
|
|
|
403
|
+
if (expiredSkipped > 0) {
|
|
404
|
+
result.expiredSkipped = expiredSkipped;
|
|
405
|
+
}
|
|
406
|
+
|
|
364
407
|
return result;
|
|
365
408
|
}
|
|
366
409
|
|
|
@@ -390,6 +433,12 @@ export function indexChangedSessions(
|
|
|
390
433
|
for (const file of files) {
|
|
391
434
|
try {
|
|
392
435
|
const metadata = getSessionFileMetadata(file);
|
|
436
|
+
if (!isWithinRetention(metadata.mtimeMs, options.retentionCutoffMs)) {
|
|
437
|
+
// Outside the retained window (e.g. pruned by pruneOldSessions):
|
|
438
|
+
// never re-queue it for indexing, so a pruned session does not come
|
|
439
|
+
// back on the next startup backfill.
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
393
442
|
if (storedSessionFileMatches(dbManager, metadata)) {
|
|
394
443
|
result.sessionsSkipped++;
|
|
395
444
|
continue;
|
|
@@ -443,25 +492,58 @@ function isRecentBackfillTimestamp(value: string | null, nowMs: number): boolean
|
|
|
443
492
|
* The check stays cheap: it compares file counts and stored file size/mtime
|
|
444
493
|
* metadata. Full JSONL parsing is left to the scheduled incremental backfill.
|
|
445
494
|
*/
|
|
446
|
-
export function needsBackfill(
|
|
495
|
+
export function needsBackfill(
|
|
496
|
+
dbManager: DatabaseManager,
|
|
497
|
+
sessionsDir: string,
|
|
498
|
+
now = new Date(),
|
|
499
|
+
retentionCutoffMs = 0,
|
|
500
|
+
): boolean {
|
|
447
501
|
const db = dbManager.getDb();
|
|
448
502
|
const files = getSessionFiles(sessionsDir);
|
|
449
503
|
const indexed = db.prepare('SELECT COUNT(*) as count FROM sessions').get() as { count: number };
|
|
450
504
|
|
|
451
|
-
if (
|
|
452
|
-
|
|
505
|
+
if (retentionCutoffMs <= 0) {
|
|
506
|
+
// Retention disabled: keep the historical cheap path — a plain file-count
|
|
507
|
+
// vs row-count comparison decides before any per-file stat work, so
|
|
508
|
+
// large session directories stay fast on startup.
|
|
509
|
+
if (files.length > indexed.count) {
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
for (const file of files) {
|
|
514
|
+
try {
|
|
515
|
+
const metadata = getSessionFileMetadata(file);
|
|
516
|
+
if (storedSessionFileMatches(dbManager, metadata)) continue;
|
|
517
|
+
return true;
|
|
518
|
+
} catch {
|
|
519
|
+
// An unreadable or malformed session file still needs indexing.
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return !isRecentBackfillTimestamp(getLastBackfillTimestamp(dbManager), now.getTime());
|
|
453
525
|
}
|
|
454
526
|
|
|
527
|
+
// Retention enabled: one metadata pass decides everything — a retained file
|
|
528
|
+
// with stale stored metadata demands a backfill, and when no file is inside
|
|
529
|
+
// the window there is no work at all. Returning here (instead of falling
|
|
530
|
+
// through to the periodic timestamp check) keeps an all-expired store from
|
|
531
|
+
// scheduling an empty backfill on every startup before a timestamp is ever
|
|
532
|
+
// written.
|
|
533
|
+
let hasRetainedFile = false;
|
|
455
534
|
for (const file of files) {
|
|
456
535
|
try {
|
|
457
536
|
const metadata = getSessionFileMetadata(file);
|
|
458
|
-
if (
|
|
459
|
-
|
|
537
|
+
if (!isWithinRetention(metadata.mtimeMs, retentionCutoffMs)) continue;
|
|
538
|
+
hasRetainedFile = true;
|
|
539
|
+
if (!storedSessionFileMatches(dbManager, metadata)) return true;
|
|
460
540
|
} catch {
|
|
461
541
|
return true;
|
|
462
542
|
}
|
|
463
543
|
}
|
|
464
|
-
|
|
544
|
+
if (!hasRetainedFile) {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
465
547
|
return !isRecentBackfillTimestamp(getLastBackfillTimestamp(dbManager), now.getTime());
|
|
466
548
|
}
|
|
467
549
|
|
|
@@ -509,3 +591,83 @@ export function getSessionStats(dbManager: DatabaseManager): {
|
|
|
509
591
|
projects,
|
|
510
592
|
};
|
|
511
593
|
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Compute the retention cutoff (ms epoch) from a retention window in days.
|
|
597
|
+
* Returns 0 (no cutoff) when retention is undefined/zero/disabled.
|
|
598
|
+
*/
|
|
599
|
+
export function retentionCutoffMs(retentionDays: number | undefined): number {
|
|
600
|
+
if (!retentionDays || retentionDays <= 0) return 0;
|
|
601
|
+
return Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Delete sessions outside the retention window, along with their messages,
|
|
606
|
+
* to bound the growth of the session index database (see #183).
|
|
607
|
+
*
|
|
608
|
+
* A session is eligible for pruning when its session file's last-mod time is
|
|
609
|
+
* older than the window (falling back to started_at when no file metadata
|
|
610
|
+
* exists). This deliberately mirrors the backfill eligibility check in
|
|
611
|
+
* `needsBackfill`/`indexChangedSessions` (also keyed to file mtime), so a
|
|
612
|
+
* pruned session's on-disk JSONL file is never re-indexed by a later startup
|
|
613
|
+
* and never re-triggers a backfill. Retention and backfill therefore agree on
|
|
614
|
+
* the same eligible file set.
|
|
615
|
+
*
|
|
616
|
+
* `messages` and `session_files` reference `sessions`, but only `session_files`
|
|
617
|
+
* is declared `ON DELETE CASCADE`. Orphaned `messages` rows are deleted
|
|
618
|
+
* explicitly first so a `PRAGMA foreign_keys`-enabled delete never trips a
|
|
619
|
+
* FK constraint, and so an accurate `messagesRemoved` count is reported.
|
|
620
|
+
*
|
|
621
|
+
* Returns the number of sessions and messages removed.
|
|
622
|
+
*/
|
|
623
|
+
export function pruneOldSessions(
|
|
624
|
+
dbManager: DatabaseManager,
|
|
625
|
+
retentionDays: number,
|
|
626
|
+
): { sessionsRemoved: number; messagesRemoved: number } {
|
|
627
|
+
const db = dbManager.getDb();
|
|
628
|
+
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
629
|
+
const cutoffIso = new Date(cutoffMs).toISOString();
|
|
630
|
+
|
|
631
|
+
// A session is pruned when it falls outside the retention window. We use the
|
|
632
|
+
// same signal the backfill eligibility uses -- the session file's last-mod
|
|
633
|
+
// time -- so that a pruned session's on-disk JSONL file is not re-indexed on
|
|
634
|
+
// a later startup. Sessions without file metadata (e.g. indexed before files
|
|
635
|
+
// were tracked) fall back to started_at. Both must be strictly older than
|
|
636
|
+
// the cutoff.
|
|
637
|
+
const eligibleSessionIds = db.prepare(`
|
|
638
|
+
SELECT s.id
|
|
639
|
+
FROM sessions s
|
|
640
|
+
LEFT JOIN session_files sf ON sf.session_id = s.id
|
|
641
|
+
WHERE
|
|
642
|
+
(sf.path IS NOT NULL AND sf.mtime_ms < ?)
|
|
643
|
+
OR (sf.path IS NULL AND s.started_at < ?)
|
|
644
|
+
`).all(cutoffMs, cutoffIso) as Array<{ id: string }>;
|
|
645
|
+
|
|
646
|
+
if (eligibleSessionIds.length === 0) {
|
|
647
|
+
return { sessionsRemoved: 0, messagesRemoved: 0 };
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Count messages upfront because SQLite does not surface cascade/orphan
|
|
651
|
+
// counts from DELETE ... RETURNING across tables.
|
|
652
|
+
const messageCount = db.prepare(
|
|
653
|
+
`SELECT COUNT(*) as cnt FROM messages WHERE session_id IN (${eligibleSessionIds.map(() => '?').join(',')})`,
|
|
654
|
+
).get(...eligibleSessionIds.map((r) => r.id)) as { cnt: number };
|
|
655
|
+
|
|
656
|
+
const prune = () => {
|
|
657
|
+
const delMessages = db.prepare(
|
|
658
|
+
`DELETE FROM messages WHERE session_id IN (${eligibleSessionIds.map(() => '?').join(',')})`,
|
|
659
|
+
).run(...eligibleSessionIds.map((r) => r.id));
|
|
660
|
+
const delSessions = db.prepare(
|
|
661
|
+
`DELETE FROM sessions WHERE id IN (${eligibleSessionIds.map(() => '?').join(',')})`,
|
|
662
|
+
).run(...eligibleSessionIds.map((r) => r.id));
|
|
663
|
+
return { messagesRemoved: delMessages.changes, sessionsRemoved: delSessions.changes };
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
const result = db.transaction ? db.transaction(prune)() : prune();
|
|
667
|
+
// Fall back to the pre-counted values if the transaction changed nothing
|
|
668
|
+
// unexpectedly (defensive; DELETE should always affect the counted rows).
|
|
669
|
+
return {
|
|
670
|
+
sessionsRemoved: result.sessionsRemoved || eligibleSessionIds.length,
|
|
671
|
+
messagesRemoved: result.messagesRemoved || messageCount.cnt,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
@@ -2,6 +2,7 @@ import { DatabaseManager } from './db.js';
|
|
|
2
2
|
import {
|
|
3
3
|
buildFallbackFts5Query,
|
|
4
4
|
buildNaturalLanguageFallbackQuery,
|
|
5
|
+
collectLikeTerms,
|
|
5
6
|
hasExplicitFts5Operator,
|
|
6
7
|
isFts5QueryError,
|
|
7
8
|
normalizeFts5Query,
|
|
@@ -38,30 +39,10 @@ type SearchMatch =
|
|
|
38
39
|
| { type: 'fts'; query: string }
|
|
39
40
|
| { type: 'like'; terms: string[] };
|
|
40
41
|
|
|
41
|
-
const QUERY_TOKEN_PATTERN = /"([^"]*)"|(\S+)/g;
|
|
42
|
-
const NATURAL_LANGUAGE_CONNECTORS = new Set(['and', 'or', 'not', 'near']);
|
|
43
|
-
|
|
44
42
|
function escapeLikePattern(text: string): string {
|
|
45
43
|
return text.replace(/[\\%_]/g, '\\$&');
|
|
46
44
|
}
|
|
47
45
|
|
|
48
|
-
function collectLikeTerms(query: string): string[] {
|
|
49
|
-
const terms: string[] = [];
|
|
50
|
-
|
|
51
|
-
for (const match of query.matchAll(QUERY_TOKEN_PATTERN)) {
|
|
52
|
-
const phrase = match[1];
|
|
53
|
-
const term = match[2];
|
|
54
|
-
if (phrase === undefined && term && NATURAL_LANGUAGE_CONNECTORS.has(term.toLowerCase())) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const rawValue = phrase ?? term ?? '';
|
|
59
|
-
if (rawValue.length > 0) terms.push(rawValue);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return terms;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
46
|
function mapRows(rows: Array<{
|
|
66
47
|
session_id: string;
|
|
67
48
|
project: string;
|
|
@@ -178,7 +159,12 @@ export function searchSessions(
|
|
|
178
159
|
|
|
179
160
|
const normalizedQuery = normalizeFts5Query(query);
|
|
180
161
|
if (normalizedQuery.length === 0) {
|
|
181
|
-
|
|
162
|
+
// Every term was a stop word or connector: nothing is left for FTS5 to
|
|
163
|
+
// match. Fall through to the scoped LIKE fallback instead of a hard [] so
|
|
164
|
+
// the query degrades to a literal substring search (the same fallback a
|
|
165
|
+
// normal query that matches nothing ends with).
|
|
166
|
+
const likeTerms = collectLikeTerms(query);
|
|
167
|
+
return executeSearch({ type: 'like', terms: likeTerms });
|
|
182
168
|
}
|
|
183
169
|
|
|
184
170
|
const exactResults = executeSearch({ type: 'fts', query: normalizedQuery });
|