devchain-cli 0.21.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/drizzle/0073_integration_storage.sql +27 -0
- package/dist/drizzle/0074_black_drax.sql +7 -0
- package/dist/drizzle/0075_misty_franklin_richards.sql +37 -0
- package/dist/drizzle/0076_epic_time_accounting.sql +48 -0
- package/dist/drizzle/meta/0073_snapshot.json +6247 -0
- package/dist/drizzle/meta/0074_snapshot.json +6299 -0
- package/dist/drizzle/meta/0075_snapshot.json +6563 -0
- package/dist/drizzle/meta/0076_snapshot.json +6913 -0
- package/dist/drizzle/meta/_journal.json +28 -0
- package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.d.ts +27 -1
- package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.d.ts.map +1 -1
- package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.js +15 -0
- package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.js.map +1 -1
- package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +52 -52
- package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
- package/dist/server/app.main.module.js +4 -0
- package/dist/server/app.main.module.js.map +1 -1
- package/dist/server/app.normal.module.js +2 -0
- package/dist/server/app.normal.module.js.map +1 -1
- package/dist/server/common/config/container-scope.d.ts +11 -0
- package/dist/server/common/config/container-scope.js +37 -0
- package/dist/server/common/config/container-scope.js.map +1 -1
- package/dist/server/common/constants/terminal.d.ts +1 -0
- package/dist/server/common/constants/terminal.js +2 -1
- package/dist/server/common/constants/terminal.js.map +1 -1
- package/dist/server/common/filters/http-exception.filter.js +3 -4
- package/dist/server/common/filters/http-exception.filter.js.map +1 -1
- package/dist/server/common/guards/integration-admission.guard.d.ts +4 -0
- package/dist/server/common/guards/integration-admission.guard.js +22 -0
- package/dist/server/common/guards/integration-admission.guard.js.map +1 -0
- package/dist/server/common/test/app-bootstrap.helper.js +1 -1
- package/dist/server/common/test/app-bootstrap.helper.js.map +1 -1
- package/dist/server/modules/agent-message-delivery/agent-message-delivery.service.js +4 -0
- package/dist/server/modules/agent-message-delivery/agent-message-delivery.service.js.map +1 -1
- package/dist/server/modules/cloud/cloud.module.js +5 -1
- package/dist/server/modules/cloud/cloud.module.js.map +1 -1
- package/dist/server/modules/cloud/services/cloud-egress-bridge.service.d.ts +3 -1
- package/dist/server/modules/cloud/services/cloud-egress-bridge.service.js +19 -3
- package/dist/server/modules/cloud/services/cloud-egress-bridge.service.js.map +1 -1
- package/dist/server/modules/cloud/services/egress-queue.service.d.ts +2 -0
- package/dist/server/modules/cloud/services/egress-queue.service.js +35 -17
- package/dist/server/modules/cloud/services/egress-queue.service.js.map +1 -1
- package/dist/server/modules/cloud/services/event-mapper.service.d.ts +1 -0
- package/dist/server/modules/cloud/services/event-mapper.service.js.map +1 -1
- package/dist/server/modules/cloud/services/notification-recipient-resolver.service.d.ts +10 -0
- package/dist/server/modules/cloud/services/notification-recipient-resolver.service.js +55 -0
- package/dist/server/modules/cloud/services/notification-recipient-resolver.service.js.map +1 -0
- package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.d.ts +31 -5
- package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.js +15 -0
- package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.js.map +1 -1
- package/dist/server/modules/cloud-tunnel/services/mobile-rpc-workspace-access.service.js +5 -2
- package/dist/server/modules/cloud-tunnel/services/mobile-rpc-workspace-access.service.js.map +1 -1
- package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.d.ts +1 -0
- package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.js +8 -0
- package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.js.map +1 -1
- package/dist/server/modules/core/controllers/runtime.controller.d.ts +1 -0
- package/dist/server/modules/core/controllers/runtime.controller.js +2 -0
- package/dist/server/modules/core/controllers/runtime.controller.js.map +1 -1
- package/dist/server/modules/e2ee/services/e2ee-device-store.service.d.ts +2 -0
- package/dist/server/modules/e2ee/services/e2ee-device-store.service.js +26 -0
- package/dist/server/modules/e2ee/services/e2ee-device-store.service.js.map +1 -1
- package/dist/server/modules/e2ee/services/e2ee-trust.service.d.ts +5 -0
- package/dist/server/modules/e2ee/services/e2ee-trust.service.js +11 -0
- package/dist/server/modules/e2ee/services/e2ee-trust.service.js.map +1 -1
- package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.d.ts +1 -0
- package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.js +4 -0
- package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.js.map +1 -1
- package/dist/server/modules/epic-time/controllers/epic-time.controller.d.ts +8 -0
- package/dist/server/modules/epic-time/controllers/epic-time.controller.js +70 -0
- package/dist/server/modules/epic-time/controllers/epic-time.controller.js.map +1 -0
- package/dist/server/modules/epic-time/epic-time.module.d.ts +2 -0
- package/dist/server/modules/epic-time/epic-time.module.js +28 -0
- package/dist/server/modules/epic-time/epic-time.module.js.map +1 -0
- package/dist/server/modules/epic-time/models/epic-time.models.d.ts +26 -0
- package/dist/server/modules/epic-time/models/epic-time.models.js +3 -0
- package/dist/server/modules/epic-time/models/epic-time.models.js.map +1 -0
- package/dist/server/modules/epic-time/services/agent-time-accounting.service.d.ts +32 -0
- package/dist/server/modules/epic-time/services/agent-time-accounting.service.js +159 -0
- package/dist/server/modules/epic-time/services/agent-time-accounting.service.js.map +1 -0
- package/dist/server/modules/epic-time/services/epic-time.service.d.ts +15 -0
- package/dist/server/modules/epic-time/services/epic-time.service.js +224 -0
- package/dist/server/modules/epic-time/services/epic-time.service.js.map +1 -0
- package/dist/server/modules/epic-time/services/epic-time.store.d.ts +75 -0
- package/dist/server/modules/epic-time/services/epic-time.store.js +453 -0
- package/dist/server/modules/epic-time/services/epic-time.store.js.map +1 -0
- package/dist/server/modules/epics/controllers/epics.controller.d.ts +10 -0
- package/dist/server/modules/epics/controllers/epics.controller.js +92 -0
- package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
- package/dist/server/modules/epics/services/epics.service.d.ts +32 -2
- package/dist/server/modules/epics/services/epics.service.js +243 -96
- package/dist/server/modules/epics/services/epics.service.js.map +1 -1
- package/dist/server/modules/events/catalog/agent.created.d.ts +4 -4
- package/dist/server/modules/events/catalog/agent.deleted.d.ts +2 -2
- package/dist/server/modules/events/catalog/agent.message.sent.d.ts +40 -40
- package/dist/server/modules/events/catalog/broadcast-registry.js +1 -0
- package/dist/server/modules/events/catalog/broadcast-registry.js.map +1 -1
- package/dist/server/modules/events/catalog/claude.hooks.ask_user_question.pending.d.ts +4 -4
- package/dist/server/modules/events/catalog/claude.hooks.session.started.d.ts +10 -10
- package/dist/server/modules/events/catalog/epic.comment.created.d.ts +6 -6
- package/dist/server/modules/events/catalog/epic.created.d.ts +8 -8
- package/dist/server/modules/events/catalog/epic.deleted.d.ts +4 -4
- package/dist/server/modules/events/catalog/epic.updated.d.ts +68 -42
- package/dist/server/modules/events/catalog/epic.updated.js +5 -0
- package/dist/server/modules/events/catalog/epic.updated.js.map +1 -1
- package/dist/server/modules/events/catalog/guest.registered.d.ts +2 -2
- package/dist/server/modules/events/catalog/guest.unregistered.d.ts +4 -4
- package/dist/server/modules/events/catalog/index.d.ts +370 -228
- package/dist/server/modules/events/catalog/index.js +11 -1
- package/dist/server/modules/events/catalog/index.js.map +1 -1
- package/dist/server/modules/events/catalog/integration.connection.created.d.ts +30 -0
- package/dist/server/modules/events/catalog/integration.connection.created.js +17 -0
- package/dist/server/modules/events/catalog/integration.connection.created.js.map +1 -0
- package/dist/server/modules/events/catalog/integration.connection.deleted.d.ts +27 -0
- package/dist/server/modules/events/catalog/integration.connection.deleted.js +16 -0
- package/dist/server/modules/events/catalog/integration.connection.deleted.js.map +1 -0
- package/dist/server/modules/events/catalog/integration.connection.updated.d.ts +39 -0
- package/dist/server/modules/events/catalog/integration.connection.updated.js +20 -0
- package/dist/server/modules/events/catalog/integration.connection.updated.js.map +1 -0
- package/dist/server/modules/events/catalog/review.comment.created.d.ts +16 -16
- package/dist/server/modules/events/catalog/review.comment.deleted.d.ts +2 -2
- package/dist/server/modules/events/catalog/review.comment.resolved.d.ts +2 -2
- package/dist/server/modules/events/catalog/review.comment.updated.d.ts +4 -4
- package/dist/server/modules/events/catalog/review.created.d.ts +12 -12
- package/dist/server/modules/events/catalog/review.updated.d.ts +22 -22
- package/dist/server/modules/events/catalog/scheduled-epic.executed.d.ts +12 -12
- package/dist/server/modules/events/catalog/session.human-prompt-state-changed.d.ts +77 -0
- package/dist/server/modules/events/catalog/session.human-prompt-state-changed.js +25 -0
- package/dist/server/modules/events/catalog/session.human-prompt-state-changed.js.map +1 -0
- package/dist/server/modules/events/catalog/session.provider-session-id.discovered.d.ts +2 -2
- package/dist/server/modules/events/catalog/session.recommendation.d.ts +4 -4
- package/dist/server/modules/events/catalog/session.restored.d.ts +2 -2
- package/dist/server/modules/events/catalog/session.started.d.ts +2 -2
- package/dist/server/modules/events/catalog/session.starting.d.ts +2 -2
- package/dist/server/modules/events/catalog/session.stopped.d.ts +2 -2
- package/dist/server/modules/events/catalog/session.transcript.discovered.d.ts +4 -4
- package/dist/server/modules/events/catalog/team.config.updated.d.ts +6 -6
- package/dist/server/modules/events/catalog/team.member.added.d.ts +2 -2
- package/dist/server/modules/events/catalog/team.member.removed.d.ts +2 -2
- package/dist/server/modules/events/catalog/terminal.watcher.triggered.d.ts +4 -4
- package/dist/server/modules/events/events-core.module.js +8 -1
- package/dist/server/modules/events/events-core.module.js.map +1 -1
- package/dist/server/modules/events/events-infra.module.js +6 -1
- package/dist/server/modules/events/events-infra.module.js.map +1 -1
- package/dist/server/modules/events/services/committed-event.store.d.ts +23 -0
- package/dist/server/modules/events/services/committed-event.store.js +169 -0
- package/dist/server/modules/events/services/committed-event.store.js.map +1 -0
- package/dist/server/modules/events/services/durable-event-dispatcher.service.d.ts +16 -0
- package/dist/server/modules/events/services/durable-event-dispatcher.service.js +97 -0
- package/dist/server/modules/events/services/durable-event-dispatcher.service.js.map +1 -0
- package/dist/server/modules/events/services/durable-event-registry.service.d.ts +25 -0
- package/dist/server/modules/events/services/durable-event-registry.service.js +59 -0
- package/dist/server/modules/events/services/durable-event-registry.service.js.map +1 -0
- package/dist/server/modules/events/services/event-log.service.d.ts +6 -1
- package/dist/server/modules/events/services/event-log.service.js +32 -18
- package/dist/server/modules/events/services/event-log.service.js.map +1 -1
- package/dist/server/modules/events/services/events.service.d.ts +11 -1
- package/dist/server/modules/events/services/events.service.js +50 -7
- package/dist/server/modules/events/services/events.service.js.map +1 -1
- package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.d.ts +97 -0
- package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js +1404 -0
- package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js.map +1 -0
- package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.d.ts +113 -0
- package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js +1790 -0
- package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js.map +1 -0
- package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.d.ts +10 -0
- package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.js +97 -0
- package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.js.map +1 -0
- package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.d.ts +16 -0
- package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.js +386 -0
- package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.js.map +1 -0
- package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.d.ts +5 -0
- package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.js +229 -0
- package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.js.map +1 -0
- package/dist/server/modules/external-integrations/adapters/vendor-shared.d.ts +42 -0
- package/dist/server/modules/external-integrations/adapters/vendor-shared.js +182 -0
- package/dist/server/modules/external-integrations/adapters/vendor-shared.js.map +1 -0
- package/dist/server/modules/external-integrations/connections/integration-connections.controller.d.ts +11 -0
- package/dist/server/modules/external-integrations/connections/integration-connections.controller.js +121 -0
- package/dist/server/modules/external-integrations/connections/integration-connections.controller.js.map +1 -0
- package/dist/server/modules/external-integrations/connections/integration-connections.service.d.ts +46 -0
- package/dist/server/modules/external-integrations/connections/integration-connections.service.js +193 -0
- package/dist/server/modules/external-integrations/connections/integration-connections.service.js.map +1 -0
- package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.d.ts +8 -0
- package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js +73 -0
- package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js.map +1 -0
- package/dist/server/modules/external-integrations/errors/external-provider.errors.d.ts +23 -0
- package/dist/server/modules/external-integrations/errors/external-provider.errors.js +105 -0
- package/dist/server/modules/external-integrations/errors/external-provider.errors.js.map +1 -0
- package/dist/server/modules/external-integrations/external-integrations.module.d.ts +2 -0
- package/dist/server/modules/external-integrations/external-integrations.module.js +72 -0
- package/dist/server/modules/external-integrations/external-integrations.module.js.map +1 -0
- package/dist/server/modules/external-integrations/external-task-provider.registry.d.ts +12 -0
- package/dist/server/modules/external-integrations/external-task-provider.registry.js +65 -0
- package/dist/server/modules/external-integrations/external-task-provider.registry.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-comment-lookup-token.d.ts +12 -0
- package/dist/server/modules/external-integrations/models/external-comment-lookup-token.js +49 -0
- package/dist/server/modules/external-integrations/models/external-comment-lookup-token.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-edit-session.models.d.ts +94 -0
- package/dist/server/modules/external-integrations/models/external-edit-session.models.js +10 -0
- package/dist/server/modules/external-integrations/models/external-edit-session.models.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-provider.models.d.ts +341 -0
- package/dist/server/modules/external-integrations/models/external-provider.models.js +21 -0
- package/dist/server/modules/external-integrations/models/external-provider.models.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-rich-capabilities.d.ts +6 -0
- package/dist/server/modules/external-integrations/models/external-rich-capabilities.js +9 -0
- package/dist/server/modules/external-integrations/models/external-rich-capabilities.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-rich-document.d.ts +59 -0
- package/dist/server/modules/external-integrations/models/external-rich-document.js +233 -0
- package/dist/server/modules/external-integrations/models/external-rich-document.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-task-source.d.ts +4 -0
- package/dist/server/modules/external-integrations/models/external-task-source.js +71 -0
- package/dist/server/modules/external-integrations/models/external-task-source.js.map +1 -0
- package/dist/server/modules/external-integrations/models/external-time-mutation.models.d.ts +74 -0
- package/dist/server/modules/external-integrations/models/external-time-mutation.models.js +19 -0
- package/dist/server/modules/external-integrations/models/external-time-mutation.models.js.map +1 -0
- package/dist/server/modules/external-integrations/my-work/external-edit-session.service.d.ts +57 -0
- package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js +776 -0
- package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js.map +1 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.controller.d.ts +37 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js +626 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js.map +1 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.d.ts +29 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js +847 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js.map +1 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.service.d.ts +34 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.service.js +330 -0
- package/dist/server/modules/external-integrations/my-work/external-my-work.service.js.map +1 -0
- package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.d.ts +27 -0
- package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js +412 -0
- package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js.map +1 -0
- package/dist/server/modules/external-integrations/ports/external-task-provider.d.ts +13 -0
- package/dist/server/modules/external-integrations/ports/external-task-provider.js +5 -0
- package/dist/server/modules/external-integrations/ports/external-task-provider.js.map +1 -0
- package/dist/server/modules/external-integrations/request-validation.d.ts +2 -0
- package/dist/server/modules/external-integrations/request-validation.js +14 -0
- package/dist/server/modules/external-integrations/request-validation.js.map +1 -0
- package/dist/server/modules/external-integrations/sessions/external-edit-session.store.d.ts +59 -0
- package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js +329 -0
- package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js.map +1 -0
- package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.d.ts +44 -0
- package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js +171 -0
- package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js.map +1 -0
- package/dist/server/modules/external-integrations/sessions/provider-operation-gate.d.ts +7 -0
- package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js +33 -0
- package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js.map +1 -0
- package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.d.ts +69 -0
- package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js +1092 -0
- package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js.map +1 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.d.ts +8 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js +23 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js.map +1 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.d.ts +7 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.js +30 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.js.map +1 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.d.ts +44 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js +135 -0
- package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js.map +1 -0
- package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.d.ts +47 -0
- package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.js +294 -0
- package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.js.map +1 -0
- package/dist/server/modules/hooks/dtos/hook-event.dto.d.ts +64 -64
- package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +36 -36
- package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.d.ts +5 -0
- package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js +51 -1
- package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js.map +1 -1
- package/dist/server/modules/orchestrator/sync/services/task-merge.service.d.ts +0 -2
- package/dist/server/modules/orchestrator/sync/services/task-merge.service.js +9 -48
- package/dist/server/modules/orchestrator/sync/services/task-merge.service.js.map +1 -1
- package/dist/server/modules/profiles/dto.d.ts +11 -11
- package/dist/server/modules/projects/controllers/projects.controller.d.ts +2 -2
- package/dist/server/modules/projects/dtos/export.dto.d.ts +6 -6
- package/dist/server/modules/projects/helpers/project-export.d.ts +1 -1
- package/dist/server/modules/projects/services/projects.service.d.ts +1 -1
- package/dist/server/modules/projects/template-codec/codecs/watchers.codec.d.ts +1 -1
- package/dist/server/modules/registry/controllers/templates.controller.d.ts +7 -7
- package/dist/server/modules/reviews/dtos/review.dto.d.ts +10 -10
- package/dist/server/modules/scheduled-epics/dtos/scheduled-epic.dto.d.ts +1 -1
- package/dist/server/modules/session-reader/adapters/antigravity-session-reader.adapter.d.ts +1 -1
- package/dist/server/modules/session-reader/adapters/opencode-session-reader.adapter.d.ts +1 -1
- package/dist/server/modules/session-reader/data/pricing.json +128 -39
- package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.d.ts +2 -0
- package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.js +18 -12
- package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.js.map +1 -1
- package/dist/server/modules/sessions/controllers/sessions.controller.d.ts +3 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.js +30 -0
- package/dist/server/modules/sessions/controllers/sessions.controller.js.map +1 -1
- package/dist/server/modules/sessions/dtos/sessions.dto.d.ts +2 -2
- package/dist/server/modules/sessions/services/message-enqueue.service.d.ts +2 -0
- package/dist/server/modules/sessions/services/message-enqueue.service.js +2 -0
- package/dist/server/modules/sessions/services/message-enqueue.service.js.map +1 -1
- package/dist/server/modules/sessions/services/message-pool.types.d.ts +8 -1
- package/dist/server/modules/sessions/services/sessions-message-pool.service.d.ts +50 -6
- package/dist/server/modules/sessions/services/sessions-message-pool.service.js +828 -190
- package/dist/server/modules/sessions/services/sessions-message-pool.service.js.map +1 -1
- package/dist/server/modules/settings/dtos/settings.dto.d.ts +8 -0
- package/dist/server/modules/settings/dtos/settings.dto.js +1 -0
- package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -1
- package/dist/server/modules/settings/local/delegates/core-settings.delegate.js +12 -0
- package/dist/server/modules/settings/local/delegates/core-settings.delegate.js.map +1 -1
- package/dist/server/modules/settings/services/settings.constants.d.ts +1 -1
- package/dist/server/modules/settings/services/settings.constants.js +2 -1
- package/dist/server/modules/settings/services/settings.constants.js.map +1 -1
- package/dist/server/modules/settings/services/settings.service.d.ts +1 -1
- package/dist/server/modules/settings/services/settings.service.js +2 -1
- package/dist/server/modules/settings/services/settings.service.js.map +1 -1
- package/dist/server/modules/skills/dtos/skill.dto.d.ts +2 -2
- package/dist/server/modules/storage/db/schema.d.ts +2420 -958
- package/dist/server/modules/storage/db/schema.js +144 -2
- package/dist/server/modules/storage/db/schema.js.map +1 -1
- package/dist/server/modules/storage/interfaces/storage.interface.d.ts +34 -6
- package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
- package/dist/server/modules/storage/local/delegates/epic.delegate.d.ts +9 -5
- package/dist/server/modules/storage/local/delegates/epic.delegate.js +67 -30
- package/dist/server/modules/storage/local/delegates/epic.delegate.js.map +1 -1
- package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.d.ts +18 -0
- package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js +320 -0
- package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js.map +1 -0
- package/dist/server/modules/storage/local/delegates/integration.delegate.d.ts +37 -0
- package/dist/server/modules/storage/local/delegates/integration.delegate.js +308 -0
- package/dist/server/modules/storage/local/delegates/integration.delegate.js.map +1 -0
- package/dist/server/modules/storage/local/integration-credential-cipher.d.ts +18 -0
- package/dist/server/modules/storage/local/integration-credential-cipher.js +138 -0
- package/dist/server/modules/storage/local/integration-credential-cipher.js.map +1 -0
- package/dist/server/modules/storage/local/local-storage.service.d.ts +36 -7
- package/dist/server/modules/storage/local/local-storage.service.js +93 -10
- package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
- package/dist/server/modules/storage/models/domain.models.d.ts +127 -0
- package/dist/server/modules/storage/models/domain.models.js +15 -0
- package/dist/server/modules/storage/models/domain.models.js.map +1 -1
- package/dist/server/modules/storage/storage.module.js +2 -1
- package/dist/server/modules/storage/storage.module.js.map +1 -1
- package/dist/server/modules/subscribers/dtos/subscriber.dto.d.ts +1 -1
- package/dist/server/modules/terminal/dtos/ws-envelope.dto.d.ts +2 -2
- package/dist/server/modules/terminal/gateways/terminal.gateway.d.ts +9 -1
- package/dist/server/modules/terminal/gateways/terminal.gateway.js +79 -3
- package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -1
- package/dist/server/modules/terminal/services/human-prompt-state.service.d.ts +58 -0
- package/dist/server/modules/terminal/services/human-prompt-state.service.js +206 -0
- package/dist/server/modules/terminal/services/human-prompt-state.service.js.map +1 -0
- package/dist/server/modules/terminal/services/terminal-activity.service.js +7 -18
- package/dist/server/modules/terminal/services/terminal-activity.service.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-io/delivery.d.ts +2 -1
- package/dist/server/modules/terminal/services/terminal-io/delivery.js +77 -33
- package/dist/server/modules/terminal/services/terminal-io/delivery.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.d.ts +14 -5
- package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.js +64 -8
- package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-io/types.d.ts +8 -0
- package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.d.ts +5 -1
- package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.js +39 -2
- package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.d.ts +3 -1
- package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.js +7 -2
- package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-session/terminal-session.d.ts +4 -0
- package/dist/server/modules/terminal/services/terminal-session/terminal-session.js +8 -0
- package/dist/server/modules/terminal/services/terminal-session/terminal-session.js.map +1 -1
- package/dist/server/modules/terminal/terminal-delivery.module.js +13 -2
- package/dist/server/modules/terminal/terminal-delivery.module.js.map +1 -1
- package/dist/server/modules/terminal/terminal.module.js +4 -3
- package/dist/server/modules/terminal/terminal.module.js.map +1 -1
- package/dist/server/modules/terminal/utils/terminal-activity.d.ts +4 -0
- package/dist/server/modules/terminal/utils/terminal-activity.js +86 -0
- package/dist/server/modules/terminal/utils/terminal-activity.js.map +1 -0
- package/dist/server/modules/watchers/dtos/watcher.dto.d.ts +8 -8
- package/dist/server/templates/teams-dev.json +9 -9
- package/dist/server/tsconfig.tsbuildinfo +1 -1
- package/dist/server/ui/assets/ExternalRichEditor-DMtnZIMZ.js +113 -0
- package/dist/server/ui/assets/{ReviewDetailPage-CNffXKv8.js → ReviewDetailPage-BmyiM5IR.js} +1 -1
- package/dist/server/ui/assets/{ReviewsPage-DAcufZ3r.js → ReviewsPage-CQ87BMwT.js} +1 -1
- package/dist/server/ui/assets/index-BX6Yze-A.js +1177 -0
- package/dist/server/ui/assets/index-qoQEv_u4.css +32 -0
- package/dist/server/ui/assets/{useReviewSubscription-DD29I7uU.js → useReviewSubscription-DjbEUzt4.js} +1 -1
- package/dist/server/ui/index.html +2 -2
- package/dist/templates/teams-dev.json +9 -9
- package/package.json +15 -4
- package/dist/server/ui/assets/index-7iCs8Dxj.js +0 -1153
- package/dist/server/ui/assets/index-CLXZjL_B.css +0 -32
|
@@ -12,7 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.SessionsMessagePoolService = exports.FAILURE_NOTICE_SOURCE = void 0;
|
|
15
|
+
exports.SessionsMessagePoolService = exports.HUMAN_DRAFT_IDLE_GRACE_MS = exports.FAILURE_NOTICE_SOURCE = void 0;
|
|
16
16
|
const common_1 = require("@nestjs/common");
|
|
17
17
|
const event_emitter_1 = require("@nestjs/event-emitter");
|
|
18
18
|
const crypto_1 = require("crypto");
|
|
@@ -27,10 +27,20 @@ const logger_1 = require("../../../common/logging/logger");
|
|
|
27
27
|
const terminal_io_service_1 = require("../../terminal/services/terminal-io/terminal-io.service");
|
|
28
28
|
const provider_adapter_factory_1 = require("../../providers/adapters/provider-adapter.factory");
|
|
29
29
|
const message_pool_types_1 = require("./message-pool.types");
|
|
30
|
+
const session_human_prompt_state_changed_1 = require("../../events/catalog/session.human-prompt-state-changed");
|
|
31
|
+
const human_prompt_state_service_1 = require("../../terminal/services/human-prompt-state.service");
|
|
30
32
|
const delivery_failure_disclosure_1 = require("./delivery-failure-disclosure");
|
|
31
33
|
var message_pool_types_2 = require("./message-pool.types");
|
|
32
34
|
Object.defineProperty(exports, "FAILURE_NOTICE_SOURCE", { enumerable: true, get: function () { return message_pool_types_2.FAILURE_NOTICE_SOURCE; } });
|
|
33
35
|
const logger = (0, logger_1.createLogger)('SessionsMessagePoolService');
|
|
36
|
+
exports.HUMAN_DRAFT_IDLE_GRACE_MS = 2_000;
|
|
37
|
+
function canStartDeferredClaimMutation(claim) {
|
|
38
|
+
return claim.state.phase === 'preparing' && !claim.state.cancellationReason;
|
|
39
|
+
}
|
|
40
|
+
function deferredClaimCancellationResult(claim, discardedCount) {
|
|
41
|
+
const reason = claim?.state.phase === 'preparing' ? claim.state.cancellationReason : undefined;
|
|
42
|
+
return reason ? { success: false, discardedCount, reason } : null;
|
|
43
|
+
}
|
|
34
44
|
const DEFAULT_CONFIG = {
|
|
35
45
|
enabled: true,
|
|
36
46
|
delayMs: 10000,
|
|
@@ -48,10 +58,12 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
48
58
|
providerAdapterFactory;
|
|
49
59
|
messageLog;
|
|
50
60
|
failureNotifier;
|
|
61
|
+
humanPromptState;
|
|
51
62
|
pools = new Map();
|
|
52
|
-
|
|
63
|
+
deferredLanes = new Map();
|
|
53
64
|
config;
|
|
54
|
-
|
|
65
|
+
closing = false;
|
|
66
|
+
constructor(sessions, coordinator, terminalIO, settings, storage, activityStream, providerAdapterFactory, messageLog, failureNotifier, humanPromptState) {
|
|
55
67
|
this.sessions = sessions;
|
|
56
68
|
this.coordinator = coordinator;
|
|
57
69
|
this.terminalIO = terminalIO;
|
|
@@ -61,6 +73,7 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
61
73
|
this.providerAdapterFactory = providerAdapterFactory;
|
|
62
74
|
this.messageLog = messageLog;
|
|
63
75
|
this.failureNotifier = failureNotifier;
|
|
76
|
+
this.humanPromptState = humanPromptState;
|
|
64
77
|
this.config = this.loadConfigFromSettings();
|
|
65
78
|
logger.info({ config: this.config }, 'SessionsMessagePoolService initialized with config');
|
|
66
79
|
}
|
|
@@ -156,12 +169,14 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
156
169
|
const logEntryId = (0, crypto_1.randomUUID)();
|
|
157
170
|
const timestamp = Date.now();
|
|
158
171
|
const deliveryMode = this.resolveDeliveryMode(options);
|
|
159
|
-
if (deliveryMode === 'on_idle') {
|
|
160
|
-
return this.
|
|
172
|
+
if (options.deferWhileHumanTyping || options.humanPromptSubmit || deliveryMode === 'on_idle') {
|
|
173
|
+
return this.enqueueSerialized({
|
|
161
174
|
agentId,
|
|
162
175
|
text,
|
|
163
176
|
source,
|
|
164
177
|
submitKeys,
|
|
178
|
+
preKeys,
|
|
179
|
+
preDelayMs,
|
|
165
180
|
senderAgentId,
|
|
166
181
|
clientMessageId,
|
|
167
182
|
failureDisclosure,
|
|
@@ -169,6 +184,9 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
169
184
|
agentName,
|
|
170
185
|
logEntryId,
|
|
171
186
|
timestamp,
|
|
187
|
+
deliveryMode,
|
|
188
|
+
deferWhileHumanTyping: options.deferWhileHumanTyping === true,
|
|
189
|
+
humanPromptSubmit: options.humanPromptSubmit === true,
|
|
172
190
|
});
|
|
173
191
|
}
|
|
174
192
|
const projectConfig = this.getConfigForProject(projectId, failureDisclosure);
|
|
@@ -342,48 +360,79 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
342
360
|
}, pool.config.delayMs);
|
|
343
361
|
return { status: 'queued', poolSize: pool.messages.length, logEntryId };
|
|
344
362
|
}
|
|
345
|
-
async
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
+
async enqueueSerialized(input) {
|
|
364
|
+
while (true) {
|
|
365
|
+
const step = await this.coordinator.withAgentLock(input.agentId, async () => {
|
|
366
|
+
const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
|
|
367
|
+
const existingLane = this.deferredLanes.get(input.agentId);
|
|
368
|
+
if (existingLane && existingLane.sessionId !== activeSession?.id) {
|
|
369
|
+
return {
|
|
370
|
+
detachment: this.detachDeferredLaneUnderAgentLock(existingLane, 'Target session was replaced before deferred delivery'),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const result = await this.enqueueSerializedUnderAgentLock(input);
|
|
374
|
+
const claim = result.status === 'queued' &&
|
|
375
|
+
input.deliveryMode === 'on_idle' &&
|
|
376
|
+
activeSession?.activityState === 'idle'
|
|
377
|
+
? this.claimDeferredLaneUnderAgentLock(input.agentId, activeSession.id, false)
|
|
378
|
+
: null;
|
|
379
|
+
return { result, claim };
|
|
380
|
+
});
|
|
381
|
+
if ('result' in step) {
|
|
382
|
+
if (!step.claim)
|
|
383
|
+
return step.result;
|
|
384
|
+
const flushResult = await this.deliverDeferredClaim(step.claim);
|
|
385
|
+
if (!flushResult.success) {
|
|
386
|
+
return { status: 'failed', error: flushResult.reason, logEntryId: input.logEntryId };
|
|
387
|
+
}
|
|
388
|
+
if (flushResult.deliveredCount === 0) {
|
|
389
|
+
const logEntry = this.messageLog.getById(input.logEntryId);
|
|
390
|
+
return {
|
|
391
|
+
status: logEntry?.status ?? 'queued',
|
|
392
|
+
...(logEntry?.error ? { error: logEntry.error } : {}),
|
|
393
|
+
logEntryId: input.logEntryId,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
status: flushResult.outcome === 'unconfirmed' ? 'unconfirmed' : 'delivered',
|
|
398
|
+
logEntryId: input.logEntryId,
|
|
399
|
+
};
|
|
363
400
|
}
|
|
364
|
-
|
|
401
|
+
await this.settleDeferredLaneDetachment(step.detachment);
|
|
365
402
|
}
|
|
403
|
+
}
|
|
404
|
+
async enqueueSerializedUnderAgentLock(input) {
|
|
405
|
+
const duplicate = this.findDuplicate(input);
|
|
406
|
+
if (duplicate)
|
|
407
|
+
return duplicate;
|
|
366
408
|
const projectConfig = this.getConfigForProject(input.projectId, input.failureDisclosure);
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
409
|
+
const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
|
|
410
|
+
const promptState = activeSession?.tmuxSessionId
|
|
411
|
+
? this.humanPromptState.getState(activeSession.tmuxSessionId)
|
|
412
|
+
: null;
|
|
413
|
+
const heldGeneration = input.deferWhileHumanTyping && promptState && promptState.phase !== 'inactive'
|
|
414
|
+
? promptState.generation
|
|
415
|
+
: undefined;
|
|
416
|
+
if (heldGeneration !== undefined || input.deliveryMode === 'on_idle') {
|
|
417
|
+
return this.enqueueDeferredUnderAgentLock(input, projectConfig, activeSession, heldGeneration);
|
|
374
418
|
}
|
|
375
|
-
if (!
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
firstEnqueueTime: input.timestamp,
|
|
382
|
-
separator: projectConfig.separator,
|
|
383
|
-
};
|
|
384
|
-
this.idleLanes.set(input.agentId, lane);
|
|
419
|
+
if (input.humanPromptSubmit || input.deliveryMode === 'immediate' || !projectConfig.enabled) {
|
|
420
|
+
const logEntry = this.createLogEntry(input, true);
|
|
421
|
+
this.messageLog.addEntry(logEntry);
|
|
422
|
+
this.activityStream.broadcastEnqueued(logEntry);
|
|
423
|
+
this.broadcastPoolsUpdate();
|
|
424
|
+
return this.deliverImmediateUnderAgentLock(input, logEntry);
|
|
385
425
|
}
|
|
386
|
-
|
|
426
|
+
return this.enqueueProtectedPoolUnderAgentLock(input, projectConfig);
|
|
427
|
+
}
|
|
428
|
+
findDuplicate(input) {
|
|
429
|
+
if (!input.clientMessageId)
|
|
430
|
+
return null;
|
|
431
|
+
const existing = this.messageLog.findByClientMessageId(input.clientMessageId, input.agentId, input.source);
|
|
432
|
+
return existing ? { status: existing.status, logEntryId: existing.id } : null;
|
|
433
|
+
}
|
|
434
|
+
createLogEntry(input, immediate) {
|
|
435
|
+
return {
|
|
387
436
|
id: input.logEntryId,
|
|
388
437
|
timestamp: input.timestamp,
|
|
389
438
|
projectId: input.projectId,
|
|
@@ -394,10 +443,11 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
394
443
|
senderAgentId: input.senderAgentId,
|
|
395
444
|
clientMessageId: input.clientMessageId,
|
|
396
445
|
status: 'queued',
|
|
397
|
-
immediate
|
|
446
|
+
immediate,
|
|
398
447
|
};
|
|
399
|
-
|
|
400
|
-
|
|
448
|
+
}
|
|
449
|
+
createPooledMessage(input, options) {
|
|
450
|
+
return {
|
|
401
451
|
text: input.text,
|
|
402
452
|
source: input.source,
|
|
403
453
|
timestamp: input.timestamp,
|
|
@@ -406,60 +456,192 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
406
456
|
logEntryId: input.logEntryId,
|
|
407
457
|
clientMessageId: input.clientMessageId,
|
|
408
458
|
failureDisclosure: input.failureDisclosure,
|
|
409
|
-
|
|
459
|
+
deferWhileHumanTyping: input.deferWhileHumanTyping,
|
|
460
|
+
requiresProviderIdle: options?.requiresProviderIdle === true,
|
|
461
|
+
heldGeneration: options?.heldGeneration,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
async enqueueProtectedPoolUnderAgentLock(input, projectConfig) {
|
|
465
|
+
if (this.getSharedQueuedCount(input.agentId) >= projectConfig.maxMessages) {
|
|
466
|
+
return this.capacityFailure(input.failureDisclosure);
|
|
467
|
+
}
|
|
468
|
+
let pool = this.pools.get(input.agentId);
|
|
469
|
+
if (!pool) {
|
|
470
|
+
pool = this.createAgentPool(input.agentId, input.projectId, projectConfig);
|
|
471
|
+
}
|
|
472
|
+
else if (!this.configsEqual(pool.config, projectConfig)) {
|
|
473
|
+
pool.config = projectConfig;
|
|
474
|
+
if (pool.messages.length > 0)
|
|
475
|
+
this.resetPoolTimers(input.agentId, pool, projectConfig);
|
|
476
|
+
}
|
|
477
|
+
const logEntry = this.createLogEntry(input, false);
|
|
478
|
+
this.messageLog.addEntry(logEntry);
|
|
479
|
+
pool.messages.push(this.createPooledMessage(input));
|
|
410
480
|
this.activityStream.broadcastEnqueued(logEntry);
|
|
411
481
|
this.broadcastPoolsUpdate();
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
const [, flushResult] = await Promise.all([
|
|
418
|
-
detachedFailure,
|
|
419
|
-
this.flushIdleLane(input.agentId, activeSession.id),
|
|
420
|
-
]);
|
|
421
|
-
if (!flushResult.success) {
|
|
422
|
-
return { status: 'failed', error: flushResult.reason, logEntryId: input.logEntryId };
|
|
423
|
-
}
|
|
424
|
-
if (flushResult.deliveredCount === 0) {
|
|
425
|
-
const logEntry = this.messageLog.getById(input.logEntryId);
|
|
482
|
+
if (pool.messages.length >= pool.config.maxMessages) {
|
|
483
|
+
const result = await this.flushPoolUnderAgentLock(input.agentId);
|
|
484
|
+
if (!result.success)
|
|
485
|
+
return { status: 'failed', error: result.reason };
|
|
486
|
+
if (result.outcome === 'deferred') {
|
|
426
487
|
return {
|
|
427
|
-
status:
|
|
428
|
-
|
|
488
|
+
status: 'queued',
|
|
489
|
+
poolSize: this.getSharedQueuedCount(input.agentId),
|
|
429
490
|
logEntryId: input.logEntryId,
|
|
430
491
|
};
|
|
431
492
|
}
|
|
432
493
|
return {
|
|
433
|
-
status:
|
|
494
|
+
status: result.outcome === 'unconfirmed' ? 'unconfirmed' : 'delivered',
|
|
434
495
|
logEntryId: input.logEntryId,
|
|
435
496
|
};
|
|
436
497
|
}
|
|
437
|
-
|
|
438
|
-
return {
|
|
498
|
+
this.schedulePoolDebounce(input.agentId, pool);
|
|
499
|
+
return {
|
|
500
|
+
status: 'queued',
|
|
501
|
+
poolSize: this.getSharedQueuedCount(input.agentId),
|
|
502
|
+
logEntryId: input.logEntryId,
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
async enqueueDeferredUnderAgentLock(input, projectConfig, activeSession, heldGeneration) {
|
|
506
|
+
if (!activeSession?.tmuxSessionId) {
|
|
507
|
+
const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(input.failureDisclosure, 'No active session', 'no_active_session');
|
|
508
|
+
return { status: 'failed', error: failure.error };
|
|
509
|
+
}
|
|
510
|
+
if (this.getSharedQueuedCount(input.agentId) >= projectConfig.maxMessages) {
|
|
511
|
+
return this.capacityFailure(input.failureDisclosure);
|
|
512
|
+
}
|
|
513
|
+
let lane = this.deferredLanes.get(input.agentId);
|
|
514
|
+
if (!lane) {
|
|
515
|
+
lane = {
|
|
516
|
+
sessionId: activeSession.id,
|
|
517
|
+
tmuxSessionName: activeSession.tmuxSessionId,
|
|
518
|
+
agentId: input.agentId,
|
|
519
|
+
projectId: input.projectId,
|
|
520
|
+
messages: [],
|
|
521
|
+
separator: projectConfig.separator,
|
|
522
|
+
requiredGeneration: heldGeneration ?? null,
|
|
523
|
+
quietTimer: null,
|
|
524
|
+
quietBaseline: null,
|
|
525
|
+
activeClaim: null,
|
|
526
|
+
};
|
|
527
|
+
this.deferredLanes.set(input.agentId, lane);
|
|
528
|
+
}
|
|
529
|
+
lane.separator = projectConfig.separator;
|
|
530
|
+
const logEntry = this.createLogEntry(input, input.deliveryMode === 'immediate' || !projectConfig.enabled);
|
|
531
|
+
const message = this.createPooledMessage(input, {
|
|
532
|
+
requiresProviderIdle: input.deliveryMode === 'on_idle',
|
|
533
|
+
heldGeneration,
|
|
534
|
+
});
|
|
535
|
+
this.messageLog.addEntry(logEntry);
|
|
536
|
+
lane.messages.push(message);
|
|
537
|
+
if (heldGeneration !== undefined)
|
|
538
|
+
this.rebindLaneGeneration(lane, heldGeneration);
|
|
539
|
+
this.activityStream.broadcastEnqueued(logEntry);
|
|
540
|
+
this.broadcastPoolsUpdate();
|
|
541
|
+
if (lane.requiredGeneration !== null) {
|
|
542
|
+
this.ensureHumanQuietTimerUnderAgentLock(lane);
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
status: 'queued',
|
|
546
|
+
poolSize: this.getSharedQueuedCount(input.agentId),
|
|
547
|
+
logEntryId: input.logEntryId,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
capacityFailure(failureDisclosure) {
|
|
551
|
+
const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(failureDisclosure, 'Message pool capacity reached', 'pool_capacity_exceeded');
|
|
552
|
+
return { status: 'failed', error: failure.error };
|
|
553
|
+
}
|
|
554
|
+
getSharedQueuedCount(agentId) {
|
|
555
|
+
return ((this.pools.get(agentId)?.messages.length ?? 0) +
|
|
556
|
+
(this.deferredLanes.get(agentId)?.messages.length ?? 0));
|
|
557
|
+
}
|
|
558
|
+
createAgentPool(agentId, projectId, config) {
|
|
559
|
+
const pool = {
|
|
560
|
+
messages: [],
|
|
561
|
+
timer: null,
|
|
562
|
+
maxWaitTimer: null,
|
|
563
|
+
firstEnqueueTime: Date.now(),
|
|
564
|
+
config,
|
|
565
|
+
projectId,
|
|
566
|
+
};
|
|
567
|
+
this.pools.set(agentId, pool);
|
|
568
|
+
pool.maxWaitTimer = setTimeout(() => {
|
|
569
|
+
void this.flushNow(agentId);
|
|
570
|
+
}, config.maxWaitMs);
|
|
571
|
+
return pool;
|
|
572
|
+
}
|
|
573
|
+
schedulePoolDebounce(agentId, pool) {
|
|
574
|
+
if (pool.timer)
|
|
575
|
+
clearTimeout(pool.timer);
|
|
576
|
+
pool.timer = setTimeout(() => {
|
|
577
|
+
void this.flushNow(agentId);
|
|
578
|
+
}, pool.config.delayMs);
|
|
439
579
|
}
|
|
440
580
|
async flushNow(agentId) {
|
|
581
|
+
while (true) {
|
|
582
|
+
const step = await this.coordinator.withAgentLock(agentId, async () => {
|
|
583
|
+
const activeSession = this.sessions.getActiveSessionForAgent(agentId);
|
|
584
|
+
const existingLane = this.deferredLanes.get(agentId);
|
|
585
|
+
if (existingLane && existingLane.sessionId !== activeSession?.id) {
|
|
586
|
+
return {
|
|
587
|
+
detachment: this.detachDeferredLaneUnderAgentLock(existingLane, 'Target session was replaced before deferred delivery'),
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
return { result: await this.flushPoolUnderAgentLock(agentId) };
|
|
591
|
+
});
|
|
592
|
+
if ('result' in step)
|
|
593
|
+
return step.result;
|
|
594
|
+
await this.settleDeferredLaneDetachment(step.detachment);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async flushPoolUnderAgentLock(agentId) {
|
|
441
598
|
const pool = this.pools.get(agentId);
|
|
442
599
|
if (!pool || pool.messages.length === 0) {
|
|
443
|
-
logger.debug({ agentId }, 'No messages to flush');
|
|
444
600
|
return { success: true, deliveredCount: 0 };
|
|
445
601
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
602
|
+
this.clearPoolTimers(pool);
|
|
603
|
+
this.pools.delete(agentId);
|
|
604
|
+
const activeSession = this.sessions.getActiveSessionForAgent(agentId);
|
|
605
|
+
const promptState = activeSession?.tmuxSessionId
|
|
606
|
+
? this.humanPromptState.getState(activeSession.tmuxSessionId)
|
|
607
|
+
: null;
|
|
608
|
+
const protectedMessages = promptState?.phase !== 'inactive'
|
|
609
|
+
? pool.messages.filter((message) => message.deferWhileHumanTyping)
|
|
610
|
+
: [];
|
|
611
|
+
const deliverableMessages = protectedMessages.length > 0
|
|
612
|
+
? pool.messages.filter((message) => !message.deferWhileHumanTyping)
|
|
613
|
+
: [...pool.messages];
|
|
614
|
+
if (protectedMessages.length > 0 &&
|
|
615
|
+
activeSession?.tmuxSessionId &&
|
|
616
|
+
promptState &&
|
|
617
|
+
!this.closing) {
|
|
618
|
+
await this.moveMessagesToDeferredLaneUnderAgentLock(agentId, pool.projectId, pool.config.separator, activeSession, protectedMessages, promptState.generation);
|
|
449
619
|
}
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
|
|
620
|
+
else if (protectedMessages.length > 0 && this.closing) {
|
|
621
|
+
await this.failDeferredLane({
|
|
622
|
+
sessionId: activeSession?.id ?? 'shutdown',
|
|
623
|
+
tmuxSessionName: activeSession?.tmuxSessionId ?? 'shutdown',
|
|
624
|
+
agentId,
|
|
625
|
+
projectId: pool.projectId,
|
|
626
|
+
messages: protectedMessages,
|
|
627
|
+
separator: pool.config.separator,
|
|
628
|
+
requiredGeneration: promptState?.generation ?? null,
|
|
629
|
+
quietTimer: null,
|
|
630
|
+
quietBaseline: null,
|
|
631
|
+
activeClaim: null,
|
|
632
|
+
}, 'Service stopped before human-held delivery', false);
|
|
453
633
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
634
|
+
if (deliverableMessages.length === 0) {
|
|
635
|
+
this.broadcastPoolsUpdate();
|
|
636
|
+
return this.closing
|
|
637
|
+
? {
|
|
638
|
+
success: false,
|
|
639
|
+
discardedCount: protectedMessages.length,
|
|
640
|
+
reason: 'Service stopped before human-held delivery',
|
|
641
|
+
}
|
|
642
|
+
: { success: true, deliveredCount: 0, outcome: 'deferred' };
|
|
643
|
+
}
|
|
644
|
+
return this.deliverBatch(agentId, deliverableMessages, pool.config.separator);
|
|
463
645
|
}
|
|
464
646
|
async flushAll() {
|
|
465
647
|
const pendingPools = Array.from(this.pools.entries()).map(([agentId, pool]) => ({
|
|
@@ -473,14 +655,14 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
473
655
|
}
|
|
474
656
|
getPoolStats() {
|
|
475
657
|
const now = Date.now();
|
|
476
|
-
const agentIds = new Set([...this.pools.keys(), ...this.
|
|
658
|
+
const agentIds = new Set([...this.pools.keys(), ...this.deferredLanes.keys()]);
|
|
477
659
|
return Array.from(agentIds).map((agentId) => {
|
|
478
660
|
const pool = this.pools.get(agentId);
|
|
479
|
-
const
|
|
480
|
-
const oldestMessageTime = Math.min(pool?.messages[0]?.timestamp ?? Number.POSITIVE_INFINITY,
|
|
661
|
+
const deferredLane = this.deferredLanes.get(agentId);
|
|
662
|
+
const oldestMessageTime = Math.min(pool?.messages[0]?.timestamp ?? Number.POSITIVE_INFINITY, deferredLane?.messages[0]?.timestamp ?? Number.POSITIVE_INFINITY);
|
|
481
663
|
return {
|
|
482
664
|
agentId,
|
|
483
|
-
messageCount: (pool?.messages.length ?? 0) + (
|
|
665
|
+
messageCount: (pool?.messages.length ?? 0) + (deferredLane?.messages.length ?? 0),
|
|
484
666
|
waitingMs: now - oldestMessageTime,
|
|
485
667
|
};
|
|
486
668
|
});
|
|
@@ -489,16 +671,16 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
489
671
|
const now = Date.now();
|
|
490
672
|
const PREVIEW_LENGTH = 100;
|
|
491
673
|
const details = [];
|
|
492
|
-
const agentIds = new Set([...this.pools.keys(), ...this.
|
|
674
|
+
const agentIds = new Set([...this.pools.keys(), ...this.deferredLanes.keys()]);
|
|
493
675
|
for (const agentId of agentIds) {
|
|
494
676
|
const pool = this.pools.get(agentId);
|
|
495
|
-
const
|
|
496
|
-
const pooledMessages = [...(pool?.messages ?? []), ...(
|
|
677
|
+
const deferredLane = this.deferredLanes.get(agentId);
|
|
678
|
+
const pooledMessages = [...(pool?.messages ?? []), ...(deferredLane?.messages ?? [])].sort((a, b) => a.timestamp - b.timestamp);
|
|
497
679
|
if (pooledMessages.length === 0)
|
|
498
680
|
continue;
|
|
499
681
|
const firstMessage = pooledMessages[0];
|
|
500
682
|
const logEntry = this.messageLog.getById(firstMessage.logEntryId);
|
|
501
|
-
const poolProjectId = logEntry?.projectId ?? pool?.projectId ??
|
|
683
|
+
const poolProjectId = logEntry?.projectId ?? pool?.projectId ?? deferredLane?.projectId ?? 'unknown';
|
|
502
684
|
const agentName = logEntry?.agentName ?? 'unknown';
|
|
503
685
|
if (projectId && poolProjectId !== projectId)
|
|
504
686
|
continue;
|
|
@@ -511,22 +693,65 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
511
693
|
timestamp: msg.timestamp,
|
|
512
694
|
};
|
|
513
695
|
});
|
|
514
|
-
|
|
696
|
+
const humanHeldMessageCount = deferredLane && deferredLane.requiredGeneration !== null ? deferredLane.messages.length : 0;
|
|
697
|
+
const humanReleaseEligibleAt = humanHeldMessageCount > 0 && deferredLane
|
|
698
|
+
? this.humanPromptState.getManualReleaseEligibleAt(deferredLane.tmuxSessionName)
|
|
699
|
+
: null;
|
|
700
|
+
const detail = {
|
|
515
701
|
agentId,
|
|
516
702
|
agentName,
|
|
517
703
|
projectId: poolProjectId,
|
|
518
704
|
messageCount: pooledMessages.length,
|
|
705
|
+
humanHeldMessageCount,
|
|
519
706
|
waitingMs: now - firstMessage.timestamp,
|
|
520
707
|
messages,
|
|
521
|
-
}
|
|
708
|
+
};
|
|
709
|
+
if (humanReleaseEligibleAt !== null) {
|
|
710
|
+
detail.humanReleaseEligibleAt = humanReleaseEligibleAt;
|
|
711
|
+
}
|
|
712
|
+
details.push(detail);
|
|
522
713
|
}
|
|
523
714
|
details.sort((a, b) => b.waitingMs - a.waitingMs);
|
|
524
715
|
return details;
|
|
525
716
|
}
|
|
717
|
+
async releaseHumanHeldMessages(agentId, projectId) {
|
|
718
|
+
return this.coordinator.withAgentLock(agentId, async () => {
|
|
719
|
+
const lane = this.deferredLanes.get(agentId);
|
|
720
|
+
const activeSession = this.sessions.getActiveSessionForAgent(agentId);
|
|
721
|
+
if (!lane ||
|
|
722
|
+
lane.projectId !== projectId ||
|
|
723
|
+
!activeSession?.tmuxSessionId ||
|
|
724
|
+
activeSession.id !== lane.sessionId ||
|
|
725
|
+
activeSession.tmuxSessionId !== lane.tmuxSessionName ||
|
|
726
|
+
lane.requiredGeneration === null) {
|
|
727
|
+
return { status: 'not_found' };
|
|
728
|
+
}
|
|
729
|
+
const eligibleAt = this.humanPromptState.getManualReleaseEligibleAt(lane.tmuxSessionName);
|
|
730
|
+
if (eligibleAt === null || Date.now() < eligibleAt) {
|
|
731
|
+
return { status: 'not_ready', eligibleAt };
|
|
732
|
+
}
|
|
733
|
+
const state = this.humanPromptState.getState(lane.tmuxSessionName);
|
|
734
|
+
if (state.phase !== 'draft_active') {
|
|
735
|
+
return { status: 'not_ready', eligibleAt: null };
|
|
736
|
+
}
|
|
737
|
+
const transition = this.humanPromptState.transitionToAwaiting(lane.tmuxSessionName, state.generation, true);
|
|
738
|
+
if (!transition.accepted) {
|
|
739
|
+
return { status: 'not_ready', eligibleAt: null };
|
|
740
|
+
}
|
|
741
|
+
await this.handleHumanPromptStateChangedUnderAgentLock(agentId, {
|
|
742
|
+
sessionId: lane.sessionId,
|
|
743
|
+
tmuxSessionName: lane.tmuxSessionName,
|
|
744
|
+
generation: transition.state.generation,
|
|
745
|
+
phase: 'awaiting_stable_idle',
|
|
746
|
+
});
|
|
747
|
+
return { status: 'released' };
|
|
748
|
+
});
|
|
749
|
+
}
|
|
526
750
|
async onModuleDestroy() {
|
|
751
|
+
this.closing = true;
|
|
527
752
|
const poolStats = this.getPoolStats();
|
|
528
753
|
const totalMessages = poolStats.reduce((sum, p) => sum + p.messageCount, 0);
|
|
529
|
-
logger.info({ agentCount: poolStats.length, totalMessages }, 'Shutting down message pool, flushing default lanes and
|
|
754
|
+
logger.info({ agentCount: poolStats.length, totalMessages }, 'Shutting down message pool, flushing default lanes and failing deferred lanes...');
|
|
530
755
|
for (const [agentId, pool] of this.pools.entries()) {
|
|
531
756
|
if (pool.timer) {
|
|
532
757
|
clearTimeout(pool.timer);
|
|
@@ -538,26 +763,45 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
538
763
|
}
|
|
539
764
|
logger.debug({ agentId }, 'Cleared timers for agent pool');
|
|
540
765
|
}
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
766
|
+
const deferredLanes = Array.from(this.deferredLanes.values());
|
|
767
|
+
const deferredSettlements = [];
|
|
768
|
+
for (const lane of deferredLanes) {
|
|
769
|
+
const detachment = await this.coordinator.withAgentLock(lane.agentId, async () => {
|
|
770
|
+
const current = this.deferredLanes.get(lane.agentId);
|
|
771
|
+
return current === lane
|
|
772
|
+
? this.detachDeferredLaneUnderAgentLock(lane, 'Service stopped before deferred delivery')
|
|
773
|
+
: null;
|
|
774
|
+
});
|
|
775
|
+
if (lane.requiredGeneration !== null) {
|
|
776
|
+
this.humanPromptState.clearSession(lane.tmuxSessionName);
|
|
777
|
+
}
|
|
778
|
+
if (detachment) {
|
|
779
|
+
deferredSettlements.push(this.settleDeferredLaneDetachment(detachment, false));
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (deferredLanes.length > 0) {
|
|
544
783
|
logger.info({
|
|
545
|
-
agentCount:
|
|
546
|
-
totalMessages:
|
|
547
|
-
}, '
|
|
548
|
-
await Promise.all(idleLanes.map((lane) => this.failIdleLane(lane, 'Service stopped before idle delivery', false)));
|
|
784
|
+
agentCount: deferredLanes.length,
|
|
785
|
+
totalMessages: deferredLanes.reduce((sum, lane) => sum + lane.messages.length, 0),
|
|
786
|
+
}, 'Settling deferred delivery lanes before terminal shutdown');
|
|
549
787
|
}
|
|
550
788
|
const SHUTDOWN_TIMEOUT_MS = 5000;
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
789
|
+
const shutdownWork = Promise.all([...deferredSettlements, this.flushAll()]).then(() => undefined);
|
|
790
|
+
let timeout;
|
|
791
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
792
|
+
timeout = setTimeout(() => {
|
|
793
|
+
const remainingPools = this.pools.size;
|
|
794
|
+
const remainingMessages = this.getPoolStats().reduce((sum, pool) => sum + pool.messageCount, 0);
|
|
795
|
+
if (remainingMessages > 0) {
|
|
796
|
+
logger.warn({ remainingPools, remainingMessages, timeoutMs: SHUTDOWN_TIMEOUT_MS }, 'Shutdown flush timeout reached, some messages may be lost');
|
|
797
|
+
}
|
|
798
|
+
resolve();
|
|
799
|
+
}, SHUTDOWN_TIMEOUT_MS);
|
|
800
|
+
});
|
|
801
|
+
await Promise.race([shutdownWork, timeoutPromise]);
|
|
802
|
+
if (timeout)
|
|
803
|
+
clearTimeout(timeout);
|
|
804
|
+
void shutdownWork.catch((error) => logger.error({ error }, 'Message pool shutdown settlement failed'));
|
|
561
805
|
logger.info('Message pool shutdown complete');
|
|
562
806
|
}
|
|
563
807
|
getMessageLog(options) {
|
|
@@ -569,50 +813,285 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
569
813
|
getMessageById(messageId) {
|
|
570
814
|
return this.messageLog.getMessageById(messageId);
|
|
571
815
|
}
|
|
816
|
+
async handleHumanPromptStateChanged(event) {
|
|
817
|
+
const session = this.sessions.getSession(event.sessionId);
|
|
818
|
+
if (!session?.agentId || session.tmuxSessionId !== event.tmuxSessionName)
|
|
819
|
+
return;
|
|
820
|
+
const agentId = session.agentId;
|
|
821
|
+
while (true) {
|
|
822
|
+
const step = await this.coordinator.withAgentLock(agentId, async () => {
|
|
823
|
+
const activeSession = this.sessions.getActiveSessionForAgent(agentId);
|
|
824
|
+
const existingLane = this.deferredLanes.get(agentId);
|
|
825
|
+
if (activeSession?.id === event.sessionId &&
|
|
826
|
+
existingLane &&
|
|
827
|
+
existingLane.sessionId !== event.sessionId) {
|
|
828
|
+
return {
|
|
829
|
+
detachment: this.detachDeferredLaneUnderAgentLock(existingLane, 'Target session was replaced before deferred delivery'),
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
await this.handleHumanPromptStateChangedUnderAgentLock(agentId, event);
|
|
833
|
+
return { done: true };
|
|
834
|
+
});
|
|
835
|
+
if ('done' in step)
|
|
836
|
+
return;
|
|
837
|
+
await this.settleDeferredLaneDetachment(step.detachment);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
async handleHumanPromptStateChangedUnderAgentLock(agentId, event) {
|
|
841
|
+
if (event.phase === 'draft_active') {
|
|
842
|
+
await this.promoteProtectedPoolMessagesUnderAgentLock(agentId, event);
|
|
843
|
+
}
|
|
844
|
+
const lane = this.deferredLanes.get(agentId);
|
|
845
|
+
if (!lane ||
|
|
846
|
+
lane.sessionId !== event.sessionId ||
|
|
847
|
+
lane.tmuxSessionName !== event.tmuxSessionName) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
this.rebindLaneGeneration(lane, event.generation);
|
|
851
|
+
if (event.phase === 'draft_active') {
|
|
852
|
+
this.clearDeferredLaneTimer(lane);
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
this.ensureHumanQuietTimerUnderAgentLock(lane);
|
|
856
|
+
}
|
|
857
|
+
this.broadcastPoolsUpdate();
|
|
858
|
+
}
|
|
859
|
+
async promoteProtectedPoolMessagesUnderAgentLock(agentId, event) {
|
|
860
|
+
const pool = this.pools.get(agentId);
|
|
861
|
+
if (!pool)
|
|
862
|
+
return;
|
|
863
|
+
const protectedMessages = pool.messages.filter((message) => message.deferWhileHumanTyping);
|
|
864
|
+
if (protectedMessages.length === 0)
|
|
865
|
+
return;
|
|
866
|
+
const session = this.sessions.getSession(event.sessionId);
|
|
867
|
+
const activeSession = this.sessions.getActiveSessionForAgent(agentId);
|
|
868
|
+
if (!session?.tmuxSessionId ||
|
|
869
|
+
session.tmuxSessionId !== event.tmuxSessionName ||
|
|
870
|
+
activeSession?.id !== event.sessionId) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
pool.messages = pool.messages.filter((message) => !message.deferWhileHumanTyping);
|
|
874
|
+
if (pool.messages.length === 0) {
|
|
875
|
+
this.clearPoolTimers(pool);
|
|
876
|
+
this.pools.delete(agentId);
|
|
877
|
+
}
|
|
878
|
+
await this.moveMessagesToDeferredLaneUnderAgentLock(agentId, pool.projectId, pool.config.separator, activeSession, protectedMessages, event.generation);
|
|
879
|
+
}
|
|
572
880
|
async handleSessionActivityChanged(event) {
|
|
573
881
|
const session = this.sessions.getSession(event.sessionId);
|
|
574
882
|
if (!session?.agentId || event.state !== 'idle')
|
|
575
883
|
return;
|
|
576
|
-
await this.
|
|
884
|
+
await this.flushDeferredLane(session.agentId, event.sessionId);
|
|
577
885
|
}
|
|
578
886
|
async handleSessionStopped(event) {
|
|
579
887
|
const session = this.sessions.getSession(event.sessionId);
|
|
580
888
|
if (!session?.agentId)
|
|
581
889
|
return;
|
|
582
|
-
|
|
890
|
+
const agentId = session.agentId;
|
|
891
|
+
await this.failMatchingDeferredLane(agentId, event.sessionId, 'Target session stopped before deferred delivery');
|
|
583
892
|
}
|
|
584
893
|
async handleSessionCrashed(event) {
|
|
585
894
|
const session = this.sessions.getSession(event.sessionId);
|
|
586
895
|
if (!session?.agentId)
|
|
587
896
|
return;
|
|
588
|
-
|
|
897
|
+
const agentId = session.agentId;
|
|
898
|
+
await this.failMatchingDeferredLane(agentId, event.sessionId, 'Target session crashed before deferred delivery');
|
|
589
899
|
}
|
|
590
|
-
async
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
900
|
+
async flushDeferredLane(agentId, expectedSessionId, quietGraceElapsed = false) {
|
|
901
|
+
const claim = await this.coordinator.withAgentLock(agentId, async () => this.claimDeferredLaneUnderAgentLock(agentId, expectedSessionId, quietGraceElapsed));
|
|
902
|
+
if (!claim)
|
|
903
|
+
return { success: true, deliveredCount: 0 };
|
|
904
|
+
return this.deliverDeferredClaim(claim);
|
|
905
|
+
}
|
|
906
|
+
async deliverDeferredClaim(claim) {
|
|
907
|
+
const result = await this.deliverBatchToTarget(claim.agentId, claim.messages, claim.separator, { sessionId: claim.sessionId, tmuxSessionName: claim.tmuxSessionName }, claim.quietSnapshot ?? undefined, claim);
|
|
908
|
+
await this.coordinator.withAgentLock(claim.agentId, async () => this.completeDeferredClaimUnderAgentLock(claim, result));
|
|
909
|
+
return result;
|
|
910
|
+
}
|
|
911
|
+
claimDeferredLaneUnderAgentLock(agentId, expectedSessionId, quietGraceElapsed) {
|
|
912
|
+
const lane = this.deferredLanes.get(agentId);
|
|
913
|
+
const expectedSession = this.sessions.getSession(expectedSessionId);
|
|
914
|
+
const activeSession = this.sessions.getActiveSessionForAgent(agentId);
|
|
915
|
+
if (!lane ||
|
|
916
|
+
lane.activeClaim ||
|
|
917
|
+
lane.sessionId !== expectedSessionId ||
|
|
918
|
+
expectedSession?.status !== 'running' ||
|
|
919
|
+
expectedSession.tmuxSessionId !== lane.tmuxSessionName ||
|
|
920
|
+
activeSession?.id !== expectedSessionId) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
let quietSnapshot = null;
|
|
924
|
+
if (lane.requiredGeneration !== null) {
|
|
925
|
+
quietSnapshot = this.humanPromptState.getQuietSnapshot(lane.tmuxSessionName);
|
|
926
|
+
if (!quietGraceElapsed ||
|
|
927
|
+
!quietSnapshot ||
|
|
928
|
+
quietSnapshot.expectedGeneration !== lane.requiredGeneration ||
|
|
929
|
+
!lane.quietBaseline ||
|
|
930
|
+
!this.quietSnapshotsEqual(lane.quietBaseline, quietSnapshot)) {
|
|
931
|
+
const current = this.humanPromptState.getState(lane.tmuxSessionName);
|
|
932
|
+
if (current.phase === 'inactive') {
|
|
933
|
+
lane.requiredGeneration = null;
|
|
934
|
+
lane.quietBaseline = null;
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
this.rebindLaneGeneration(lane, current.generation);
|
|
938
|
+
if (current.phase === 'awaiting_stable_idle') {
|
|
939
|
+
this.ensureHumanQuietTimerUnderAgentLock(lane, true);
|
|
940
|
+
}
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
602
943
|
}
|
|
603
|
-
|
|
604
|
-
|
|
944
|
+
}
|
|
945
|
+
if (lane.messages.some((message) => message.requiresProviderIdle) &&
|
|
946
|
+
expectedSession.activityState !== 'idle') {
|
|
947
|
+
if (lane.requiredGeneration !== null) {
|
|
948
|
+
this.ensureHumanQuietTimerUnderAgentLock(lane, true);
|
|
949
|
+
}
|
|
950
|
+
return null;
|
|
951
|
+
}
|
|
952
|
+
this.clearDeferredLaneTimer(lane);
|
|
953
|
+
let resolveCompletion;
|
|
954
|
+
const completion = new Promise((resolve) => {
|
|
955
|
+
resolveCompletion = resolve;
|
|
605
956
|
});
|
|
606
|
-
|
|
957
|
+
const claim = {
|
|
958
|
+
agentId,
|
|
959
|
+
sessionId: lane.sessionId,
|
|
960
|
+
tmuxSessionName: lane.tmuxSessionName,
|
|
961
|
+
projectId: lane.projectId,
|
|
962
|
+
messages: [...lane.messages],
|
|
963
|
+
separator: lane.separator,
|
|
964
|
+
quietSnapshot: lane.requiredGeneration !== null ? quietSnapshot : null,
|
|
965
|
+
state: { phase: 'preparing' },
|
|
966
|
+
completion,
|
|
967
|
+
resolveCompletion,
|
|
968
|
+
};
|
|
969
|
+
lane.activeClaim = claim;
|
|
970
|
+
return claim;
|
|
607
971
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (!lane || lane.sessionId !== sessionId)
|
|
972
|
+
completeDeferredClaimUnderAgentLock(claim, result) {
|
|
973
|
+
if (claim.state.phase === 'complete')
|
|
611
974
|
return;
|
|
612
|
-
|
|
613
|
-
|
|
975
|
+
const cancellationReason = claim.state.phase === 'preparing' ? claim.state.cancellationReason : undefined;
|
|
976
|
+
const lane = this.deferredLanes.get(claim.agentId);
|
|
977
|
+
const ownsLane = lane?.sessionId === claim.sessionId && lane.activeClaim === claim ? lane : null;
|
|
978
|
+
if (ownsLane)
|
|
979
|
+
ownsLane.activeClaim = null;
|
|
980
|
+
if (cancellationReason) {
|
|
981
|
+
claim.state = { phase: 'complete', outcome: 'failed' };
|
|
982
|
+
claim.resolveCompletion(null);
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (ownsLane && result.outcome !== 'deferred') {
|
|
986
|
+
const claimedIds = new Set(claim.messages.map((message) => message.logEntryId));
|
|
987
|
+
ownsLane.messages.splice(0, ownsLane.messages.length, ...ownsLane.messages.filter((message) => !claimedIds.has(message.logEntryId)));
|
|
988
|
+
}
|
|
989
|
+
const outcome = result.success
|
|
990
|
+
? (result.outcome ?? 'delivered')
|
|
991
|
+
: 'failed';
|
|
992
|
+
claim.state = { phase: 'complete', outcome };
|
|
993
|
+
if (!ownsLane) {
|
|
994
|
+
const failureLane = claim.detachReason && result.outcome === 'deferred'
|
|
995
|
+
? this.copyDeferredLaneWithMessages(claim, claim.messages)
|
|
996
|
+
: null;
|
|
997
|
+
claim.resolveCompletion(failureLane);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
if (ownsLane.messages.length === 0) {
|
|
1001
|
+
this.clearDeferredLaneTimer(ownsLane);
|
|
1002
|
+
this.deferredLanes.delete(claim.agentId);
|
|
1003
|
+
this.broadcastPoolsUpdate();
|
|
1004
|
+
claim.resolveCompletion(null);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const state = this.humanPromptState.getState(ownsLane.tmuxSessionName);
|
|
1008
|
+
if (state.phase === 'inactive') {
|
|
1009
|
+
ownsLane.requiredGeneration = null;
|
|
1010
|
+
ownsLane.quietBaseline = null;
|
|
1011
|
+
setTimeout(() => {
|
|
1012
|
+
void this.flushDeferredLane(claim.agentId, ownsLane.sessionId).catch((error) => logger.error({ agentId: claim.agentId, error }, 'Deferred follow-up delivery failed'));
|
|
1013
|
+
}, 0);
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
this.rebindLaneGeneration(ownsLane, state.generation);
|
|
1017
|
+
if (state.phase === 'awaiting_stable_idle') {
|
|
1018
|
+
this.ensureHumanQuietTimerUnderAgentLock(ownsLane, true);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
this.broadcastPoolsUpdate();
|
|
1022
|
+
claim.resolveCompletion(null);
|
|
1023
|
+
}
|
|
1024
|
+
async failMatchingDeferredLane(agentId, sessionId, reason, notifySenders = true) {
|
|
1025
|
+
const detachment = await this.coordinator.withAgentLock(agentId, async () => {
|
|
1026
|
+
const lane = this.deferredLanes.get(agentId);
|
|
1027
|
+
return !lane || lane.sessionId !== sessionId
|
|
1028
|
+
? null
|
|
1029
|
+
: this.detachDeferredLaneUnderAgentLock(lane, reason);
|
|
1030
|
+
});
|
|
1031
|
+
if (detachment) {
|
|
1032
|
+
await this.settleDeferredLaneDetachment(detachment, notifySenders);
|
|
1033
|
+
}
|
|
614
1034
|
}
|
|
615
|
-
|
|
1035
|
+
detachDeferredLaneUnderAgentLock(lane, reason) {
|
|
1036
|
+
if (this.deferredLanes.get(lane.agentId) === lane) {
|
|
1037
|
+
this.deferredLanes.delete(lane.agentId);
|
|
1038
|
+
}
|
|
1039
|
+
this.clearDeferredLaneTimer(lane);
|
|
1040
|
+
const claim = lane.activeClaim;
|
|
1041
|
+
lane.activeClaim = null;
|
|
1042
|
+
if (!claim || claim.state.phase === 'complete') {
|
|
1043
|
+
return {
|
|
1044
|
+
reason,
|
|
1045
|
+
failureLane: this.copyDeferredLaneWithMessages(lane, lane.messages),
|
|
1046
|
+
claimCompletion: null,
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
claim.detachReason = reason;
|
|
1050
|
+
if (claim.state.phase === 'preparing') {
|
|
1051
|
+
claim.state = { phase: 'preparing', cancellationReason: reason };
|
|
1052
|
+
return {
|
|
1053
|
+
reason,
|
|
1054
|
+
failureLane: this.copyDeferredLaneWithMessages(lane, lane.messages),
|
|
1055
|
+
claimCompletion: null,
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
const claimedIds = new Set(claim.messages.map((message) => message.logEntryId));
|
|
1059
|
+
const unclaimedMessages = lane.messages.filter((message) => !claimedIds.has(message.logEntryId));
|
|
1060
|
+
return {
|
|
1061
|
+
reason,
|
|
1062
|
+
failureLane: unclaimedMessages.length > 0
|
|
1063
|
+
? this.copyDeferredLaneWithMessages(lane, unclaimedMessages)
|
|
1064
|
+
: null,
|
|
1065
|
+
claimCompletion: claim.completion,
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
async settleDeferredLaneDetachment(detachment, notifySenders = true) {
|
|
1069
|
+
if (detachment.failureLane) {
|
|
1070
|
+
await this.failDeferredLane(detachment.failureLane, detachment.reason, notifySenders);
|
|
1071
|
+
}
|
|
1072
|
+
if (!detachment.claimCompletion)
|
|
1073
|
+
return;
|
|
1074
|
+
const lateFailureLane = await detachment.claimCompletion;
|
|
1075
|
+
if (lateFailureLane) {
|
|
1076
|
+
await this.failDeferredLane(lateFailureLane, detachment.reason, notifySenders);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
copyDeferredLaneWithMessages(lane, messages) {
|
|
1080
|
+
return {
|
|
1081
|
+
sessionId: lane.sessionId,
|
|
1082
|
+
tmuxSessionName: lane.tmuxSessionName,
|
|
1083
|
+
agentId: lane.agentId,
|
|
1084
|
+
projectId: lane.projectId,
|
|
1085
|
+
messages: [...messages],
|
|
1086
|
+
separator: lane.separator,
|
|
1087
|
+
requiredGeneration: null,
|
|
1088
|
+
quietTimer: null,
|
|
1089
|
+
quietBaseline: null,
|
|
1090
|
+
activeClaim: null,
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
async failDeferredLane(lane, reason, notifySenders = true) {
|
|
1094
|
+
this.clearDeferredLaneTimer(lane);
|
|
616
1095
|
const batchId = (0, crypto_1.randomUUID)();
|
|
617
1096
|
const failureDisclosure = (0, delivery_failure_disclosure_1.getStrictestFailureDisclosure)(lane.messages);
|
|
618
1097
|
const batchFailure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(failureDisclosure, reason, 'no_active_session');
|
|
@@ -634,7 +1113,7 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
634
1113
|
sessionId: lane.sessionId,
|
|
635
1114
|
messageCount: lane.messages.length,
|
|
636
1115
|
error: batchFailure.error,
|
|
637
|
-
}, '
|
|
1116
|
+
}, 'Deferred delivery lane discarded');
|
|
638
1117
|
if (!notifySenders)
|
|
639
1118
|
return;
|
|
640
1119
|
await this.failureNotifier
|
|
@@ -644,6 +1123,75 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
644
1123
|
error: this.disclosedLogError(failureDisclosure, error),
|
|
645
1124
|
}, 'Failure notification error (best-effort)'));
|
|
646
1125
|
}
|
|
1126
|
+
clearPoolTimers(pool) {
|
|
1127
|
+
if (pool.timer)
|
|
1128
|
+
clearTimeout(pool.timer);
|
|
1129
|
+
if (pool.maxWaitTimer)
|
|
1130
|
+
clearTimeout(pool.maxWaitTimer);
|
|
1131
|
+
pool.timer = null;
|
|
1132
|
+
pool.maxWaitTimer = null;
|
|
1133
|
+
}
|
|
1134
|
+
clearDeferredLaneTimer(lane) {
|
|
1135
|
+
if (lane.quietTimer)
|
|
1136
|
+
clearTimeout(lane.quietTimer);
|
|
1137
|
+
lane.quietTimer = null;
|
|
1138
|
+
lane.quietBaseline = null;
|
|
1139
|
+
}
|
|
1140
|
+
rebindLaneGeneration(lane, generation) {
|
|
1141
|
+
if (lane.requiredGeneration !== generation)
|
|
1142
|
+
this.clearDeferredLaneTimer(lane);
|
|
1143
|
+
lane.requiredGeneration = generation;
|
|
1144
|
+
for (const message of lane.messages)
|
|
1145
|
+
message.heldGeneration = generation;
|
|
1146
|
+
}
|
|
1147
|
+
ensureHumanQuietTimerUnderAgentLock(lane, restart = false) {
|
|
1148
|
+
if (lane.activeClaim)
|
|
1149
|
+
return;
|
|
1150
|
+
if (restart)
|
|
1151
|
+
this.clearDeferredLaneTimer(lane);
|
|
1152
|
+
if (lane.quietTimer)
|
|
1153
|
+
return;
|
|
1154
|
+
const snapshot = this.humanPromptState.getQuietSnapshot(lane.tmuxSessionName);
|
|
1155
|
+
if (!snapshot || snapshot.expectedGeneration !== lane.requiredGeneration)
|
|
1156
|
+
return;
|
|
1157
|
+
lane.quietBaseline = snapshot;
|
|
1158
|
+
lane.quietTimer = setTimeout(() => {
|
|
1159
|
+
lane.quietTimer = null;
|
|
1160
|
+
void this.flushDeferredLane(lane.agentId, lane.sessionId, true).catch((error) => logger.error({ agentId: lane.agentId, error }, 'Human-quiet delivery failed'));
|
|
1161
|
+
}, exports.HUMAN_DRAFT_IDLE_GRACE_MS);
|
|
1162
|
+
}
|
|
1163
|
+
quietSnapshotsEqual(left, right) {
|
|
1164
|
+
return (left.expectedGeneration === right.expectedGeneration &&
|
|
1165
|
+
left.executedInputEpoch === right.executedInputEpoch &&
|
|
1166
|
+
left.meaningfulOutputEpoch === right.meaningfulOutputEpoch);
|
|
1167
|
+
}
|
|
1168
|
+
async moveMessagesToDeferredLaneUnderAgentLock(agentId, projectId, separator, activeSession, messages, generation) {
|
|
1169
|
+
if (!activeSession.tmuxSessionId)
|
|
1170
|
+
return;
|
|
1171
|
+
let lane = this.deferredLanes.get(agentId);
|
|
1172
|
+
if (!lane) {
|
|
1173
|
+
lane = {
|
|
1174
|
+
sessionId: activeSession.id,
|
|
1175
|
+
tmuxSessionName: activeSession.tmuxSessionId,
|
|
1176
|
+
agentId,
|
|
1177
|
+
projectId,
|
|
1178
|
+
messages: [],
|
|
1179
|
+
separator,
|
|
1180
|
+
requiredGeneration: generation,
|
|
1181
|
+
quietTimer: null,
|
|
1182
|
+
quietBaseline: null,
|
|
1183
|
+
activeClaim: null,
|
|
1184
|
+
};
|
|
1185
|
+
this.deferredLanes.set(agentId, lane);
|
|
1186
|
+
}
|
|
1187
|
+
lane.separator = separator;
|
|
1188
|
+
lane.messages.push(...messages);
|
|
1189
|
+
this.rebindLaneGeneration(lane, generation);
|
|
1190
|
+
const state = this.humanPromptState.getState(lane.tmuxSessionName);
|
|
1191
|
+
if (state.phase === 'awaiting_stable_idle')
|
|
1192
|
+
this.ensureHumanQuietTimerUnderAgentLock(lane);
|
|
1193
|
+
this.broadcastPoolsUpdate();
|
|
1194
|
+
}
|
|
647
1195
|
async deliverBatch(agentId, messages, separator = this.config.separator) {
|
|
648
1196
|
const batchId = (0, crypto_1.randomUUID)();
|
|
649
1197
|
const failureDisclosure = (0, delivery_failure_disclosure_1.getStrictestFailureDisclosure)(messages);
|
|
@@ -675,21 +1223,43 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
675
1223
|
}, 'Failure notification error (best-effort)'));
|
|
676
1224
|
return { success: false, discardedCount: messages.length, reason: batchFailure.error };
|
|
677
1225
|
}
|
|
678
|
-
|
|
679
|
-
|
|
1226
|
+
return this.deliverBatchToTarget(agentId, messages, separator, {
|
|
1227
|
+
sessionId: session.id,
|
|
1228
|
+
tmuxSessionName: session.tmuxSessionId,
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
async deliverBatchToTarget(agentId, messages, separator, target, quietSnapshot, claim) {
|
|
1232
|
+
const batchId = (0, crypto_1.randomUUID)();
|
|
1233
|
+
const failureDisclosure = (0, delivery_failure_disclosure_1.getStrictestFailureDisclosure)(messages);
|
|
1234
|
+
const baseText = messages.map((message) => message.text).join(separator);
|
|
680
1235
|
const submitKeys = messages[messages.length - 1]?.submitKeys ?? ['Enter'];
|
|
681
1236
|
try {
|
|
682
1237
|
const postPasteDelayMs = await this.providerAdapterFactory.getPostPasteDelayMsForAgent(agentId);
|
|
683
|
-
const result =
|
|
684
|
-
agentId,
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
1238
|
+
const result = claim
|
|
1239
|
+
? await this.terminalIO.deliverGuarded({ name: target.tmuxSessionName }, baseText, { agentId, submitKeys, postPasteDelayMs }, quietSnapshot, {
|
|
1240
|
+
canStartMutation: () => canStartDeferredClaimMutation(claim),
|
|
1241
|
+
markMutationStarted: () => {
|
|
1242
|
+
if (canStartDeferredClaimMutation(claim)) {
|
|
1243
|
+
claim.state = { phase: 'mutating' };
|
|
1244
|
+
}
|
|
1245
|
+
},
|
|
1246
|
+
})
|
|
1247
|
+
: await this.terminalIO.deliver({ name: target.tmuxSessionName }, baseText, {
|
|
1248
|
+
agentId,
|
|
1249
|
+
submitKeys,
|
|
1250
|
+
postPasteDelayMs,
|
|
1251
|
+
});
|
|
1252
|
+
const cancellation = deferredClaimCancellationResult(claim, messages.length);
|
|
1253
|
+
if (cancellation)
|
|
1254
|
+
return cancellation;
|
|
1255
|
+
if ('deferred' in result) {
|
|
1256
|
+
return { success: true, deliveredCount: 0, outcome: 'deferred' };
|
|
1257
|
+
}
|
|
688
1258
|
const deliveredAt = Date.now();
|
|
689
1259
|
const status = result.confirmed ? 'delivered' : 'unconfirmed';
|
|
690
1260
|
const entries = [];
|
|
691
|
-
for (const
|
|
692
|
-
this.messageLog.update(
|
|
1261
|
+
for (const message of messages) {
|
|
1262
|
+
this.messageLog.update(message.logEntryId, {
|
|
693
1263
|
status,
|
|
694
1264
|
batchId,
|
|
695
1265
|
deliveredAt,
|
|
@@ -698,7 +1268,7 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
698
1268
|
retryCount: result.retryCount,
|
|
699
1269
|
failureCode: result.confirmed ? undefined : 'paste_not_confirmed',
|
|
700
1270
|
});
|
|
701
|
-
const entry = this.messageLog.getById(
|
|
1271
|
+
const entry = this.messageLog.getById(message.logEntryId);
|
|
702
1272
|
if (entry)
|
|
703
1273
|
entries.push(entry);
|
|
704
1274
|
}
|
|
@@ -709,13 +1279,6 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
709
1279
|
this.activityStream.broadcastUnconfirmed(batchId, entries);
|
|
710
1280
|
}
|
|
711
1281
|
this.broadcastPoolsUpdate();
|
|
712
|
-
logger.info({
|
|
713
|
-
agentId,
|
|
714
|
-
sessionId: session.id,
|
|
715
|
-
messageCount: messages.length,
|
|
716
|
-
batchId,
|
|
717
|
-
confirmed: result.confirmed,
|
|
718
|
-
}, 'Batch delivered to agent session');
|
|
719
1282
|
return {
|
|
720
1283
|
success: true,
|
|
721
1284
|
deliveredCount: messages.length,
|
|
@@ -724,72 +1287,140 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
724
1287
|
}
|
|
725
1288
|
catch (error) {
|
|
726
1289
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1290
|
+
const cancellation = deferredClaimCancellationResult(claim, messages.length);
|
|
1291
|
+
if (cancellation)
|
|
1292
|
+
return cancellation;
|
|
727
1293
|
const failureCode = errorMsg.includes('send keys')
|
|
728
1294
|
? 'send_keys_failed'
|
|
729
1295
|
: 'tmux_error';
|
|
730
1296
|
const batchFailure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(failureDisclosure, errorMsg, failureCode);
|
|
731
|
-
logger.error({ agentId, sessionId:
|
|
732
|
-
for (const
|
|
733
|
-
const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(
|
|
734
|
-
this.messageLog.update(
|
|
1297
|
+
logger.error({ agentId, sessionId: target.sessionId, error: batchFailure.error }, 'Failed to deliver batch to exact agent session');
|
|
1298
|
+
for (const message of messages) {
|
|
1299
|
+
const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(message.failureDisclosure, errorMsg, failureCode);
|
|
1300
|
+
this.messageLog.update(message.logEntryId, {
|
|
735
1301
|
status: 'failed',
|
|
736
1302
|
batchId,
|
|
737
1303
|
error: failure.error,
|
|
738
1304
|
failureCode: failure.failureCode,
|
|
739
1305
|
});
|
|
740
|
-
const entry = this.messageLog.getById(
|
|
1306
|
+
const entry = this.messageLog.getById(message.logEntryId);
|
|
741
1307
|
if (entry)
|
|
742
1308
|
this.activityStream.broadcastFailed(entry);
|
|
743
1309
|
}
|
|
744
1310
|
this.broadcastPoolsUpdate();
|
|
745
1311
|
await this.failureNotifier
|
|
746
1312
|
.notifySendersOfFailure(messages, agentId, batchFailure.error)
|
|
747
|
-
.catch((
|
|
1313
|
+
.catch((notifyError) => logger.warn({
|
|
748
1314
|
agentId,
|
|
749
|
-
error: failureDisclosure === 'project-safe' ? delivery_failure_disclosure_1.PROJECT_SAFE_DELIVERY_ERROR :
|
|
1315
|
+
error: failureDisclosure === 'project-safe' ? delivery_failure_disclosure_1.PROJECT_SAFE_DELIVERY_ERROR : notifyError,
|
|
750
1316
|
}, 'Failure notification error (best-effort)'));
|
|
751
1317
|
return { success: false, discardedCount: messages.length, reason: batchFailure.error };
|
|
752
1318
|
}
|
|
753
1319
|
}
|
|
754
1320
|
async deliverMessage(agentId, text, submitKeys, opts) {
|
|
1321
|
+
return this.coordinator.withAgentLock(agentId, () => this.deliverMessageUnderAgentLock(agentId, text, submitKeys, opts));
|
|
1322
|
+
}
|
|
1323
|
+
async deliverMessageUnderAgentLock(agentId, text, submitKeys, opts) {
|
|
755
1324
|
const activeSessions = await this.sessions.listActiveSessions();
|
|
756
|
-
const session = activeSessions.find((
|
|
757
|
-
if (!session
|
|
1325
|
+
const session = activeSessions.find((candidate) => candidate.agentId === agentId);
|
|
1326
|
+
if (!session?.tmuxSessionId)
|
|
758
1327
|
throw new Error(`No active session for agent ${agentId}`);
|
|
1328
|
+
const postPasteDelayMs = await this.providerAdapterFactory.getPostPasteDelayMsForAgent(agentId);
|
|
1329
|
+
if (opts?.skipConfirmation) {
|
|
1330
|
+
const delivery = await this.terminalIO.deliverImmediate({ name: session.tmuxSessionId }, text, {
|
|
1331
|
+
submitKeys,
|
|
1332
|
+
postPasteDelayMs,
|
|
1333
|
+
confirm: false,
|
|
1334
|
+
preKeys: opts.preKeys,
|
|
1335
|
+
preDelayMs: opts.preDelayMs,
|
|
1336
|
+
});
|
|
1337
|
+
return {
|
|
1338
|
+
nonce: delivery.nonce,
|
|
1339
|
+
skipped: true,
|
|
1340
|
+
retryCount: 0,
|
|
1341
|
+
sessionId: session.id,
|
|
1342
|
+
tmuxSessionName: session.tmuxSessionId,
|
|
1343
|
+
};
|
|
759
1344
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
1345
|
+
const delivery = await this.terminalIO.deliver({ name: session.tmuxSessionId }, text, {
|
|
1346
|
+
agentId,
|
|
1347
|
+
submitKeys,
|
|
1348
|
+
postPasteDelayMs,
|
|
1349
|
+
preKeys: opts?.preKeys,
|
|
1350
|
+
preDelayMs: opts?.preDelayMs,
|
|
1351
|
+
});
|
|
1352
|
+
return {
|
|
1353
|
+
nonce: delivery.nonce,
|
|
1354
|
+
unconfirmed: !delivery.confirmed,
|
|
1355
|
+
retryCount: delivery.retryCount,
|
|
1356
|
+
sessionId: session.id,
|
|
1357
|
+
tmuxSessionName: session.tmuxSessionId,
|
|
763
1358
|
};
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
}
|
|
774
|
-
|
|
1359
|
+
}
|
|
1360
|
+
async deliverImmediateUnderAgentLock(input, logEntry) {
|
|
1361
|
+
try {
|
|
1362
|
+
const activeSession = this.sessions.getActiveSessionForAgent(input.agentId);
|
|
1363
|
+
const expectedSubmit = input.humanPromptSubmit && activeSession?.tmuxSessionId
|
|
1364
|
+
? {
|
|
1365
|
+
sessionId: activeSession.id,
|
|
1366
|
+
tmuxSessionName: activeSession.tmuxSessionId,
|
|
1367
|
+
generation: this.humanPromptState.getState(activeSession.tmuxSessionId).generation,
|
|
1368
|
+
}
|
|
1369
|
+
: null;
|
|
1370
|
+
const delivery = await this.deliverMessageUnderAgentLock(input.agentId, input.text, input.submitKeys, {
|
|
1371
|
+
skipConfirmation: input.deliveryMode === 'immediate',
|
|
1372
|
+
preKeys: input.preKeys,
|
|
1373
|
+
preDelayMs: input.preDelayMs,
|
|
1374
|
+
});
|
|
1375
|
+
const status = delivery.unconfirmed ? 'unconfirmed' : 'delivered';
|
|
1376
|
+
const deliveredAt = Date.now();
|
|
1377
|
+
this.messageLog.update(logEntry.id, {
|
|
1378
|
+
status,
|
|
1379
|
+
deliveredAt,
|
|
1380
|
+
nonce: delivery.skipped ? undefined : delivery.nonce,
|
|
1381
|
+
confirmedAt: delivery.skipped || delivery.unconfirmed ? undefined : deliveredAt,
|
|
1382
|
+
retryCount: delivery.retryCount,
|
|
1383
|
+
failureCode: delivery.unconfirmed ? 'paste_not_confirmed' : undefined,
|
|
1384
|
+
});
|
|
1385
|
+
const updated = this.messageLog.getById(logEntry.id);
|
|
1386
|
+
if (updated) {
|
|
1387
|
+
if (delivery.unconfirmed)
|
|
1388
|
+
this.activityStream.broadcastUnconfirmed(logEntry.id, [updated]);
|
|
1389
|
+
else
|
|
1390
|
+
this.activityStream.broadcastDelivered(logEntry.id, [updated]);
|
|
775
1391
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
postPasteDelayMs,
|
|
781
|
-
preKeys: opts?.preKeys,
|
|
782
|
-
preDelayMs: opts?.preDelayMs,
|
|
783
|
-
});
|
|
784
|
-
result = {
|
|
785
|
-
nonce: delivery.nonce,
|
|
786
|
-
unconfirmed: !delivery.confirmed,
|
|
787
|
-
retryCount: delivery.retryCount,
|
|
788
|
-
};
|
|
1392
|
+
if (expectedSubmit &&
|
|
1393
|
+
expectedSubmit.sessionId === delivery.sessionId &&
|
|
1394
|
+
expectedSubmit.tmuxSessionName === delivery.tmuxSessionName) {
|
|
1395
|
+
await this.completeHumanSubmitUnderAgentLock(input.agentId, delivery.sessionId, delivery.tmuxSessionName, expectedSubmit.generation);
|
|
789
1396
|
}
|
|
1397
|
+
return { status, logEntryId: logEntry.id };
|
|
1398
|
+
}
|
|
1399
|
+
catch (error) {
|
|
1400
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1401
|
+
const failure = (0, delivery_failure_disclosure_1.classifyDeliveryFailure)(input.failureDisclosure, errorMsg, 'tmux_error');
|
|
1402
|
+
logger.error({ agentId: input.agentId, source: input.source, error: failure.error }, 'Immediate delivery failed');
|
|
1403
|
+
this.messageLog.update(logEntry.id, {
|
|
1404
|
+
status: 'failed',
|
|
1405
|
+
error: failure.error,
|
|
1406
|
+
failureCode: failure.failureCode,
|
|
1407
|
+
});
|
|
1408
|
+
const failed = this.messageLog.getById(logEntry.id);
|
|
1409
|
+
if (failed)
|
|
1410
|
+
this.activityStream.broadcastFailed(failed);
|
|
1411
|
+
return { status: 'failed', error: failure.error, logEntryId: logEntry.id };
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
async completeHumanSubmitUnderAgentLock(agentId, sessionId, tmuxSessionName, expectedGeneration) {
|
|
1415
|
+
const transition = this.humanPromptState.transitionToAwaiting(tmuxSessionName, expectedGeneration);
|
|
1416
|
+
if (!transition.accepted)
|
|
1417
|
+
return;
|
|
1418
|
+
await this.handleHumanPromptStateChangedUnderAgentLock(agentId, {
|
|
1419
|
+
sessionId,
|
|
1420
|
+
tmuxSessionName,
|
|
1421
|
+
generation: transition.state.generation,
|
|
1422
|
+
phase: 'awaiting_stable_idle',
|
|
790
1423
|
});
|
|
791
|
-
logger.info({ agentId, sessionId: session.id, unconfirmed: result.unconfirmed }, 'Immediate message delivered');
|
|
792
|
-
return result;
|
|
793
1424
|
}
|
|
794
1425
|
broadcastPoolsUpdate() {
|
|
795
1426
|
const pools = this.getPoolDetails();
|
|
@@ -817,6 +1448,12 @@ let SessionsMessagePoolService = class SessionsMessagePoolService {
|
|
|
817
1448
|
}
|
|
818
1449
|
};
|
|
819
1450
|
exports.SessionsMessagePoolService = SessionsMessagePoolService;
|
|
1451
|
+
__decorate([
|
|
1452
|
+
(0, event_emitter_1.OnEvent)(session_human_prompt_state_changed_1.sessionHumanPromptStateChangedEvent.name, { suppressErrors: false }),
|
|
1453
|
+
__metadata("design:type", Function),
|
|
1454
|
+
__metadata("design:paramtypes", [Object]),
|
|
1455
|
+
__metadata("design:returntype", Promise)
|
|
1456
|
+
], SessionsMessagePoolService.prototype, "handleHumanPromptStateChanged", null);
|
|
820
1457
|
__decorate([
|
|
821
1458
|
(0, event_emitter_1.OnEvent)('session.activity.changed', { async: true }),
|
|
822
1459
|
__metadata("design:type", Function),
|
|
@@ -844,6 +1481,7 @@ exports.SessionsMessagePoolService = SessionsMessagePoolService = __decorate([
|
|
|
844
1481
|
settings_service_1.SettingsService, Object, message_activity_stream_service_1.MessageActivityStreamService,
|
|
845
1482
|
provider_adapter_factory_1.ProviderAdapterFactory,
|
|
846
1483
|
message_log_service_1.MessageLogService,
|
|
847
|
-
delivery_failure_notifier_service_1.DeliveryFailureNotifierService
|
|
1484
|
+
delivery_failure_notifier_service_1.DeliveryFailureNotifierService,
|
|
1485
|
+
human_prompt_state_service_1.HumanPromptStateService])
|
|
848
1486
|
], SessionsMessagePoolService);
|
|
849
1487
|
//# sourceMappingURL=sessions-message-pool.service.js.map
|