@chorus-aidlc/chorus 0.16.1 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +373 -372
- package/.next/standalone/.next/app-path-routes-manifest.json +41 -41
- package/.next/standalone/.next/build-manifest.json +5 -5
- package/.next/standalone/.next/prerender-manifest.json +42 -42
- package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/graph/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/graph/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/graph/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/page.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/companies/[uuid]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin/companies/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin/companies/new.html +2 -2
- package/.next/standalone/.next/server/app/admin/companies/new.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/companies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin/companies.html +2 -2
- package/.next/standalone/.next/server/app/admin/companies.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/admin.html +2 -2
- package/.next/standalone/.next/server/app/admin.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin/companies/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/companies/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/login/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/admin/session/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agent-connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent-connections/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/agents/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/api-keys/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/api-keys/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/callback/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/check-default/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/company-oidc/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/default-login/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/identify/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/sync-token/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/comments/route.js +1 -1
- package/.next/standalone/.next/server/app/api/comments/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/comments/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/connection-heartbeat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/connection-heartbeat/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/control/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/control/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/directory-request/report/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/directory-request/report/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/execution-state/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/execution-state/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/executions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/executions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/pending-turns/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/pending-turns/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/pending-turns/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/report-interrupt/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/report-interrupt/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/report-interrupt/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/resume/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/resume/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/resume/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/transcript/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/transcript/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/transcript/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/turn-advance/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon/turn-advance/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon/turn-advance/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-directory-requests/[uuid]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-directory-requests/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-directory-requests/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-directory-requests/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/instruction/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/instruction/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/instruction/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/repoint/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/repoint/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/repoint/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/ad-hoc/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/ad-hoc/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/ad-hoc/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/daemon-sessions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/documents/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/entities/[type]/[uuid]/root-idea/route.js +1 -1
- package/.next/standalone/.next/server/app/api/entities/[type]/[uuid]/root-idea/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/entities/[type]/[uuid]/root-idea/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/events/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/events/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/events/notifications/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/preview/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/preview/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/parent/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/parent/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/parent/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/wake-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/[uuid]/wake-preview/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/conversational/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ideas/conversational/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ideas/conversational/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/mcp/route.js +2 -2
- package/.next/standalone/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/me/assignments/route.js +1 -1
- package/.next/standalone/.next/server/app/api/me/assignments/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/me/assignments/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/mentionables/route.js +1 -1
- package/.next/standalone/.next/server/app/api/mentionables/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/mentionables/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/preferences/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/preferences/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/preferences/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/read-all/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/read-all/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/read-all/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/unread-count/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/unread-count/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/unread-count/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-groups/[uuid]/dashboard/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-groups/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-groups/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-visits/pin/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-visits/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/project-visits/visit/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/activity/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/agent-cwds/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/agent-cwds/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/group/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/resource-graph/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/resource-graph/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/resource-graph/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/agent-cwd-options/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/agent-cwd-options/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/proposals/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/references/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/references/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/session/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[uuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[uuid]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +2 -2
- package/.next/standalone/.next/server/app/index.rsc +2 -2
- package/.next/standalone/.next/server/app/login/admin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/admin.html +2 -2
- package/.next/standalone/.next/server/app/login/admin.rsc +2 -2
- package/.next/standalone/.next/server/app/login/callback/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/callback.html +2 -2
- package/.next/standalone/.next/server/app/login/callback.rsc +2 -2
- package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/pick-workspace/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login/pick-workspace.html +2 -2
- package/.next/standalone/.next/server/app/login/pick-workspace.rsc +2 -2
- package/.next/standalone/.next/server/app/login.html +2 -2
- package/.next/standalone/.next/server/app/login.rsc +2 -2
- package/.next/standalone/.next/server/app/onboarding/page.js +1 -1
- package/.next/standalone/.next/server/app/onboarding/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/onboarding.html +2 -2
- package/.next/standalone/.next/server/app/onboarding.rsc +3 -3
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects.html +2 -2
- package/.next/standalone/.next/server/app/projects.rsc +3 -3
- package/.next/standalone/.next/server/app/settings.html +2 -2
- package/.next/standalone/.next/server/app/settings.rsc +4 -4
- package/.next/standalone/.next/server/app-paths-manifest.json +41 -41
- package/.next/standalone/.next/server/chunks/2572.js +1 -1
- package/.next/standalone/.next/server/chunks/2618.js +1 -1
- package/.next/standalone/.next/server/chunks/2961.js +1 -1
- package/.next/standalone/.next/server/chunks/3594.js +1 -1
- package/.next/standalone/.next/server/chunks/{2547.js → 4011.js} +3 -3
- package/.next/standalone/.next/server/chunks/4827.js +1 -1
- package/.next/standalone/.next/server/chunks/5205.js +1 -0
- package/.next/standalone/.next/server/chunks/5245.js +1 -1
- package/.next/standalone/.next/server/chunks/6113.js +1 -1
- package/.next/standalone/.next/server/chunks/6506.js +3 -3
- package/.next/standalone/.next/server/chunks/6515.js +2 -2
- package/.next/standalone/.next/server/chunks/6995.js +1 -1
- package/.next/standalone/.next/server/chunks/7364.js +1 -1
- package/.next/standalone/.next/server/chunks/7368.js +1 -1
- package/.next/standalone/.next/server/chunks/7530.js +1 -1
- package/.next/standalone/.next/server/chunks/8454.js +1 -1
- package/.next/standalone/.next/server/chunks/920.js +1 -1
- package/.next/standalone/.next/server/chunks/9213.js +1 -1
- package/.next/standalone/.next/server/chunks/9378.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/.next/standalone/.next/server/middleware-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/{11595.1bcd2b65089007ae.js → 11595.b6f8ad33572b32fe.js} +1 -1
- package/.next/standalone/.next/static/chunks/19887-9f459ee9e8d0855f.js +1 -0
- package/.next/standalone/.next/static/chunks/{29108-e171d8b8c680cbb5.js → 29108-b9b232255811ffa8.js} +1 -1
- package/.next/standalone/.next/static/chunks/{31308-bb4a97b3f39c22dd.js → 31308-fda6febfb2882a28.js} +1 -1
- package/.next/standalone/.next/static/chunks/{33456.c98d3e21528b61d8.js → 33456.e8550ad410031e29.js} +1 -1
- package/.next/standalone/.next/static/chunks/35432-ee83da56c49ef6f8.js +1 -0
- package/.next/standalone/.next/static/chunks/{4860.9122a86d33dc6b62.js → 4860.b405fc235b36271b.js} +1 -1
- package/.next/standalone/.next/static/chunks/66980-652990fcf3212c94.js +10 -0
- package/.next/standalone/.next/static/chunks/{94643-90a9ec7b9a9abaf0.js → 94643-6d31df78aa3074b5.js} +1 -1
- package/.next/standalone/.next/static/chunks/96170-75da13decdb6f73f.js +1 -0
- package/.next/standalone/.next/static/chunks/{99373.585230dcb315a068.js → 99373.9dce8f30802824fc.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/{layout-3d3b3b9e16c03cda.js → layout-cea0bb3e3a295bea.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/{page-69d746c98079024d.js → page-007278623926490f.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/{page-2af705f6c3789131.js → page-b6ecaf52f6b81ec7.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/graph/{page-8119df915138a9c3.js → page-f5a351c544ed23d7.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-3cd46bec9383b191.js +1 -0
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/{page-0789c6589363154c.js → page-7f87113e5d8bcf6e.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/{page-a612f6fc5be0239d.js → page-30ab1573c4bd466f.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/{page-5fb9fd3db155d5ca.js → page-b21d4184d9ca0b25.js} +1 -1
- package/.next/standalone/.next/static/chunks/app/onboarding/page-3b6c54f887108635.js +1 -0
- package/.next/standalone/.next/static/chunks/{webpack-dec1f54ebb9bf963.js → webpack-b54e0c78eded76be.js} +1 -1
- package/.next/standalone/.next/static/css/893d99a21153c9b0.css +1 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/public/chorus-plugin/.claude-plugin/plugin.json +1 -1
- package/.next/standalone/public/chorus-plugin/agents/code-reviewer.md +1 -1
- package/.next/standalone/public/chorus-plugin/agents/proposal-reviewer.md +1 -0
- package/.next/standalone/public/chorus-plugin/agents/task-reviewer.md +2 -0
- package/.next/standalone/public/chorus-plugin/skills/brainstorm/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/chorus/SKILL.md +3 -3
- package/.next/standalone/public/chorus-plugin/skills/develop/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/docs/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/idea/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/openspec-aware/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/proposal/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/quick-dev/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/review/SKILL.md +1 -1
- package/.next/standalone/public/chorus-plugin/skills/yolo/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/agents/chorus-code-reviewer.json +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/agents/chorus-proposal-reviewer.json +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/agents/chorus-task-reviewer.json +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-brainstorm/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-develop/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-docs/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-idea/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-openspec-aware/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-proposal/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-quick-dev/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-review/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-yolo/SKILL.md +1 -1
- package/.next/standalone/public/kiro-plugin/.kiro/steering/chorus.md +2 -2
- package/.next/standalone/public/kiro-plugin/bin/chorus-api.sh +1 -1
- package/.next/standalone/public/skill/chorus/SKILL.md +2 -2
- package/.next/standalone/public/skill/code-reviewer-chorus/SKILL.md +2 -2
- package/.next/standalone/public/skill/proposal-reviewer-chorus/SKILL.md +2 -1
- package/.next/standalone/public/skill/task-reviewer-chorus/SKILL.md +2 -1
- package/README.ja.md +26 -346
- package/README.ko.md +28 -347
- package/README.md +33 -353
- package/README.zh.md +31 -328
- package/cli/control-handler.mjs +13 -3
- package/cli/daemon-rest-client.mjs +11 -3
- package/cli/daemon.mjs +52 -10
- package/cli/event-router.mjs +17 -5
- package/cli/prompts.mjs +116 -1
- package/cli/turn-reporter.mjs +5 -1
- package/cli/wake-queue.mjs +57 -35
- package/cli/waker.mjs +125 -36
- package/package.json +1 -1
- package/prisma/migrations/20260814101500_add_typed_assignment_provenance/migration.sql +2 -0
- package/prisma/schema.prisma +4 -2
- package/.next/standalone/.next/server/chunks/1114.js +0 -1
- package/.next/standalone/.next/static/chunks/19887-4d76db57a7ce4437.js +0 -1
- package/.next/standalone/.next/static/chunks/4340-a33120fb742af57e.js +0 -1
- package/.next/standalone/.next/static/chunks/94426-39b6da238425f9b7.js +0 -10
- package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-f619bd65e1eed8c9.js +0 -1
- package/.next/standalone/.next/static/chunks/app/onboarding/page-e85315278aafc300.js +0 -1
- package/.next/standalone/.next/static/css/67fba0943f494f0e.css +0 -1
- /package/.next/standalone/.next/static/{H92ZuhpmDWcL3WOfYRc-- → YTZwWTfE0BsJnjCDYCEHi}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{H92ZuhpmDWcL3WOfYRc-- → YTZwWTfE0BsJnjCDYCEHi}/_ssgManifest.js +0 -0
package/cli/event-router.mjs
CHANGED
|
@@ -37,8 +37,8 @@ export class EventRouter {
|
|
|
37
37
|
/**
|
|
38
38
|
* @param {{
|
|
39
39
|
* mcpClient: { callTool: (name: string, args?: Record<string, unknown>) => Promise<any> },
|
|
40
|
-
* waker: { keyFor: (n: any) => Promise<{ key: string, rootIdeaUuid: string|null, directIdeaUuid: string|null }>,
|
|
41
|
-
* queue: { enqueue: (key: string,
|
|
40
|
+
* waker: { keyFor: (n: any) => Promise<{ key: string, rootIdeaUuid: string|null, directIdeaUuid: string|null }>, wakeBatch: (ns: any[], key: string, attribution?: any) => Promise<void>, markQueued?: (n: any, key: string, attribution?: any) => void },
|
|
41
|
+
* queue: { enqueue: (key: string, item: { notification: any, attribution: any }) => void },
|
|
42
42
|
* wakeActions: Set<string>,
|
|
43
43
|
* getConnectionUuid?: () => (string|null), This daemon's registered connection uuid
|
|
44
44
|
* (null until the SSE handshake reports it).
|
|
@@ -228,7 +228,8 @@ export class EventRouter {
|
|
|
228
228
|
* reason ("user" | "crash") threaded from the control event; it is stamped on the
|
|
229
229
|
* synthetic notification as `resumedFrom` so the prompt builder can state a crash
|
|
230
230
|
* explicitly. Absent/unknown → not stamped → the user-resume prompt.
|
|
231
|
-
* @param {{ entityType?: string, entityUuid?: string, resumeReason?: string
|
|
231
|
+
* @param {{ entityType?: string, entityUuid?: string, resumeReason?: string,
|
|
232
|
+
* orchestrator?: { type?: string, uuid?: string, name?: string } | null }} target
|
|
232
233
|
*/
|
|
233
234
|
dispatchResume(target) {
|
|
234
235
|
const entityType = target?.entityType;
|
|
@@ -247,6 +248,11 @@ export class EventRouter {
|
|
|
247
248
|
entityUuid,
|
|
248
249
|
...(resumedFrom ? { resumedFrom } : {}),
|
|
249
250
|
...(typeof target?.runtimeCwd === "string" ? { runtimeCwd: target.runtimeCwd } : {}),
|
|
251
|
+
...(target?.orchestrator?.type === "agent" &&
|
|
252
|
+
typeof target.orchestrator.uuid === "string" &&
|
|
253
|
+
typeof target.orchestrator.name === "string"
|
|
254
|
+
? { orchestrator: target.orchestrator }
|
|
255
|
+
: {}),
|
|
250
256
|
};
|
|
251
257
|
this.#resolveAndEnqueue(n, `resume:${entityType}:${entityUuid}`).catch((err) => {
|
|
252
258
|
this.logger.error(`[Chorus] failed to dispatch resume for ${entityType}:${entityUuid}: ${err}`);
|
|
@@ -383,7 +389,10 @@ export class EventRouter {
|
|
|
383
389
|
} catch (err) {
|
|
384
390
|
this.logger.warn(`[Chorus] markQueued failed for pending turn ${turnUuid}: ${err}`);
|
|
385
391
|
}
|
|
386
|
-
|
|
392
|
+
// Enqueue an opaque DATA payload (add-daemon-wake-coalescing §C4), NOT a thunk: the
|
|
393
|
+
// queue coalesces all same-key pending items and hands the whole batch to its runBatch
|
|
394
|
+
// (wired to waker.wakeBatch in daemon.mjs). markQueued was already called per-item above.
|
|
395
|
+
this.queue.enqueue(key, { notification: n, attribution });
|
|
387
396
|
}
|
|
388
397
|
|
|
389
398
|
/**
|
|
@@ -531,6 +540,9 @@ export class EventRouter {
|
|
|
531
540
|
} catch (err) {
|
|
532
541
|
this.logger.warn(`[Chorus] markQueued failed for ${label}: ${err}`);
|
|
533
542
|
}
|
|
534
|
-
|
|
543
|
+
// Enqueue an opaque DATA payload (add-daemon-wake-coalescing §C4), NOT a thunk: the
|
|
544
|
+
// queue coalesces all same-key pending items and hands the whole batch to its runBatch
|
|
545
|
+
// (wired to waker.wakeBatch in daemon.mjs). markQueued was already called per-item above.
|
|
546
|
+
this.queue.enqueue(key, { notification: n, attribution });
|
|
535
547
|
}
|
|
536
548
|
}
|
package/cli/prompts.mjs
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* @property {string} actorType
|
|
26
26
|
* @property {string} actorUuid
|
|
27
27
|
* @property {string} actorName
|
|
28
|
+
* @property {{type: string, uuid: string, name: string} | null} [orchestrator]
|
|
28
29
|
* @property {string} [instructionText] Free-text body of a `human_instruction` wake
|
|
29
30
|
* (子1 — daemon-session-conversation). The server denormalizes the canonical turn
|
|
30
31
|
* promptText onto the wake notification so the daemon reads it in the
|
|
@@ -40,6 +41,29 @@ function mentionGuidance(n, entityType) {
|
|
|
40
41
|
);
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Orchestrator-handoff guidance, appended to every wake body by buildPrompt. This is
|
|
46
|
+
* the SINGLE home of the "hand back to the agent that dispatched you" instruction —
|
|
47
|
+
* it is runtime PE (it depends on who the wake resolved as orchestrator), so it lives
|
|
48
|
+
* here in the wake prompt, NOT in the static skill docs. Its OpenClaw twin is
|
|
49
|
+
* `buildOrchestratorGuidance` in packages/openclaw-plugin/src/event-router.ts —
|
|
50
|
+
* KEEP THE TWO WORDINGS IN SYNC.
|
|
51
|
+
* Returns null unless the resource has an AGENT orchestrator; a human-assigned
|
|
52
|
+
* resource hands back to the human via the per-event actor @mention (mentionGuidance).
|
|
53
|
+
* @param {NotificationDetail} n
|
|
54
|
+
*/
|
|
55
|
+
function orchestratorGuidance(n) {
|
|
56
|
+
if (n.orchestrator?.type !== "agent") return null;
|
|
57
|
+
return (
|
|
58
|
+
`Your orchestrator for this resource is @${n.orchestrator.name}.\n` +
|
|
59
|
+
`At a human-only gate you cannot cross, or when this child resource is complete, hand control ` +
|
|
60
|
+
`back by commenting on the resource and mentioning ` +
|
|
61
|
+
`@[${n.orchestrator.name}](agent:${n.orchestrator.uuid}) with the decision needed or completion ` +
|
|
62
|
+
`evidence, then leave any human-gated resource pending and end the turn. Do not @mention the ` +
|
|
63
|
+
`orchestrator for ordinary internal progress.`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
43
67
|
/**
|
|
44
68
|
* Shared headless preamble prepended to EVERY wake prompt (add-daemon-headless-
|
|
45
69
|
* interaction-guard). A daemon-woken session is a headless `claude -p` run with no
|
|
@@ -96,7 +120,98 @@ export const HEADLESS_PREAMBLE = [
|
|
|
96
120
|
export function buildPrompt(n) {
|
|
97
121
|
const body = buildPromptBody(n);
|
|
98
122
|
if (body == null) return null;
|
|
99
|
-
|
|
123
|
+
const handoff = orchestratorGuidance(n);
|
|
124
|
+
return `${HEADLESS_PREAMBLE}\n\n${body}${handoff ? `\n\n${handoff}` : ""}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Build ONE combined wake prompt for a coalesced batch of same-session notifications
|
|
129
|
+
* (add-daemon-wake-coalescing, tech design §C2). The daemon serializes wakes per session
|
|
130
|
+
* key; while a turn runs, later same-key wakes pile up and — when the slot frees — are
|
|
131
|
+
* drained together into a single `claude --resume` turn. This composes their one prompt.
|
|
132
|
+
*
|
|
133
|
+
* Rules:
|
|
134
|
+
* - **Size 1** → delegate to `buildPrompt(n)` verbatim, so the single-event path is
|
|
135
|
+
* BYTE-IDENTICAL to today (and all current prompt tests keep passing). This is keyed on
|
|
136
|
+
* the count of RENDERABLE events (non-null body): a batch that reduces to a single
|
|
137
|
+
* renderable event — e.g. a real wake plus an empty `human_instruction` — also takes
|
|
138
|
+
* the single-event path.
|
|
139
|
+
* - **Size > 1 renderable** → `HEADLESS_PREAMBLE` ONCE (it is a per-turn guard, paid once
|
|
140
|
+
* per turn, not per event), then a short backlog preamble, then one labeled block per
|
|
141
|
+
* event in ARRIVAL order, each block being the reused per-action `buildPromptBody(n)` so
|
|
142
|
+
* every event keeps its own tool hints and @mention guidance.
|
|
143
|
+
* - **Same-entity collapse (Q2)**: events sharing `(action, entityUuid)` collapse into one
|
|
144
|
+
* block at the group's FIRST-SEEN position, noting the occurrence count and showing the
|
|
145
|
+
* NEWEST message. This is safe only because such actions' full content is re-derivable
|
|
146
|
+
* server-side (comments via chorus_get_comments, task lifecycle via chorus_get_*).
|
|
147
|
+
* - **`human_instruction` is NEVER collapsed (Q3, Round-1 BLOCKER-2)**: every queued chat
|
|
148
|
+
* message carries `action=human_instruction` and `entityUuid=directIdeaUuid`, and its
|
|
149
|
+
* text lives ONLY on the turn/notification (not re-fetchable), so collapsing to
|
|
150
|
+
* newest-only would silently drop earlier chat and defeat the feature. Each renders its
|
|
151
|
+
* full body as its own block, in arrival order.
|
|
152
|
+
* - Events whose body is null (empty `human_instruction`, non-wake action) are omitted; a
|
|
153
|
+
* batch with no renderable event returns null (the router then spawns nothing).
|
|
154
|
+
*
|
|
155
|
+
* @param {NotificationDetail[]} notifications Arrival-ordered batch (FIFO drain order).
|
|
156
|
+
* @returns {string | null}
|
|
157
|
+
*/
|
|
158
|
+
export function buildBatchPrompt(notifications) {
|
|
159
|
+
if (!Array.isArray(notifications) || notifications.length === 0) return null;
|
|
160
|
+
|
|
161
|
+
// Keep only events with a renderable per-action body, in arrival order.
|
|
162
|
+
const items = [];
|
|
163
|
+
for (const n of notifications) {
|
|
164
|
+
const body = buildPromptBody(n);
|
|
165
|
+
if (body != null) items.push({ n, body });
|
|
166
|
+
}
|
|
167
|
+
if (items.length === 0) return null;
|
|
168
|
+
// One renderable event → byte-identical single-event prompt (delegates to buildPrompt so
|
|
169
|
+
// the exact "PREAMBLE\n\nbody" shape is preserved). Covers a true batch of size 1 and a
|
|
170
|
+
// batch that reduced to one renderable event after null-body events were dropped.
|
|
171
|
+
if (items.length === 1) return buildPrompt(items[0].n);
|
|
172
|
+
|
|
173
|
+
// Group into ordered blocks. Collapsible actions merge by (action, entityUuid) at their
|
|
174
|
+
// first-seen slot; human_instruction is exempt and always gets its own block.
|
|
175
|
+
const blocks = [];
|
|
176
|
+
const collapseIndex = new Map(); // "action::entityUuid" -> index into `blocks`
|
|
177
|
+
for (const { n, body } of items) {
|
|
178
|
+
if (n.action === "human_instruction") {
|
|
179
|
+
blocks.push({ notif: n, body, count: 1 });
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const groupKey = `${n.action}::${n.entityUuid ?? ""}`;
|
|
183
|
+
const existing = collapseIndex.get(groupKey);
|
|
184
|
+
if (existing != null) {
|
|
185
|
+
const g = blocks[existing];
|
|
186
|
+
g.count += 1;
|
|
187
|
+
g.notif = n; // newest wins (arrival order → last member is newest)
|
|
188
|
+
g.body = body; // newest body
|
|
189
|
+
} else {
|
|
190
|
+
collapseIndex.set(groupKey, blocks.length);
|
|
191
|
+
blocks.push({ notif: n, body, count: 1 });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Backlog preamble. N = number of renderable events that arrived (not the collapsed block
|
|
196
|
+
// count) — collapse is a rendering optimization; each collapsed block states its own count.
|
|
197
|
+
const backlogPreamble =
|
|
198
|
+
`You have ${items.length} queued Chorus events on this session that arrived while you ` +
|
|
199
|
+
`were busy — handle them together, in order; each is labeled with its type below.`;
|
|
200
|
+
|
|
201
|
+
const rendered = blocks.map((g, idx) => {
|
|
202
|
+
const n = g.notif;
|
|
203
|
+
const entityType = n.entityType || "session";
|
|
204
|
+
const entityUuid = n.entityUuid || "";
|
|
205
|
+
const header = `### Event ${idx + 1} — ${n.action} on ${entityType} ${entityUuid}`.replace(/\s+$/, "");
|
|
206
|
+
const collapseNote =
|
|
207
|
+
g.count > 1
|
|
208
|
+
? `\n(${g.count} ${n.action} events on this ${entityType} arrived — showing the newest ` +
|
|
209
|
+
`below; earlier ones are re-derivable via the entity's own chorus_get_* tools.)`
|
|
210
|
+
: "";
|
|
211
|
+
return `${header}${collapseNote}\n\n${g.body}`;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return [HEADLESS_PREAMBLE, backlogPreamble, ...rendered].join("\n\n");
|
|
100
215
|
}
|
|
101
216
|
|
|
102
217
|
/**
|
package/cli/turn-reporter.mjs
CHANGED
|
@@ -52,7 +52,7 @@ export const TURN_INTERRUPT_REASONS = new Set([
|
|
|
52
52
|
* logger?: { info(m:string):void, warn(m:string):void, error(m:string):void },
|
|
53
53
|
* fetchImpl?: typeof fetch, Injectable for tests.
|
|
54
54
|
* }} opts
|
|
55
|
-
* @returns {(params: { sessionId: string, status: "running"|"ended"|"interrupted", entityType?: string|null, entityUuid?: string|null, interruptedReason?: "user"|"crash"|"shutdown"|"invalid_path"|null, transcriptRelayError?: string|null, usage?: import("./upload-hooks.mjs").TokenUsage|null }) => Promise<void>}
|
|
55
|
+
* @returns {(params: { sessionId: string, status: "running"|"ended"|"interrupted", entityType?: string|null, entityUuid?: string|null, interruptedReason?: "user"|"crash"|"shutdown"|"invalid_path"|null, transcriptRelayError?: string|null, usage?: import("./upload-hooks.mjs").TokenUsage|null, coalescedCount?: number }) => Promise<void>}
|
|
56
56
|
*/
|
|
57
57
|
export function createTurnReporter(opts) {
|
|
58
58
|
const logger = opts.logger ?? NOOP_LOGGER;
|
|
@@ -76,6 +76,7 @@ export function createTurnReporter(opts) {
|
|
|
76
76
|
transcriptRelayError,
|
|
77
77
|
usage,
|
|
78
78
|
backendSessionId,
|
|
79
|
+
coalescedCount,
|
|
79
80
|
}) {
|
|
80
81
|
if (typeof sessionId !== "string" || !sessionId || !TURN_STATUSES.has(status)) {
|
|
81
82
|
logger.warn(
|
|
@@ -111,6 +112,9 @@ export function createTurnReporter(opts) {
|
|
|
111
112
|
// only on a terminal edge. No validation here — usage is an opaque nested object.
|
|
112
113
|
usage,
|
|
113
114
|
backendSessionId,
|
|
115
|
+
// Coalesced-wake count (add-daemon-wake-coalescing): threaded straight through; the
|
|
116
|
+
// client sends it only on the → running edge and only when > 1 (a single wake omits it).
|
|
117
|
+
coalescedCount,
|
|
114
118
|
});
|
|
115
119
|
};
|
|
116
120
|
}
|
package/cli/wake-queue.mjs
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
// cli/wake-queue.mjs
|
|
2
|
-
// Per-key FIFO scheduler with a global concurrency cap
|
|
3
|
-
// idea_root session anchor safe
|
|
4
|
-
// serialization", design.md "Concurrency
|
|
5
|
-
//
|
|
6
|
-
//
|
|
2
|
+
// Per-key FIFO scheduler with a global concurrency cap, coalescing same-key
|
|
3
|
+
// wakes into batches. This is what makes the idea_root session anchor safe
|
|
4
|
+
// (cli-daemon spec "Per-root-idea wake serialization", design.md "Concurrency
|
|
5
|
+
// model") AND implements daemon-wake-coalescing (design.md §C1):
|
|
6
|
+
// • within one key (root idea) → strictly serial, FIFO. The next batch waits
|
|
7
|
+
// for the current runBatch to settle, so we never run two
|
|
7
8
|
// `claude --resume <sameSessionId>` against one session.
|
|
9
|
+
// • coalescing: when a key's slot frees, the ENTIRE pending array for that key
|
|
10
|
+
// is drained (splice) and delivered to runBatch ONCE as a single batch. So
|
|
11
|
+
// N events that pile up while the previous turn runs become ONE turn.
|
|
12
|
+
// Natural batching only — NO debounce/collect timer, NO batch-size cap.
|
|
8
13
|
// • across keys → concurrent, bounded by maxConcurrency.
|
|
9
14
|
// • enqueue() returns immediately — never blocks the SSE loop.
|
|
10
|
-
// • a
|
|
11
|
-
// poisoned wake must not wedge the key's queue forever).
|
|
15
|
+
// • a batch whose runBatch throws is logged and the next batch for that key
|
|
16
|
+
// proceeds (a poisoned wake must not wedge the key's queue forever).
|
|
17
|
+
// The queue carries opaque DATA items (the router passes `{ notification,
|
|
18
|
+
// attribution }`); it never introspects them — the runBatch callback (supplied
|
|
19
|
+
// at construction, wired to waker.wakeBatch in daemon.mjs) does.
|
|
12
20
|
// Plain ESM, zero deps, in-memory.
|
|
13
21
|
|
|
14
22
|
const NOOP_LOGGER = { info() {}, warn() {}, error() {} };
|
|
@@ -18,41 +26,49 @@ export class WakeQueue {
|
|
|
18
26
|
* @param {{
|
|
19
27
|
* maxConcurrency?: number,
|
|
20
28
|
* logger?: { info(m:string):void, warn(m:string):void, error(m:string):void },
|
|
29
|
+
* runBatch?: (key: string, items: any[]) => Promise<void>,
|
|
21
30
|
* }} [opts]
|
|
22
31
|
*/
|
|
23
32
|
constructor(opts = {}) {
|
|
24
33
|
this.maxConcurrency = opts.maxConcurrency ?? 4;
|
|
25
34
|
this.logger = opts.logger ?? NOOP_LOGGER;
|
|
26
|
-
|
|
35
|
+
// The batch runner: called ONCE per drained batch with every pending item
|
|
36
|
+
// for the key. Defaults to a no-op so an unwired queue never throws (the
|
|
37
|
+
// daemon always supplies the real waker.wakeBatch runner).
|
|
38
|
+
this.runBatch = opts.runBatch ?? (async () => {});
|
|
39
|
+
/** @type {Map<string, any[]>} pending data items per key. */
|
|
27
40
|
this.pending = new Map();
|
|
28
|
-
/** @type {Set<string>} keys with a
|
|
41
|
+
/** @type {Set<string>} keys with a batch currently running. */
|
|
29
42
|
this.running = new Set();
|
|
30
43
|
/** @type {string[]} keys waiting for a global concurrency slot. */
|
|
31
44
|
this.readyKeys = [];
|
|
32
45
|
this.activeCount = 0;
|
|
33
|
-
// Graceful-shutdown latch: once set, #pump starts NOTHING new — in-flight
|
|
34
|
-
// finish (drain observes them) but queued work stays queued and dies
|
|
35
|
-
// process. Never cleared; a stopping queue is on its way out.
|
|
46
|
+
// Graceful-shutdown latch: once set, #pump starts NOTHING new — in-flight
|
|
47
|
+
// batches finish (drain observes them) but queued work stays queued and dies
|
|
48
|
+
// with the process. Never cleared; a stopping queue is on its way out.
|
|
36
49
|
this.stopped = false;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
/** Stop starting new
|
|
52
|
+
/** Stop starting new batches (graceful shutdown). In-flight batches are unaffected. */
|
|
40
53
|
stop() {
|
|
41
54
|
this.stopped = true;
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
/**
|
|
45
|
-
* Enqueue
|
|
46
|
-
*
|
|
47
|
-
*
|
|
58
|
+
* Enqueue an opaque data item under a key. Returns immediately. Items on the
|
|
59
|
+
* same key coalesce: while a key's batch runs, later items pile up and are
|
|
60
|
+
* drained together as ONE batch when the slot frees. Different keys run
|
|
61
|
+
* concurrently up to maxConcurrency.
|
|
48
62
|
* @param {string} key
|
|
49
|
-
* @param {(
|
|
63
|
+
* @param {any} item opaque data (e.g. `{ notification, attribution }`)
|
|
50
64
|
*/
|
|
51
|
-
enqueue(key,
|
|
65
|
+
enqueue(key, item) {
|
|
52
66
|
if (!this.pending.has(key)) this.pending.set(key, []);
|
|
53
|
-
this.pending.get(key).push(
|
|
67
|
+
this.pending.get(key).push(item);
|
|
54
68
|
// A key becomes "ready" to claim a global slot only when it's not already
|
|
55
|
-
// running (serial-per-key) and not already queued for a slot.
|
|
69
|
+
// running (serial-per-key) and not already queued for a slot. While it IS
|
|
70
|
+
// running, later items simply accumulate in `pending` and are picked up by
|
|
71
|
+
// the next batch — this is where coalescing happens.
|
|
56
72
|
if (!this.running.has(key) && !this.readyKeys.includes(key)) {
|
|
57
73
|
this.readyKeys.push(key);
|
|
58
74
|
}
|
|
@@ -65,7 +81,7 @@ export class WakeQueue {
|
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
/**
|
|
68
|
-
* Snapshot of the keys with a
|
|
84
|
+
* Snapshot of the keys with a batch currently running (observability read).
|
|
69
85
|
* Returns a fresh array so a caller can't mutate the internal Set.
|
|
70
86
|
* @returns {string[]}
|
|
71
87
|
*/
|
|
@@ -74,7 +90,7 @@ export class WakeQueue {
|
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
/**
|
|
77
|
-
* Snapshot of the keys that have at least one
|
|
93
|
+
* Snapshot of the keys that have at least one item still waiting to run —
|
|
78
94
|
* i.e. enqueued but not yet started (observability read). A key that is
|
|
79
95
|
* currently running with no further queued work is NOT pending. Returns a
|
|
80
96
|
* fresh array so a caller can't mutate internal state.
|
|
@@ -85,13 +101,13 @@ export class WakeQueue {
|
|
|
85
101
|
}
|
|
86
102
|
|
|
87
103
|
/**
|
|
88
|
-
* Wait (bounded) for every in-flight
|
|
104
|
+
* Wait (bounded) for every in-flight batch to finish — the graceful-shutdown drain
|
|
89
105
|
* (fix-daemon-exit-orphan-running-turn). Resolves `true` when the queue went idle
|
|
90
|
-
* (no active
|
|
106
|
+
* (no active batch) within `timeoutMs`, `false` on timeout — the caller exits
|
|
91
107
|
* anyway and leaves the rest to the server-side reconcile backstop. Pending
|
|
92
|
-
* (not-yet-started)
|
|
108
|
+
* (not-yet-started) items are NOT waited for: a shutting-down daemon stops
|
|
93
109
|
* starting new work, so only the in-flight subprocesses (and their exit reports)
|
|
94
|
-
* matter. Polling (50ms) keeps this zero-dep and independent of
|
|
110
|
+
* matter. Polling (50ms) keeps this zero-dep and independent of batch internals.
|
|
95
111
|
* @param {number} timeoutMs
|
|
96
112
|
* @returns {Promise<boolean>}
|
|
97
113
|
*/
|
|
@@ -112,33 +128,39 @@ export class WakeQueue {
|
|
|
112
128
|
if (this.running.has(key)) continue; // already running under another slot
|
|
113
129
|
const queue = this.pending.get(key);
|
|
114
130
|
if (!queue || queue.length === 0) continue;
|
|
115
|
-
this.#
|
|
131
|
+
this.#startBatch(key);
|
|
116
132
|
}
|
|
117
133
|
}
|
|
118
134
|
|
|
119
|
-
/**
|
|
120
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Drain the ENTIRE pending array for a key into one batch and run it, then
|
|
137
|
+
* chain to the following batch. No batch-size cap (design.md §C1, Q7).
|
|
138
|
+
*/
|
|
139
|
+
#startBatch(key) {
|
|
121
140
|
const queue = this.pending.get(key);
|
|
122
141
|
if (!queue || queue.length === 0) {
|
|
123
142
|
this.running.delete(key);
|
|
124
143
|
return;
|
|
125
144
|
}
|
|
126
|
-
|
|
145
|
+
// Coalesce: take everything pending for this key right now as one batch.
|
|
146
|
+
// Items that arrive after this splice accumulate for the NEXT batch.
|
|
147
|
+
const items = queue.splice(0);
|
|
127
148
|
this.running.add(key);
|
|
128
149
|
this.activeCount++;
|
|
129
150
|
|
|
130
151
|
Promise.resolve()
|
|
131
|
-
.then(
|
|
152
|
+
.then(() => this.runBatch(key, items))
|
|
132
153
|
.catch((err) => {
|
|
133
|
-
// Poisoned
|
|
134
|
-
this.logger.warn(`[Chorus] wake
|
|
154
|
+
// Poisoned batch: log, do NOT let it wedge the key's queue.
|
|
155
|
+
this.logger.warn(`[Chorus] wake batch for ${key} failed: ${err}`);
|
|
135
156
|
})
|
|
136
157
|
.finally(() => {
|
|
137
158
|
this.activeCount--;
|
|
138
159
|
const remaining = this.pending.get(key);
|
|
139
160
|
if (remaining && remaining.length > 0) {
|
|
140
|
-
// Same key
|
|
141
|
-
// #pump will pick it up
|
|
161
|
+
// Same key accumulated more work while this batch ran → it must run
|
|
162
|
+
// serially as the next batch. Re-mark ready; #pump will pick it up
|
|
163
|
+
// (respecting the global cap).
|
|
142
164
|
if (!this.readyKeys.includes(key)) this.readyKeys.push(key);
|
|
143
165
|
this.running.delete(key); // free the key so #pump can re-claim it
|
|
144
166
|
} else {
|
package/cli/waker.mjs
CHANGED
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
// the server-resolved root, NEVER re-derived from the (direct-idea) serialization
|
|
13
13
|
// key.
|
|
14
14
|
//
|
|
15
|
-
// Module contract (design.md): Waker.
|
|
16
|
-
// Promise<void
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
// Module contract (design.md): Waker.wakeBatch(notifications, key, attribution) →
|
|
16
|
+
// Promise<void> runs ONE coalesced turn for a batch of same-session notifications
|
|
17
|
+
// (add-daemon-wake-coalescing); Waker.wake(n, key, attribution) is a thin wrapper =
|
|
18
|
+
// wakeBatch([n], ...) so a batch of one is byte-identical to the pre-coalescing single
|
|
19
|
+
// wake. A failure is logged and swallowed — it must never crash the daemon
|
|
20
|
+
// (no-silent-errors: visible log, no throw).
|
|
21
|
+
|
|
22
|
+
import { buildBatchPrompt } from "./prompts.mjs";
|
|
20
23
|
import { writeMcpConfig } from "./mcp-config.mjs";
|
|
21
24
|
import { isNewSession } from "./claude-spawner.mjs";
|
|
22
25
|
import { killProcessTree, DEFAULT_SIGINT_TIMEOUT_MS } from "./process-killer.mjs";
|
|
@@ -320,49 +323,122 @@ export class Waker {
|
|
|
320
323
|
}
|
|
321
324
|
|
|
322
325
|
/**
|
|
323
|
-
* Run one wake. Never throws.
|
|
326
|
+
* Run one wake. Never throws. Thin wrapper over {@link wakeBatch} with a batch of one,
|
|
327
|
+
* so a single wake is byte-identical to the pre-coalescing behavior (prompt, execution
|
|
328
|
+
* row, turn accounting, coalescedCount = 1).
|
|
324
329
|
* @param {import("./prompts.mjs").NotificationDetail} notification
|
|
325
330
|
* @param {string} key The serialization key (from keyFor) — anchored on the direct idea.
|
|
326
331
|
* @param {{ rootIdeaUuid?: string|null, directIdeaUuid?: string|null }} [attribution]
|
|
327
|
-
* Server-resolved ids from keyFor. `rootIdeaUuid` → execution snapshot;
|
|
328
|
-
* `directIdeaUuid` → the deterministic Claude session id (when the entity has
|
|
329
|
-
* an idea ancestor). When there is no direct idea, the session is anchored on
|
|
330
|
-
* the entity's own uuid instead (see below).
|
|
331
332
|
*/
|
|
332
333
|
async wake(notification, key, attribution) {
|
|
334
|
+
return this.wakeBatch([notification], key, attribution);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Run ONE coalesced turn for a batch of same-session notifications
|
|
339
|
+
* (add-daemon-wake-coalescing, Tech Design §C3). The WakeQueue drains all same-key
|
|
340
|
+
* pending wakes and hands them here as one batch; this composes their single combined
|
|
341
|
+
* prompt (buildBatchPrompt), spawns ONE subprocess (one `claude --resume <anchor>`), and
|
|
342
|
+
* accounts for the whole thing as ONE running turn. Never throws.
|
|
343
|
+
*
|
|
344
|
+
* A batch of ONE is byte-identical to the pre-coalescing single wake: `buildBatchPrompt`
|
|
345
|
+
* delegates to `buildPrompt`, the execution row + turn-advance carry the item's OWN
|
|
346
|
+
* entity, and `coalescedCount = 1` (so the wire is unchanged).
|
|
347
|
+
*
|
|
348
|
+
* Execution snapshot (Q6): a coalesced batch (> 1) is anchored on the SESSION, so it emits
|
|
349
|
+
* ONE running row synthesized from `attribution` — `idea:<directIdeaUuid>` (NOT necessarily
|
|
350
|
+
* one of the merged items — a batch of mentions on child tasks A+B under idea D has no
|
|
351
|
+
* `idea:D` item, yet the anchor IS `idea:D`), else the ad-hoc entity. Every drained/merged
|
|
352
|
+
* resource is removed from the executions map so the server reconcile ends its queued row.
|
|
353
|
+
*
|
|
354
|
+
* @param {import("./prompts.mjs").NotificationDetail[]} notifications Arrival-ordered batch.
|
|
355
|
+
* @param {string} key The serialization key (from keyFor) — shared by every item.
|
|
356
|
+
* @param {{ rootIdeaUuid?: string|null, directIdeaUuid?: string|null }} [attribution]
|
|
357
|
+
* Server-resolved ids (shared across the batch — the key IS `idea:<directIdeaUuid>`, so
|
|
358
|
+
* all items carry the same ids). `rootIdeaUuid` → snapshot; `directIdeaUuid` → session anchor.
|
|
359
|
+
*/
|
|
360
|
+
async wakeBatch(notifications, key, attribution) {
|
|
333
361
|
let cfg;
|
|
334
|
-
const
|
|
335
|
-
|
|
362
|
+
const list = Array.isArray(notifications) ? notifications : [];
|
|
363
|
+
// Physical batch size — how many wakes were coalesced into this one turn. Drives the
|
|
364
|
+
// session-anchor synthesis (a coalesced batch shows ONE synthesized idea row) and the
|
|
365
|
+
// arrival log label. Independent of the wire count below.
|
|
366
|
+
const batchSize = list.length;
|
|
367
|
+
// The number of coalesced wakes reported to the server on the running-transition so it can
|
|
368
|
+
// settle the next (coalescedCount − 1) same-session pending turns. Counted as ONLY the
|
|
369
|
+
// TURN-BACKED items: each notification-backed wake created a server-side pending
|
|
370
|
+
// DaemonSessionTurn at the chokepoint, so those are the turns the server can settle.
|
|
371
|
+
//
|
|
372
|
+
// A synthetic `resource_resumed` (子3 control-channel resume) is EXCLUDED: it is NEVER a
|
|
373
|
+
// persisted notification and creates NO server pending turn (src/services/notification-turn.ts
|
|
374
|
+
// deliberately omits it from the trigger map), so counting it would inflate N. An inflated N
|
|
375
|
+
// lets the server's `take: coalescedCount-1` seq-window settlement over-reach and mark a
|
|
376
|
+
// genuinely-separate post-drain pending turn as `merged` — which getPendingTurnsForConnection
|
|
377
|
+
// then never re-dispatches, silently dropping a piled-up chat message (defeats Q3).
|
|
378
|
+
// `resource_resumed` is the ONLY synthetic, non-notification wake action; every other
|
|
379
|
+
// WAKE_ACTIONS entry (incl. human_instruction) is notification-backed and turn-creating. A
|
|
380
|
+
// batch that reduces to 0 or 1 turn-backed items reports ≤ 1, which the client omits from the
|
|
381
|
+
// wire (see #advanceTurn) so no settlement runs.
|
|
382
|
+
const coalescedCount = list.filter((n) => n?.action !== "resource_resumed").length;
|
|
383
|
+
const first = list[0];
|
|
336
384
|
// Both ids come from the resolved `attribution` (supplied by keyFor via the
|
|
337
385
|
// router) and are threaded SEPARATELY — NEVER sliced from `key`. The ROOT idea
|
|
338
386
|
// is reported in the snapshot; the DIRECT idea is the preferred session anchor.
|
|
339
387
|
const rootIdeaUuid = attribution?.rootIdeaUuid ?? null;
|
|
340
388
|
const directIdeaUuid = attribution?.directIdeaUuid ?? null;
|
|
389
|
+
// The resources markQueued tracked for THIS batch — every item's own entity. They must
|
|
390
|
+
// all leave the executions map when the batch settles so the server reconcile ends the
|
|
391
|
+
// queued rows the batch coalesced away (a coalesced batch shows only ONE running row).
|
|
392
|
+
const drainedExecKeys = [];
|
|
393
|
+
for (const n of list) {
|
|
394
|
+
const e = this.#entityOf(n);
|
|
395
|
+
if (e) drainedExecKeys.push(this.#execKey(e.entityType, e.entityUuid));
|
|
396
|
+
}
|
|
397
|
+
// The session-anchor resource for the ONE running row:
|
|
398
|
+
// - a coalesced batch (> 1) is anchored on the SESSION, synthesized from attribution:
|
|
399
|
+
// idea:<directIdeaUuid> when idea-anchored (NOT necessarily one of the merged items),
|
|
400
|
+
// else the ad-hoc entity (all ad-hoc items share one entity → the first item's entity).
|
|
401
|
+
// - a single wake (batch of one) keeps the item's OWN entity — byte-identical to before.
|
|
402
|
+
const entity =
|
|
403
|
+
batchSize > 1 && directIdeaUuid
|
|
404
|
+
? { entityType: "idea", entityUuid: directIdeaUuid }
|
|
405
|
+
: this.#entityOf(first);
|
|
406
|
+
const execKey = entity ? this.#execKey(entity.entityType, entity.entityUuid) : null;
|
|
341
407
|
// Per-wake lifecycle logging: stamp the start so completion can report a
|
|
342
408
|
// duration. `Date.now()` is fine here (runtime metric, not a resume seed).
|
|
343
409
|
const startMs = Date.now();
|
|
344
410
|
const target = entity ? `${entity.entityType}:${entity.entityUuid}` : key;
|
|
345
|
-
const sessionId = directIdeaUuid ??
|
|
411
|
+
const sessionId = directIdeaUuid ?? first?.entityUuid ?? null;
|
|
346
412
|
const requestedRuntimeCwd =
|
|
347
|
-
typeof
|
|
348
|
-
?
|
|
413
|
+
typeof first?.runtimeCwd === "string" && first.runtimeCwd
|
|
414
|
+
? first.runtimeCwd
|
|
349
415
|
: null;
|
|
416
|
+
// Arrival label: the single action for a batch of one, else a batch count (the physical
|
|
417
|
+
// number of merged events — includes any resource_resumed, which is a real wake even
|
|
418
|
+
// though it is not turn-backed for settlement).
|
|
419
|
+
const arrivalAction =
|
|
420
|
+
batchSize > 1 ? `coalesced batch of ${batchSize}` : first?.action;
|
|
350
421
|
try {
|
|
351
|
-
const prompt =
|
|
422
|
+
const prompt = buildBatchPrompt(list);
|
|
352
423
|
if (!prompt) {
|
|
353
|
-
this.logger.info(`[Chorus] no wake prompt for
|
|
424
|
+
this.logger.info(`[Chorus] no wake prompt for ${arrivalAction} on ${key} — skipping`);
|
|
354
425
|
return;
|
|
355
426
|
}
|
|
356
427
|
|
|
357
|
-
// Lifecycle line 1 — arrival: which action
|
|
358
|
-
this.logger.info(`[Chorus] ▶ wake: ${
|
|
428
|
+
// Lifecycle line 1 — arrival: which action(s) target which idea/task/entity.
|
|
429
|
+
this.logger.info(`[Chorus] ▶ wake: ${arrivalAction} → ${target}`);
|
|
359
430
|
|
|
360
|
-
// Transition
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
//
|
|
431
|
+
// Transition to RUNNING and emit a fresh snapshot. First REMOVE every merged/drained
|
|
432
|
+
// resource (so the server reconcile ends their queued rows), then set the ONE
|
|
433
|
+
// synthesized anchor running row with a start timestamp — so the server/UI sees the
|
|
434
|
+
// batch as a single running entry with no leftover queued entries. Report the
|
|
435
|
+
// server-resolved ROOT idea (not the direct-idea key). `child` starts null and is
|
|
436
|
+
// filled in by the spawner's onChild the moment the subprocess spawns (子3) — so the
|
|
437
|
+
// control handler can target it for interrupt.
|
|
365
438
|
if (entity && execKey) {
|
|
439
|
+
for (const dk of drainedExecKeys) {
|
|
440
|
+
if (dk !== execKey) this.executions.delete(dk);
|
|
441
|
+
}
|
|
366
442
|
this.executions.set(execKey, {
|
|
367
443
|
entityType: entity.entityType,
|
|
368
444
|
entityUuid: entity.entityUuid,
|
|
@@ -416,7 +492,7 @@ export class Waker {
|
|
|
416
492
|
this.logger.info(`[Chorus] dispatching session ${sessionId ?? "(none)"}`);
|
|
417
493
|
}
|
|
418
494
|
if (this.verbose) {
|
|
419
|
-
this.logger.info(`[Chorus] cwd=${cwd} action=${
|
|
495
|
+
this.logger.info(`[Chorus] cwd=${cwd} action=${arrivalAction} root=${rootIdeaUuid ?? "(none)"}`);
|
|
420
496
|
}
|
|
421
497
|
|
|
422
498
|
cfg = this.writeMcpConfigFn(this.creds);
|
|
@@ -456,7 +532,10 @@ export class Waker {
|
|
|
456
532
|
turnAdvancedToRunning = true;
|
|
457
533
|
// Fire-and-forget; #advanceTurn swallows + logs its own failures so a
|
|
458
534
|
// turn-report error never crashes the spawn callback (no-silent-errors).
|
|
459
|
-
this
|
|
535
|
+
// The coalescedCount rides this → running edge so the server settles the
|
|
536
|
+
// (count − 1) same-session pending turns coalesced into this one (a single
|
|
537
|
+
// wake reports 1, which the client omits from the wire).
|
|
538
|
+
this.#advanceTurn(sessionId, "running", entity, null, null, null, null, coalescedCount).catch(() => {});
|
|
460
539
|
}
|
|
461
540
|
},
|
|
462
541
|
onMessage: (message) => {
|
|
@@ -577,7 +656,7 @@ export class Waker {
|
|
|
577
656
|
);
|
|
578
657
|
if (this.verbose) {
|
|
579
658
|
this.logger.info(
|
|
580
|
-
`[Chorus] action=${
|
|
659
|
+
`[Chorus] action=${arrivalAction} session=${result?.sessionId} key=${key}`
|
|
581
660
|
);
|
|
582
661
|
}
|
|
583
662
|
} catch (err) {
|
|
@@ -593,17 +672,22 @@ export class Waker {
|
|
|
593
672
|
);
|
|
594
673
|
}
|
|
595
674
|
} finally {
|
|
596
|
-
// Wake finished (cleanly or not):
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
//
|
|
675
|
+
// Wake finished (cleanly or not): EVERY resource this batch touched leaves the active
|
|
676
|
+
// set — the running anchor AND any drained/merged resources that were never promoted to
|
|
677
|
+
// the running row (e.g. a null-prompt batch that returned before the running transition,
|
|
678
|
+
// so their queued rows still linger). Drop them all and emit ONE fresh snapshot so the
|
|
679
|
+
// server ends their running/queued rows. The server reconcile is snapshot-authoritative,
|
|
680
|
+
// so absence == ended. Also clear the anchor's interrupting flag so it can never leak
|
|
681
|
+
// into a later wake of the same entity.
|
|
682
|
+
let changed = false;
|
|
683
|
+
for (const dk of drainedExecKeys) {
|
|
684
|
+
if (this.executions.delete(dk)) changed = true;
|
|
685
|
+
}
|
|
601
686
|
if (execKey) {
|
|
602
687
|
this.interrupting.delete(execKey);
|
|
603
|
-
if (this.executions.delete(execKey))
|
|
604
|
-
this.#emitExecutionChange();
|
|
605
|
-
}
|
|
688
|
+
if (this.executions.delete(execKey)) changed = true;
|
|
606
689
|
}
|
|
690
|
+
if (changed) this.#emitExecutionChange();
|
|
607
691
|
try {
|
|
608
692
|
cfg?.cleanup?.();
|
|
609
693
|
} catch {
|
|
@@ -647,8 +731,12 @@ export class Waker {
|
|
|
647
731
|
* @param {string|null} [transcriptRelayError]
|
|
648
732
|
* @param {import("./upload-hooks.mjs").TokenUsage|null} [usage] Per-turn token usage
|
|
649
733
|
* (daemon-token-usage); forwarded only on a terminal edge, mirroring transcriptRelayError.
|
|
734
|
+
* @param {string|null} [backendSessionId]
|
|
735
|
+
* @param {number} [coalescedCount] Number of wakes coalesced into this turn
|
|
736
|
+
* (add-daemon-wake-coalescing); forwarded only on the → running edge and only when > 1,
|
|
737
|
+
* so a single wake (default 1) leaves the field absent — byte-identical to before.
|
|
650
738
|
*/
|
|
651
|
-
async #advanceTurn(sessionId, status, entity, interruptedReason = null, transcriptRelayError = null, usage = null, backendSessionId = null) {
|
|
739
|
+
async #advanceTurn(sessionId, status, entity, interruptedReason = null, transcriptRelayError = null, usage = null, backendSessionId = null, coalescedCount = 1) {
|
|
652
740
|
try {
|
|
653
741
|
await this.advanceTurn({
|
|
654
742
|
sessionId,
|
|
@@ -659,6 +747,7 @@ export class Waker {
|
|
|
659
747
|
...(transcriptRelayError ? { transcriptRelayError } : {}),
|
|
660
748
|
...(usage ? { usage } : {}),
|
|
661
749
|
...(backendSessionId ? { backendSessionId } : {}),
|
|
750
|
+
...(status === "running" && coalescedCount > 1 ? { coalescedCount } : {}),
|
|
662
751
|
});
|
|
663
752
|
} catch (err) {
|
|
664
753
|
this.logger.warn(
|