crewly 1.6.1 → 1.6.3
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/config/roles/orchestrator/prompt.md +16 -0
- package/config/skills/agent/core/get-my-active-work/SKILL.md +101 -0
- package/config/skills/agent/core/get-my-active-work/execute.sh +122 -0
- package/config/skills/agent/core/record-learning/SKILL.md +29 -0
- package/config/skills/agent/core/reply-channel/SKILL.md +41 -0
- package/config/skills/agent/core/reply-channel/execute.sh +165 -0
- package/config/skills/agent/core/reply-channel/execute.test.sh +148 -0
- package/config/skills/agent/remote-browser/execute.sh +296 -14
- package/config/skills/agent/remote-browser/execute.test.sh +482 -0
- package/config/skills/orchestrator/send-message/SKILL.md +30 -7
- package/config/skills/orchestrator/team-health-scan/SKILL.md +98 -0
- package/config/skills/orchestrator/team-health-scan/execute.sh +44 -0
- package/config/skills/registry.json +62 -1
- package/config/slack-app-manifest.json +2 -1
- package/config/sops/developer/git-workflow.md +38 -3
- package/dist/backend/backend/src/constants.d.ts +69 -1
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +69 -2
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/active-work/active-work.controller.d.ts +53 -0
- package/dist/backend/backend/src/controllers/active-work/active-work.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/active-work/active-work.controller.js +92 -0
- package/dist/backend/backend/src/controllers/active-work/active-work.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/agent-stream/agent-stream.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/agent-stream/agent-stream.controller.js +18 -1
- package/dist/backend/backend/src/controllers/agent-stream/agent-stream.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts +68 -0
- package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.js +233 -5
- package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.routes.js +10 -1
- package/dist/backend/backend/src/controllers/browser/browser.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +8 -3
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts +132 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js +401 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts +29 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js +39 -0
- package/dist/backend/backend/src/controllers/chat-v2/chat-v2.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/index.d.ts +8 -0
- package/dist/backend/backend/src/controllers/chat-v2/index.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/chat-v2/index.js +8 -0
- package/dist/backend/backend/src/controllers/chat-v2/index.js.map +1 -0
- package/dist/backend/backend/src/controllers/onboarding/onboarding.routes.d.ts +13 -13
- package/dist/backend/backend/src/controllers/onboarding/onboarding.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/onboarding/onboarding.routes.js +74 -234
- package/dist/backend/backend/src/controllers/onboarding/onboarding.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.js +76 -15
- package/dist/backend/backend/src/controllers/orchestrator/orchestrator.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/request/request.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/request/request.controller.js +4 -6
- package/dist/backend/backend/src/controllers/request/request.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-management/tasks.controller.d.ts +43 -0
- package/dist/backend/backend/src/controllers/task-management/tasks.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-management/tasks.controller.js +200 -72
- package/dist/backend/backend/src/controllers/task-management/tasks.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/team/team.controller.js +49 -0
- package/dist/backend/backend/src/controllers/team/team.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/team-health/team-health.controller.d.ts +59 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.controller.js +127 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.routes.d.ts +13 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.routes.js +20 -0
- package/dist/backend/backend/src/controllers/team-health/team-health.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts +9 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +233 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +40 -6
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.d.ts +498 -0
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.js +759 -0
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.js.map +1 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +25 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +221 -58
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.d.ts +9 -2
- package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.js +35 -2
- package/dist/backend/backend/src/services/agent/crewly-agent/model-manager.js.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/types.d.ts +8 -2
- package/dist/backend/backend/src/services/agent/crewly-agent/types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/crewly-agent/types.js +1 -0
- package/dist/backend/backend/src/services/agent/crewly-agent/types.js.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux-command.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux-command.service.js +2 -1
- package/dist/backend/backend/src/services/agent/tmux-command.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/tmux.service.js +2 -1
- package/dist/backend/backend/src/services/agent/tmux.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +148 -3
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js +241 -2
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/recovery.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/recovery.module.js +13 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/recovery.module.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/role-boundary.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/role-boundary.module.js +26 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/role-boundary.module.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.d.ts +79 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.d.ts.map +1 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.js +118 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/sop-norm-distinction.module.js.map +1 -0
- package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts +161 -0
- package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-bridge.service.js +382 -2
- package/dist/backend/backend/src/services/browser/browser-bridge.service.js.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts +105 -0
- package/dist/backend/backend/src/services/browser/browser-proxy.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-proxy.service.js +232 -13
- package/dist/backend/backend/src/services/browser/browser-proxy.service.js.map +1 -1
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts +178 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js +254 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.dispatcher.service.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.mention-resolver.d.ts +134 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.mention-resolver.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.mention-resolver.js +232 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.mention-resolver.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.realtime-holder.d.ts +25 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.realtime-holder.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.realtime-holder.js +23 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.realtime-holder.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts +254 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js +467 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.service.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.singleton.d.ts +27 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.singleton.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.singleton.js +57 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.singleton.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.team-membership.d.ts +43 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.team-membership.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.team-membership.js +54 -0
- package/dist/backend/backend/src/services/chat-v2/chat-v2.team-membership.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/config.d.ts +100 -0
- package/dist/backend/backend/src/services/chat-v2/config.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/config.js +174 -0
- package/dist/backend/backend/src/services/chat-v2/config.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/index.d.ts +11 -0
- package/dist/backend/backend/src/services/chat-v2/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/index.js +12 -0
- package/dist/backend/backend/src/services/chat-v2/index.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts +114 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js +194 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/channel.store.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.d.ts +100 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.js +351 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/chat-db.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts +132 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js +281 -0
- package/dist/backend/backend/src/services/chat-v2/sqlite/message.store.js.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/types.d.ts +295 -0
- package/dist/backend/backend/src/services/chat-v2/types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/chat-v2/types.js +61 -0
- package/dist/backend/backend/src/services/chat-v2/types.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-bridge.service.d.ts +113 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-bridge.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-bridge.service.js +179 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-bridge.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-forwarder.service.d.ts +131 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-forwarder.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-forwarder.service.js +227 -0
- package/dist/backend/backend/src/services/cloud/cloud-event-forwarder.service.js.map +1 -0
- package/dist/backend/backend/src/services/core/config.service.js +3 -3
- package/dist/backend/backend/src/services/core/config.service.js.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.d.ts +22 -0
- package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.js +57 -0
- package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-bus.service.d.ts +69 -1
- package/dist/backend/backend/src/services/event-bus/event-bus.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-bus.service.js +118 -0
- package/dist/backend/backend/src/services/event-bus/event-bus.service.js.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts +275 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js +736 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js.map +1 -0
- package/dist/backend/backend/src/services/knowledge/fts5-index.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/knowledge/fts5-index.service.js +18 -2
- package/dist/backend/backend/src/services/knowledge/fts5-index.service.js.map +1 -1
- package/dist/backend/backend/src/services/knowledge/knowledge-search.service.d.ts +49 -13
- package/dist/backend/backend/src/services/knowledge/knowledge-search.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/knowledge/knowledge-search.service.js +123 -29
- package/dist/backend/backend/src/services/knowledge/knowledge-search.service.js.map +1 -1
- package/dist/backend/backend/src/services/knowledge/learnings-index.service.d.ts +159 -0
- package/dist/backend/backend/src/services/knowledge/learnings-index.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/knowledge/learnings-index.service.js +304 -0
- package/dist/backend/backend/src/services/knowledge/learnings-index.service.js.map +1 -0
- package/dist/backend/backend/src/services/knowledge/vector-store.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/knowledge/vector-store.service.js +24 -4
- package/dist/backend/backend/src/services/knowledge/vector-store.service.js.map +1 -1
- package/dist/backend/backend/src/services/memory/auto-learning.subscriber.d.ts +174 -0
- package/dist/backend/backend/src/services/memory/auto-learning.subscriber.d.ts.map +1 -0
- package/dist/backend/backend/src/services/memory/auto-learning.subscriber.js +375 -0
- package/dist/backend/backend/src/services/memory/auto-learning.subscriber.js.map +1 -0
- package/dist/backend/backend/src/services/memory/learning-format.validator.d.ts +97 -0
- package/dist/backend/backend/src/services/memory/learning-format.validator.d.ts.map +1 -0
- package/dist/backend/backend/src/services/memory/learning-format.validator.js +209 -0
- package/dist/backend/backend/src/services/memory/learning-format.validator.js.map +1 -0
- package/dist/backend/backend/src/services/memory/vector-store.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/memory/vector-store.service.js +19 -4
- package/dist/backend/backend/src/services/memory/vector-store.service.js.map +1 -1
- package/dist/backend/backend/src/services/onboarding/onboarding-provision.service.d.ts +16 -5
- package/dist/backend/backend/src/services/onboarding/onboarding-provision.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/onboarding/onboarding-provision.service.js +32 -5
- package/dist/backend/backend/src/services/onboarding/onboarding-provision.service.js.map +1 -1
- package/dist/backend/backend/src/services/onboarding/onboarding.service.d.ts +157 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.service.js +229 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.service.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.types.d.ts +141 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.types.js +18 -0
- package/dist/backend/backend/src/services/onboarding/onboarding.types.js.map +1 -0
- package/dist/backend/backend/src/services/pr-review/pr-review.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/pr-review/pr-review.service.js +1 -1
- package/dist/backend/backend/src/services/pr-review/pr-review.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.js +17 -1
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +39 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +158 -26
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +248 -6
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +531 -51
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/team-health/index.d.ts +16 -0
- package/dist/backend/backend/src/services/team-health/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/index.js +16 -0
- package/dist/backend/backend/src/services/team-health/index.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.d.ts +52 -0
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.js +161 -0
- package/dist/backend/backend/src/services/team-health/live-team-health-data-provider.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/lost-dispatch-detector.d.ts +53 -0
- package/dist/backend/backend/src/services/team-health/lost-dispatch-detector.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/lost-dispatch-detector.js +88 -0
- package/dist/backend/backend/src/services/team-health/lost-dispatch-detector.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/stale-trigger-detector.d.ts +44 -0
- package/dist/backend/backend/src/services/team-health/stale-trigger-detector.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/stale-trigger-detector.js +83 -0
- package/dist/backend/backend/src/services/team-health/stale-trigger-detector.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-alert-router.d.ts +92 -0
- package/dist/backend/backend/src/services/team-health/team-health-alert-router.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-alert-router.js +328 -0
- package/dist/backend/backend/src/services/team-health/team-health-alert-router.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-config.d.ts +41 -0
- package/dist/backend/backend/src/services/team-health/team-health-config.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-config.js +213 -0
- package/dist/backend/backend/src/services/team-health/team-health-config.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-detector.d.ts +46 -0
- package/dist/backend/backend/src/services/team-health/team-health-detector.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-detector.js +347 -0
- package/dist/backend/backend/src/services/team-health/team-health-detector.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-types.d.ts +154 -0
- package/dist/backend/backend/src/services/team-health/team-health-types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-types.js +94 -0
- package/dist/backend/backend/src/services/team-health/team-health-types.js.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-watchdog.service.d.ts +111 -0
- package/dist/backend/backend/src/services/team-health/team-health-watchdog.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-health/team-health-watchdog.service.js +226 -0
- package/dist/backend/backend/src/services/team-health/team-health-watchdog.service.js.map +1 -0
- package/dist/backend/backend/src/services/v3/mission-reminder.service.d.ts +148 -0
- package/dist/backend/backend/src/services/v3/mission-reminder.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/mission-reminder.service.js +545 -0
- package/dist/backend/backend/src/services/v3/mission-reminder.service.js.map +1 -0
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.d.ts +499 -0
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.js +1105 -0
- package/dist/backend/backend/src/services/v3/request-sla.subscriber.js.map +1 -0
- package/dist/backend/backend/src/services/v3/request.service.d.ts +22 -0
- package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.js +71 -0
- package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.d.ts +1 -0
- package/dist/backend/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.js +22 -6
- package/dist/backend/backend/src/services/v3/v3-data.service.js.map +1 -1
- package/dist/backend/backend/src/types/event-bus.types.d.ts +19 -1
- package/dist/backend/backend/src/types/event-bus.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/event-bus.types.js +43 -0
- package/dist/backend/backend/src/types/event-bus.types.js.map +1 -1
- package/dist/backend/backend/src/types/index.d.ts +22 -1
- package/dist/backend/backend/src/types/index.d.ts.map +1 -1
- package/dist/backend/backend/src/types/index.js.map +1 -1
- package/dist/backend/backend/src/types/review-reason.types.d.ts +63 -0
- package/dist/backend/backend/src/types/review-reason.types.d.ts.map +1 -0
- package/dist/backend/backend/src/types/review-reason.types.js +50 -0
- package/dist/backend/backend/src/types/review-reason.types.js.map +1 -0
- package/dist/backend/backend/src/types/slack.types.d.ts +4 -1
- package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/slack.types.js.map +1 -1
- package/dist/backend/backend/src/types/v2/mission.types.d.ts +18 -0
- package/dist/backend/backend/src/types/v2/mission.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/mission.types.js +1 -0
- package/dist/backend/backend/src/types/v2/mission.types.js.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js +25 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/backend/backend/src/utils/team.utils.d.ts +38 -0
- package/dist/backend/backend/src/utils/team.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/team.utils.js +45 -0
- package/dist/backend/backend/src/utils/team.utils.js.map +1 -0
- package/dist/backend/backend/src/websocket/chat-v2.gateway.d.ts +195 -0
- package/dist/backend/backend/src/websocket/chat-v2.gateway.d.ts.map +1 -0
- package/dist/backend/backend/src/websocket/chat-v2.gateway.js +401 -0
- package/dist/backend/backend/src/websocket/chat-v2.gateway.js.map +1 -0
- package/dist/backend/backend/src/websocket/terminal.gateway.d.ts +37 -2
- package/dist/backend/backend/src/websocket/terminal.gateway.d.ts.map +1 -1
- package/dist/backend/backend/src/websocket/terminal.gateway.js +106 -5
- package/dist/backend/backend/src/websocket/terminal.gateway.js.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +69 -1
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +69 -2
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/core/config.service.js +3 -3
- package/dist/cli/backend/src/services/core/config.service.js.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.d.ts +22 -0
- package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.js +57 -0
- package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/cli/backend/src/services/knowledge/fts5-index.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/knowledge/fts5-index.service.js +18 -2
- package/dist/cli/backend/src/services/knowledge/fts5-index.service.js.map +1 -1
- package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts +49 -13
- package/dist/cli/backend/src/services/knowledge/knowledge-search.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js +123 -29
- package/dist/cli/backend/src/services/knowledge/knowledge-search.service.js.map +1 -1
- package/dist/cli/backend/src/services/knowledge/vector-store.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/knowledge/vector-store.service.js +24 -4
- package/dist/cli/backend/src/services/knowledge/vector-store.service.js.map +1 -1
- package/dist/cli/backend/src/types/index.d.ts +22 -1
- package/dist/cli/backend/src/types/index.d.ts.map +1 -1
- package/dist/cli/backend/src/types/index.js.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js +25 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
- package/frontend/dist/assets/{index-70356616.js → index-7a4e7df5.js} +328 -326
- package/frontend/dist/assets/index-b7e59b2b.css +33 -0
- package/frontend/dist/index.html +2 -2
- package/package.json +2 -1
- package/config/skills/orchestrator/recall/SKILL.md +0 -47
- package/config/skills/orchestrator/recall/execute.sh +0 -13
- package/config/skills/orchestrator/record-learning/SKILL.md +0 -47
- package/config/skills/orchestrator/record-learning/execute.sh +0 -13
- package/config/skills/orchestrator/remember/SKILL.md +0 -55
- package/config/skills/orchestrator/remember/execute.sh +0 -15
- package/frontend/dist/assets/index-6aaa0630.css +0 -33
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
import { PoolStorage } from './pool-storage.js';
|
|
12
12
|
import { ClaimService } from './claim.service.js';
|
|
13
13
|
import { LoggerService } from '../core/logger.service.js';
|
|
14
|
-
import {
|
|
14
|
+
import { formatError } from '../../utils/format-error.js';
|
|
15
|
+
import { isWorkItem, isValidWorkItemTransition, isTransitionPermitted } from '../../types/v2/work-item.types.js';
|
|
15
16
|
import { createTaskClaim, } from '../../types/v2/claim.types.js';
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Service
|
|
@@ -42,6 +43,15 @@ export class TaskPoolService {
|
|
|
42
43
|
storage;
|
|
43
44
|
claimService;
|
|
44
45
|
logger;
|
|
46
|
+
/**
|
|
47
|
+
* Optional EventBus reference — wired via {@link setEventBusService} from
|
|
48
|
+
* the boot path. When set, {@link addToPool} publishes a `workitem:queued`
|
|
49
|
+
* event so subscribers (notably {@link RequestSlaSubscriber}) can react to
|
|
50
|
+
* queue mutations. Optional because singleton callers (tests, CLI) bring
|
|
51
|
+
* up the pool before the bus exists; missing bus is treated as a no-op
|
|
52
|
+
* publish at warn level.
|
|
53
|
+
*/
|
|
54
|
+
eventBus = null;
|
|
45
55
|
/**
|
|
46
56
|
* Serializes claim operations to prevent the race where two concurrent
|
|
47
57
|
* claimFromPool / claimSpecificItem calls both select the same queued
|
|
@@ -55,6 +65,17 @@ export class TaskPoolService {
|
|
|
55
65
|
this.claimService = new ClaimService(this.storage);
|
|
56
66
|
this.logger = LoggerService.getInstance().createComponentLogger('TaskPoolService');
|
|
57
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Wire the EventBus reference used by {@link addToPool} to publish
|
|
70
|
+
* `workitem:queued` events (INBOUND-1.f1). Called from the backend boot
|
|
71
|
+
* path after both services have been constructed. Idempotent and may be
|
|
72
|
+
* called with `null` to disable publishing (testing).
|
|
73
|
+
*
|
|
74
|
+
* @param bus - The EventBus instance, or null to clear
|
|
75
|
+
*/
|
|
76
|
+
setEventBusService(bus) {
|
|
77
|
+
this.eventBus = bus;
|
|
78
|
+
}
|
|
58
79
|
/**
|
|
59
80
|
* Chains the given critical section after any in-flight claim operation.
|
|
60
81
|
* Guarantees FIFO ordering even under concurrent invocation.
|
|
@@ -118,6 +139,177 @@ export class TaskPoolService {
|
|
|
118
139
|
type: workItem.type,
|
|
119
140
|
title: workItem.title,
|
|
120
141
|
});
|
|
142
|
+
// INBOUND-1.f1: announce the queue mutation so subscribers (notably the
|
|
143
|
+
// RequestSlaSubscriber) can react. We publish AFTER the storage flush
|
|
144
|
+
// so any subscriber that re-reads via taskPool.findWorkItem sees the
|
|
145
|
+
// committed item. Publish failures are logged-but-isolated — the pool
|
|
146
|
+
// mutation is the source of truth, the event is informational.
|
|
147
|
+
this.publishWorkItemQueued(workItem);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* INBOUND-1.f1 helper: publish a `workitem:queued` event with correlation
|
|
151
|
+
* ids the SLA subscriber needs (`requestId`, `missionId`, plus the new
|
|
152
|
+
* `workItemId`). Called by {@link addToPool} after the storage flush.
|
|
153
|
+
*
|
|
154
|
+
* Stays a separate method (vs inlining) so:
|
|
155
|
+
* 1. The dependency on EventBus stays explicit and grep-able.
|
|
156
|
+
* 2. A future caller adding an alternate enqueue path (e.g. a batch
|
|
157
|
+
* addAll) can route through the same publisher for consistent
|
|
158
|
+
* observability.
|
|
159
|
+
* 3. Error handling stays in one place — a thrown publisher must NOT
|
|
160
|
+
* back out the pool mutation (the storage write already committed).
|
|
161
|
+
*/
|
|
162
|
+
publishWorkItemQueued(workItem) {
|
|
163
|
+
if (!this.eventBus) {
|
|
164
|
+
this.logger.debug('No EventBus wired — skipping workitem:queued publish', {
|
|
165
|
+
workItemId: workItem.id,
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
this.eventBus.publish({
|
|
171
|
+
// Deterministic event id keyed on the WI id so a redelivered storage
|
|
172
|
+
// path (theoretical) collapses through the bus's per-(type,session)
|
|
173
|
+
// debounce window without firing the SLA handler twice.
|
|
174
|
+
id: `workitem:queued:${workItem.id}`,
|
|
175
|
+
type: 'workitem:queued',
|
|
176
|
+
timestamp: new Date().toISOString(),
|
|
177
|
+
teamId: '',
|
|
178
|
+
teamName: '',
|
|
179
|
+
memberId: '',
|
|
180
|
+
memberName: '',
|
|
181
|
+
// sessionName is empty — `workitem:queued` is a system-level event
|
|
182
|
+
// not attributable to a specific agent session. The bus's dedup key
|
|
183
|
+
// is `${type}:${sessionName}` so a unique sessionName per WI would
|
|
184
|
+
// defeat the dedup; an empty sessionName scoped per-id is fine since
|
|
185
|
+
// the event id already encodes the WI uniquely.
|
|
186
|
+
sessionName: '',
|
|
187
|
+
previousValue: '',
|
|
188
|
+
newValue: workItem.status,
|
|
189
|
+
changedField: 'taskStatus',
|
|
190
|
+
// INBOUND-1.f1 correlation fields. Mandatory: workItemId. Optional:
|
|
191
|
+
// requestId, missionId — populated when the WI carries them. The SLA
|
|
192
|
+
// subscriber no-ops when requestId is undefined (per spec), so we
|
|
193
|
+
// don't need a fallback chain here.
|
|
194
|
+
workItemId: workItem.id,
|
|
195
|
+
requestId: workItem.requestId,
|
|
196
|
+
missionId: workItem.missionId,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
this.logger.warn('workitem:queued publish threw', {
|
|
201
|
+
workItemId: workItem.id,
|
|
202
|
+
error: formatError(err),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* F1-BRIDGE-1 helper: publish `task:done_by_worker` after a successful
|
|
208
|
+
* `submitForVerification` transition. The {@link EventToWorkItemBridge}
|
|
209
|
+
* subscribes to this event and creates a verification WorkItem for the
|
|
210
|
+
* resolved team-lead session.
|
|
211
|
+
*
|
|
212
|
+
* Stays a separate method (mirrors {@link publishWorkItemQueued}) so:
|
|
213
|
+
* 1. The dependency on EventBus stays explicit and grep-able — Arch's
|
|
214
|
+
* grep guard checks both the type declaration AND a publish call
|
|
215
|
+
* site for `task:done_by_worker`.
|
|
216
|
+
* 2. Error handling is uniform with the other publishers — a thrown
|
|
217
|
+
* bus must NOT back out the verified state transition; the storage
|
|
218
|
+
* flush has already committed.
|
|
219
|
+
* 3. A future caller adding an alternate verification-submission path
|
|
220
|
+
* (e.g. an admin endpoint or a CLI) routes through the same publisher.
|
|
221
|
+
*
|
|
222
|
+
* The deterministic event id (`task:done_by_worker:${workItemId}`) keys
|
|
223
|
+
* dedup so a redelivered submitForVerification (theoretical) collapses
|
|
224
|
+
* through the bus without firing the bridge handler twice.
|
|
225
|
+
*/
|
|
226
|
+
publishTaskDoneByWorker(workItem) {
|
|
227
|
+
if (!this.eventBus) {
|
|
228
|
+
this.logger.debug('No EventBus wired — skipping task:done_by_worker publish', {
|
|
229
|
+
workItemId: workItem.id,
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
this.eventBus.publish({
|
|
235
|
+
id: `task:done_by_worker:${workItem.id}`,
|
|
236
|
+
type: 'task:done_by_worker',
|
|
237
|
+
timestamp: new Date().toISOString(),
|
|
238
|
+
teamId: '',
|
|
239
|
+
teamName: '',
|
|
240
|
+
memberId: '',
|
|
241
|
+
memberName: '',
|
|
242
|
+
// sessionName is empty — `task:done_by_worker` is published from the
|
|
243
|
+
// pool layer which doesn't carry the worker's PTY session. The bridge
|
|
244
|
+
// uses `workItemId` (and falls back to `taskId`) to resolve the
|
|
245
|
+
// source WI; sessionName is informational. Empty matches the
|
|
246
|
+
// workitem:queued publish convention.
|
|
247
|
+
sessionName: '',
|
|
248
|
+
// The state machine just landed at done_by_worker. Carry both ends
|
|
249
|
+
// so any subscriber filtering on (previousValue, newValue) sees
|
|
250
|
+
// the canonical transition.
|
|
251
|
+
previousValue: 'running',
|
|
252
|
+
newValue: 'done_by_worker',
|
|
253
|
+
changedField: 'taskStatus',
|
|
254
|
+
// BRIDGE-1.1 hybrid extension. `workItemId` is mandatory for the
|
|
255
|
+
// bridge handler; the `taskId` fallback path is unused here because
|
|
256
|
+
// WorkItem itself does not carry a taskId (the bridge resolves the
|
|
257
|
+
// source WI by id alone via taskPool.findWorkItem).
|
|
258
|
+
workItemId: workItem.id,
|
|
259
|
+
missionId: workItem.missionId,
|
|
260
|
+
requestId: workItem.requestId,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
this.logger.warn('task:done_by_worker publish threw', {
|
|
265
|
+
workItemId: workItem.id,
|
|
266
|
+
error: formatError(err),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* F1-BRIDGE-1 helper: publish `task:rejected` after a successful
|
|
272
|
+
* `verifyItem` transition with verdict='rejected'. The
|
|
273
|
+
* {@link EventToWorkItemBridge} subscribes and either creates a retry
|
|
274
|
+
* WI (`retryCount < maxRetries`) or escalates to a TL review WI with
|
|
275
|
+
* `reviewReason='max_retries_exceeded'` at the cap.
|
|
276
|
+
*
|
|
277
|
+
* Mirrors {@link publishTaskDoneByWorker} for symmetry. Only the
|
|
278
|
+
* verdict='rejected' branch reaches this publisher — `verdict='verified'`
|
|
279
|
+
* does NOT publish (terminal-success path goes through
|
|
280
|
+
* {@link resolveBlockedDependents}, which is not a bridge trigger).
|
|
281
|
+
*/
|
|
282
|
+
publishTaskRejected(workItem) {
|
|
283
|
+
if (!this.eventBus) {
|
|
284
|
+
this.logger.debug('No EventBus wired — skipping task:rejected publish', {
|
|
285
|
+
workItemId: workItem.id,
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
this.eventBus.publish({
|
|
291
|
+
id: `task:rejected:${workItem.id}`,
|
|
292
|
+
type: 'task:rejected',
|
|
293
|
+
timestamp: new Date().toISOString(),
|
|
294
|
+
teamId: '',
|
|
295
|
+
teamName: '',
|
|
296
|
+
memberId: '',
|
|
297
|
+
memberName: '',
|
|
298
|
+
sessionName: '',
|
|
299
|
+
previousValue: 'done_by_worker',
|
|
300
|
+
newValue: 'rejected',
|
|
301
|
+
changedField: 'taskStatus',
|
|
302
|
+
workItemId: workItem.id,
|
|
303
|
+
missionId: workItem.missionId,
|
|
304
|
+
requestId: workItem.requestId,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
this.logger.warn('task:rejected publish threw', {
|
|
309
|
+
workItemId: workItem.id,
|
|
310
|
+
error: formatError(err),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
121
313
|
}
|
|
122
314
|
/**
|
|
123
315
|
* Claims the next available WorkItem from the pool for an agent.
|
|
@@ -159,13 +351,15 @@ export class TaskPoolService {
|
|
|
159
351
|
return null;
|
|
160
352
|
}
|
|
161
353
|
const selected = candidates[0];
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
354
|
+
// TRANS-2: route the queued → running flip through the guarded
|
|
355
|
+
// transitionStatus helper so internal callers are subject to the
|
|
356
|
+
// same V3 actor-role + state-machine gates as external callers.
|
|
357
|
+
// `startedAt` is set automatically by transitionStatus when
|
|
358
|
+
// newStatus === 'running'; the mutator carries the agent target.
|
|
359
|
+
const claimedItem = await this.transitionStatus(selected.id, 'running', 'system', (wi) => {
|
|
166
360
|
wi.target = agentId;
|
|
167
361
|
});
|
|
168
|
-
if (!
|
|
362
|
+
if (!claimedItem) {
|
|
169
363
|
this.logger.warn('Failed to update WorkItem during claim', { workItemId: selected.id });
|
|
170
364
|
return null;
|
|
171
365
|
}
|
|
@@ -183,8 +377,6 @@ export class TaskPoolService {
|
|
|
183
377
|
claimId: claim.id,
|
|
184
378
|
title: selected.title,
|
|
185
379
|
});
|
|
186
|
-
// Return the updated item
|
|
187
|
-
const claimedItem = await this.storage.findWorkItem(selected.id);
|
|
188
380
|
return {
|
|
189
381
|
workItem: claimedItem,
|
|
190
382
|
claim,
|
|
@@ -214,20 +406,19 @@ export class TaskPoolService {
|
|
|
214
406
|
const claims = await this.storage.getClaims();
|
|
215
407
|
if (claims.some((c) => c.workItemId === workItemId && c.status === 'active'))
|
|
216
408
|
return null;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
409
|
+
// TRANS-2: route the queued → running flip through transitionStatus
|
|
410
|
+
// (mirrors claimFromPool — same V3 + state-machine gates).
|
|
411
|
+
const claimedItem = await this.transitionStatus(workItemId, 'running', 'system', (wi) => {
|
|
220
412
|
wi.target = agentId;
|
|
221
413
|
});
|
|
222
|
-
if (!
|
|
414
|
+
if (!claimedItem)
|
|
223
415
|
return null;
|
|
224
416
|
const claimInput = { workItemId, agentId };
|
|
225
417
|
const claim = createTaskClaim(claimInput);
|
|
226
418
|
await this.storage.addClaim(claim);
|
|
227
419
|
await this.storage.flush();
|
|
228
420
|
this.logger.info('WorkItem claimed (specific)', { workItemId, agentId, claimId: claim.id });
|
|
229
|
-
|
|
230
|
-
return claimedItem ? { workItem: claimedItem, claim } : null;
|
|
421
|
+
return { workItem: claimedItem, claim };
|
|
231
422
|
});
|
|
232
423
|
}
|
|
233
424
|
/**
|
|
@@ -256,11 +447,14 @@ export class TaskPoolService {
|
|
|
256
447
|
c.endReason = reason;
|
|
257
448
|
});
|
|
258
449
|
}
|
|
259
|
-
//
|
|
260
|
-
//
|
|
450
|
+
// TRANS-2: route the (running|blocked) → queued flip through the
|
|
451
|
+
// guarded transitionStatus helper. The state-machine entry for
|
|
452
|
+
// `running → queued` is a TRANS-2 addition, gated to system/TL/orc;
|
|
453
|
+
// `blocked → queued` was already TL/orc/system-gated by TRANS-1.
|
|
454
|
+
// Side-effect mutations (startedAt clear, target preservation when
|
|
455
|
+
// unblocking, retryCount bump) move into the atomic mutator.
|
|
261
456
|
const wasBlocked = workItem.status === 'blocked';
|
|
262
|
-
await this.
|
|
263
|
-
wi.status = 'queued';
|
|
457
|
+
await this.transitionStatus(workItemId, 'queued', 'system', (wi) => {
|
|
264
458
|
wi.startedAt = undefined;
|
|
265
459
|
if (!wasBlocked) {
|
|
266
460
|
wi.target = undefined;
|
|
@@ -275,40 +469,204 @@ export class TaskPoolService {
|
|
|
275
469
|
});
|
|
276
470
|
}
|
|
277
471
|
/**
|
|
278
|
-
*
|
|
472
|
+
* Decide whether a WorkItem requires TL verification before reaching a
|
|
473
|
+
* terminal-success status.
|
|
279
474
|
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
475
|
+
* Default policy (VERIF-1):
|
|
476
|
+
* - `delegate` items default to *requires verification* — a worker
|
|
477
|
+
* marking their own delegated work as "done" should produce
|
|
478
|
+
* `done_by_worker` and wake the TL for sign-off.
|
|
479
|
+
* - All other types (`cron_run`, `notify`, `reconcile`, `check`,
|
|
480
|
+
* `confirm`, `review`, ...) default to simple completion (`done`).
|
|
481
|
+
*
|
|
482
|
+
* The default is overridable via `wi.metadata.requiresVerification`:
|
|
483
|
+
* - `true` — force verification path even for non-delegate types
|
|
484
|
+
* - `false` — skip verification even for delegate types (this is the
|
|
485
|
+
* F-H affordance REVIEW-1 needs: review WIs are themselves the
|
|
486
|
+
* verification step, so they must NOT loop back to TL self-verify)
|
|
487
|
+
*
|
|
488
|
+
* @param wi - The WorkItem under inspection
|
|
489
|
+
* @returns true when the verification path should fire
|
|
283
490
|
*/
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
491
|
+
requiresVerification(wi) {
|
|
492
|
+
const metaFlag = wi.metadata?.requiresVerification;
|
|
493
|
+
if (metaFlag === true)
|
|
494
|
+
return true;
|
|
495
|
+
if (metaFlag === false)
|
|
496
|
+
return false;
|
|
497
|
+
return wi.type === 'delegate';
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Internal: release the active claim on a WorkItem (if any). Shared by
|
|
501
|
+
* `completeSimpleItem` and `submitForVerification` because both
|
|
502
|
+
* represent the worker handing the item back to the system.
|
|
503
|
+
*
|
|
504
|
+
* @param workItemId - WorkItem whose claim should be released
|
|
505
|
+
* @param endReason - Reason recorded on the claim (`completed` /
|
|
506
|
+
* `submitted_for_verification`) for auditability
|
|
507
|
+
*/
|
|
508
|
+
async releaseClaim(workItemId, endReason) {
|
|
293
509
|
const claim = await this.storage.findActiveClaimByWorkItem(workItemId);
|
|
294
|
-
if (claim)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
510
|
+
if (!claim)
|
|
511
|
+
return;
|
|
512
|
+
await this.storage.updateClaim(claim.id, (c) => {
|
|
513
|
+
c.status = 'released';
|
|
514
|
+
c.endedAt = new Date().toISOString();
|
|
515
|
+
c.endReason = endReason;
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Worker reports a delegated WorkItem as done and submits it for TL
|
|
520
|
+
* verification.
|
|
521
|
+
*
|
|
522
|
+
* Transitions `running → done_by_worker` via {@link transitionStatus},
|
|
523
|
+
* which enforces the V3 actor-role gate (`'agent'` is allowed; any
|
|
524
|
+
* other role throws). The active claim is released as
|
|
525
|
+
* `submitted_for_verification` so the TL queue is the only thing
|
|
526
|
+
* blocking forward progress.
|
|
527
|
+
*
|
|
528
|
+
* Used by the `completeItem` facade for `delegate`-type items and any
|
|
529
|
+
* item whose `metadata.requiresVerification === true`.
|
|
530
|
+
*
|
|
531
|
+
* @param workItemId - WorkItem id
|
|
532
|
+
* @param actorRole - Role of the caller (`'agent'` for normal worker
|
|
533
|
+
* submissions; passed through to `isTransitionPermitted`)
|
|
534
|
+
* @param result - Optional result payload to attach to the WorkItem
|
|
535
|
+
* @returns The updated WorkItem, or `null` if the WI was deleted
|
|
536
|
+
* between the find and the update (race window)
|
|
537
|
+
* @throws When the WorkItem is missing, the transition is invalid, or
|
|
538
|
+
* the actor is not permitted to perform `running → done_by_worker`.
|
|
539
|
+
*/
|
|
540
|
+
async submitForVerification(workItemId, actorRole, result) {
|
|
541
|
+
await this.releaseClaim(workItemId, 'submitted_for_verification');
|
|
542
|
+
const updated = await this.transitionStatus(workItemId, 'done_by_worker', actorRole, (wi) => {
|
|
543
|
+
if (result)
|
|
544
|
+
wi.result = result;
|
|
545
|
+
});
|
|
546
|
+
await this.storage.flush();
|
|
547
|
+
this.logger.info('WorkItem submitted for verification', {
|
|
548
|
+
workItemId,
|
|
549
|
+
actorRole,
|
|
550
|
+
// BRIDGE-1 turns this into a real `task:done_by_worker` event below
|
|
551
|
+
// (F1-BRIDGE-1). The log line is preserved as a wake signal for any
|
|
552
|
+
// tail-based TL-watching subscriber that still greps it.
|
|
553
|
+
tlWakeRequested: true,
|
|
554
|
+
});
|
|
555
|
+
// F1-BRIDGE-1: publish AFTER the storage flush so any subscriber that
|
|
556
|
+
// re-reads via taskPool.findWorkItem sees the committed `done_by_worker`
|
|
557
|
+
// status. `updated` is null only on race-window deletion — skip the
|
|
558
|
+
// publish in that case (no source WI for the bridge to resolve).
|
|
559
|
+
if (updated) {
|
|
560
|
+
this.publishTaskDoneByWorker(updated);
|
|
300
561
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
562
|
+
return updated;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Worker (or system) marks a non-delegated WorkItem as fully done.
|
|
566
|
+
*
|
|
567
|
+
* Transitions `running → done` via {@link transitionStatus}. Used by
|
|
568
|
+
* the `completeItem` facade for `cron_run`, `notify`, `reconcile`,
|
|
569
|
+
* `check`, `confirm`, `review` types — anything whose lifecycle does
|
|
570
|
+
* NOT include a TL verification step.
|
|
571
|
+
*
|
|
572
|
+
* The Reconciler service is the only system-actor caller and uses
|
|
573
|
+
* `actorRole='system'`, which bypasses the actor check while still
|
|
574
|
+
* respecting the state-machine legality check.
|
|
575
|
+
*
|
|
576
|
+
* @param workItemId - WorkItem id
|
|
577
|
+
* @param actorRole - Role of the caller (`'agent'`, `'system'`, etc.)
|
|
578
|
+
* @param result - Optional result payload to attach
|
|
579
|
+
* @returns The updated WorkItem, or `null` if the WI was deleted
|
|
580
|
+
* between the find and the update (race window)
|
|
581
|
+
* @throws When the WorkItem is missing, the transition is invalid, or
|
|
582
|
+
* the actor is not permitted to perform `running → done`.
|
|
583
|
+
*/
|
|
584
|
+
async completeSimpleItem(workItemId, actorRole, result) {
|
|
585
|
+
await this.releaseClaim(workItemId, 'completed');
|
|
586
|
+
const updated = await this.transitionStatus(workItemId, 'done', actorRole, (wi) => {
|
|
305
587
|
if (result)
|
|
306
588
|
wi.result = result;
|
|
307
589
|
});
|
|
308
590
|
// Promote any blocked dependents whose deps are now all satisfied.
|
|
309
591
|
await this.resolveBlockedDependents(workItemId);
|
|
310
592
|
await this.storage.flush();
|
|
311
|
-
this.logger.info('WorkItem completed', { workItemId });
|
|
593
|
+
this.logger.info('WorkItem completed', { workItemId, actorRole });
|
|
594
|
+
return updated;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* TL records a verdict on a WorkItem in `done_by_worker` status.
|
|
598
|
+
*
|
|
599
|
+
* `verified` advances to terminal success and unblocks dependents;
|
|
600
|
+
* `rejected` parks the item until the TL re-queues it (which TRANS-1
|
|
601
|
+
* gates to TL/orchestrator/system actors). Worker-actor calls throw
|
|
602
|
+
* automatically via {@link transitionStatus}'s permission gate — we
|
|
603
|
+
* do NOT need to add an explicit role check here, the matrix in
|
|
604
|
+
* `TRANSITION_PERMISSIONS` handles it.
|
|
605
|
+
*
|
|
606
|
+
* @param workItemId - WorkItem id (must currently be `done_by_worker`)
|
|
607
|
+
* @param actorRole - Role of the caller (`'team_lead'` or `'orchestrator'`
|
|
608
|
+
* for verify/reject; worker calls throw)
|
|
609
|
+
* @param verdict - `'verified'` or `'rejected'`
|
|
610
|
+
* @param comment - Optional reviewer comment recorded in `wi.error`
|
|
611
|
+
* (the field is reused — the WorkItem schema does not yet have a
|
|
612
|
+
* dedicated `verifierComment` slot; keeping this on `error` lets
|
|
613
|
+
* downstream UIs render TL feedback alongside failure causes)
|
|
614
|
+
* @returns The updated WorkItem, or `null` on race-window deletion
|
|
615
|
+
* @throws When the WorkItem is missing, the verdict is invalid, the
|
|
616
|
+
* transition is illegal, or the actor is not permitted to verify.
|
|
617
|
+
*/
|
|
618
|
+
async verifyItem(workItemId, actorRole, verdict, comment) {
|
|
619
|
+
if (verdict !== 'verified' && verdict !== 'rejected') {
|
|
620
|
+
throw new Error(`Invalid verdict: "${verdict}". Must be "verified" or "rejected".`);
|
|
621
|
+
}
|
|
622
|
+
const updated = await this.transitionStatus(workItemId, verdict, actorRole, (wi) => {
|
|
623
|
+
if (comment)
|
|
624
|
+
wi.error = comment;
|
|
625
|
+
});
|
|
626
|
+
if (verdict === 'verified') {
|
|
627
|
+
await this.resolveBlockedDependents(workItemId);
|
|
628
|
+
}
|
|
629
|
+
await this.storage.flush();
|
|
630
|
+
this.logger.info('WorkItem verdict recorded', { workItemId, verdict, actorRole });
|
|
631
|
+
// F1-BRIDGE-1: only the `rejected` branch publishes. The bridge's
|
|
632
|
+
// task:rejected handler creates the retry-or-escalate WI; the `verified`
|
|
633
|
+
// branch is terminal-success and unblocks dependents above without any
|
|
634
|
+
// bridge involvement. Skip the publish on race-window deletion.
|
|
635
|
+
if (verdict === 'rejected' && updated) {
|
|
636
|
+
this.publishTaskRejected(updated);
|
|
637
|
+
}
|
|
638
|
+
return updated;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Legacy facade — picks the verification path for the caller.
|
|
642
|
+
*
|
|
643
|
+
* Existing call sites (REST controller, task-management controllers,
|
|
644
|
+
* V3 data service) invoke `completeItem(id, result)` without an
|
|
645
|
+
* explicit actor role. The facade reads the WorkItem, applies the
|
|
646
|
+
* {@link requiresVerification} policy, and dispatches to either
|
|
647
|
+
* {@link submitForVerification} (delegate items / explicit opt-in)
|
|
648
|
+
* or {@link completeSimpleItem} (everything else).
|
|
649
|
+
*
|
|
650
|
+
* The legacy actor role for these implicit callers is `'agent'`.
|
|
651
|
+
* Migrations to explicit-actor calls can land in follow-up tickets
|
|
652
|
+
* without touching the five call sites in this PR.
|
|
653
|
+
*
|
|
654
|
+
* @param workItemId - WorkItem id
|
|
655
|
+
* @param result - Optional result payload
|
|
656
|
+
* @throws When the WorkItem is missing or the underlying transition
|
|
657
|
+
* is rejected (invalid state, forbidden actor).
|
|
658
|
+
*/
|
|
659
|
+
async completeItem(workItemId, result) {
|
|
660
|
+
const workItem = await this.storage.findWorkItem(workItemId);
|
|
661
|
+
if (!workItem) {
|
|
662
|
+
throw new Error(`WorkItem not found: ${workItemId}`);
|
|
663
|
+
}
|
|
664
|
+
if (this.requiresVerification(workItem)) {
|
|
665
|
+
await this.submitForVerification(workItemId, 'agent', result);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
await this.completeSimpleItem(workItemId, 'agent', result);
|
|
669
|
+
}
|
|
312
670
|
}
|
|
313
671
|
/**
|
|
314
672
|
* Scans blocked WorkItems that list `completedId` in their `dependsOn` and
|
|
@@ -332,9 +690,12 @@ export class TaskPoolService {
|
|
|
332
690
|
const allSatisfied = (candidate.dependsOn ?? []).every((depId) => terminalSuccess.has(depId));
|
|
333
691
|
if (!allSatisfied)
|
|
334
692
|
continue;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
693
|
+
// TRANS-2: route the blocked → queued promotion through
|
|
694
|
+
// transitionStatus. The 'system' actor matches both the V3
|
|
695
|
+
// permission gate (blocked→queued requires TL/orc/system) and
|
|
696
|
+
// the existing intent — dependency resolution is server-side
|
|
697
|
+
// bookkeeping, not a user-initiated action.
|
|
698
|
+
await this.transitionStatus(candidate.id, 'queued', 'system');
|
|
338
699
|
this.logger.info('WorkItem unblocked — all deps satisfied', {
|
|
339
700
|
workItemId: candidate.id,
|
|
340
701
|
via: completedId,
|
|
@@ -366,10 +727,10 @@ export class TaskPoolService {
|
|
|
366
727
|
c.endReason = `failed: ${error}`;
|
|
367
728
|
});
|
|
368
729
|
}
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
730
|
+
// TRANS-2: route the running → failed flip through transitionStatus.
|
|
731
|
+
// `completedAt` is set automatically when newStatus === 'failed'; the
|
|
732
|
+
// mutator only needs to attach the error description.
|
|
733
|
+
await this.transitionStatus(workItemId, 'failed', 'system', (wi) => {
|
|
373
734
|
wi.error = error;
|
|
374
735
|
});
|
|
375
736
|
await this.storage.flush();
|
|
@@ -514,6 +875,22 @@ export class TaskPoolService {
|
|
|
514
875
|
async getAllItems() {
|
|
515
876
|
return this.storage.getWorkItems();
|
|
516
877
|
}
|
|
878
|
+
/**
|
|
879
|
+
* Find a WorkItem by id without mutating it.
|
|
880
|
+
*
|
|
881
|
+
* Public read accessor used by callers that need to inspect a
|
|
882
|
+
* specific item — for example REVIEW-1's reentrancy lock checks the
|
|
883
|
+
* status of a Mission's `pendingReviewWorkItemId` to decide whether
|
|
884
|
+
* to clear the lock. Returns `null` (not `undefined`) so callers can
|
|
885
|
+
* use a uniform null-fallthrough idiom shared with
|
|
886
|
+
* `getWorkItemSnapshot` and `transitionStatus`.
|
|
887
|
+
*
|
|
888
|
+
* @param workItemId - WorkItem id to look up
|
|
889
|
+
* @returns The WorkItem, or `null` if no item has that id
|
|
890
|
+
*/
|
|
891
|
+
async findWorkItem(workItemId) {
|
|
892
|
+
return (await this.storage.findWorkItem(workItemId)) ?? null;
|
|
893
|
+
}
|
|
517
894
|
/**
|
|
518
895
|
* Removes a WorkItem from the pool entirely.
|
|
519
896
|
* Used for purging old completed/cancelled items.
|
|
@@ -543,11 +920,19 @@ export class TaskPoolService {
|
|
|
543
920
|
* Updates a work item's status directly.
|
|
544
921
|
* Used by the Reconciler for corrections (e.g., stuck → blocked).
|
|
545
922
|
*
|
|
923
|
+
* Reconciler invocations pass through `actorRole='system'` (default) which
|
|
924
|
+
* bypasses the per-role gate at {@link isTransitionPermitted} but still
|
|
925
|
+
* enforces the state-machine via {@link isValidWorkItemTransition}. Other
|
|
926
|
+
* callers MUST supply the actor's role so TRANS-1 V3 enforcement applies.
|
|
927
|
+
*
|
|
546
928
|
* @param workItemId - The work item ID
|
|
547
929
|
* @param newStatus - The target status
|
|
548
|
-
* @
|
|
930
|
+
* @param actorRole - Role of the caller (defaults to `'system'` for Reconciler)
|
|
931
|
+
* @throws Error if work item not found
|
|
932
|
+
* @throws Error if transition is invalid (state machine — see WORK_ITEM_TRANSITIONS)
|
|
933
|
+
* @throws Error if actor is not permitted (role check — see TRANSITION_PERMISSIONS)
|
|
549
934
|
*/
|
|
550
|
-
async updateItemStatus(workItemId, newStatus) {
|
|
935
|
+
async updateItemStatus(workItemId, newStatus, actorRole = 'system') {
|
|
551
936
|
const items = await this.storage.getWorkItems();
|
|
552
937
|
const item = items.find((wi) => wi.id === workItemId);
|
|
553
938
|
if (!item) {
|
|
@@ -556,6 +941,11 @@ export class TaskPoolService {
|
|
|
556
941
|
if (!isValidWorkItemTransition(item.status, newStatus)) {
|
|
557
942
|
throw new Error(`Invalid status transition for WorkItem ${workItemId}: ${item.status} → ${newStatus}`);
|
|
558
943
|
}
|
|
944
|
+
// TRANS-1 V3: enforce per-role permissions. system role always passes.
|
|
945
|
+
if (!isTransitionPermitted(item.status, newStatus, actorRole)) {
|
|
946
|
+
throw new Error(`Forbidden transition for WorkItem ${workItemId}: actor='${actorRole}' ` +
|
|
947
|
+
`not permitted to perform ${item.status} → ${newStatus}.`);
|
|
948
|
+
}
|
|
559
949
|
await this.storage.updateWorkItem(workItemId, (wi) => {
|
|
560
950
|
wi.status = newStatus;
|
|
561
951
|
// Use startedAt for running, completedAt for done/failed
|
|
@@ -570,7 +960,97 @@ export class TaskPoolService {
|
|
|
570
960
|
workItemId,
|
|
571
961
|
from: item.status,
|
|
572
962
|
to: newStatus,
|
|
963
|
+
actorRole,
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Public guarded transition helper — TRANS-1's canonical entrypoint.
|
|
968
|
+
*
|
|
969
|
+
* Routes any externally-initiated WorkItem status change through the
|
|
970
|
+
* combined state-machine + actor-role gates. Designed as the public API
|
|
971
|
+
* VERIF-1 will call from `submitForVerification` / `verifyItem`, and as
|
|
972
|
+
* the recommended path for any future caller that previously reached
|
|
973
|
+
* for `storage.updateWorkItem` to flip status.
|
|
974
|
+
*
|
|
975
|
+
* Differences vs {@link updateItemStatus}:
|
|
976
|
+
* - Requires `actorRole` explicitly (no default) — forces the caller to
|
|
977
|
+
* decide the trust posture rather than silently inheriting `'system'`.
|
|
978
|
+
* - Accepts an optional `mutator` so callers can carry additional field
|
|
979
|
+
* updates (e.g. `result`, `error`, `completedAt`) atomically with the
|
|
980
|
+
* status flip — preventing races between status update and metadata
|
|
981
|
+
* attachment that direct `storage.updateWorkItem` callers risked.
|
|
982
|
+
*
|
|
983
|
+
* @param workItemId - The work item ID
|
|
984
|
+
* @param newStatus - The target status
|
|
985
|
+
* @param actorRole - Role of the caller (REQUIRED; pass `'system'` for trusted server-internal paths)
|
|
986
|
+
* @param mutator - Optional additional WorkItem field updates applied atomically with the status change
|
|
987
|
+
* @returns The updated WorkItem after the transition
|
|
988
|
+
* @throws Error if work item not found
|
|
989
|
+
* @throws Error if transition is invalid (state machine)
|
|
990
|
+
* @throws Error if actor is not permitted (role check)
|
|
991
|
+
*
|
|
992
|
+
* @example
|
|
993
|
+
* ```typescript
|
|
994
|
+
* // VERIF-1 worker submitting for verification
|
|
995
|
+
* await pool.transitionStatus(wiId, 'done_by_worker', 'agent', (wi) => {
|
|
996
|
+
* wi.result = output;
|
|
997
|
+
* });
|
|
998
|
+
*
|
|
999
|
+
* // VERIF-1 TL verifying
|
|
1000
|
+
* await pool.transitionStatus(wiId, 'verified', 'team_lead');
|
|
1001
|
+
*
|
|
1002
|
+
* // VERIF-1 TL rejecting (allowed for TL only)
|
|
1003
|
+
* await pool.transitionStatus(wiId, 'rejected', 'team_lead', (wi) => {
|
|
1004
|
+
* wi.error = 'Did not meet acceptance criteria';
|
|
1005
|
+
* });
|
|
1006
|
+
* ```
|
|
1007
|
+
*/
|
|
1008
|
+
async transitionStatus(workItemId, newStatus, actorRole, mutator) {
|
|
1009
|
+
const item = await this.storage.findWorkItem(workItemId);
|
|
1010
|
+
if (!item) {
|
|
1011
|
+
throw new Error(`WorkItem not found: ${workItemId}`);
|
|
1012
|
+
}
|
|
1013
|
+
if (!isValidWorkItemTransition(item.status, newStatus)) {
|
|
1014
|
+
throw new Error(`Invalid status transition for WorkItem ${workItemId}: ${item.status} → ${newStatus}`);
|
|
1015
|
+
}
|
|
1016
|
+
if (!isTransitionPermitted(item.status, newStatus, actorRole)) {
|
|
1017
|
+
throw new Error(`Forbidden transition for WorkItem ${workItemId}: actor='${actorRole}' ` +
|
|
1018
|
+
`not permitted to perform ${item.status} → ${newStatus}.`);
|
|
1019
|
+
}
|
|
1020
|
+
const ok = await this.storage.updateWorkItem(workItemId, (wi) => {
|
|
1021
|
+
wi.status = newStatus;
|
|
1022
|
+
// Standard timestamp side-effects mirror updateItemStatus so callers
|
|
1023
|
+
// get consistent metadata regardless of which API they used.
|
|
1024
|
+
if (newStatus === 'running') {
|
|
1025
|
+
wi.startedAt = new Date().toISOString();
|
|
1026
|
+
}
|
|
1027
|
+
else if (newStatus === 'done' ||
|
|
1028
|
+
newStatus === 'failed' ||
|
|
1029
|
+
newStatus === 'verified' ||
|
|
1030
|
+
newStatus === 'done_by_worker' ||
|
|
1031
|
+
newStatus === 'rejected') {
|
|
1032
|
+
wi.completedAt = new Date().toISOString();
|
|
1033
|
+
}
|
|
1034
|
+
if (mutator)
|
|
1035
|
+
mutator(wi);
|
|
1036
|
+
});
|
|
1037
|
+
if (!ok) {
|
|
1038
|
+
// Race window: someone else removed the WorkItem between findWorkItem
|
|
1039
|
+
// and updateWorkItem. Surface as null so callers can distinguish from
|
|
1040
|
+
// a thrown invalid-transition / forbidden-transition error.
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
this.logger.info('WorkItem transitioned', {
|
|
1044
|
+
workItemId,
|
|
1045
|
+
from: item.status,
|
|
1046
|
+
to: newStatus,
|
|
1047
|
+
actorRole,
|
|
573
1048
|
});
|
|
1049
|
+
// Return the post-update WorkItem snapshot so callers can chain on the
|
|
1050
|
+
// resolved value rather than re-fetching. Coerce `undefined` (item
|
|
1051
|
+
// removed during the race window) to `null` to match the declared
|
|
1052
|
+
// return type.
|
|
1053
|
+
return (await this.storage.findWorkItem(workItemId)) ?? null;
|
|
574
1054
|
}
|
|
575
1055
|
/**
|
|
576
1056
|
* Update token usage and cost on a WorkItem.
|