@weavecode/weave 0.1.0 → 0.1.2
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/node_modules/@weavecode/cli/dist/cli.js +3 -3
- package/node_modules/@weavecode/cli/dist/commands/daemon.d.ts +7 -0
- package/node_modules/@weavecode/cli/dist/commands/daemon.js +2 -2
- package/node_modules/@weavecode/cli/dist/commands/dashboard.d.ts +0 -2
- package/node_modules/@weavecode/cli/dist/commands/dashboard.js +2 -1
- package/node_modules/@weavecode/cli/dist/commands/doctor.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/init.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/msg.js +2 -2
- package/node_modules/@weavecode/cli/dist/commands/scan.js +1 -1
- package/node_modules/@weavecode/cli/dist/commands/status.js +2 -2
- package/node_modules/@weavecode/cli/dist/i18n/dict/en.js +6 -12
- package/node_modules/@weavecode/cli/dist/i18n/dict/zh.d.ts +44 -2
- package/node_modules/@weavecode/cli/dist/i18n/dict/zh.js +6 -12
- package/node_modules/@weavecode/cli/dist/init/claudemd-gen.d.ts +2 -3
- package/node_modules/@weavecode/cli/dist/init/claudemd-gen.js +4 -4
- package/node_modules/@weavecode/cli/dist/init/executor.js +3 -3
- package/node_modules/@weavecode/cli/dist/init/git-commit.js +1 -1
- package/node_modules/@weavecode/cli/dist/init/harnesses/claude.js +7 -7
- package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.d.ts +29 -0
- package/node_modules/@weavecode/cli/dist/init/harnesses/zcode.js +1 -0
- package/node_modules/@weavecode/cli/dist/init/helpers-gen.d.ts +4 -0
- package/node_modules/@weavecode/cli/dist/init/helpers-gen.js +1 -20
- package/node_modules/@weavecode/cli/dist/init/mcp-gen.d.ts +3 -20
- package/node_modules/@weavecode/cli/dist/init/mcp-gen.js +1 -1
- package/node_modules/@weavecode/cli/dist/init/prompt.js +4 -4
- package/node_modules/@weavecode/cli/dist/init/settings-gen.d.ts +3 -30
- package/node_modules/@weavecode/cli/dist/init/settings-gen.js +1 -1
- package/node_modules/@weavecode/cli/package.json +5 -5
- package/node_modules/@weavecode/core/dist/harness.d.ts +45 -2
- package/node_modules/@weavecode/core/dist/harness.js +1 -1
- package/node_modules/@weavecode/core/dist/index.d.ts +2 -2
- package/node_modules/@weavecode/core/dist/index.js +1 -1
- package/node_modules/@weavecode/core/dist/terminal.d.ts +28 -4
- package/node_modules/@weavecode/core/dist/terminal.js +1 -1
- package/node_modules/@weavecode/core/dist/types.d.ts +188 -19
- package/node_modules/@weavecode/core/dist/types.js +1 -1
- package/node_modules/@weavecode/core/package.json +1 -1
- package/node_modules/@weavecode/harness/dist/claude/assets.d.ts +10 -0
- package/node_modules/@weavecode/harness/dist/claude/assets.js +4 -1
- package/node_modules/@weavecode/harness/dist/claude/capture.d.ts +15 -3
- package/node_modules/@weavecode/harness/dist/claude/capture.js +15 -9
- package/node_modules/@weavecode/harness/dist/claude/event-source.d.ts +1 -1
- package/node_modules/@weavecode/harness/dist/claude/event-source.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/helpers-gen.d.ts +5 -0
- package/node_modules/@weavecode/harness/dist/claude/helpers-gen.js +20 -0
- package/node_modules/@weavecode/harness/dist/claude/index.d.ts +10 -1
- package/node_modules/@weavecode/harness/dist/claude/index.js +1 -1
- package/node_modules/@weavecode/harness/dist/claude/mcp-gen.d.ts +22 -0
- package/node_modules/@weavecode/harness/dist/claude/mcp-gen.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/messaging.d.ts +11 -2
- package/node_modules/@weavecode/harness/dist/claude/messaging.js +4 -1
- package/node_modules/@weavecode/harness/dist/claude/model-aliases.d.ts +39 -0
- package/node_modules/@weavecode/harness/dist/claude/model-aliases.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/plan.d.ts +2 -0
- package/node_modules/@weavecode/harness/dist/claude/plan.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/questions.js +2 -1
- package/node_modules/@weavecode/harness/dist/claude/settings-gen.d.ts +32 -0
- package/node_modules/@weavecode/harness/dist/claude/settings-gen.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/side-question.d.ts +12 -0
- package/node_modules/@weavecode/harness/dist/claude/side-question.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/config.d.ts +200 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/config.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/generator.d.ts +12 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/generator.js +745 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.d.ts +10 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/git-cache.js +1 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/surface.d.ts +20 -0
- package/node_modules/@weavecode/harness/dist/claude/statusline/surface.js +2 -0
- package/node_modules/@weavecode/harness/dist/claude/transcript.d.ts +16 -0
- package/node_modules/@weavecode/harness/dist/claude/transcript.js +4 -2
- package/node_modules/@weavecode/harness/dist/codex/assets.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/index.d.ts +1 -0
- package/node_modules/@weavecode/harness/dist/codex/index.js +1 -1
- package/node_modules/@weavecode/harness/dist/codex/mcp.d.ts +30 -0
- package/node_modules/@weavecode/harness/dist/codex/mcp.js +1 -0
- package/node_modules/@weavecode/harness/dist/codex/messaging.js +1 -1
- package/node_modules/@weavecode/harness/dist/driver.d.ts +168 -3
- package/node_modules/@weavecode/harness/dist/index.d.ts +1 -0
- package/node_modules/@weavecode/harness/dist/index.js +1 -1
- package/node_modules/@weavecode/harness/dist/init/generated.d.ts +37 -0
- package/node_modules/@weavecode/harness/dist/init/generated.js +2 -0
- package/node_modules/@weavecode/harness/dist/registry-wiring.d.ts +3 -1
- package/node_modules/@weavecode/harness/dist/zcode/assets.d.ts +8 -0
- package/node_modules/@weavecode/harness/dist/zcode/assets.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/capture.d.ts +36 -0
- package/node_modules/@weavecode/harness/dist/zcode/capture.js +3 -0
- package/node_modules/@weavecode/harness/dist/zcode/detect.d.ts +2 -0
- package/node_modules/@weavecode/harness/dist/zcode/detect.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/event-source.d.ts +8 -0
- package/node_modules/@weavecode/harness/dist/zcode/event-source.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/index.d.ts +13 -0
- package/node_modules/@weavecode/harness/dist/zcode/index.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/mcp.d.ts +6 -0
- package/node_modules/@weavecode/harness/dist/zcode/mcp.js +2 -0
- package/node_modules/@weavecode/harness/dist/zcode/messaging.d.ts +13 -0
- package/node_modules/@weavecode/harness/dist/zcode/messaging.js +1 -0
- package/node_modules/@weavecode/harness/dist/zcode/resume.d.ts +7 -0
- package/node_modules/@weavecode/harness/dist/zcode/resume.js +1 -0
- package/node_modules/@weavecode/harness/package.json +2 -2
- package/node_modules/@weavecode/server/dist/daemon/ai-config.js +2 -2
- package/node_modules/@weavecode/server/dist/daemon/ai-context.js +6 -6
- package/node_modules/@weavecode/server/dist/daemon/ai-routes.js +4 -4
- package/node_modules/@weavecode/server/dist/daemon/api-log.d.ts +48 -0
- package/node_modules/@weavecode/server/dist/daemon/api-log.js +3 -0
- package/node_modules/@weavecode/server/dist/daemon/auth-routes.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/auto-start.js +6 -6
- package/node_modules/@weavecode/server/dist/daemon/capture-routes.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/claude-liveness.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/dashboard-auth.js +2 -2
- package/node_modules/@weavecode/server/dist/daemon/data-bus.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/dev-entry.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/fs-browser.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.d.ts +13 -1
- package/node_modules/@weavecode/server/dist/daemon/hook-pipeline.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/hooks-routes.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/http.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/opener.d.ts +102 -19
- package/node_modules/@weavecode/server/dist/daemon/opener.js +97 -7
- package/node_modules/@weavecode/server/dist/daemon/port.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/project-harnesses.js +2 -2
- package/node_modules/@weavecode/server/dist/daemon/project-updates-refresher.d.ts +4 -3
- package/node_modules/@weavecode/server/dist/daemon/project-updates-refresher.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/server.d.ts +15 -0
- package/node_modules/@weavecode/server/dist/daemon/server.js +8 -6
- package/node_modules/@weavecode/server/dist/daemon/session-probe.d.ts +72 -2
- package/node_modules/@weavecode/server/dist/daemon/session-probe.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/settings.d.ts +10 -0
- package/node_modules/@weavecode/server/dist/daemon/settings.js +2 -2
- package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.d.ts +7 -1
- package/node_modules/@weavecode/server/dist/daemon/snapshot-refresher.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/sse-hub.d.ts +40 -0
- package/node_modules/@weavecode/server/dist/daemon/sse-hub.js +1 -0
- package/node_modules/@weavecode/server/dist/daemon/start.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/state.js +2 -2
- package/node_modules/@weavecode/server/dist/daemon/static.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/terminal-topology-refresher.d.ts +4 -1
- package/node_modules/@weavecode/server/dist/daemon/tls.js +1 -1
- package/node_modules/@weavecode/server/dist/daemon/toolkit-routes.js +1 -1
- package/node_modules/@weavecode/server/dist/db/db.js +38 -2
- package/node_modules/@weavecode/server/dist/db/paths.d.ts +4 -0
- package/node_modules/@weavecode/server/dist/db/paths.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/answer-keys.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/bus.d.ts +5 -0
- package/node_modules/@weavecode/server/dist/hooks/bus.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/dispatcher.js +6 -6
- package/node_modules/@weavecode/server/dist/hooks/handlers/webhook.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/headless-runner.js +2 -2
- package/node_modules/@weavecode/server/dist/hooks/inbox-socket.d.ts +17 -4
- package/node_modules/@weavecode/server/dist/hooks/inbox-socket.js +2 -1
- package/node_modules/@weavecode/server/dist/hooks/inbox-worker.d.ts +27 -6
- package/node_modules/@weavecode/server/dist/hooks/inbox-worker.js +3 -3
- package/node_modules/@weavecode/server/dist/hooks/match.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/message-deliverer.d.ts +200 -13
- package/node_modules/@weavecode/server/dist/hooks/message-deliverer.js +3 -2
- package/node_modules/@weavecode/server/dist/hooks/mirror.d.ts +9 -4
- package/node_modules/@weavecode/server/dist/hooks/mirror.js +4 -4
- package/node_modules/@weavecode/server/dist/hooks/plugin-installer.js +3 -3
- package/node_modules/@weavecode/server/dist/hooks/plugin-loader.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/plugin-project-state.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/prompt-flusher.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/promptbox.d.ts +13 -0
- package/node_modules/@weavecode/server/dist/hooks/promptbox.js +8 -8
- package/node_modules/@weavecode/server/dist/hooks/push-notify.js +2 -2
- package/node_modules/@weavecode/server/dist/hooks/question-tracker.d.ts +6 -4
- package/node_modules/@weavecode/server/dist/hooks/question-tracker.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/registry.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/resume-runner.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/sequence-engine.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/session-purge.d.ts +7 -0
- package/node_modules/@weavecode/server/dist/hooks/session-purge.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/session-sweeper.d.ts +5 -8
- package/node_modules/@weavecode/server/dist/hooks/session-sweeper.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/side-questions.d.ts +41 -0
- package/node_modules/@weavecode/server/dist/hooks/side-questions.js +1 -0
- package/node_modules/@weavecode/server/dist/hooks/toolkit-scan.js +1 -1
- package/node_modules/@weavecode/server/dist/hooks/toolkit.d.ts +8 -0
- package/node_modules/@weavecode/server/dist/hooks/toolkit.js +5 -5
- package/node_modules/@weavecode/server/dist/hooks/watch-engine.d.ts +7 -0
- package/node_modules/@weavecode/server/dist/hooks/watch-engine.js +1 -1
- package/node_modules/@weavecode/server/dist/index.d.ts +1 -1
- package/node_modules/@weavecode/server/dist/install/apply-update.d.ts +1 -1
- package/node_modules/@weavecode/server/dist/install/apply-update.js +1 -1
- package/node_modules/@weavecode/server/dist/install/capabilities.d.ts +10 -0
- package/node_modules/@weavecode/server/dist/install/capabilities.js +1 -1
- package/node_modules/@weavecode/server/dist/install/codex-toml.d.ts +8 -26
- package/node_modules/@weavecode/server/dist/install/codex-toml.js +1 -1
- package/node_modules/@weavecode/server/dist/install/file-assets.js +1 -1
- package/node_modules/@weavecode/server/dist/install/fingerprint.d.ts +5 -1
- package/node_modules/@weavecode/server/dist/install/fingerprint.js +1 -1
- package/node_modules/@weavecode/server/dist/install/git-classify.d.ts +30 -0
- package/node_modules/@weavecode/server/dist/install/git-classify.js +1 -0
- package/node_modules/@weavecode/server/dist/install/global-config.d.ts +2 -4
- package/node_modules/@weavecode/server/dist/install/global-config.js +1 -2
- package/node_modules/@weavecode/server/dist/install/global-file-assets.js +1 -1
- package/node_modules/@weavecode/server/dist/install/init-files.d.ts +46 -0
- package/node_modules/@weavecode/server/dist/install/init-files.js +2 -0
- package/node_modules/@weavecode/server/dist/install/install-ledger.js +1 -1
- package/node_modules/@weavecode/server/dist/install/installer.d.ts +3 -5
- package/node_modules/@weavecode/server/dist/install/installer.js +1 -3
- package/node_modules/@weavecode/server/dist/install/library-index.js +1 -1
- package/node_modules/@weavecode/server/dist/install/mcp-rows.d.ts +5 -5
- package/node_modules/@weavecode/server/dist/install/mcp-rows.js +1 -1
- package/node_modules/@weavecode/server/dist/install/plugin-install.js +1 -1
- package/node_modules/@weavecode/server/dist/install/plugin-rows.js +1 -1
- package/node_modules/@weavecode/server/dist/install/plugin-templates.js +1 -1
- package/node_modules/@weavecode/server/dist/install/project-config.d.ts +0 -2
- package/node_modules/@weavecode/server/dist/install/project-config.js +2 -2
- package/node_modules/@weavecode/server/dist/install/reader.js +1 -1
- package/node_modules/@weavecode/server/dist/install/scan-cache.js +1 -1
- package/node_modules/@weavecode/server/dist/install/scanner.js +1 -1
- package/node_modules/@weavecode/server/dist/install/settings-mcp.js +2 -2
- package/node_modules/@weavecode/server/dist/install/updates.d.ts +17 -5
- package/node_modules/@weavecode/server/dist/install/updates.js +1 -1
- package/node_modules/@weavecode/server/dist/market/builtin.js +1 -1
- package/node_modules/@weavecode/server/dist/market/identity.js +1 -1
- package/node_modules/@weavecode/server/dist/market/import.js +2 -2
- package/node_modules/@weavecode/server/dist/market/refresh.js +1 -1
- package/node_modules/@weavecode/server/dist/market/resolve.js +1 -1
- package/node_modules/@weavecode/server/dist/market/updates.js +1 -1
- package/node_modules/@weavecode/server/dist/registry.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/hook-dispatches.js +5 -5
- package/node_modules/@weavecode/server/dist/repositories/hook-events.d.ts +7 -0
- package/node_modules/@weavecode/server/dist/repositories/hook-events.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/hook-rules.js +4 -4
- package/node_modules/@weavecode/server/dist/repositories/libraries.js +1 -1
- package/node_modules/@weavecode/server/dist/repositories/plugin-states.js +1 -1
- package/node_modules/@weavecode/server/dist/repositories/projects.d.ts +7 -0
- package/node_modules/@weavecode/server/dist/repositories/projects.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/push-subscriptions.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/session-watches.js +4 -4
- package/node_modules/@weavecode/server/dist/repositories/sessions.d.ts +32 -8
- package/node_modules/@weavecode/server/dist/repositories/sessions.js +2 -2
- package/node_modules/@weavecode/server/dist/repositories/side-questions.d.ts +50 -0
- package/node_modules/@weavecode/server/dist/repositories/side-questions.js +6 -0
- package/node_modules/@weavecode/server/dist/repositories/uploads.d.ts +66 -0
- package/node_modules/@weavecode/server/dist/repositories/uploads.js +7 -0
- package/node_modules/@weavecode/server/dist/statusline/cache-sweeper.js +1 -1
- package/node_modules/@weavecode/server/dist/statusline/config.d.ts +4 -198
- package/node_modules/@weavecode/server/dist/statusline/config.js +1 -1
- package/node_modules/@weavecode/server/dist/statusline/generator.d.ts +5 -0
- package/node_modules/@weavecode/server/dist/statusline/generator.js +1 -676
- package/node_modules/@weavecode/server/dist/statusline/manager.d.ts +7 -5
- package/node_modules/@weavecode/server/dist/statusline/manager.js +3 -4
- package/node_modules/@weavecode/server/dist/surfaces.d.ts +11 -1
- package/node_modules/@weavecode/server/dist/surfaces.js +1 -1
- package/node_modules/@weavecode/server/dist/watch/cache.js +1 -1
- package/node_modules/@weavecode/server/dist/watch/classifier.js +1 -1
- package/node_modules/@weavecode/server/dist/watch/plugin-mods.js +1 -1
- package/node_modules/@weavecode/server/dist/watch/sync-scheduler.d.ts +8 -4
- package/node_modules/@weavecode/server/dist/watch/sync-scheduler.js +1 -1
- package/node_modules/@weavecode/server/dist/watch/watch-service.js +1 -1
- package/node_modules/@weavecode/server/package.json +4 -4
- package/node_modules/@weavecode/server/web/dist/assets/index-CHWbLAje.js +594 -0
- package/node_modules/@weavecode/server/web/dist/assets/index-D0s5v8QS.css +11 -0
- package/node_modules/@weavecode/server/web/dist/index.html +14 -2
- package/node_modules/@weavecode/templates/dist/resources/weave-market/plugins/core-skills/weave-plugin-creator/skills/weave-plugin-creator/SKILL.md +2 -2
- package/node_modules/@weavecode/templates/package.json +2 -2
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-cms/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-csr/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-ecc/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-pkcs9/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-rsa/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-schema/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/@peculiar/x509/node_modules/@peculiar/asn1-x509/node_modules/asn1js/package.json +20 -17
- package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/README.md +50 -32
- package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/build/index.d.ts +86 -40
- package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/build/index.es.js +860 -353
- package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/build/index.js +860 -353
- package/node_modules/selfsigned/node_modules/pkijs/node_modules/asn1js/package.json +20 -17
- package/package.json +6 -6
- package/node_modules/@weavecode/server/web/dist/assets/index-BOzaRlQE.js +0 -560
- package/node_modules/@weavecode/server/web/dist/assets/index-ChX8G8cT.css +0 -11
|
@@ -21,6 +21,17 @@ import { HeadlessRunner, type SpawnLike } from './headless-runner.js';
|
|
|
21
21
|
* pending 只存在于 promptbox 文件。重启即丢 headless 终态 = 显式高级选项的
|
|
22
22
|
* 已文档代价。通道规格全部来自 MessagingSurface 能力面,本类不含 harness 硬编码。
|
|
23
23
|
*
|
|
24
|
+
* 层③ 只收「还有可能送达」的行:三层全无通路且目标已判死(claude 注册表
|
|
25
|
+
* `pidAlive === false`)时**不入队**,落 failed + `reason: 'offline'`(web 出中文
|
|
26
|
+
* 说明)——死会话的队列行永远不会被取走,排在那里只是骗人。判不动(null)不猜,
|
|
27
|
+
* 照旧入队。
|
|
28
|
+
*
|
|
29
|
+
* **同一会话严格按发送顺序投递**(顺序即真相:到达顺序错乱比慢一拍更糟):
|
|
30
|
+
* 队头还压着(上一条终端投递没回执 / 队列有积压 / 泵在推进)时新消息只入队尾,
|
|
31
|
+
* 由冲洗泵一条条推进;终端层把一条贴进输入框后必须等它的 UPS 回执才发下一条
|
|
32
|
+
* ——再贴一份会在同一个框里叠成一段,两条消息并成一条提交(直投/中继不压:
|
|
33
|
+
* 交出去即进入会话自己的队列,会话按序处理)。
|
|
34
|
+
*
|
|
24
35
|
* send() 校验失败同步返回错误码;成功立即返回(inject 已落 outbox,headless
|
|
25
36
|
* 「已启动」,prompt 视层可用性返回 delivering/pending),终态经回调后台落定,
|
|
26
37
|
* 绝不 throw 出 send()。
|
|
@@ -31,22 +42,44 @@ export type MessageMode = 'prompt' | 'inject' | 'headless' | 'auto';
|
|
|
31
42
|
export type MessageFromKind = 'dashboard' | 'session';
|
|
32
43
|
/** 管道消息状态:pending 只活在 promptbox 文件,其余在内存注册表。 */
|
|
33
44
|
export type PipeStatus = 'pending' | 'delivering' | 'running' | 'injected' | 'delivered' | 'succeeded' | 'failed' | 'timeout';
|
|
45
|
+
/** 随 prompt 投递的图(上传件引用;路径 = 落盘件的绝对路径,harness 合成引用形态)。 */
|
|
46
|
+
export interface MessageImage {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
mediaType: string;
|
|
50
|
+
path: string;
|
|
51
|
+
}
|
|
34
52
|
/** 管道消息(内存态;发号 = uuid,全生命周期稳定)。 */
|
|
35
53
|
export interface PipeMessage {
|
|
36
54
|
id: string;
|
|
37
55
|
sessionId: string;
|
|
56
|
+
/** **用户原文**(回执比对、气泡文案都用它);投递出去的文本是它经 harness
|
|
57
|
+
* `imageAttach.composePrompt` 合成的形态(带图时追加图引用),见 `wireTextOf`。 */
|
|
38
58
|
text: string;
|
|
59
|
+
/** 随附图(空/缺省 = 纯文本消息)。 */
|
|
60
|
+
images?: MessageImage[];
|
|
39
61
|
mode: MessageMode;
|
|
40
62
|
/** 单向注入(手动面:无接收方会话,中继载荷带「无需回复」注记);编排域不置。 */
|
|
41
63
|
oneWay?: boolean;
|
|
64
|
+
/** 发送人强制层② 终端(层序收窄成只剩它,且重发沿用)。 */
|
|
65
|
+
forceTerminal?: boolean;
|
|
42
66
|
/** prompt 通道的实际落法(socket 直投 / 中继 / 终端 send-text)。 */
|
|
43
67
|
via?: 'socket' | 'relay' | 'terminal';
|
|
44
68
|
/** 投递回执:会话自己的 UserPromptSubmit 与本文匹配上了(真到达,非「已交给」)。 */
|
|
45
69
|
acked?: boolean;
|
|
70
|
+
/** 回执命中的那条开轮事件 id:对话面据此把投递行精确挂到它的气泡上(比比对
|
|
71
|
+
* 原文可靠——重发只补的那记回车提交的是整个输入框,投递行记的原文与实际提交
|
|
72
|
+
* 的文本可以不一样)。 */
|
|
73
|
+
ackedEventId?: number | null;
|
|
46
74
|
/** 终态落定时刻(在途视图驻留窗口的起点;在途态无值)。 */
|
|
47
75
|
settledAt?: string | null;
|
|
48
76
|
/** 本条 = 该会话暂停闸的成因(未回执 / 投递失败);重发或回执清除。 */
|
|
49
77
|
stalled?: boolean;
|
|
78
|
+
/** 闸自动清除本行的时刻(只给「已发出却没回执」那种成因行):到点行消失、队列续排。
|
|
79
|
+
* web 拿它走倒计时。 */
|
|
80
|
+
releaseAt?: string | null;
|
|
81
|
+
/** 判死而没投出去(会话进程不在、三层全无通路):`failed` 的成因,web 据此出中文说明。 */
|
|
82
|
+
reason?: 'offline';
|
|
50
83
|
/** 实际投递通道(auto 落定后回填;pending = 尚未裁决成通道)。 */
|
|
51
84
|
channel: 'prompt' | 'inject' | 'headless' | null;
|
|
52
85
|
status: PipeStatus;
|
|
@@ -78,6 +111,8 @@ export interface PromptTerminal {
|
|
|
78
111
|
sendText(paneId: string, text: string): Promise<void>;
|
|
79
112
|
/** 裸键序注入(作答的选项键 / 中断的 ESC:模态框只吃键盘,不吃粘贴文本)。 */
|
|
80
113
|
sendKeys(paneId: string, keys: string): Promise<void>;
|
|
114
|
+
/** 关掉这个 pane(会话生命周期收尾);无通道的后端明确拒绝。 */
|
|
115
|
+
closePane(paneId: string): Promise<void>;
|
|
81
116
|
}
|
|
82
117
|
export interface MessageDelivererDeps {
|
|
83
118
|
sessions: SessionRepository;
|
|
@@ -113,6 +148,12 @@ export interface MessageDelivererDeps {
|
|
|
113
148
|
/** 未回执闸窗口(毫秒);缺省 120s——prompt 送达后没等到 UPS 回执即暂停该会话
|
|
114
149
|
* 的队列冲洗(防继续向 Held/Refused 的会话灌消息),手动重发或回执解除。 */
|
|
115
150
|
ackStallMs?: number;
|
|
151
|
+
/** 闸自动解除窗口(毫秒);缺省 10min——闸挂着而回执永远不来(消息其实丢了)时,
|
|
152
|
+
* 队列不能就此停到天荒地老:到点把成因行落成 failed(`unacked`)并续排。 */
|
|
153
|
+
ackStallReleaseMs?: number;
|
|
154
|
+
/** 会话存活图(测试注入);缺省 `claudeLivenessById()`——同两个引擎(watch/sequence)
|
|
155
|
+
* 的取法:没有注册表就空图,会话一律「查不动」,不猜。 */
|
|
156
|
+
livenessOf?: () => Map<string, boolean>;
|
|
116
157
|
}
|
|
117
158
|
/** send() 入参(路由层已做 zod 形状校验)。 */
|
|
118
159
|
export interface SendInput {
|
|
@@ -123,6 +164,11 @@ export interface SendInput {
|
|
|
123
164
|
fromSessionId?: string | null;
|
|
124
165
|
/** 单向注入(手动面置 true:无接收方会话);编排域省略 = 双向对话保留。 */
|
|
125
166
|
oneWay?: boolean;
|
|
167
|
+
/** 强制层② 终端 send-text(手动面下拉选的通道):跳过直投与中继——宿主把
|
|
168
|
+
* 终端击键当用户输入,另外两层都是「另一个会话发的」。 */
|
|
169
|
+
forceTerminal?: boolean;
|
|
170
|
+
/** 随附图(已落盘的上传件);带图且目标 harness 的 `imageAttach` 为 null 即 409。 */
|
|
171
|
+
images?: readonly MessageImage[];
|
|
126
172
|
}
|
|
127
173
|
/** send() 返回:ok 由路由映射 200,否则 status 即 HTTP 码。 */
|
|
128
174
|
export type SendResult = {
|
|
@@ -143,6 +189,25 @@ export type AnswerResult = {
|
|
|
143
189
|
status: 400 | 404 | 409;
|
|
144
190
|
error: string;
|
|
145
191
|
};
|
|
192
|
+
/** 通道现况:ready = 现在就能投;unknown = 判不动(终端只剩标题/cwd 回落,被动面看不出来)。 */
|
|
193
|
+
interface ChannelState<R extends string> {
|
|
194
|
+
status: 'ready' | 'unavailable' | 'unknown';
|
|
195
|
+
reason: R | null;
|
|
196
|
+
}
|
|
197
|
+
/** 会话级投递诊断(`diagnose` 返回值;只含布尔与枚举,无凭据值)。 */
|
|
198
|
+
export interface SessionDeliveryDiagnosis {
|
|
199
|
+
/** 存活三态(同 `pidAliveOf` 口径:判不动 = null)。 */
|
|
200
|
+
alive: boolean | null;
|
|
201
|
+
inbox: ChannelState<'no-mechanism' | 'no-credentials' | 'auth-token-required'>;
|
|
202
|
+
relay: ChannelState<'no-mechanism' | 'no-peer-name'>;
|
|
203
|
+
terminal: ChannelState<'no-backend' | 'no-pane-env'>;
|
|
204
|
+
/** 该会话有没有上报过 pane 定位 env。 */
|
|
205
|
+
paneEnvReported: boolean;
|
|
206
|
+
/** 队列里压着的行数。 */
|
|
207
|
+
pending: number;
|
|
208
|
+
/** 暂停闸是否已置(未回执超窗 / 投递失败)。 */
|
|
209
|
+
stalled: boolean;
|
|
210
|
+
}
|
|
146
211
|
export declare class MessageDeliverer {
|
|
147
212
|
private readonly deps;
|
|
148
213
|
private readonly headless;
|
|
@@ -151,16 +216,29 @@ export declare class MessageDeliverer {
|
|
|
151
216
|
private readonly memory;
|
|
152
217
|
/** 有队列行的会话(ingest 冲洗守卫 + 刷新器探活索引;构造时从磁盘重建)。 */
|
|
153
218
|
private readonly queuedSessions;
|
|
154
|
-
/** 会话 inbox 直投目标(hook
|
|
219
|
+
/** 会话 inbox 直投目标(hook 旁路上报,落 `<dataDir>/hooks/inbox-targets.json` 0600)。 */
|
|
155
220
|
private readonly inboxSockets;
|
|
156
|
-
/**
|
|
221
|
+
/**
|
|
222
|
+
* 会话所在终端 pane 的定位 env(hook 旁路上报)。落
|
|
223
|
+
* `<dataDir>/hooks/pane-envs.json` 0600——**只解 daemon 自己的重启**:pane 活在
|
|
224
|
+
* 会话终端里,重启不动它;不落盘的话重启后要等下一次 hook 事件才认回 pane,而
|
|
225
|
+
* 长时间没动静的会话根本等不到,于是从终端分组里掉出去。
|
|
226
|
+
*/
|
|
157
227
|
private readonly paneEnvs;
|
|
228
|
+
/** 上次落盘的 pane env(hook 每次事件都上报,值没变就不重写盘)。 */
|
|
229
|
+
private persistedPaneEnvs;
|
|
158
230
|
/** 已排自续冲洗的会话(防重复排拍)。 */
|
|
159
231
|
private readonly flushScheduled;
|
|
160
232
|
/** 正在冲洗的会话(重入闸:异步冲洗 + 多条触发源,防同批行被投两次)。 */
|
|
161
233
|
private readonly flushing;
|
|
162
234
|
/** 队列暂停闸(sessionId → 成因消息):未回执超窗 / 投递失败即置;重发或回执解除。 */
|
|
163
235
|
private readonly stalled;
|
|
236
|
+
/**
|
|
237
|
+
* 队头在等回执(sessionId → 消息 id):层② 终端把一条贴进输入框后,下一条必须
|
|
238
|
+
* 等它的 UPS 才发——再贴一份会在同一个框里叠成一段,两条消息并成一条提交。
|
|
239
|
+
* 直投/中投不压(交出去即入会话自己的队列,会话按序处理)。
|
|
240
|
+
*/
|
|
241
|
+
private readonly awaitingAck;
|
|
164
242
|
/**
|
|
165
243
|
* 刚删掉的队列行 id:冲洗「先把队列读进内存、再逐条投递」——这中间被删的行
|
|
166
244
|
* 仍在手里那一批里,靠它跳过(否则删掉的消息照样发出去)。只兜这一个窗口,
|
|
@@ -180,7 +258,8 @@ export declare class MessageDeliverer {
|
|
|
180
258
|
* 在途视图(GET /sessions/:id/messages):promptbox 待递送 ∪ 内存态——
|
|
181
259
|
* 在途(delivering/running)与 failed/timeout/暂停闸成因常驻;delivered/
|
|
182
260
|
* injected/succeeded 在驻留窗口(visibleTtlMs)内展示 via/acked,过后消失
|
|
183
|
-
*
|
|
261
|
+
* (历史在目标会话自己的转录里)。例外 = **已送达还没回执**的:过了窗口也留着,
|
|
262
|
+
* 它就是「还没落进对话」,等回执或未回执闸落定再说。
|
|
184
263
|
*/
|
|
185
264
|
listVisible(sessionId: string): PipeMessage[];
|
|
186
265
|
/** 该会话是否有队列行(ingest 冲洗守卫,O(1))。 */
|
|
@@ -197,6 +276,13 @@ export declare class MessageDeliverer {
|
|
|
197
276
|
* 只有「有进展」才续(通道不可用时不续,避免热循环);沿用节流间隔作拍长。
|
|
198
277
|
*/
|
|
199
278
|
private scheduleFlushContinuation;
|
|
279
|
+
/** 等回执的那条已经落地(回执到 / 失败 / 行没了)→ 撤防,返回 true。 */
|
|
280
|
+
private ackGateOpen;
|
|
281
|
+
/**
|
|
282
|
+
* 队头是否还压着:上一条终端投递没回执 / 队列有积压 / 泵正在推进。
|
|
283
|
+
* 是 → 新消息只能排到队尾;不是 → 立刻投(低延迟路径)。
|
|
284
|
+
*/
|
|
285
|
+
private blockedAhead;
|
|
200
286
|
/**
|
|
201
287
|
* hook 旁路上报的会话 inbox 目标(内存态;socket 空 = 该会话此刻不可直投)。
|
|
202
288
|
* token 只在此存活,不落库、不进 UI、不写日志。
|
|
@@ -210,21 +296,54 @@ export declare class MessageDeliverer {
|
|
|
210
296
|
* resume 到新窗口后,下一次事件就把定位改过来,与 inbox 凭据同一自愈口径)。
|
|
211
297
|
*/
|
|
212
298
|
notePaneEnv(sessionId: string, env: Record<string, string>): void;
|
|
299
|
+
/** 原子写(临时文件 + rename)+ 0600;失败静默(内存态照常能定位,下次再落)。 */
|
|
300
|
+
private persistPaneEnvs;
|
|
301
|
+
/**
|
|
302
|
+
* 会话退出(`SessionEnd` 到达):旁路事实随进程作废——inbox 凭据出盘(旧 socket
|
|
303
|
+
* 已死,留着只会让下一次投递白试一轮)、pane 定位清(窗口随会话关)。**会话行与
|
|
304
|
+
* 它的对话照常保留**(那是历史,不是旁路事实),所以不走 `forgetSession` 的删会话
|
|
305
|
+
* 级联;压队的行此刻也已成死信 → 一并按 offline 落定(同 A 的口径)。
|
|
306
|
+
*/
|
|
307
|
+
sessionExited(sessionId: string): void;
|
|
213
308
|
/**
|
|
214
|
-
* 会话 →
|
|
215
|
-
*
|
|
309
|
+
* 会话 → 所在可见屏幕(会话列表分块的数据面)。旁路 env 与投递路径共用同一份
|
|
310
|
+
* 缓存,不另采一次;后端没有反查能力 = 空表(那些会话不分组)。顺手摘掉已判死
|
|
311
|
+
* 会话的 env:那是上一次见到它时的快照,它不会再来报一次,留着只会让死会话常占块头。
|
|
216
312
|
*/
|
|
217
313
|
sessionTerminals(): Promise<Record<string, SessionTerminal | null>>;
|
|
314
|
+
/**
|
|
315
|
+
* 会话级投递诊断(只读内存态 + 既有 repo,纯现算):三通道各自「现在能不能投」
|
|
316
|
+
* + 队列积压/暂停闸。判定条件集就是投递路径的可用前提,不另立第二份事实源。
|
|
317
|
+
*
|
|
318
|
+
* 只回布尔与枚举——**socket 地址与 token 永不出栈**(与「凭据不进 UI/日志」同口径)。
|
|
319
|
+
*/
|
|
320
|
+
diagnose(sessionId: string): SessionDeliveryDiagnosis;
|
|
218
321
|
/**
|
|
219
322
|
* 投递回执:会话自己的 UserPromptSubmit 到了 —— 其 prompt 原文与在途条目匹配
|
|
220
323
|
* 即成真(`acked`)。中继/终端/socket 三路通用(中继的原文外面裹着 Claude Code
|
|
221
|
-
* 自己的 `<cross-session-message …>`
|
|
324
|
+
* 自己的 `<cross-session-message …>` 壳,比对时归一掉)。返回命中消息的随附图
|
|
325
|
+
* id(调用方挂到开轮事件上做气泡缩略图;空表 = 命中但没图 / 没命中)。
|
|
326
|
+
*
|
|
327
|
+
* 带图的消息比对放宽:claude 的粘贴处理器把图路径从 prompt 剥掉、还会把多行
|
|
328
|
+
* 文本 chunk 用空串连起来,`msg.text` 与 UPS 原文可能对不上——对不上不代表
|
|
329
|
+
* 没送达,按「去空白后相同」再认一次;纯图发送(无用户文本)的 UPS prompt 是
|
|
330
|
+
* 空的 / 只剩 `[Image #N]`,无文本可比,这样一条 UPS 认领最新那条纯图在途。
|
|
331
|
+
*
|
|
332
|
+
* `eventId` = 这条 UPS 自己的事件 id:命中即记下来,对话面据此把投递行精确挂到
|
|
333
|
+
* 对应气泡上(见 `PipeMessage.ackedEventId`)。
|
|
222
334
|
*/
|
|
223
|
-
onSessionPrompt(sessionId: string, prompt: string):
|
|
335
|
+
onSessionPrompt(sessionId: string, prompt: string, eventId?: number): string[];
|
|
224
336
|
/** 未回执闸布防:prompt 送达后 ackStallMs 内没等到 UPS 回执 → 暂停该会话队列。 */
|
|
225
337
|
private armAckWatch;
|
|
226
338
|
/** 置闸:成因消息标记 stalled(常驻在途视图)+ message.stalled 帧(web 重拉)。 */
|
|
227
339
|
private stall;
|
|
340
|
+
/**
|
|
341
|
+
* 闸到点自动解除:回执一直不来 = 那条很可能压根没进对话(贴着文本按下去的时候
|
|
342
|
+
* 会话正在重启,输入框被清掉就是这样),闸不能因此把该会话的队列永久停住。
|
|
343
|
+
* 「已发出却没回执」的行到点**直接从队列清除**(用户裁决:正文只在这一行里,
|
|
344
|
+
* 窗口期就是给人看清与重发的);投递失败的行留着等人重发,只解闸续排。
|
|
345
|
+
*/
|
|
346
|
+
private releaseStall;
|
|
228
347
|
/**
|
|
229
348
|
* 远程作答(阻塞式交互):向目标会话所在 pane 发裸键序——模态框只吃键盘,
|
|
230
349
|
* inbox/中继投递答不了。键序经白名单(`ANSWER_KEYS`);pane 匹配复用层② 的
|
|
@@ -239,8 +358,44 @@ export declare class MessageDeliverer {
|
|
|
239
358
|
*/
|
|
240
359
|
interrupt(sessionId: string): Promise<AnswerResult>;
|
|
241
360
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
361
|
+
* 关掉会话所在的 pane(会话生命周期收尾):宿主随 pane 一起退出——这是「退出
|
|
362
|
+
* 会话」的远程等价(harness 没有「请退出」这种信号)。**会话行与它的对话照常
|
|
363
|
+
* 保留**(那是历史,同 SessionEnd 口径),只把它从「活着」的世界里摘出去。
|
|
364
|
+
*/
|
|
365
|
+
closePane(sessionId: string): Promise<AnswerResult>;
|
|
366
|
+
/**
|
|
367
|
+
* 执行模式切换(Shift+Tab 的远程等价):一记固定键,宿主自己算下一个模式。
|
|
368
|
+
* 答「现在是哪个模式」不由这里管——Shift+Tab 不发任何 hook 事件、不落转录,
|
|
369
|
+
* 实时态只能是 hook 事件里最近一次的 `permission_mode`(「最后已知」)。
|
|
370
|
+
*/
|
|
371
|
+
cycleMode(sessionId: string): Promise<AnswerResult>;
|
|
372
|
+
/**
|
|
373
|
+
* 模型切换:往会话的输入行打宿主自己的 `/model <名>` 并提交(会话级、不落盘)。
|
|
374
|
+
* 名字是**用户自填**的(网关模型 ID 会增删,不做静态映射表),故只验形态不验
|
|
375
|
+
* 词表——宿主对未知名自己回 `Unknown model '…'`,那句话进会话转录、用户看得见。
|
|
376
|
+
*/
|
|
377
|
+
setModel(sessionId: string, name: string): Promise<AnswerResult>;
|
|
378
|
+
/**
|
|
379
|
+
* 旁路会话(claude = `/btw`):问句经层② 终端打进会话 pane——宿主的侧问只
|
|
380
|
+
* 活在 TUI 里(`claude -p "/btw …"` 直说不支持这个环境),也不落任何文件,
|
|
381
|
+
* 故终端击键是唯一通道。不入队、不布防回执:回答不是 UPS 而是之后的一条
|
|
382
|
+
* 采集事件(认领见 `hooks/side-questions.ts`)。
|
|
383
|
+
*
|
|
384
|
+
* 返回归一化后的问句(已过 harness 的形态校验):调用方据此落历史行。
|
|
385
|
+
*/
|
|
386
|
+
askSideQuestion(sessionId: string, question: string): Promise<{
|
|
387
|
+
ok: true;
|
|
388
|
+
question: string;
|
|
389
|
+
} | {
|
|
390
|
+
ok: false;
|
|
391
|
+
status: 400 | 404 | 409;
|
|
392
|
+
error: string;
|
|
393
|
+
}>;
|
|
394
|
+
/**
|
|
395
|
+
* 键序注入路径(作答 / 中断 / 关 pane)共用的落点解析:会话 → 项目 → 终端通道
|
|
396
|
+
* → pane。全部失败原因是**可判定的语义拒绝**(404/409),调用方原样回给 HTTP;
|
|
397
|
+
* 两条 409 是给用户看的(web 直拼在「停止失败:」这类动作名后面),故写成中文
|
|
398
|
+
* 修法,动作名随调用方换。
|
|
244
399
|
*/
|
|
245
400
|
private sessionKeysTarget;
|
|
246
401
|
/**
|
|
@@ -262,12 +417,13 @@ export declare class MessageDeliverer {
|
|
|
262
417
|
/**
|
|
263
418
|
* 队列冲洗(幂等):直投命中即走;否则中继 / 终端按面定序(手动面终端优先);
|
|
264
419
|
* 都不行留队列。
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* 所以节流在这里统一兜)。超出的留队列,下轮触发(ingest / 刷新器)继续。
|
|
420
|
+
* **严格串行**:一轮只送一条,等它的 UPS 回执到了再自续下一拍——同一队列由各模式共用,
|
|
421
|
+
* 顺序与「一条一条」是队列本身的语义,不是各通道各自节流的副产品。
|
|
268
422
|
*/
|
|
269
423
|
flushSession(sessionId: string): Promise<void>;
|
|
270
424
|
private flushSessionInner;
|
|
425
|
+
/** 队列空了就把冲洗索引摘掉(本轮提前返回的路径都得过它,否则刷新器永远白探)。 */
|
|
426
|
+
private settleQueueIndex;
|
|
271
427
|
/** 刷新器入口:扫全部队列会话(只在队列非空时被调用才有意义)。 */
|
|
272
428
|
flushAll(): Promise<void>;
|
|
273
429
|
/** 会话删除级联:内存态 + 队列行。返回清理行数(口径与旧 DB 计数一致)。 */
|
|
@@ -291,7 +447,7 @@ export declare class MessageDeliverer {
|
|
|
291
447
|
* (无定位能力的终端 / 老 hook),歧义拒绝不打。
|
|
292
448
|
*/
|
|
293
449
|
private terminalPaneFor;
|
|
294
|
-
/** 层②
|
|
450
|
+
/** 层② 通道现取(装配期注入;与 settings 的 preset 无关,只有「没装」才是 null)。 */
|
|
295
451
|
private terminalChannel;
|
|
296
452
|
/** prompt 通道入口(send 的即时路径):层① 命中即时中继,层② 异步找 pane,都不行入队。 */
|
|
297
453
|
private deliverPrompt;
|
|
@@ -303,6 +459,22 @@ export declare class MessageDeliverer {
|
|
|
303
459
|
private tryInboxDeliver;
|
|
304
460
|
/** 入队(promptbox 追加 + 会话索引登记)。 */
|
|
305
461
|
private queueEntry;
|
|
462
|
+
/**
|
|
463
|
+
* 该会话是否已判死:claude 注册表 `pidAlive === false` 才认,`null` 不猜
|
|
464
|
+
* (codex / 新会话豁免窗 / SessionStart 空壳行都落 null)。判据与「已终止」
|
|
465
|
+
* 态、两条 terminated 补射同源(claude-liveness 的 pidAliveOf)——消息行与
|
|
466
|
+
* 会话行不会各说各话。
|
|
467
|
+
*/
|
|
468
|
+
private sessionJudgedDead;
|
|
469
|
+
/**
|
|
470
|
+
* 判死落定:三层全无通路、目标进程又不在——**不假装排队**,落 `failed` +
|
|
471
|
+
* `reason: 'offline'`(`message.finished` 帧照抛,既有 messageFailed 订阅提醒
|
|
472
|
+
* 生效)。**刻意不置暂停闸**:队列整批转 offline 后已空,闸只会挡住用户重发
|
|
473
|
+
* ——重发是有意的可逆出口(判活残差 / 会话其实活着时,一点就回来)。
|
|
474
|
+
*/
|
|
475
|
+
private failOffline;
|
|
476
|
+
/** 冲洗时判死:**该会话全部队列行**都是死信(死会话投不进去),整批落定 + 清队。 */
|
|
477
|
+
private failQueuedOffline;
|
|
306
478
|
/**
|
|
307
479
|
* 层① 中继:复用 HeadlessRunner 的 spawn 基建(win32 shell 单串 + stdin +
|
|
308
480
|
* 超时),键 = 合成键(不占目标会话的 headless 并发闸)。返回是否启动成功。
|
|
@@ -316,6 +488,20 @@ export declare class MessageDeliverer {
|
|
|
316
488
|
private settleRelay;
|
|
317
489
|
/** 层② 终端注入终态(语义同中继:成功 delivered / 失败 failed 可见)。 */
|
|
318
490
|
private runTerminalPrompt;
|
|
491
|
+
/**
|
|
492
|
+
* 层② 只补一记回车(重发「已贴进框、没回执」的行):文本在上一次投递里已经
|
|
493
|
+
* 落到会话的输入框,缺的只是那记提交键——重贴一次会在框里叠一份,用户收到
|
|
494
|
+
* 两遍正文与两遍图。
|
|
495
|
+
*/
|
|
496
|
+
private runTerminalSubmit;
|
|
497
|
+
private runTerminal;
|
|
498
|
+
/**
|
|
499
|
+
* 投递文本 = 用户原文经 harness `imageAttach.composePrompt` 现合成(带图时在
|
|
500
|
+
* 原文后追加图引用形态)。**只在投递那一刻合成**——`msg.text` 恒为用户原文
|
|
501
|
+
* (回执比对与气泡文案都要它),队列行也不存合成结果(换 harness 后仍按现面合成)。
|
|
502
|
+
*/
|
|
503
|
+
private wireTextOf;
|
|
504
|
+
private hasImages;
|
|
319
505
|
/** inject 通道:信封写 outbox → injected(fire-and-forget 语义)。 */
|
|
320
506
|
private deliverInject;
|
|
321
507
|
/** headless 通道:置 running → 无头 spawn;终态经 onSettle 后台回写。 */
|
|
@@ -326,3 +512,4 @@ export declare class MessageDeliverer {
|
|
|
326
512
|
private settle;
|
|
327
513
|
private bus;
|
|
328
514
|
}
|
|
515
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import{randomUUID as w}from"node:crypto";import{tmpdir as b}from"node:os";import{WEAVE_INJECTION_MARKER as D,isHarness as x}from"@weavecode/core";import{messagingSurfaceFor as j}from"../surfaces.js";import{normalizeDirKey as g}from"../daemon/opener.js";import{HeadlessRunner as E}from"./headless-runner.js";import{appendOutbox as O,hooksRoot as P}from"./mirror.js";import{InboxSocketRegistry as A,deliverViaInboxSocket as B}from"./inbox-socket.js";import{appendPromptbox as C,readAllPromptbox as W,readPromptbox as m,removePromptboxBySession as M,removePromptboxEntries as c,scanQueuedSessionIds as q}from"./promptbox.js";const R=()=>b(),T=5,y=1e3,$=6e4,F=12e4,K=8,N=200,S=/^(?:[1-9]|n|\t|\r|\x1b\[B)$/,H=/^[^\x00-\x1f\x7f]*$/,_="\x1B",V=120,L=D;function k(u){return u.replace(/<\/?cross-session-message[^>]*>/g,"").replace(/\s+/g," ").trim()}const v=u=>new Promise(s=>setTimeout(s,u));export class MessageDeliverer{deps;headless;hooksDir;memory=new Map;queuedSessions;inboxSockets=new A;paneEnvs=new Map;flushScheduled=new Set;flushing=new Set;stalled=new Map;dismissedIds=new Set;constructor(s){this.deps=s,this.headless=s.headless??new E({spawnImpl:s.spawnImpl,killImpl:s.killImpl}),this.hooksDir=s.hooksDir??P(),this.queuedSessions=q(this.hooksDir)}async send(s){const t=this.deps.sessions.getBySessionId(s.toSessionId);if(!t)return{ok:!1,status:404,error:"Session not found"};const e=this.surfaceOfSession(s.toSessionId);if(!e)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};const n=this.resolveChannel(s.mode,s.toSessionId,e);if(typeof n!="string")return{ok:!1,status:409,error:n.reason};if(t.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const o=this.deps.projects.getById(t.projectId);if(!o)return{ok:!1,status:404,error:"Project not found"};if(n==="headless"&&this.headless.isBusy(s.toSessionId))return{ok:!1,status:409,error:"Session already has a running run"};const i={fromLabel:s.fromKind==="session"?`session ${s.fromSessionId??"unknown"}`:s.fromKind,at:new Date().toISOString(),mode:n},r={id:w(),sessionId:s.toSessionId,text:s.text,mode:s.mode,oneWay:s.oneWay===!0,channel:n,status:"pending",at:i.at},a=n==="inject"?this.deliverInject(r,o.token,i,e):n==="headless"?this.deliverHeadless(r,o.path,i,e):await this.deliverPrompt(r,t,o,e);return this.bus({kind:"message.created",messageId:r.id,sessionId:r.sessionId,mode:r.mode,at:r.at}),a}getMessage(s){return this.memory.get(s)??null}listVisible(s){const t=this.deps.visibleTtlMs??$,e=Date.now(),n=[],o=this.deps.sessions.getBySessionId(s);if(o?.projectId!=null){const i=this.deps.projects.getById(o.projectId);if(i)for(const r of m(this.hooksDir,i.token))r.sessionId===s&&n.push({id:r.id,sessionId:s,text:r.text,mode:"prompt",oneWay:r.oneWay===!0,channel:"prompt",status:"pending",at:r.at})}for(const i of this.memory.values()){if(i.sessionId!==s)continue;if(i.stalled===!0||i.status==="delivering"||i.status==="running"){n.push(i);continue}if(i.status==="failed"||i.status==="timeout"){n.push(i);continue}const r=i.settledAt?Date.parse(i.settledAt):NaN;!Number.isNaN(r)&&e-r<t&&n.push(i)}return n.sort((i,r)=>i.at<r.at?-1:1)}hasQueued(s){return this.queuedSessions.has(s)}listInflightCounts(){const s=new Map,t=e=>{s.set(e,(s.get(e)??0)+1)};for(const e of W(this.hooksDir))t(e.sessionId);for(const e of this.memory.values())(e.status==="delivering"||e.status==="running")&&t(e.sessionId);return s}hasTerminalChannel(){return this.terminalChannel()!=null}scheduleFlushContinuation(s){this.flushScheduled.has(s)||(this.flushScheduled.add(s),setTimeout(()=>{this.flushScheduled.delete(s),this.flushSession(s)},this.deps.flushGapMs??y).unref?.())}noteInboxSocket(s,t){this.inboxSockets.set(s,t)}notePaneEnv(s,t){this.paneEnvs.set(s,t)}async sessionTerminals(){const s=this.terminalChannel();return!s?.locateScreens||this.paneEnvs.size===0?{}:s.locateScreens(Object.fromEntries(this.paneEnvs))}onSessionPrompt(s,t){const e=k(t);if(e)for(const n of[...this.memory.values()].reverse()){if(n.sessionId!==s||n.acked||n.mode!=="prompt")continue;const o=k(n.text);if(!(!o||o.length<K||!e.includes(o))){this.memory.set(n.id,{...n,acked:!0,stalled:!1}),this.stalled.get(s)?.messageId===n.id&&(this.stalled.delete(s),this.queuedSessions.has(s)&&this.scheduleFlushContinuation(s)),this.bus({kind:"message.acknowledged",messageId:n.id,sessionId:s,at:new Date().toISOString()});return}}}armAckWatch(s){setTimeout(()=>{const t=this.memory.get(s.id);!t||t.acked||t.status!=="delivered"||this.stall(t.sessionId,t,"unacked")},this.deps.ackStallMs??F).unref?.()}stall(s,t,e){this.stalled.set(s,{messageId:t.id,reason:e});const n=this.memory.get(t.id);n&&this.memory.set(t.id,{...n,stalled:!0}),this.bus({kind:"message.stalled",messageId:t.id,sessionId:s,reason:e,at:new Date().toISOString()})}async answer(s,t){if(t.length===0||!S.test(t[0])||t.some(n=>!S.test(n)&&!H.test(n)))return{ok:!1,status:400,error:"Invalid answer keys"};const e=await this.sessionKeysTarget(s,"Answering");if(!e.ok)return e;try{for(const[n,o]of t.entries())n>0&&await v(V),await e.terminal.sendKeys(e.paneId,o);return{ok:!0}}catch(n){return{ok:!1,status:409,error:n instanceof Error?n.message:String(n)}}}async interrupt(s){const t=await this.sessionKeysTarget(s,"Interrupting");if(!t.ok)return t;try{return await t.terminal.sendKeys(t.paneId,_),{ok:!0}}catch(e){return{ok:!1,status:409,error:e instanceof Error?e.message:String(e)}}}async sessionKeysTarget(s,t){const e=this.deps.sessions.getBySessionId(s);if(!e)return{ok:!1,status:404,error:"Session not found"};if(e.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const n=this.deps.projects.getById(e.projectId);if(!n)return{ok:!1,status:404,error:"Project not found"};const o=this.terminalChannel();if(!o)return{ok:!1,status:409,error:`${t} needs a text-capable terminal (pick wezterm in settings)`};const i=await this.terminalPaneFor(e,n);return i?{ok:!0,terminal:o,paneId:i.id}:{ok:!1,status:409,error:"Session pane not found \u2014 is the session window open?"}}async resend(s){const t=this.memory.get(s);if(!t)return{ok:!1,status:404,error:"Message not found"};if(!(t.channel==="prompt"&&(t.status==="failed"||t.status==="delivered"&&!t.acked)))return{ok:!1,status:409,error:"Message is not resendable"};const e=this.deps.sessions.getBySessionId(t.sessionId);if(!e)return{ok:!1,status:404,error:"Session not found"};if(e.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const n=this.deps.projects.getById(e.projectId);if(!n)return{ok:!1,status:404,error:"Project not found"};const o=this.surfaceOfSession(t.sessionId);if(!o)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};this.stalled.get(t.sessionId)?.messageId===s&&this.stalled.delete(t.sessionId),this.memory.delete(s);const i={...t,status:"pending",stalled:!1,acked:!1,settledAt:null},r=await this.deliverPrompt(i,e,n,o);return this.bus({kind:"message.created",messageId:i.id,sessionId:i.sessionId,mode:i.mode,at:new Date().toISOString()}),r}dismiss(s,t){const e=this.memory.get(t);if(e){if(e.status==="delivering"||e.status==="running")return{ok:!1,status:409,error:"Message is still in flight"};this.memory.delete(t),this.stalled.get(e.sessionId)?.messageId===t&&(this.stalled.delete(e.sessionId),this.queuedSessions.has(e.sessionId)&&this.scheduleFlushContinuation(e.sessionId))}else if(!this.dropQueued(s,t))return{ok:!1,status:404,error:"Message not found"};return this.bus({kind:"message.dismissed",messageId:t,sessionId:s,at:new Date().toISOString()}),{ok:!0}}dropQueued(s,t){const e=this.deps.sessions.getBySessionId(s);if(e?.projectId==null)return!1;const n=this.deps.projects.getById(e.projectId);if(!n)return!1;const o=m(this.hooksDir,n.token);if(!o.some(i=>i.id===t&&i.sessionId===s))return!1;for(c(this.hooksDir,n.token,new Set([t])),this.dismissedIds.add(t);this.dismissedIds.size>N;){const i=this.dismissedIds.values().next().value;if(i===void 0)break;this.dismissedIds.delete(i)}return o.some(i=>i.sessionId===s&&i.id!==t)||this.queuedSessions.delete(s),!0}async flushSession(s){if(!this.stalled.has(s)&&!this.flushing.has(s)&&this.queuedSessions.has(s)){this.flushing.add(s);try{await this.flushSessionInner(s)}catch{}finally{this.flushing.delete(s)}}}async flushSessionInner(s){const t=this.deps.sessions.getBySessionId(s);if(t?.projectId==null)return;const e=this.deps.projects.getById(t.projectId);if(!e)return;const n=m(this.hooksDir,e.token).filter(a=>a.sessionId===s);if(n.length===0){this.queuedSessions.delete(s);return}const o=this.surfaceOfSession(s),i=o?.prompt??null;let r=0;for(const a of n){if(this.stalled.has(s))return;if(this.dismissedIds.has(a.id))continue;if(r>=T){this.scheduleFlushContinuation(s);return}r>0&&await v(this.deps.flushGapMs??y);const d={id:a.id,sessionId:s,text:a.text,mode:"prompt",oneWay:a.oneWay===!0,via:"socket",channel:"prompt",status:"pending",at:a.at};if(o&&await this.tryInboxDeliver(d,o)){c(this.hooksDir,e.token,new Set([a.id])),r++;continue}const l=i?.resolve(s)??null,h=d.oneWay===!0?["terminal","relay"]:["relay","terminal"];let p=!1;for(const f of h){if(f==="relay"&&i&&l){if(this.memory.set(d.id,{...d,status:"delivering",via:"relay"}),!this.runRelay(d,i,l.name)){this.memory.delete(d.id);return}c(this.hooksDir,e.token,new Set([a.id])),r++,p=!0;break}if(f==="terminal"&&this.terminalChannel()){const I=await this.terminalPaneFor(t,e);if(I){c(this.hooksDir,e.token,new Set([a.id])),this.memory.set(d.id,{...d,via:"terminal",status:"delivering"}),await this.runTerminalPrompt(d.id,I.id,d.text),r++,p=!0;break}}}if(!p)return}m(this.hooksDir,e.token).filter(a=>a.sessionId===s).length===0&&this.queuedSessions.delete(s)}async flushAll(){for(const s of[...this.queuedSessions])await this.flushSession(s)}forgetSession(s){let t=0;for(const[e,n]of this.memory)n.sessionId===s&&(this.memory.delete(e),t++);return t+=M(this.hooksDir,s),this.queuedSessions.delete(s),this.stalled.delete(s),this.inboxSockets.forget(s),this.paneEnvs.delete(s),t}forgetProject(s){let t=0;for(const[e,n]of this.memory)this.deps.sessions.getBySessionId(n.sessionId)?.projectId===s&&(this.memory.delete(e),this.inboxSockets.forget(n.sessionId),this.paneEnvs.delete(n.sessionId),this.stalled.delete(n.sessionId),t++);for(const e of[...this.queuedSessions])this.deps.sessions.getBySessionId(e)?.projectId===s&&(t+=this.forgetSession(e));return t}surfaceOfSession(s){const t=this.deps.hookEvents.listBySession(s,1),e=t[t.length-1]?.source;return x(e)?this.deps.surfaceFor?.(e)??j(e):null}resolveChannel(s,t,e){if(s==="prompt")return e.prompt||this.terminalChannel()?"prompt":{reason:e.unsupportedReasons?.prompt??`Harness "${e.harness}" has no native prompt channel and the configured terminal cannot inject text`};if(s==="inject"||s==="headless")return(s==="inject"?e.inject:e.headless)?s:{reason:this.unsupportedReason(e,s)};const n=this.deps.sessions.turnOpenOf(t)?"inject":"headless";return(n==="inject"?e.inject:e.headless)?n:{reason:this.unsupportedReason(e,n)}}unsupportedReason(s,t){return s.unsupportedReasons?.[t]??`Harness "${s.harness}" does not support the ${t} messaging channel`}async terminalPaneFor(s,t){const e=this.terminalChannel();if(!e)return null;const n=this.paneEnvs.get(s.sessionId);if(n){const d=await e.locatePane?.(n).catch(()=>null);if(d)return{id:d}}if(!s.title)return null;const o=s.title.replace(/^[✻✽✶✳✢⋅◦✦✧∘◐◑◒◓*]+\s*/,""),i=g(t.path,process.platform);let r;try{r=await e.listPanes()}catch{return null}const a=r.filter(d=>d.title!=null&&d.cwd!=null&&i!=null&&d.title.replace(/^[✻✽✶✳✢⋅◦✦✧∘◐◑◒◓*]+\s*/,"")===o&&g(d.cwd,process.platform)===i);return a.length===1?a[0]:null}terminalChannel(){const s=this.deps.terminal;return s&&s.available()?s:null}async deliverPrompt(s,t,e,n){const o=await this.tryInboxDeliver(s,n);if(o)return{ok:!0,messageId:s.id,status:"delivered",message:o};const i=n.prompt,r=i?.resolve(s.sessionId)??null,a=s.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const l of a){if(l==="relay"&&i&&r){if(this.memory.set(s.id,{...s,status:"delivering",via:"relay"}),this.runRelay(s,i,r.name))return{ok:!0,messageId:s.id,status:"delivering",message:this.memory.get(s.id)};this.memory.delete(s.id)}if(l==="terminal"&&this.terminalChannel()){const h=await this.terminalPaneFor(t,e);if(h)return this.memory.set(s.id,{...s,status:"delivering",via:"terminal"}),this.runTerminalPrompt(s.id,h.id,s.text),{ok:!0,messageId:s.id,status:"delivering",message:this.memory.get(s.id)}}}this.queueEntry(s,e.token);const d={...s,status:"pending"};return{ok:!0,messageId:s.id,status:"pending",message:d}}async tryInboxDeliver(s,t){const e=t.promptInbox,n=this.inboxSockets.get(s.sessionId);if(!e||s.oneWay!==!0||!n)return null;this.memory.set(s.id,{...s,status:"delivering",via:"socket"});try{await B(e,n,`${L}${s.text}`)}catch{return this.memory.delete(s.id),null}const o={...this.memory.get(s.id),status:"delivered",via:"socket",settledAt:new Date().toISOString()};return this.memory.set(s.id,o),this.armAckWatch(o),this.bus({kind:"message.delivered",messageId:s.id,sessionId:s.sessionId,at:new Date().toISOString()}),o}queueEntry(s,t){C(this.hooksDir,t,[{id:s.id,sessionId:s.sessionId,text:s.text,oneWay:s.oneWay===!0}]),this.queuedSessions.add(s.sessionId)}runRelay(s,t,e){try{return this.headless.run({sessionId:`prompt-relay:${s.sessionId}`,cwd:R(),prompt:t.instruction(e,s.text,{oneWay:s.oneWay===!0}),spec:{supported:!0,bin:t.bin,args:()=>[...t.args],promptStdin:!0},bin:this.deps.bin??process.env.WEAVE_CLAUDE_BIN??t.bin,timeoutMs:this.deps.relayTimeoutMs??0,detached:!1,onSettle:n=>this.settleRelay(s.id,n)})}catch(n){return this.settleRelay(s.id,{status:"failed",output:null,error:n instanceof Error?n.message:String(n)}),!0}}settleRelay(s,t){const e=this.memory.get(s);if(!e)return;if(t.status==="succeeded"){this.queuedSessions.has(e.sessionId)&&this.scheduleFlushContinuation(e.sessionId);const o={...e,status:"delivered",settledAt:new Date().toISOString()};this.memory.set(s,o),this.armAckWatch(o),this.bus({kind:"message.delivered",messageId:s,sessionId:e.sessionId,at:new Date().toISOString()});return}const n={...e,status:"failed",error:t.error??`prompt relay ${t.status}`,settledAt:new Date().toISOString()};this.memory.set(s,n),this.stall(e.sessionId,n,"failed"),this.bus({kind:"message.finished",messageId:s,sessionId:e.sessionId,status:"failed",at:new Date().toISOString()})}async runTerminalPrompt(s,t,e){const n=this.memory.get(s);if(!n)return;const o=this.terminalChannel();if(o)try{await o.sendText(t,e);const i={...n,status:"delivered",settledAt:new Date().toISOString()};this.memory.set(s,i),this.armAckWatch(i),this.bus({kind:"message.delivered",messageId:s,sessionId:n.sessionId,at:new Date().toISOString()})}catch(i){const r={...n,status:"failed",error:i instanceof Error?i.message:String(i),settledAt:new Date().toISOString()};this.memory.set(s,r),this.stall(n.sessionId,r,"failed"),this.bus({kind:"message.finished",messageId:s,sessionId:n.sessionId,status:"failed",at:new Date().toISOString()})}}deliverInject(s,t,e,n){const o=this.renderEnvelope(s.text,e,n);O(this.hooksDir,t,[{sessionId:s.sessionId,text:o}]);const i={...s,status:"injected",settledAt:new Date().toISOString()};return this.memory.set(i.id,i),this.bus({kind:"message.delivered",messageId:i.id,sessionId:i.sessionId,at:new Date().toISOString()}),{ok:!0,messageId:i.id,status:"injected",message:i}}deliverHeadless(s,t,e,n){const o=n.headless,i={...s,status:"running"};this.memory.set(i.id,i);const r={settled:!1};try{this.headless.run({sessionId:i.sessionId,cwd:t,prompt:this.renderEnvelope(i.text,e,n),spec:o,bin:this.deps.bin??process.env.WEAVE_CLAUDE_BIN??o.bin,timeoutMs:this.deps.timeoutMs,onSettle:a=>this.settle(i,r,a)})||this.settle(i,r,{status:"failed",output:null,error:"Session already has a running run"})}catch(a){this.settle(i,r,{status:"failed",output:null,error:a instanceof Error?a.message:String(a)})}return{ok:!0,messageId:i.id,status:"running",message:this.memory.get(i.id)}}renderEnvelope(s,t,e){return e.renderEnvelope?.(s,t)??`[weave message] from=${t.fromLabel} at=${t.at} mode=${t.mode}
|
|
1
|
+
import{randomUUID as A}from"node:crypto";import{chmodSync as E,mkdirSync as D,readFileSync as T,renameSync as j,writeFileSync as P}from"node:fs";import{tmpdir as M}from"node:os";import{dirname as _,join as p}from"node:path";import{isHarness as B}from"@weavecode/core";import{messagingSurfaceFor as C}from"../surfaces.js";import{claudeLivenessById as g,pidAliveOf as S}from"../daemon/claude-liveness.js";import{normalizeDirKey as v}from"../daemon/opener.js";import{HeadlessRunner as R}from"./headless-runner.js";import{appendOutbox as q,hooksRoot as W}from"./mirror.js";import{INBOX_TARGETS_FILE as F,InboxSocketRegistry as N,deliverViaInboxSocket as K}from"./inbox-socket.js";import{appendPromptbox as $,readAllPromptbox as L,readPromptbox as m,removePromptboxBySession as Q,removePromptboxEntries as f,scanQueuedSessionIds as H}from"./promptbox.js";const G=()=>M(),J=1e3,U=6e4,V=12e4,z=10*6e4,X=8,Z="Session is not running",Y=200,I=/^(?:[1-9]|n|\t|\r|\x1b\[B)$/,ee=/^[^\x00-\x1f\x7f]*$/,se="\x1B",te="\x1B[Z",ie=/^[^\s\x00-\x1f\x7f]{1,128}$/,ne=h=>`/model ${h}`,re=120;function w(h){return h.replace(/<\/?cross-session-message[^>]*>/g,"").replace(/\s+/g," ").trim()}function y(h){return h.replace(/\s+/g,"")}const b="pane-envs.json";function oe(h){try{const e=JSON.parse(T(h,"utf8"));if(typeof e!="object"||e===null)return{};const t={};for(const[s,i]of Object.entries(e)){if(typeof i!="object"||i===null)continue;const n={};for(const[r,o]of Object.entries(i))typeof o=="string"&&o&&(n[r]=o);Object.keys(n).length>0&&(t[s]=n)}return t}catch{return{}}}const ae=h=>new Promise(e=>setTimeout(e,h));export class MessageDeliverer{deps;headless;hooksDir;memory=new Map;queuedSessions;inboxSockets;paneEnvs;persistedPaneEnvs;flushScheduled=new Set;flushing=new Set;stalled=new Map;awaitingAck=new Map;dismissedIds=new Set;constructor(e){this.deps=e,this.headless=e.headless??new R({spawnImpl:e.spawnImpl,killImpl:e.killImpl}),this.hooksDir=e.hooksDir??W(),this.inboxSockets=new N(p(this.hooksDir,F));const t=oe(p(this.hooksDir,b));this.paneEnvs=new Map(Object.entries(t)),this.persistedPaneEnvs=JSON.stringify(t),this.queuedSessions=H(this.hooksDir)}async send(e){const t=this.deps.sessions.getBySessionId(e.toSessionId);if(!t)return{ok:!1,status:404,error:"Session not found"};const s=this.surfaceOfSession(e.toSessionId);if(!s)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};const i=e.images?[...e.images]:[];if(i.length>0&&!s.imageAttach)return{ok:!1,status:409,error:"This harness cannot receive images"};const n=this.resolveChannel(e.mode,e.toSessionId,s);if(typeof n!="string")return{ok:!1,status:409,error:n.reason};if(t.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const r=this.deps.projects.getById(t.projectId);if(!r)return{ok:!1,status:404,error:"Project not found"};if(n==="headless"&&this.headless.isBusy(e.toSessionId))return{ok:!1,status:409,error:"Session already has a running run"};const o={fromLabel:e.fromKind==="session"?`session ${e.fromSessionId??"unknown"}`:e.fromKind,at:new Date().toISOString(),mode:n},a={id:A(),sessionId:e.toSessionId,text:e.text,...i.length>0?{images:i}:{},mode:e.mode,oneWay:e.oneWay===!0,...e.forceTerminal===!0?{forceTerminal:!0}:{},channel:n,status:"pending",at:o.at},l=n==="prompt"&&this.blockedAhead(a.sessionId);l&&(this.queueEntry(a,r.token),this.scheduleFlushContinuation(a.sessionId));const d=l?{ok:!0,messageId:a.id,status:"pending",message:a}:n==="inject"?this.deliverInject(a,r.token,o,s):n==="headless"?this.deliverHeadless(a,r.path,o,s):await this.deliverPrompt(a,t,r,s);return this.bus({kind:"message.created",messageId:a.id,sessionId:a.sessionId,mode:a.mode,at:a.at}),d}getMessage(e){return this.memory.get(e)??null}listVisible(e){const t=this.deps.visibleTtlMs??U,s=Date.now(),i=[],n=this.deps.sessions.getBySessionId(e);if(n?.projectId!=null){const r=this.deps.projects.getById(n.projectId);if(r)for(const o of m(this.hooksDir,r.token))o.sessionId===e&&i.push({id:o.id,sessionId:e,text:o.text,...o.images&&o.images.length>0?{images:o.images}:{},mode:"prompt",oneWay:o.oneWay===!0,...o.forceTerminal===!0?{forceTerminal:!0}:{},channel:"prompt",status:"pending",at:o.at})}for(const r of this.memory.values()){if(r.sessionId!==e)continue;if(r.stalled===!0||r.status==="delivering"||r.status==="running"){i.push(r);continue}if(r.status==="failed"||r.status==="timeout"){i.push(r);continue}const o=r.settledAt?Date.parse(r.settledAt):NaN;(r.status==="delivered"&&r.acked!==!0||!Number.isNaN(o)&&s-o<t)&&i.push(r)}return i.sort((r,o)=>r.at<o.at?-1:1)}hasQueued(e){return this.queuedSessions.has(e)}listInflightCounts(){const e=new Map,t=s=>{e.set(s,(e.get(s)??0)+1)};for(const s of L(this.hooksDir))t(s.sessionId);for(const s of this.memory.values())(s.status==="delivering"||s.status==="running")&&t(s.sessionId);return e}hasTerminalChannel(){return this.terminalChannel()!=null}scheduleFlushContinuation(e){if(this.flushScheduled.has(e))return;this.flushScheduled.add(e),setTimeout(()=>{this.flushScheduled.delete(e),this.flushSession(e)},this.deps.flushGapMs??J).unref?.()}ackGateOpen(e){const t=this.awaitingAck.get(e);if(t===void 0)return!0;const s=this.memory.get(t);return s&&s.acked!==!0&&s.status!=="failed"&&s.status!=="timeout"?!1:(this.awaitingAck.delete(e),!0)}blockedAhead(e){return this.ackGateOpen(e)?this.queuedSessions.has(e)||this.flushing.has(e):!0}noteInboxSocket(e,t){this.inboxSockets.set(e,t)}notePaneEnv(e,t){this.paneEnvs.set(e,t),this.persistPaneEnvs()}persistPaneEnvs(){const e=JSON.stringify(Object.fromEntries(this.paneEnvs));if(e===this.persistedPaneEnvs)return;const t=p(this.hooksDir,b);try{D(_(t),{recursive:!0});const s=`${t}.tmp`;P(s,`${e}
|
|
2
|
+
`,{encoding:"utf8",mode:384}),j(s,t),E(t,384),this.persistedPaneEnvs=e}catch{}}sessionExited(e){this.inboxSockets.forget(e),this.paneEnvs.delete(e),this.persistPaneEnvs(),this.awaitingAck.delete(e);const t=this.deps.sessions.getBySessionId(e),s=t?.projectId==null?null:this.deps.projects.getById(t.projectId);if(!s)return;const i=m(this.hooksDir,s.token).filter(n=>n.sessionId===e);this.failQueuedOffline(e,i.filter(n=>!this.dismissedIds.has(n.id)))}async sessionTerminals(){const e=this.terminalChannel();if(!e?.locateScreens||this.paneEnvs.size===0)return{};const t={},s=this.deps.livenessOf?.()??g();for(const[i,n]of this.paneEnvs){if(this.sessionJudgedDead(i,s)){this.paneEnvs.delete(i),this.persistPaneEnvs();continue}t[i]=n}return e.locateScreens(t)}diagnose(e){const t=this.surfaceOfSession(e),s=this.deps.sessions.livenessRowOf(e),i=s?S(s,this.deps.livenessOf?.()??g()):null,n=t?.promptInbox??null,r=this.inboxSockets.get(e),o=n?r?n.requiresAuthOn(process.platform)&&!r.token?{status:"unavailable",reason:"auth-token-required"}:{status:"ready",reason:null}:{status:"unavailable",reason:"no-credentials"}:{status:"unavailable",reason:"no-mechanism"},a=t?.prompt?.resolve(e)??null,l=t?.prompt?a?{status:"ready",reason:null}:{status:"unavailable",reason:"no-peer-name"}:{status:"unavailable",reason:"no-mechanism"},d=this.paneEnvs.has(e),u=this.terminalChannel()?d?{status:"ready",reason:null}:{status:"unknown",reason:"no-pane-env"}:{status:"unavailable",reason:"no-backend"},c=this.deps.sessions.getBySessionId(e),k=c?.projectId==null?null:this.deps.projects.getById(c.projectId),O=k?m(this.hooksDir,k.token).filter(x=>x.sessionId===e).length:0;return{alive:i,inbox:o,relay:l,terminal:u,paneEnvReported:d,pending:O,stalled:this.stalled.has(e)}}onSessionPrompt(e,t,s){const i=w(t),n=i.replace(/\[Image #\d+\]/gi,"").trim(),r=[];let o=!1;for(const a of[...this.memory.values()].reverse()){if(a.sessionId!==e||a.acked||a.mode!=="prompt")continue;const l=w(a.text),d=!!l&&(l.length>=X?i.includes(l):i===l),u=(a.images?.length??0)>0&&(l?y(i).includes(y(l)):n.length===0);!d&&!u||(o=!0,this.memory.set(a.id,{...a,acked:!0,stalled:!1,...s===void 0?{}:{ackedEventId:s}}),this.stalled.get(e)?.messageId===a.id&&this.stalled.delete(e),this.awaitingAck.get(e)===a.id&&this.awaitingAck.delete(e),r.push(...a.images?.map(c=>c.id)??[]),this.bus({kind:"message.acknowledged",messageId:a.id,sessionId:e,at:new Date().toISOString()}))}return o&&this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e),r}armAckWatch(e){setTimeout(()=>{const s=this.memory.get(e.id);!s||s.acked||s.status!=="delivered"||this.stall(s.sessionId,s,"unacked")},this.deps.ackStallMs??V).unref?.()}stall(e,t,s){this.stalled.set(e,{messageId:t.id,reason:s});const i=this.deps.ackStallReleaseMs??z,n=this.memory.get(t.id),r=n&&n.status==="delivered"&&n.acked!==!0?new Date(Date.now()+i).toISOString():null;n&&this.memory.set(t.id,{...n,stalled:!0,releaseAt:r}),this.bus({kind:"message.stalled",messageId:t.id,sessionId:e,reason:s,at:new Date().toISOString()}),setTimeout(()=>this.releaseStall(e,t.id),i).unref?.()}releaseStall(e,t){if(this.stalled.get(e)?.messageId!==t)return;const s=this.memory.get(t);if(s&&s.status==="delivered"&&s.acked!==!0){this.dismiss(e,t);return}this.stalled.delete(e),this.awaitingAck.get(e)===t&&this.awaitingAck.delete(e),this.queuedSessions.has(e)&&this.scheduleFlushContinuation(e)}async answer(e,t){if(t.length===0||!I.test(t[0])||t.some(i=>!I.test(i)&&!ee.test(i)))return{ok:!1,status:400,error:"Invalid answer keys"};const s=await this.sessionKeysTarget(e);if(!s.ok)return s;try{for(const[i,n]of t.entries())i>0&&await ae(re),await s.terminal.sendKeys(s.paneId,n);return{ok:!0}}catch(i){return{ok:!1,status:409,error:i instanceof Error?i.message:String(i)}}}async interrupt(e){const t=await this.sessionKeysTarget(e);if(!t.ok)return t;try{return await t.terminal.sendKeys(t.paneId,se),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async closePane(e){const t=await this.sessionKeysTarget(e,"\u5173\u95ED");if(!t.ok)return t;try{return await t.terminal.closePane(t.paneId),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async cycleMode(e){const t=await this.sessionKeysTarget(e);if(!t.ok)return t;try{return await t.terminal.sendKeys(t.paneId,te),{ok:!0}}catch(s){return{ok:!1,status:409,error:s instanceof Error?s.message:String(s)}}}async setModel(e,t){const s=t.trim();if(!ie.test(s))return{ok:!1,status:400,error:"\u6A21\u578B\u540D\u5F62\u6001\u4E0D\u5408\u6CD5\uFF08\u4E0D\u542B\u7A7A\u767D\u4E0E\u63A7\u5236\u5B57\u7B26\uFF0C\u2264128 \u5B57\u7B26\uFF09"};const i=await this.sessionKeysTarget(e);if(!i.ok)return i;try{return await i.terminal.sendText(i.paneId,ne(s)),{ok:!0}}catch(n){return{ok:!1,status:409,error:n instanceof Error?n.message:String(n)}}}async askSideQuestion(e,t){if(!this.deps.sessions.getBySessionId(e))return{ok:!1,status:404,error:"Session not found"};const s=this.surfaceOfSession(e),i=s?.sideQuestion??null;if(!i)return{ok:!1,status:409,error:s?.unsupportedReasons?.sideQuestion??"\u8FD9\u4E2A harness \u6CA1\u6709\u65C1\u8DEF\u4F1A\u8BDD\u901A\u9053"};const n=i.validate(t);if(!n.ok)return{ok:!1,status:400,error:n.error};const r=await this.sessionKeysTarget(e,"\u53D1\u8D77\u65C1\u8DEF\u4F1A\u8BDD");if(!r.ok)return r;try{return await r.terminal.sendText(r.paneId,i.command(n.text)),{ok:!0,question:n.text}}catch(o){return{ok:!1,status:409,error:o instanceof Error?o.message:String(o)}}}async sessionKeysTarget(e,t="\u51FB\u952E"){const s=this.deps.sessions.getBySessionId(e);if(!s)return{ok:!1,status:404,error:"Session not found"};if(s.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const i=this.deps.projects.getById(s.projectId);if(!i)return{ok:!1,status:404,error:"Project not found"};const n=this.terminalChannel();if(!n)return{ok:!1,status:409,error:`\u7EC8\u7AEF\u901A\u9053\u672A\u88C5\u914D\uFF0C\u65E0\u6CD5\u8FDC\u7A0B${t}`};const r=await this.terminalPaneFor(s,i);return r?{ok:!0,terminal:n,paneId:r.id}:{ok:!1,status:409,error:`\u627E\u4E0D\u5230\u4F1A\u8BDD\u6240\u5728\u7684\u7EC8\u7AEF\u7A97\u53E3\u2014\u2014\u8FD9\u4E2A\u4F1A\u8BDD\u6240\u5728\u7EC8\u7AEF\u6CA1\u6709\u53EF\u7528\u7684\u8FDC\u7A0B${t}\u901A\u9053`}}async resend(e){const t=this.memory.get(e);if(!t)return{ok:!1,status:404,error:"Message not found"};if(!(t.channel==="prompt"&&(t.status==="failed"||t.status==="delivered"&&!t.acked)))return{ok:!1,status:409,error:"Message is not resendable"};const i=this.deps.sessions.getBySessionId(t.sessionId);if(!i)return{ok:!1,status:404,error:"Session not found"};if(i.projectId==null)return{ok:!1,status:400,error:"Session is not bound to a project"};const n=this.deps.projects.getById(i.projectId);if(!n)return{ok:!1,status:404,error:"Project not found"};const r=this.surfaceOfSession(t.sessionId);if(!r)return{ok:!1,status:400,error:"Session has no hook events to determine its harness"};if(this.stalled.get(t.sessionId)?.messageId===e&&this.stalled.delete(t.sessionId),t.status==="delivered"&&t.via==="terminal"&&this.terminalChannel()){const l=await this.terminalPaneFor(i,n);if(l)return this.memory.set(e,{...t,status:"delivering",stalled:!1,settledAt:null}),this.runTerminalSubmit(e,l.id),this.bus({kind:"message.created",messageId:e,sessionId:t.sessionId,mode:t.mode,at:new Date().toISOString()}),{ok:!0,messageId:e,status:"delivering",message:this.memory.get(e)}}this.memory.delete(e);const o={...t,status:"pending",stalled:!1,acked:!1,settledAt:null,reason:void 0,error:null},a=await this.deliverPrompt(o,i,n,r);return this.bus({kind:"message.created",messageId:o.id,sessionId:o.sessionId,mode:o.mode,at:new Date().toISOString()}),a}dismiss(e,t){const s=this.memory.get(t);if(s){if(s.status==="delivering"||s.status==="running")return{ok:!1,status:409,error:"Message is still in flight"};this.memory.delete(t),this.stalled.get(s.sessionId)?.messageId===t&&this.stalled.delete(s.sessionId),this.awaitingAck.get(s.sessionId)===t&&this.awaitingAck.delete(s.sessionId),this.queuedSessions.has(s.sessionId)&&this.scheduleFlushContinuation(s.sessionId)}else if(!this.dropQueued(e,t))return{ok:!1,status:404,error:"Message not found"};return this.bus({kind:"message.dismissed",messageId:t,sessionId:e,at:new Date().toISOString()}),{ok:!0}}dropQueued(e,t){const s=this.deps.sessions.getBySessionId(e);if(s?.projectId==null)return!1;const i=this.deps.projects.getById(s.projectId);if(!i)return!1;const n=m(this.hooksDir,i.token);if(!n.some(r=>r.id===t&&r.sessionId===e))return!1;for(f(this.hooksDir,i.token,new Set([t])),this.dismissedIds.add(t);this.dismissedIds.size>Y;){const r=this.dismissedIds.values().next().value;if(r===void 0)break;this.dismissedIds.delete(r)}return n.some(r=>r.sessionId===e&&r.id!==t)||this.queuedSessions.delete(e),!0}async flushSession(e){if(!this.stalled.has(e)&&!this.flushing.has(e)&&this.queuedSessions.has(e)){this.flushing.add(e);try{await this.flushSessionInner(e)}catch{}finally{this.flushing.delete(e)}}}async flushSessionInner(e){if(!this.ackGateOpen(e))return;const t=this.deps.sessions.getBySessionId(e);if(t?.projectId==null)return;const s=this.deps.projects.getById(t.projectId);if(!s)return;const i=m(this.hooksDir,s.token).filter(o=>o.sessionId===e);if(i.length===0){this.queuedSessions.delete(e);return}if(this.sessionJudgedDead(e)){this.failQueuedOffline(e,i.filter(o=>!this.dismissedIds.has(o.id)));return}const n=this.surfaceOfSession(e),r=n?.prompt??null;for(const o of i){if(this.stalled.has(e))return;if(this.dismissedIds.has(o.id))continue;const a={id:o.id,sessionId:e,text:o.text,...o.images&&o.images.length>0?{images:o.images}:{},mode:"prompt",oneWay:o.oneWay===!0,...o.forceTerminal===!0?{forceTerminal:!0}:{},via:"socket",channel:"prompt",status:"pending",at:o.at};if(this.hasImages(a)&&this.terminalChannel()){const u=await this.terminalPaneFor(t,s);if(u){f(this.hooksDir,s.token,new Set([o.id])),this.memory.set(a.id,{...a,via:"terminal",status:"delivering"}),this.awaitingAck.set(e,a.id),await this.runTerminalPrompt(a.id,u.id,this.wireTextOf(a)),this.settleQueueIndex(e,s.token);return}}if(n&&await this.tryInboxDeliver(a,n)){f(this.hooksDir,s.token,new Set([o.id])),this.settleQueueIndex(e,s.token);return}const l=r?.resolve(e)??null,d=a.forceTerminal===!0?["terminal"]:a.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const u of d){if(u==="relay"&&r&&l){if(this.memory.set(a.id,{...a,status:"delivering",via:"relay"}),!this.runRelay(a,r,l.name)){this.memory.delete(a.id);return}this.awaitingAck.set(e,a.id),f(this.hooksDir,s.token,new Set([o.id]));break}if(u==="terminal"&&this.terminalChannel()){const c=await this.terminalPaneFor(t,s);if(c){f(this.hooksDir,s.token,new Set([o.id])),this.memory.set(a.id,{...a,via:"terminal",status:"delivering"}),this.awaitingAck.set(e,a.id),await this.runTerminalPrompt(a.id,c.id,this.wireTextOf(a));break}}}if(this.awaitingAck.get(e)===a.id){this.settleQueueIndex(e,s.token);return}return}this.settleQueueIndex(e,s.token)}settleQueueIndex(e,t){m(this.hooksDir,t).every(s=>s.sessionId!==e)&&this.queuedSessions.delete(e)}async flushAll(){for(const e of[...this.queuedSessions])await this.flushSession(e)}forgetSession(e){let t=0;for(const[s,i]of this.memory)i.sessionId===e&&(this.memory.delete(s),t++);return t+=Q(this.hooksDir,e),this.queuedSessions.delete(e),this.stalled.delete(e),this.awaitingAck.delete(e),this.inboxSockets.forget(e),this.paneEnvs.delete(e),this.persistPaneEnvs(),t}forgetProject(e){let t=0;for(const[s,i]of this.memory)this.deps.sessions.getBySessionId(i.sessionId)?.projectId===e&&(this.memory.delete(s),this.inboxSockets.forget(i.sessionId),this.paneEnvs.delete(i.sessionId),this.stalled.delete(i.sessionId),this.awaitingAck.delete(i.sessionId),t++);for(const s of[...this.queuedSessions])this.deps.sessions.getBySessionId(s)?.projectId===e&&(t+=this.forgetSession(s));return this.persistPaneEnvs(),t}surfaceOfSession(e){const t=this.deps.hookEvents.listBySession(e,1),s=t[t.length-1]?.source;return B(s)?this.deps.surfaceFor?.(s)??C(s):null}resolveChannel(e,t,s){if(e==="prompt")return s.prompt||this.terminalChannel()?"prompt":{reason:s.unsupportedReasons?.prompt??`Harness "${s.harness}" has no native prompt channel and the configured terminal cannot inject text`};if(e==="inject"||e==="headless")return(e==="inject"?s.inject:s.headless)?e:{reason:this.unsupportedReason(s,e)};const n=this.deps.sessions.turnOpenOf(t)?"inject":"headless";return(n==="inject"?s.inject:s.headless)?n:{reason:this.unsupportedReason(s,n)}}unsupportedReason(e,t){return e.unsupportedReasons?.[t]??`Harness "${e.harness}" does not support the ${t} messaging channel`}async terminalPaneFor(e,t){const s=this.terminalChannel();if(!s)return null;const i=this.paneEnvs.get(e.sessionId);if(i){const l=await s.locatePane?.(i).catch(()=>null);if(l)return{id:l}}if(!e.title)return null;const n=e.title.replace(/^[✻✽✶✳✢⋅◦✦✧∘◐◑◒◓*]+\s*/,""),r=v(t.path,process.platform);let o;try{o=await s.listPanes()}catch{return null}const a=o.filter(l=>l.title!=null&&l.cwd!=null&&r!=null&&l.title.replace(/^[✻✽✶✳✢⋅◦✦✧∘◐◑◒◓*]+\s*/,"")===n&&v(l.cwd,process.platform)===r);return a.length===1?a[0]:null}terminalChannel(){const e=this.deps.terminal;return e&&e.available()?e:null}async deliverPrompt(e,t,s,i){if(this.sessionJudgedDead(e.sessionId))return{ok:!0,messageId:e.id,status:"failed",message:this.failOffline(e)};if(this.hasImages(e)&&this.terminalChannel()){const d=await this.terminalPaneFor(t,s);if(d)return this.memory.set(e.id,{...e,status:"delivering",via:"terminal"}),this.awaitingAck.set(e.sessionId,e.id),this.runTerminalPrompt(e.id,d.id,this.wireTextOf(e)),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)}}const n=await this.tryInboxDeliver(e,i);if(n)return{ok:!0,messageId:e.id,status:"delivered",message:n};const r=i.prompt,o=r?.resolve(e.sessionId)??null,a=e.forceTerminal===!0?["terminal"]:e.oneWay===!0?["terminal","relay"]:["relay","terminal"];for(const d of a){if(d==="relay"&&r&&o){if(this.memory.set(e.id,{...e,status:"delivering",via:"relay"}),this.runRelay(e,r,o.name))return this.awaitingAck.set(e.sessionId,e.id),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)};this.memory.delete(e.id)}if(d==="terminal"&&this.terminalChannel()){const u=await this.terminalPaneFor(t,s);if(u)return this.memory.set(e.id,{...e,status:"delivering",via:"terminal"}),this.awaitingAck.set(e.sessionId,e.id),this.runTerminalPrompt(e.id,u.id,this.wireTextOf(e)),{ok:!0,messageId:e.id,status:"delivering",message:this.memory.get(e.id)}}}if(this.sessionJudgedDead(e.sessionId))return{ok:!0,messageId:e.id,status:"failed",message:this.failOffline(e)};this.queueEntry(e,s.token);const l={...e,status:"pending"};return{ok:!0,messageId:e.id,status:"pending",message:l}}async tryInboxDeliver(e,t){const s=t.promptInbox,i=this.inboxSockets.get(e.sessionId);if(!s||e.oneWay!==!0||!i||e.forceTerminal===!0)return null;this.memory.set(e.id,{...e,status:"delivering",via:"socket"});try{await K(s,i,this.wireTextOf(e))}catch{return this.memory.delete(e.id),null}const n={...this.memory.get(e.id),status:"delivered",via:"socket",settledAt:new Date().toISOString()};return this.memory.set(e.id,n),this.awaitingAck.set(e.sessionId,e.id),this.armAckWatch(n),this.bus({kind:"message.delivered",messageId:e.id,sessionId:e.sessionId,at:new Date().toISOString()}),n}queueEntry(e,t){$(this.hooksDir,t,[{id:e.id,sessionId:e.sessionId,text:e.text,oneWay:e.oneWay===!0,...e.forceTerminal===!0?{forceTerminal:!0}:{},...e.images&&e.images.length>0?{images:e.images}:{}}]),this.queuedSessions.add(e.sessionId)}sessionJudgedDead(e,t=this.deps.livenessOf?.()??g()){const s=this.deps.sessions.livenessRowOf(e);return s?S(s,t)===!1:!1}failOffline(e){const t=new Date().toISOString(),s={...e,status:"failed",reason:"offline",error:Z,settledAt:t};return this.memory.set(e.id,s),this.bus({kind:"message.finished",messageId:e.id,sessionId:e.sessionId,status:"failed",at:t}),s}failQueuedOffline(e,t){const s=this.deps.sessions.getBySessionId(e),i=s?.projectId==null?null:this.deps.projects.getById(s.projectId);if(!(!i||t.length===0)){for(const n of t)this.failOffline({id:n.id,sessionId:e,text:n.text,...n.images&&n.images.length>0?{images:n.images}:{},mode:"prompt",oneWay:n.oneWay===!0,channel:"prompt",status:"pending",at:n.at});f(this.hooksDir,i.token,new Set(t.map(n=>n.id))),this.queuedSessions.delete(e)}}runRelay(e,t,s){try{return this.headless.run({sessionId:`prompt-relay:${e.sessionId}`,cwd:G(),prompt:t.instruction(s,this.wireTextOf(e),{oneWay:e.oneWay===!0}),spec:{supported:!0,bin:t.bin,args:()=>[...t.args],promptStdin:!0},bin:this.deps.bin??process.env.WEAVE_CLAUDE_BIN??t.bin,timeoutMs:this.deps.relayTimeoutMs??0,detached:!1,onSettle:i=>this.settleRelay(e.id,i)})}catch(i){return this.settleRelay(e.id,{status:"failed",output:null,error:i instanceof Error?i.message:String(i)}),!0}}settleRelay(e,t){const s=this.memory.get(e);if(!s)return;if(t.status==="succeeded"){this.queuedSessions.has(s.sessionId)&&this.scheduleFlushContinuation(s.sessionId);const n={...s,status:"delivered",settledAt:new Date().toISOString()};this.memory.set(e,n),this.armAckWatch(n),this.bus({kind:"message.delivered",messageId:e,sessionId:s.sessionId,at:new Date().toISOString()});return}if(s.acked===!0){const n=new Date().toISOString();this.memory.set(e,{...s,status:"delivered",settledAt:n}),this.bus({kind:"message.delivered",messageId:e,sessionId:s.sessionId,at:n});return}const i={...s,status:"failed",error:t.error??`prompt relay ${t.status}`,settledAt:new Date().toISOString()};this.memory.set(e,i),this.stall(s.sessionId,i,"failed"),this.bus({kind:"message.finished",messageId:e,sessionId:s.sessionId,status:"failed",at:new Date().toISOString()})}async runTerminalPrompt(e,t,s){await this.runTerminal(e,i=>i.sendText(t,s))}async runTerminalSubmit(e,t){await this.runTerminal(e,s=>s.sendKeys(t,"\r"))}async runTerminal(e,t){const s=this.memory.get(e);if(!s)return;const i=this.terminalChannel();if(i)try{await t(i);const r={...this.memory.get(e)??s,status:"delivered",settledAt:new Date().toISOString()};this.memory.set(e,r),this.armAckWatch(r),this.bus({kind:"message.delivered",messageId:e,sessionId:s.sessionId,at:new Date().toISOString()})}catch(n){const r=this.memory.get(e)??s;if(r.acked===!0){const a=new Date().toISOString();this.memory.set(e,{...r,status:"delivered",settledAt:a}),this.bus({kind:"message.delivered",messageId:e,sessionId:r.sessionId,at:a});return}const o={...r,status:"failed",error:n instanceof Error?n.message:String(n),settledAt:new Date().toISOString()};this.memory.set(e,o),this.stall(s.sessionId,o,"failed"),this.bus({kind:"message.finished",messageId:e,sessionId:s.sessionId,status:"failed",at:new Date().toISOString()})}}wireTextOf(e){const t=e.images;return!t||t.length===0?e.text:this.surfaceOfSession(e.sessionId)?.imageAttach?.composePrompt(e.text,t)??e.text}hasImages(e){return(e.images?.length??0)>0}deliverInject(e,t,s,i){const n=this.renderEnvelope(this.wireTextOf(e),s,i);q(this.hooksDir,t,[{sessionId:e.sessionId,text:n}]);const r={...e,status:"injected",settledAt:new Date().toISOString()};return this.memory.set(r.id,r),this.bus({kind:"message.delivered",messageId:r.id,sessionId:r.sessionId,at:new Date().toISOString()}),{ok:!0,messageId:r.id,status:"injected",message:r}}deliverHeadless(e,t,s,i){const n=i.headless,r={...e,status:"running"};this.memory.set(r.id,r);const o={settled:!1};try{this.headless.run({sessionId:r.sessionId,cwd:t,prompt:this.renderEnvelope(this.wireTextOf(r),s,i),spec:n,bin:this.deps.bin??process.env.WEAVE_CLAUDE_BIN??n.bin,timeoutMs:this.deps.timeoutMs,onSettle:l=>this.settle(r,o,l)})||this.settle(r,o,{status:"failed",output:null,error:"Session already has a running run"})}catch(a){this.settle(r,o,{status:"failed",output:null,error:a instanceof Error?a.message:String(a)})}return{ok:!0,messageId:r.id,status:"running",message:this.memory.get(r.id)}}renderEnvelope(e,t,s){return s.renderEnvelope?.(e,t)??`[weave message] from=${t.fromLabel} at=${t.at} mode=${t.mode}
|
|
2
3
|
|
|
3
|
-
${
|
|
4
|
+
${e}`}settle(e,t,s){if(t.settled)return;t.settled=!0;const i=this.memory.get(e.id);if(i)try{this.memory.set(e.id,{...i,status:s.status,output:s.output,error:s.error,settledAt:new Date().toISOString()}),this.bus({kind:"message.finished",messageId:e.id,sessionId:e.sessionId,status:s.status,at:new Date().toISOString()})}catch{}}bus(e){try{this.deps.bus?.emit(e)}catch{}}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HookRule } from '@weavecode/core';
|
|
1
|
+
import type { Harness, HookRule } from '@weavecode/core';
|
|
2
2
|
import type { ProjectRow, ProjectRepository } from '../repositories/projects.js';
|
|
3
3
|
import type { SessionRepository } from '../repositories/sessions.js';
|
|
4
4
|
import type { HookRuleRegistry } from './registry.js';
|
|
@@ -23,6 +23,12 @@ export declare function outboxPathOf(hooksDir: string, shardId: string): string;
|
|
|
23
23
|
export interface MirrorRule {
|
|
24
24
|
id: string;
|
|
25
25
|
name: string;
|
|
26
|
+
/**
|
|
27
|
+
* 归属 harness('any' = 各 harness 都该吃)。同一项目的各 harness 共读一份
|
|
28
|
+
* 镜像,故必须逐条盖章、由生成脚本按自己的 harness 过滤——不能在这一侧按某个
|
|
29
|
+
* harness 剪枝。
|
|
30
|
+
*/
|
|
31
|
+
harness: Harness | 'any';
|
|
26
32
|
events: string[];
|
|
27
33
|
tools: string[] | null;
|
|
28
34
|
globs: string[] | null;
|
|
@@ -37,9 +43,8 @@ export interface HookMirrorDoc {
|
|
|
37
43
|
}
|
|
38
44
|
/**
|
|
39
45
|
* 项目 → hook 本地注入计划。只投影 inject 规则(manual/webhook/log/plugin 的
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* outbox 顺延。
|
|
46
|
+
* 动作都在消费侧);crossProjectOnly 需要全项目表参与判定、hook 无法本地裁决
|
|
47
|
+
* → 不投影,其注入走 outbox 顺延。
|
|
43
48
|
*/
|
|
44
49
|
export declare function buildMirrorDoc(project: ProjectRow, rules: HookRule[], pendingTitles: Record<string, string>): HookMirrorDoc;
|
|
45
50
|
export interface MirrorWriterDeps {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{createHash as
|
|
2
|
-
`,{flag:"a"})}export function appendOutbox(
|
|
3
|
-
`).filter(c=>c.trim())}catch{t=[]}for(const c of e)t.push(JSON.stringify({...c,at:new Date().toISOString()}));const
|
|
1
|
+
import{createHash as h}from"node:crypto";import{mkdirSync as a,readFileSync as u,renameSync as f,writeFileSync as p}from"node:fs";import{join as s}from"node:path";import{hookSourceHarness as m}from"@weavecode/core";import{getDataDir as l}from"../db/paths.js";import{projectLabel as y}from"./match.js";export const MIRROR_VERSION=1,OUTBOX_MAX_LINES=200;export function hookShardId(n){return h("sha256").update(n).digest("hex").slice(0,16)}export function hooksRoot(n=l()){return s(n,"hooks")}export function mirrorPathOf(n,r){return s(n,"mirror",`${r}.json`)}export function outboxPathOf(n,r){return s(n,"outbox",`${r}.jsonl`)}export function buildMirrorDoc(n,r,e){const o=r.filter(t=>!(t.source.kind!=="hook-event"||t.handler.type!=="inject"||!t.enabled||t.projectId!==null&&t.projectId!==n.id||t.filter.crossProjectOnly)).sort((t,i)=>i.priority-t.priority).map(t=>({id:t.id,name:t.name,harness:m(t.source)??"claude",events:[t.source.kind==="hook-event"?t.source.event:""],tools:t.filter.toolNames?.length?[...t.filter.toolNames]:null,globs:t.filter.pathGlobs?.length?[...t.filter.pathGlobs]:null,template:t.handler.type==="inject"?t.handler.template:""}));return{version:MIRROR_VERSION,project:y(n.path),pendingTitles:e,rules:o}}export class MirrorWriter{deps;dataDir;constructor(r){this.deps=r,this.dataDir=r.dataDir??l()}syncAll(){for(const r of this.deps.projects.list())this.syncProject(r.id)}syncProject(r){const e=this.deps.projects.getById(r);if(!e||e.archivedAt)return;const o={};for(const t of this.deps.sessions.list(r))t.pendingTitle&&(o[t.sessionId]=t.pendingTitle);this.write(e.token,buildMirrorDoc(e,this.deps.rules.list(),o))}write(r,e){try{const o=mirrorPathOf(hooksRoot(this.dataDir),hookShardId(r));a(s(hooksRoot(this.dataDir),"mirror"),{recursive:!0});const t=`${o}.tmp`;p(t,JSON.stringify(e)),f(t,o)}catch{}}attachToBus(r){r.subscribe(e=>{if(e.kind!=="session.renamed"||!e.sessionId)return;const o=this.deps.sessions.getBySessionId(e.sessionId);o?.projectId!=null&&this.syncProject(o.projectId)})}}export function appendInbox(n,r,e){const o=s(n,"inbox");a(o,{recursive:!0}),p(s(o,`${hookShardId(r)}.jsonl`),JSON.stringify({v:1,token:r,report:e})+`
|
|
2
|
+
`,{flag:"a"})}export function appendOutbox(n,r,e){if(e.length!==0)try{const o=outboxPathOf(n,hookShardId(r));a(s(n,"outbox"),{recursive:!0});let t=[];try{t=u(o,"utf8").split(`
|
|
3
|
+
`).filter(c=>c.trim())}catch{t=[]}for(const c of e)t.push(JSON.stringify({...c,at:new Date().toISOString()}));const i=t.slice(-OUTBOX_MAX_LINES),d=`${o}.tmp`;p(d,i.length?i.join(`
|
|
4
4
|
`)+`
|
|
5
|
-
`:""),d
|
|
5
|
+
`:""),f(d,o)}catch{}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{cp as j,mkdir as p,readFile as
|
|
2
|
-
`,"utf8")}async function
|
|
3
|
-
`,"utf8")}export async function searchPlugins(
|
|
1
|
+
import{cp as j,mkdir as p,readFile as g,readdir as D,rm as m,writeFile as P}from"node:fs/promises";import{existsSync as v}from"node:fs";import s from"node:path";import{spawn as F}from"node:child_process";import{WEAVE_PLUGIN_MANIFEST as x,WeavePluginManifestSchema as J}from"@weavecode/core";import{getDataDir as A}from"../db/paths.js";export function globalPluginRoot(){return s.join(A(),"plugins")}function E(t){return s.join(t??globalPluginRoot(),"installed.json")}async function N(t){try{const r=JSON.parse(await g(E(t),"utf8"));return Array.isArray(r)?r:[]}catch{return[]}}async function O(t,r){const i=r??globalPluginRoot();await p(i,{recursive:!0}),await P(E(r),JSON.stringify(t,null,2)+`
|
|
2
|
+
`,"utf8")}async function S(t){try{const r=JSON.parse(await g(s.join(t,x),"utf8"));return J.safeParse(r).success?r:null}catch{return null}}async function T(t){const r=[],i=await S(t);if(i)return[{dir:t,manifest:i}];let o;try{o=await D(t)}catch{return r}for(const a of o){const e=s.join(t,a),n=await S(e);n&&r.push({dir:e,manifest:n})}return r}function W(t,r,i,o=6e4){return new Promise((a,e)=>{const n=F(t,r,{cwd:i,windowsHide:!0,stdio:["ignore","pipe","pipe"]});let u="";const l=setTimeout(()=>{n.kill(),e(new Error(`${t} ${r.join(" ")} \u8D85\u65F6\uFF08${o}ms\uFF09`))},o);n.stderr?.on("data",c=>u+=c.toString()),n.on("error",c=>{clearTimeout(l),e(c)}),n.on("close",c=>{clearTimeout(l),c===0?a():e(new Error(`${t} \u9000\u51FA\u7801 ${c}: ${u.slice(0,500)}`))})})}export function isGitSource(t){return/^(https?:\/\/|git@|ssh:\/\/|git:\/\/)/.test(t)||t.endsWith(".git")}export async function installPlugin(t,r){const i=[];let o,a;if(isGitSource(t)){i.push(`\u4ECE ${t} \u5B89\u88C5\u7684\u63D2\u4EF6\u672A\u7ECF\u6C99\u7BB1\u9694\u79BB\uFF0C\u8BF7\u786E\u8BA4\u6765\u6E90\u53EF\u4FE1`);const e=s.join(A(),"tmp",`install-${Date.now()}`);await p(e,{recursive:!0}),await W("git",["clone","--depth","1",t,s.join(e,"repo")]),o=s.join(e,"repo"),a=()=>m(e,{recursive:!0,force:!0})}else if(o=s.resolve(t),!v(o))throw new Error(`\u672C\u5730\u8DEF\u5F84\u4E0D\u5B58\u5728: ${o}`);try{const e=await T(o);if(e.length===0)throw new Error(`\u672A\u627E\u5230 ${x}\uFF08src \u6216\u5176\u4E00\u7EA7\u5B50\u76EE\u5F55\uFF09`);const n=r?.root??globalPluginRoot();await p(n,{recursive:!0});const u=await N(r?.root),l=[];for(const{dir:c,manifest:y}of e){const f=String(y.id),h=s.join(n,f);s.resolve(h).startsWith(s.resolve(n)+s.sep)&&(await m(h,{recursive:!0,force:!0}),await j(c,h,{recursive:!0,filter:w=>!w.includes("node_modules")&&!w.includes(`${s.sep}.git`)}));const d={id:f,name:String(y.name??f),version:String(y.version??"0.0.0"),source:t,dir:f,installedAt:new Date().toISOString()},$=u.findIndex(w=>w.id===f);$>=0?u[$]=d:u.push(d),l.push({id:f,name:d.name,version:d.version,dir:f})}return await O(u,r?.root),{installed:l,warnings:i}}finally{a&&await a()}}export async function uninstallPlugin(t,r){const i=r?.root??globalPluginRoot(),o=await N(r?.root),a=o.find(n=>n.id===t),e=a?s.join(i,a.dir):s.join(i,t);return v(e)?(await m(e,{recursive:!0,force:!0}),a&&await O(o.filter(n=>n.id!==t),r?.root),!0):!1}export async function exportPlugin(t,r,i){const o=s.join(i?.root??globalPluginRoot(),t);if(!v(o))throw new Error(`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728: ${o}`);if(!await S(o))throw new Error(`\u63D2\u4EF6\u7F3A\u5C11\u5408\u6CD5 ${x}: ${t}`);const e=s.join(s.resolve(r),t);return await m(e,{recursive:!0,force:!0}),await j(o,e,{recursive:!0,filter:n=>!n.includes("node_modules")&&!n.includes(`${s.sep}.git`)}),e}function _(t){return s.join(t??globalPluginRoot(),"sources.json")}export function parseSources(t){if(Array.isArray(t))return{indexes:t.filter(r=>typeof r=="string")};if(t&&typeof t=="object"){const r=t.indexes;return{indexes:Array.isArray(r)?r.filter(i=>typeof i=="string"):[]}}return{indexes:[]}}export async function readSourcesFile(t){try{return parseSources(JSON.parse(await g(_(t),"utf8")))}catch{return{indexes:[]}}}export async function writeSourcesFile(t,r){const i=r??globalPluginRoot();await p(i,{recursive:!0}),await P(_(r),JSON.stringify(t,null,2)+`
|
|
3
|
+
`,"utf8")}export async function searchPlugins(t){const{indexes:r}=await readSourcesFile(),i=[];for(const o of r)try{const a=await fetch(o,{signal:AbortSignal.timeout(1e4)});if(!a.ok)continue;const e=await a.json();for(const n of e.plugins??[]){const u=`${n.id} ${n.name} ${n.description??""}`.toLowerCase();(!t||u.includes(t.toLowerCase()))&&i.push(n)}}catch{}return i}
|