@co0ontty/wand 4.106.0 → 4.106.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/ai-text-runner.js +24 -14
- package/dist/build-info.d.ts +2 -0
- package/dist/build-info.js +18 -0
- package/dist/build-info.json +3 -3
- package/dist/cli.js +8 -2
- package/dist/daemon-maintenance-state.js +1 -1
- package/dist/daemon-maintenance-targets.d.ts +1 -0
- package/dist/daemon-maintenance-targets.js +25 -9
- package/dist/daemon-maintenance.d.ts +3 -0
- package/dist/daemon-maintenance.js +60 -13
- package/dist/employee-im-types.d.ts +1 -1
- package/dist/employee-input-scheduling.d.ts +4 -0
- package/dist/employee-knowledge-cli.js +5 -5
- package/dist/employee-knowledge.d.ts +3 -1
- package/dist/employee-knowledge.js +24 -8
- package/dist/git-quick-commit.d.ts +2 -0
- package/dist/git-quick-commit.js +15 -2
- package/dist/git-worktree-batch.d.ts +26 -0
- package/dist/git-worktree-batch.js +139 -0
- package/dist/global-knowledge.d.ts +12 -0
- package/dist/global-knowledge.js +124 -0
- package/dist/iteration-log.d.ts +13 -5
- package/dist/iteration-log.js +120 -59
- package/dist/knowledge-types.d.ts +12 -1
- package/dist/knowledge-types.js +4 -0
- package/dist/middleware/api-scope.js +1 -0
- package/dist/model-groups.js +6 -3
- package/dist/models/parsers/opencode.js +31 -1
- package/dist/models.js +15 -14
- package/dist/npm-update-utils.d.ts +5 -2
- package/dist/npm-update-utils.js +34 -4
- package/dist/opencode-cli.d.ts +6 -0
- package/dist/opencode-cli.js +51 -0
- package/dist/opencode-system-fallback.d.ts +1 -0
- package/dist/opencode-system-fallback.js +16 -4
- package/dist/pi-native-event-bridge.js +55 -3
- package/dist/pi-native-input.d.ts +22 -0
- package/dist/pi-native-input.js +77 -0
- package/dist/pi-tmux-host.d.ts +14 -5
- package/dist/pi-tmux-host.js +68 -17
- package/dist/process-manager.js +17 -3
- package/dist/pty-tmux-backend.d.ts +27 -2
- package/dist/pty-tmux-backend.js +471 -70
- package/dist/pty-tmux-log.d.ts +1 -1
- package/dist/pty-tmux-log.js +23 -2
- package/dist/retention-protection.d.ts +1 -1
- package/dist/retention-protection.js +3 -2
- package/dist/retention.d.ts +5 -0
- package/dist/retention.js +11 -0
- package/dist/server-knowledge-routes.js +2 -2
- package/dist/server-session-input-routes.js +38 -19
- package/dist/server-session-lifecycle-routes.js +14 -1
- package/dist/server-session-worktree-routes.js +105 -12
- package/dist/server-task-routes.js +41 -12
- package/dist/server-workspace-routes.js +77 -9
- package/dist/server.js +67 -35
- package/dist/session-registry.d.ts +17 -3
- package/dist/session-registry.js +66 -13
- package/dist/session-service.js +1 -1
- package/dist/storage/internal.d.ts +4 -3
- package/dist/storage/internal.js +3 -3
- package/dist/storage/knowledge-repository.d.ts +5 -0
- package/dist/storage/knowledge-repository.js +75 -16
- package/dist/storage/mappers.d.ts +1 -0
- package/dist/storage/mappers.js +6 -1
- package/dist/storage/migrations.js +60 -0
- package/dist/storage/session-input-repository.d.ts +19 -0
- package/dist/storage/session-input-repository.js +35 -0
- package/dist/storage/task-workflow-repository.d.ts +29 -0
- package/dist/storage/task-workflow-repository.js +110 -0
- package/dist/storage/tasks-repository.d.ts +6 -1
- package/dist/storage/tasks-repository.js +94 -34
- package/dist/storage/workspaces-repository.d.ts +7 -0
- package/dist/storage/workspaces-repository.js +28 -2
- package/dist/storage-cleanup.d.ts +7 -2
- package/dist/storage-cleanup.js +16 -0
- package/dist/storage-schema.js +2 -0
- package/dist/storage.d.ts +5 -1
- package/dist/storage.js +12 -0
- package/dist/structured-exec-host.d.ts +4 -0
- package/dist/structured-exec-pump.d.ts +1 -0
- package/dist/structured-exec-pump.js +18 -11
- package/dist/structured-opencode-adapter.d.ts +2 -1
- package/dist/structured-opencode-adapter.js +32 -8
- package/dist/structured-pi-adapter.js +13 -2
- package/dist/structured-runner.d.ts +6 -0
- package/dist/structured-session-manager-replay.d.ts +2 -0
- package/dist/structured-session-manager-replay.js +27 -0
- package/dist/structured-session-manager.d.ts +32 -21
- package/dist/structured-session-manager.js +395 -118
- package/dist/structured-tmux-host.d.ts +23 -2
- package/dist/structured-tmux-host.js +143 -20
- package/dist/task-actions.js +5 -3
- package/dist/task-archive-policy.js +2 -1
- package/dist/task-comment-dispatch.d.ts +2 -1
- package/dist/task-comment-dispatch.js +19 -6
- package/dist/task-execution-queue.d.ts +2 -0
- package/dist/task-execution-queue.js +10 -10
- package/dist/task-hierarchy.js +1 -1
- package/dist/task-read-model.d.ts +1 -1
- package/dist/task-read-model.js +2 -1
- package/dist/task-types.d.ts +34 -3
- package/dist/task-types.js +21 -5
- package/dist/task-workflow-cli.d.ts +2 -0
- package/dist/task-workflow-cli.js +29 -0
- package/dist/task-workflow-runner.d.ts +4 -0
- package/dist/task-workflow-runner.js +36 -0
- package/dist/task-workflow.d.ts +13 -0
- package/dist/task-workflow.js +48 -0
- package/dist/terminal-daemon-server.js +12 -4
- package/dist/tmux-policy.d.ts +9 -0
- package/dist/tmux-policy.js +46 -0
- package/dist/tmux-terminal-host.js +5 -2
- package/dist/tui/commands.js +2 -1
- package/dist/types.d.ts +5 -0
- package/dist/wand-task-sync.js +5 -0
- package/dist/web-ui/content/ai-teams.js +9 -9
- package/dist/web-ui/content/module-manifest.json +1 -1
- package/dist/web-ui/content/modules/{52574891ef08e29b → 014fa5c6f973badf}/host.js +3 -3
- package/dist/web-ui/content/modules/0818b1d31a11d006/employee-profile.js +1 -0
- package/dist/web-ui/content/modules/{31157ceb6d23b809 → 0a58d9ec273078e4}/chunk.js +3 -3
- package/dist/web-ui/content/modules/{bf3ee58e0da44822 → 0a979dcd165cd0cc}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{5f298a8f43dd0589 → 0c143928e8f96abc}/chunk.js +1 -1
- package/dist/web-ui/content/modules/0e9e42a319f6b8fc/workspace-window.js +1 -0
- package/dist/web-ui/content/modules/{b713cad51ca4c512 → 0f0d1769b732c0bd}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{4970319a83432ec7 → 1447886e6a1e74a4}/chunk.js +1 -1
- package/dist/web-ui/content/modules/151c734cbca31da5/host.js +1 -0
- package/dist/web-ui/content/modules/{e5174201f9d918fb → 16e4ab9c5263fcfd}/chunk.js +1 -1
- package/dist/web-ui/content/modules/17e752fb53952753/host.js +1 -0
- package/dist/web-ui/content/modules/{913187c92a98a1ac → 20ed1454fc09fa6f}/chunk.js +1 -1
- package/dist/web-ui/content/modules/255a3baf59d2b734/home.js +6 -0
- package/dist/web-ui/content/modules/{2f801e53dda62770 → 296dd81886c42b45}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{8891ca8165435ca1 → 2a01f4e525c0725e}/chunk.js +1 -1
- package/dist/web-ui/content/modules/2ab4409c82b44fba/markdown.js +1 -0
- package/dist/web-ui/content/modules/2c955eced11610c2/chunk.js +1 -0
- package/dist/web-ui/content/modules/2d7ee4f9e2c0aca4/panel.js +1 -0
- package/dist/web-ui/content/modules/{3f8830f8ad28decc → 33ae550e4a295c85}/chunk.js +1 -1
- package/dist/web-ui/content/modules/35f4aa4443696bd0/chunk.js +1 -0
- package/dist/web-ui/content/modules/388b37ffcce9d4ff/host.js +1 -0
- package/dist/web-ui/content/modules/39279604dcdac7c7/runtime.js +338 -0
- package/dist/web-ui/content/modules/{57c357d321db5df9 → 3a3cdff2a662b7d3}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{d49f106c4d106057 → 3ab646be55514243}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{16dab1297efd71a7 → 3ae88a5c5e1949d2}/chunk.js +1 -1
- package/dist/web-ui/content/modules/3e3b0e71d6ce80d8/chunk.js +1 -0
- package/dist/web-ui/content/modules/{ec33e17c00c1eb24 → 434aaaaf9604dc34}/chunk.js +1 -1
- package/dist/web-ui/content/modules/4385cd48771e1d4f/host.js +1 -0
- package/dist/web-ui/content/modules/{face3dbf117d63e3 → 4638129d60f549a0}/chunk.js +1 -1
- package/dist/web-ui/content/modules/47407209931b7011/host.js +13 -0
- package/dist/web-ui/content/modules/4829dd712d8b366b/chunk.js +1 -0
- package/dist/web-ui/content/modules/488d5b747bf374c8/chunk.js +1 -0
- package/dist/web-ui/content/modules/{ed7405f064944215 → 4ab085c195f7d76a}/sha256.js +1 -1
- package/dist/web-ui/content/modules/{f27a5d3c6c02a71c → 4c485bd44740fec7}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{771439a41e737ee2 → 4ca0494308ab2bb3}/chunk.js +1 -1
- package/dist/web-ui/content/modules/4ec571218779ba3e/composer-surface-adapter.js +1 -0
- package/dist/web-ui/content/modules/{5933ce8965aa4004 → 5023bf0294c2e7cb}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{e63582a3bce48f7d → 5456e3417600bf15}/chunk.js +1 -1
- package/dist/web-ui/content/modules/55c588b0f5b41a52/host-bridge.js +1 -0
- package/dist/web-ui/content/modules/{9d941d6ff39861b8 → 5c3c8237ba7d8460}/chunk.js +1 -1
- package/dist/web-ui/content/modules/624b858da9834757/host.js +1 -0
- package/dist/web-ui/content/modules/6290be936320d4b1/host.js +1 -0
- package/dist/web-ui/content/modules/66be408793048cc3/progress.js +1 -0
- package/dist/web-ui/content/modules/67a11176a563317b/chunk.js +1 -0
- package/dist/web-ui/content/modules/{e060508ecd50fa7e → 68ef6ec7d26d78cd}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{41445c6589dba325 → 6ec3e41df31927d9}/host.js +2 -2
- package/dist/web-ui/content/modules/{97d7f267959fd385 → 6f10f369445bbcbe}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{d47d75a78ae598e3 → 7295d9ff92d5e1e7}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{f383d892fef73483 → 72cd613de1a5f69b}/chunk.js +1 -1
- package/dist/web-ui/content/modules/73aae1daafeea7e7/workspaces-panel.js +4 -0
- package/dist/web-ui/content/modules/{c2f09c8a472f5d83 → 73d2689f13b9a3e9}/dialog-modal.js +1 -1
- package/dist/web-ui/content/modules/{2f97ddba3842adaa → 75cf0c048e9b92ab}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{4ba1e8c74bed7ef2 → 75dabda3b141296e}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{f3cff7792ba39df8 → 76c10b58d5f92d02}/login-visual.js +1 -1
- package/dist/web-ui/content/modules/{b63e075f222a1266 → 783583a12f5010f8}/library-bridge.js +1 -1
- package/dist/web-ui/content/modules/7e13a8a4a981f2f1/group-editor.js +1 -0
- package/dist/web-ui/content/modules/{0d76e582a7a71b2f → 7e3034fea34c6dae}/chunk.js +1 -1
- package/dist/web-ui/content/modules/80bf3291f2577a68/host.js +1 -0
- package/dist/web-ui/content/modules/{cba6f13c0b2aec1c → 82c3e08f34a6b828}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{dedd4a19bb70e327 → 83d1f4cf82c65f19}/page.js +3 -3
- package/dist/web-ui/content/modules/844f97958f25240c/shell-file-panel-drawer.js +1 -0
- package/dist/web-ui/content/modules/{62e0023451f428cf → 899cdc467044d418}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{5083a5dbb5b155b7 → 8c7ca1fe72741c07}/chunk.js +1 -1
- package/dist/web-ui/content/modules/8dae4ca2261525e4/task-board-host.js +27 -0
- package/dist/web-ui/content/modules/92195c14c74929da/list-filter-panel.js +1 -0
- package/dist/web-ui/content/modules/{a61393bfa976d8d5 → 93793696af76bcbf}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{db521a246e14e5e4 → 97cd6af3da5622b8}/chunk.js +4 -4
- package/dist/web-ui/content/modules/{8de8368570ab089d → 9d3179258249ec77}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{579d486763d47c0d → a234c484078c0568}/chunk.js +1 -1
- package/dist/web-ui/content/modules/a4d215448a089a7d/host.js +1 -0
- package/dist/web-ui/content/modules/{c0a446100d969f3e → ae003f89901c5687}/chunk.js +1 -1
- package/dist/web-ui/content/modules/ae712bdd45b9917e/employee-card.js +1 -0
- package/dist/web-ui/content/modules/{a2caed0139df4b21 → b2a15293f7965d09}/chunk.js +1 -1
- package/dist/web-ui/content/modules/b4ee65e9998f0088/host.js +61 -0
- package/dist/web-ui/content/modules/{bd3daf04a8bf4422 → b666137d17f83be9}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{831b6c7b43e800be → b66beb6c0185256b}/host.js +1 -1
- package/dist/web-ui/content/modules/{816eb9b3a000fb2c → bb03b17e877657de}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{1eab7410d5586038 → bc343afc991db1a1}/chunk.js +1 -1
- package/dist/web-ui/content/modules/bca45976ff8fe2ac/host.js +69 -0
- package/dist/web-ui/content/modules/{d434edbb2f4672e5 → c2715dbfc9eaf510}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{d6fffe2c2a3a738d → c30c220281f35d74}/toast-region.js +1 -1
- package/dist/web-ui/content/modules/c4cc46fa9b4adddc/dropdown-surface.js +1 -0
- package/dist/web-ui/content/modules/c6dd19ee3d3ffd59/brand-identity.js +1 -0
- package/dist/web-ui/content/modules/c7ea6a114e6ff8ae/host.js +1 -0
- package/dist/web-ui/content/modules/{a59734be368daa7e → c916d82c8acef9c8}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{182929e188c1ab3a → cceaedda619a737a}/host.js +1 -1
- package/dist/web-ui/content/modules/cf67f52b076b9d2d/panel-host.js +1 -0
- package/dist/web-ui/content/modules/{9e163d54ff8fd1aa → d26c86558d422313}/chunk.js +1 -1
- package/dist/web-ui/content/modules/d2b8c6c6f3706032/queue-view-adapter.js +1 -0
- package/dist/web-ui/content/modules/d52ebe2ef12a9fa8/chunk.js +3 -0
- package/dist/web-ui/content/modules/{3fe11a75284ad8ae → d552b3bff2151eb6}/host.js +2 -2
- package/dist/web-ui/content/modules/d5d6b4511ac76a6e/chunk.js +3 -0
- package/dist/web-ui/content/modules/{bd7dc4bb46731ee7 → df42e151d625463b}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{2a791cd2e14aa1e0 → e0a04f5ae56c68aa}/notice-view.js +1 -1
- package/dist/web-ui/content/modules/{438807b8bf112cd0 → e28e607c95d6f63a}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{50da446c1377c220 → e3880df97ea62d0c}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{87c7f50d9455d55e → e66196f1ece3e3ad}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{9b12c2301f3a18f3 → eb76307444ac9d71}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{c33935155289c22d → ebff7ca22cee1468}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{4861eca9a8a87ad0 → ed1c712c569960d6}/chunk.js +1 -1
- package/dist/web-ui/content/modules/ee8b9c2d4bec63d4/chunk.js +1 -0
- package/dist/web-ui/content/modules/f458a194ce4208e2/chunk.js +1 -0
- package/dist/web-ui/content/modules/{11af5d14b30e57b6 → f938e826420cfe1b}/chunk.js +1 -1
- package/dist/web-ui/content/modules/fa7b9bf6cc84bfc1/chat-render.js +81 -0
- package/dist/web-ui/content/modules/{ea8fe4b9999fd2aa → fd1fb1834f5f06df}/chunk.js +1 -1
- package/dist/web-ui/content/modules/fd3084e562a3dbd6/host.js +1 -0
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +416 -413
- package/dist/web-ui/embedded-assets.js +5 -5
- package/dist/web-ui/page-route.js +2 -2
- package/dist/web-ui/react/workspaces/types.d.ts +3 -0
- package/dist/web-ui/thinking-efforts.d.ts +2 -0
- package/dist/web-ui/thinking-efforts.js +8 -0
- package/package.json +1 -2
- package/scripts/stage-render-binaries.js +0 -1
- package/skills/wand-knowledge/SKILL.md +5 -3
- package/dist/structured-bounded-keys.d.ts +0 -19
- package/dist/structured-bounded-keys.js +0 -47
- package/dist/web-ui/content/modules/00b168a19664439e/dropdown-surface.js +0 -1
- package/dist/web-ui/content/modules/059c5fb7f128d340/task-board-host.js +0 -27
- package/dist/web-ui/content/modules/07494f5a623e4471/chat-render.js +0 -76
- package/dist/web-ui/content/modules/0a743ad3b4db6874/chunk.js +0 -3
- package/dist/web-ui/content/modules/1858670c0f84fec5/employee-profile.js +0 -1
- package/dist/web-ui/content/modules/20c47dc0442f5a04/host.js +0 -1
- package/dist/web-ui/content/modules/226241b999d12008/host.js +0 -1
- package/dist/web-ui/content/modules/23c4b8d223ec64c5/host.js +0 -61
- package/dist/web-ui/content/modules/2882c97388ff4f7b/employee-card.js +0 -1
- package/dist/web-ui/content/modules/2c90c3be260e6cce/progress.js +0 -1
- package/dist/web-ui/content/modules/2d7be9935394f397/workspace-window.js +0 -1
- package/dist/web-ui/content/modules/389a5112332ec23a/host.js +0 -1
- package/dist/web-ui/content/modules/3ea13e8b3c2deaa4/panel-host.js +0 -1
- package/dist/web-ui/content/modules/42e5c34002fcffb7/chunk.js +0 -1
- package/dist/web-ui/content/modules/4a5d669c90f545cb/host.js +0 -1
- package/dist/web-ui/content/modules/5705d25468274534/host.js +0 -1
- package/dist/web-ui/content/modules/5961ab2947c86ca1/shell-file-panel-drawer.js +0 -1
- package/dist/web-ui/content/modules/5b6e1369df7dea69/chunk.js +0 -1
- package/dist/web-ui/content/modules/6787bdcf56f743a5/host.js +0 -1
- package/dist/web-ui/content/modules/67c39b0398186a9f/host.js +0 -1
- package/dist/web-ui/content/modules/6b16f35c0627bafb/chunk.js +0 -1
- package/dist/web-ui/content/modules/6dd4f3e2f619f7a0/chunk.js +0 -1
- package/dist/web-ui/content/modules/70a2e13e0ccc8549/panel.js +0 -1
- package/dist/web-ui/content/modules/710db590e9707f73/runtime.js +0 -338
- package/dist/web-ui/content/modules/7249950f8771dbeb/workspaces-panel.js +0 -4
- package/dist/web-ui/content/modules/87dcd516ffd9f688/composer-surface-adapter.js +0 -1
- package/dist/web-ui/content/modules/8b7cd7bffdfd69e7/brand-identity.js +0 -1
- package/dist/web-ui/content/modules/8bf9917f6c3429f6/host.js +0 -1
- package/dist/web-ui/content/modules/8e612d4881fa4118/home.js +0 -6
- package/dist/web-ui/content/modules/8e6178262ea63dec/markdown.js +0 -1
- package/dist/web-ui/content/modules/96f2ae970f8c576b/host.js +0 -1
- package/dist/web-ui/content/modules/ba89cd97627e6682/queue-view-adapter.js +0 -1
- package/dist/web-ui/content/modules/c351eb11e0bdec2c/sidebar-peek.js +0 -1
- package/dist/web-ui/content/modules/ca7ca5902a35d26e/chunk.js +0 -1
- package/dist/web-ui/content/modules/d5d28ec375826ef2/chunk.js +0 -3
- package/dist/web-ui/content/modules/d6b16dedb9011c33/chunk.js +0 -1
- package/dist/web-ui/content/modules/d8a7011760f61719/group-editor.js +0 -1
- package/dist/web-ui/content/modules/d9e2b65e9679ad39/host.js +0 -75
- package/dist/web-ui/content/modules/eac72f69716dba05/chunk.js +0 -1
- package/dist/web-ui/content/modules/ebc53dedc3945514/host.js +0 -1
- package/dist/web-ui/content/modules/f43da7050badf54c/host.js +0 -1
- package/dist/web-ui/content/modules/fb2356eced24c4cc/host-bridge.js +0 -1
|
@@ -97,6 +97,13 @@ function sortTaskDirectoryGroups(groups, order) {
|
|
|
97
97
|
const global = Number(Boolean(right.global)) - Number(Boolean(left.global));
|
|
98
98
|
if (global)
|
|
99
99
|
return global;
|
|
100
|
+
const leftHasContent = (left.tasks?.length ?? 0) > 0 || (left.standaloneSessions?.length ?? 0) > 0;
|
|
101
|
+
const rightHasContent = (right.tasks?.length ?? 0) > 0 || (right.standaloneSessions?.length ?? 0) > 0;
|
|
102
|
+
if (leftHasContent !== rightHasContent)
|
|
103
|
+
return rightHasContent ? 1 : -1;
|
|
104
|
+
const activity = compareCreatedDesc(left.latestActivityAt ?? left.createdAt, right.latestActivityAt ?? right.createdAt);
|
|
105
|
+
if (activity)
|
|
106
|
+
return activity;
|
|
100
107
|
const created = compareCreatedDesc(left.createdAt, right.createdAt);
|
|
101
108
|
if (created)
|
|
102
109
|
return created;
|
|
@@ -133,6 +140,8 @@ function rememberCreatedAt(target, iso) {
|
|
|
133
140
|
return;
|
|
134
141
|
if (!target.createdAt || value < target.createdAt)
|
|
135
142
|
target.createdAt = value;
|
|
143
|
+
if (!target.latestActivityAt || value > target.latestActivityAt)
|
|
144
|
+
target.latestActivityAt = value;
|
|
136
145
|
}
|
|
137
146
|
function parseDefaultProvider(value) {
|
|
138
147
|
return isSessionProvider(value) ? value : undefined;
|
|
@@ -466,7 +475,7 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
466
475
|
cleanupWorktreeSync(task.worktree);
|
|
467
476
|
const card = storage.getWandTaskByWorkspaceTaskId(task.id);
|
|
468
477
|
if (card)
|
|
469
|
-
storage.
|
|
478
|
+
storage.archiveTaskForWorkspaceRemoval(card.id);
|
|
470
479
|
}
|
|
471
480
|
storage.deleteWorkspace(existing.id, { cascade });
|
|
472
481
|
res.json({ ok: true });
|
|
@@ -558,6 +567,47 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
558
567
|
}
|
|
559
568
|
res.json(result);
|
|
560
569
|
}));
|
|
570
|
+
// Sections are layout containers, independent of task-board metadata.
|
|
571
|
+
app.post("/api/workspace-sections", (req, res) => {
|
|
572
|
+
try {
|
|
573
|
+
const name = typeof req.body?.name === "string" ? req.body.name.trim() : "";
|
|
574
|
+
if (!name)
|
|
575
|
+
throw badRequest("请输入分区名称。");
|
|
576
|
+
const cwd = resolveWorkspaceCwd(req.body?.cwd);
|
|
577
|
+
const workspaceId = typeof req.body?.workspaceId === "string" ? req.body.workspaceId : "";
|
|
578
|
+
const workspace = workspaceId ? storage.getWorkspace(workspaceId)
|
|
579
|
+
: storage.listWorkspaces().find(candidate => !isGlobalWorkspace(candidate)
|
|
580
|
+
&& normalizeProjectCwd(candidate.cwd) === normalizeProjectCwd(cwd)) ?? storage.ensureGlobalWorkspace();
|
|
581
|
+
if (!workspace)
|
|
582
|
+
throw badRequest("未找到工作区。");
|
|
583
|
+
if (!isGlobalWorkspace(workspace) && normalizeProjectCwd(workspace.cwd) !== normalizeProjectCwd(cwd)) {
|
|
584
|
+
throw badRequest("分区目录与工作区不一致。");
|
|
585
|
+
}
|
|
586
|
+
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId.trim() : undefined;
|
|
587
|
+
const section = storage.createWorkspaceSection({ workspaceId: workspace.id, name, cwd, sessionId });
|
|
588
|
+
if (sessionId)
|
|
589
|
+
sessions?.refreshSessionWorkspace(sessionId);
|
|
590
|
+
res.status(201).json({ ...section, sessions: [], isolated: false });
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
sendRouteError(res, error, "无法创建分区。");
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
app.post("/api/workspace-sections/unassign", (req, res) => {
|
|
597
|
+
try {
|
|
598
|
+
const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId.trim() : "";
|
|
599
|
+
if (!storage.getSessionSlim(sessionId)) {
|
|
600
|
+
res.status(404).json({ error: "未找到会话。" });
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
storage.moveSessionToWorkspaceTask(sessionId, null);
|
|
604
|
+
sessions?.refreshSessionWorkspace(sessionId);
|
|
605
|
+
res.json({ ok: true });
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
sendRouteError(res, error, "无法移回工作区。");
|
|
609
|
+
}
|
|
610
|
+
});
|
|
561
611
|
// 两个创建入口只在项目来源上不同;独立任务挂到隐藏全局空间。
|
|
562
612
|
// 默认只创建逻辑分组,仅显式 worktree:true 才创建隔离目录。
|
|
563
613
|
app.post(["/api/tasks", "/api/workspaces/:id/tasks"], asyncRoute(async (req, res) => {
|
|
@@ -601,7 +651,7 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
601
651
|
const snapshots = sessions?.listSlim();
|
|
602
652
|
const revision = crypto.createHash("sha256").update(JSON.stringify({
|
|
603
653
|
storage: storage.projectionRevision(),
|
|
604
|
-
projection: "workspace-work-sessions-
|
|
654
|
+
projection: "workspace-work-sessions-v3-task-status",
|
|
605
655
|
query: { includeArchived, workspaceFilter, taskLimit, sessionLimit },
|
|
606
656
|
runtime: snapshots,
|
|
607
657
|
})).digest("base64url");
|
|
@@ -682,17 +732,19 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
682
732
|
target.tasks.push({
|
|
683
733
|
...task,
|
|
684
734
|
boardTaskId: board?.id ?? null,
|
|
735
|
+
boardTaskStatus: board?.status ?? null,
|
|
685
736
|
...(board ? model.execution(board) : taskExecutionProjection(visibleSessions)),
|
|
686
737
|
parentWorkspaceTaskId: parent?.workspaceTaskId ?? null,
|
|
687
|
-
canArchive: (() => { const card = model.cardsByContainer.get(task.id); return card ? model.canArchive(card) : true; })(),
|
|
738
|
+
canArchive: task.kind !== "section" && (() => { const card = model.cardsByContainer.get(task.id); return card ? model.canArchive(card) : true; })(),
|
|
688
739
|
...model.milestone(task.milestoneId),
|
|
689
740
|
cwd: taskRuntimeCwd(task, workspace),
|
|
690
741
|
isolated: task.worktree !== null,
|
|
691
742
|
sessions,
|
|
692
743
|
totalSessions: visibleSessions.length,
|
|
693
744
|
});
|
|
694
|
-
|
|
695
|
-
|
|
745
|
+
rememberCreatedAt(target, task.createdAt);
|
|
746
|
+
for (const session of visibleSessions)
|
|
747
|
+
rememberCreatedAt(target, session.startedAt);
|
|
696
748
|
};
|
|
697
749
|
for (const workspace of visibleWorkspaces) {
|
|
698
750
|
const base = groups.get(workspace.id);
|
|
@@ -788,8 +840,17 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
788
840
|
group.standaloneSessions.push(summarize(session, {
|
|
789
841
|
workspaceName: group.workspaceName,
|
|
790
842
|
}));
|
|
791
|
-
|
|
792
|
-
|
|
843
|
+
rememberCreatedAt(group, session.startedAt);
|
|
844
|
+
}
|
|
845
|
+
// 目录内的任务与会话排序:新创建的必须在上面(严格按创建/启动时间倒序)。
|
|
846
|
+
for (const group of groups.values()) {
|
|
847
|
+
group.tasks.sort((left, right) => compareCreatedDesc(left.createdAt, right.createdAt));
|
|
848
|
+
for (const task of group.tasks) {
|
|
849
|
+
if (Array.isArray(task.sessions)) {
|
|
850
|
+
task.sessions.sort((left, right) => compareCreatedDesc(left.startedAt, right.startedAt));
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
group.standaloneSessions.sort((left, right) => compareCreatedDesc(left.startedAt, right.startedAt));
|
|
793
854
|
}
|
|
794
855
|
const orderedGroups = [...groups.values()]
|
|
795
856
|
.filter((group) => !group.global || group.tasks.length > 0 || group.standaloneSessions.length > 0);
|
|
@@ -993,6 +1054,10 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
993
1054
|
res.status(404).json({ error: "未找到该任务。" });
|
|
994
1055
|
return;
|
|
995
1056
|
}
|
|
1057
|
+
if (existing.kind === "section") {
|
|
1058
|
+
res.status(409).json({ error: "分区不需要归档,可删除分区并保留会话。" });
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
996
1061
|
const card = storage.getWandTaskByWorkspaceTaskId(existing.id);
|
|
997
1062
|
if (card && !canArchiveTask(storage, sessions, card.id)) {
|
|
998
1063
|
res.status(409).json({ error: "任务尚未完成或仍有执行,完成后才能归档。" });
|
|
@@ -1018,16 +1083,19 @@ export function registerWorkspaceRoutes(app, storage, sessions, titleOptions = {
|
|
|
1018
1083
|
}
|
|
1019
1084
|
// 删除隔离任务必然会移除其 cwd,因此即使调用方没有显式传 cascade,
|
|
1020
1085
|
// 也必须同步删除会话;非隔离任务仍保留旧的“默认解绑”API 语义。
|
|
1021
|
-
const cascade = req.query.cascade === "1"
|
|
1086
|
+
const cascade = existing.kind !== "section" && (req.query.cascade === "1"
|
|
1022
1087
|
|| req.query.cascade === "true"
|
|
1023
|
-
|| existing.worktree !== null;
|
|
1088
|
+
|| existing.worktree !== null);
|
|
1024
1089
|
if (cascade) {
|
|
1025
1090
|
deleteSessions(storage, sessions, storage.listSessionsByWorkspaceTaskSlim(existing.id).map((session) => session.id));
|
|
1026
1091
|
}
|
|
1027
1092
|
// 尽力清理 worktree 与分支;失败不阻塞删除任务行。
|
|
1028
1093
|
if (cascade)
|
|
1029
1094
|
cleanupWorktreeSync(existing.worktree);
|
|
1095
|
+
const retained = !cascade ? storage.listSessionsByWorkspaceTaskSlim(existing.id).map(session => session.id) : [];
|
|
1030
1096
|
storage.deleteWorkspaceTask(existing.id, { cascade });
|
|
1097
|
+
for (const id of retained)
|
|
1098
|
+
sessions?.refreshSessionWorkspace(id);
|
|
1031
1099
|
res.json({ ok: true });
|
|
1032
1100
|
});
|
|
1033
1101
|
// 保存任务的标签 / 分屏布局
|
package/dist/server.js
CHANGED
|
@@ -16,6 +16,7 @@ import { AuthService, principalHasScope, } from "./auth.js";
|
|
|
16
16
|
import { authenticateBearerAppToken, createRequireAuth, getEffectivePassword, requireAdmin as requireAdminScope, requireAdminOrSessionPreferences, requireScope, } from "./middleware/auth.js";
|
|
17
17
|
import { createApiScopeMiddleware } from "./middleware/api-scope.js";
|
|
18
18
|
import { createExtensionCors } from "./middleware/cors.js";
|
|
19
|
+
import { RUNNING_BUILD_INFO as BUILD_INFO } from "./build-info.js";
|
|
19
20
|
import { ensureCertificates } from "./cert.js";
|
|
20
21
|
import { getDefaultModelForProvider, getProviderDefaultModels, resolveConfigDir, writePreferenceToStorage, } from "./config.js";
|
|
21
22
|
import { ModelCatalogService } from "./models.js";
|
|
@@ -83,6 +84,7 @@ import { registerClaudeHistoryRoutes, registerSessionRoutes } from "./server-ses
|
|
|
83
84
|
import { registerWorkspaceRoutes } from "./server-workspace-routes.js";
|
|
84
85
|
import { backfillSessionWorkspaces } from "./workspace-binding.js";
|
|
85
86
|
import { getErrorMessage } from "./error-utils.js";
|
|
87
|
+
import { stopIterationPrompts, whenIterationPromptsSettled } from "./iteration-log.js";
|
|
86
88
|
import { asyncRoute, jsonErrorHandler } from "./express-async.js";
|
|
87
89
|
import { FeedbackReceiver, FeedbackReporter } from "./feedback.js";
|
|
88
90
|
import { feedbackFeatureForApiPath } from "./feedback-feature-map.js";
|
|
@@ -111,8 +113,10 @@ import { registerDaemonMaintenanceRoutes } from "./server-daemon-maintenance-rou
|
|
|
111
113
|
import { terminalDaemonBuildIsCurrent } from "./terminal-daemon-build.js";
|
|
112
114
|
import { createUpgradeAwareStructuredHost } from "./render-structured-host.js";
|
|
113
115
|
import { PiTmuxHost } from "./pi-tmux-host.js";
|
|
116
|
+
import { PtyTmuxBackend } from "./pty-tmux-backend.js";
|
|
114
117
|
import { StructuredTmuxHost } from "./structured-tmux-host.js";
|
|
115
|
-
import {
|
|
118
|
+
import { logInfo } from "./runtime-log.js";
|
|
119
|
+
import { probeTmuxSupport, tmuxEnabled } from "./tmux-policy.js";
|
|
116
120
|
import { structuredHostMaintenance } from "./structured-host-maintenance.js";
|
|
117
121
|
import { createTmuxTerminalHost } from "./tmux-terminal-host.js";
|
|
118
122
|
import { updateProviderClis, verifyProviderCliUpdateResults, } from "./provider-cli-updater.js";
|
|
@@ -140,31 +144,13 @@ async function checkLatestPackageVersion(channel, forceRefresh = false) {
|
|
|
140
144
|
}
|
|
141
145
|
// The immutable running build is also what the UI reports. Shared package metadata may
|
|
142
146
|
// belong to a different installer; it must not make a local Beta look like an older stable.
|
|
143
|
-
const info = await checkPackageUpdateAsync(DISPLAY_VERSION, channel);
|
|
147
|
+
const info = await checkPackageUpdateAsync(DISPLAY_VERSION, channel, undefined, BUILD_INFO);
|
|
144
148
|
if (info.latest) {
|
|
145
149
|
packageUpdateCache.set(channel, { info, timestamp: now });
|
|
146
150
|
}
|
|
147
151
|
return info;
|
|
148
152
|
}
|
|
149
153
|
// ── Build info (构建时打入的 commit SHA) + Beta 通道 ──
|
|
150
|
-
/** 读取 dist/build-info.json(由 scripts/stamp-build-info.js 在 build 时生成)。 */
|
|
151
|
-
function readBuildInfo() {
|
|
152
|
-
try {
|
|
153
|
-
const raw = readFileSync(path.join(SERVER_MODULE_DIR, "build-info.json"), "utf8");
|
|
154
|
-
const j = JSON.parse(raw);
|
|
155
|
-
return {
|
|
156
|
-
commit: typeof j.commit === "string" && j.commit ? j.commit : null,
|
|
157
|
-
builtAt: typeof j.builtAt === "string" && j.builtAt ? j.builtAt : null,
|
|
158
|
-
version: typeof j.version === "string" && j.version ? j.version : null,
|
|
159
|
-
channel: typeof j.channel === "string" && j.channel ? j.channel : null,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
// dev(tsx 跑 src/)或老版本(无此文件)时降级为全 null。
|
|
164
|
-
return { commit: null, builtAt: null, version: null, channel: null };
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
const BUILD_INFO = readBuildInfo();
|
|
168
154
|
const DISPLAY_VERSION = BUILD_INFO.version || PKG_VERSION;
|
|
169
155
|
const SERVER_INSTANCE_ID = crypto.randomUUID();
|
|
170
156
|
// ── Startup error handling ──
|
|
@@ -308,8 +294,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
308
294
|
const ensured = formatManagedSkillsEnsureLog(await ensureManagedSkills(configPath));
|
|
309
295
|
if (ensured)
|
|
310
296
|
process.stdout.write(ensured);
|
|
297
|
+
storage.syncGlobalKnowledge();
|
|
311
298
|
}
|
|
312
299
|
let notifyFreeDefaultChanged = (_employeeId) => { };
|
|
300
|
+
let collectNativeRunArtifacts = null;
|
|
301
|
+
let collectPtyTmuxArtifacts = null;
|
|
313
302
|
const systemFallback = testMode || process.env.WAND_DISABLE_OPENCODE_FALLBACK === "1" ? undefined : new OpenCodeFreeModelsService({ configDir,
|
|
314
303
|
storage, inheritEnv: config.inheritEnv, onLog: line => process.stdout.write(`[wand] ${line}\n`),
|
|
315
304
|
onModelsChanged(models) {
|
|
@@ -352,11 +341,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
352
341
|
freedBytes: results.reduce((total, result) => total + result.freedBytes, 0),
|
|
353
342
|
};
|
|
354
343
|
},
|
|
355
|
-
// Pi 原生回合目录(pi-native
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
344
|
+
// Pi 原生回合目录(pi-native/)和 structured-tmux 按回合数增长,交给同一轮定时回收。
|
|
345
|
+
// 只保护未归档会话。已删除或已归档的 slot 在 ack 完成、进程退出后整槽删除。
|
|
346
|
+
// 宿主在下面才包好,这里先留空,避免清理闭包引用尚未赋值的变量。
|
|
347
|
+
collectNativeArtifacts: () => collectNativeRunArtifacts?.() ?? null,
|
|
348
|
+
collectPtyTmuxArtifacts: () => collectPtyTmuxArtifacts?.() ?? null,
|
|
360
349
|
// 旧版本遗留的迁移前快照:无上限堆积。迁移成功路径已在 storage-schema 修剪。
|
|
361
350
|
pruneMigrationSnapshots: () => pruneMigrationBackups(databasePath, { dryRun: false }) });
|
|
362
351
|
const localModels = new LocalModelSetupService({ configDir, decisions,
|
|
@@ -391,6 +380,15 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
391
380
|
const tmuxTerminals = options.terminalHost
|
|
392
381
|
? { host: ptyHosts.host, client: null, configPath, maintenanceTarget: undefined }
|
|
393
382
|
: await createTmuxTerminalHost(configPath, ptyHosts.host);
|
|
383
|
+
if (tmuxEnabled()) {
|
|
384
|
+
const probe = probeTmuxSupport();
|
|
385
|
+
if (probe.ok) {
|
|
386
|
+
logInfo(`tmux runtime active (${probe.version}, ${probe.path}); terminal and structured CLI runs hosted.`);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
wandWarn(`tmux 执行底座检查未通过: ${probe.error}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
394
392
|
const daemonAdmission = new DaemonAdmission();
|
|
395
393
|
const terminalHost = daemonAdmission.terminal(tmuxTerminals.host);
|
|
396
394
|
let decisionRuntime = null;
|
|
@@ -404,17 +402,43 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
404
402
|
?? (ptyHosts.host instanceof TerminalDaemonClient ? ptyHosts.host : null);
|
|
405
403
|
const structuredHosts = await createUpgradeAwareStructuredHost(configPath, legacyStructuredHost, config.structured?.processHost);
|
|
406
404
|
let structuredHost = structuredHosts.host;
|
|
405
|
+
let structuredTmuxHost = null;
|
|
407
406
|
if (structuredHost?.persistent) {
|
|
408
407
|
const enabled = tmuxEnabled();
|
|
409
|
-
|
|
408
|
+
structuredTmuxHost = new StructuredTmuxHost(configDir, structuredHost, enabled);
|
|
410
409
|
// Keep both routes available after disabling new tmux launches: existing
|
|
411
410
|
// native and generic runs still belong to their original persistent owner.
|
|
412
|
-
structuredHost = new PiTmuxHost(configDir,
|
|
411
|
+
structuredHost = new PiTmuxHost(configDir, structuredTmuxHost, enabled && process.env.WAND_PI_NATIVE_TMUX !== "0");
|
|
413
412
|
}
|
|
414
413
|
const structuredSessions = new StructuredSessionManager(storage, config, structuredLogger, {}, structuredHost ? daemonAdmission.structured(structuredHost) : undefined, () => decisionRuntime, openRouter, () => autoAssignEvaluate, webLoginTickets);
|
|
415
414
|
// No SDK prewarm: Pi defaults to CLI. Only an explicit Wand Agent selection
|
|
416
415
|
// probes the in-process harness; CLI startup/settings must not depend on it.
|
|
417
416
|
const sessionRegistry = new SessionRegistry(processes, structuredSessions, storage);
|
|
417
|
+
collectNativeRunArtifacts = () => {
|
|
418
|
+
const activeSessionIds = new Set(sessionRegistry.listSlim().filter((session) => !session.archived).map((session) => session.id));
|
|
419
|
+
const retentionMs = STORAGE_CLEANUP_RETENTION_DAYS * 24 * 60 * 60 * 1000;
|
|
420
|
+
const hosts = [structuredHost, structuredTmuxHost].filter((host) => Boolean(host?.collectGarbage));
|
|
421
|
+
let removedSlots = 0;
|
|
422
|
+
let removedTurns = 0;
|
|
423
|
+
let freedBytes = 0;
|
|
424
|
+
let ran = false;
|
|
425
|
+
for (const host of hosts) {
|
|
426
|
+
const part = host.collectGarbage?.({ liveSessionIds: activeSessionIds, activeSessionIds, retentionMs });
|
|
427
|
+
if (!part)
|
|
428
|
+
continue;
|
|
429
|
+
ran = true;
|
|
430
|
+
removedSlots += part.removedSlots;
|
|
431
|
+
removedTurns += part.removedTurns;
|
|
432
|
+
freedBytes += part.freedBytes;
|
|
433
|
+
}
|
|
434
|
+
const report = ran ? { removedSlots, removedTurns, freedBytes } : null;
|
|
435
|
+
return report;
|
|
436
|
+
};
|
|
437
|
+
collectPtyTmuxArtifacts = () => {
|
|
438
|
+
const activeSessionIds = new Set(sessionRegistry.listSlim().filter((session) => !session.archived).map((session) => session.id));
|
|
439
|
+
const retentionMs = STORAGE_CLEANUP_RETENTION_DAYS * 24 * 60 * 60 * 1000;
|
|
440
|
+
return PtyTmuxBackend.collectGarbage(configDir, { activeSessionIds, retentionMs });
|
|
441
|
+
};
|
|
418
442
|
// 员工任务并发闸门:所有员工派发共用同一实例,排队状态随存储持久化。
|
|
419
443
|
const taskExecutionQueue = new TaskExecutionQueue({
|
|
420
444
|
storage, config, structured: structuredSessions, processes, sessions: sessionRegistry,
|
|
@@ -912,14 +936,14 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
912
936
|
while (structuredSessions.getCoreTurnStatus().hasActiveTurns) {
|
|
913
937
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
914
938
|
}
|
|
915
|
-
// Do not dispose tools/services
|
|
916
|
-
//
|
|
939
|
+
// Do not dispose tools/services while a native turn is still running.
|
|
940
|
+
// The same barrier covers manual and auto restart.
|
|
917
941
|
const plan = computeRelaunch({
|
|
918
942
|
serviceInstalled: safeServiceInstalled(),
|
|
919
943
|
globalCli: resolveGlobalWandCli(),
|
|
920
944
|
});
|
|
921
|
-
|
|
922
|
-
|
|
945
|
+
// Accepted iteration records can need the bounded Git compatibility retry;
|
|
946
|
+
// a force-exit timer here would discard them before close() drains the queue.
|
|
923
947
|
await close();
|
|
924
948
|
if (plan.mode === "spawn") {
|
|
925
949
|
spawn(process.execPath, [plan.bin ?? "", ...(plan.args ?? [])], {
|
|
@@ -964,6 +988,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
964
988
|
await new Promise((resolve, reject) => {
|
|
965
989
|
const cleanupFailedListen = async () => {
|
|
966
990
|
shuttingDown = true;
|
|
991
|
+
stopIterationPrompts(storage);
|
|
967
992
|
dingtalk.dispose();
|
|
968
993
|
dingtalkDws.dispose();
|
|
969
994
|
employeeImLifecycle.dispose();
|
|
@@ -1008,17 +1033,20 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1008
1033
|
authService.dispose();
|
|
1009
1034
|
taskExecutionQueue.dispose();
|
|
1010
1035
|
try {
|
|
1036
|
+
await whenIterationPromptsSettled(storage);
|
|
1037
|
+
}
|
|
1038
|
+
finally {
|
|
1011
1039
|
storage.close();
|
|
1012
1040
|
}
|
|
1013
|
-
catch { /* noop */ }
|
|
1014
1041
|
};
|
|
1015
1042
|
const onListenError = (err) => {
|
|
1016
1043
|
feedback.capture("startup_failed", err);
|
|
1017
1044
|
feedback.stop();
|
|
1018
1045
|
server.off("error", onListenError);
|
|
1046
|
+
const startupError = err.code === "EADDRINUSE" ? new PortInUseError(config.port, config.host) : err;
|
|
1019
1047
|
void cleanupFailedListen().then(() => {
|
|
1020
|
-
reject(
|
|
1021
|
-
}, reject);
|
|
1048
|
+
reject(startupError);
|
|
1049
|
+
}, cleanupError => reject(new AggregateError([startupError, cleanupError], "Server startup rollback failed")));
|
|
1022
1050
|
};
|
|
1023
1051
|
server.once("error", onListenError);
|
|
1024
1052
|
server.listen(config.port, config.host, () => {
|
|
@@ -1082,6 +1110,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1082
1110
|
storage,
|
|
1083
1111
|
sessions: sessionRegistry,
|
|
1084
1112
|
taskRetention: () => config.taskRetention,
|
|
1113
|
+
releaseArchivedProviderHistory: (sessionIds) => sessionRegistry.releaseArchivedProviderHistory(sessionIds),
|
|
1085
1114
|
};
|
|
1086
1115
|
const sweepRetention = createRetentionSweep(retentionDeps);
|
|
1087
1116
|
runTaskRetentionSweep = () => {
|
|
@@ -1244,6 +1273,7 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1244
1273
|
return closePromise;
|
|
1245
1274
|
closePromise = (async () => {
|
|
1246
1275
|
shuttingDown = true;
|
|
1276
|
+
stopIterationPrompts(storage);
|
|
1247
1277
|
feedback.stop();
|
|
1248
1278
|
if (activeFeedback === feedback)
|
|
1249
1279
|
activeFeedback = null;
|
|
@@ -1343,9 +1373,11 @@ export async function startServer(config, configPath, options = {}) {
|
|
|
1343
1373
|
// can never retain or call a closed storage instance.
|
|
1344
1374
|
authService.dispose();
|
|
1345
1375
|
try {
|
|
1376
|
+
await whenIterationPromptsSettled(storage);
|
|
1377
|
+
}
|
|
1378
|
+
finally {
|
|
1346
1379
|
storage.close();
|
|
1347
1380
|
}
|
|
1348
|
-
catch { /* ignore */ }
|
|
1349
1381
|
}
|
|
1350
1382
|
})();
|
|
1351
1383
|
return closePromise;
|
|
@@ -53,13 +53,27 @@ export declare class SessionRegistry {
|
|
|
53
53
|
setSessionThinkingEffort(id: string, effort: SessionSnapshot["thinkingEffort"]): SessionSnapshot | null;
|
|
54
54
|
setSessionMode(id: string, mode: ExecutionMode): SessionSnapshot | null;
|
|
55
55
|
setSessionTopic(id: string, title: string, description: string): SessionSnapshot | null;
|
|
56
|
+
/** 停掉空闲 PTY 壳。保留期只对「进程还在、但没有正在处理的回合」调用。 */
|
|
57
|
+
stop(id: string): SessionSnapshot | null;
|
|
56
58
|
/**
|
|
57
|
-
* 归档 /
|
|
59
|
+
* 归档 / 取消归档一个会话:不杀进程。归档后这次对话不再续接,
|
|
60
|
+
* 立刻删掉 CLI 原生历史,避免 ~/.pi、~/.codex 这类目录继续堆积;
|
|
61
|
+
* 取消归档时把原生 id 移出隐藏名单,已有的 CLI 历史可以继续恢复。
|
|
58
62
|
* 会话可能只存在于 storage(重启后未加载),因此 storage-only 也要能改。
|
|
59
63
|
*/
|
|
60
|
-
/** 停掉空闲 PTY 壳。保留期只对「进程还在、但没有正在处理的回合」调用。 */
|
|
61
|
-
stop(id: string): SessionSnapshot | null;
|
|
62
64
|
setArchived(id: string, archived: boolean): SessionSnapshot | null;
|
|
65
|
+
/**
|
|
66
|
+
* 归档与 CLI 原生历史的对应关系。别的未归档会话还在用同一个原生 id 时不动。
|
|
67
|
+
* 删文件失败只记日志:归档标记已经写上,不能因为清理失败把请求打回去。
|
|
68
|
+
*/
|
|
69
|
+
private syncArchivedProviderHistory;
|
|
70
|
+
/**
|
|
71
|
+
* 清掉这些已归档会话独占的 CLI 原生历史,并记入隐藏名单。
|
|
72
|
+
* 同一个原生 id 仍被未归档会话使用时跳过。重复调用只是再删一次已不存在的文件。
|
|
73
|
+
*/
|
|
74
|
+
releaseArchivedProviderHistory(sessionIds: readonly string[]): void;
|
|
75
|
+
/** 另一个未归档会话是否还拿着同一个 CLI 原生 id。 */
|
|
76
|
+
private activeNativeOwner;
|
|
63
77
|
updateWorktreeState(id: string, status: SessionSnapshot["worktreeMergeStatus"], info: SessionSnapshot["worktreeMergeInfo"]): SessionSnapshot | null;
|
|
64
78
|
private deleteManagedSession;
|
|
65
79
|
/** All destructive paths, including retention, remove native session artifacts. */
|
package/dist/session-registry.js
CHANGED
|
@@ -259,16 +259,18 @@ export class SessionRegistry {
|
|
|
259
259
|
return this.processes.setSessionTopic(id, title, description);
|
|
260
260
|
return this.updateStored(id, (snapshot) => ({ ...snapshot, title, description, summary: description }));
|
|
261
261
|
}
|
|
262
|
-
/**
|
|
263
|
-
* 归档 / 取消归档一个会话:只写标记,不杀进程、不删历史;真正的清理留给保留期扫描。
|
|
264
|
-
* 会话可能只存在于 storage(重启后未加载),因此 storage-only 也要能改。
|
|
265
|
-
*/
|
|
266
262
|
/** 停掉空闲 PTY 壳。保留期只对「进程还在、但没有正在处理的回合」调用。 */
|
|
267
263
|
stop(id) {
|
|
268
264
|
if (this.ownerOf(id) !== "pty")
|
|
269
265
|
return this.get(id);
|
|
270
266
|
return this.processes.stop(id);
|
|
271
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* 归档 / 取消归档一个会话:不杀进程。归档后这次对话不再续接,
|
|
270
|
+
* 立刻删掉 CLI 原生历史,避免 ~/.pi、~/.codex 这类目录继续堆积;
|
|
271
|
+
* 取消归档时把原生 id 移出隐藏名单,已有的 CLI 历史可以继续恢复。
|
|
272
|
+
* 会话可能只存在于 storage(重启后未加载),因此 storage-only 也要能改。
|
|
273
|
+
*/
|
|
272
274
|
setArchived(id, archived) {
|
|
273
275
|
const current = this.getLatest(id);
|
|
274
276
|
if (current && !!current.archived === archived && (!archived || current.archivedAt))
|
|
@@ -277,15 +279,66 @@ export class SessionRegistry {
|
|
|
277
279
|
throw Object.assign(new Error("会话仍在执行或等待处理,完成后才能归档。"), { statusCode: 409 });
|
|
278
280
|
}
|
|
279
281
|
const owner = this.ownerOf(id);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
282
|
+
const updated = owner === "structured"
|
|
283
|
+
? this.structured.setSessionArchived(id, archived)
|
|
284
|
+
: owner === "pty"
|
|
285
|
+
? this.processes.setSessionArchived(id, archived)
|
|
286
|
+
: this.updateStored(id, (snapshot) => ({
|
|
287
|
+
...snapshot,
|
|
288
|
+
archived,
|
|
289
|
+
archivedAt: archived ? new Date().toISOString() : null,
|
|
290
|
+
}));
|
|
291
|
+
if (updated)
|
|
292
|
+
this.syncArchivedProviderHistory(updated);
|
|
293
|
+
return updated;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 归档与 CLI 原生历史的对应关系。别的未归档会话还在用同一个原生 id 时不动。
|
|
297
|
+
* 删文件失败只记日志:归档标记已经写上,不能因为清理失败把请求打回去。
|
|
298
|
+
*/
|
|
299
|
+
syncArchivedProviderHistory(snapshot) {
|
|
300
|
+
if (!snapshot.archived) {
|
|
301
|
+
const providerSessionId = snapshot.claudeSessionId?.trim();
|
|
302
|
+
if (!providerSessionId)
|
|
303
|
+
return;
|
|
304
|
+
const provider = sessionProvider(snapshot);
|
|
305
|
+
const shared = this.activeNativeOwner(snapshot.id, provider, providerSessionId);
|
|
306
|
+
if (!shared)
|
|
307
|
+
removeHiddenSessionIds(this.storage, [providerSessionId]);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
this.releaseArchivedProviderHistory([snapshot.id]);
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* 清掉这些已归档会话独占的 CLI 原生历史,并记入隐藏名单。
|
|
314
|
+
* 同一个原生 id 仍被未归档会话使用时跳过。重复调用只是再删一次已不存在的文件。
|
|
315
|
+
*/
|
|
316
|
+
releaseArchivedProviderHistory(sessionIds) {
|
|
317
|
+
const wanted = new Set(sessionIds);
|
|
318
|
+
for (const snapshot of this.listSlim()) {
|
|
319
|
+
if (!wanted.has(snapshot.id) || !snapshot.archived)
|
|
320
|
+
continue;
|
|
321
|
+
const providerSessionId = snapshot.claudeSessionId?.trim();
|
|
322
|
+
if (!providerSessionId)
|
|
323
|
+
continue;
|
|
324
|
+
const provider = sessionProvider(snapshot);
|
|
325
|
+
if (this.activeNativeOwner(snapshot.id, provider, providerSessionId))
|
|
326
|
+
continue;
|
|
327
|
+
addHiddenSessionIds(this.storage, [providerSessionId]);
|
|
328
|
+
try {
|
|
329
|
+
deleteProviderNativeHistory(this.processes, provider, providerSessionId, snapshot.cwd);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
logInfo("[SessionCleanup] Archived CLI session cleanup could not complete; artifacts were preserved.");
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/** 另一个未归档会话是否还拿着同一个 CLI 原生 id。 */
|
|
337
|
+
activeNativeOwner(sessionId, provider, providerSessionId) {
|
|
338
|
+
return this.listSlim().some((other) => other.id !== sessionId
|
|
339
|
+
&& !other.archived
|
|
340
|
+
&& other.claudeSessionId?.trim() === providerSessionId
|
|
341
|
+
&& sessionProvider(other) === provider);
|
|
289
342
|
}
|
|
290
343
|
updateWorktreeState(id, status, info) {
|
|
291
344
|
const owner = this.ownerOf(id);
|
package/dist/session-service.js
CHANGED
|
@@ -55,7 +55,7 @@ export function getInputErrorResponse(error, sessionId) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
const errorCode = error?.code;
|
|
58
|
-
if (errorCode === "duplicate_queued_message" || errorCode === "duplicate_idempotency_key") {
|
|
58
|
+
if (errorCode === "duplicate_queued_message" || errorCode === "duplicate_idempotency_key" || errorCode === "idempotency_key_conflict") {
|
|
59
59
|
return {
|
|
60
60
|
statusCode: 409,
|
|
61
61
|
payload: {
|
|
@@ -44,13 +44,14 @@ export interface CreateWandTaskInput {
|
|
|
44
44
|
priority?: WandTask["priority"];
|
|
45
45
|
labels?: string[];
|
|
46
46
|
dueDate?: string | null;
|
|
47
|
+
cwd?: string | null;
|
|
47
48
|
milestoneId?: string | null;
|
|
48
49
|
agent?: WandTaskAgent | null;
|
|
49
50
|
executionSubject?: WandTask["executionSubject"];
|
|
50
51
|
}
|
|
51
|
-
export type WandTaskPatch = Partial<Pick<WandTask, "workspaceId" | "workspaceTaskId" | "parentTaskId" | "title" | "titleSource" | "autoTitleSignature" | "description" | "status" | "priority" | "labels" | "dueDate" | "milestoneId" | "sortOrder" | "agent" | "executionSubject" | "acceptance">>;
|
|
52
|
-
export declare const WAND_TASK_FIELDS = "id, identifier, workspace_id, workspace_task_id, parent_task_id,\n title, title_source, auto_title_signature, description, status, priority, labels_json,\n due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json";
|
|
53
|
-
export declare const WORKSPACE_TASK_PROJECTION = "SELECT wt.id,\n CASE WHEN card.id IS NULL THEN wt.workspace_id\n ELSE COALESCE(card.workspace_id, 'wand-global') END AS workspace_id,\n CASE WHEN card.id IS NULL THEN wt.name ELSE card.title END AS name,\n CASE WHEN card.id IS NULL THEN wt.status\n WHEN card.status
|
|
52
|
+
export type WandTaskPatch = Partial<Pick<WandTask, "workspaceId" | "workspaceTaskId" | "parentTaskId" | "title" | "titleSource" | "autoTitleSignature" | "description" | "status" | "priority" | "labels" | "dueDate" | "milestoneId" | "sortOrder" | "agent" | "executionSubject" | "acceptance" | "verification" | "deliveryRevision">>;
|
|
53
|
+
export declare const WAND_TASK_FIELDS = "id, identifier, workspace_id, workspace_task_id, parent_task_id,\n title, title_source, auto_title_signature, description, status, priority, labels_json,\n due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json, verification_json, delivery_revision";
|
|
54
|
+
export declare const WORKSPACE_TASK_PROJECTION = "SELECT wt.id,\n CASE WHEN card.id IS NULL THEN wt.workspace_id\n ELSE COALESCE(card.workspace_id, 'wand-global') END AS workspace_id,\n CASE WHEN card.id IS NULL THEN wt.name ELSE card.title END AS name,\n CASE WHEN card.id IS NULL THEN wt.status\n WHEN card.status = 'archived' THEN 'done' ELSE 'active' END AS status,\n CASE WHEN card.id IS NULL THEN wt.milestone_id ELSE card.milestone_id END AS milestone_id,\n wt.container_kind, wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,\n card.status AS card_status, wt.rowid AS _created_order\n FROM workspace_tasks wt LEFT JOIN wand_tasks card ON card.id = (\n SELECT id FROM wand_tasks WHERE workspace_task_id = wt.id\n ORDER BY updated_at DESC, rowid DESC LIMIT 1\n )";
|
|
54
55
|
export declare function withoutSessionTab(node: LayoutNode, sessionId: string): LayoutNode;
|
|
55
56
|
export declare function mapSiliconEmployeeRow(row: Record<string, unknown>): SiliconEmployee;
|
|
56
57
|
export declare function mapAiTeamRow(row: Record<string, unknown>): AiTeam;
|
package/dist/storage/internal.js
CHANGED
|
@@ -17,7 +17,7 @@ export function resolveDatabasePath(configPath) {
|
|
|
17
17
|
}
|
|
18
18
|
export const WAND_TASK_FIELDS = `id, identifier, workspace_id, workspace_task_id, parent_task_id,
|
|
19
19
|
title, title_source, auto_title_signature, description, status, priority, labels_json,
|
|
20
|
-
due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json`;
|
|
20
|
+
due_date, milestone_id, sort_order, agent_json, execution_subject_json, archived_at, created_at, updated_at, acceptance_json, verification_json, delivery_revision`;
|
|
21
21
|
// Shared metadata comes from the latest explicitly linked card, including meaningful NULLs.
|
|
22
22
|
// The old container columns remain readable for legacy rows until the startup migration.
|
|
23
23
|
export const WORKSPACE_TASK_PROJECTION = `SELECT wt.id,
|
|
@@ -25,9 +25,9 @@ export const WORKSPACE_TASK_PROJECTION = `SELECT wt.id,
|
|
|
25
25
|
ELSE COALESCE(card.workspace_id, '${GLOBAL_WORKSPACE_ID}') END AS workspace_id,
|
|
26
26
|
CASE WHEN card.id IS NULL THEN wt.name ELSE card.title END AS name,
|
|
27
27
|
CASE WHEN card.id IS NULL THEN wt.status
|
|
28
|
-
WHEN card.status
|
|
28
|
+
WHEN card.status = 'archived' THEN 'done' ELSE 'active' END AS status,
|
|
29
29
|
CASE WHEN card.id IS NULL THEN wt.milestone_id ELSE card.milestone_id END AS milestone_id,
|
|
30
|
-
wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,
|
|
30
|
+
wt.container_kind, wt.worktree_json, wt.layout_json, wt.cwd, wt.created_at, wt.last_opened_at, wt.layout_revision,
|
|
31
31
|
card.status AS card_status, wt.rowid AS _created_order
|
|
32
32
|
FROM workspace_tasks wt LEFT JOIN wand_tasks card ON card.id = (
|
|
33
33
|
SELECT id FROM wand_tasks WHERE workspace_task_id = wt.id
|
|
@@ -7,6 +7,10 @@ export declare class KnowledgeRepository {
|
|
|
7
7
|
private readonly s;
|
|
8
8
|
constructor(db: DatabaseSync, s: WandStorage);
|
|
9
9
|
knowledgeOwners(): {
|
|
10
|
+
global: {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
10
14
|
employees: Array<{
|
|
11
15
|
id: string;
|
|
12
16
|
name: string;
|
|
@@ -19,6 +23,7 @@ export declare class KnowledgeRepository {
|
|
|
19
23
|
};
|
|
20
24
|
readKnowledgeLibrary(scope: KnowledgeScope, ownerId: string, query?: string, accessToken?: string): KnowledgeLibrary;
|
|
21
25
|
getKnowledgeDocument(scope: KnowledgeScope, ownerId: string, id: string, accessToken?: string): KnowledgeDocument;
|
|
26
|
+
syncGlobalKnowledge(): void;
|
|
22
27
|
createKnowledgeDocument(scope: KnowledgeScope, ownerId: string, input: KnowledgeDocumentInput, accessToken?: string): {
|
|
23
28
|
entry: KnowledgeDocument;
|
|
24
29
|
created: boolean;
|