@xgjktech/xg_cwork_im 1.19.21 → 1.19.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -26
- package/dist/index.js.map +1 -1
- package/dist/src/connection/account-connection.d.ts +37 -0
- package/dist/src/connection/account-connection.d.ts.map +1 -0
- package/dist/src/connection/account-connection.js +548 -0
- package/dist/src/connection/account-connection.js.map +1 -0
- package/dist/src/connection/account-reconciler.d.ts +64 -0
- package/dist/src/connection/account-reconciler.d.ts.map +1 -0
- package/dist/src/connection/account-reconciler.js +314 -0
- package/dist/src/connection/account-reconciler.js.map +1 -0
- package/dist/src/connection/account-reconciler.test.d.ts +2 -0
- package/dist/src/connection/account-reconciler.test.d.ts.map +1 -0
- package/dist/src/connection/account-reconciler.test.js +166 -0
- package/dist/src/connection/account-reconciler.test.js.map +1 -0
- package/dist/src/connection/agent-stream-client-registry.d.ts +8 -0
- package/dist/src/connection/agent-stream-client-registry.d.ts.map +1 -0
- package/dist/src/connection/agent-stream-client-registry.js +19 -0
- package/dist/src/connection/agent-stream-client-registry.js.map +1 -0
- package/dist/src/connection/connection.d.ts +116 -0
- package/dist/src/connection/connection.d.ts.map +1 -0
- package/dist/src/connection/connection.js +469 -0
- package/dist/src/connection/connection.js.map +1 -0
- package/dist/src/connection/connection.test.d.ts +2 -0
- package/dist/src/connection/connection.test.d.ts.map +1 -0
- package/dist/src/connection/connection.test.js +126 -0
- package/dist/src/connection/connection.test.js.map +1 -0
- package/dist/src/connection/ws-inbound-schema.d.ts +14 -0
- package/dist/src/connection/ws-inbound-schema.d.ts.map +1 -0
- package/dist/src/connection/ws-inbound-schema.js +68 -0
- package/dist/src/connection/ws-inbound-schema.js.map +1 -0
- package/dist/src/connection/ws-inbound-schema.test.d.ts +2 -0
- package/dist/src/connection/ws-inbound-schema.test.d.ts.map +1 -0
- package/dist/src/connection/ws-inbound-schema.test.js +58 -0
- package/dist/src/connection/ws-inbound-schema.test.js.map +1 -0
- package/dist/src/delivery/assistant-item-delivery.d.ts +27 -0
- package/dist/src/delivery/assistant-item-delivery.d.ts.map +1 -0
- package/dist/src/delivery/assistant-item-delivery.js +134 -0
- package/dist/src/delivery/assistant-item-delivery.js.map +1 -0
- package/dist/src/delivery/assistant-item-delivery.test.d.ts +2 -0
- package/dist/src/delivery/assistant-item-delivery.test.d.ts.map +1 -0
- package/dist/src/delivery/assistant-item-delivery.test.js +116 -0
- package/dist/src/delivery/assistant-item-delivery.test.js.map +1 -0
- package/dist/src/delivery/assistant-item-sink.d.ts +17 -0
- package/dist/src/delivery/assistant-item-sink.d.ts.map +1 -0
- package/dist/src/delivery/assistant-item-sink.js +34 -0
- package/dist/src/delivery/assistant-item-sink.js.map +1 -0
- package/dist/src/delivery/block-streaming-config.test.d.ts +2 -0
- package/dist/src/delivery/block-streaming-config.test.d.ts.map +1 -0
- package/dist/src/delivery/block-streaming-config.test.js +91 -0
- package/dist/src/delivery/block-streaming-config.test.js.map +1 -0
- package/dist/src/delivery/deliver-text.d.ts +30 -0
- package/dist/src/delivery/deliver-text.d.ts.map +1 -0
- package/dist/src/delivery/deliver-text.js +88 -0
- package/dist/src/delivery/deliver-text.js.map +1 -0
- package/dist/src/delivery/deliver-text.test.d.ts +2 -0
- package/dist/src/delivery/deliver-text.test.d.ts.map +1 -0
- package/dist/src/delivery/deliver-text.test.js +117 -0
- package/dist/src/delivery/deliver-text.test.js.map +1 -0
- package/dist/src/delivery/im-tool-progress.d.ts +4 -0
- package/dist/src/delivery/im-tool-progress.d.ts.map +1 -0
- package/dist/src/delivery/im-tool-progress.js +87 -0
- package/dist/src/delivery/im-tool-progress.js.map +1 -0
- package/dist/src/delivery/inbound-terminal-delivery.d.ts +77 -0
- package/dist/src/delivery/inbound-terminal-delivery.d.ts.map +1 -0
- package/dist/src/delivery/inbound-terminal-delivery.js +251 -0
- package/dist/src/delivery/inbound-terminal-delivery.js.map +1 -0
- package/dist/src/delivery/inbound-terminal-state.d.ts +68 -0
- package/dist/src/delivery/inbound-terminal-state.d.ts.map +1 -0
- package/dist/src/delivery/inbound-terminal-state.js +185 -0
- package/dist/src/delivery/inbound-terminal-state.js.map +1 -0
- package/dist/src/delivery/inbound-terminal-state.test.d.ts +2 -0
- package/dist/src/delivery/inbound-terminal-state.test.d.ts.map +1 -0
- package/dist/src/delivery/inbound-terminal-state.test.js +224 -0
- package/dist/src/delivery/inbound-terminal-state.test.js.map +1 -0
- package/dist/src/delivery/send-file-event.d.ts +26 -0
- package/dist/src/delivery/send-file-event.d.ts.map +1 -0
- package/dist/src/delivery/send-file-event.js +33 -0
- package/dist/src/delivery/send-file-event.js.map +1 -0
- package/dist/src/delivery/send-file-event.test.d.ts +2 -0
- package/dist/src/delivery/send-file-event.test.d.ts.map +1 -0
- package/dist/src/delivery/send-file-event.test.js +86 -0
- package/dist/src/delivery/send-file-event.test.js.map +1 -0
- package/dist/src/delivery/send-service.d.ts +83 -0
- package/dist/src/delivery/send-service.d.ts.map +1 -0
- package/dist/src/delivery/send-service.js +324 -0
- package/dist/src/delivery/send-service.js.map +1 -0
- package/dist/src/delivery/send-service.test.d.ts +2 -0
- package/dist/src/delivery/send-service.test.d.ts.map +1 -0
- package/dist/src/delivery/send-service.test.js +40 -0
- package/dist/src/delivery/send-service.test.js.map +1 -0
- package/dist/src/delivery/thinking-ws-progress.d.ts +24 -0
- package/dist/src/delivery/thinking-ws-progress.d.ts.map +1 -0
- package/dist/src/delivery/thinking-ws-progress.js +45 -0
- package/dist/src/delivery/thinking-ws-progress.js.map +1 -0
- package/dist/src/delivery/thinking-ws-progress.test.d.ts +2 -0
- package/dist/src/delivery/thinking-ws-progress.test.d.ts.map +1 -0
- package/dist/src/delivery/thinking-ws-progress.test.js +38 -0
- package/dist/src/delivery/thinking-ws-progress.test.js.map +1 -0
- package/dist/src/delivery/turn-deliver-buffer.d.ts +60 -0
- package/dist/src/delivery/turn-deliver-buffer.d.ts.map +1 -0
- package/dist/src/delivery/turn-deliver-buffer.js +133 -0
- package/dist/src/delivery/turn-deliver-buffer.js.map +1 -0
- package/dist/src/delivery/turn-deliver-buffer.test.d.ts +2 -0
- package/dist/src/delivery/turn-deliver-buffer.test.d.ts.map +1 -0
- package/dist/src/delivery/turn-deliver-buffer.test.js +184 -0
- package/dist/src/delivery/turn-deliver-buffer.test.js.map +1 -0
- package/dist/src/delivery/user-facing-errors.d.ts +32 -0
- package/dist/src/delivery/user-facing-errors.d.ts.map +1 -0
- package/dist/src/delivery/user-facing-errors.js +120 -0
- package/dist/src/delivery/user-facing-errors.js.map +1 -0
- package/dist/src/delivery/user-facing-errors.test.d.ts +2 -0
- package/dist/src/delivery/user-facing-errors.test.d.ts.map +1 -0
- package/dist/src/delivery/user-facing-errors.test.js +102 -0
- package/dist/src/delivery/user-facing-errors.test.js.map +1 -0
- package/dist/src/inbound/dispatch-mentioned-reply.d.ts +47 -0
- package/dist/src/inbound/dispatch-mentioned-reply.d.ts.map +1 -0
- package/dist/src/inbound/dispatch-mentioned-reply.js +1014 -0
- package/dist/src/inbound/dispatch-mentioned-reply.js.map +1 -0
- package/dist/src/inbound/inbound-archive.d.ts +4 -0
- package/dist/src/inbound/inbound-archive.d.ts.map +1 -0
- package/dist/src/inbound/inbound-archive.js +105 -0
- package/dist/src/inbound/inbound-archive.js.map +1 -0
- package/dist/src/inbound/inbound-attachment-meta.d.ts +13 -0
- package/dist/src/inbound/inbound-attachment-meta.d.ts.map +1 -0
- package/dist/src/inbound/inbound-attachment-meta.js +39 -0
- package/dist/src/inbound/inbound-attachment-meta.js.map +1 -0
- package/dist/src/inbound/inbound-chat-type.d.ts +33 -0
- package/dist/src/inbound/inbound-chat-type.d.ts.map +1 -0
- package/dist/src/inbound/inbound-chat-type.js +52 -0
- package/dist/src/inbound/inbound-chat-type.js.map +1 -0
- package/dist/src/inbound/inbound-display.d.ts +41 -0
- package/dist/src/inbound/inbound-display.d.ts.map +1 -0
- package/dist/src/inbound/inbound-display.js +170 -0
- package/dist/src/inbound/inbound-display.js.map +1 -0
- package/dist/src/inbound/reply-dispatch-driver.d.ts +89 -0
- package/dist/src/inbound/reply-dispatch-driver.d.ts.map +1 -0
- package/dist/src/inbound/reply-dispatch-driver.js +294 -0
- package/dist/src/inbound/reply-dispatch-driver.js.map +1 -0
- package/dist/src/inbound/reply-dispatch-driver.test.d.ts +2 -0
- package/dist/src/inbound/reply-dispatch-driver.test.d.ts.map +1 -0
- package/dist/src/inbound/reply-dispatch-driver.test.js +363 -0
- package/dist/src/inbound/reply-dispatch-driver.test.js.map +1 -0
- package/dist/src/inbound/resolve-inbound-group-prompt.d.ts +37 -0
- package/dist/src/inbound/resolve-inbound-group-prompt.d.ts.map +1 -0
- package/dist/src/inbound/resolve-inbound-group-prompt.js +57 -0
- package/dist/src/inbound/resolve-inbound-group-prompt.js.map +1 -0
- package/dist/src/plan/action-run-event.d.ts +20 -0
- package/dist/src/plan/action-run-event.d.ts.map +1 -0
- package/dist/src/plan/action-run-event.js +62 -0
- package/dist/src/plan/action-run-event.js.map +1 -0
- package/dist/src/plan/action-run-event.test.d.ts +2 -0
- package/dist/src/plan/action-run-event.test.d.ts.map +1 -0
- package/dist/src/plan/action-run-event.test.js +83 -0
- package/dist/src/plan/action-run-event.test.js.map +1 -0
- package/dist/src/plan/action-store-access.d.ts +29 -0
- package/dist/src/plan/action-store-access.d.ts.map +1 -0
- package/dist/src/plan/action-store-access.js +81 -0
- package/dist/src/plan/action-store-access.js.map +1 -0
- package/dist/src/plan/action-store-lifecycle.test.d.ts +2 -0
- package/dist/src/plan/action-store-lifecycle.test.d.ts.map +1 -0
- package/dist/src/plan/action-store-lifecycle.test.js +195 -0
- package/dist/src/plan/action-store-lifecycle.test.js.map +1 -0
- package/dist/src/plan/plan-core-adapter.d.ts +54 -0
- package/dist/src/plan/plan-core-adapter.d.ts.map +1 -0
- package/dist/src/plan/plan-core-adapter.js +104 -0
- package/dist/src/plan/plan-core-adapter.js.map +1 -0
- package/dist/src/plan/plan-core-adapter.test.d.ts +2 -0
- package/dist/src/plan/plan-core-adapter.test.d.ts.map +1 -0
- package/dist/src/plan/plan-core-adapter.test.js +183 -0
- package/dist/src/plan/plan-core-adapter.test.js.map +1 -0
- package/dist/src/plan/plan-core-db.d.ts +22 -0
- package/dist/src/plan/plan-core-db.d.ts.map +1 -0
- package/dist/src/plan/plan-core-db.js +91 -0
- package/dist/src/plan/plan-core-db.js.map +1 -0
- package/dist/src/plan/plan-core-db.test.d.ts +2 -0
- package/dist/src/plan/plan-core-db.test.d.ts.map +1 -0
- package/dist/src/plan/plan-core-db.test.js +115 -0
- package/dist/src/plan/plan-core-db.test.js.map +1 -0
- package/dist/src/plan/plan-core-snapshot.d.ts +23 -0
- package/dist/src/plan/plan-core-snapshot.d.ts.map +1 -0
- package/dist/src/plan/plan-core-snapshot.js +66 -0
- package/dist/src/plan/plan-core-snapshot.js.map +1 -0
- package/dist/src/plan/plan-core-snapshot.test.d.ts +2 -0
- package/dist/src/plan/plan-core-snapshot.test.d.ts.map +1 -0
- package/dist/src/plan/plan-core-snapshot.test.js +157 -0
- package/dist/src/plan/plan-core-snapshot.test.js.map +1 -0
- package/dist/src/plan/plan-stat-event.d.ts +17 -0
- package/dist/src/plan/plan-stat-event.d.ts.map +1 -0
- package/dist/src/plan/plan-stat-event.js +41 -0
- package/dist/src/plan/plan-stat-event.js.map +1 -0
- package/dist/src/plan/plan-stat-event.test.d.ts +2 -0
- package/dist/src/plan/plan-stat-event.test.d.ts.map +1 -0
- package/dist/src/plan/plan-stat-event.test.js +131 -0
- package/dist/src/plan/plan-stat-event.test.js.map +1 -0
- package/dist/src/plan/plan-tool-middleware.d.ts +19 -0
- package/dist/src/plan/plan-tool-middleware.d.ts.map +1 -0
- package/dist/src/plan/plan-tool-middleware.js +63 -0
- package/dist/src/plan/plan-tool-middleware.js.map +1 -0
- package/dist/src/plan/plan-tool-middleware.test.d.ts +2 -0
- package/dist/src/plan/plan-tool-middleware.test.d.ts.map +1 -0
- package/dist/src/plan/plan-tool-middleware.test.js +103 -0
- package/dist/src/plan/plan-tool-middleware.test.js.map +1 -0
- package/dist/src/plan/plan-ws-registry.d.ts +15 -0
- package/dist/src/plan/plan-ws-registry.d.ts.map +1 -0
- package/dist/src/plan/plan-ws-registry.js +39 -0
- package/dist/src/plan/plan-ws-registry.js.map +1 -0
- package/dist/src/plan/plan-ws-registry.test.d.ts +2 -0
- package/dist/src/plan/plan-ws-registry.test.d.ts.map +1 -0
- package/dist/src/plan/plan-ws-registry.test.js +61 -0
- package/dist/src/plan/plan-ws-registry.test.js.map +1 -0
- package/dist/src/plan/tool-event-adapter.d.ts +31 -0
- package/dist/src/plan/tool-event-adapter.d.ts.map +1 -0
- package/dist/src/plan/tool-event-adapter.js +83 -0
- package/dist/src/plan/tool-event-adapter.js.map +1 -0
- package/dist/src/plan/tool-event-adapter.test.d.ts +2 -0
- package/dist/src/plan/tool-event-adapter.test.d.ts.map +1 -0
- package/dist/src/plan/tool-event-adapter.test.js +93 -0
- package/dist/src/plan/tool-event-adapter.test.js.map +1 -0
- package/dist/src/plugin/auth.d.ts +32 -0
- package/dist/src/plugin/auth.d.ts.map +1 -0
- package/dist/src/plugin/auth.js +70 -0
- package/dist/src/plugin/auth.js.map +1 -0
- package/dist/src/plugin/channel-config.d.ts +716 -0
- package/dist/src/plugin/channel-config.d.ts.map +1 -0
- package/dist/src/plugin/channel-config.js +405 -0
- package/dist/src/plugin/channel-config.js.map +1 -0
- package/dist/src/plugin/channel-config.test.d.ts +2 -0
- package/dist/src/plugin/channel-config.test.d.ts.map +1 -0
- package/dist/src/plugin/channel-config.test.js +113 -0
- package/dist/src/plugin/channel-config.test.js.map +1 -0
- package/dist/src/plugin/channel-disabled-account.test.d.ts +2 -0
- package/dist/src/plugin/channel-disabled-account.test.d.ts.map +1 -0
- package/dist/src/plugin/channel-disabled-account.test.js +44 -0
- package/dist/src/plugin/channel-disabled-account.test.js.map +1 -0
- package/dist/src/plugin/channel-log.d.ts +17 -0
- package/dist/src/plugin/channel-log.d.ts.map +1 -0
- package/dist/src/plugin/channel-log.js +45 -0
- package/dist/src/plugin/channel-log.js.map +1 -0
- package/dist/src/plugin/channel.d.ts +17 -0
- package/dist/src/plugin/channel.d.ts.map +1 -0
- package/dist/src/plugin/channel.js +272 -0
- package/dist/src/plugin/channel.js.map +1 -0
- package/dist/src/plugin/plugin-dispose.d.ts +22 -0
- package/dist/src/plugin/plugin-dispose.d.ts.map +1 -0
- package/dist/src/plugin/plugin-dispose.js +66 -0
- package/dist/src/plugin/plugin-dispose.js.map +1 -0
- package/dist/src/plugin/process-shared-store.d.ts +48 -0
- package/dist/src/plugin/process-shared-store.d.ts.map +1 -0
- package/dist/src/plugin/process-shared-store.js +89 -0
- package/dist/src/plugin/process-shared-store.js.map +1 -0
- package/dist/src/plugin/process-shared-store.test.d.ts +2 -0
- package/dist/src/plugin/process-shared-store.test.d.ts.map +1 -0
- package/dist/src/plugin/process-shared-store.test.js +60 -0
- package/dist/src/plugin/process-shared-store.test.js.map +1 -0
- package/dist/src/plugin/recommended-system-prompt.d.ts +3 -0
- package/dist/src/plugin/recommended-system-prompt.d.ts.map +1 -0
- package/dist/src/plugin/recommended-system-prompt.js +8 -0
- package/dist/src/plugin/recommended-system-prompt.js.map +1 -0
- package/dist/src/plugin/types.d.ts +433 -0
- package/dist/src/plugin/types.d.ts.map +1 -0
- package/dist/src/plugin/types.js +32 -0
- package/dist/src/plugin/types.js.map +1 -0
- package/dist/src/session/agent-context-types.d.ts +241 -0
- package/dist/src/session/agent-context-types.d.ts.map +1 -0
- package/dist/src/session/agent-context-types.js +142 -0
- package/dist/src/session/agent-context-types.js.map +1 -0
- package/dist/src/session/agent-context-types.test.d.ts +2 -0
- package/dist/src/session/agent-context-types.test.d.ts.map +1 -0
- package/dist/src/session/agent-context-types.test.js +140 -0
- package/dist/src/session/agent-context-types.test.js.map +1 -0
- package/dist/src/session/auto-model-router.d.ts +36 -0
- package/dist/src/session/auto-model-router.d.ts.map +1 -0
- package/dist/src/session/auto-model-router.js +60 -0
- package/dist/src/session/auto-model-router.js.map +1 -0
- package/dist/src/session/auto-model-router.test.d.ts +2 -0
- package/dist/src/session/auto-model-router.test.d.ts.map +1 -0
- package/dist/src/session/auto-model-router.test.js +99 -0
- package/dist/src/session/auto-model-router.test.js.map +1 -0
- package/dist/src/session/build-project-prompt.d.ts +40 -0
- package/dist/src/session/build-project-prompt.d.ts.map +1 -0
- package/dist/src/session/build-project-prompt.js +190 -0
- package/dist/src/session/build-project-prompt.js.map +1 -0
- package/dist/src/session/build-project-prompt.test.d.ts +2 -0
- package/dist/src/session/build-project-prompt.test.d.ts.map +1 -0
- package/dist/src/session/build-project-prompt.test.js +100 -0
- package/dist/src/session/build-project-prompt.test.js.map +1 -0
- package/dist/src/session/inbound-model.d.ts +39 -0
- package/dist/src/session/inbound-model.d.ts.map +1 -0
- package/dist/src/session/inbound-model.js +66 -0
- package/dist/src/session/inbound-model.js.map +1 -0
- package/dist/src/session/inbound-model.test.d.ts +2 -0
- package/dist/src/session/inbound-model.test.d.ts.map +1 -0
- package/dist/src/session/inbound-model.test.js +88 -0
- package/dist/src/session/inbound-model.test.js.map +1 -0
- package/dist/src/session/per-message-fallback-notice.d.ts +43 -0
- package/dist/src/session/per-message-fallback-notice.d.ts.map +1 -0
- package/dist/src/session/per-message-fallback-notice.js +93 -0
- package/dist/src/session/per-message-fallback-notice.js.map +1 -0
- package/dist/src/session/per-message-fallback-notice.test.d.ts +2 -0
- package/dist/src/session/per-message-fallback-notice.test.d.ts.map +1 -0
- package/dist/src/session/per-message-fallback-notice.test.js +47 -0
- package/dist/src/session/per-message-fallback-notice.test.js.map +1 -0
- package/dist/src/session/per-message-model-pending.d.ts +52 -0
- package/dist/src/session/per-message-model-pending.d.ts.map +1 -0
- package/dist/src/session/per-message-model-pending.js +134 -0
- package/dist/src/session/per-message-model-pending.js.map +1 -0
- package/dist/src/session/register-group-prompt-hook.d.ts +17 -0
- package/dist/src/session/register-group-prompt-hook.d.ts.map +1 -0
- package/dist/src/session/register-group-prompt-hook.js +45 -0
- package/dist/src/session/register-group-prompt-hook.js.map +1 -0
- package/dist/src/session/register-group-prompt-hook.test.d.ts +2 -0
- package/dist/src/session/register-group-prompt-hook.test.d.ts.map +1 -0
- package/dist/src/session/register-group-prompt-hook.test.js +60 -0
- package/dist/src/session/register-group-prompt-hook.test.js.map +1 -0
- package/dist/src/session/register-per-message-model-hook.d.ts +14 -0
- package/dist/src/session/register-per-message-model-hook.d.ts.map +1 -0
- package/dist/src/session/register-per-message-model-hook.js +48 -0
- package/dist/src/session/register-per-message-model-hook.js.map +1 -0
- package/dist/src/session/register-per-message-model-hook.test.d.ts +2 -0
- package/dist/src/session/register-per-message-model-hook.test.d.ts.map +1 -0
- package/dist/src/session/register-per-message-model-hook.test.js +208 -0
- package/dist/src/session/register-per-message-model-hook.test.js.map +1 -0
- package/dist/src/session/register-run-model-context-hook.d.ts +11 -0
- package/dist/src/session/register-run-model-context-hook.d.ts.map +1 -0
- package/dist/src/session/register-run-model-context-hook.js +23 -0
- package/dist/src/session/register-run-model-context-hook.js.map +1 -0
- package/dist/src/session/run-model-context.d.ts +45 -0
- package/dist/src/session/run-model-context.d.ts.map +1 -0
- package/dist/src/session/run-model-context.js +108 -0
- package/dist/src/session/run-model-context.js.map +1 -0
- package/dist/src/session/run-model-context.test.d.ts +2 -0
- package/dist/src/session/run-model-context.test.d.ts.map +1 -0
- package/dist/src/session/run-model-context.test.js +68 -0
- package/dist/src/session/run-model-context.test.js.map +1 -0
- package/dist/src/session/session-env-store.d.ts +55 -0
- package/dist/src/session/session-env-store.d.ts.map +1 -0
- package/dist/src/session/session-env-store.js +328 -0
- package/dist/src/session/session-env-store.js.map +1 -0
- package/dist/src/session/session-env-store.test.d.ts +2 -0
- package/dist/src/session/session-env-store.test.d.ts.map +1 -0
- package/dist/src/session/session-env-store.test.js +151 -0
- package/dist/src/session/session-env-store.test.js.map +1 -0
- package/dist/src/session/session-group-prompt.d.ts +34 -0
- package/dist/src/session/session-group-prompt.d.ts.map +1 -0
- package/dist/src/session/session-group-prompt.js +101 -0
- package/dist/src/session/session-group-prompt.js.map +1 -0
- package/dist/src/session/session-group-prompt.test.d.ts +2 -0
- package/dist/src/session/session-group-prompt.test.d.ts.map +1 -0
- package/dist/src/session/session-group-prompt.test.js +72 -0
- package/dist/src/session/session-group-prompt.test.js.map +1 -0
- package/dist/src/session/session-im-context.d.ts +15 -0
- package/dist/src/session/session-im-context.d.ts.map +1 -0
- package/dist/src/session/session-im-context.js +54 -0
- package/dist/src/session/session-im-context.js.map +1 -0
- package/dist/src/session/session-model-fallback.d.ts +13 -0
- package/dist/src/session/session-model-fallback.d.ts.map +1 -0
- package/dist/src/session/session-model-fallback.js +24 -0
- package/dist/src/session/session-model-fallback.js.map +1 -0
- package/dist/src/tools/active-cron-context-store.d.ts +20 -0
- package/dist/src/tools/active-cron-context-store.d.ts.map +1 -0
- package/dist/src/tools/active-cron-context-store.js +73 -0
- package/dist/src/tools/active-cron-context-store.js.map +1 -0
- package/dist/src/tools/cron-appkey-binding-store.d.ts +29 -0
- package/dist/src/tools/cron-appkey-binding-store.d.ts.map +1 -0
- package/dist/src/tools/cron-appkey-binding-store.js +239 -0
- package/dist/src/tools/cron-appkey-binding-store.js.map +1 -0
- package/dist/src/tools/cron-appkey-binding-store.test.d.ts +2 -0
- package/dist/src/tools/cron-appkey-binding-store.test.d.ts.map +1 -0
- package/dist/src/tools/cron-appkey-binding-store.test.js +387 -0
- package/dist/src/tools/cron-appkey-binding-store.test.js.map +1 -0
- package/dist/src/tools/group-history-tool.d.ts +31 -0
- package/dist/src/tools/group-history-tool.d.ts.map +1 -0
- package/dist/src/tools/group-history-tool.js +177 -0
- package/dist/src/tools/group-history-tool.js.map +1 -0
- package/dist/src/tools/register-exec-env-hook.d.ts +44 -0
- package/dist/src/tools/register-exec-env-hook.d.ts.map +1 -0
- package/dist/src/tools/register-exec-env-hook.js +473 -0
- package/dist/src/tools/register-exec-env-hook.js.map +1 -0
- package/dist/src/tools/register-exec-env-hook.test.d.ts +2 -0
- package/dist/src/tools/register-exec-env-hook.test.d.ts.map +1 -0
- package/dist/src/tools/register-exec-env-hook.test.js +189 -0
- package/dist/src/tools/register-exec-env-hook.test.js.map +1 -0
- package/dist/src/tools/send-file-tool.d.ts +81 -0
- package/dist/src/tools/send-file-tool.d.ts.map +1 -0
- package/dist/src/tools/send-file-tool.js +363 -0
- package/dist/src/tools/send-file-tool.js.map +1 -0
- package/dist/src/tools/send-file-tool.test.d.ts +2 -0
- package/dist/src/tools/send-file-tool.test.d.ts.map +1 -0
- package/dist/src/tools/send-file-tool.test.js +176 -0
- package/dist/src/tools/send-file-tool.test.js.map +1 -0
- package/dist/src/tools/send-group-message-tool.d.ts +17 -0
- package/dist/src/tools/send-group-message-tool.d.ts.map +1 -0
- package/dist/src/tools/send-group-message-tool.js +82 -0
- package/dist/src/tools/send-group-message-tool.js.map +1 -0
- package/dist/src/tools/tool-json-result.d.ts +14 -0
- package/dist/src/tools/tool-json-result.d.ts.map +1 -0
- package/dist/src/tools/tool-json-result.js +20 -0
- package/dist/src/tools/tool-json-result.js.map +1 -0
- package/dist/src/turn/active-round-registry.d.ts +35 -0
- package/dist/src/turn/active-round-registry.d.ts.map +1 -0
- package/dist/src/turn/active-round-registry.js +85 -0
- package/dist/src/turn/active-round-registry.js.map +1 -0
- package/dist/src/turn/active-round-registry.test.d.ts +2 -0
- package/dist/src/turn/active-round-registry.test.d.ts.map +1 -0
- package/dist/src/turn/active-round-registry.test.js +62 -0
- package/dist/src/turn/active-round-registry.test.js.map +1 -0
- package/dist/src/turn/interrupt-round.d.ts +9 -0
- package/dist/src/turn/interrupt-round.d.ts.map +1 -0
- package/dist/src/turn/interrupt-round.js +44 -0
- package/dist/src/turn/interrupt-round.js.map +1 -0
- package/dist/src/turn/interrupt-round.test.d.ts +2 -0
- package/dist/src/turn/interrupt-round.test.d.ts.map +1 -0
- package/dist/src/turn/interrupt-round.test.js +133 -0
- package/dist/src/turn/interrupt-round.test.js.map +1 -0
- package/dist/src/turn/item-event-guard.d.ts +28 -0
- package/dist/src/turn/item-event-guard.d.ts.map +1 -0
- package/dist/src/turn/item-event-guard.js +22 -0
- package/dist/src/turn/item-event-guard.js.map +1 -0
- package/dist/src/turn/item-event-guard.test.d.ts +2 -0
- package/dist/src/turn/item-event-guard.test.d.ts.map +1 -0
- package/dist/src/turn/item-event-guard.test.js +55 -0
- package/dist/src/turn/item-event-guard.test.js.map +1 -0
- package/dist/src/turn/run-usage-session.d.ts +13 -0
- package/dist/src/turn/run-usage-session.d.ts.map +1 -0
- package/dist/src/turn/run-usage-session.js +41 -0
- package/dist/src/turn/run-usage-session.js.map +1 -0
- package/dist/src/turn/run-usage.d.ts +67 -0
- package/dist/src/turn/run-usage.d.ts.map +1 -0
- package/dist/src/turn/run-usage.js +119 -0
- package/dist/src/turn/run-usage.js.map +1 -0
- package/dist/src/turn/run-usage.test.d.ts +2 -0
- package/dist/src/turn/run-usage.test.d.ts.map +1 -0
- package/dist/src/turn/run-usage.test.js +178 -0
- package/dist/src/turn/run-usage.test.js.map +1 -0
- package/dist/src/turn/subagent-monitor.d.ts +45 -0
- package/dist/src/turn/subagent-monitor.d.ts.map +1 -0
- package/dist/src/turn/subagent-monitor.js +498 -0
- package/dist/src/turn/subagent-monitor.js.map +1 -0
- package/dist/src/turn/subagent-monitor.test.d.ts +2 -0
- package/dist/src/turn/subagent-monitor.test.d.ts.map +1 -0
- package/dist/src/turn/subagent-monitor.test.js +335 -0
- package/dist/src/turn/subagent-monitor.test.js.map +1 -0
- package/dist/src/turn/turn-agent-event-subscription.d.ts +29 -0
- package/dist/src/turn/turn-agent-event-subscription.d.ts.map +1 -0
- package/dist/src/turn/turn-agent-event-subscription.js +350 -0
- package/dist/src/turn/turn-agent-event-subscription.js.map +1 -0
- package/dist/src/turn/turn-agent-event-subscription.test.d.ts +2 -0
- package/dist/src/turn/turn-agent-event-subscription.test.d.ts.map +1 -0
- package/dist/src/turn/turn-agent-event-subscription.test.js +257 -0
- package/dist/src/turn/turn-agent-event-subscription.test.js.map +1 -0
- package/dist/src/turn/turn-end-controller.d.ts +61 -0
- package/dist/src/turn/turn-end-controller.d.ts.map +1 -0
- package/dist/src/turn/turn-end-controller.js +239 -0
- package/dist/src/turn/turn-end-controller.js.map +1 -0
- package/dist/src/turn/turn-end-controller.test.d.ts +2 -0
- package/dist/src/turn/turn-end-controller.test.d.ts.map +1 -0
- package/dist/src/turn/turn-end-controller.test.js +97 -0
- package/dist/src/turn/turn-end-controller.test.js.map +1 -0
- package/dist/src/turn/ws-run-event-unmapped.d.ts +10 -0
- package/dist/src/turn/ws-run-event-unmapped.d.ts.map +1 -0
- package/dist/src/turn/ws-run-event-unmapped.js +16 -0
- package/dist/src/turn/ws-run-event-unmapped.js.map +1 -0
- package/dist/src/turn/ws-run-event-unmapped.test.d.ts +2 -0
- package/dist/src/turn/ws-run-event-unmapped.test.d.ts.map +1 -0
- package/dist/src/turn/ws-run-event-unmapped.test.js +15 -0
- package/dist/src/turn/ws-run-event-unmapped.test.js.map +1 -0
- package/dist/src/workspace/apply-sync-mapping.d.ts +15 -0
- package/dist/src/workspace/apply-sync-mapping.d.ts.map +1 -0
- package/dist/src/workspace/apply-sync-mapping.js +98 -0
- package/dist/src/workspace/apply-sync-mapping.js.map +1 -0
- package/dist/src/workspace/connection-workspace-event.test.d.ts +2 -0
- package/dist/src/workspace/connection-workspace-event.test.d.ts.map +1 -0
- package/dist/src/workspace/connection-workspace-event.test.js +74 -0
- package/dist/src/workspace/connection-workspace-event.test.js.map +1 -0
- package/dist/src/workspace/inbound-file-download.d.ts +15 -0
- package/dist/src/workspace/inbound-file-download.d.ts.map +1 -0
- package/dist/src/workspace/inbound-file-download.js +48 -0
- package/dist/src/workspace/inbound-file-download.js.map +1 -0
- package/dist/src/workspace/inbound-input-media.d.ts +43 -0
- package/dist/src/workspace/inbound-input-media.d.ts.map +1 -0
- package/dist/src/workspace/inbound-input-media.js +230 -0
- package/dist/src/workspace/inbound-input-media.js.map +1 -0
- package/dist/src/workspace/inbound-media-local.d.ts +49 -0
- package/dist/src/workspace/inbound-media-local.d.ts.map +1 -0
- package/dist/src/workspace/inbound-media-local.js +194 -0
- package/dist/src/workspace/inbound-media-local.js.map +1 -0
- package/dist/src/workspace/nas-site.d.ts +16 -0
- package/dist/src/workspace/nas-site.d.ts.map +1 -0
- package/dist/src/workspace/nas-site.js +17 -0
- package/dist/src/workspace/nas-site.js.map +1 -0
- package/dist/src/workspace/nas-volume-fs-sync.d.ts +37 -0
- package/dist/src/workspace/nas-volume-fs-sync.d.ts.map +1 -0
- package/dist/src/workspace/nas-volume-fs-sync.js +156 -0
- package/dist/src/workspace/nas-volume-fs-sync.js.map +1 -0
- package/dist/src/workspace/nas-volume-fs-sync.test.d.ts +2 -0
- package/dist/src/workspace/nas-volume-fs-sync.test.d.ts.map +1 -0
- package/dist/src/workspace/nas-volume-fs-sync.test.js +49 -0
- package/dist/src/workspace/nas-volume-fs-sync.test.js.map +1 -0
- package/dist/src/workspace/nas-volumes.d.ts +73 -0
- package/dist/src/workspace/nas-volumes.d.ts.map +1 -0
- package/dist/src/workspace/nas-volumes.js +546 -0
- package/dist/src/workspace/nas-volumes.js.map +1 -0
- package/dist/src/workspace/nas-volumes.test.d.ts +2 -0
- package/dist/src/workspace/nas-volumes.test.d.ts.map +1 -0
- package/dist/src/workspace/nas-volumes.test.js +386 -0
- package/dist/src/workspace/nas-volumes.test.js.map +1 -0
- package/dist/src/workspace/project-path-normalize.d.ts +30 -0
- package/dist/src/workspace/project-path-normalize.d.ts.map +1 -0
- package/dist/src/workspace/project-path-normalize.js +101 -0
- package/dist/src/workspace/project-path-normalize.js.map +1 -0
- package/dist/src/workspace/resource-file.d.ts +119 -0
- package/dist/src/workspace/resource-file.d.ts.map +1 -0
- package/dist/src/workspace/resource-file.js +533 -0
- package/dist/src/workspace/resource-file.js.map +1 -0
- package/dist/src/workspace/resource-file.test.d.ts +2 -0
- package/dist/src/workspace/resource-file.test.d.ts.map +1 -0
- package/dist/src/workspace/resource-file.test.js +64 -0
- package/dist/src/workspace/resource-file.test.js.map +1 -0
- package/dist/src/workspace/sync-mapping-types.d.ts +220 -0
- package/dist/src/workspace/sync-mapping-types.d.ts.map +1 -0
- package/dist/src/workspace/sync-mapping-types.js +55 -0
- package/dist/src/workspace/sync-mapping-types.js.map +1 -0
- package/dist/src/workspace/sync-service-client.d.ts +29 -0
- package/dist/src/workspace/sync-service-client.d.ts.map +1 -0
- package/dist/src/workspace/sync-service-client.js +72 -0
- package/dist/src/workspace/sync-service-client.js.map +1 -0
- package/dist/src/workspace/workspace-event-protocol.d.ts +3 -0
- package/dist/src/workspace/workspace-event-protocol.d.ts.map +1 -0
- package/dist/src/workspace/workspace-event-protocol.js +3 -0
- package/dist/src/workspace/workspace-event-protocol.js.map +1 -0
- package/dist/src/workspace/workspace-event-scheduler.d.ts +54 -0
- package/dist/src/workspace/workspace-event-scheduler.d.ts.map +1 -0
- package/dist/src/workspace/workspace-event-scheduler.js +131 -0
- package/dist/src/workspace/workspace-event-scheduler.js.map +1 -0
- package/dist/src/workspace/workspace-event-scheduler.test.d.ts +2 -0
- package/dist/src/workspace/workspace-event-scheduler.test.d.ts.map +1 -0
- package/dist/src/workspace/workspace-event-scheduler.test.js +157 -0
- package/dist/src/workspace/workspace-event-scheduler.test.js.map +1 -0
- package/dist/src/workspace/workspace-event.d.ts +28 -0
- package/dist/src/workspace/workspace-event.d.ts.map +1 -0
- package/dist/src/workspace/workspace-event.js +674 -0
- package/dist/src/workspace/workspace-event.js.map +1 -0
- package/dist/src/workspace/workspace-event.test.d.ts +2 -0
- package/dist/src/workspace/workspace-event.test.d.ts.map +1 -0
- package/dist/src/workspace/workspace-event.test.js +265 -0
- package/dist/src/workspace/workspace-event.test.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ 触发的单轮回复:WebSocket BEGIN、OpenClaw dispatchReplyWithBufferedBlockDispatcher、HTTP deliver、RUN_USAGE、FINISHED。
|
|
3
|
+
*/
|
|
4
|
+
import { isImSingleChat, resolveSendMessageReply } from "./inbound-chat-type.js";
|
|
5
|
+
import { buildTerminalSendFn, InboundTerminalController, } from "../delivery/inbound-terminal-delivery.js";
|
|
6
|
+
import { formatConfirmedUpstreamErrorForIm } from "../delivery/user-facing-errors.js";
|
|
7
|
+
import { shouldOmitDuplicateFileCaption, shouldSuppressDuplicateTextDeliver, stripMediaDirectiveLines, stripModelUsageFooter, } from "../delivery/deliver-text.js";
|
|
8
|
+
import { shouldSuppressFallbackNoticeForIm } from "../session/per-message-fallback-notice.js";
|
|
9
|
+
import { resolveThinkingWsDisplayUpdate, THINKING_WS_SUMMARY_HINT, ThinkingWsBuffer, WS_THINKING_DISPLAY_MAX, } from "../delivery/thinking-ws-progress.js";
|
|
10
|
+
import { resolveItemEventGuard } from "../turn/item-event-guard.js";
|
|
11
|
+
import { clearPendingPerMessageModel, setPendingPerMessageModel, splitModelRef, } from "../session/per-message-model-pending.js";
|
|
12
|
+
import { resolveSessionModelFallbackContext } from "../session/session-model-fallback.js";
|
|
13
|
+
import { IM_BLOCK_STREAMING_COALESCE_DEFAULT, isDeliverAssistantItemsEnabled, isDeliverPartialSnapshotEnabled, isDeliverSegmentBufferEnabled, isInboundBlockStreamingEnabled, resolveBlockStreamingReplyOptions, resolveDispatchOpenClawCfg, } from "../plugin/channel-config.js";
|
|
14
|
+
import { clearAssistantItemSink, setAssistantItemSink } from "../delivery/assistant-item-sink.js";
|
|
15
|
+
import { flushAssistantItem, resetAssistantItem } from "../delivery/assistant-item-delivery.js";
|
|
16
|
+
import { sendReplyDeliverBlock, sendTextMessage } from "../delivery/send-service.js";
|
|
17
|
+
import { isDeliverPayloadSendable, snapshotSupersedesBufferText, TurnDeliverBuffer } from "../delivery/turn-deliver-buffer.js";
|
|
18
|
+
import { buildRunUsageFromSessionEntry, hasReportableUsage, toImStreamRunUsageData } from "../turn/run-usage.js";
|
|
19
|
+
import { readSessionUsageFields } from "../turn/run-usage-session.js";
|
|
20
|
+
import { finalizeRoundMonitoring } from "../turn/subagent-monitor.js";
|
|
21
|
+
import { registerCorePlanSnapshotClient } from "../plan/plan-core-snapshot.js";
|
|
22
|
+
import { setSessionImContext } from "../session/session-im-context.js";
|
|
23
|
+
import { completeActiveRound, registerActiveRound, roundExists, } from "../turn/active-round-registry.js";
|
|
24
|
+
import { turnEnsureRound, turnForget, turnIsEligible, turnIsSawSubagent, turnOnDispatchReturned, } from "../turn/turn-end-controller.js";
|
|
25
|
+
import { noteWrappedRunId } from "../turn/turn-agent-event-subscription.js";
|
|
26
|
+
import { resolveNasVolumeDispatchConfig } from "../workspace/nas-volumes.js";
|
|
27
|
+
import { resolveDispatchDriver, dispatchXgImInboundReply } from "./reply-dispatch-driver.js";
|
|
28
|
+
/** WS `displayText` 过长封顶(避免网关/客户端溢出) */
|
|
29
|
+
const WS_DISPLAY_TEXT_MAX = 200;
|
|
30
|
+
/** 子任务轮次等整轮结束(等父会话非 yield 终态 / 静默兜底)的最长等待时间;超时强制收尾,避免悬挂。 */
|
|
31
|
+
const TURN_END_WAIT_MAX_MS = 30 * 60_000;
|
|
32
|
+
/** 进度 `summary` 封顶(与网关约定一致) */
|
|
33
|
+
const WS_PROGRESS_SUMMARY_MAX = 200;
|
|
34
|
+
/** 流式正文预览(onPartialReply 快照)经 WS 展示的封顶长度 */
|
|
35
|
+
const WS_REPLY_PREVIEW_MAX = 2000;
|
|
36
|
+
/** 流式正文预览 WS 推送最小间隔(节流,避免 ~13 次/秒刷屏) */
|
|
37
|
+
const WS_REPLY_PREVIEW_MIN_INTERVAL_MS = 500;
|
|
38
|
+
/** 流式正文预览的 WS summary 标记(客户端据此更新同一输出位;定稿 HTTP 后清草稿) */
|
|
39
|
+
const WS_REPLY_PREVIEW_SUMMARY = "reply_preview";
|
|
40
|
+
function clipWsDisplayText(text, max = WS_DISPLAY_TEXT_MAX) {
|
|
41
|
+
const t = text.trim();
|
|
42
|
+
if (!t)
|
|
43
|
+
return "";
|
|
44
|
+
if (t.length <= max)
|
|
45
|
+
return t;
|
|
46
|
+
return `${t.slice(0, Math.max(0, max - 1))}…`;
|
|
47
|
+
}
|
|
48
|
+
function clipWsProgressSummary(text, max = WS_PROGRESS_SUMMARY_MAX) {
|
|
49
|
+
const t = text.trim().replace(/\r\n/g, "\n").replace(/\n+/g, " ");
|
|
50
|
+
if (!t)
|
|
51
|
+
return "";
|
|
52
|
+
if (t.length <= max)
|
|
53
|
+
return t;
|
|
54
|
+
return `${t.slice(0, Math.max(0, max - 1))}…`;
|
|
55
|
+
}
|
|
56
|
+
/** 流式 BEGIN → dispatch → 可选 RUN_USAGE → 必发 FINISHED;首包超时仅日志(不对 IM 发超时话术) */
|
|
57
|
+
export async function dispatchMentionedReply(args) {
|
|
58
|
+
const { accountId, rt, cfg, config, log, logPrefix, route, inboundCtx, params, currentIdentity, streamClient, agentWorkspaceDir, nasVolumes, modelOverride, thinkingLevelOverride, autoRouteMeta, inboundTrigger, isStopControl = false, driver: explicitDriver, inboundRecord } = args;
|
|
59
|
+
const terminal = new InboundTerminalController({
|
|
60
|
+
messageId: params.msgId,
|
|
61
|
+
groupId: params.groupId,
|
|
62
|
+
sessionKey: route.sessionKey,
|
|
63
|
+
trigger: inboundTrigger ?? "user",
|
|
64
|
+
log,
|
|
65
|
+
logPrefix,
|
|
66
|
+
});
|
|
67
|
+
const replyMetaForSend = resolveSendMessageReply(params);
|
|
68
|
+
const terminalSend = buildTerminalSendFn({
|
|
69
|
+
isSingleChat: isImSingleChat(params),
|
|
70
|
+
replyMeta: replyMetaForSend,
|
|
71
|
+
sendText: async (text, reply) => {
|
|
72
|
+
await sendTextMessage(config, currentIdentity.token, params.groupId, text, reply ? [reply.targetUserId] : [], log, undefined, reply);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
log.info(`${logPrefix} [dispatch] Dispatching to OpenClaw AI, sessionKey=${route.sessionKey}`);
|
|
76
|
+
if (modelOverride) {
|
|
77
|
+
log.info(`${logPrefix} [model] per-message override requested: ${modelOverride}`);
|
|
78
|
+
}
|
|
79
|
+
if (autoRouteMeta) {
|
|
80
|
+
log.info(`${logPrefix} [route] dispatch version=${autoRouteMeta.version} tier=${autoRouteMeta.tier} ` +
|
|
81
|
+
`reason=${autoRouteMeta.reason} requestedModel=${modelOverride ?? "(none)"} ` +
|
|
82
|
+
`requestedThinking=${thinkingLevelOverride ?? "(default)"}`);
|
|
83
|
+
}
|
|
84
|
+
let isFirstReply = true;
|
|
85
|
+
let subagentNotificationSent = false;
|
|
86
|
+
const dispatchStart = Date.now();
|
|
87
|
+
const replyAnchorId = params.msgId.trim();
|
|
88
|
+
const progressGroupId = params.groupId;
|
|
89
|
+
const toolNameStack = [];
|
|
90
|
+
const notifyStreamProgress = async (event, displayText, summary, displayMax = WS_DISPLAY_TEXT_MAX) => {
|
|
91
|
+
if (isStopControl)
|
|
92
|
+
return;
|
|
93
|
+
const msg = clipWsDisplayText(displayText, displayMax);
|
|
94
|
+
const sum = summary?.trim() ? clipWsProgressSummary(summary) : undefined;
|
|
95
|
+
if (!progressGroupId.trim() || !replyAnchorId || !msg)
|
|
96
|
+
return;
|
|
97
|
+
try {
|
|
98
|
+
await streamClient.streamProgress({
|
|
99
|
+
groupId: progressGroupId,
|
|
100
|
+
replyToMsgId: replyAnchorId,
|
|
101
|
+
event,
|
|
102
|
+
displayText: msg,
|
|
103
|
+
...(sum ? { summary: sum } : {}),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
log.warn(`${logPrefix} [stream] ${event} notify failed: ${String(e)}`);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const thinkingWsBuffer = new ThinkingWsBuffer();
|
|
111
|
+
let lastThinkingWsDisplay = "";
|
|
112
|
+
const flushThinkingWsOnce = async (reason) => {
|
|
113
|
+
const text = thinkingWsBuffer.take();
|
|
114
|
+
if (!text)
|
|
115
|
+
return;
|
|
116
|
+
const { displayText, changed } = resolveThinkingWsDisplayUpdate(lastThinkingWsDisplay, text);
|
|
117
|
+
if (!changed)
|
|
118
|
+
return;
|
|
119
|
+
lastThinkingWsDisplay = displayText;
|
|
120
|
+
await notifyStreamProgress("RUN_EVENT", displayText, THINKING_WS_SUMMARY_HINT, WS_THINKING_DISPLAY_MAX);
|
|
121
|
+
log.info(`${logPrefix} [stream] Thinking WS once (${reason}) len=${displayText.length}`);
|
|
122
|
+
};
|
|
123
|
+
let hasFirstReply = false;
|
|
124
|
+
let agentRunStarted = false;
|
|
125
|
+
let dispatchInFlight = false;
|
|
126
|
+
const firstReplyTimeoutMs = config.firstReplyTimeoutMs ?? 30 * 60_000;
|
|
127
|
+
const resolveFirstReplyTimeoutCause = () => {
|
|
128
|
+
if (!agentRunStarted)
|
|
129
|
+
return "never_started";
|
|
130
|
+
if (dispatchInFlight)
|
|
131
|
+
return "in_progress";
|
|
132
|
+
return "ended_without_http_reply";
|
|
133
|
+
};
|
|
134
|
+
const timeoutLabel = `${logPrefix} [stream] First reply timeout after ${firstReplyTimeoutMs}ms`;
|
|
135
|
+
const firstReplyTimeout = setTimeout(() => {
|
|
136
|
+
if (hasFirstReply)
|
|
137
|
+
return;
|
|
138
|
+
const timeoutCause = resolveFirstReplyTimeoutCause();
|
|
139
|
+
terminal.markTimeoutFired();
|
|
140
|
+
log.warn(`${timeoutLabel} cause=${timeoutCause} agentRunStarted=${agentRunStarted} ` +
|
|
141
|
+
`dispatchInFlight=${dispatchInFlight} (no IM timeout notice; channel timer ≠ OpenClaw terminal)`);
|
|
142
|
+
}, firstReplyTimeoutMs);
|
|
143
|
+
let deliverInvokeCount = 0;
|
|
144
|
+
let deliverCallCount = 0;
|
|
145
|
+
let deliverSkippedCount = 0;
|
|
146
|
+
let compactionNoticeDelivered = 0;
|
|
147
|
+
let lastDeliveredText = "";
|
|
148
|
+
let lastDeliveredHadMedia = false;
|
|
149
|
+
let agentRunId;
|
|
150
|
+
let dispatchError = undefined;
|
|
151
|
+
let lastDeliverError = undefined;
|
|
152
|
+
let dispatchResult;
|
|
153
|
+
let assistantItemSink;
|
|
154
|
+
// assistant-item 模式下的兜底:核心命令(如 /status)不跑 agent、无 assistant 流,
|
|
155
|
+
// assistant-item 订阅不会触发。这里记录被跳过的正文,收尾若确认本轮无任何投递则补发。
|
|
156
|
+
let assistantItemDelivered = false;
|
|
157
|
+
let assistantItemFallback;
|
|
158
|
+
const blockStreamingEnabled = isInboundBlockStreamingEnabled(config);
|
|
159
|
+
// assistant item 投递:订阅 assistant 流按每条 assistant 消息逐条发;开启时 block reply 正文不再发。
|
|
160
|
+
const assistantItemDeliveryEnabled = isDeliverAssistantItemsEnabled(config);
|
|
161
|
+
const segmentBufferEnabled = blockStreamingEnabled && isDeliverSegmentBufferEnabled(config) && !assistantItemDeliveryEnabled;
|
|
162
|
+
const segmentBuffer = segmentBufferEnabled ? new TurnDeliverBuffer() : undefined;
|
|
163
|
+
let hasBufferedSendable = false;
|
|
164
|
+
let flushSegmentBuffer = async () => { };
|
|
165
|
+
// —— onPartialReply 全量快照:当前 tool 区间内正文的权威来源 ——
|
|
166
|
+
// text 是「当前 assistant 消息」的完整快照(非增量);tool 边界 / 轮末 flush 时用它覆盖
|
|
167
|
+
// deliver 拼接文本,消除段缓冲对「覆盖 vs 追加」的启发式猜测(CO2)。
|
|
168
|
+
const partialSnapshotEnabled = segmentBufferEnabled && isDeliverPartialSnapshotEnabled(config);
|
|
169
|
+
let segmentSnapshotText = "";
|
|
170
|
+
let segmentSnapshotHadPartial = false;
|
|
171
|
+
// WS 正文预览节流状态
|
|
172
|
+
let lastReplyPreviewAt = 0;
|
|
173
|
+
let lastReplyPreviewText = "";
|
|
174
|
+
// 观测统计(保留少量,便于线上核对)
|
|
175
|
+
let partialReplyCount = 0;
|
|
176
|
+
let partialReplyMaxTextLen = 0;
|
|
177
|
+
let partialReplyFirstAt = 0;
|
|
178
|
+
const resetSegmentSnapshot = () => {
|
|
179
|
+
segmentSnapshotText = "";
|
|
180
|
+
segmentSnapshotHadPartial = false;
|
|
181
|
+
lastReplyPreviewText = "";
|
|
182
|
+
};
|
|
183
|
+
const emitReplyPreviewWs = async (snapshot, force) => {
|
|
184
|
+
const text = snapshot.trim();
|
|
185
|
+
if (!text)
|
|
186
|
+
return;
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
if (!force && now - lastReplyPreviewAt < WS_REPLY_PREVIEW_MIN_INTERVAL_MS)
|
|
189
|
+
return;
|
|
190
|
+
const clipped = clipWsDisplayText(text, WS_REPLY_PREVIEW_MAX);
|
|
191
|
+
if (clipped === lastReplyPreviewText)
|
|
192
|
+
return;
|
|
193
|
+
lastReplyPreviewAt = now;
|
|
194
|
+
lastReplyPreviewText = clipped;
|
|
195
|
+
await notifyStreamProgress("RUN_EVENT", clipped, WS_REPLY_PREVIEW_SUMMARY, WS_REPLY_PREVIEW_MAX);
|
|
196
|
+
};
|
|
197
|
+
const postHttpDeliver = async (args) => {
|
|
198
|
+
const { payloadForSend, textPart, sendLabel, invokeNum, hasMedia, omitDuplicateFileCaption } = args;
|
|
199
|
+
deliverCallCount++;
|
|
200
|
+
if (isFirstReply) {
|
|
201
|
+
const ttfr = Date.now() - dispatchStart;
|
|
202
|
+
log.info(`${logPrefix} [deliver] First response block received from AI (TTFB: ${ttfr}ms)`);
|
|
203
|
+
isFirstReply = false;
|
|
204
|
+
}
|
|
205
|
+
const reply = resolveSendMessageReply(params);
|
|
206
|
+
const atIds = reply ? [reply.targetUserId] : [];
|
|
207
|
+
if (!hasFirstReply) {
|
|
208
|
+
clearTimeout(firstReplyTimeout);
|
|
209
|
+
}
|
|
210
|
+
await sendReplyDeliverBlock(config, currentIdentity.token, params.groupId, payloadForSend, atIds, log, undefined, reply, agentWorkspaceDir);
|
|
211
|
+
if (!hasFirstReply) {
|
|
212
|
+
hasFirstReply = true;
|
|
213
|
+
}
|
|
214
|
+
terminal.markBusinessReplySent();
|
|
215
|
+
if (hasMedia) {
|
|
216
|
+
lastDeliveredHadMedia = true;
|
|
217
|
+
if (!omitDuplicateFileCaption) {
|
|
218
|
+
lastDeliveredText = textPart || "[media]";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
lastDeliveredText = textPart;
|
|
223
|
+
lastDeliveredHadMedia = false;
|
|
224
|
+
}
|
|
225
|
+
const preview = textPart.length > 80 ? `${textPart.slice(0, 80)}...` : textPart || "[media]";
|
|
226
|
+
log.info(`${logPrefix} [send] ${sendLabel} sent via HTTP: groupId=${params.groupId} ` +
|
|
227
|
+
`invoke=#${invokeNum} preview="${preview}"`);
|
|
228
|
+
};
|
|
229
|
+
let activeRound;
|
|
230
|
+
try {
|
|
231
|
+
if (!isStopControl) {
|
|
232
|
+
setSessionImContext(route.sessionKey, {
|
|
233
|
+
groupId: params.groupId,
|
|
234
|
+
replyToMsgId: params.msgId,
|
|
235
|
+
agentId: config.agentId || "main",
|
|
236
|
+
});
|
|
237
|
+
if (assistantItemDeliveryEnabled) {
|
|
238
|
+
// assistant 流订阅按 item 逐条把完整正文交到这里;复用本 dispatch 的发送链路
|
|
239
|
+
// (config/token/reply/终态/去重),因此 block reply 正文不再重复发送。
|
|
240
|
+
assistantItemSink = (itemId, text) => {
|
|
241
|
+
const label = deliverCallCount === 0 ? "First reply (assistant item)" : "Assistant item";
|
|
242
|
+
return postHttpDeliver({
|
|
243
|
+
payloadForSend: { text },
|
|
244
|
+
textPart: text,
|
|
245
|
+
sendLabel: label,
|
|
246
|
+
invokeNum: deliverInvokeCount,
|
|
247
|
+
hasMedia: false,
|
|
248
|
+
omitDuplicateFileCaption: false,
|
|
249
|
+
}).then(() => {
|
|
250
|
+
assistantItemDelivered = true;
|
|
251
|
+
}).catch((err) => {
|
|
252
|
+
lastDeliverError = err;
|
|
253
|
+
log.error(`${logPrefix} [assistant-item] send failed itemId=${itemId}: ${String(err)}`);
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
setAssistantItemSink(route.sessionKey, assistantItemSink);
|
|
257
|
+
}
|
|
258
|
+
await streamClient.start({
|
|
259
|
+
groupId: params.groupId,
|
|
260
|
+
replyToMsgId: params.msgId,
|
|
261
|
+
});
|
|
262
|
+
activeRound = registerActiveRound({
|
|
263
|
+
accountId,
|
|
264
|
+
groupId: params.groupId,
|
|
265
|
+
agentId: config.agentId || "main",
|
|
266
|
+
sessionKey: route.sessionKey,
|
|
267
|
+
replyToMsgId: params.msgId,
|
|
268
|
+
streamClient,
|
|
269
|
+
});
|
|
270
|
+
turnEnsureRound(activeRound.roundKey, activeRound.generation);
|
|
271
|
+
registerCorePlanSnapshotClient({
|
|
272
|
+
groupId: params.groupId,
|
|
273
|
+
client: streamClient,
|
|
274
|
+
replyToMsgId: params.msgId,
|
|
275
|
+
agentId: config.agentId || "main",
|
|
276
|
+
sessionKey: route.sessionKey,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
if (modelOverride) {
|
|
280
|
+
const parsed = splitModelRef(modelOverride);
|
|
281
|
+
if (parsed) {
|
|
282
|
+
const fallbackContext = resolveSessionModelFallbackContext({
|
|
283
|
+
storePath: inboundRecord?.storePath?.trim() ||
|
|
284
|
+
rt.channel.session.resolveStorePath(cfg.session?.store, {
|
|
285
|
+
agentId: route.agentId,
|
|
286
|
+
}),
|
|
287
|
+
sessionKey: route.sessionKey,
|
|
288
|
+
});
|
|
289
|
+
if (fallbackContext.sessionInAutoFallback) {
|
|
290
|
+
log.info(`${logPrefix} [model] session in auto-fallback ` +
|
|
291
|
+
`origin=${fallbackContext.fallbackOrigin?.provider ?? ""}/` +
|
|
292
|
+
`${fallbackContext.fallbackOrigin?.model ?? ""}`);
|
|
293
|
+
}
|
|
294
|
+
setPendingPerMessageModel(route.sessionKey, modelOverride, fallbackContext);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
log.warn(`${logPrefix} [model] invalid per-message model ref, ignoring: ${modelOverride}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const baseDispatchCfg = resolveDispatchOpenClawCfg(cfg, config);
|
|
301
|
+
let nasDispatch;
|
|
302
|
+
try {
|
|
303
|
+
nasDispatch = await resolveNasVolumeDispatchConfig({
|
|
304
|
+
cfg: baseDispatchCfg,
|
|
305
|
+
config,
|
|
306
|
+
agentId: route.agentId,
|
|
307
|
+
sessionKey: route.sessionKey,
|
|
308
|
+
volumes: nasVolumes,
|
|
309
|
+
agentWorkspaceDir,
|
|
310
|
+
log: {
|
|
311
|
+
info: (message) => log.info(`${logPrefix} ${message}`),
|
|
312
|
+
warn: (message) => log.warn(`${logPrefix} ${message}`),
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
// NAS 是可选增强信息。任何非法声明或运行时挂载校验失败都不得阻断原消息。
|
|
318
|
+
log.warn(`${logPrefix} [nas-volumes] invalid mount ignored: ${String(error)}`);
|
|
319
|
+
try {
|
|
320
|
+
// 若本会话此前已有有效声明,继续复用它;否则完全退回原 dispatch 配置。
|
|
321
|
+
nasDispatch = await resolveNasVolumeDispatchConfig({
|
|
322
|
+
cfg: baseDispatchCfg,
|
|
323
|
+
config,
|
|
324
|
+
agentId: route.agentId,
|
|
325
|
+
sessionKey: route.sessionKey,
|
|
326
|
+
agentWorkspaceDir,
|
|
327
|
+
log: {
|
|
328
|
+
info: (message) => log.info(`${logPrefix} ${message}`),
|
|
329
|
+
warn: (message) => log.warn(`${logPrefix} ${message}`),
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
catch (cachedError) {
|
|
334
|
+
log.warn(`${logPrefix} [nas-volumes] cached mount unavailable, continuing without NAS: ${String(cachedError)}`);
|
|
335
|
+
nasDispatch = { cfg: baseDispatchCfg, reused: false };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const dispatchCfg = nasDispatch.cfg;
|
|
339
|
+
const nasConfigOverride = nasDispatch.configOverride;
|
|
340
|
+
if (nasDispatch.specHash || nasDispatch.syncDeclarationHash) {
|
|
341
|
+
log.info(`${logPrefix} [nas-volumes] session=${route.sessionKey} volumes=${nasVolumes?.length ?? "cached"} ` +
|
|
342
|
+
`spec=${nasDispatch.specHash?.slice(0, 12) ?? "-"} sync=${nasDispatch.syncDeclarationHash?.slice(0, 12) ?? "-"} ` +
|
|
343
|
+
`reused=${nasDispatch.reused}` +
|
|
344
|
+
` configOverride=${Boolean(nasConfigOverride)}` +
|
|
345
|
+
` workspace=${agentWorkspaceDir ? "yes" : "no"}`);
|
|
346
|
+
}
|
|
347
|
+
else if (nasVolumes?.length) {
|
|
348
|
+
// 声明存在但未产出 mount/sync 指纹:通常是校验失败已吞掉,或 pull/push 尚未成功落指纹。
|
|
349
|
+
log.warn(`${logPrefix} [nas-volumes] session=${route.sessionKey} volumes=${nasVolumes.length} ` +
|
|
350
|
+
`no active mount/sync fingerprint (check prior [nas-volumes] warn); workspace=${agentWorkspaceDir ? "yes" : "no"}`);
|
|
351
|
+
}
|
|
352
|
+
if (blockStreamingEnabled && baseDispatchCfg !== cfg) {
|
|
353
|
+
log.info(`${logPrefix} [dispatch] IM blockStreamingCoalesce override ` +
|
|
354
|
+
`(minChars=${IM_BLOCK_STREAMING_COALESCE_DEFAULT.minChars} idleMs=${IM_BLOCK_STREAMING_COALESCE_DEFAULT.idleMs}) ` +
|
|
355
|
+
`— agents.defaults coalesce will not delay tool-before HTTP`);
|
|
356
|
+
}
|
|
357
|
+
if (blockStreamingEnabled) {
|
|
358
|
+
log.info(`${logPrefix} [dispatch] blockStreaming=on (assistant text before tools → HTTP; tool progress → WS)`);
|
|
359
|
+
}
|
|
360
|
+
if (segmentBufferEnabled) {
|
|
361
|
+
log.info(`${logPrefix} [dispatch] deliverSegmentBuffer=on (segment buffer → flush on tool start / dispatch end)`);
|
|
362
|
+
}
|
|
363
|
+
flushSegmentBuffer = async (kind) => {
|
|
364
|
+
if (!segmentBuffer)
|
|
365
|
+
return;
|
|
366
|
+
const result = segmentBuffer.takeForFlush(kind);
|
|
367
|
+
// 本段快照(onPartialReply 全量);仅在启用且确有回调时作为正文权威来源。
|
|
368
|
+
const snapshot = partialSnapshotEnabled && segmentSnapshotHadPartial ? segmentSnapshotText.trim() : "";
|
|
369
|
+
const sendLabel = kind === "interim"
|
|
370
|
+
? "Interim block"
|
|
371
|
+
: deliverCallCount === 0
|
|
372
|
+
? "First reply"
|
|
373
|
+
: "Final block";
|
|
374
|
+
if (result.status === "empty") {
|
|
375
|
+
// buffer 无内容但有快照(纯文本、无 deliver 文本块)→ 直接发快照。
|
|
376
|
+
if (snapshot) {
|
|
377
|
+
if (shouldSuppressDuplicateTextDeliver({
|
|
378
|
+
textPart: snapshot,
|
|
379
|
+
hasMedia: false,
|
|
380
|
+
lastDeliveredText,
|
|
381
|
+
lastDeliveredHadMedia,
|
|
382
|
+
})) {
|
|
383
|
+
log.info(`${logPrefix} [deliver-buffer] flush_${kind} snapshot_only skipped ` +
|
|
384
|
+
`(normalized same as last HTTP, len=${snapshot.length})`);
|
|
385
|
+
resetSegmentSnapshot();
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
log.info(`${logPrefix} [deliver-buffer] flush_${kind} snapshot_only textLen=${snapshot.length}`);
|
|
389
|
+
resetSegmentSnapshot();
|
|
390
|
+
await postHttpDeliver({
|
|
391
|
+
payloadForSend: { text: snapshot },
|
|
392
|
+
textPart: snapshot,
|
|
393
|
+
sendLabel,
|
|
394
|
+
invokeNum: deliverInvokeCount,
|
|
395
|
+
hasMedia: false,
|
|
396
|
+
omitDuplicateFileCaption: false,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (result.status === "skipped_duplicate") {
|
|
403
|
+
log.info(`${logPrefix} [deliver-buffer] flush_${kind} skipped (same as previous HTTP, comparable match)`);
|
|
404
|
+
resetSegmentSnapshot();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
let { payload: payloadForSend, textPart } = result.entry;
|
|
408
|
+
const { hasMedia } = result.entry;
|
|
409
|
+
// 快照覆盖正文:仅当快照包含缓冲文本(归一化后为超集)时才覆盖,避免竞态截断;
|
|
410
|
+
// 媒体/附件等元数据仍沿用 deliver 的 payload。
|
|
411
|
+
if (snapshot && snapshot !== textPart) {
|
|
412
|
+
if (snapshotSupersedesBufferText(snapshot, textPart)) {
|
|
413
|
+
log.info(`${logPrefix} [deliver-buffer] flush_${kind} snapshot_override ` +
|
|
414
|
+
`bufferLen=${textPart.length} → snapshotLen=${snapshot.length}`);
|
|
415
|
+
payloadForSend = {
|
|
416
|
+
...payloadForSend,
|
|
417
|
+
text: snapshot,
|
|
418
|
+
...(payloadForSend.markdown != null ? { markdown: snapshot } : {}),
|
|
419
|
+
};
|
|
420
|
+
textPart = snapshot;
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
log.warn(`${logPrefix} [deliver-buffer] flush_${kind} snapshot_skip (not superset; keep buffer) ` +
|
|
424
|
+
`bufferLen=${textPart.length} snapshotLen=${snapshot.length}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
log.info(`${logPrefix} [deliver-buffer] flush_${kind} textLen=${textPart.length} hasMedia=${hasMedia}`);
|
|
428
|
+
resetSegmentSnapshot();
|
|
429
|
+
await postHttpDeliver({
|
|
430
|
+
payloadForSend,
|
|
431
|
+
textPart,
|
|
432
|
+
sendLabel,
|
|
433
|
+
invokeNum: deliverInvokeCount,
|
|
434
|
+
hasMedia,
|
|
435
|
+
omitDuplicateFileCaption: false,
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
dispatchInFlight = true;
|
|
439
|
+
const dispatchRequest = {
|
|
440
|
+
ctx: inboundCtx,
|
|
441
|
+
cfg: dispatchCfg,
|
|
442
|
+
replyOptions: {
|
|
443
|
+
...resolveBlockStreamingReplyOptions(config),
|
|
444
|
+
sourceReplyDeliveryMode: "automatic",
|
|
445
|
+
suppressDefaultToolProgressMessages: true,
|
|
446
|
+
allowProgressCallbacksWhenSourceDeliverySuppressed: true,
|
|
447
|
+
// 单轮模型覆盖不走 replyOptions(OpenClaw 不消费 modelOverride 字段),
|
|
448
|
+
// 改由 dispatch 前登记的 pending + before_model_resolve hook 生效。
|
|
449
|
+
...(thinkingLevelOverride ? { thinkingLevelOverride } : {}),
|
|
450
|
+
onAgentRunStart: (runId) => {
|
|
451
|
+
agentRunStarted = true;
|
|
452
|
+
agentRunId = runId.trim() || undefined;
|
|
453
|
+
terminal.setRunId(runId);
|
|
454
|
+
noteWrappedRunId(route.sessionKey, runId.trim());
|
|
455
|
+
log.info(`${logPrefix} [dispatch] agent run started runId=${runId}`);
|
|
456
|
+
},
|
|
457
|
+
onModelSelected: (m) => {
|
|
458
|
+
// failover 候选启动时会多次触发;终态模型以 session store 为准(RUN_USAGE)
|
|
459
|
+
log.info(`${logPrefix} [model] run candidate provider=${m.provider} model=${m.model}`);
|
|
460
|
+
},
|
|
461
|
+
onPartialReply: async (payload) => {
|
|
462
|
+
if (!partialSnapshotEnabled)
|
|
463
|
+
return;
|
|
464
|
+
const text = typeof payload.text === "string" ? payload.text : "";
|
|
465
|
+
if (!text)
|
|
466
|
+
return;
|
|
467
|
+
partialReplyCount++;
|
|
468
|
+
if (text.length > partialReplyMaxTextLen)
|
|
469
|
+
partialReplyMaxTextLen = text.length;
|
|
470
|
+
const now = Date.now();
|
|
471
|
+
if (partialReplyFirstAt === 0) {
|
|
472
|
+
partialReplyFirstAt = now;
|
|
473
|
+
log.info(`${logPrefix} [partial] FIRST +${now - dispatchStart}ms textLen=${text.length}`);
|
|
474
|
+
}
|
|
475
|
+
// text 是当前 assistant 消息的全量快照(非增量):直接作为本段正文权威来源。
|
|
476
|
+
segmentSnapshotText = text;
|
|
477
|
+
segmentSnapshotHadPartial = true;
|
|
478
|
+
// 节流推送 WS 正文预览,供前端尽早覆盖显示同一输出位(HTTP 定稿前)。
|
|
479
|
+
await emitReplyPreviewWs(text, false);
|
|
480
|
+
},
|
|
481
|
+
onReasoningStream: async (payload) => {
|
|
482
|
+
const text = typeof payload.text === "string" ? payload.text : "";
|
|
483
|
+
if (!text.trim())
|
|
484
|
+
return;
|
|
485
|
+
thinkingWsBuffer.noteStream(text);
|
|
486
|
+
},
|
|
487
|
+
onReasoningEnd: async () => {
|
|
488
|
+
await flushThinkingWsOnce("reasoning_end");
|
|
489
|
+
},
|
|
490
|
+
onToolStart: async (p) => {
|
|
491
|
+
const name = typeof p.name === "string" && p.name.trim().length > 0 ? p.name.trim() : "tool";
|
|
492
|
+
const rawPhase = typeof p.phase === "string" ? p.phase.toLowerCase() : "";
|
|
493
|
+
console.log(`[xg_cwork_im] ${logPrefix} [onToolStart] name="${name}" phase="${rawPhase}" argsKeys=${p.args ? Object.keys(p.args).join(",") : "none"}`);
|
|
494
|
+
if (rawPhase === "update" || rawPhase === "delta" || rawPhase === "progress") {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
if (rawPhase === "end" || rawPhase === "fail" || rawPhase === "error") {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (!rawPhase || rawPhase === "start") {
|
|
501
|
+
try {
|
|
502
|
+
await flushSegmentBuffer("interim");
|
|
503
|
+
}
|
|
504
|
+
catch (interimErr) {
|
|
505
|
+
lastDeliverError = interimErr;
|
|
506
|
+
log.error(`${logPrefix} [deliver-buffer] flush_interim failed: ${String(interimErr)}`);
|
|
507
|
+
}
|
|
508
|
+
await flushThinkingWsOnce("before_tool");
|
|
509
|
+
toolNameStack.push(name);
|
|
510
|
+
if (name.includes("sessions_spawn") && !subagentNotificationSent) {
|
|
511
|
+
subagentNotificationSent = true;
|
|
512
|
+
try {
|
|
513
|
+
await sendTextMessage(config, currentIdentity.token, params.groupId, "已将任务分配给子助手处理,稍后会把结果发给您。");
|
|
514
|
+
console.log(`[xg_cwork_im] ${logPrefix} [subagent] sent notification from onToolStart`);
|
|
515
|
+
}
|
|
516
|
+
catch (sendErr) {
|
|
517
|
+
console.warn(`[xg_cwork_im] ${logPrefix} [subagent] onToolStart send failed: ${String(sendErr)}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// WS TOOL_PHASE 已移至全局 before_tool_call hook
|
|
522
|
+
},
|
|
523
|
+
onToolResult: async (payload) => {
|
|
524
|
+
const toolName = toolNameStack.length > 0 ? toolNameStack.pop() : "tool";
|
|
525
|
+
// WS TOOL_PHASE 已移至全局 after_tool_call hook
|
|
526
|
+
},
|
|
527
|
+
onItemEvent: async (p) => {
|
|
528
|
+
// 工具生命周期已由 onToolStart/onToolResult 覆盖;Reasoning 已由
|
|
529
|
+
// onReasoningStream/onReasoningEnd 覆盖(start/end 各触发一次)。二者
|
|
530
|
+
// 均不再进入 RUN_EVENT 兜底,避免内部诊断标识 `unmapped:*` 展示给用户。
|
|
531
|
+
const guard = resolveItemEventGuard(p);
|
|
532
|
+
if (guard.suppress)
|
|
533
|
+
return;
|
|
534
|
+
const text = (typeof p.progressText === "string" && p.progressText.trim()) ||
|
|
535
|
+
(typeof p.summary === "string" && p.summary.trim()) ||
|
|
536
|
+
(typeof p.title === "string" && p.title.trim()) ||
|
|
537
|
+
(typeof p.meta === "string" && p.meta.trim()) ||
|
|
538
|
+
"";
|
|
539
|
+
if (!text)
|
|
540
|
+
return;
|
|
541
|
+
// 未识别事件只记录日志,不发送给最终用户
|
|
542
|
+
log.debug?.(`${logPrefix} [stream] ignored unmapped onItemEvent ` +
|
|
543
|
+
`kind=${guard.kind || "unknown"} title=${guard.title || "unknown"}`);
|
|
544
|
+
},
|
|
545
|
+
onCompactionStart: async () => {
|
|
546
|
+
terminal.noteCompactionWsEvent("start");
|
|
547
|
+
await notifyStreamProgress("RUN_EVENT", "正在整理上下文…");
|
|
548
|
+
},
|
|
549
|
+
onCompactionEnd: async () => {
|
|
550
|
+
terminal.noteCompactionWsEvent("end");
|
|
551
|
+
await notifyStreamProgress("RUN_EVENT", "上下文整理完成");
|
|
552
|
+
},
|
|
553
|
+
onApprovalEvent: async (p) => {
|
|
554
|
+
const raw = typeof p.title === "string" && p.title.trim().length > 0
|
|
555
|
+
? p.title.trim()
|
|
556
|
+
: typeof p.message === "string" && p.message.trim().length > 0
|
|
557
|
+
? p.message.trim()
|
|
558
|
+
: "审批状态更新";
|
|
559
|
+
await notifyStreamProgress("RUN_EVENT", clipWsDisplayText(raw, 160));
|
|
560
|
+
},
|
|
561
|
+
onCommandOutput: async (p) => {
|
|
562
|
+
const ph = typeof p.phase === "string" ? p.phase.toLowerCase() : "";
|
|
563
|
+
if (ph === "delta" || ph === "stream" || ph === "progress")
|
|
564
|
+
return;
|
|
565
|
+
const st = typeof p.status === "string" ? p.status.toLowerCase() : "";
|
|
566
|
+
const terminalPhase = ["completed", "complete", "done", "end", "failed", "fail", "error"].includes(ph);
|
|
567
|
+
const terminalStatus = ["completed", "failed", "error", "done"].includes(st);
|
|
568
|
+
const hasExit = p.exitCode != null;
|
|
569
|
+
if (!terminalPhase && !terminalStatus && !hasExit)
|
|
570
|
+
return;
|
|
571
|
+
const outRaw = typeof p.output === "string" ? p.output.trim() : "";
|
|
572
|
+
const outSum = outRaw ? clipWsProgressSummary(outRaw) : undefined;
|
|
573
|
+
if (ph === "failed" || ph === "fail" || ph === "error" || st === "failed" || st === "error") {
|
|
574
|
+
await notifyStreamProgress("RUN_EVENT", "计划有变,正在调整策略", outSum); //命令执行失败
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (hasExit && p.exitCode !== 0) {
|
|
578
|
+
await notifyStreamProgress("RUN_EVENT", `命令已结束(退出码 ${p.exitCode})`, outSum);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
await notifyStreamProgress("RUN_EVENT", "命令已执行完毕", outSum);
|
|
582
|
+
},
|
|
583
|
+
onPatchSummary: async (_p) => {
|
|
584
|
+
await notifyStreamProgress("RUN_EVENT", "已记录文件变更摘要");
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
dispatcherOptions: {
|
|
588
|
+
responsePrefix: "",
|
|
589
|
+
onSkip: (payload, meta) => {
|
|
590
|
+
deliverSkippedCount++;
|
|
591
|
+
terminal.noteNormalizerSkip(meta.reason);
|
|
592
|
+
log.info(`${logPrefix} [deliver] Payload skipped by normalizer kind=${meta.kind} reason=${meta.reason}`);
|
|
593
|
+
},
|
|
594
|
+
onError: (err, meta) => {
|
|
595
|
+
lastDeliverError = err;
|
|
596
|
+
log.error(`${logPrefix} [deliver] Dispatcher caught unhandled error kind=${meta.kind}: ${String(err)}`);
|
|
597
|
+
},
|
|
598
|
+
deliver: async (payload, info) => {
|
|
599
|
+
try {
|
|
600
|
+
deliverInvokeCount++;
|
|
601
|
+
const textPayload = payload.markdown || payload.text || "";
|
|
602
|
+
const trimmedForSend = textPayload.trim();
|
|
603
|
+
const textPart = stripMediaDirectiveLines(trimmedForSend);
|
|
604
|
+
const hasMedia = Boolean(payload.mediaUrl?.trim()) ||
|
|
605
|
+
Boolean(payload.mediaUrls?.some((u) => typeof u === "string" && u.trim()));
|
|
606
|
+
const textPreview = textPart.length > 160
|
|
607
|
+
? `${textPart.slice(0, 80)}…${textPart.slice(-80)}`
|
|
608
|
+
: textPart;
|
|
609
|
+
log.info(`${logPrefix} [deliver] invoke #${deliverInvokeCount} isThinking=${payload.isThinking ?? false} ` +
|
|
610
|
+
`textLen=${textPart.length} hasMedia=${hasMedia} mediaUrl=${Boolean(payload.mediaUrl?.trim())} ` +
|
|
611
|
+
`kind=${info?.kind ?? "none"} ` +
|
|
612
|
+
`preview=${JSON.stringify(textPreview || "(empty)")}`);
|
|
613
|
+
if (!hasMedia && !stripModelUsageFooter(textPart).trim()) {
|
|
614
|
+
deliverSkippedCount++;
|
|
615
|
+
log.info(`${logPrefix} [deliver] skip: model usage footer only (rawLen=${trimmedForSend.length})`);
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
if (payload.isError === true) {
|
|
619
|
+
const errRaw = textPart || "(empty)";
|
|
620
|
+
terminal.noteRuntimeErrorFromPayload(errRaw);
|
|
621
|
+
const userText = formatConfirmedUpstreamErrorForIm(errRaw);
|
|
622
|
+
if (!userText) {
|
|
623
|
+
deliverSkippedCount++;
|
|
624
|
+
log.info(`${logPrefix} [deliver] isError payload empty after format; skipped HTTP`);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
// 上游已 deliver 的错误:HTTP 发出(白名单人话或原文),不再等到终态换插件空话
|
|
628
|
+
const errorPayload = {
|
|
629
|
+
...payload,
|
|
630
|
+
text: userText,
|
|
631
|
+
markdown: userText,
|
|
632
|
+
isError: true,
|
|
633
|
+
};
|
|
634
|
+
log.info(`${logPrefix} [deliver] isError → HTTP (confirmed map or passthrough) ` +
|
|
635
|
+
`len=${userText.length}`);
|
|
636
|
+
await postHttpDeliver({
|
|
637
|
+
payloadForSend: errorPayload,
|
|
638
|
+
textPart: userText,
|
|
639
|
+
sendLabel: deliverInvokeCount === 1 ? "First reply (error)" : "Error payload",
|
|
640
|
+
invokeNum: deliverInvokeCount,
|
|
641
|
+
hasMedia: false,
|
|
642
|
+
omitDuplicateFileCaption: false,
|
|
643
|
+
});
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (payload.isCompactionNotice === true) {
|
|
647
|
+
terminal.noteCompactionNotice(textPart || "Compacting context...");
|
|
648
|
+
if (textPart) {
|
|
649
|
+
// 放开 HTTP 屏蔽:compaction notice 不再丢弃,作为群消息真实发出,
|
|
650
|
+
// 让用户能看到“会话正在整理/已整理”而不是莫名等待。
|
|
651
|
+
// 故意不走 postHttpDeliver:不标记业务回复、不进入 lastDelivered 去重链,
|
|
652
|
+
// 避免把“整理上下文”误当首条正式答复而破坏 first-reply 计时与终态兜底判定。
|
|
653
|
+
try {
|
|
654
|
+
await terminalSend(textPart);
|
|
655
|
+
compactionNoticeDelivered += 1;
|
|
656
|
+
log.info(`${logPrefix} [deliver] Compaction notice sent via HTTP: ` +
|
|
657
|
+
`${JSON.stringify(textPart.slice(0, 80))}`);
|
|
658
|
+
}
|
|
659
|
+
catch (err) {
|
|
660
|
+
log.warn(`${logPrefix} [deliver] Compaction notice HTTP send failed: ${String(err)}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
deliverSkippedCount++;
|
|
665
|
+
log.info(`${logPrefix} [deliver] Compaction notice empty; skipped`);
|
|
666
|
+
}
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (payload.isReasoning === true) {
|
|
670
|
+
deliverSkippedCount++;
|
|
671
|
+
if (textPart)
|
|
672
|
+
thinkingWsBuffer.noteStream(textPart);
|
|
673
|
+
await flushThinkingWsOnce("deliver_reasoning");
|
|
674
|
+
log.info(`${logPrefix} [deliver] Thinking block → single WS RUN_EVENT (len=${textPart.length})`);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (shouldSuppressFallbackNoticeForIm({
|
|
678
|
+
isFallbackNotice: payload.isFallbackNotice === true,
|
|
679
|
+
deliverModelFallbackNotice: config.deliverModelFallbackNotice,
|
|
680
|
+
requestedModelRaw: modelOverride,
|
|
681
|
+
noticeText: textPart,
|
|
682
|
+
})) {
|
|
683
|
+
deliverSkippedCount++;
|
|
684
|
+
const reason = config.deliverModelFallbackNotice === true
|
|
685
|
+
? "spurious per-message fallback notice"
|
|
686
|
+
: "model fallback notice disabled for IM (deliverModelFallbackNotice)";
|
|
687
|
+
log.info(`${logPrefix} [model] Suppressing fallback notice (${reason}): ` +
|
|
688
|
+
`${JSON.stringify(textPart.slice(0, 120))}`);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (!segmentBufferEnabled &&
|
|
692
|
+
shouldSuppressDuplicateTextDeliver({
|
|
693
|
+
textPart,
|
|
694
|
+
hasMedia,
|
|
695
|
+
lastDeliveredText,
|
|
696
|
+
lastDeliveredHadMedia,
|
|
697
|
+
})) {
|
|
698
|
+
deliverSkippedCount++;
|
|
699
|
+
log.info(`${logPrefix} [deliver] Suppressed duplicate text-only deliver ` +
|
|
700
|
+
`(normalized same as previous, rawLen=${textPart.length})`);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
let omitDuplicateFileCaption = false;
|
|
704
|
+
if (!segmentBufferEnabled) {
|
|
705
|
+
omitDuplicateFileCaption = shouldOmitDuplicateFileCaption({
|
|
706
|
+
textPart,
|
|
707
|
+
hasMedia,
|
|
708
|
+
lastDeliveredText,
|
|
709
|
+
lastDeliveredHadMedia,
|
|
710
|
+
});
|
|
711
|
+
if (omitDuplicateFileCaption) {
|
|
712
|
+
log.info(`${logPrefix} [deliver] Omitting duplicate file caption ` +
|
|
713
|
+
`(normalized same as previous text, rawLen=${textPart.length})`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (!isDeliverPayloadSendable({
|
|
717
|
+
textPart,
|
|
718
|
+
hasMedia,
|
|
719
|
+
isThinking: payload.isThinking,
|
|
720
|
+
})) {
|
|
721
|
+
deliverSkippedCount++;
|
|
722
|
+
log.info(`${logPrefix} [deliver] Payload has no sendable content, skipping` +
|
|
723
|
+
` (isThinking=${payload.isThinking ?? false} hasMedia=${hasMedia})`);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
// assistant item 投递开启时:正文由 assistant 流订阅按 item 发送,
|
|
727
|
+
// block reply 不再发纯文本;媒体照发但清空 caption,避免正文重复。
|
|
728
|
+
if (assistantItemDeliveryEnabled && !hasMedia) {
|
|
729
|
+
deliverSkippedCount++;
|
|
730
|
+
// 记录兜底正文:核心命令/无 agent run 的轮次不会有 assistant 流,
|
|
731
|
+
// assistant-item 订阅不会投递,收尾时用它补发避免用户看不到回复。
|
|
732
|
+
assistantItemFallback = {
|
|
733
|
+
payloadForSend: { ...payload, text: textPart, markdown: textPart },
|
|
734
|
+
textPart,
|
|
735
|
+
};
|
|
736
|
+
log.info(`${logPrefix} [deliver] assistant-item mode: text handled by assistant subscription; ` +
|
|
737
|
+
`skipping deliver text (textLen=${textPart.length})`);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
let payloadForSend = textPart !== trimmedForSend
|
|
741
|
+
? {
|
|
742
|
+
...payload,
|
|
743
|
+
...(payload.markdown != null
|
|
744
|
+
? { markdown: textPart }
|
|
745
|
+
: { text: textPart }),
|
|
746
|
+
}
|
|
747
|
+
: payload;
|
|
748
|
+
if (omitDuplicateFileCaption || (assistantItemDeliveryEnabled && hasMedia)) {
|
|
749
|
+
payloadForSend = { ...payloadForSend, text: "", markdown: "" };
|
|
750
|
+
}
|
|
751
|
+
if (segmentBufferEnabled && segmentBuffer) {
|
|
752
|
+
if (info?.kind === "final" && segmentBuffer.hasContent()) {
|
|
753
|
+
segmentBuffer.clear();
|
|
754
|
+
log.info(`${logPrefix} [deliver] final block replaces buffer (intermediates discarded, textLen=${textPart.length})`);
|
|
755
|
+
}
|
|
756
|
+
if (shouldSuppressDuplicateTextDeliver({ textPart, hasMedia, lastDeliveredText, lastDeliveredHadMedia })) {
|
|
757
|
+
deliverSkippedCount++;
|
|
758
|
+
log.info(`${logPrefix} [deliver-buffer] skip: same as last HTTP delivered ` +
|
|
759
|
+
`(normalized match, textLen=${textPart.length})`);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
segmentBuffer.write({ payload: payloadForSend, textPart, rawTextPart: textPayload, hasMedia });
|
|
763
|
+
if (!hasBufferedSendable) {
|
|
764
|
+
hasBufferedSendable = true;
|
|
765
|
+
clearTimeout(firstReplyTimeout);
|
|
766
|
+
}
|
|
767
|
+
log.info(`${logPrefix} [deliver-buffer] write invoke=#${deliverInvokeCount} ` +
|
|
768
|
+
`textLen=${textPart.length} hasMedia=${hasMedia}`);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const sendLabel = deliverInvokeCount === 1
|
|
772
|
+
? "First reply"
|
|
773
|
+
: blockStreamingEnabled
|
|
774
|
+
? "Interim block"
|
|
775
|
+
: "Additional reply";
|
|
776
|
+
await postHttpDeliver({
|
|
777
|
+
payloadForSend,
|
|
778
|
+
textPart,
|
|
779
|
+
sendLabel,
|
|
780
|
+
invokeNum: deliverInvokeCount,
|
|
781
|
+
hasMedia,
|
|
782
|
+
omitDuplicateFileCaption,
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
catch (err) {
|
|
786
|
+
log.error(`${logPrefix} [deliver] Reply deliver failed: ${String(err)}`);
|
|
787
|
+
throw err;
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
const effectiveDriver = explicitDriver ?? resolveDispatchDriver(rt, config.dispatchDriver ?? "auto").driver;
|
|
793
|
+
log.info(`${logPrefix} [dispatch] driver=${effectiveDriver} recordInCore=${Boolean(inboundRecord)}`);
|
|
794
|
+
const storePath = inboundRecord?.storePath?.trim() ||
|
|
795
|
+
rt.channel.session.resolveStorePath(cfg.session?.store, {
|
|
796
|
+
agentId: config.agentId || "main",
|
|
797
|
+
});
|
|
798
|
+
dispatchResult = await dispatchXgImInboundReply({
|
|
799
|
+
rt,
|
|
800
|
+
ctx: dispatchRequest.ctx,
|
|
801
|
+
cfg: dispatchRequest.cfg,
|
|
802
|
+
channel: "xg_cwork_im",
|
|
803
|
+
accountId,
|
|
804
|
+
agentId: config.agentId || "main",
|
|
805
|
+
routeSessionKey: route.sessionKey,
|
|
806
|
+
storePath,
|
|
807
|
+
replyOptions: dispatchRequest.replyOptions,
|
|
808
|
+
dispatcherOptions: dispatchRequest.dispatcherOptions,
|
|
809
|
+
driver: effectiveDriver,
|
|
810
|
+
inboundRecord,
|
|
811
|
+
logPrefix,
|
|
812
|
+
warn: (message) => log.warn(message),
|
|
813
|
+
...(nasConfigOverride ? { configOverride: nasConfigOverride } : {}),
|
|
814
|
+
});
|
|
815
|
+
dispatchInFlight = false;
|
|
816
|
+
const bufStats = segmentBuffer?.getStats();
|
|
817
|
+
log.info(`${logPrefix} [dispatch] Dispatch completed for sessionKey=${route.sessionKey}` +
|
|
818
|
+
` (invoke=${deliverInvokeCount} delivered=${deliverCallCount} skipped=${deliverSkippedCount}` +
|
|
819
|
+
` compaction_http=${compactionNoticeDelivered}` +
|
|
820
|
+
(bufStats
|
|
821
|
+
? ` buffer_writes=${bufStats.writeCount}` +
|
|
822
|
+
` revision_overwrite=${bufStats.revisionOverwriteCount}` +
|
|
823
|
+
` segment_append=${bufStats.segmentAppendCount}` +
|
|
824
|
+
` flush_interim=${bufStats.flushInterimCount}` +
|
|
825
|
+
` flush_final=${bufStats.flushFinalCount}` +
|
|
826
|
+
` flush_dup_skip=${bufStats.skippedDuplicateFlushCount}`
|
|
827
|
+
: "") +
|
|
828
|
+
`)`);
|
|
829
|
+
if (partialSnapshotEnabled) {
|
|
830
|
+
log.info(`${logPrefix} [partial] SUMMARY count=${partialReplyCount} ` +
|
|
831
|
+
`maxTextLen=${partialReplyMaxTextLen} ` +
|
|
832
|
+
`firstAt=${partialReplyFirstAt ? partialReplyFirstAt - dispatchStart : -1}ms ` +
|
|
833
|
+
`(count=0 → 该配置下未回调 partial,flush 回退到 deliver 段缓冲)`);
|
|
834
|
+
}
|
|
835
|
+
if (deliverInvokeCount === 0) {
|
|
836
|
+
log.warn(`${logPrefix} [dispatch] OpenClaw never invoked channel deliver() for this turn — ` +
|
|
837
|
+
`this plugin will not POST /im/message/send; IM「收到消息」若存在,多半来自占位/END 或其它通道,而非本插件 HTTP 合并`);
|
|
838
|
+
}
|
|
839
|
+
else if (deliverCallCount === 0 && compactionNoticeDelivered === 0 && !segmentBuffer?.hasContent()) {
|
|
840
|
+
log.warn(`${logPrefix} [dispatch] deliver() invoked ${deliverInvokeCount} time(s) but none had sendable text/media — ` +
|
|
841
|
+
`check model output (thinking-only / empty blocks) or OpenClaw normalizer`);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
catch (dispatchErr) {
|
|
845
|
+
dispatchError = dispatchErr;
|
|
846
|
+
log.error(`${logPrefix} [dispatch] Dispatch failed (invoke=${deliverInvokeCount} delivered=${deliverCallCount} skipped=${deliverSkippedCount}): ${String(dispatchErr)}`);
|
|
847
|
+
}
|
|
848
|
+
finally {
|
|
849
|
+
dispatchInFlight = false;
|
|
850
|
+
clearPendingPerMessageModel(route.sessionKey);
|
|
851
|
+
if (assistantItemDeliveryEnabled) {
|
|
852
|
+
// 收尾前把当前 item 的完整正文交给 sink 发完,再摘除 sink(避免终态判定漏掉最后一条)。
|
|
853
|
+
try {
|
|
854
|
+
await flushAssistantItem(route.sessionKey);
|
|
855
|
+
}
|
|
856
|
+
catch (itemErr) {
|
|
857
|
+
lastDeliverError = itemErr;
|
|
858
|
+
log.error(`${logPrefix} [assistant-item] flush_final failed: ${String(itemErr)}`);
|
|
859
|
+
}
|
|
860
|
+
clearAssistantItemSink(route.sessionKey, assistantItemSink);
|
|
861
|
+
resetAssistantItem(route.sessionKey);
|
|
862
|
+
// 兜底:assistant-item 订阅整轮没投出任何东西(核心命令/无 agent run 的轮次,
|
|
863
|
+
// 或 assistant 流缺失),但 deliver() 收到了正文——补发一次,避免 no_reply 静默。
|
|
864
|
+
if (!assistantItemDelivered && assistantItemFallback && deliverCallCount === 0) {
|
|
865
|
+
try {
|
|
866
|
+
await postHttpDeliver({
|
|
867
|
+
payloadForSend: assistantItemFallback.payloadForSend,
|
|
868
|
+
textPart: assistantItemFallback.textPart,
|
|
869
|
+
sendLabel: "Fallback reply (assistant item not delivered)",
|
|
870
|
+
invokeNum: deliverInvokeCount,
|
|
871
|
+
hasMedia: false,
|
|
872
|
+
omitDuplicateFileCaption: false,
|
|
873
|
+
});
|
|
874
|
+
log.info(`${logPrefix} [assistant-item] fallback HTTP send: assistant subscription produced ` +
|
|
875
|
+
`no item this turn; sent textLen=${assistantItemFallback.textPart.length}`);
|
|
876
|
+
}
|
|
877
|
+
catch (fbErr) {
|
|
878
|
+
lastDeliverError = fbErr;
|
|
879
|
+
log.error(`${logPrefix} [assistant-item] fallback send failed: ${String(fbErr)}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
try {
|
|
884
|
+
await flushSegmentBuffer("final");
|
|
885
|
+
}
|
|
886
|
+
catch (flushErr) {
|
|
887
|
+
lastDeliverError = flushErr;
|
|
888
|
+
log.error(`${logPrefix} [deliver-buffer] flush_final failed: ${String(flushErr)}`);
|
|
889
|
+
}
|
|
890
|
+
clearTimeout(firstReplyTimeout);
|
|
891
|
+
const terminalState = await terminal.finalizeAndDeliver({
|
|
892
|
+
sendFn: terminalSend,
|
|
893
|
+
dispatchThrew: dispatchError !== undefined,
|
|
894
|
+
dispatchError,
|
|
895
|
+
lastDeliverError,
|
|
896
|
+
beforeAgentRunBlocked: dispatchResult?.beforeAgentRunBlocked === true,
|
|
897
|
+
deliverInvokeCount,
|
|
898
|
+
deliverCallCount,
|
|
899
|
+
});
|
|
900
|
+
const endReason = terminalState === "success" ||
|
|
901
|
+
hasFirstReply ||
|
|
902
|
+
terminal.wasTerminalDelivered() ||
|
|
903
|
+
terminal.didTimeoutFire() ||
|
|
904
|
+
subagentNotificationSent
|
|
905
|
+
? "stop"
|
|
906
|
+
: "no_reply";
|
|
907
|
+
// `/stop` 仅借用 OpenClaw dispatch 触发 fast-abort;原轮次的 suspended/aborted
|
|
908
|
+
// 由 account-connection 在 dispatch 返回后串行收口,控制消息自身不产生 WS 事件。
|
|
909
|
+
if (isStopControl)
|
|
910
|
+
return;
|
|
911
|
+
// 当前业务轮次已被并发 `/stop` 接管时,收口事件由 interrupt-round 负责。
|
|
912
|
+
if (activeRound?.interrupted) {
|
|
913
|
+
log.info(`${logPrefix} [stream] round interrupted; skip late RUN_USAGE/FINISHED`);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
// —— 整轮收尾(OpenClaw 7.1+/8.x):子任务流程中 dispatch 返回只是「一段 run 结束」,
|
|
917
|
+
// FINISHED/RUN_USAGE 必须等主会话出现非 yield 段终态(或静默兜底)后才发出,
|
|
918
|
+
// 否则会在主会话 resume 继续派发更多子任务时提前结束本轮。——
|
|
919
|
+
const finishWsRound = async (reason) => {
|
|
920
|
+
const roundKey = activeRound?.roundKey;
|
|
921
|
+
const generation = activeRound?.generation;
|
|
922
|
+
const hasRound = roundKey !== undefined && generation !== undefined;
|
|
923
|
+
if (hasRound && !roundExists(roundKey, generation)) {
|
|
924
|
+
log.info(`${logPrefix} [stream] round already completed/interrupted; skip late RUN_USAGE/FINISHED`);
|
|
925
|
+
turnForget(roundKey, generation);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
try {
|
|
929
|
+
const storePath = rt.channel.session.resolveStorePath(cfg.session?.store, {
|
|
930
|
+
agentId: config.agentId || "main",
|
|
931
|
+
});
|
|
932
|
+
const sessionFields = readSessionUsageFields({
|
|
933
|
+
storePath,
|
|
934
|
+
sessionKey: route.sessionKey,
|
|
935
|
+
});
|
|
936
|
+
const usage = buildRunUsageFromSessionEntry(sessionFields, {
|
|
937
|
+
runId: agentRunId ?? terminal.getRunId(),
|
|
938
|
+
});
|
|
939
|
+
if (hasReportableUsage(usage) && usage) {
|
|
940
|
+
await streamClient.streamUsage(toImStreamRunUsageData(usage, {
|
|
941
|
+
groupId: params.groupId,
|
|
942
|
+
replyToMsgId: params.msgId,
|
|
943
|
+
}));
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
log.info(`${logPrefix} [stream] RUN_USAGE skipped (session store has no model/token; ` +
|
|
947
|
+
`sessionKey=${route.sessionKey} deliver_call=${deliverCallCount})`);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
catch (usageErr) {
|
|
951
|
+
log.warn(`${logPrefix} [stream] RUN_USAGE failed: ${String(usageErr)}`);
|
|
952
|
+
}
|
|
953
|
+
try {
|
|
954
|
+
await streamClient.end(reason, {
|
|
955
|
+
groupId: params.groupId,
|
|
956
|
+
replyToMsgId: params.msgId,
|
|
957
|
+
});
|
|
958
|
+
log.info(`${logPrefix} [stream] FINISHED sent: reason=${reason} terminal_state=${terminalState} ` +
|
|
959
|
+
`has_first_reply=${hasFirstReply} deliver_invoke=${deliverInvokeCount} ` +
|
|
960
|
+
`deliver_call=${deliverCallCount} skipped=${deliverSkippedCount}`);
|
|
961
|
+
}
|
|
962
|
+
catch (endErr) {
|
|
963
|
+
log.error(`${logPrefix} [stream] FINISHED failed: ${String(endErr)}`);
|
|
964
|
+
}
|
|
965
|
+
finally {
|
|
966
|
+
if (hasRound) {
|
|
967
|
+
finalizeRoundMonitoring(roundKey, generation);
|
|
968
|
+
completeActiveRound(roundKey, generation);
|
|
969
|
+
turnForget(roundKey, generation);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
const roundKey = activeRound?.roundKey;
|
|
974
|
+
const generation = activeRound?.generation;
|
|
975
|
+
const sawSubagent = subagentNotificationSent ||
|
|
976
|
+
(roundKey !== undefined && generation !== undefined &&
|
|
977
|
+
turnIsSawSubagent(roundKey, generation));
|
|
978
|
+
if (sawSubagent) {
|
|
979
|
+
turnEnsureRound(roundKey, generation);
|
|
980
|
+
turnOnDispatchReturned(roundKey, generation);
|
|
981
|
+
const deadline = Date.now() + TURN_END_WAIT_MAX_MS;
|
|
982
|
+
log.info(`${logPrefix} [stream] FINISHED deferred: subagent turn, waiting for lifecycle ` +
|
|
983
|
+
`final segment (no premature finish on resume). reason=${endReason} ` +
|
|
984
|
+
`terminal_state=${terminalState} deliver_invoke=${deliverInvokeCount}`);
|
|
985
|
+
const waitStartedAt = Date.now();
|
|
986
|
+
// 等待整轮结束信号(lifecycle 非 yield 终态 / 子任务完成后的静默兜底);/
|
|
987
|
+
// 若轮次被 `/stop` 或其它收口移除,则由该路径发送过 aborted,这里直接退出。
|
|
988
|
+
while (!turnIsEligible(roundKey, generation)) {
|
|
989
|
+
if (!roundExists(roundKey, generation) || activeRound?.interrupted) {
|
|
990
|
+
log.info(`${logPrefix} [stream] round interrupted while waiting; skip late RUN_USAGE/FINISHED`);
|
|
991
|
+
turnForget(roundKey, generation);
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
if (Date.now() > deadline) {
|
|
995
|
+
log.warn(`${logPrefix} [stream] turn-end wait exceeded ${TURN_END_WAIT_MAX_MS}ms; force finishing`);
|
|
996
|
+
break;
|
|
997
|
+
}
|
|
998
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
999
|
+
}
|
|
1000
|
+
log.info(`${logPrefix} [stream] turn-end released round=${roundKey}#${generation} ` +
|
|
1001
|
+
`eligible=${turnIsEligible(roundKey, generation)} waitMs=${Date.now() - waitStartedAt}`);
|
|
1002
|
+
await finishWsRound(endReason);
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
// 无子任务的普通轮次:dispatch 返回即整轮结束,立即收尾(保持既有行为)。
|
|
1006
|
+
if (roundKey !== undefined && generation !== undefined) {
|
|
1007
|
+
turnEnsureRound(roundKey, generation);
|
|
1008
|
+
turnOnDispatchReturned(roundKey, generation);
|
|
1009
|
+
}
|
|
1010
|
+
await finishWsRound(endReason);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
//# sourceMappingURL=dispatch-mentioned-reply.js.map
|