commandmate 0.31.1 → 0.31.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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +170 -170
- package/.next/app-path-routes-manifest.json +29 -29
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +2 -2
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +2 -2
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +2 -2
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route.js +2 -2
- package/.next/server/app/api/push/escalation/route.js.nft.json +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route.js +2 -2
- package/.next/server/app/api/push/vapid/route.js.nft.json +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +10 -10
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +29 -29
- package/.next/server/chunks/1058.js +1 -1
- package/.next/server/chunks/1068.js +1 -1
- package/.next/server/chunks/1191.js +2 -2
- package/.next/server/chunks/1886.js +1 -1
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/261.js +1 -1
- package/.next/server/chunks/2739.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3484.js +1 -0
- package/.next/server/chunks/358.js +1 -0
- package/.next/server/chunks/4325.js +1 -1
- package/.next/server/chunks/4393.js +1 -1
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/5017.js +1 -1
- package/.next/server/chunks/5120.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/552.js +3 -3
- package/.next/server/chunks/595.js +8 -0
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/8800.js +1 -1
- package/.next/server/chunks/8918.js +3 -3
- package/.next/server/chunks/9123.js +1 -1
- package/.next/server/chunks/9130.js +2 -2
- package/.next/server/chunks/9627.js +3 -3
- package/.next/server/chunks/9696.js +2 -2
- package/.next/server/chunks/9798.js +2 -2
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{1783-11c9b4d8a52ebd91.js → 2813-de885de8a4b11910.js} +1 -1
- package/.next/static/chunks/4718-07ec4e10186928db.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-91b639c90795baa9.js +1 -0
- package/.next/static/css/acc4b9c3feb04287.css +1 -0
- package/.next/trace +13 -13
- package/dist/lib/detection/tools/verified-against.d.ts +24 -24
- package/dist/lib/detection/tools/verified-against.js +24 -24
- package/dist/server/locales/en/worktree.json +1402 -0
- package/dist/server/locales/ja/worktree.json +1402 -0
- package/dist/server/src/lib/db/chat-db.js +44 -1
- package/dist/server/src/lib/detection/cli-patterns.js +157 -23
- package/dist/server/src/lib/detection/model-info-extractor.js +185 -4
- package/dist/server/src/lib/detection/tools/antigravity/detect.js +105 -25
- package/dist/server/src/lib/detection/tools/antigravity/dialog.js +233 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +24 -24
- package/dist/server/src/lib/hooks/hook-settings-generator.js +16 -0
- package/dist/server/src/lib/hooks/sources/claude/model-switch.js +169 -0
- package/dist/server/src/lib/hooks/sources/claude/source.js +14 -1
- package/dist/server/src/lib/polling/response-checker.js +17 -1
- package/dist/server/src/lib/polling/response-poller-core.js +143 -9
- package/dist/server/src/lib/push/index.js +13 -1
- package/dist/server/src/lib/push/model-change-push-notifier.js +174 -0
- package/dist/server/src/lib/push/notification-dedup.js +2 -1
- package/dist/server/src/lib/push/push-sender.js +48 -20
- package/dist/server/src/lib/realtime/model-change-broadcast.js +171 -0
- package/dist/server/src/lib/realtime/types.js +19 -1
- package/dist/server/src/lib/realtime/waiting-broadcast.js +13 -0
- package/dist/server/src/lib/session/agent-event-state.js +344 -12
- package/package.json +1 -1
- package/.next/server/chunks/6894.js +0 -8
- package/.next/server/chunks/7187.js +0 -1
- package/.next/server/chunks/7472.js +0 -1
- package/.next/static/chunks/4718-46e1d2f07c8423ba.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-64d4ec3051971760.js +0 -1
- package/.next/static/css/5c13ea243dfdcb66.css +0 -1
- /package/.next/static/{60mm4_QBuFtFTfo5UMyKU → 0hTIackGXw6v7diXJS6yN}/_buildManifest.js +0 -0
- /package/.next/static/{60mm4_QBuFtFTfo5UMyKU → 0hTIackGXw6v7diXJS6yN}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The wording of "the model changed", and the push that carries it
|
|
4
|
+
* (Issue #2357).
|
|
5
|
+
*
|
|
6
|
+
* ## What this module owns
|
|
7
|
+
*
|
|
8
|
+
* Three things, and the first is the reason the other two live together:
|
|
9
|
+
*
|
|
10
|
+
* 1. **The sentence.** `Model changed from gpt-5.6-sol to gpt-5-mini`, in the
|
|
11
|
+
* reader's language, from `locales/<locale>/worktree.json` —
|
|
12
|
+
* `agentModel.changed`, next to the session row that shows the value on the
|
|
13
|
+
* phone. It is rendered here rather than in `push-sender` because it is also
|
|
14
|
+
* what the history row says: the model edge writes one row into
|
|
15
|
+
* `chat_messages` and sends one push, and a reader comparing the two must
|
|
16
|
+
* find the same words.
|
|
17
|
+
* 2. **Which language the history row is written in.** A row is stored once,
|
|
18
|
+
* and the server has no request to read a cookie from — the edge fires from
|
|
19
|
+
* the hook route or the status poll, neither of which is the reader. The
|
|
20
|
+
* best evidence it has is the language the readers registered their push
|
|
21
|
+
* subscriptions in, so the newest subscription's locale is used, and the
|
|
22
|
+
* default locale when nobody has registered one.
|
|
23
|
+
* 3. **The push itself.** One per transition, through `notifyPushSubscribers`,
|
|
24
|
+
* on the `enabled_prompt` bucket (see `NotificationEvent.modelChange` for
|
|
25
|
+
* why that bucket and why the transport kind reads `failure`).
|
|
26
|
+
*
|
|
27
|
+
* ## Why the dictionaries are static imports
|
|
28
|
+
*
|
|
29
|
+
* The same constraint `push-sender` documents: this module is reached from the
|
|
30
|
+
* custom server's graph (`ws-server` → `waiting-broadcast` →
|
|
31
|
+
* `model-change-broadcast` → here) and is therefore emitted into `dist/server`
|
|
32
|
+
* as CommonJS, where `next-intl` (ESM-only) cannot be required. The JSON is
|
|
33
|
+
* imported directly and `{from}` / `{to}` are substituted by hand.
|
|
34
|
+
*
|
|
35
|
+
* @module lib/push/model-change-push-notifier
|
|
36
|
+
*/
|
|
37
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
38
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
39
|
+
};
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.buildModelChangedSentence = buildModelChangedSentence;
|
|
42
|
+
exports.buildModelChangedBodies = buildModelChangedBodies;
|
|
43
|
+
exports.resolveReadersLocale = resolveReadersLocale;
|
|
44
|
+
exports.notifyModelChangePush = notifyModelChangePush;
|
|
45
|
+
const i18n_config_1 = require("../../config/i18n-config");
|
|
46
|
+
const db_instance_1 = require("../../lib/db/db-instance");
|
|
47
|
+
const push_subscriptions_db_1 = require("../../lib/db/push-subscriptions-db");
|
|
48
|
+
const worktree_db_1 = require("../../lib/db/worktree-db");
|
|
49
|
+
const logger_1 = require("../../lib/logger");
|
|
50
|
+
const worktree_json_1 = __importDefault(require("../../../locales/en/worktree.json"));
|
|
51
|
+
const worktree_json_2 = __importDefault(require("../../../locales/ja/worktree.json"));
|
|
52
|
+
const push_sender_1 = require("./push-sender");
|
|
53
|
+
const vapid_1 = require("./vapid");
|
|
54
|
+
const logger = (0, logger_1.createLogger)('push/model-change-notifier');
|
|
55
|
+
/**
|
|
56
|
+
* The one sentence, per locale. Keyed by `SupportedLocale` so adding a locale
|
|
57
|
+
* without a dictionary is a type error here rather than an English push in a
|
|
58
|
+
* Japanese install.
|
|
59
|
+
*/
|
|
60
|
+
const MODEL_CHANGED_TEMPLATES = {
|
|
61
|
+
en: worktree_json_1.default.agentModel.changed,
|
|
62
|
+
ja: worktree_json_2.default.agentModel.changed,
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* "Model changed from {from} to {to}", in one language.
|
|
66
|
+
*
|
|
67
|
+
* `from` / `to` are the resolved values `sessionStatusByInstance[].model`
|
|
68
|
+
* publishes, verbatim — the same string the phone's session row shows, so the
|
|
69
|
+
* sentence and the row cannot name the same model two ways.
|
|
70
|
+
*/
|
|
71
|
+
function buildModelChangedSentence(locale, from, to) {
|
|
72
|
+
return MODEL_CHANGED_TEMPLATES[(0, push_sender_1.resolvePushLocale)(locale)]
|
|
73
|
+
.replace('{from}', from)
|
|
74
|
+
.replace('{to}', to);
|
|
75
|
+
}
|
|
76
|
+
/** The sentence in every supported language, for a per-device fan-out. */
|
|
77
|
+
function buildModelChangedBodies(from, to) {
|
|
78
|
+
const bodies = {};
|
|
79
|
+
for (const locale of i18n_config_1.SUPPORTED_LOCALES) {
|
|
80
|
+
bodies[locale] = buildModelChangedSentence(locale, from, to);
|
|
81
|
+
}
|
|
82
|
+
return bodies;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The language the history row should be written in (Issue #2357).
|
|
86
|
+
*
|
|
87
|
+
* The newest push subscription's locale, because that is the only place the
|
|
88
|
+
* server has ever been told what language a reader reads in — registration
|
|
89
|
+
* resolves it the way the UI does (`resolveLocale`, cookie then
|
|
90
|
+
* `Accept-Language`) and stores it on the row. No subscription at all means the
|
|
91
|
+
* default locale, which is what the UI shows a reader who has chosen nothing.
|
|
92
|
+
*
|
|
93
|
+
* Never throws: a database that cannot answer is a reason to write the row in
|
|
94
|
+
* the default language, not a reason to write no row.
|
|
95
|
+
*/
|
|
96
|
+
function resolveReadersLocale(db) {
|
|
97
|
+
try {
|
|
98
|
+
const subscriptions = (0, push_subscriptions_db_1.getAllPushSubscriptions)(db);
|
|
99
|
+
let newest = null;
|
|
100
|
+
for (const sub of subscriptions) {
|
|
101
|
+
const updatedAt = sub.updatedAt.getTime();
|
|
102
|
+
if (newest === null || updatedAt > newest.updatedAt) {
|
|
103
|
+
newest = { updatedAt, locale: sub.locale };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return (0, push_sender_1.resolvePushLocale)(newest?.locale ?? i18n_config_1.DEFAULT_LOCALE);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return i18n_config_1.DEFAULT_LOCALE;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The worktree's display name, for the notification title. Falls back to the
|
|
114
|
+
* id rather than dropping the notification — the same rule
|
|
115
|
+
* `waiting-push-notifier` applies.
|
|
116
|
+
*/
|
|
117
|
+
function resolveWorktreeName(worktreeId) {
|
|
118
|
+
try {
|
|
119
|
+
return (0, worktree_db_1.getWorktreeById)((0, db_instance_1.getDbInstance)(), worktreeId)?.name ?? worktreeId;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return worktreeId;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Notify every opted-in device that this instance changed model (Issue #2357).
|
|
127
|
+
*
|
|
128
|
+
* States the fact and nothing more — no ranking, no "downgraded", because the
|
|
129
|
+
* Issue rules that out and because the server has no table that could say
|
|
130
|
+
* which of two models is the lesser. Never throws; push is advisory and the
|
|
131
|
+
* edge that called this has already been observed.
|
|
132
|
+
*
|
|
133
|
+
* @returns Null when a fan-out was attempted, otherwise why it was not.
|
|
134
|
+
*/
|
|
135
|
+
async function notifyModelChangePush(change) {
|
|
136
|
+
if (!(0, vapid_1.isPushConfigured)()) {
|
|
137
|
+
logger.debug('model-change-push-suppressed', {
|
|
138
|
+
worktreeId: change.worktreeId,
|
|
139
|
+
instanceId: change.instanceId,
|
|
140
|
+
reason: 'unconfigured',
|
|
141
|
+
});
|
|
142
|
+
return 'unconfigured';
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
await (0, push_sender_1.notifyPushSubscribers)({
|
|
146
|
+
worktreeId: change.worktreeId,
|
|
147
|
+
worktreeName: resolveWorktreeName(change.worktreeId),
|
|
148
|
+
// The delivery bucket, not the meaning — see `NotificationEvent.modelChange`.
|
|
149
|
+
kind: 'failure',
|
|
150
|
+
agentName: change.instanceId,
|
|
151
|
+
instanceId: change.instanceId,
|
|
152
|
+
modelChange: {
|
|
153
|
+
from: change.from,
|
|
154
|
+
to: change.to,
|
|
155
|
+
body: buildModelChangedBodies(change.from, change.to),
|
|
156
|
+
},
|
|
157
|
+
}, change.at);
|
|
158
|
+
logger.info('model-change-push-raised', {
|
|
159
|
+
worktreeId: change.worktreeId,
|
|
160
|
+
instanceId: change.instanceId,
|
|
161
|
+
from: change.from,
|
|
162
|
+
to: change.to,
|
|
163
|
+
source: change.source,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
logger.warn('model-change-push-failed', {
|
|
168
|
+
worktreeId: change.worktreeId,
|
|
169
|
+
instanceId: change.instanceId,
|
|
170
|
+
error: error instanceof Error ? error.message : String(error),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
@@ -20,7 +20,8 @@ exports.resetWaitingPushDedup = resetWaitingPushDedup;
|
|
|
20
20
|
const crypto_1 = require("crypto");
|
|
21
21
|
/** Default suppression window: repeats of identical content within this are dropped. */
|
|
22
22
|
exports.DEFAULT_DEDUP_WINDOW_MS = 30_000;
|
|
23
|
-
const lastSent =
|
|
23
|
+
const lastSent = globalThis.__notificationDedupLastSent ??
|
|
24
|
+
(globalThis.__notificationDedupLastSent = new Map());
|
|
24
25
|
function contentHash(content) {
|
|
25
26
|
return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
26
27
|
}
|
|
@@ -27,6 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
28
|
};
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.MODEL_CHANGE_TAG_SUFFIX = void 0;
|
|
30
31
|
exports.resolvePushLocale = resolvePushLocale;
|
|
31
32
|
exports.buildExcerpt = buildExcerpt;
|
|
32
33
|
exports.buildPushPayload = buildPushPayload;
|
|
@@ -51,6 +52,11 @@ const PUSH_MESSAGES = {
|
|
|
51
52
|
en: notifications_json_1.default.push,
|
|
52
53
|
ja: notifications_json_2.default.push,
|
|
53
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* The `tag` segment a model-change card carries in place of its kind
|
|
57
|
+
* (Issue #2357). Exported so the notifier's tests can name the card they expect.
|
|
58
|
+
*/
|
|
59
|
+
exports.MODEL_CHANGE_TAG_SUFFIX = 'model';
|
|
54
60
|
/**
|
|
55
61
|
* Narrow a stored subscription locale to one we can actually render.
|
|
56
62
|
* Subscriptions registered before v42 have `locale = NULL` and land on
|
|
@@ -147,25 +153,31 @@ function buildPushPayload(event, locale = i18n_config_1.DEFAULT_LOCALE, now = Da
|
|
|
147
153
|
const excerpt = buildExcerpt(event.excerpt);
|
|
148
154
|
const agentSuffix = event.agentName ? ` (${event.agentName})` : '';
|
|
149
155
|
const title = `${event.worktreeName}${agentSuffix}`;
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
156
|
+
const resolvedLocale = resolvePushLocale(locale);
|
|
157
|
+
const messages = PUSH_MESSAGES[resolvedLocale];
|
|
158
|
+
const body =
|
|
159
|
+
// Issue #2357: read first, because the event's `kind` is the bucket it is
|
|
160
|
+
// delivered to and says nothing about what it is. See `modelChange`.
|
|
161
|
+
event.modelChange
|
|
162
|
+
? event.modelChange.body[resolvedLocale]
|
|
163
|
+
: event.kind === 'prompt'
|
|
164
|
+
? // Issue #2001: the resolution's body replaces the stale card's, so it
|
|
165
|
+
// has to answer the question that card asked. It quotes no excerpt —
|
|
166
|
+
// the prompt is over, and repeating it would read as a new one.
|
|
167
|
+
event.resolved === true
|
|
168
|
+
? messages.promptResolved
|
|
169
|
+
: buildWaitingBody(event, messages, excerpt, now)
|
|
170
|
+
: event.kind === 'failure'
|
|
171
|
+
? buildFailureBody(event, messages, excerpt)
|
|
172
|
+
: // Issue #2045: an update notice is a completion by *bucket*, not by
|
|
173
|
+
// content, so it is the one completion whose body is not "Done".
|
|
174
|
+
event.updateAvailable
|
|
175
|
+
? messages.updateAvailable
|
|
176
|
+
.replace('{agent}', event.updateAvailable.agent)
|
|
177
|
+
.replace('{version}', event.updateAvailable.version)
|
|
178
|
+
: excerpt
|
|
179
|
+
? messages.completionWithExcerpt.replace('{excerpt}', excerpt)
|
|
180
|
+
: messages.completion;
|
|
169
181
|
return {
|
|
170
182
|
kind: event.kind,
|
|
171
183
|
title,
|
|
@@ -176,7 +188,12 @@ function buildPushPayload(event, locale = i18n_config_1.DEFAULT_LOCALE, now = Da
|
|
|
176
188
|
// Unchanged by #1790, and deliberately: the escalation carries the same tag
|
|
177
189
|
// as the notification it follows up, so the Service Worker replaces the
|
|
178
190
|
// stale one instead of stacking a second card for the same wait.
|
|
179
|
-
|
|
191
|
+
// Issue #2357: a model change gets a tag of its own, so it neither replaces
|
|
192
|
+
// a failure card (its transport bucket) nor is replaced by one; two changes
|
|
193
|
+
// of the same worktree do collapse, which is right — the newer is the fact.
|
|
194
|
+
tag: event.modelChange
|
|
195
|
+
? `${event.worktreeId}:${exports.MODEL_CHANGE_TAG_SUFFIX}`
|
|
196
|
+
: `${event.worktreeId}:${event.kind}`,
|
|
180
197
|
timestamp: now,
|
|
181
198
|
...(event.waitingKind ? { waitingKind: event.waitingKind } : {}),
|
|
182
199
|
...(event.resolved === true ? { resolved: true } : {}),
|
|
@@ -252,6 +269,17 @@ function passesDedup(event) {
|
|
|
252
269
|
// uses, so the two would suppress each other on a 30 s window neither wants.
|
|
253
270
|
if (event.resolved === true)
|
|
254
271
|
return true;
|
|
272
|
+
// Issue #2357: keyed on the transition, not on its wording — the body is
|
|
273
|
+
// localized per device and the same change must not send twice because two
|
|
274
|
+
// readers speak different languages. The producer already emits one edge per
|
|
275
|
+
// transition; this is the second net, on the same 30 s window as the rest.
|
|
276
|
+
if (event.modelChange) {
|
|
277
|
+
return (0, notification_dedup_1.shouldSendNotification)({
|
|
278
|
+
worktreeId: event.worktreeId,
|
|
279
|
+
kind: event.kind,
|
|
280
|
+
content: `${exports.MODEL_CHANGE_TAG_SUFFIX}:${event.instanceId ?? event.agentName ?? ''}:${event.modelChange.from}>${event.modelChange.to}`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
255
283
|
if (event.kind === 'prompt' && typeof event.waitingSince === 'number') {
|
|
256
284
|
return (0, notification_dedup_1.shouldSendWaitingPush)({
|
|
257
285
|
worktreeId: event.worktreeId,
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What happens when an agent instance changes model (Issue #2357).
|
|
4
|
+
*
|
|
5
|
+
* ## The edge, and its three receivers
|
|
6
|
+
*
|
|
7
|
+
* `agent-event-state` judges the model edge — the merged hook/frame value moving
|
|
8
|
+
* from one model to another, with the first sighting, the value going quiet
|
|
9
|
+
* and a spelling change all ruled out there — and hands it out through
|
|
10
|
+
* `onAgentModelChange`. This module is that subscription, and it is the ONE
|
|
11
|
+
* subscription: the three things the Issue asks for on the receiving side are
|
|
12
|
+
* fanned out from here, in the order that leaves the most durable trace first.
|
|
13
|
+
*
|
|
14
|
+
* 1. **The history row.** One assistant row in `chat_messages`, in the
|
|
15
|
+
* readers' language, so the chat transcript and the History tab both keep
|
|
16
|
+
* "the model changed from A to B" where the conversation is. Written
|
|
17
|
+
* first, because it is the receiver that survives a phone being off.
|
|
18
|
+
* 2. **The room frames.** The row is pushed as a `message` frame — the same
|
|
19
|
+
* frame `sendUserMessage` publishes — so a transcript that is open shows
|
|
20
|
+
* the line without waiting for its poll; then the `model_changed` frame
|
|
21
|
+
* itself, which is what the phone's session row turns amber on.
|
|
22
|
+
* 3. **The push.** `notifyModelChangePush`, fire-and-forget; it has its own
|
|
23
|
+
* dedup and its own logging.
|
|
24
|
+
*
|
|
25
|
+
* Each receiver is wrapped separately: a database that cannot be written must
|
|
26
|
+
* not cost the reader the socket frame, and a socket with no owner must not
|
|
27
|
+
* cost them the push.
|
|
28
|
+
*
|
|
29
|
+
* ## Why `publish` is injected, and who arms this
|
|
30
|
+
*
|
|
31
|
+
* The same shape as `waiting-broadcast`, and armed by it: `ws-server` owns the
|
|
32
|
+
* rooms and calls `startWaitingStatusBroadcast(handleBroadcast)` from
|
|
33
|
+
* `setupWebSocket`, which is the one place in the process that knows which
|
|
34
|
+
* bundle's `rooms` a frame has to reach. That call now arms this subscription
|
|
35
|
+
* too, with the same publisher, and `stopWaitingStatusBroadcast` (from
|
|
36
|
+
* `closeWebSocket`) disarms both — so the listener's lifetime is the server's,
|
|
37
|
+
* and a suite that stands a server up and tears it down leaves nothing pointed
|
|
38
|
+
* at a dead room map.
|
|
39
|
+
*
|
|
40
|
+
* ## Authorization
|
|
41
|
+
*
|
|
42
|
+
* None is bypassed: the publisher is the room broadcaster, and a socket only
|
|
43
|
+
* joins a room through the authenticated `subscribe` path.
|
|
44
|
+
*
|
|
45
|
+
* @module lib/realtime/model-change-broadcast
|
|
46
|
+
*/
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.buildModelChangedEvent = buildModelChangedEvent;
|
|
49
|
+
exports.handleAgentModelChange = handleAgentModelChange;
|
|
50
|
+
exports.startModelChangeBroadcast = startModelChangeBroadcast;
|
|
51
|
+
exports.stopModelChangeBroadcast = stopModelChangeBroadcast;
|
|
52
|
+
exports.isModelChangeBroadcastActive = isModelChangeBroadcastActive;
|
|
53
|
+
const db_instance_1 = require("../../lib/db/db-instance");
|
|
54
|
+
const chat_db_1 = require("../../lib/db/chat-db");
|
|
55
|
+
const logger_1 = require("../../lib/logger");
|
|
56
|
+
const model_change_push_notifier_1 = require("../../lib/push/model-change-push-notifier");
|
|
57
|
+
const types_1 = require("../../lib/realtime/types");
|
|
58
|
+
const agent_event_state_1 = require("../../lib/session/agent-event-state");
|
|
59
|
+
const logger = (0, logger_1.createLogger)('model-change-broadcast');
|
|
60
|
+
/**
|
|
61
|
+
* Translate a model edge into the frame clients receive.
|
|
62
|
+
*
|
|
63
|
+
* Field for field the producer's record; `instance` is already resolved there
|
|
64
|
+
* (`instanceId ?? cliToolId`), so a client can match it against its own pane
|
|
65
|
+
* without re-deriving the convention.
|
|
66
|
+
*/
|
|
67
|
+
function buildModelChangedEvent(change) {
|
|
68
|
+
return {
|
|
69
|
+
type: types_1.MODEL_CHANGED_EVENT_TYPE,
|
|
70
|
+
worktreeId: change.worktreeId,
|
|
71
|
+
cliTool: change.cliToolId,
|
|
72
|
+
instance: change.instanceId,
|
|
73
|
+
from: change.from,
|
|
74
|
+
to: change.to,
|
|
75
|
+
source: change.source,
|
|
76
|
+
at: change.at,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Write the history row and hand back the `message` frame for it, or null
|
|
81
|
+
* when the database could not take it.
|
|
82
|
+
*
|
|
83
|
+
* Never throws. The sentence is localized once, for the readers' registered
|
|
84
|
+
* language (`resolveReadersLocale`) — a stored row cannot be re-rendered per
|
|
85
|
+
* viewer the way the push body is.
|
|
86
|
+
*/
|
|
87
|
+
function recordModelChangeHistory(change) {
|
|
88
|
+
try {
|
|
89
|
+
const db = (0, db_instance_1.getDbInstance)();
|
|
90
|
+
const locale = (0, model_change_push_notifier_1.resolveReadersLocale)(db);
|
|
91
|
+
const message = (0, chat_db_1.createModelChangeMessage)(db, {
|
|
92
|
+
worktreeId: change.worktreeId,
|
|
93
|
+
cliToolId: change.cliToolId,
|
|
94
|
+
instanceId: change.instanceId,
|
|
95
|
+
content: (0, model_change_push_notifier_1.buildModelChangedSentence)(locale, change.from, change.to),
|
|
96
|
+
at: change.at,
|
|
97
|
+
});
|
|
98
|
+
return { type: 'message', worktreeId: change.worktreeId, message };
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
logger.warn('model-change-history-failed', {
|
|
102
|
+
worktreeId: change.worktreeId,
|
|
103
|
+
instanceId: change.instanceId,
|
|
104
|
+
error: error instanceof Error ? error.message : String(error),
|
|
105
|
+
});
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Deliver one edge to all three receivers. Exported so a suite can drive the
|
|
111
|
+
* fan-out without a live subscription; never throws.
|
|
112
|
+
*/
|
|
113
|
+
function handleAgentModelChange(change, publish) {
|
|
114
|
+
logger.info('model-changed', {
|
|
115
|
+
worktreeId: change.worktreeId,
|
|
116
|
+
cliToolId: change.cliToolId,
|
|
117
|
+
instanceId: change.instanceId,
|
|
118
|
+
from: change.from,
|
|
119
|
+
to: change.to,
|
|
120
|
+
source: change.source,
|
|
121
|
+
});
|
|
122
|
+
const messageFrame = recordModelChangeHistory(change);
|
|
123
|
+
try {
|
|
124
|
+
if (messageFrame)
|
|
125
|
+
publish(change.worktreeId, messageFrame);
|
|
126
|
+
publish(change.worktreeId, buildModelChangedEvent(change));
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
logger.warn('model-change-broadcast-failed', {
|
|
130
|
+
worktreeId: change.worktreeId,
|
|
131
|
+
instanceId: change.instanceId,
|
|
132
|
+
error: error instanceof Error ? error.message : String(error),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// Fire-and-forget: `notifyModelChangePush` contains its own failures and
|
|
136
|
+
// logs its own decision, and nothing here has anything to do with the result.
|
|
137
|
+
void (0, model_change_push_notifier_1.notifyModelChangePush)(change);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Subscribe to the model edge and fan it out.
|
|
141
|
+
*
|
|
142
|
+
* Idempotent: calling it again replaces the previous subscription rather than
|
|
143
|
+
* adding a second one, so a re-entered `setupWebSocket` cannot double-send.
|
|
144
|
+
*
|
|
145
|
+
* @param publish - Room broadcaster, normally `ws-server`'s internal one.
|
|
146
|
+
* @returns The unsubscribe function (also reachable as
|
|
147
|
+
* {@link stopModelChangeBroadcast}).
|
|
148
|
+
*/
|
|
149
|
+
function startModelChangeBroadcast(publish) {
|
|
150
|
+
stopModelChangeBroadcast();
|
|
151
|
+
const unsubscribe = (0, agent_event_state_1.onAgentModelChange)((change) => {
|
|
152
|
+
handleAgentModelChange(change, publish);
|
|
153
|
+
});
|
|
154
|
+
globalThis.__modelChangeBroadcastUnsubscribe = unsubscribe;
|
|
155
|
+
return unsubscribe;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Drop the subscription, if any. Safe to call when none is active — and called
|
|
159
|
+
* from `stopWaitingStatusBroadcast`, i.e. from `closeWebSocket`.
|
|
160
|
+
*/
|
|
161
|
+
function stopModelChangeBroadcast() {
|
|
162
|
+
const existing = globalThis.__modelChangeBroadcastUnsubscribe;
|
|
163
|
+
if (existing) {
|
|
164
|
+
existing();
|
|
165
|
+
globalThis.__modelChangeBroadcastUnsubscribe = undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Whether a subscription is currently active. Test seam. */
|
|
169
|
+
function isModelChangeBroadcastActive() {
|
|
170
|
+
return globalThis.__modelChangeBroadcastUnsubscribe !== undefined;
|
|
171
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* the canonical {@link RealtimeEvent}.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.MESSAGES_INVALIDATED_EVENT_TYPE = exports.MAX_CHAT_TURN_PROGRESS_BODY_LENGTH = exports.CHAT_TURN_PROGRESS_MIN_INTERVAL_MS = exports.CHAT_TURN_PROGRESS_EVENT_TYPE = exports.VERSION_MISMATCH_EVENT_TYPE = exports.CLIENT_VERSION_MESSAGE_TYPE = void 0;
|
|
12
|
+
exports.MODEL_CHANGED_EVENT_TYPE = exports.MESSAGES_INVALIDATED_EVENT_TYPE = exports.MAX_CHAT_TURN_PROGRESS_BODY_LENGTH = exports.CHAT_TURN_PROGRESS_MIN_INTERVAL_MS = exports.CHAT_TURN_PROGRESS_EVENT_TYPE = exports.VERSION_MISMATCH_EVENT_TYPE = exports.CLIENT_VERSION_MESSAGE_TYPE = void 0;
|
|
13
13
|
exports.isVersionMismatch = isVersionMismatch;
|
|
14
14
|
exports.truncateChatTurnProgressBody = truncateChatTurnProgressBody;
|
|
15
15
|
exports.parseRealtimeEvent = parseRealtimeEvent;
|
|
@@ -143,6 +143,24 @@ function truncateChatTurnProgressBody(body, maxLength = exports.MAX_CHAT_TURN_PR
|
|
|
143
143
|
* an omitted field would simply never match.
|
|
144
144
|
*/
|
|
145
145
|
exports.MESSAGES_INVALIDATED_EVENT_TYPE = 'messages_invalidated';
|
|
146
|
+
/**
|
|
147
|
+
* "This instance is now on a different model" (Issue #2357).
|
|
148
|
+
*
|
|
149
|
+
* Emitted once per transition from `agent-event-state`'s model edge, on the
|
|
150
|
+
* same room broadcast `session_status_changed` rides — so every client that
|
|
151
|
+
* subscribed to the worktree hears it, and one that never subscribed does not.
|
|
152
|
+
* It is its own type rather than a field on `session_status_changed` because
|
|
153
|
+
* the consumers do not overlap: the phone's session row is the only thing that
|
|
154
|
+
* reacts, and every `session_status_changed` listener would otherwise have to
|
|
155
|
+
* learn to ignore it.
|
|
156
|
+
*
|
|
157
|
+
* `from` and `to` are the resolved values `sessionStatusByInstance[].model`
|
|
158
|
+
* publishes, verbatim — the client shows them, it does not compare them. The
|
|
159
|
+
* suppression rules (`null → value`, `value → null`, a first sighting, a
|
|
160
|
+
* spelling change) are applied by the producer, and there is nothing here for
|
|
161
|
+
* a client to re-judge.
|
|
162
|
+
*/
|
|
163
|
+
exports.MODEL_CHANGED_EVENT_TYPE = 'model_changed';
|
|
146
164
|
/**
|
|
147
165
|
* Parse a raw WebSocket frame into the inner realtime event.
|
|
148
166
|
*
|
|
@@ -37,6 +37,7 @@ exports.buildWaitingStatusEvent = buildWaitingStatusEvent;
|
|
|
37
37
|
exports.startWaitingStatusBroadcast = startWaitingStatusBroadcast;
|
|
38
38
|
exports.stopWaitingStatusBroadcast = stopWaitingStatusBroadcast;
|
|
39
39
|
exports.isWaitingStatusBroadcastActive = isWaitingStatusBroadcastActive;
|
|
40
|
+
const model_change_broadcast_1 = require("../../lib/realtime/model-change-broadcast");
|
|
40
41
|
const waiting_episode_state_1 = require("../../lib/session/waiting-episode-state");
|
|
41
42
|
/**
|
|
42
43
|
* Translate a waiting crossing into the frame clients receive.
|
|
@@ -63,6 +64,14 @@ function buildWaitingStatusEvent(transition) {
|
|
|
63
64
|
* Idempotent: calling it again replaces the previous subscription rather than
|
|
64
65
|
* adding a second one, so a re-entered `setupWebSocket` cannot double-send.
|
|
65
66
|
*
|
|
67
|
+
* Issue #2357: this is also where the model edge is armed. `setupWebSocket`
|
|
68
|
+
* calls exactly one function to start the edge → room subscriptions, and it
|
|
69
|
+
* is this one; the model-change fan-out (`model-change-broadcast`) needs the
|
|
70
|
+
* same publisher for the same reason the waiting edge does — the closure has
|
|
71
|
+
* to hold the bundle's own `rooms` — so it is started here with it, and
|
|
72
|
+
* stopped with it below, rather than growing a second arming call in
|
|
73
|
+
* `ws-server` for every edge that comes along.
|
|
74
|
+
*
|
|
66
75
|
* @param publish - Room broadcaster, normally `ws-server`'s internal one.
|
|
67
76
|
* @returns The unsubscribe function (also reachable as
|
|
68
77
|
* {@link stopWaitingStatusBroadcast}).
|
|
@@ -73,12 +82,15 @@ function startWaitingStatusBroadcast(publish) {
|
|
|
73
82
|
publish(transition.worktreeId, buildWaitingStatusEvent(transition));
|
|
74
83
|
});
|
|
75
84
|
globalThis.__waitingStatusBroadcastUnsubscribe = unsubscribe;
|
|
85
|
+
(0, model_change_broadcast_1.startModelChangeBroadcast)(publish);
|
|
76
86
|
return unsubscribe;
|
|
77
87
|
}
|
|
78
88
|
/**
|
|
79
89
|
* Drop the subscription, if any. Safe to call when none is active — and called
|
|
80
90
|
* from `closeWebSocket`, so a suite that stands a server up and tears it down
|
|
81
91
|
* does not leave a listener pointed at a dead room map.
|
|
92
|
+
*
|
|
93
|
+
* Issue #2357: drops the model edge's subscription with it, for the same reason.
|
|
82
94
|
*/
|
|
83
95
|
function stopWaitingStatusBroadcast() {
|
|
84
96
|
const existing = globalThis.__waitingStatusBroadcastUnsubscribe;
|
|
@@ -86,6 +98,7 @@ function stopWaitingStatusBroadcast() {
|
|
|
86
98
|
existing();
|
|
87
99
|
globalThis.__waitingStatusBroadcastUnsubscribe = undefined;
|
|
88
100
|
}
|
|
101
|
+
(0, model_change_broadcast_1.stopModelChangeBroadcast)();
|
|
89
102
|
}
|
|
90
103
|
/** Whether a subscription is currently active. Test seam. */
|
|
91
104
|
function isWaitingStatusBroadcastActive() {
|