@co0ontty/wand 4.105.1 → 4.106.0
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/dist/build-info.json +3 -3
- package/dist/cli.js +7 -2
- package/dist/distribution-manager.d.ts +2 -0
- package/dist/distribution-manager.js +23 -0
- package/dist/employee-input-scheduling.d.ts +2 -1
- package/dist/employee-knowledge-cli.d.ts +1 -1
- package/dist/employee-knowledge-cli.js +91 -20
- package/dist/employee-knowledge-content.js +1 -1
- package/dist/employee-knowledge-types.d.ts +1 -1
- package/dist/employee-knowledge-types.js +1 -1
- package/dist/employee-knowledge.d.ts +6 -3
- package/dist/employee-knowledge.js +54 -58
- package/dist/env-utils.js +1 -1
- package/dist/html-preview-policy.d.ts +4 -0
- package/dist/html-preview-policy.js +4 -0
- package/dist/ios-ota.d.ts +5 -0
- package/dist/ios-ota.js +11 -1
- package/dist/knowledge-skill.d.ts +2 -0
- package/dist/knowledge-skill.js +17 -0
- package/dist/knowledge-types.d.ts +36 -0
- package/dist/knowledge-types.js +8 -0
- package/dist/managed-skills-ensure.js +2 -0
- package/dist/middleware/api-scope.js +2 -0
- package/dist/path-repair.js +28 -6
- package/dist/pi-resource-catalog.d.ts +3 -1
- package/dist/pi-resource-catalog.js +25 -0
- package/dist/pi-session-settings.d.ts +2 -0
- package/dist/pi-session-settings.js +25 -0
- package/dist/process-manager.d.ts +2 -0
- package/dist/process-manager.js +35 -12
- package/dist/quick-terminal-session.d.ts +3 -0
- package/dist/quick-terminal-session.js +5 -0
- package/dist/retention-protection.d.ts +15 -0
- package/dist/retention-protection.js +134 -0
- package/dist/retention.d.ts +3 -2
- package/dist/retention.js +58 -76
- package/dist/server-employee-routes.js +1 -1
- package/dist/server-file-routes.js +84 -16
- package/dist/server-knowledge-routes.d.ts +4 -0
- package/dist/server-knowledge-routes.js +61 -0
- package/dist/server-local-preview-routes.js +28 -6
- package/dist/server-pi-defaults-routes.d.ts +1 -1
- package/dist/server-pi-defaults-routes.js +13 -20
- package/dist/server-session-lifecycle-routes.js +17 -17
- package/dist/server-session-list-routes.js +1 -1
- package/dist/server-task-routes.js +6 -0
- package/dist/server-update-routes.d.ts +1 -0
- package/dist/server-update-routes.js +62 -17
- package/dist/server-workspace-routes.js +5 -5
- package/dist/server.js +4 -2
- package/dist/session-questions.d.ts +7 -0
- package/dist/session-questions.js +25 -0
- package/dist/session-registry.d.ts +3 -0
- package/dist/session-registry.js +17 -0
- package/dist/session-request.d.ts +1 -1
- package/dist/session-request.js +6 -2
- package/dist/session-service.js +1 -1
- package/dist/storage/employees-repository.js +5 -2
- package/dist/storage/knowledge-repository.d.ts +35 -0
- package/dist/storage/knowledge-repository.js +208 -0
- package/dist/storage/mappers.js +1 -1
- package/dist/storage/migrations.js +51 -0
- package/dist/storage/sessions-repository.d.ts +2 -0
- package/dist/storage/sessions-repository.js +16 -0
- package/dist/storage/tasks-repository.d.ts +7 -0
- package/dist/storage/tasks-repository.js +21 -0
- package/dist/storage/workspaces-repository.d.ts +1 -0
- package/dist/storage/workspaces-repository.js +2 -1
- package/dist/storage.d.ts +3 -1
- package/dist/storage.js +4 -0
- package/dist/structured-session-manager.d.ts +3 -0
- package/dist/structured-session-manager.js +13 -4
- package/dist/task-action-types.d.ts +22 -0
- package/dist/task-action-types.js +1 -0
- package/dist/task-actions.d.ts +5 -0
- package/dist/task-actions.js +86 -0
- package/dist/task-execution-queue.d.ts +1 -1
- package/dist/task-execution-queue.js +4 -1
- package/dist/task-retention.d.ts +4 -2
- package/dist/task-retention.js +2 -0
- package/dist/types.d.ts +2 -1
- package/dist/web-ui/content/ai-teams.js +12 -12
- package/dist/web-ui/content/module-manifest.json +1 -1
- package/dist/web-ui/content/modules/00b168a19664439e/dropdown-surface.js +1 -0
- package/dist/web-ui/content/modules/059c5fb7f128d340/task-board-host.js +27 -0
- package/dist/web-ui/content/modules/07494f5a623e4471/chat-render.js +76 -0
- package/dist/web-ui/content/modules/{bc28252270bcc51a → 0a743ad3b4db6874}/chunk.js +1 -1
- package/dist/web-ui/content/modules/0d76e582a7a71b2f/chunk.js +1 -0
- package/dist/web-ui/content/modules/11af5d14b30e57b6/chunk.js +1 -0
- package/dist/web-ui/content/modules/16dab1297efd71a7/chunk.js +10 -0
- package/dist/web-ui/content/modules/182929e188c1ab3a/host.js +5 -0
- package/dist/web-ui/content/modules/1858670c0f84fec5/employee-profile.js +1 -0
- package/dist/web-ui/content/modules/{1cf252d2f64a6279 → 1eab7410d5586038}/chunk.js +1 -1
- package/dist/web-ui/content/modules/20c47dc0442f5a04/host.js +1 -0
- package/dist/web-ui/content/modules/226241b999d12008/host.js +1 -0
- package/dist/web-ui/content/modules/23c4b8d223ec64c5/host.js +61 -0
- package/dist/web-ui/content/modules/2882c97388ff4f7b/employee-card.js +1 -0
- package/dist/web-ui/content/modules/2a791cd2e14aa1e0/notice-view.js +1 -0
- package/dist/web-ui/content/modules/2c90c3be260e6cce/progress.js +1 -0
- package/dist/web-ui/content/modules/2d7be9935394f397/workspace-window.js +1 -0
- package/dist/web-ui/content/modules/2f801e53dda62770/chunk.js +1 -0
- package/dist/web-ui/content/modules/2f97ddba3842adaa/chunk.js +1 -0
- package/dist/web-ui/content/modules/31157ceb6d23b809/chunk.js +15 -0
- package/dist/web-ui/content/modules/389a5112332ec23a/host.js +1 -0
- package/dist/web-ui/content/modules/3ea13e8b3c2deaa4/panel-host.js +1 -0
- package/dist/web-ui/content/modules/3f8830f8ad28decc/chunk.js +3 -0
- package/dist/web-ui/content/modules/{70715360138f4d99 → 3fe11a75284ad8ae}/host.js +1 -1
- package/dist/web-ui/content/modules/{e0a1ae9163e77556 → 41445c6589dba325}/host.js +2 -2
- package/dist/web-ui/content/modules/{8b2f9a027ae1d7eb → 42e5c34002fcffb7}/chunk.js +1 -1
- package/dist/web-ui/content/modules/438807b8bf112cd0/chunk.js +1 -0
- package/dist/web-ui/content/modules/4861eca9a8a87ad0/chunk.js +1 -0
- package/dist/web-ui/content/modules/{78d06dfdc4b13d78 → 4970319a83432ec7}/chunk.js +1 -1
- package/dist/web-ui/content/modules/4a5d669c90f545cb/host.js +1 -0
- package/dist/web-ui/content/modules/4ba1e8c74bed7ef2/chunk.js +1 -0
- package/dist/web-ui/content/modules/5083a5dbb5b155b7/chunk.js +1 -0
- package/dist/web-ui/content/modules/50da446c1377c220/chunk.js +20 -0
- package/dist/web-ui/content/modules/52574891ef08e29b/host.js +30 -0
- package/dist/web-ui/content/modules/5705d25468274534/host.js +1 -0
- package/dist/web-ui/content/modules/579d486763d47c0d/chunk.js +1 -0
- package/dist/web-ui/content/modules/57c357d321db5df9/chunk.js +1 -0
- package/dist/web-ui/content/modules/5933ce8965aa4004/chunk.js +1 -0
- package/dist/web-ui/content/modules/5961ab2947c86ca1/shell-file-panel-drawer.js +1 -0
- package/dist/web-ui/content/modules/5b6e1369df7dea69/chunk.js +1 -0
- package/dist/web-ui/content/modules/5f298a8f43dd0589/chunk.js +1 -0
- package/dist/web-ui/content/modules/62e0023451f428cf/chunk.js +1 -0
- package/dist/web-ui/content/modules/6787bdcf56f743a5/host.js +1 -0
- package/dist/web-ui/content/modules/67c39b0398186a9f/host.js +1 -0
- package/dist/web-ui/content/modules/6b16f35c0627bafb/chunk.js +1 -0
- package/dist/web-ui/content/modules/6dd4f3e2f619f7a0/chunk.js +1 -0
- package/dist/web-ui/content/modules/70a2e13e0ccc8549/panel.js +1 -0
- package/dist/web-ui/content/modules/710db590e9707f73/runtime.js +338 -0
- package/dist/web-ui/content/modules/7249950f8771dbeb/workspaces-panel.js +4 -0
- package/dist/web-ui/content/modules/771439a41e737ee2/chunk.js +1 -0
- package/dist/web-ui/content/modules/{2061d4a88875b9f9 → 816eb9b3a000fb2c}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{c0384ffb4ef740a0 → 831b6c7b43e800be}/host.js +1 -1
- package/dist/web-ui/content/modules/87c7f50d9455d55e/chunk.js +1 -0
- package/dist/web-ui/content/modules/87dcd516ffd9f688/composer-surface-adapter.js +1 -0
- package/dist/web-ui/content/modules/8891ca8165435ca1/chunk.js +1 -0
- package/dist/web-ui/content/modules/8b7cd7bffdfd69e7/brand-identity.js +1 -0
- package/dist/web-ui/content/modules/8bf9917f6c3429f6/host.js +1 -0
- package/dist/web-ui/content/modules/8de8368570ab089d/chunk.js +57 -0
- package/dist/web-ui/content/modules/8e612d4881fa4118/home.js +6 -0
- package/dist/web-ui/content/modules/8e6178262ea63dec/markdown.js +1 -0
- package/dist/web-ui/content/modules/913187c92a98a1ac/chunk.js +1 -0
- package/dist/web-ui/content/modules/96f2ae970f8c576b/host.js +1 -0
- package/dist/web-ui/content/modules/97d7f267959fd385/chunk.js +1 -0
- package/dist/web-ui/content/modules/{22497085d7cdd83b → 9b12c2301f3a18f3}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{9f4e26a1c1867d65 → 9d941d6ff39861b8}/chunk.js +2 -2
- package/dist/web-ui/content/modules/9e163d54ff8fd1aa/chunk.js +1 -0
- package/dist/web-ui/content/modules/{0567e161e2cca41e → a2caed0139df4b21}/chunk.js +1 -1
- package/dist/web-ui/content/modules/a59734be368daa7e/chunk.js +1 -0
- package/dist/web-ui/content/modules/{e4503f8e6379ccdc → a61393bfa976d8d5}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{df5d48b4ff04b443 → b63e075f222a1266}/library-bridge.js +2 -2
- package/dist/web-ui/content/modules/b713cad51ca4c512/chunk.js +1 -0
- package/dist/web-ui/content/modules/ba89cd97627e6682/queue-view-adapter.js +1 -0
- package/dist/web-ui/content/modules/bd3daf04a8bf4422/chunk.js +1 -0
- package/dist/web-ui/content/modules/{aa7d409d04bbd736 → bd7dc4bb46731ee7}/chunk.js +1 -1
- package/dist/web-ui/content/modules/bf3ee58e0da44822/chunk.js +1 -0
- package/dist/web-ui/content/modules/c0a446100d969f3e/chunk.js +8 -0
- package/dist/web-ui/content/modules/c2f09c8a472f5d83/dialog-modal.js +4 -0
- package/dist/web-ui/content/modules/c33935155289c22d/chunk.js +3 -0
- package/dist/web-ui/content/modules/c351eb11e0bdec2c/sidebar-peek.js +1 -0
- package/dist/web-ui/content/modules/ca7ca5902a35d26e/chunk.js +1 -0
- package/dist/web-ui/content/modules/cba6f13c0b2aec1c/chunk.js +1 -0
- package/dist/web-ui/content/modules/d434edbb2f4672e5/chunk.js +1 -0
- package/dist/web-ui/content/modules/{d569c96d4acfdec3 → d47d75a78ae598e3}/chunk.js +25 -25
- package/dist/web-ui/content/modules/d49f106c4d106057/chunk.js +1 -0
- package/dist/web-ui/content/modules/d5d28ec375826ef2/chunk.js +3 -0
- package/dist/web-ui/content/modules/{7edec8f23f8e0834 → d6b16dedb9011c33}/chunk.js +1 -1
- package/dist/web-ui/content/modules/d6fffe2c2a3a738d/toast-region.js +1 -0
- package/dist/web-ui/content/modules/d8a7011760f61719/group-editor.js +1 -0
- package/dist/web-ui/content/modules/{bf0b693a654e2655 → d9e2b65e9679ad39}/host.js +3 -3
- package/dist/web-ui/content/modules/db521a246e14e5e4/chunk.js +4 -0
- package/dist/web-ui/content/modules/{7994dec16e8b1039 → dedd4a19bb70e327}/page.js +2 -2
- package/dist/web-ui/content/modules/e060508ecd50fa7e/chunk.js +1 -0
- package/dist/web-ui/content/modules/e5174201f9d918fb/chunk.js +1 -0
- package/dist/web-ui/content/modules/e63582a3bce48f7d/chunk.js +1 -0
- package/dist/web-ui/content/modules/ea8fe4b9999fd2aa/chunk.js +1 -0
- package/dist/web-ui/content/modules/{d7cdd7dca9a0c9f8 → eac72f69716dba05}/chunk.js +1 -1
- package/dist/web-ui/content/modules/ebc53dedc3945514/host.js +1 -0
- package/dist/web-ui/content/modules/{0d7f7d954382dc42 → ec33e17c00c1eb24}/chunk.js +1 -1
- package/dist/web-ui/content/modules/{99e5cdf0c3bd0e3d → ed7405f064944215}/sha256.js +1 -1
- package/dist/web-ui/content/modules/f27a5d3c6c02a71c/chunk.js +1 -0
- package/dist/web-ui/content/modules/f383d892fef73483/chunk.js +1 -0
- package/dist/web-ui/content/modules/{084dca1ea52bf38e → f3cff7792ba39df8}/login-visual.js +1 -1
- package/dist/web-ui/content/modules/f43da7050badf54c/host.js +1 -0
- package/dist/web-ui/content/modules/face3dbf117d63e3/chunk.js +1 -0
- package/dist/web-ui/content/modules/fb2356eced24c4cc/host-bridge.js +1 -0
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.d.ts +413 -420
- package/dist/web-ui/embedded-assets.js +5 -5
- package/dist/web-ui/group-chat-visibility.d.ts +1 -0
- package/dist/web-ui/group-chat-visibility.js +1 -1
- package/dist/web-ui/markdown.js +10 -36
- package/dist/web-ui/message-file-target.d.ts +8 -0
- package/dist/web-ui/message-file-target.js +105 -15
- package/dist/web-ui/page-route.js +2 -0
- package/dist/web-ui/react/local-preview/controller.d.ts +2 -2
- package/dist/web-ui/react/local-preview/controller.js +53 -10
- package/dist/web-ui/react/workspaces/types.d.ts +1 -0
- package/dist/web-ui/server-link-url.d.ts +3 -0
- package/dist/web-ui/server-link-url.js +18 -0
- package/package.json +2 -1
- package/skills/wand-knowledge/SKILL.md +47 -0
- package/skills/wand-knowledge/scripts/knowledge.mjs +18 -0
- package/dist/web-ui/content/modules/0341f0d00f88a16f/chunk.js +0 -1
- package/dist/web-ui/content/modules/069f5a4267b0a9e3/runtime.js +0 -279
- package/dist/web-ui/content/modules/0d62c22b7aed5f1f/host-bridge.js +0 -1
- package/dist/web-ui/content/modules/0da28408ec089cf7/chunk.js +0 -1
- package/dist/web-ui/content/modules/130e8905cc7f639c/toast-region.js +0 -1
- package/dist/web-ui/content/modules/186d6e3b44a3c539/host.js +0 -1
- package/dist/web-ui/content/modules/1870280f0e783c62/chunk.js +0 -4
- package/dist/web-ui/content/modules/19d3bc36fa1e9160/chunk.js +0 -1
- package/dist/web-ui/content/modules/1a0b5db535599f72/chunk.js +0 -1
- package/dist/web-ui/content/modules/1d1dc0fd8a0210aa/composer-surface-adapter.js +0 -1
- package/dist/web-ui/content/modules/23f15023aa2279a3/shell-file-panel-drawer.js +0 -1
- package/dist/web-ui/content/modules/2cab58e5d148844c/host.js +0 -1
- package/dist/web-ui/content/modules/32edeb64dbf339f6/chunk.js +0 -1
- package/dist/web-ui/content/modules/33daaeb4bf68c7ba/chunk.js +0 -9
- package/dist/web-ui/content/modules/37da177222b6308d/workspaces-panel.js +0 -4
- package/dist/web-ui/content/modules/3af7facf8a7588c7/chunk.js +0 -1
- package/dist/web-ui/content/modules/3b95dc1376e932ea/host.js +0 -1
- package/dist/web-ui/content/modules/3cbbad48df7998f8/chunk.js +0 -1
- package/dist/web-ui/content/modules/41cf79c899bc83c8/host.js +0 -1
- package/dist/web-ui/content/modules/444af9de1698eea0/host.js +0 -1
- package/dist/web-ui/content/modules/452327275cc5d8f8/host.js +0 -1
- package/dist/web-ui/content/modules/466310a17dac465f/chunk.js +0 -3
- package/dist/web-ui/content/modules/46f1b6c3274d399b/host.js +0 -1
- package/dist/web-ui/content/modules/47998d82dc65bfeb/sidebar-peek.js +0 -1
- package/dist/web-ui/content/modules/486a828390c5d44d/host.js +0 -30
- package/dist/web-ui/content/modules/4cc15b385bc6cc32/chunk.js +0 -1
- package/dist/web-ui/content/modules/4d5ab67f6c0ce19f/chunk.js +0 -1
- package/dist/web-ui/content/modules/54aaf60ac85dcb2b/chunk.js +0 -1
- package/dist/web-ui/content/modules/54c22eddaf009d00/chunk.js +0 -1
- package/dist/web-ui/content/modules/55b931232c672f4f/chunk.js +0 -1
- package/dist/web-ui/content/modules/583049de28ef2b02/panel.js +0 -1
- package/dist/web-ui/content/modules/5a2a58334d63e127/chunk.js +0 -20
- package/dist/web-ui/content/modules/5ba0668f5a22133b/home.js +0 -6
- package/dist/web-ui/content/modules/5cd5d3d65d966c5f/chunk.js +0 -1
- package/dist/web-ui/content/modules/6b41e59ab342482f/notice-view.js +0 -1
- package/dist/web-ui/content/modules/6b5445f41bef0a66/chunk.js +0 -1
- package/dist/web-ui/content/modules/6e8395f61c89b5aa/chunk.js +0 -1
- package/dist/web-ui/content/modules/6f74cede70164e19/chunk.js +0 -1
- package/dist/web-ui/content/modules/704690f5d6ab9157/chunk.js +0 -1
- package/dist/web-ui/content/modules/7dce56c1987e8d43/chunk.js +0 -29
- package/dist/web-ui/content/modules/80e92c14a6789896/progress.js +0 -1
- package/dist/web-ui/content/modules/8a828b4d087667dd/chunk.js +0 -1
- package/dist/web-ui/content/modules/8d6f223764846e18/brand-identity.js +0 -1
- package/dist/web-ui/content/modules/9136bae16758fd4c/chunk.js +0 -1
- package/dist/web-ui/content/modules/93fa5c35e4bcab96/chunk.js +0 -1
- package/dist/web-ui/content/modules/96fe39f39930d9e4/chunk.js +0 -8
- package/dist/web-ui/content/modules/9db41ad5ac96ffa2/chunk.js +0 -1
- package/dist/web-ui/content/modules/a073cb6d182ccd32/chunk.js +0 -1
- package/dist/web-ui/content/modules/a8cba9f1d6b49ef1/chunk.js +0 -1
- package/dist/web-ui/content/modules/a97b3b9b68f9ca38/chunk.js +0 -57
- package/dist/web-ui/content/modules/ac34ed6692b43ede/dropdown-surface.js +0 -1
- package/dist/web-ui/content/modules/aff19b31898aa157/chunk.js +0 -1
- package/dist/web-ui/content/modules/b2f6e088e6e45878/chunk.js +0 -1
- package/dist/web-ui/content/modules/b4446bf92e182bfb/employee-card.js +0 -1
- package/dist/web-ui/content/modules/b79c15aace456028/chunk.js +0 -6
- package/dist/web-ui/content/modules/b8c18baf7cfb61b1/chat-render.js +0 -76
- package/dist/web-ui/content/modules/b8c447171e3d60a0/chunk.js +0 -1
- package/dist/web-ui/content/modules/b945370f1fa784f9/chunk.js +0 -13
- package/dist/web-ui/content/modules/ba5fa76884e1f806/chunk.js +0 -1
- package/dist/web-ui/content/modules/bb1bb9460cc10fbe/host.js +0 -1
- package/dist/web-ui/content/modules/be7f1f9183a1247f/chunk.js +0 -3
- package/dist/web-ui/content/modules/bf7449bcf95e1454/chunk.js +0 -1
- package/dist/web-ui/content/modules/c091efd46e221c29/chunk.js +0 -1
- package/dist/web-ui/content/modules/c1ec2ebce8ea5dd8/dialog-modal.js +0 -4
- package/dist/web-ui/content/modules/c2f7bb50118f1603/chunk.js +0 -1
- package/dist/web-ui/content/modules/c369ce4eb71277e6/host.js +0 -1
- package/dist/web-ui/content/modules/c46b3501573953e9/chunk.js +0 -1
- package/dist/web-ui/content/modules/c577ad641502d509/panel-host.js +0 -1
- package/dist/web-ui/content/modules/c75976857c18f251/host.js +0 -1
- package/dist/web-ui/content/modules/c8eaffbedda0d3d0/queue-view-adapter.js +0 -3
- package/dist/web-ui/content/modules/cadd26ae50359434/chunk.js +0 -1
- package/dist/web-ui/content/modules/d37ff6684c9aeeec/chunk.js +0 -3
- package/dist/web-ui/content/modules/d48c1f89342bc4bc/workspace-window.js +0 -1
- package/dist/web-ui/content/modules/d859a83f486aab23/chunk.js +0 -15
- package/dist/web-ui/content/modules/d8b258a6455d77da/host.js +0 -5
- package/dist/web-ui/content/modules/d937e939122180b9/host.js +0 -1
- package/dist/web-ui/content/modules/e020beea3e7da7db/chunk.js +0 -1
- package/dist/web-ui/content/modules/e2feda63a603c64e/group-editor.js +0 -1
- package/dist/web-ui/content/modules/e70296674a841d60/chunk.js +0 -1
- package/dist/web-ui/content/modules/ec2f6b23d12d3a15/chunk.js +0 -1
- package/dist/web-ui/content/modules/ede751e1f2500d03/employee-profile.js +0 -1
- package/dist/web-ui/content/modules/ee4301b734e2fd43/chunk.js +0 -1
- package/dist/web-ui/content/modules/f4e6095edc32562c/markdown.js +0 -1
- package/dist/web-ui/content/modules/f7f0710962cab0e0/chunk.js +0 -10
- package/dist/web-ui/content/modules/fe30627fc1e59d4f/chunk.js +0 -1
- package/dist/web-ui/content/modules/ffbd3bcaecd8e814/task-board-host.js +0 -10
package/dist/path-repair.js
CHANGED
|
@@ -303,6 +303,9 @@ function probeLoginShell(shell, timeoutMs) {
|
|
|
303
303
|
`printf '\\n${ENV_BEGIN_MARKER}\\n'; command env; printf '${ENV_END_MARKER}\\n'`;
|
|
304
304
|
return new Promise((resolve, reject) => {
|
|
305
305
|
const child = spawn(shell, ["-lic", script], {
|
|
306
|
+
// Own a separate probe group: shell rc can spawn children which inherit
|
|
307
|
+
// our pipes, and an interactive shell may ignore SIGTERM.
|
|
308
|
+
detached: process.platform !== "win32",
|
|
306
309
|
env: {
|
|
307
310
|
...process.env,
|
|
308
311
|
// 防止 PROMPT_COMMAND / PS1 等钩子往 stdout 喷东西干扰解析
|
|
@@ -314,6 +317,25 @@ function probeLoginShell(shell, timeoutMs) {
|
|
|
314
317
|
let stdout = "";
|
|
315
318
|
let stderr = "";
|
|
316
319
|
let settled = false;
|
|
320
|
+
let killTimer;
|
|
321
|
+
const terminateProbe = (signal) => {
|
|
322
|
+
if (process.platform !== "win32" && child.pid) {
|
|
323
|
+
try {
|
|
324
|
+
process.kill(-child.pid, signal);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
catch { /* group already gone, or spawn failed */ }
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
child.kill(signal);
|
|
331
|
+
}
|
|
332
|
+
catch { /* probe already exited */ }
|
|
333
|
+
};
|
|
334
|
+
const clearTimers = () => {
|
|
335
|
+
clearTimeout(timer);
|
|
336
|
+
if (killTimer)
|
|
337
|
+
clearTimeout(killTimer);
|
|
338
|
+
};
|
|
317
339
|
const finalize = (fn) => {
|
|
318
340
|
if (settled)
|
|
319
341
|
return;
|
|
@@ -321,20 +343,20 @@ function probeLoginShell(shell, timeoutMs) {
|
|
|
321
343
|
fn();
|
|
322
344
|
};
|
|
323
345
|
const timer = setTimeout(() => {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
346
|
+
terminateProbe("SIGTERM");
|
|
347
|
+
// Kill only this detached probe group, never a provider PTY/daemon or
|
|
348
|
+
// another session. Descendants can keep stdout open after the shell exits.
|
|
349
|
+
killTimer = setTimeout(() => terminateProbe("SIGKILL"), 250);
|
|
328
350
|
finalize(() => reject(new Error(`login shell probe timed out after ${timeoutMs}ms`)));
|
|
329
351
|
}, timeoutMs);
|
|
330
352
|
child.stdout?.on("data", (b) => { stdout += b.toString(); });
|
|
331
353
|
child.stderr?.on("data", (b) => { stderr += b.toString(); });
|
|
332
354
|
child.on("error", (err) => {
|
|
333
|
-
|
|
355
|
+
clearTimers();
|
|
334
356
|
finalize(() => reject(err));
|
|
335
357
|
});
|
|
336
358
|
child.on("close", (code) => {
|
|
337
|
-
|
|
359
|
+
clearTimers();
|
|
338
360
|
finalize(() => {
|
|
339
361
|
if (!stdout && code !== 0) {
|
|
340
362
|
reject(new Error(`login shell exited ${code}: ${stderr.trim().slice(0, 200)}`));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PiResourceCatalog, type PiResourceSelection, type PiSessionSettings, type PiSessionSettingsPatch } from "./pi-session-settings.js";
|
|
2
2
|
import type { WandConfig } from "./types.js";
|
|
3
3
|
import type { Skill, McpExtensionOptions } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
type McpConfig = ReturnType<NonNullable<McpExtensionOptions["loadConfig"]>>;
|
|
@@ -15,4 +15,6 @@ export declare function resolvePiResourceSelection(inventory: PiResourceInventor
|
|
|
15
15
|
skills: Skill[];
|
|
16
16
|
servers: McpConfig["servers"];
|
|
17
17
|
};
|
|
18
|
+
/** Validate additions; an explicit unlock also releases that skill's unavailable selected reference. */
|
|
19
|
+
export declare function resolvePiResourceSettingsPatch(inventory: PiResourceInventory, current: PiSessionSettings, patch: PiSessionSettingsPatch): PiSessionSettingsPatch;
|
|
18
20
|
export {};
|
|
@@ -3,6 +3,7 @@ import { realpath, stat } from "node:fs/promises";
|
|
|
3
3
|
import { coreHarnessAgentDir } from "./harness-engine.js";
|
|
4
4
|
import { importPiCliModule, resolvePiCliPackage } from "./pi-cli-runtime.js";
|
|
5
5
|
import { cliPiResourceReader, installedPiUserResources } from "./pi-user-resources.js";
|
|
6
|
+
import { patchPiSessionSettings } from "./pi-session-settings.js";
|
|
6
7
|
function resourceId(kind, identity) {
|
|
7
8
|
return `${kind}-${createHash("sha256").update(identity).digest("hex").slice(0, 24)}`;
|
|
8
9
|
}
|
|
@@ -65,3 +66,27 @@ export function resolvePiResourceSelection(inventory, selection) {
|
|
|
65
66
|
});
|
|
66
67
|
return { skills, servers };
|
|
67
68
|
}
|
|
69
|
+
/** Validate additions; an explicit unlock also releases that skill's unavailable selected reference. */
|
|
70
|
+
export function resolvePiResourceSettingsPatch(inventory, current, patch) {
|
|
71
|
+
const next = patchPiSessionSettings(current, patch);
|
|
72
|
+
let resolved = patch;
|
|
73
|
+
if (patch.lockedSkills !== undefined && next.resources) {
|
|
74
|
+
const released = new Set((current.lockedSkills ?? []).filter((id) => !next.lockedSkills.includes(id) && !inventory.skills.has(id)));
|
|
75
|
+
if (next.resources.skills.some((id) => released.has(id))) {
|
|
76
|
+
resolved = { ...patch, resources: { ...next.resources,
|
|
77
|
+
skills: next.resources.skills.filter((id) => !released.has(id)) } };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Keep other stale references until explicitly removed, so several unavailable items can be
|
|
81
|
+
// repaired one at a time. Execution still validates the complete selection and fails closed.
|
|
82
|
+
if (resolved.resources !== undefined)
|
|
83
|
+
resolvePiResourceSelection(inventory, {
|
|
84
|
+
skills: resolved.resources.skills.filter((id) => !current.resources?.skills.includes(id)),
|
|
85
|
+
mcpServers: resolved.resources.mcpServers.filter((id) => !current.resources?.mcpServers.includes(id)),
|
|
86
|
+
});
|
|
87
|
+
if (resolved.lockedSkills !== undefined)
|
|
88
|
+
resolvePiResourceSelection(inventory, {
|
|
89
|
+
skills: resolved.lockedSkills.filter((id) => !current.lockedSkills?.includes(id)), mcpServers: [],
|
|
90
|
+
});
|
|
91
|
+
return resolved;
|
|
92
|
+
}
|
|
@@ -33,6 +33,8 @@ export type PiSkillMode = "off" | "on" | "locked";
|
|
|
33
33
|
export declare function piSkillMode(settings: PiSessionSettings, id: string): PiSkillMode;
|
|
34
34
|
/** A single manual gesture changes selection and lock atomically, never mutating its snapshot. */
|
|
35
35
|
export declare function piSkillModePatch(settings: PiSessionSettings, id: string, mode: PiSkillMode): PiSessionSettingsPatch;
|
|
36
|
+
/** The server may also deselect a newly unlocked Skill if it disappeared since the panel loaded. */
|
|
37
|
+
export declare function piResourceSettingsConfirmed(current: PiSessionSettings, patch: PiSessionSettingsPatch, confirmed: PiSessionSettings | undefined): boolean;
|
|
36
38
|
export interface PiResourceSelectionNotice {
|
|
37
39
|
status: "selecting" | "selected" | "fallback" | "cancelled";
|
|
38
40
|
label: string;
|
|
@@ -18,6 +18,31 @@ export function piSkillModePatch(settings, id, mode) {
|
|
|
18
18
|
lockedSkills: mode === "locked" ? [...new Set([...locks, id])] : locks.filter((value) => value !== id),
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
/** The server may also deselect a newly unlocked Skill if it disappeared since the panel loaded. */
|
|
22
|
+
export function piResourceSettingsConfirmed(current, patch, confirmed) {
|
|
23
|
+
if (!confirmed)
|
|
24
|
+
return false;
|
|
25
|
+
if (patch.lockedSkills !== undefined
|
|
26
|
+
&& JSON.stringify(confirmed.lockedSkills) !== JSON.stringify(patch.lockedSkills))
|
|
27
|
+
return false;
|
|
28
|
+
if (patch.resources === undefined && patch.lockedSkills === undefined)
|
|
29
|
+
return true;
|
|
30
|
+
const expected = patch.resources ?? current.resources;
|
|
31
|
+
if (!confirmed.resources)
|
|
32
|
+
return !expected && !patch.lockedSkills?.length;
|
|
33
|
+
let resources;
|
|
34
|
+
try {
|
|
35
|
+
resources = patchPiResourceSelection(confirmed.resources);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const unlocked = new Set(patch.lockedSkills === undefined ? []
|
|
41
|
+
: (current.lockedSkills ?? []).filter((id) => !patch.lockedSkills.includes(id)));
|
|
42
|
+
const skills = (expected?.skills ?? []).filter((id) => !unlocked.has(id) || resources.skills.includes(id));
|
|
43
|
+
return JSON.stringify(resources.skills) === JSON.stringify(skills)
|
|
44
|
+
&& JSON.stringify(resources.mcpServers) === JSON.stringify(expected?.mcpServers ?? []);
|
|
45
|
+
}
|
|
21
46
|
export function patchPiResourceSelection(raw) {
|
|
22
47
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
23
48
|
throw new Error("请选择有效的 Skills / MCP 列表。");
|
|
@@ -302,6 +302,8 @@ export declare class ProcessManager extends EventEmitter {
|
|
|
302
302
|
private snapshotSlim;
|
|
303
303
|
private isPermissionBlocked;
|
|
304
304
|
setSessionWorkspace(id: string, membership: Pick<SessionSnapshot, "workspaceId" | "workspaceTaskId">): void;
|
|
305
|
+
/** The source is committed by storage before the live checkpoint projection is refreshed. */
|
|
306
|
+
setSessionSource(id: string, sessionSource: SessionSource): void;
|
|
305
307
|
setSessionTopic(id: string, title: string, description: string): SessionSnapshot;
|
|
306
308
|
private setSessionTopicGenerating;
|
|
307
309
|
/**
|
package/dist/process-manager.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { knowledgeInstructions } from "./employee-knowledge.js";
|
|
1
2
|
import { isOpenRouterFreeSelector } from "./openrouter-free-models.js";
|
|
2
3
|
import { isAutoAssignSelector, defaultModelGroupSelector, resolveModelGroupModels } from "./model-groups.js";
|
|
3
4
|
import { resolveAutoAssign } from "./model-auto-assign.js";
|
|
@@ -563,6 +564,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
563
564
|
// PTY emits its asynchronous exit event. Ignore that stale incarnation.
|
|
564
565
|
if (current !== record || current.ptyProcess !== child)
|
|
565
566
|
return;
|
|
567
|
+
this.storage.revokeSessionKnowledgeAccess(record.id);
|
|
566
568
|
current.timers.clearAll();
|
|
567
569
|
if (current.ptyBridge) {
|
|
568
570
|
current.ptyBridge.onExit(exitCode);
|
|
@@ -844,10 +846,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
844
846
|
let processedCommand = this.processCommandForMode(command, effectiveMode, provider, preferredModels[0], initialThinkingEffort);
|
|
845
847
|
// 会话级系统提示:provider 有系统提示开关就走开关(PTY 与结构化行为一致),没有就并进首条输入。
|
|
846
848
|
const systemPrompt = (opts?.systemPrompt ?? inheritedRole?.systemPrompt)?.trim() || undefined;
|
|
847
|
-
|
|
848
|
-
? composeSystemFallback(systemPrompt, initialInput)
|
|
849
|
-
: initialInput;
|
|
850
|
-
processedCommand = commandWithSystemPrompt(processedCommand, provider, systemPrompt);
|
|
849
|
+
let initialInputText = initialInput;
|
|
851
850
|
const isCodexProvider = provider === "codex";
|
|
852
851
|
const isOpenCodeProvider = provider === "opencode";
|
|
853
852
|
const existingProviderSessionId = provider
|
|
@@ -873,12 +872,6 @@ export class ProcessManager extends EventEmitter {
|
|
|
873
872
|
: null;
|
|
874
873
|
const initialClaudeSessionId = existingProviderSessionId ?? assignedProviderSessionId;
|
|
875
874
|
const startedAt = new Date().toISOString();
|
|
876
|
-
const launchPlan = buildPtyShellLaunchPlan({
|
|
877
|
-
shell: this.config.shell,
|
|
878
|
-
command: processedCommand,
|
|
879
|
-
bareShell: opts?.interactiveShell === true,
|
|
880
|
-
providerCommand: provider !== undefined,
|
|
881
|
-
});
|
|
882
875
|
const record = {
|
|
883
876
|
id,
|
|
884
877
|
sessionSource: opts?.sessionSource ?? inheritedSessionSource ?? "interactive",
|
|
@@ -912,7 +905,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
912
905
|
processId: null,
|
|
913
906
|
ptyProcess: null,
|
|
914
907
|
terminalState: new PtyTerminalState(opts?.cols !== undefined ? clampDimension(opts.cols, 20, 1000) : 120, opts?.rows !== undefined ? clampDimension(opts.rows, 5, 500) : 36),
|
|
915
|
-
providerShellMarker:
|
|
908
|
+
providerShellMarker: null,
|
|
916
909
|
providerCliActive: provider !== undefined,
|
|
917
910
|
providerCliExitCode: null,
|
|
918
911
|
stopRequested: false,
|
|
@@ -939,7 +932,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
939
932
|
ptyCols: opts?.cols !== undefined ? clampDimension(opts.cols, 20, 1000) : 120,
|
|
940
933
|
ptyRows: opts?.rows !== undefined ? clampDimension(opts.rows, 5, 500) : 36,
|
|
941
934
|
ptyOutputSeq: 0,
|
|
942
|
-
ptyLaunchMarkerToken:
|
|
935
|
+
ptyLaunchMarkerToken: null,
|
|
943
936
|
};
|
|
944
937
|
this.sessions.set(id, record);
|
|
945
938
|
this.persist(record, { forceFullSave: true });
|
|
@@ -947,13 +940,31 @@ export class ProcessManager extends EventEmitter {
|
|
|
947
940
|
this.providerHistory.invalidate(provider);
|
|
948
941
|
}
|
|
949
942
|
let child;
|
|
943
|
+
let launchPlan;
|
|
950
944
|
try {
|
|
945
|
+
// Derive authority only after the real session/project binding has been persisted.
|
|
946
|
+
const access = provider ? this.storage.knowledgeSessionScope(id) : null;
|
|
947
|
+
const knowledgeToken = access ? this.storage.issueKnowledgeAccess(id) : undefined;
|
|
948
|
+
const runtimePrompt = access ? [knowledgeInstructions(access),
|
|
949
|
+
"本终端会话的知识工具只在有效执行期内可用;每次使用知识先 search/read,不把旧轮次快照当作当前状态。访问失效时新开一轮结构化执行或恢复会话,不绕过权限。"].join("\n\n") : undefined;
|
|
950
|
+
const launchPrompt = [systemPrompt, runtimePrompt].filter(Boolean).join("\n\n") || undefined;
|
|
951
|
+
initialInputText = initialInput && launchPrompt && !systemPromptFlag(provider)
|
|
952
|
+
? composeSystemFallback(launchPrompt, initialInput) : initialInput;
|
|
953
|
+
processedCommand = commandWithSystemPrompt(processedCommand, provider, launchPrompt);
|
|
954
|
+
launchPlan = buildPtyShellLaunchPlan({ shell: this.config.shell, command: processedCommand,
|
|
955
|
+
bareShell: opts?.interactiveShell === true, providerCommand: provider !== undefined });
|
|
956
|
+
record.providerShellMarker = launchPlan.cliExitMarker;
|
|
957
|
+
record.ptyLaunchMarkerToken = launchPlan.cliExitMarker?.token ?? null;
|
|
958
|
+
this.persist(record, { forceFullSave: true });
|
|
951
959
|
const attached = await this.terminalHost.createOrAttach({
|
|
952
960
|
sessionId: id,
|
|
953
961
|
file: this.config.shell,
|
|
954
962
|
args: launchPlan.shellArgs,
|
|
955
963
|
cwd: resolvedCwd,
|
|
956
964
|
env: buildChildEnv(this.config.inheritEnv !== false, {
|
|
965
|
+
WAND_SESSION_ID: id,
|
|
966
|
+
WAND_KNOWLEDGE_DB: knowledgeToken ? this.storage.databasePath() : undefined,
|
|
967
|
+
WAND_KNOWLEDGE_TOKEN: knowledgeToken,
|
|
957
968
|
WAND_MODE: effectiveMode,
|
|
958
969
|
WAND_AUTO_CONFIRM: record.autoApprovePermissions ? "1" : "0",
|
|
959
970
|
WAND_AUTO_EDIT: effectiveMode === "auto-edit" ? "1" : "0",
|
|
@@ -1503,6 +1514,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
1503
1514
|
finishProviderCli(record, exitCode) {
|
|
1504
1515
|
if (!record.providerCliActive)
|
|
1505
1516
|
return;
|
|
1517
|
+
this.storage.revokeSessionKnowledgeAccess(record.id);
|
|
1506
1518
|
record.providerCliActive = false;
|
|
1507
1519
|
record.providerCliExitCode = exitCode;
|
|
1508
1520
|
record.providerShellMarker = null;
|
|
@@ -1539,6 +1551,7 @@ export class ProcessManager extends EventEmitter {
|
|
|
1539
1551
|
if (this.unknownTerminalOwners.has(id))
|
|
1540
1552
|
throw new Error("Terminal ownership is unknown; stop was not delivered. Retry after recovery.");
|
|
1541
1553
|
const record = this.mustGet(id);
|
|
1554
|
+
this.storage.revokeSessionKnowledgeAccess(id);
|
|
1542
1555
|
const start = this.startingSessions.get(id);
|
|
1543
1556
|
if (start)
|
|
1544
1557
|
start.cancelled = true;
|
|
@@ -1591,6 +1604,8 @@ export class ProcessManager extends EventEmitter {
|
|
|
1591
1604
|
return this.snapshot(record);
|
|
1592
1605
|
}
|
|
1593
1606
|
cleanupRecord(record, terminateTerminal = true) {
|
|
1607
|
+
if (terminateTerminal && record.status === "running")
|
|
1608
|
+
this.storage.revokeSessionKnowledgeAccess(record.id);
|
|
1594
1609
|
this.clearPtyTurn(record);
|
|
1595
1610
|
record.timers.clearAll();
|
|
1596
1611
|
const pendingPersist = this.persistDebounceTimers.get(record.id);
|
|
@@ -1843,6 +1858,14 @@ export class ProcessManager extends EventEmitter {
|
|
|
1843
1858
|
Object.assign(this.mustGet(id), membership);
|
|
1844
1859
|
this.emitEvent({ type: "status", sessionId: id, data: membership });
|
|
1845
1860
|
}
|
|
1861
|
+
/** The source is committed by storage before the live checkpoint projection is refreshed. */
|
|
1862
|
+
setSessionSource(id, sessionSource) {
|
|
1863
|
+
const record = this.mustGet(id);
|
|
1864
|
+
if (record.sessionSource === sessionSource)
|
|
1865
|
+
return;
|
|
1866
|
+
record.sessionSource = sessionSource;
|
|
1867
|
+
this.emitEvent({ type: "status", sessionId: id, data: { sessionSource } });
|
|
1868
|
+
}
|
|
1846
1869
|
setSessionTopic(id, title, description) {
|
|
1847
1870
|
const record = this.mustGet(id);
|
|
1848
1871
|
record.title = title;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** A quick-terminal claim never takes ownership of an Agent or automation session. */
|
|
2
|
+
export function canClaimQuickTerminal(session) {
|
|
3
|
+
return (session.sessionKind ?? "pty") === "pty" && !session.provider && !session.automationId
|
|
4
|
+
&& (!session.sessionSource || session.sessionSource === "interactive" || session.sessionSource === "quick-terminal");
|
|
5
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WandStorage } from "./storage.js";
|
|
2
|
+
import type { WandTask } from "./task-types.js";
|
|
3
|
+
import type { SessionSnapshot } from "./types.js";
|
|
4
|
+
/** Read all ownership/execution facts before the first destructive action. Never loads transcripts or settings. */
|
|
5
|
+
export declare function createRetentionProtection(storage: WandStorage, sessions: readonly SessionSnapshot[], archivedMs: number | null, now: number): {
|
|
6
|
+
tasks: WandTask[];
|
|
7
|
+
runs: import("./ai-team-types.js").AiTeamRun[];
|
|
8
|
+
teamSessionIds: Map<string, string[]>;
|
|
9
|
+
canArchiveTask: (task: WandTask) => boolean;
|
|
10
|
+
canPurgeTask: (id: string) => boolean;
|
|
11
|
+
canArchiveSession: (id: string) => boolean;
|
|
12
|
+
canPurgeSession: (id: string) => boolean;
|
|
13
|
+
canArchiveRun: (id: string) => boolean;
|
|
14
|
+
canPurgeRun: (id: string) => boolean;
|
|
15
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { AI_TEAM_ACTIVE_RUN_STATUSES } from "./ai-team-types.js";
|
|
2
|
+
import { isArchiveBlocked } from "./archive-policy.js";
|
|
3
|
+
import { TASK_RETENTION_ARCHIVE_VERSION } from "./task-retention.js";
|
|
4
|
+
import { parseTimestampMs } from "./time-utils.js";
|
|
5
|
+
function acceptedTask(task) {
|
|
6
|
+
const acceptance = task.acceptance;
|
|
7
|
+
return (task.status === "done" || task.status === "archived")
|
|
8
|
+
&& typeof acceptance?.acceptedAt === "string" && parseTimestampMs(acceptance.acceptedAt) !== null
|
|
9
|
+
&& typeof acceptance.evidence === "string" && !!acceptance.evidence.trim()
|
|
10
|
+
&& typeof acceptance.blocker === "string" && !acceptance.blocker.trim();
|
|
11
|
+
}
|
|
12
|
+
/** Iterative, order-independent propagation; shared histories and legacy cycles cannot evade a hold. */
|
|
13
|
+
function spread(graph, seeds) {
|
|
14
|
+
const held = new Set(seeds), pending = [...held];
|
|
15
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
16
|
+
for (const next of graph.get(pending[index]) ?? []) {
|
|
17
|
+
if (held.has(next))
|
|
18
|
+
continue;
|
|
19
|
+
held.add(next);
|
|
20
|
+
pending.push(next);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return held;
|
|
24
|
+
}
|
|
25
|
+
/** Read all ownership/execution facts before the first destructive action. Never loads transcripts or settings. */
|
|
26
|
+
export function createRetentionProtection(storage, sessions, archivedMs, now) {
|
|
27
|
+
const tasks = storage.listWandTasks(), runs = storage.listAiTeamRuns();
|
|
28
|
+
const versions = storage.taskRetentionArchiveVersions();
|
|
29
|
+
const byId = new Map(tasks.map(task => [task.id, task]));
|
|
30
|
+
const byContainer = new Map(tasks.flatMap(task => task.workspaceTaskId ? [[task.workspaceTaskId, task]] : []));
|
|
31
|
+
const graph = new Map(), ancestors = new Map();
|
|
32
|
+
const taskNode = (id) => `task:${id}`;
|
|
33
|
+
const sessionNode = (id) => `session:${id}`;
|
|
34
|
+
const runNode = (id) => `run:${id}`;
|
|
35
|
+
const busy = new Set(), unfinished = new Set(), notPurgeReady = new Set();
|
|
36
|
+
const link = (left, right) => {
|
|
37
|
+
if (!graph.has(left))
|
|
38
|
+
graph.set(left, new Set());
|
|
39
|
+
if (!graph.has(right))
|
|
40
|
+
graph.set(right, new Set());
|
|
41
|
+
graph.get(left).add(right);
|
|
42
|
+
graph.get(right).add(left);
|
|
43
|
+
};
|
|
44
|
+
for (const task of tasks) {
|
|
45
|
+
const node = taskNode(task.id);
|
|
46
|
+
if (!acceptedTask(task))
|
|
47
|
+
unfinished.add(node);
|
|
48
|
+
const archivedAt = parseTimestampMs(task.archivedAt), acceptedAt = parseTimestampMs(task.acceptance?.acceptedAt);
|
|
49
|
+
if (!acceptedTask(task) || task.status !== "archived" || archivedAt === null || acceptedAt === null
|
|
50
|
+
|| versions.get(task.id) !== TASK_RETENTION_ARCHIVE_VERSION || archivedMs === null
|
|
51
|
+
|| now - Math.max(archivedAt, acceptedAt) < archivedMs)
|
|
52
|
+
notPurgeReady.add(node);
|
|
53
|
+
if (task.parentTaskId) {
|
|
54
|
+
link(node, taskNode(task.parentTaskId));
|
|
55
|
+
ancestors.set(node, new Set([taskNode(task.parentTaskId)]));
|
|
56
|
+
if (!byId.has(task.parentTaskId))
|
|
57
|
+
busy.add(node); // Unknown ancestry is not permission to detach history.
|
|
58
|
+
}
|
|
59
|
+
// Container-less archives can still have historical membership. Live membership comes from snapshots below.
|
|
60
|
+
if (!task.workspaceTaskId) {
|
|
61
|
+
for (const id of storage.listWandTaskSessionIds(task.id))
|
|
62
|
+
link(node, sessionNode(id));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const checked = new Set();
|
|
66
|
+
for (const task of tasks) {
|
|
67
|
+
const path = new Set();
|
|
68
|
+
let cursor = task.id;
|
|
69
|
+
while (cursor && !checked.has(cursor)) {
|
|
70
|
+
if (path.has(cursor)) {
|
|
71
|
+
busy.add(taskNode(cursor));
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
path.add(cursor);
|
|
75
|
+
cursor = byId.get(cursor)?.parentTaskId ?? null;
|
|
76
|
+
}
|
|
77
|
+
for (const id of path)
|
|
78
|
+
checked.add(id);
|
|
79
|
+
}
|
|
80
|
+
for (const session of sessions) {
|
|
81
|
+
const node = sessionNode(session.id);
|
|
82
|
+
if (isArchiveBlocked(session))
|
|
83
|
+
busy.add(node);
|
|
84
|
+
if (session.workspaceTaskId) {
|
|
85
|
+
const owner = byContainer.get(session.workspaceTaskId);
|
|
86
|
+
if (owner)
|
|
87
|
+
link(node, taskNode(owner.id));
|
|
88
|
+
else
|
|
89
|
+
busy.add(node);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const fact of storage.taskCommentRetentionFacts()) {
|
|
93
|
+
const node = taskNode(fact.taskId);
|
|
94
|
+
if (fact.sessionId)
|
|
95
|
+
link(node, sessionNode(fact.sessionId));
|
|
96
|
+
if (fact.state !== "accepted" && fact.state !== "failed")
|
|
97
|
+
busy.add(node);
|
|
98
|
+
}
|
|
99
|
+
const teamSessionIds = new Map();
|
|
100
|
+
for (const run of runs) {
|
|
101
|
+
const node = runNode(run.id), ids = new Set();
|
|
102
|
+
if (run.chatSessionId)
|
|
103
|
+
ids.add(run.chatSessionId);
|
|
104
|
+
for (const step of storage.listAiTeamSteps(run.id))
|
|
105
|
+
if (step.sessionId)
|
|
106
|
+
ids.add(step.sessionId);
|
|
107
|
+
teamSessionIds.set(run.id, [...ids]);
|
|
108
|
+
for (const id of ids)
|
|
109
|
+
link(node, sessionNode(id));
|
|
110
|
+
if (run.taskId) {
|
|
111
|
+
const owner = byId.get(run.taskId) ?? byContainer.get(run.taskId);
|
|
112
|
+
// A dangling historical id has no surviving task context to detach; active status is handled above.
|
|
113
|
+
if (owner)
|
|
114
|
+
link(node, taskNode(owner.id));
|
|
115
|
+
}
|
|
116
|
+
if (AI_TEAM_ACTIVE_RUN_STATUSES.includes(run.status))
|
|
117
|
+
busy.add(node);
|
|
118
|
+
}
|
|
119
|
+
const executionHeld = spread(graph, busy);
|
|
120
|
+
const archiveHeld = spread(graph, [...busy, ...unfinished]);
|
|
121
|
+
// A task-bound history must outlive the latest acceptance/archive grace window in its whole family.
|
|
122
|
+
const purgeHeld = spread(graph, [...busy, ...notPurgeReady]);
|
|
123
|
+
const unfinishedAncestors = spread(ancestors, unfinished);
|
|
124
|
+
return {
|
|
125
|
+
tasks, runs, teamSessionIds,
|
|
126
|
+
canArchiveTask: (task) => task.status === "done" && acceptedTask(task)
|
|
127
|
+
&& !executionHeld.has(taskNode(task.id)) && !unfinishedAncestors.has(taskNode(task.id)),
|
|
128
|
+
canPurgeTask: (id) => !purgeHeld.has(taskNode(id)),
|
|
129
|
+
canArchiveSession: (id) => !archiveHeld.has(sessionNode(id)),
|
|
130
|
+
canPurgeSession: (id) => !purgeHeld.has(sessionNode(id)),
|
|
131
|
+
canArchiveRun: (id) => !archiveHeld.has(runNode(id)),
|
|
132
|
+
canPurgeRun: (id) => !purgeHeld.has(runNode(id)),
|
|
133
|
+
};
|
|
134
|
+
}
|
package/dist/retention.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ import type { SessionSnapshot } from "./types.js";
|
|
|
5
5
|
export declare const RETENTION_IDLE_MS: number;
|
|
6
6
|
export declare const RETENTION_ARCHIVED_MS: number;
|
|
7
7
|
/**
|
|
8
|
-
* 看板任务、会话和已结束的团队运行共用 `taskRetention
|
|
9
|
-
*
|
|
8
|
+
* 看板任务、会话和已结束的团队运行共用 `taskRetention` 的开关与天数。
|
|
9
|
+
* 只有已验收、无未完成上下文且无执行/审批/排队事实的任务才能自动归档;
|
|
10
|
+
* 关联会话与团队历史跟随保护。升级前的任务归档不自动删除。
|
|
10
11
|
* 结构化会话看运行状态;PTY 只有正在回复或等待确认才跳过,空闲壳不算在处理。
|
|
11
12
|
*/
|
|
12
13
|
export interface RetentionDeps {
|