@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/models.js
CHANGED
|
@@ -12,6 +12,7 @@ import { AUTO_ASSIGN_LABEL, AUTO_ASSIGN_SELECTOR, FREE_MODEL_GROUP_ID, isAutoAss
|
|
|
12
12
|
import { MODEL_ID_PATTERN, QODER_MODEL_ID_PATTERN, CLAUDE_BUILTIN_MODELS, CODEX_FALLBACK_MODELS, OPENCODE_FALLBACK_MODELS, GROK_FALLBACK_MODELS, QODER_FALLBACK_MODELS, PI_FALLBACK_MODELS, cloneModels, isRecord } from "./models/shared.js";
|
|
13
13
|
import { EFFORT_ID_PATTERN, FALLBACK_PROVIDER_THINKING, cloneThinkingEfforts, effortLevelsFromIds, unionModelEfforts, parseCliEffortList } from "./models/thinking.js";
|
|
14
14
|
import { parseOpenCodeModelCatalog } from "./models/parsers/opencode.js";
|
|
15
|
+
import { openCodeCatalogArgs } from "./opencode-cli.js";
|
|
15
16
|
import { parseGrokModels } from "./models/parsers/grok.js";
|
|
16
17
|
import { parseQoderModels } from "./models/parsers/qoder.js";
|
|
17
18
|
import { parsePiRpcModels, parsePiModels } from "./models/parsers/pi.js";
|
|
@@ -288,20 +289,20 @@ async function probeCodexModels(runner, env) {
|
|
|
288
289
|
}
|
|
289
290
|
}
|
|
290
291
|
async function probeOpenCode(runner, env) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
runner("opencode", ["--version"], { env, timeout: 5000 })
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
: { ok: false };
|
|
304
|
-
|
|
292
|
+
let version = { ok: false };
|
|
293
|
+
try {
|
|
294
|
+
const result = await runner("opencode", ["--version"], { env, timeout: 5000 });
|
|
295
|
+
version = { ok: true, value: extractSemver(result.stdout) ?? (result.stdout.trim().slice(0, 64) || null) };
|
|
296
|
+
}
|
|
297
|
+
catch { /* Directory failure preserves the last known catalog. */ }
|
|
298
|
+
try {
|
|
299
|
+
const major = version.ok ? Number(version.value?.split(".")[0]) : 1;
|
|
300
|
+
const result = await runner("opencode", openCodeCatalogArgs(major), { env, timeout: 8000 });
|
|
301
|
+
return { models: { ok: true, value: parseOpenCodeModelCatalog(result.stdout) }, version };
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return { models: { ok: false }, version };
|
|
305
|
+
}
|
|
305
306
|
}
|
|
306
307
|
async function probeGrokModels(runner, env) {
|
|
307
308
|
try {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* 我们的策略:安装前备份当前全局包,补齐 npm 子进程 PATH,清掉
|
|
12
12
|
* `@co0ontty/.wand-*` 残留目录;失败时恢复备份,避免运行中的服务被半成品安装拆掉。
|
|
13
13
|
*/
|
|
14
|
+
import type { WandBuildInfo } from "./build-info.js";
|
|
14
15
|
export declare const PACKAGE_NAME = "@co0ontty/wand";
|
|
15
16
|
export type UpdateChannel = "stable" | "beta";
|
|
16
17
|
export interface PackageUpdateInfo {
|
|
@@ -28,8 +29,9 @@ export declare function normalizeUpdateChannel(value: unknown): UpdateChannel;
|
|
|
28
29
|
export declare function getInstallSpecForChannel(channel: UpdateChannel): string;
|
|
29
30
|
export declare function buildPackageUpdateInfo(currentVersion: string, channel: UpdateChannel, latestVersion: string | null): PackageUpdateInfo;
|
|
30
31
|
export declare function selectPackageUpdateInfo(currentVersion: string, channel: UpdateChannel, stableVersion: string | null, betaVersion?: string | null): PackageUpdateInfo;
|
|
31
|
-
|
|
32
|
-
export declare function
|
|
32
|
+
type UpdateBuildIdentity = Pick<WandBuildInfo, "version" | "commit">;
|
|
33
|
+
export declare function checkPackageUpdateAsync(currentVersion: string, channel: UpdateChannel, timeoutMs?: number, build?: UpdateBuildIdentity): Promise<PackageUpdateInfo>;
|
|
34
|
+
export declare function checkPackageUpdateSync(currentVersion: string, channel: UpdateChannel, timeoutMs?: number, build?: UpdateBuildIdentity): PackageUpdateInfo;
|
|
33
35
|
export declare function resolveInstalledNpmPrefix(moduleUrl?: string, platform?: NodeJS.Platform): {
|
|
34
36
|
root: string;
|
|
35
37
|
prefix: string;
|
|
@@ -88,3 +90,4 @@ export declare function resolveGlobalWandCli(): string | null;
|
|
|
88
90
|
* dist/cli.js;更新回滚期间 shim 会临时指向同盘安全备份,始终保持可启动。
|
|
89
91
|
*/
|
|
90
92
|
export declare function resolveGlobalWandBin(): string | null;
|
|
93
|
+
export {};
|
package/dist/npm-update-utils.js
CHANGED
|
@@ -103,17 +103,47 @@ export function selectPackageUpdateInfo(currentVersion, channel, stableVersion,
|
|
|
103
103
|
const info = buildPackageUpdateInfo(currentVersion, useBeta ? "beta" : "stable", useBeta ? beta : stable);
|
|
104
104
|
return { ...info, channel, stableVersion: stable, betaVersion: beta };
|
|
105
105
|
}
|
|
106
|
-
|
|
106
|
+
function localDebugCommit(info, build) {
|
|
107
|
+
if (!info.updateAvailable || !/^\d+\.\d+\.\d+-debug\./.test(info.current)
|
|
108
|
+
|| !build?.commit || build.version === null || cleanVersion(build.version) !== info.current
|
|
109
|
+
|| !/^[a-f0-9]{40}$/i.test(build.commit))
|
|
110
|
+
return null;
|
|
111
|
+
return build.commit.toLowerCase();
|
|
112
|
+
}
|
|
113
|
+
function applySourceCommit(info, currentCommit, candidateCommit) {
|
|
114
|
+
// start.sh uses X.Y.Z-debug while CI uses X.Y.(Z+1)-beta, even for the same
|
|
115
|
+
// commit. A published copy must not replace local changes on that commit.
|
|
116
|
+
return candidateCommit.trim().toLowerCase() === currentCommit ? { ...info, updateAvailable: false } : info;
|
|
117
|
+
}
|
|
118
|
+
export async function checkPackageUpdateAsync(currentVersion, channel, timeoutMs = NPM_VIEW_TIMEOUT_MS, build) {
|
|
107
119
|
const [stable, beta] = await Promise.all([
|
|
108
120
|
viewPackageVersionAsync("stable", timeoutMs),
|
|
109
121
|
channel === "beta" ? viewPackageVersionAsync("beta", timeoutMs) : null,
|
|
110
122
|
]);
|
|
111
|
-
|
|
123
|
+
const info = selectPackageUpdateInfo(currentVersion, channel, stable, beta);
|
|
124
|
+
const commit = localDebugCommit(info, build);
|
|
125
|
+
if (!commit)
|
|
126
|
+
return info;
|
|
127
|
+
try {
|
|
128
|
+
const { stdout } = await execFileAsync(npmBin(), ["view", info.installSpec, "gitHead"], {
|
|
129
|
+
timeout: timeoutMs, env: getChildEnv(), maxBuffer: INSTALL_MAX_BUFFER,
|
|
130
|
+
});
|
|
131
|
+
return applySourceCommit(info, commit, stdout);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Missing provenance does not prove equal source; retain version ordering.
|
|
135
|
+
return info;
|
|
136
|
+
}
|
|
112
137
|
}
|
|
113
|
-
export function checkPackageUpdateSync(currentVersion, channel, timeoutMs = NPM_VIEW_TIMEOUT_MS) {
|
|
138
|
+
export function checkPackageUpdateSync(currentVersion, channel, timeoutMs = NPM_VIEW_TIMEOUT_MS, build) {
|
|
114
139
|
const stable = viewPackageVersionSync("stable", timeoutMs);
|
|
115
140
|
const beta = channel === "beta" ? viewPackageVersionSync("beta", timeoutMs) : null;
|
|
116
|
-
|
|
141
|
+
const info = selectPackageUpdateInfo(currentVersion, channel, stable, beta);
|
|
142
|
+
const commit = localDebugCommit(info, build);
|
|
143
|
+
if (!commit)
|
|
144
|
+
return info;
|
|
145
|
+
const result = runNpmSync(["view", info.installSpec, "gitHead"], timeoutMs);
|
|
146
|
+
return result.status === 0 ? applySourceCommit(info, commit, result.stdout ?? "") : info;
|
|
117
147
|
}
|
|
118
148
|
export function resolveInstalledNpmPrefix(moduleUrl = import.meta.url, platform = process.platform) {
|
|
119
149
|
try {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Resolve the actual executable; an upgrade invalidates the cached CLI dialect. */
|
|
2
|
+
export declare function openCodeMajorVersion(env: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<number>;
|
|
3
|
+
/** v2 returns priced native records through its read-only API, v1 through verbose models. */
|
|
4
|
+
export declare function openCodeCatalogArgs(major: number, nativeFree?: boolean): string[];
|
|
5
|
+
/** v2 folds the reasoning variant into the model reference. */
|
|
6
|
+
export declare function openCodeModelArgs(model: string | null | undefined, variant: string | null, major: number): string[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { whichSync } from "./path-repair.js";
|
|
5
|
+
import { extractSemver } from "./version-utils.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
let cachedVersion;
|
|
8
|
+
/** Resolve the actual executable; an upgrade invalidates the cached CLI dialect. */
|
|
9
|
+
export async function openCodeMajorVersion(env, signal) {
|
|
10
|
+
signal?.throwIfAborted();
|
|
11
|
+
const executable = whichSync("opencode", { env });
|
|
12
|
+
if (!executable)
|
|
13
|
+
throw new Error("未找到 OpenCode CLI。");
|
|
14
|
+
const stat = statSync(executable);
|
|
15
|
+
const identity = `${executable}:${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}`;
|
|
16
|
+
if (cachedVersion?.identity !== identity) {
|
|
17
|
+
const version = execFileAsync(executable, ["--version"], { env, timeout: 5_000, maxBuffer: 64 * 1024, encoding: "utf8" })
|
|
18
|
+
.then(({ stdout }) => {
|
|
19
|
+
const parsed = extractSemver(stdout);
|
|
20
|
+
if (!parsed)
|
|
21
|
+
throw new Error("无法识别 OpenCode CLI 版本。");
|
|
22
|
+
return Number(parsed.split(".")[0]);
|
|
23
|
+
});
|
|
24
|
+
cachedVersion = { identity, version };
|
|
25
|
+
void version.catch(() => { if (cachedVersion?.version === version)
|
|
26
|
+
cachedVersion = undefined; });
|
|
27
|
+
}
|
|
28
|
+
const major = await cachedVersion.version;
|
|
29
|
+
signal?.throwIfAborted();
|
|
30
|
+
return major;
|
|
31
|
+
}
|
|
32
|
+
/** v2 returns priced native records through its read-only API, v1 through verbose models. */
|
|
33
|
+
export function openCodeCatalogArgs(major, nativeFree = false) {
|
|
34
|
+
return major >= 2
|
|
35
|
+
? ["api", "--standalone", "model.list"]
|
|
36
|
+
: nativeFree ? ["models", "opencode", "--verbose", "--refresh", "--pure"] : ["models", "--verbose"];
|
|
37
|
+
}
|
|
38
|
+
/** v2 folds the reasoning variant into the model reference. */
|
|
39
|
+
export function openCodeModelArgs(model, variant, major) {
|
|
40
|
+
const value = model?.trim();
|
|
41
|
+
const args = [];
|
|
42
|
+
if (value && value !== "default") {
|
|
43
|
+
args.push("--model", major >= 2 && variant ? `${value.split("#")[0]}#${variant}` : value);
|
|
44
|
+
}
|
|
45
|
+
else if (major >= 2 && variant) {
|
|
46
|
+
throw new Error("OpenCode 2 的思考深度需要具体模型,请先选择模型。");
|
|
47
|
+
}
|
|
48
|
+
if (major < 2 && variant)
|
|
49
|
+
args.push("--variant", variant);
|
|
50
|
+
return args;
|
|
51
|
+
}
|
|
@@ -11,6 +11,7 @@ import { AI_TEAM_MAX_CANDIDATES } from "./ai-team-types.js";
|
|
|
11
11
|
import { systemEmployeeDefaultAgents } from "./system-employee.js";
|
|
12
12
|
import { whichSync } from "./path-repair.js";
|
|
13
13
|
import { parseProviderCliVersion } from "./provider-cli-updater.js";
|
|
14
|
+
import { openCodeCatalogArgs } from "./opencode-cli.js";
|
|
14
15
|
import { asRecord } from "./structured-content.js";
|
|
15
16
|
import { redactSystemAiText } from "./system-ai-redaction.js";
|
|
16
17
|
const pools = new WeakMap();
|
|
@@ -56,10 +57,19 @@ export function parseOpenCodeFreeModels(stdout) {
|
|
|
56
57
|
return !!row && Object.keys(row).length > 0 && Object.values(row).every(allZero);
|
|
57
58
|
};
|
|
58
59
|
return [...new Set(parseOpenCodeModelRecords(stdout).flatMap(({ id, record }) => {
|
|
59
|
-
const api = asRecord(record.api),
|
|
60
|
+
const api = asRecord(record.api), capabilities = asRecord(record.capabilities);
|
|
61
|
+
// v2 supplies a price per context tier. Every tier and every price must be zero.
|
|
62
|
+
const costs = Array.isArray(record.cost) ? record.cost : [record.cost];
|
|
63
|
+
const free = costs.length > 0 && costs.every((value) => {
|
|
64
|
+
const cost = asRecord(value);
|
|
65
|
+
if (!cost || !zero(cost.input) || !zero(cost.output))
|
|
66
|
+
return false;
|
|
67
|
+
const { tier: _tier, ...prices } = cost;
|
|
68
|
+
return allZero(prices);
|
|
69
|
+
});
|
|
60
70
|
if (!isOpenCodeNativeModelId(id) || record.providerID !== "opencode" || record.status !== "active"
|
|
61
71
|
|| api?.id !== id.slice("opencode/".length) || typeof api.url !== "string" || !NATIVE_API_URLS.has(api.url)
|
|
62
|
-
|| typeof api.npm !== "string" || !NATIVE_SDKS.has(api.npm) || !
|
|
72
|
+
|| typeof api.npm !== "string" || !NATIVE_SDKS.has(api.npm) || !free
|
|
63
73
|
|| asRecord(capabilities?.input)?.text !== true || asRecord(capabilities?.output)?.text !== true
|
|
64
74
|
|| capabilities?.toolcall !== true)
|
|
65
75
|
return [];
|
|
@@ -122,6 +132,7 @@ export class OpenCodeFreeModelsService {
|
|
|
122
132
|
options;
|
|
123
133
|
controller = new AbortController();
|
|
124
134
|
setup;
|
|
135
|
+
cliMajor = 1;
|
|
125
136
|
pending;
|
|
126
137
|
retryAfter = 0;
|
|
127
138
|
disposed = false;
|
|
@@ -160,7 +171,7 @@ export class OpenCodeFreeModelsService {
|
|
|
160
171
|
return this.options.find ? this.options.find(command, this.env()) : whichSync(command, { env: this.env() });
|
|
161
172
|
}
|
|
162
173
|
async run(file, args, timeout, signal = this.controller.signal) {
|
|
163
|
-
const nativeModels = args[0] === "models";
|
|
174
|
+
const nativeModels = args[0] === "models" || args[0] === "api";
|
|
164
175
|
const result = await (this.options.run ?? runCommand)(file, args, { env: nativeModels ? this.execution.env(this.env()) : this.env(), timeout,
|
|
165
176
|
...(nativeModels ? { cwd: this.execution.cwd } : {}), signal: AbortSignal.any([signal, this.controller.signal]) });
|
|
166
177
|
this.controller.signal.throwIfAborted();
|
|
@@ -200,6 +211,7 @@ export class OpenCodeFreeModelsService {
|
|
|
200
211
|
const version = parseProviderCliVersion(await this.run(executable, ["--version"], 8_000));
|
|
201
212
|
if (!version || /^0\.0\./.test(version))
|
|
202
213
|
throw new Error("现有 OpenCode 版本不可用;未覆盖用户安装。");
|
|
214
|
+
this.cliMajor = Number(version.split(".")[0]);
|
|
203
215
|
return executable;
|
|
204
216
|
}
|
|
205
217
|
ensure() {
|
|
@@ -222,7 +234,7 @@ export class OpenCodeFreeModelsService {
|
|
|
222
234
|
const sequence = ++this.catalogSequence;
|
|
223
235
|
const executable = await this.ensure();
|
|
224
236
|
signal?.throwIfAborted();
|
|
225
|
-
return { sequence, models: parseOpenCodeFreeModels(await this.run(executable,
|
|
237
|
+
return { sequence, models: parseOpenCodeFreeModels(await this.run(executable, openCodeCatalogArgs(this.cliMajor, true), 15_000, signal)) };
|
|
226
238
|
}
|
|
227
239
|
async probe(model) {
|
|
228
240
|
try {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Do not import a Wand SDK runtime: the native CLI owns its extensions and TUI.
|
|
3
3
|
import { appendFileSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { projectPiProtocolEvent } from "./pi-event-projection.js";
|
|
5
|
+
import { claimNativeInput, nativeInputFile, openNativeInputBoundary, writeNativeInput } from "./pi-native-input.js";
|
|
5
6
|
export default function wandNativeEvents(pi) {
|
|
6
7
|
const file = process.env.WAND_PI_NATIVE_PATHS;
|
|
7
8
|
if (!file)
|
|
@@ -28,7 +29,10 @@ export default function wandNativeEvents(pi) {
|
|
|
28
29
|
if (ctx.mode !== "tui")
|
|
29
30
|
throw new Error("Pi 原生 tmux 会话必须使用 TUI 模式。");
|
|
30
31
|
append({ type: "session", id: ctx.sessionManager.getSessionId() });
|
|
31
|
-
|
|
32
|
+
if (paths.input)
|
|
33
|
+
append({ type: "wand_input_ready", id: paths.initialMessageId });
|
|
34
|
+
writeFileSync(paths.ready, JSON.stringify({ mode: ctx.mode, hasUI: ctx.hasUI, pid: process.pid,
|
|
35
|
+
sessionFile: ctx.sessionManager.getSessionFile() }) + "\n", { mode: 0o600 });
|
|
32
36
|
});
|
|
33
37
|
const capture = (event) => {
|
|
34
38
|
if (event.type === "message_update") {
|
|
@@ -42,8 +46,55 @@ export default function wandNativeEvents(pi) {
|
|
|
42
46
|
else
|
|
43
47
|
append(event);
|
|
44
48
|
};
|
|
49
|
+
let pending = null;
|
|
50
|
+
const confirmInput = (ctx) => {
|
|
51
|
+
if (!pending || !paths.input)
|
|
52
|
+
return;
|
|
53
|
+
const offer = pending;
|
|
54
|
+
const found = ctx.sessionManager.getBranch().some((entry) => entry.type === "custom_message"
|
|
55
|
+
&& entry.customType === "wand-user-input"
|
|
56
|
+
&& entry.details?.inputId === offer.id);
|
|
57
|
+
if (!found)
|
|
58
|
+
return;
|
|
59
|
+
writeNativeInput(nativeInputFile(paths.input, offer.boundaryId, "committed"), offer);
|
|
60
|
+
append({ type: "wand_input_committed", id: offer.boundaryId, message: offer });
|
|
61
|
+
pending = null;
|
|
62
|
+
};
|
|
63
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
64
|
+
if (!paths.input || !Array.isArray(event.entries) || !event.context
|
|
65
|
+
|| event.outcome === "error" || event.outcome === "aborted" || ctx.signal?.aborted
|
|
66
|
+
|| event.message.stopReason === "error"
|
|
67
|
+
|| event.message.stopReason === "aborted")
|
|
68
|
+
return;
|
|
69
|
+
confirmInput(ctx);
|
|
70
|
+
// A prior unconfirmed handoff must never be followed by another injection.
|
|
71
|
+
if (pending || ctx.hasPendingMessages())
|
|
72
|
+
return;
|
|
73
|
+
const boundaryId = openNativeInputBoundary(paths.input);
|
|
74
|
+
append({ type: "wand_input_boundary", id: boundaryId });
|
|
75
|
+
try {
|
|
76
|
+
const deadline = Date.now() + 2000;
|
|
77
|
+
while (!ctx.signal?.aborted && Date.now() < deadline) {
|
|
78
|
+
const offer = claimNativeInput(paths.input, boundaryId);
|
|
79
|
+
if (offer !== undefined) {
|
|
80
|
+
if (!offer)
|
|
81
|
+
return;
|
|
82
|
+
pending = offer;
|
|
83
|
+
// Official awaited boundary entries commit to the native session before
|
|
84
|
+
// continuing. Unlike sendUserMessage(), no discarded async enqueue runs.
|
|
85
|
+
return { entries: [...event.entries, { type: "custom_message",
|
|
86
|
+
customType: "wand-user-input", content: offer.text, display: true,
|
|
87
|
+
details: { inputId: offer.id, boundaryId } }], continue: true };
|
|
88
|
+
}
|
|
89
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
writeNativeInput(nativeInputFile(paths.input, boundaryId, "closed"), { id: boundaryId });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
45
96
|
pi.on("agent_start", capture);
|
|
46
|
-
pi.on("turn_start", capture);
|
|
97
|
+
pi.on("turn_start", (event, ctx) => { confirmInput(ctx); capture(event); });
|
|
47
98
|
pi.on("message_start", capture);
|
|
48
99
|
pi.on("message_update", capture);
|
|
49
100
|
pi.on("message_end", capture);
|
|
@@ -51,8 +102,9 @@ export default function wandNativeEvents(pi) {
|
|
|
51
102
|
pi.on("tool_execution_start", capture);
|
|
52
103
|
pi.on("tool_execution_update", capture);
|
|
53
104
|
pi.on("tool_execution_end", capture);
|
|
54
|
-
pi.on("agent_end", capture);
|
|
105
|
+
pi.on("agent_end", (event, ctx) => { confirmInput(ctx); capture(event); });
|
|
55
106
|
pi.on("agent_settled", (event, ctx) => {
|
|
107
|
+
confirmInput(ctx);
|
|
56
108
|
append(event);
|
|
57
109
|
// Match the existing per-submit CLI lifetime. This boundary is later than
|
|
58
110
|
// agent_end: queued/background work, retry and compaction have drained.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Private per-execution mailbox; never a client request path or an SDK runtime. */
|
|
2
|
+
export interface PiInputHandoff {
|
|
3
|
+
id: string;
|
|
4
|
+
boundaryId: string;
|
|
5
|
+
incarnationId: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PiInputOffer extends PiInputHandoff {
|
|
8
|
+
text: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PiInputResolution {
|
|
11
|
+
state: "committed" | "unclaimed" | "unknown";
|
|
12
|
+
offer?: PiInputOffer;
|
|
13
|
+
}
|
|
14
|
+
export declare function nativeInputFile(directory: string, boundaryId: string, name: string): string;
|
|
15
|
+
export declare function writeNativeInput(file: string, value: unknown): void;
|
|
16
|
+
export declare function readNativeInput<T>(file: string): T | null;
|
|
17
|
+
export declare function openNativeInputBoundary(directory: string): string;
|
|
18
|
+
/** Queue reservation is synchronous and durable before publishing the response. */
|
|
19
|
+
export declare function offerNativeInput(directory: string, boundaryId: string, incarnationId: string, take: (identity: PiInputHandoff) => string | null): boolean;
|
|
20
|
+
/** Claim precedes any native transcript mutation; an absent claim is safe to release after close/exit. */
|
|
21
|
+
export declare function claimNativeInput(directory: string, boundaryId: string): PiInputOffer | null | undefined;
|
|
22
|
+
export declare function resolveNativeInput(directory: string, handoff: PiInputHandoff, exited: boolean): PiInputResolution;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { constants, closeSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
|
|
5
|
+
const MAX_MAILBOX_BYTES = 8 * 1024 * 1024;
|
|
6
|
+
export function nativeInputFile(directory, boundaryId, name) {
|
|
7
|
+
if (!UUID.test(boundaryId))
|
|
8
|
+
throw new Error("Pi 输入边界标识无效。");
|
|
9
|
+
return path.join(directory, boundaryId, name + ".json");
|
|
10
|
+
}
|
|
11
|
+
export function writeNativeInput(file, value) {
|
|
12
|
+
const temporary = file + "." + randomUUID() + ".tmp";
|
|
13
|
+
writeFileSync(temporary, JSON.stringify(value) + "\n", { mode: 0o600, flag: "wx" });
|
|
14
|
+
renameSync(temporary, file);
|
|
15
|
+
}
|
|
16
|
+
export function readNativeInput(file) {
|
|
17
|
+
let fd;
|
|
18
|
+
try {
|
|
19
|
+
fd = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.code === "ENOENT")
|
|
23
|
+
return null;
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const stat = fstatSync(fd);
|
|
28
|
+
if (!stat.isFile() || stat.size > MAX_MAILBOX_BYTES)
|
|
29
|
+
throw new Error("Pi 输入邮箱无效或超过容量。");
|
|
30
|
+
return JSON.parse(readFileSync(fd, "utf8"));
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
closeSync(fd);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function openNativeInputBoundary(directory) {
|
|
37
|
+
const id = randomUUID();
|
|
38
|
+
mkdirSync(path.join(directory, id), { recursive: true, mode: 0o700 });
|
|
39
|
+
writeNativeInput(nativeInputFile(directory, id, "request"), { id });
|
|
40
|
+
return id;
|
|
41
|
+
}
|
|
42
|
+
/** Queue reservation is synchronous and durable before publishing the response. */
|
|
43
|
+
export function offerNativeInput(directory, boundaryId, incarnationId, take) {
|
|
44
|
+
const file = (name) => nativeInputFile(directory, boundaryId, name);
|
|
45
|
+
if (readNativeInput(file("request"))?.id !== boundaryId
|
|
46
|
+
|| existsSync(file("closed")) || existsSync(file("response")) || existsSync(file("claimed")))
|
|
47
|
+
return false;
|
|
48
|
+
const identity = { id: randomUUID(), boundaryId, incarnationId };
|
|
49
|
+
const text = take(identity);
|
|
50
|
+
writeNativeInput(file("response"), text === null ? null : { ...identity, text });
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/** Claim precedes any native transcript mutation; an absent claim is safe to release after close/exit. */
|
|
54
|
+
export function claimNativeInput(directory, boundaryId) {
|
|
55
|
+
const response = nativeInputFile(directory, boundaryId, "response");
|
|
56
|
+
if (!existsSync(response))
|
|
57
|
+
return undefined;
|
|
58
|
+
const claimed = nativeInputFile(directory, boundaryId, "claimed");
|
|
59
|
+
renameSync(response, claimed);
|
|
60
|
+
const offer = readNativeInput(claimed);
|
|
61
|
+
if (offer === null)
|
|
62
|
+
return null;
|
|
63
|
+
if (!UUID.test(offer.id) || offer.boundaryId !== boundaryId || !UUID.test(offer.incarnationId)
|
|
64
|
+
|| typeof offer.text !== "string" || !offer.text.trim())
|
|
65
|
+
throw new Error("Pi 输入交接内容无效。");
|
|
66
|
+
return offer;
|
|
67
|
+
}
|
|
68
|
+
export function resolveNativeInput(directory, handoff, exited) {
|
|
69
|
+
const file = (name) => nativeInputFile(directory, handoff.boundaryId, name);
|
|
70
|
+
const committed = readNativeInput(file("committed"));
|
|
71
|
+
if (committed?.id === handoff.id && committed.incarnationId === handoff.incarnationId) {
|
|
72
|
+
return { state: "committed", offer: committed };
|
|
73
|
+
}
|
|
74
|
+
if (!existsSync(file("claimed")) && (exited || existsSync(file("closed"))))
|
|
75
|
+
return { state: "unclaimed" };
|
|
76
|
+
return { state: "unknown" };
|
|
77
|
+
}
|
package/dist/pi-tmux-host.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import { type PiInputHandoff, type PiInputResolution } from "./pi-native-input.js";
|
|
1
2
|
import { type StructuredExecHost, type StructuredExecProcess, type StructuredSpawnRequest, type StructuredRunState } from "./structured-exec-host.js";
|
|
2
3
|
interface NativeJob {
|
|
3
4
|
runId: string;
|
|
4
5
|
requestId: string;
|
|
5
6
|
incarnationId: string;
|
|
6
7
|
tmux: string;
|
|
8
|
+
pane?: string;
|
|
7
9
|
events: string;
|
|
8
10
|
stderr: string;
|
|
9
11
|
state: string;
|
|
10
12
|
ready: string;
|
|
11
13
|
ack: string;
|
|
14
|
+
input?: string;
|
|
12
15
|
}
|
|
13
16
|
interface WorkerState {
|
|
14
17
|
status: "starting" | "running" | "exited";
|
|
@@ -36,6 +39,8 @@ export declare class PiTmuxHost implements StructuredExecHost {
|
|
|
36
39
|
state(job: NativeJob): WorkerState;
|
|
37
40
|
private snapshot;
|
|
38
41
|
spawnStructured(request: StructuredSpawnRequest): Promise<StructuredExecProcess>;
|
|
42
|
+
offerPiInput(runId: string, boundaryId: string, take: (identity: PiInputHandoff) => string | null): boolean;
|
|
43
|
+
resolvePiInput(runId: string, handoff: PiInputHandoff): PiInputResolution;
|
|
39
44
|
attachRun(runId: string, checkpoint?: import("./pi-cli-checkpoint.js").PiCliCheckpoint): Promise<StructuredRunState | null>;
|
|
40
45
|
adoptRun(runId: string): Promise<StructuredExecProcess | null>;
|
|
41
46
|
listRuns(): Promise<StructuredRunState[]>;
|
|
@@ -47,21 +52,25 @@ export declare class PiTmuxHost implements StructuredExecHost {
|
|
|
47
52
|
* 而 forgetRun 只写 ack 标记、从不删目录,所以会按回合数无限堆积。
|
|
48
53
|
*
|
|
49
54
|
* 只回收三类确定无主的东西:
|
|
50
|
-
* - slot 已 ack 且 runId
|
|
55
|
+
* - slot 已 ack 且 runId 不在 activeSessionIds 里:整个 slot 无主;
|
|
51
56
|
* - slot 无主的,整槽删除;
|
|
52
57
|
* - slot 仍有主的,只删非 current.json 引用、已 ack 且进程已退出、超保留期的 turn。
|
|
53
58
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
59
|
+
* activeSessionIds 只含未归档会话。归档后不再续接这些 turn,已 ack 且进程退出
|
|
60
|
+
* 就可以整槽删除,不再额外等保留期;取消归档从会话自己的消息快照恢复。
|
|
61
|
+
* 保留未归档会话 current.json 指向的 turn:`listRuns` / `adoptRun` 仍靠它的
|
|
62
|
+
* events.jsonl 重放恢复;未 ack 的 slot 可能是尚未收尾的回合,一律不动。
|
|
56
63
|
*/
|
|
57
64
|
collectGarbage(options: {
|
|
58
|
-
/**
|
|
65
|
+
/** 未归档会话 id;不在其中的 slot(已删除或已归档)视为无主。 */
|
|
59
66
|
liveSessionIds: ReadonlySet<string>;
|
|
67
|
+
/** 与 liveSessionIds 同义。传入时优先于旧字段,避免把归档会话算成仍在使用。 */
|
|
68
|
+
activeSessionIds?: ReadonlySet<string>;
|
|
60
69
|
retentionMs: number;
|
|
61
70
|
now?: number;
|
|
62
71
|
}): PiNativeGcReport;
|
|
63
72
|
private readJob;
|
|
64
|
-
/** 无主 slot 是否已整体静默:所有 turn
|
|
73
|
+
/** 无主 slot 是否已整体静默:所有 turn 都不再是运行中。retentionMs 为 0 时不等待。 */
|
|
65
74
|
private slotIsIdle;
|
|
66
75
|
}
|
|
67
76
|
/** pi-native 回收结果:删了几个 slot / 几个旧 turn / 释放多少字节。 */
|