@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
package/dist/pty-tmux-log.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Executed inside tmux's pipe-pane process. No launch environment or prompt is persisted. */
|
|
2
|
-
export declare const ptyTmuxLogWriter = "\nconst fs=require('node:fs'),path=require('node:path');\nconst directory=process.argv[1];\nconst limit=1024*1024,keep=8;\nlet segment=0,size=0,carry=Buffer.alloc(0);\nlet fd=fs.openSync(path.join(directory,'0.chunk'),'a',0o600);\nprocess.stdin.on('data',data=>{\n const scan=Buffer.concat([carry,data]);\n const start=scan.lastIndexOf(Buffer.from('\\x1eWAND_CLI_EXIT:'));\n if(start>=0){const end=scan.indexOf(31,start);if(end>=0){\n const marker=scan.subarray(start,end+1);\n fs.writeFileSync(path.join(directory,'marker.tmp'),marker,{mode:0o600});\n fs.renameSync(path.join(directory,'marker.tmp'),path.join(directory,'marker'));\n }}\n carry=scan.subarray(Math.max(0,scan.length-512));\n for(let offset=0;offset<data.length;){\n if(size>=limit){fs.closeSync(fd);segment++;size=0;\n fd=fs.openSync(path.join(directory,segment+'.chunk'),'a',0o600);\n if(segment>=keep)fs.unlinkSync(path.join(directory,(segment-keep)+'.chunk'));\n }\n const length=Math.min(limit-size,data.length-offset);\n fs.writeSync(fd,data,offset,length);offset+=length;size+=length;\n }\n});\nprocess.stdin.on('end',()=>{fs.closeSync(fd);});\n";
|
|
2
|
+
export declare const ptyTmuxLogWriter = "\nconst fs=require('node:fs'),path=require('node:path'),net=require('node:net');\nconst directory=process.argv[1];\nconst sockPath=process.argv[2];\nconst limit=1024*1024,keep=8;\nlet segment=0,size=0,carry=Buffer.alloc(0);\nlet fd=fs.openSync(path.join(directory,'0.chunk'),'a',0o600);\nconst clients=new Set();\nlet server=null;\nif(sockPath){\n try{fs.unlinkSync(sockPath);}catch(e){}\n server=net.createServer(socket=>{\n try{socket.write(JSON.stringify({segment,size})+'\\n');}catch(e){}\n clients.add(socket);\n socket.on('close',()=>{clients.delete(socket);});\n socket.on('error',()=>{clients.delete(socket);try{socket.destroy();}catch(e){}});\n });\n server.listen(sockPath,()=>{try{fs.chmodSync(sockPath,0o600);}catch(e){}});\n process.on('exit',()=>{try{fs.unlinkSync(sockPath);}catch(e){}});\n}\nprocess.stdin.on('data',data=>{\n const scan=Buffer.concat([carry,data]);\n const start=scan.lastIndexOf(Buffer.from('\\x1eWAND_CLI_EXIT:'));\n if(start>=0){const end=scan.indexOf(31,start);if(end>=0){\n const marker=scan.subarray(start,end+1);\n fs.writeFileSync(path.join(directory,'marker.tmp'),marker,{mode:0o600});\n fs.renameSync(path.join(directory,'marker.tmp'),path.join(directory,'marker'));\n }}\n carry=scan.subarray(Math.max(0,scan.length-512));\n for(let offset=0;offset<data.length;){\n if(size>=limit){fs.closeSync(fd);segment++;size=0;\n fd=fs.openSync(path.join(directory,segment+'.chunk'),'a',0o600);\n if(segment>=keep)fs.unlinkSync(path.join(directory,(segment-keep)+'.chunk'));\n }\n const length=Math.min(limit-size,data.length-offset);\n fs.writeSync(fd,data,offset,length);offset+=length;size+=length;\n }\n for(const client of clients){\n try{client.write(data);}catch(e){}\n }\n});\nprocess.stdin.on('end',()=>{\n try{fs.closeSync(fd);}catch(e){}\n for(const client of clients){try{client.end();}catch(e){}}\n if(server){try{server.close(()=>{try{fs.unlinkSync(sockPath);}catch(e){}});}catch(e){}}\n});\n";
|
package/dist/pty-tmux-log.js
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
/** Executed inside tmux's pipe-pane process. No launch environment or prompt is persisted. */
|
|
2
2
|
export const ptyTmuxLogWriter = `
|
|
3
|
-
const fs=require('node:fs'),path=require('node:path');
|
|
3
|
+
const fs=require('node:fs'),path=require('node:path'),net=require('node:net');
|
|
4
4
|
const directory=process.argv[1];
|
|
5
|
+
const sockPath=process.argv[2];
|
|
5
6
|
const limit=1024*1024,keep=8;
|
|
6
7
|
let segment=0,size=0,carry=Buffer.alloc(0);
|
|
7
8
|
let fd=fs.openSync(path.join(directory,'0.chunk'),'a',0o600);
|
|
9
|
+
const clients=new Set();
|
|
10
|
+
let server=null;
|
|
11
|
+
if(sockPath){
|
|
12
|
+
try{fs.unlinkSync(sockPath);}catch(e){}
|
|
13
|
+
server=net.createServer(socket=>{
|
|
14
|
+
try{socket.write(JSON.stringify({segment,size})+'\\n');}catch(e){}
|
|
15
|
+
clients.add(socket);
|
|
16
|
+
socket.on('close',()=>{clients.delete(socket);});
|
|
17
|
+
socket.on('error',()=>{clients.delete(socket);try{socket.destroy();}catch(e){}});
|
|
18
|
+
});
|
|
19
|
+
server.listen(sockPath,()=>{try{fs.chmodSync(sockPath,0o600);}catch(e){}});
|
|
20
|
+
process.on('exit',()=>{try{fs.unlinkSync(sockPath);}catch(e){}});
|
|
21
|
+
}
|
|
8
22
|
process.stdin.on('data',data=>{
|
|
9
23
|
const scan=Buffer.concat([carry,data]);
|
|
10
24
|
const start=scan.lastIndexOf(Buffer.from('\\x1eWAND_CLI_EXIT:'));
|
|
@@ -22,6 +36,13 @@ process.stdin.on('data',data=>{
|
|
|
22
36
|
const length=Math.min(limit-size,data.length-offset);
|
|
23
37
|
fs.writeSync(fd,data,offset,length);offset+=length;size+=length;
|
|
24
38
|
}
|
|
39
|
+
for(const client of clients){
|
|
40
|
+
try{client.write(data);}catch(e){}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
process.stdin.on('end',()=>{
|
|
44
|
+
try{fs.closeSync(fd);}catch(e){}
|
|
45
|
+
for(const client of clients){try{client.end();}catch(e){}}
|
|
46
|
+
if(server){try{server.close(()=>{try{fs.unlinkSync(sockPath);}catch(e){}});}catch(e){}}
|
|
25
47
|
});
|
|
26
|
-
process.stdin.on('end',()=>{fs.closeSync(fd);});
|
|
27
48
|
`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { WandStorage } from "./storage.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type WandTask } from "./task-types.js";
|
|
3
3
|
import type { SessionSnapshot } from "./types.js";
|
|
4
4
|
/** Read all ownership/execution facts before the first destructive action. Never loads transcripts or settings. */
|
|
5
5
|
export declare function createRetentionProtection(storage: WandStorage, sessions: readonly SessionSnapshot[], archivedMs: number | null, now: number): {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { AI_TEAM_ACTIVE_RUN_STATUSES } from "./ai-team-types.js";
|
|
2
2
|
import { isArchiveBlocked } from "./archive-policy.js";
|
|
3
|
+
import { taskHumanAccepted } from "./task-types.js";
|
|
3
4
|
import { TASK_RETENTION_ARCHIVE_VERSION } from "./task-retention.js";
|
|
4
5
|
import { parseTimestampMs } from "./time-utils.js";
|
|
5
6
|
function acceptedTask(task) {
|
|
6
7
|
const acceptance = task.acceptance;
|
|
7
|
-
return (task.status === "
|
|
8
|
+
return taskHumanAccepted(task) && (task.status === "ready" || task.status === "archived")
|
|
8
9
|
&& typeof acceptance?.acceptedAt === "string" && parseTimestampMs(acceptance.acceptedAt) !== null
|
|
9
10
|
&& typeof acceptance.evidence === "string" && !!acceptance.evidence.trim()
|
|
10
11
|
&& typeof acceptance.blocker === "string" && !acceptance.blocker.trim();
|
|
@@ -123,7 +124,7 @@ export function createRetentionProtection(storage, sessions, archivedMs, now) {
|
|
|
123
124
|
const unfinishedAncestors = spread(ancestors, unfinished);
|
|
124
125
|
return {
|
|
125
126
|
tasks, runs, teamSessionIds,
|
|
126
|
-
canArchiveTask: (task) => task.status === "
|
|
127
|
+
canArchiveTask: (task) => task.status === "ready" && taskHumanAccepted(task)
|
|
127
128
|
&& !executionHeld.has(taskNode(task.id)) && !unfinishedAncestors.has(taskNode(task.id)),
|
|
128
129
|
canPurgeTask: (id) => !purgeHeld.has(taskNode(id)),
|
|
129
130
|
canArchiveSession: (id) => !archiveHeld.has(sessionNode(id)),
|
package/dist/retention.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface RetentionDeps {
|
|
|
15
15
|
sessions: SessionRegistry;
|
|
16
16
|
/** 每次扫描读取。传入函数才能在设置保存后立刻用新窗口,而不是启动时的快照。 */
|
|
17
17
|
taskRetention?: TaskRetentionSettings | (() => TaskRetentionSettings | undefined);
|
|
18
|
+
/**
|
|
19
|
+
* 归档或删除之后清掉不再属于任何未归档会话的 CLI 原生历史。
|
|
20
|
+
* 覆盖升级前就已归档、当时还没写清理的存量;本轮刚归档的 id 由调用方排除。
|
|
21
|
+
*/
|
|
22
|
+
releaseArchivedProviderHistory?: (sessionIds: readonly string[]) => void;
|
|
18
23
|
}
|
|
19
24
|
export interface RetentionResult {
|
|
20
25
|
archivedSessions: number;
|
package/dist/retention.js
CHANGED
|
@@ -122,6 +122,17 @@ export function runRetentionSweep(deps, now = Date.now()) {
|
|
|
122
122
|
result.archivedTasks += 1;
|
|
123
123
|
}
|
|
124
124
|
sweepTeamRuns(deps, protection, taskWindows, snapshots, purgedSessionIds, archivedSessionIds, now, result);
|
|
125
|
+
// 本轮刚归档的会话已经由 setArchived 清过 CLI 历史。这里只补更早的存量,
|
|
126
|
+
// 避免和本轮写入抢同一批文件。
|
|
127
|
+
const staleArchived = snapshots
|
|
128
|
+
.filter((session) => session.archived && !archivedSessionIds.has(session.id) && !purgedSessionIds.has(session.id))
|
|
129
|
+
.map((session) => session.id);
|
|
130
|
+
if (staleArchived.length > 0) {
|
|
131
|
+
try {
|
|
132
|
+
deps.releaseArchivedProviderHistory?.(staleArchived);
|
|
133
|
+
}
|
|
134
|
+
catch { /* 清理失败不回滚归档 */ }
|
|
135
|
+
}
|
|
125
136
|
return result;
|
|
126
137
|
}
|
|
127
138
|
function archiveRetentionSession(deps, session, archivedSessionIds, result) {
|
|
@@ -6,8 +6,8 @@ function fields(value) {
|
|
|
6
6
|
}
|
|
7
7
|
function namespace(value) {
|
|
8
8
|
const data = fields(value);
|
|
9
|
-
if (data.scope !== "personal" && data.scope !== "project")
|
|
10
|
-
throw new KnowledgeError("
|
|
9
|
+
if (data.scope !== "global" && data.scope !== "personal" && data.scope !== "project")
|
|
10
|
+
throw new KnowledgeError("请选择全局、个人或项目知识库。");
|
|
11
11
|
if (typeof data.ownerId !== "string" || !data.ownerId.trim() || data.ownerId.length > 200)
|
|
12
12
|
throw new KnowledgeError("请选择员工或项目。");
|
|
13
13
|
return { scope: data.scope, ownerId: data.ownerId };
|
|
@@ -36,6 +36,8 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
36
36
|
// 让退出 handler 不要把剩余 queuedMessages 清空(默认行为是清空)。
|
|
37
37
|
const preserveQueue = !!req.body?.preserveQueue;
|
|
38
38
|
const idempotencyKey = typeof req.body?.idempotencyKey === "string" ? req.body.idempotencyKey : undefined;
|
|
39
|
+
let inputReceipt;
|
|
40
|
+
let inputAccepted = false;
|
|
39
41
|
try {
|
|
40
42
|
const session = structured.get(req.params.id);
|
|
41
43
|
if (!session) {
|
|
@@ -43,19 +45,22 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
47
|
const snapshot = await structured.sendMessage(req.params.id, input, {
|
|
48
|
+
onAccepted: receipt => { inputAccepted = true; inputReceipt = receipt; },
|
|
46
49
|
interrupt,
|
|
47
50
|
preserveQueue,
|
|
48
51
|
idempotencyKey,
|
|
49
52
|
source: "user",
|
|
50
53
|
});
|
|
51
|
-
res.json(sessionResponseDTO(snapshot, req));
|
|
54
|
+
res.json({ ...sessionResponseDTO(snapshot, req), ...(inputReceipt ? { inputReceipt } : {}) });
|
|
52
55
|
}
|
|
53
56
|
catch (error) {
|
|
54
57
|
const errorCode = error?.code;
|
|
55
|
-
const status =
|
|
58
|
+
const status = inputAccepted ? 503
|
|
59
|
+
: errorCode === "idempotency_key_conflict" || errorCode === "duplicate_idempotency_key" || errorCode === "duplicate_queued_message" ? 409 : 400;
|
|
56
60
|
res.status(status).json({
|
|
57
61
|
error: getErrorMessage(error, "无法发送结构化消息。"),
|
|
58
|
-
errorCode,
|
|
62
|
+
errorCode: inputAccepted ? "INPUT_DELIVERY_UNCONFIRMED" : errorCode,
|
|
63
|
+
...(inputReceipt ? { inputReceipt } : {}),
|
|
59
64
|
});
|
|
60
65
|
}
|
|
61
66
|
}));
|
|
@@ -73,7 +78,7 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
73
78
|
res.json(sessionResponseDTO(snapshot, req));
|
|
74
79
|
}
|
|
75
80
|
catch (error) {
|
|
76
|
-
sendRouteError(res, surfaceDomainError(error, /^(
|
|
81
|
+
sendRouteError(res, surfaceDomainError(error, /^(排序长度与当前队列不一致,请刷新后重试。|排序参数无效。|排队消息已变化,请刷新后重试。|排队消息正在交接,请按最新状态重试。)$/) ?? error, "无法调整排队顺序。");
|
|
77
82
|
}
|
|
78
83
|
});
|
|
79
84
|
app.patch("/api/structured-sessions/:id/queued/:index", (req, res) => {
|
|
@@ -86,7 +91,7 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
86
91
|
res.json(sessionResponseDTO(structured.editQueuedMessage(req.params.id, Number(req.params.index), expectedText, text), req));
|
|
87
92
|
}
|
|
88
93
|
catch (error) {
|
|
89
|
-
sendRouteError(res, surfaceDomainError(error, /^(
|
|
94
|
+
sendRouteError(res, surfaceDomainError(error, /^(排队消息已变化,请刷新后重试。|排队消息不能为空。|排队消息正在交接,请按最新状态重试。)$/) ?? error, "无法编辑排队消息。");
|
|
90
95
|
}
|
|
91
96
|
});
|
|
92
97
|
app.delete("/api/structured-sessions/:id/queued/:index", (req, res) => {
|
|
@@ -101,7 +106,7 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
101
106
|
res.json(sessionResponseDTO(snapshot, req));
|
|
102
107
|
}
|
|
103
108
|
catch (error) {
|
|
104
|
-
sendRouteError(res, surfaceDomainError(error, /^(
|
|
109
|
+
sendRouteError(res, surfaceDomainError(error, /^(队列中没有该条消息(可能已被处理)。|排队消息已变化,请按最新顺序重试。|排队消息正在交接,请按最新状态重试。)$/) ?? error, "无法删除排队消息。");
|
|
105
110
|
}
|
|
106
111
|
});
|
|
107
112
|
app.post("/api/structured-sessions/:id/queued/:index/promote", asyncRoute(async (req, res) => {
|
|
@@ -112,16 +117,22 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
112
117
|
}
|
|
113
118
|
const expectedText = typeof req.body?.expectedText === "string" ? req.body.expectedText : undefined;
|
|
114
119
|
const idempotencyKey = typeof req.body?.idempotencyKey === "string" ? req.body.idempotencyKey : undefined;
|
|
120
|
+
let inputAccepted = false;
|
|
121
|
+
let inputReceipt;
|
|
115
122
|
try {
|
|
116
|
-
const snapshot = await structured.promoteQueuedMessage(req.params.id, index, expectedText, idempotencyKey
|
|
117
|
-
|
|
123
|
+
const snapshot = await structured.promoteQueuedMessage(req.params.id, index, expectedText, idempotencyKey, receipt => {
|
|
124
|
+
inputAccepted = true;
|
|
125
|
+
inputReceipt = receipt;
|
|
126
|
+
});
|
|
127
|
+
res.json({ ...sessionResponseDTO(snapshot, req), ...(inputReceipt ? { inputReceipt } : {}) });
|
|
118
128
|
}
|
|
119
129
|
catch (error) {
|
|
120
130
|
const errorCode = error?.code;
|
|
121
|
-
const status = errorCode === "duplicate_idempotency_key" ? 409 : 400;
|
|
131
|
+
const status = inputAccepted ? 503 : errorCode === "idempotency_key_conflict" || errorCode === "duplicate_idempotency_key" ? 409 : 400;
|
|
122
132
|
res.status(status).json({
|
|
123
133
|
error: getErrorMessage(error, "无法立即发送排队消息。"),
|
|
124
|
-
errorCode,
|
|
134
|
+
errorCode: inputAccepted ? "INPUT_DELIVERY_UNCONFIRMED" : errorCode,
|
|
135
|
+
...(inputReceipt ? { inputReceipt } : {}),
|
|
125
136
|
});
|
|
126
137
|
}
|
|
127
138
|
}));
|
|
@@ -141,16 +152,21 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
141
152
|
const input = body.input ?? "";
|
|
142
153
|
const view = body.view;
|
|
143
154
|
const shortcutKey = body.shortcutKey;
|
|
155
|
+
let inputReceipt;
|
|
156
|
+
let inputAccepted = false;
|
|
144
157
|
try {
|
|
145
158
|
if (structured.get(sessionId)) {
|
|
146
|
-
|
|
159
|
+
let confirmAcceptance;
|
|
160
|
+
const acceptance = new Promise(resolve => { confirmAcceptance = resolve; });
|
|
161
|
+
const completion = structured.sendMessage(sessionId, input, { source: "user",
|
|
162
|
+
idempotencyKey: body.idempotencyKey,
|
|
163
|
+
onAccepted: receipt => { inputAccepted = true; inputReceipt = receipt; confirmAcceptance(); },
|
|
164
|
+
});
|
|
147
165
|
if (body.respondImmediately === true || (structured.get(sessionId)?.provider === "pi"
|
|
148
166
|
&& structured.get(sessionId)?.piSettings?.autoResources === true)) {
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
// timeout, especially while the first-turn title job is also active).
|
|
153
|
-
// The normal structured events continue to carry progress/failure.
|
|
167
|
+
// Reject before returning 202 if validation/storage did not accept the
|
|
168
|
+
// input. After acceptance, execution failures arrive on the event stream.
|
|
169
|
+
await Promise.race([acceptance, completion]);
|
|
154
170
|
completion.catch((error) => {
|
|
155
171
|
console.error("[wand] Accepted structured input later failed", {
|
|
156
172
|
sessionId,
|
|
@@ -161,11 +177,11 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
161
177
|
if (!accepted) {
|
|
162
178
|
throw badRequest("未找到该结构化会话。");
|
|
163
179
|
}
|
|
164
|
-
res.status(202).json(sessionResponseDTO(accepted, req));
|
|
180
|
+
res.status(202).json({ ...sessionResponseDTO(accepted, req), ...(inputReceipt ? { inputReceipt } : {}) });
|
|
165
181
|
return;
|
|
166
182
|
}
|
|
167
183
|
const snapshot = await completion;
|
|
168
|
-
res.json(sessionResponseDTO(snapshot, req));
|
|
184
|
+
res.json({ ...sessionResponseDTO(snapshot, req), ...(inputReceipt ? { inputReceipt } : {}) });
|
|
169
185
|
return;
|
|
170
186
|
}
|
|
171
187
|
const existingSession = processes.get(sessionId) || storage.getSession(sessionId);
|
|
@@ -190,7 +206,10 @@ export function registerSessionInputRoutes(app, ctx) {
|
|
|
190
206
|
res.json(sessionResponseDTO(snapshot, req));
|
|
191
207
|
}
|
|
192
208
|
catch (error) {
|
|
193
|
-
const response =
|
|
209
|
+
const response = inputAccepted
|
|
210
|
+
? { statusCode: 503, payload: { error: getErrorMessage(error), errorCode: "INPUT_DELIVERY_UNCONFIRMED",
|
|
211
|
+
...(inputReceipt ? { inputReceipt } : {}), sessionId, sessionStatus: structured.get(sessionId)?.status ?? null } }
|
|
212
|
+
: getInputErrorResponse(error, sessionId);
|
|
194
213
|
console.error("[wand] Input request failed", {
|
|
195
214
|
sessionId, inputLength: input.length, view: view ?? "chat",
|
|
196
215
|
responseStatus: response.statusCode, responsePayload: response.payload,
|
|
@@ -27,6 +27,19 @@ export function registerSessionLifecycleRoutes(app, ctx) {
|
|
|
27
27
|
app.post("/api/sessions", asyncRoute(async (req, res) => {
|
|
28
28
|
const body = req.body;
|
|
29
29
|
try {
|
|
30
|
+
const isWorktreeMergeRequest = !body.employeeId && ((typeof body.systemPrompt === "string" && body.systemPrompt.includes("Worktree 合并 Agent"))
|
|
31
|
+
|| (typeof body.initialInput === "string" && body.initialInput.includes("Worktree 合并 Agent"))
|
|
32
|
+
|| (typeof body.prompt === "string" && body.prompt.includes("Worktree 合并 Agent")));
|
|
33
|
+
if (isWorktreeMergeRequest) {
|
|
34
|
+
body.kind = "structured";
|
|
35
|
+
body.employeeId = "e_wand_ops";
|
|
36
|
+
if (body.initialInput && !body.prompt) {
|
|
37
|
+
body.prompt = body.initialInput;
|
|
38
|
+
delete body.initialInput;
|
|
39
|
+
}
|
|
40
|
+
delete body.command;
|
|
41
|
+
delete body.provider;
|
|
42
|
+
}
|
|
30
43
|
if (body.kind !== "structured" && body.kind !== "pty")
|
|
31
44
|
throw badRequest("kind 必须是 structured 或 pty。");
|
|
32
45
|
if (body.prompt !== undefined && typeof body.prompt !== "string")
|
|
@@ -205,7 +218,7 @@ export function registerSessionLifecycleRoutes(app, ctx) {
|
|
|
205
218
|
res.status(201).json(sessionResponseDTO(structured.get(snapshot.id) ?? snapshot, req));
|
|
206
219
|
}
|
|
207
220
|
catch (error) {
|
|
208
|
-
sendRouteError(res, surfaceDomainError(error, /Wand Agent|harness|core/i) ?? error, "无法启动结构化会话。");
|
|
221
|
+
sendRouteError(res, surfaceDomainError(error, /Wand Agent|harness|core/i) ?? error, body.kind === "pty" ? "无法启动终端会话。" : "无法启动结构化会话。");
|
|
209
222
|
}
|
|
210
223
|
}));
|
|
211
224
|
app.put("/api/sessions/:id/quick-terminal", (req, res) => {
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { generateCommitMessageOnly, getGitStatusAsync, QuickCommitError, runPush, runQuickCommitWithFallback, runTagHead, } from "./git-quick-commit.js";
|
|
2
2
|
import { checkSessionWorktreeMergeabilityAsync, cleanupSessionWorktreeAsync, mergeSessionWorktreeAsync } from "./git-worktree.js";
|
|
3
3
|
import { resolveSystemAiContext } from "./session-ai-context.js";
|
|
4
|
+
import { listGitWorktrees, mergeAllWorktreesAndPush, withGitRepositoryOperation } from "./git-worktree-batch.js";
|
|
4
5
|
import { getErrorMessage } from "./error-utils.js";
|
|
5
6
|
import { asyncRoute } from "./express-async.js";
|
|
6
7
|
import { buildIterationCommitContext, COMMIT_CONTEXT_MODE_PREF_KEY, isCommitContextMode } from "./iteration-log.js";
|
|
7
8
|
import { sendRouteError } from "./server-request.js";
|
|
8
|
-
import { commitModeForRead, iterationCommitInput, commitMessageFromIteration, requireWorktreeSession, saveWorktreeMergeState, getWorktreeMergeResponseStatus, getWorktreeMergePayload, canMergeSession, isMergeActionAllowed } from "./session-service.js";
|
|
9
|
+
import { commitModeForRead, iterationCommitInput, commitMessageFromIteration, requireWorktreeSession, saveWorktreeMergeState, getWorktreeMergeResponseStatus, getWorktreeMergePayload, canMergeSession, isMergeActionAllowed, getInputDebugMeta } from "./session-service.js";
|
|
10
|
+
import { selectEmployeeCandidate } from "./silicon-employee-dispatch.js";
|
|
11
|
+
import { resolveExplicitOrFallbackModel } from "./session-model.js";
|
|
9
12
|
/** Worktree merge/cleanup, git status/commit/push and iteration context routes. */
|
|
10
13
|
export function registerSessionWorktreeRoutes(app, ctx) {
|
|
11
|
-
const { storage, config, sessions, free, wantsCompactTools, sessionResponseDTO, } = ctx;
|
|
14
|
+
const { storage, config, sessions, free, structured, onSessionCreated, wantsCompactTools, sessionResponseDTO, } = ctx;
|
|
12
15
|
app.post("/api/sessions/:id/worktree/merge/check", asyncRoute(async (req, res) => {
|
|
13
16
|
try {
|
|
14
17
|
const current = requireWorktreeSession(sessions.getLatest(req.params.id));
|
|
@@ -50,10 +53,10 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
50
53
|
lastError: undefined,
|
|
51
54
|
conflict: false,
|
|
52
55
|
});
|
|
53
|
-
const result = await mergeSessionWorktreeAsync({
|
|
56
|
+
const result = await withGitRepositoryOperation(merging.worktree.repoRoot ?? merging.cwd, () => mergeSessionWorktreeAsync({
|
|
54
57
|
worktree: merging.worktree,
|
|
55
58
|
targetBranch: current.worktreeMergeInfo?.targetBranch,
|
|
56
|
-
});
|
|
59
|
+
}));
|
|
57
60
|
const updated = saveWorktreeMergeState(sessions, merging, "merged", {
|
|
58
61
|
targetBranch: result.targetBranch,
|
|
59
62
|
mergedAt: result.mergedAt,
|
|
@@ -92,12 +95,37 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
92
95
|
return;
|
|
93
96
|
}
|
|
94
97
|
try {
|
|
95
|
-
|
|
98
|
+
const status = await getGitStatusAsync(snapshot.cwd);
|
|
99
|
+
const worktreeCount = status.isGit ? Math.max(0, (await listGitWorktrees(snapshot.cwd)).length - 1) : 0;
|
|
100
|
+
res.json({ ...status, worktreeCount });
|
|
96
101
|
}
|
|
97
102
|
catch (error) {
|
|
98
103
|
res.json({ isGit: false, error: getErrorMessage(error, "无法读取 git 状态。") });
|
|
99
104
|
}
|
|
100
105
|
}));
|
|
106
|
+
app.post("/api/sessions/:id/git/merge-worktrees", asyncRoute(async (req, res) => {
|
|
107
|
+
const snapshot = sessions.getLatest(req.params.id);
|
|
108
|
+
if (!snapshot) {
|
|
109
|
+
res.status(404).json({ error: "未找到该会话。" });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!snapshot.cwd) {
|
|
113
|
+
res.status(400).json({ error: "会话没有工作目录。" });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const { expectedBranch, expectedHead } = req.body ?? {};
|
|
117
|
+
if (typeof expectedBranch !== "string" || !expectedBranch.trim()
|
|
118
|
+
|| typeof expectedHead !== "string" || !/^[a-f0-9]{40,64}$/.test(expectedHead)) {
|
|
119
|
+
res.status(400).json({ error: "请先刷新 Git 状态,确认当前分支与提交。" });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
res.json(await mergeAllWorktreesAndPush({ cwd: snapshot.cwd, expectedBranch, expectedHead }));
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
sendRouteError(res, error, "无法合并 Worktree。");
|
|
127
|
+
}
|
|
128
|
+
}));
|
|
101
129
|
app.post("/api/sessions/:id/quick-commit", asyncRoute(async (req, res) => {
|
|
102
130
|
const snapshot = sessions.getLatest(req.params.id);
|
|
103
131
|
if (!snapshot) {
|
|
@@ -113,7 +141,7 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
113
141
|
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee(), free, { storage });
|
|
114
142
|
// 自动 message + 迭代模式:拿本轮提示词当输入;提交成功后标记已用掉。
|
|
115
143
|
const context = await iterationCommitInput(storage, snapshot, body);
|
|
116
|
-
const result = await runQuickCommitWithFallback({
|
|
144
|
+
const result = await withGitRepositoryOperation(snapshot.cwd, () => runQuickCommitWithFallback({
|
|
117
145
|
cwd: snapshot.cwd,
|
|
118
146
|
language: config.language ?? "",
|
|
119
147
|
...ai,
|
|
@@ -125,7 +153,7 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
125
153
|
submodule: !!body.submodule,
|
|
126
154
|
iterationDigest: commitMessageFromIteration(body) ? context.digest : "",
|
|
127
155
|
includeDiff: !!body.includeDiff,
|
|
128
|
-
});
|
|
156
|
+
}));
|
|
129
157
|
const consumed = result.ok && result.commit?.hash && context.entryIds.length > 0
|
|
130
158
|
? storage.markIterationPromptsConsumed(context.entryIds, result.commit.hash)
|
|
131
159
|
: 0;
|
|
@@ -232,14 +260,14 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
232
260
|
const body = (req.body ?? {});
|
|
233
261
|
try {
|
|
234
262
|
const ai = resolveSystemAiContext(snapshot, config, storage.getSystemSiliconEmployee(), free, { storage });
|
|
235
|
-
const result = await runTagHead({
|
|
263
|
+
const result = await withGitRepositoryOperation(snapshot.cwd, () => runTagHead({
|
|
236
264
|
cwd: snapshot.cwd,
|
|
237
265
|
language: config.language ?? "",
|
|
238
266
|
...ai,
|
|
239
267
|
tag: typeof body.tag === "string" ? body.tag : undefined,
|
|
240
268
|
autoTag: !!body.autoTag,
|
|
241
269
|
push: !!body.push,
|
|
242
|
-
});
|
|
270
|
+
}));
|
|
243
271
|
res.json(result);
|
|
244
272
|
}
|
|
245
273
|
catch (error) {
|
|
@@ -263,13 +291,13 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
263
291
|
}
|
|
264
292
|
const body = (req.body ?? {});
|
|
265
293
|
try {
|
|
266
|
-
const result = await runPush({
|
|
294
|
+
const result = await withGitRepositoryOperation(snapshot.cwd, () => runPush({
|
|
267
295
|
cwd: snapshot.cwd,
|
|
268
296
|
pushCommits: body.pushCommits !== false,
|
|
269
297
|
pushTags: !!body.pushTags,
|
|
270
298
|
submodule: !!body.submodule,
|
|
271
299
|
tagName: typeof body.tag === "string" ? body.tag : undefined,
|
|
272
|
-
});
|
|
300
|
+
}));
|
|
273
301
|
res.json(result);
|
|
274
302
|
}
|
|
275
303
|
catch (error) {
|
|
@@ -287,7 +315,7 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
287
315
|
res.status(400).json({ error: "当前 worktree 无需补偿清理。", errorCode: "WORKTREE_CLEANUP_NOT_NEEDED" });
|
|
288
316
|
return;
|
|
289
317
|
}
|
|
290
|
-
await cleanupSessionWorktreeAsync({ worktree: current.worktree });
|
|
318
|
+
await withGitRepositoryOperation(current.worktree.repoRoot ?? current.cwd, () => cleanupSessionWorktreeAsync({ worktree: current.worktree }));
|
|
291
319
|
const updated = saveWorktreeMergeState(sessions, current, "merged", {
|
|
292
320
|
...(current.worktreeMergeInfo ?? null),
|
|
293
321
|
cleanupDone: true,
|
|
@@ -300,4 +328,69 @@ export function registerSessionWorktreeRoutes(app, ctx) {
|
|
|
300
328
|
res.status(getWorktreeMergeResponseStatus(error)).json(getWorktreeMergePayload(error, "无法清理 worktree。"));
|
|
301
329
|
}
|
|
302
330
|
}));
|
|
331
|
+
app.post("/api/workspaces/:id/worktree-merge-agent", asyncRoute(async (req, res) => {
|
|
332
|
+
const workspace = storage.getWorkspace(req.params.id);
|
|
333
|
+
if (!workspace) {
|
|
334
|
+
res.status(404).json({ error: "未找到该项目。" });
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const prompt = typeof req.body?.prompt === "string" ? req.body.prompt.trim() : "";
|
|
338
|
+
if (!prompt) {
|
|
339
|
+
res.status(400).json({ error: "请输入合并任务书内容。" });
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const systemPromptOverride = typeof req.body?.systemPrompt === "string" ? req.body.systemPrompt.trim() : "";
|
|
343
|
+
// 所有系统自动发起的系统功能调用(包含 Worktree 合并)统一使用「勤劳的初二」
|
|
344
|
+
const employee = storage.getSystemSiliconEmployee() ?? storage.ensureSystemSiliconEmployee();
|
|
345
|
+
const candidate = selectEmployeeCandidate(employee);
|
|
346
|
+
const agent = candidate.agent;
|
|
347
|
+
const provider = agent.provider;
|
|
348
|
+
const mode = agent.mode ?? "managed";
|
|
349
|
+
const requestedEngine = agent.engine;
|
|
350
|
+
const model = agent.model && agent.model !== "default"
|
|
351
|
+
? agent.model
|
|
352
|
+
: resolveExplicitOrFallbackModel(config, provider, agent.model);
|
|
353
|
+
const baseSystemPrompt = systemPromptOverride || [
|
|
354
|
+
`你是 Wand 为项目「${workspace.name}」启动的 Worktree 合并 Agent。`,
|
|
355
|
+
`项目主工作区:${workspace.cwd}`,
|
|
356
|
+
"",
|
|
357
|
+
employee.prompt,
|
|
358
|
+
].join("\n");
|
|
359
|
+
const engineDecision = requestedEngine ? await structured.resolveNewSessionPiEngine(requestedEngine) : null;
|
|
360
|
+
const snapshot = structured.createSession({
|
|
361
|
+
cwd: workspace.cwd,
|
|
362
|
+
mode,
|
|
363
|
+
provider,
|
|
364
|
+
runner: undefined,
|
|
365
|
+
worktreeEnabled: false,
|
|
366
|
+
model,
|
|
367
|
+
thinkingEffort: agent.thinkingEffort ?? config.defaultThinkingEffort,
|
|
368
|
+
sessionSource: "interactive",
|
|
369
|
+
workspaceId: workspace.id,
|
|
370
|
+
systemPrompt: baseSystemPrompt,
|
|
371
|
+
employeeId: employee.id,
|
|
372
|
+
employeeName: employee.name,
|
|
373
|
+
employeeAvatar: employee.avatar,
|
|
374
|
+
employeeCandidates: employee.agents,
|
|
375
|
+
employeeCandidateIndex: candidate.index,
|
|
376
|
+
title: `合并 Worktree - ${workspace.name}`,
|
|
377
|
+
...(engineDecision ? { engine: engineDecision.engine } : {}),
|
|
378
|
+
});
|
|
379
|
+
onSessionCreated?.(snapshot.cwd);
|
|
380
|
+
try {
|
|
381
|
+
Promise.resolve(structured.sendMessage(snapshot.id, prompt, { source: "user" })).catch((error) => {
|
|
382
|
+
console.error("[wand] Accepted merge agent prompt later failed", {
|
|
383
|
+
sessionId: snapshot.id,
|
|
384
|
+
error: getInputDebugMeta(error),
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
console.error("[wand] Accepted merge agent prompt later failed", {
|
|
390
|
+
sessionId: snapshot.id,
|
|
391
|
+
error: getInputDebugMeta(error),
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
res.status(201).json(wantsCompactTools(req) ? sessionResponseDTO(snapshot, req) : snapshot);
|
|
395
|
+
}));
|
|
303
396
|
}
|
|
@@ -20,7 +20,7 @@ import { TaskReadModel } from "./task-read-model.js";
|
|
|
20
20
|
import { readTaskActions } from "./task-actions.js";
|
|
21
21
|
import { refreshAutoBoardTaskTitles, taskTitleService } from "./task-title-service.js";
|
|
22
22
|
import { provisionalTaskTitleFromDescription } from "./task-title.js";
|
|
23
|
-
import { isWandTaskStatus, statusAfterWandTaskDispatch } from "./task-types.js";
|
|
23
|
+
import { isWandTaskStatus, taskWorkflowStage, statusAfterWandTaskDispatch } from "./task-types.js";
|
|
24
24
|
import { DEFAULT_WAND_TASK_AGENT_KIND, DEFAULT_WAND_TASK_AGENT_MODE, DEFAULT_WAND_TASK_PRIORITY, WAND_MILESTONE_NAME_MAX_LENGTH } from "./task-types.js";
|
|
25
25
|
import { badRequest, surfaceDomainError } from "./http-error.js";
|
|
26
26
|
import { parseTaskAgent, parentTaskIdFrom } from "./task-agent-parsing.js";
|
|
@@ -157,6 +157,9 @@ function taskDto({ storage, sessions }, task, model) {
|
|
|
157
157
|
: task.executionSubject?.type === "team" ? storage.getAiTeam(task.executionSubject.id) : null;
|
|
158
158
|
return {
|
|
159
159
|
...task,
|
|
160
|
+
status: taskWorkflowStage(task.status),
|
|
161
|
+
workflowVersion: 1,
|
|
162
|
+
...(task.status === "done" ? { legacyReview: true } : {}),
|
|
160
163
|
commentCount: model ? model.commentCounts.get(task.id) ?? 0 : storage.countWandTaskComments(task.id),
|
|
161
164
|
queuedCommentCount: model ? model.queuedCommentCounts.get(task.id) ?? 0 : storage.countQueuedTaskCommentDispatchesForTask(task.id),
|
|
162
165
|
...(model ? model.execution(task) : taskExecutionProjection(sessionIds.flatMap(id => {
|
|
@@ -333,6 +336,8 @@ export function registerTaskRoutes(app, deps) {
|
|
|
333
336
|
const titleSource = providedTitle ? "user" : "auto";
|
|
334
337
|
const title = providedTitle || provisionalTaskTitleFromDescription(description) || "新建任务";
|
|
335
338
|
const status = isWandTaskStatus(body.status) ? body.status : "todo";
|
|
339
|
+
if (!["backlog", "todo", "doing"].includes(status))
|
|
340
|
+
throw badRequest("新建任务不能冒充已交付或已验收,请先创建再提交交付。");
|
|
336
341
|
const priority = PRIORITIES.has(body.priority) ? body.priority : DEFAULT_WAND_TASK_PRIORITY;
|
|
337
342
|
const agent = body.agent === undefined ? null : parseTaskAgent(body.agent);
|
|
338
343
|
const executionSubject = body.executionSubject === undefined
|
|
@@ -353,8 +358,10 @@ export function registerTaskRoutes(app, deps) {
|
|
|
353
358
|
const requestedMilestoneId = milestoneIdFrom(storage, body.milestoneId) ?? defaultMilestoneIdForWrite(storage);
|
|
354
359
|
const milestoneId = scopedMilestoneId(storage, requestedMilestoneId, workspaceId)
|
|
355
360
|
?? defaultMilestoneIdForWrite(storage);
|
|
361
|
+
const cwd = typeof body.cwd === "string" && body.cwd.trim() ? text(body.cwd) : undefined;
|
|
356
362
|
const task = storage.createWandTask({
|
|
357
363
|
workspaceId,
|
|
364
|
+
cwd,
|
|
358
365
|
parentTaskId,
|
|
359
366
|
title,
|
|
360
367
|
titleSource,
|
|
@@ -392,6 +399,15 @@ export function registerTaskRoutes(app, deps) {
|
|
|
392
399
|
res.status(404).json({ error: "未找到该任务。" });
|
|
393
400
|
return;
|
|
394
401
|
}
|
|
402
|
+
if (body.workflow !== undefined) {
|
|
403
|
+
if (Object.keys(body).some(key => key !== "workflow"))
|
|
404
|
+
throw badRequest("工作流动作不能与其他编辑混合提交。");
|
|
405
|
+
const result = storage.applyTaskWorkflow(currentTask.id, body.workflow, { kind: "user" }, sessions);
|
|
406
|
+
res.json(dto(result));
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (body.verification !== undefined || body.deliveryRevision !== undefined || body.legacyReview !== undefined)
|
|
410
|
+
throw badRequest("验收版本与 AI 结论只能通过工作流写入。");
|
|
395
411
|
const patch = {};
|
|
396
412
|
if (body.title !== undefined) {
|
|
397
413
|
// 显式改标题(包括原生端的可编辑标题框)都算用户自己写的。
|
|
@@ -407,17 +423,22 @@ export function registerTaskRoutes(app, deps) {
|
|
|
407
423
|
const acceptance = bodyObject(body.acceptance);
|
|
408
424
|
const evidence = text(acceptance.evidence).slice(0, 16000);
|
|
409
425
|
const blocker = text(acceptance.blocker).slice(0, 4000);
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (accepted && storage.listWandTasks().some(child => child.parentTaskId === currentTask.id && !child.acceptance?.acceptedAt))
|
|
414
|
-
throw badRequest("仍有子任务未验收通过,不能通过主目标整体验收。");
|
|
415
|
-
patch.acceptance = { evidence, blocker, acceptedAt: accepted ? new Date().toISOString() : null };
|
|
426
|
+
if (acceptance.acceptedAt != null || acceptance.confirmedRevision !== undefined)
|
|
427
|
+
throw badRequest("请使用‘确认并归档’工作流;保存证据不能代替人工确认。");
|
|
428
|
+
patch.acceptance = { evidence, blocker, acceptedAt: null };
|
|
416
429
|
}
|
|
417
430
|
if (body.status !== undefined) {
|
|
418
431
|
if (!isWandTaskStatus(body.status))
|
|
419
432
|
throw badRequest("任务状态无效。");
|
|
420
|
-
|
|
433
|
+
if (body.status !== taskWorkflowStage(currentTask.status) && body.status !== currentTask.status) {
|
|
434
|
+
if (body.status === "archived") {
|
|
435
|
+
res.status(409).json({ error: "请先核对 AI 验收,再由人工确认并归档。" });
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (!["backlog", "todo", "doing"].includes(body.status))
|
|
439
|
+
throw badRequest("验收、待归档和归档只能通过明确的验收动作流转。");
|
|
440
|
+
patch.status = body.status;
|
|
441
|
+
}
|
|
421
442
|
}
|
|
422
443
|
if (body.priority !== undefined) {
|
|
423
444
|
if (!PRIORITIES.has(body.priority))
|
|
@@ -584,6 +605,8 @@ export function registerTaskRoutes(app, deps) {
|
|
|
584
605
|
res.status(404).json({ error: "未找到该任务。" });
|
|
585
606
|
return;
|
|
586
607
|
}
|
|
608
|
+
if (task.status === "archived")
|
|
609
|
+
throw badRequest("归档任务需要先恢复,再新建执行。");
|
|
587
610
|
if (!config)
|
|
588
611
|
throw badRequest("当前服务未启用派发,无法派发 Agent。");
|
|
589
612
|
const body = bodyObject(req.body);
|
|
@@ -594,6 +617,12 @@ export function registerTaskRoutes(app, deps) {
|
|
|
594
617
|
&& (body.kind === "pty" || (body.agent && bodyObject(body.agent).kind === "pty"))) {
|
|
595
618
|
throw badRequest("PTY 只能选择 CLI 工具。");
|
|
596
619
|
}
|
|
620
|
+
const reviewing = body.purpose === "review";
|
|
621
|
+
if (body.purpose !== undefined && body.purpose !== "review" && body.purpose !== "execution")
|
|
622
|
+
throw badRequest("派发目的无效。");
|
|
623
|
+
if (reviewing && (taskWorkflowStage(task.status) !== "review" || subject?.type !== "employee"))
|
|
624
|
+
throw badRequest("AI 验收请在待验收阶段明确选择员工,以获得当前任务的受限回填能力。");
|
|
625
|
+
const dispatchStatus = reviewing ? "review" : statusAfterWandTaskDispatch(task.status);
|
|
597
626
|
const requestedPrompt = text(body.prompt);
|
|
598
627
|
const existingSessions = storage.listWandTaskSessionIds(task.id).length;
|
|
599
628
|
if (existingSessions > 0 && !requestedPrompt)
|
|
@@ -621,7 +650,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
621
650
|
storage.updateWandTask(task.id, {
|
|
622
651
|
executionSubject: subject,
|
|
623
652
|
agent: preferredAgent,
|
|
624
|
-
status:
|
|
653
|
+
status: dispatchStatus,
|
|
625
654
|
});
|
|
626
655
|
recordIterationPromptForTask(storage, {
|
|
627
656
|
sessionId: detail.run.chatSessionId ?? undefined,
|
|
@@ -685,12 +714,12 @@ export function registerTaskRoutes(app, deps) {
|
|
|
685
714
|
// Compatibility creation/drag clients also enter the same durable comment path for employees.
|
|
686
715
|
// No implicit description fallback: a first dispatch needs an explicit round instruction too.
|
|
687
716
|
if (employee) {
|
|
688
|
-
const result = await submitTaskComment(deps, task.id, { requestId: text(body.requestId) || randomUUID(), body: requestedPrompt, employeeId: employee.id }, override ?? undefined);
|
|
717
|
+
const result = await submitTaskComment(deps, task.id, { requestId: text(body.requestId) || randomUUID(), body: requestedPrompt, employeeId: employee.id, purpose: reviewing ? "review" : "execution" }, override ?? undefined);
|
|
689
718
|
const session = result.comment.dispatch?.sessionId ? sessions?.getSlim(result.comment.dispatch.sessionId)
|
|
690
719
|
?? storage.getSessionSlim(result.comment.dispatch.sessionId) : null;
|
|
691
720
|
// 排队中的派发同样要把任务挂到员工并保持活动;不能因为没有会话就跳过。
|
|
692
721
|
if (result.comment.dispatch)
|
|
693
|
-
storage.updateWandTask(task.id, { agent, executionSubject: { type: "employee", id: employee.id, ...(override ? { agentOverride: override } : {}) }, status:
|
|
722
|
+
storage.updateWandTask(task.id, { agent, executionSubject: { type: "employee", id: employee.id, ...(override ? { agentOverride: override } : {}) }, status: dispatchStatus });
|
|
694
723
|
res.status(202).json({ ok: true, taskId: task.id, subject: { type: "employee", id: employee.id }, comment: result.comment,
|
|
695
724
|
queued: result.comment.dispatch?.state === "queued",
|
|
696
725
|
session: session ? { ...taskSessionSummary(session), employeeCandidateIndex: session.employeeCandidateIndex } : null });
|
|
@@ -702,7 +731,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
702
731
|
storage.updateWandTask(task.id, {
|
|
703
732
|
agent,
|
|
704
733
|
executionSubject: usedSubject,
|
|
705
|
-
status:
|
|
734
|
+
status: dispatchStatus,
|
|
706
735
|
});
|
|
707
736
|
if (body.rememberAgentDefaults === true && !employee)
|
|
708
737
|
writeTaskBoardLastAgent(storage, agent);
|