synabun 2.0.5 → 2.0.9
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/CHANGELOG.md +78 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +112 -75
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/package-processes.js +670 -0
- package/lib/packaged-runtime.js +138 -11
- package/lib/runtime-dependencies.js +225 -0
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/browser-bench.js +1 -1
- package/mcp-server/dist/services/browser-bench.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/categories.d.ts +35 -2
- package/mcp-server/dist/services/categories.d.ts.map +1 -1
- package/mcp-server/dist/services/categories.js +77 -33
- package/mcp-server/dist/services/categories.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/desktop-bench.js +1 -1
- package/mcp-server/dist/services/desktop-bench.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +11 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +35 -14
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/bluesky.js +1 -1
- package/mcp-server/dist/tools/bluesky.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +1 -1
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-batch.js +1 -1
- package/mcp-server/dist/tools/browser-batch.js.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +1 -1
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +1 -1
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +1 -1
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/card-tools.js +1 -1
- package/mcp-server/dist/tools/card-tools.js.map +1 -1
- package/mcp-server/dist/tools/category.d.ts +1 -1
- package/mcp-server/dist/tools/category.d.ts.map +1 -1
- package/mcp-server/dist/tools/category.js +5 -1
- package/mcp-server/dist/tools/category.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +1 -1
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +1 -1
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/computer.d.ts +1 -1
- package/mcp-server/dist/tools/computer.d.ts.map +1 -1
- package/mcp-server/dist/tools/computer.js +1 -1
- package/mcp-server/dist/tools/computer.js.map +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-channel.js +1 -1
- package/mcp-server/dist/tools/discord-channel.js.map +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-guild.js +1 -1
- package/mcp-server/dist/tools/discord-guild.js.map +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-member.js +1 -1
- package/mcp-server/dist/tools/discord-member.js.map +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-message.js +1 -1
- package/mcp-server/dist/tools/discord-message.js.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-role.js +1 -1
- package/mcp-server/dist/tools/discord-role.js.map +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-thread.js +1 -1
- package/mcp-server/dist/tools/discord-thread.js.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.js +1 -1
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
- package/mcp-server/dist/tools/fb-groups.js +1 -1
- package/mcp-server/dist/tools/fb-groups.js.map +1 -1
- package/mcp-server/dist/tools/forget.d.ts +1 -1
- package/mcp-server/dist/tools/forget.d.ts.map +1 -1
- package/mcp-server/dist/tools/forget.js +1 -1
- package/mcp-server/dist/tools/forget.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +1 -1
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.js +1 -1
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.js +1 -1
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.js +1 -1
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/image-tools.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/dist/tools/loop.d.ts +1 -1
- package/mcp-server/dist/tools/loop.d.ts.map +1 -1
- package/mcp-server/dist/tools/loop.js +1 -1
- package/mcp-server/dist/tools/loop.js.map +1 -1
- package/mcp-server/dist/tools/memories.d.ts +1 -1
- package/mcp-server/dist/tools/memories.d.ts.map +1 -1
- package/mcp-server/dist/tools/memories.js +1 -1
- package/mcp-server/dist/tools/memories.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +1 -1
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/profile.d.ts +1 -1
- package/mcp-server/dist/tools/profile.d.ts.map +1 -1
- package/mcp-server/dist/tools/profile.js +1 -1
- package/mcp-server/dist/tools/profile.js.map +1 -1
- package/mcp-server/dist/tools/recall.d.ts +1 -1
- package/mcp-server/dist/tools/recall.d.ts.map +1 -1
- package/mcp-server/dist/tools/recall.js +1 -1
- package/mcp-server/dist/tools/recall.js.map +1 -1
- package/mcp-server/dist/tools/reflect.d.ts +1 -1
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
- package/mcp-server/dist/tools/reflect.js +1 -1
- package/mcp-server/dist/tools/reflect.js.map +1 -1
- package/mcp-server/dist/tools/remember.d.ts +1 -1
- package/mcp-server/dist/tools/remember.d.ts.map +1 -1
- package/mcp-server/dist/tools/remember.js +13 -11
- package/mcp-server/dist/tools/remember.js.map +1 -1
- package/mcp-server/dist/tools/restore.d.ts +1 -1
- package/mcp-server/dist/tools/restore.d.ts.map +1 -1
- package/mcp-server/dist/tools/restore.js +1 -1
- package/mcp-server/dist/tools/restore.js.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
- package/mcp-server/dist/tools/sync.d.ts +1 -1
- package/mcp-server/dist/tools/sync.d.ts.map +1 -1
- package/mcp-server/dist/tools/sync.js +1 -1
- package/mcp-server/dist/tools/sync.js.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
- package/mcp-server/dist/tools/utils.d.ts +1 -1
- package/mcp-server/dist/tools/utils.d.ts.map +1 -1
- package/mcp-server/dist/tools/utils.js +1 -1
- package/mcp-server/dist/tools/utils.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/download.js +1 -1
- package/mcp-server/dist/tools/youtube/download.js.map +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/media.js +1 -1
- package/mcp-server/dist/tools/youtube/media.js.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/source.js +1 -1
- package/mcp-server/dist/tools/youtube/source.js.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.js +4 -9
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
- package/mcp-server/dist/tui.js +18 -5
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +34 -15
- package/mcp-server/src/tools/bluesky.ts +1 -1
- package/mcp-server/src/tools/browser-advanced.ts +1 -1
- package/mcp-server/src/tools/browser-batch.ts +1 -1
- package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
- package/mcp-server/src/tools/browser-interact.ts +1 -1
- package/mcp-server/src/tools/browser-navigate.ts +1 -1
- package/mcp-server/src/tools/browser-observe.ts +1 -1
- package/mcp-server/src/tools/card-tools.ts +1 -1
- package/mcp-server/src/tools/category.ts +6 -1
- package/mcp-server/src/tools/choice.ts +1 -1
- package/mcp-server/src/tools/computer.ts +1 -1
- package/mcp-server/src/tools/discord-channel.ts +1 -1
- package/mcp-server/src/tools/discord-guild.ts +1 -1
- package/mcp-server/src/tools/discord-member.ts +1 -1
- package/mcp-server/src/tools/discord-message.ts +1 -1
- package/mcp-server/src/tools/discord-onboarding.ts +1 -1
- package/mcp-server/src/tools/discord-role.ts +1 -1
- package/mcp-server/src/tools/discord-thread.ts +1 -1
- package/mcp-server/src/tools/discord-webhook.ts +1 -1
- package/mcp-server/src/tools/fb-groups.ts +1 -1
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git-tools.ts +1 -1
- package/mcp-server/src/tools/gsc/experience.ts +1 -1
- package/mcp-server/src/tools/gsc/indexing.ts +1 -1
- package/mcp-server/src/tools/gsc/inspect.ts +1 -1
- package/mcp-server/src/tools/gsc/navigate.ts +1 -1
- package/mcp-server/src/tools/gsc/performance.ts +1 -1
- package/mcp-server/src/tools/gsc/settings.ts +1 -1
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
- package/mcp-server/src/tools/loop.ts +1 -1
- package/mcp-server/src/tools/memories.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +1 -1
- package/mcp-server/src/tools/profile.ts +1 -1
- package/mcp-server/src/tools/recall.ts +1 -1
- package/mcp-server/src/tools/reflect.ts +1 -1
- package/mcp-server/src/tools/remember.ts +13 -12
- package/mcp-server/src/tools/restore.ts +1 -1
- package/mcp-server/src/tools/style-guide-tools.ts +1 -1
- package/mcp-server/src/tools/sync.ts +1 -1
- package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
- package/mcp-server/src/tools/utils.ts +1 -1
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
- package/mcp-server/src/tools/youtube/analytics.ts +1 -1
- package/mcp-server/src/tools/youtube/download.ts +1 -1
- package/mcp-server/src/tools/youtube/media.ts +1 -1
- package/mcp-server/src/tools/youtube/metadata.ts +1 -1
- package/mcp-server/src/tools/youtube/navigate.ts +1 -1
- package/mcp-server/src/tools/youtube/playlist.ts +1 -1
- package/mcp-server/src/tools/youtube/schedule.ts +1 -1
- package/mcp-server/src/tools/youtube/source.ts +1 -1
- package/mcp-server/src/tools/youtube/upload.ts +4 -12
- package/mcp-server/src/tui.ts +24 -10
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +645 -57
- package/neural-interface/i18n/pt-BR.json +644 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/assistant-api.js +274 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +312 -48
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +180 -0
- package/neural-interface/lib/codex-accounts.js +502 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +122 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/exec-loop-command.js +3 -1
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +163 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +22 -4
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +96 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +6 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +542 -0
- package/neural-interface/public/shared/account-usage.js +79 -0
- package/neural-interface/public/shared/account-view.js +336 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +39 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +288 -32
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +219 -80
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +87 -16
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +160 -29
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +49 -108
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +172 -111
- package/neural-interface/public/shared/cdx/cdx-tabs.js +264 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +36 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +169 -2
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +490 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +281 -32
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +262 -217
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +8 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +598 -7
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1384 -679
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +57 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +80 -47
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +1 -1
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -29,6 +29,9 @@ import { emit, on, state as appState } from '../state.js';
|
|
|
29
29
|
import { storage } from '../storage.js';
|
|
30
30
|
import { KEYS } from '../constants.js';
|
|
31
31
|
import { t } from '../i18n.js';
|
|
32
|
+
// The account words of the brain picker and the accounts manager (account-view.js) in the page's language.
|
|
33
|
+
// Here, not in those modules: node tests load them, and i18n.js reads the page's storage.
|
|
34
|
+
import '../account-i18n.js';
|
|
32
35
|
import { forgetGreeting, mountGreeting } from '../greeting/greeting-card.js';
|
|
33
36
|
import { notify, NOTIF_TYPE } from '../ui-notifications.js';
|
|
34
37
|
import { getProviderMeta } from '../provider-icons.js';
|
|
@@ -51,7 +54,7 @@ import { createAgentsDock } from './asst-agents-dock.js';
|
|
|
51
54
|
import { createUsageGauge } from './asst-usage.js';
|
|
52
55
|
import { closeMenu, isMenuOpen, openMenu } from './asst-menu.js';
|
|
53
56
|
import { createRouteModeControl, openModelsManager, openRoutesEditor, refreshModelsManager, renderRouteCard, routeModeLabel } from './asst-route.js';
|
|
54
|
-
import { budgetChipText, budgetTone, openBudgetEditor } from './asst-budget.js';
|
|
57
|
+
import { budgetChipText, budgetSpendText, budgetTone, closeBudgetEditor, createBudgetController, mountBudgetChip, openBudgetEditor } from './asst-budget.js';
|
|
55
58
|
import { computerRemoteView, createComputerPanel, createComputerToggle, normalizeComputerRemote, openFrameLightbox, closeFrameLightbox, requestDesktopStop } from './asst-computer.js';
|
|
56
59
|
import {
|
|
57
60
|
applyLimit, applyLimitPacket, applyUsagePacket, brainLabel, createRenameWriter, createTabState, DEFAULT_ROUTE_MODE, deserializeTabState, fmtCost, fmtRunLine, limitExpiresIn, limitNoticeText, modelShortName,
|
|
@@ -61,7 +64,7 @@ import {
|
|
|
61
64
|
import {
|
|
62
65
|
closeAssistantSession, dispatchAssistantRun, escalateAssistantRun, focusAssistantRun, getAssistantRun,
|
|
63
66
|
fetchManageCatalog, getAssistantRunResult, getAssistantSession, getAssistantSessionUsage, getDesktopStatus, getRouting, killAllAssistantRuns, removeAssistantRun, clearFinishedAssistantRuns,
|
|
64
|
-
listAssistantSessions, patchAssistantSession, patchHiddenModels, stopAssistantRun, uploadAssistantAttachment,
|
|
67
|
+
listAssistantSessions, patchAssistantSession, patchHiddenModels, setAdminSandbox, stopAssistantRun, uploadAssistantAttachment,
|
|
65
68
|
} from './asst-api.js';
|
|
66
69
|
|
|
67
70
|
const ICON_SESSIONS = '<svg viewBox="0 0 24 24"><path d="M3 6h18M3 12h18M3 18h12"/></svg>';
|
|
@@ -151,8 +154,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
151
154
|
engine: '',
|
|
152
155
|
sdkVersion: '',
|
|
153
156
|
routing: null, // server view { mode, effectiveMode, defaultMode, askBelow }
|
|
154
|
-
features: { routing: null, computer: null, testDensity: null, rename: null }, // null = unknown yet
|
|
157
|
+
features: { routing: null, computer: null, testDensity: null, rename: null, adminSandbox: null }, // null = unknown yet
|
|
155
158
|
testDensityEffective: null, // server view: the level in effect
|
|
159
|
+
adminSandbox: null, // server view: the global Admin sandbox setting (every agent)
|
|
156
160
|
brainCapabilities: null,
|
|
157
161
|
computerEffective: st.computerUse === true,
|
|
158
162
|
desktopSupported: null,
|
|
@@ -365,11 +369,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
365
369
|
}
|
|
366
370
|
|
|
367
371
|
function renderCost() {
|
|
368
|
-
// The
|
|
369
|
-
const budget = st.budget && Number.isFinite(Number(st.budget.
|
|
370
|
-
const total = budget ? Number(budget.
|
|
371
|
-
costEl.textContent = fmtCost(total);
|
|
372
|
-
costEl.hidden = !(total > 0);
|
|
372
|
+
// The confirmed SessionBudget includes both brain and worker spend.
|
|
373
|
+
const budget = st.budget && Number.isFinite(Number(st.budget.spentUsd)) ? st.budget : null;
|
|
374
|
+
const total = budget ? Number(budget.spentUsd) : totalCost();
|
|
375
|
+
costEl.textContent = budget ? budgetSpendText(budget, { t }) : fmtCost(total);
|
|
376
|
+
costEl.hidden = !budget && !(total > 0);
|
|
373
377
|
const tone = budgetTone(budget);
|
|
374
378
|
if (tone && tone !== 'ok') costEl.dataset.tone = tone;
|
|
375
379
|
else delete costEl.dataset.tone;
|
|
@@ -379,6 +383,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
379
383
|
? tt('assistant.topbar.costSplit', 'Session {session} · agents {agents}', { session: fmtCost(st.costUsd || 0), agents: fmtCost(st.dispatchUsd) })
|
|
380
384
|
: tt('assistant.topbar.cost', 'Session cost');
|
|
381
385
|
costEl.setAttribute('data-tooltip', tip);
|
|
386
|
+
costEl.setAttribute('aria-label', `${tt('assistant.budget.title', 'Budget')}: ${tip}`);
|
|
382
387
|
}
|
|
383
388
|
|
|
384
389
|
function brainModelLabel() {
|
|
@@ -441,6 +446,12 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
441
446
|
});
|
|
442
447
|
|
|
443
448
|
// ── Renderer ──
|
|
449
|
+
let emptyBudgetChip = null;
|
|
450
|
+
const budgetController = createBudgetController({ t, sessionId, onSaved: (data) => {
|
|
451
|
+
if (destroyed) return;
|
|
452
|
+
if (data?.session) st.budget = data.session;
|
|
453
|
+
renderCost();
|
|
454
|
+
} });
|
|
444
455
|
const renderer = createRenderer(transcriptEl, {
|
|
445
456
|
t,
|
|
446
457
|
providerIcon,
|
|
@@ -463,6 +474,16 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
463
474
|
emptySuggestions: () => emptySuggestions(),
|
|
464
475
|
// "Choose a project" in the empty state is the toolbar's Working project field (asst-brain-picker.js).
|
|
465
476
|
mountEmptyProject: (slot) => picker.mountEmptyProject(slot),
|
|
477
|
+
mountEmptyBudget: (slot) => {
|
|
478
|
+
emptyBudgetChip?.destroy();
|
|
479
|
+
emptyBudgetChip = mountBudgetChip(slot, budgetController, { t, mascot: () => director });
|
|
480
|
+
return emptyBudgetChip.el;
|
|
481
|
+
},
|
|
482
|
+
unmountEmptyControls: () => {
|
|
483
|
+
picker.mountEmptyProject(null);
|
|
484
|
+
emptyBudgetChip?.destroy();
|
|
485
|
+
emptyBudgetChip = null;
|
|
486
|
+
},
|
|
466
487
|
onSuggestion: (template) => composer.fillTemplate(template),
|
|
467
488
|
heroOrigin: () => { try { return host.takeHeroOrigin?.() || null; } catch { return null; } },
|
|
468
489
|
onMascotChange: () => director?.retarget(),
|
|
@@ -549,7 +570,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
549
570
|
// Model · effort · permission mode · account sit in the composer row next to attach; project and MCP in the toolbar.
|
|
550
571
|
const picker = createBrainPicker({ brain: composer.el.querySelector('.asst-composer-brain'), context: barGroups.context }, {
|
|
551
572
|
t,
|
|
573
|
+
mascot: () => director,
|
|
552
574
|
getBrain: () => st.brain,
|
|
575
|
+
getRunning: () => st.running,
|
|
553
576
|
onChange: (next, meta) => applyBrain(next, meta),
|
|
554
577
|
onAddAccount: (provider) => openAccountsManager({ provider, ...accountHooks() }).openAdd(),
|
|
555
578
|
onManageAccounts: (provider) => openAccountsManager({ provider, ...accountHooks() }),
|
|
@@ -561,6 +584,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
561
584
|
// Test density sits right after the permission mode; hidden on a server that does not know it.
|
|
562
585
|
getTestDensity: () => testDensityView(),
|
|
563
586
|
onTestDensity: (level) => setTestDensity(level),
|
|
587
|
+
// The global Admin sandbox switch, in the permission menu; hidden on a server that does not serve it.
|
|
588
|
+
getAdminSandbox: () => adminSandboxView(),
|
|
589
|
+
onAdminSandbox: (enabled) => changeAdminSandbox(enabled),
|
|
564
590
|
// The registered projects arrived: their labels name the project in the fields and the "Fix … in" suggestion.
|
|
565
591
|
onProjects: () => { picker.render(); renderer.refreshEmpty(); },
|
|
566
592
|
});
|
|
@@ -625,7 +651,10 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
625
651
|
function fetchUsage() {
|
|
626
652
|
const seq = ++usageFetchSeq;
|
|
627
653
|
getAssistantSessionUsage(sessionId).then((data) => {
|
|
628
|
-
if (!destroyed && seq === usageFetchSeq && data?.ok
|
|
654
|
+
if (!destroyed && seq === usageFetchSeq && data?.ok) {
|
|
655
|
+
if (data.budget) budgetController.receiveSession(data.budget);
|
|
656
|
+
if (data.usage) applyUsage(data.usage);
|
|
657
|
+
}
|
|
629
658
|
}).catch(() => { /* older servers do not expose usage */ });
|
|
630
659
|
}
|
|
631
660
|
function applyUsage(packet) {
|
|
@@ -813,6 +842,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
813
842
|
case 'assistant:mailbox':
|
|
814
843
|
renderer.appendMailbox(packet.items || packet.events || packet.mailbox || []);
|
|
815
844
|
return;
|
|
845
|
+
case 'assistant:budget':
|
|
846
|
+
budgetController.receiveSession(packet.budget);
|
|
847
|
+
return;
|
|
816
848
|
case 'assistant:cost':
|
|
817
849
|
applyCost(packet);
|
|
818
850
|
return;
|
|
@@ -885,7 +917,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
885
917
|
if (session != null) st.costUsd = session;
|
|
886
918
|
else if (!packet?.runId && num(packet?.costUsd) != null) st.costUsd = num(packet.costUsd);
|
|
887
919
|
if (num(packet?.dispatchUsd) != null) st.dispatchUsd = num(packet.dispatchUsd);
|
|
888
|
-
if (packet?.budget && typeof packet.budget === 'object')
|
|
920
|
+
if (packet?.budget && typeof packet.budget === 'object') budgetController.receiveSession(packet.budget);
|
|
889
921
|
if (typeof packet?.brainMetered === 'boolean') st.brainMetered = packet.brainMetered;
|
|
890
922
|
if (packet?.runId && num(packet.costUsd) != null) dock.patchRun(packet.runId, { costUsd: num(packet.costUsd) });
|
|
891
923
|
if (session != null) st.serverCost = true;
|
|
@@ -905,7 +937,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
905
937
|
const next = normalizeBrain({ ...st.brain, provider: meta.brain, model: meta.model ?? st.brain.model, effort: meta.effort ?? st.brain.effort, accountId: meta.accountId ?? st.brain.accountId, mcpProfile: meta.mcpProfile ?? st.brain.mcpProfile, project: meta.cwd ?? st.brain.project });
|
|
906
938
|
if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
|
|
907
939
|
}
|
|
908
|
-
if (meta.budget && typeof meta.budget === 'object')
|
|
940
|
+
if (meta.budget && typeof meta.budget === 'object') budgetController.receiveSession(meta.budget);
|
|
909
941
|
if (Number.isFinite(Number(meta.costUsd))) { st.costUsd = Number(meta.costUsd); renderCost(); }
|
|
910
942
|
if (meta.providerSessionId) st.providerSessionId = meta.providerSessionId;
|
|
911
943
|
if (meta.routing && typeof meta.routing === 'object') {
|
|
@@ -922,7 +954,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
922
954
|
if (typeof meta.features.computer === 'boolean') st.features.computer = meta.features.computer;
|
|
923
955
|
if (typeof meta.features.testDensity === 'boolean') st.features.testDensity = meta.features.testDensity;
|
|
924
956
|
if (typeof meta.features.rename === 'boolean') st.features.rename = meta.features.rename;
|
|
957
|
+
if (typeof meta.features.adminSandbox === 'boolean') st.features.adminSandbox = meta.features.adminSandbox;
|
|
925
958
|
}
|
|
959
|
+
if (typeof meta.adminSandbox === 'boolean') st.adminSandbox = meta.adminSandbox;
|
|
926
960
|
if ('testDensityExplicit' in meta) st.testDensity = normalizeTestDensity(meta.testDensityExplicit, null);
|
|
927
961
|
if (meta.testDensity) st.testDensityEffective = normalizeTestDensity(meta.testDensity);
|
|
928
962
|
if (typeof meta.computerUse === 'boolean') st.computerEffective = meta.computerUse;
|
|
@@ -1038,6 +1072,31 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1038
1072
|
if (!quiet) renderer.appendStatus(tt('assistant.status.testDensity', 'Test density: {level}', { level: testDensityLabel(next, t) }));
|
|
1039
1073
|
}
|
|
1040
1074
|
|
|
1075
|
+
/** The global Admin sandbox setting (true / false), or null while the server does not serve it. */
|
|
1076
|
+
function adminSandboxView() {
|
|
1077
|
+
if (st.features.adminSandbox !== true || typeof st.adminSandbox !== 'boolean') return null;
|
|
1078
|
+
return st.adminSandbox;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/** Every agent SynaBun starts follows it; every window hears the change (sync:agents:admin-sandbox-changed). */
|
|
1082
|
+
async function changeAdminSandbox(enabled) {
|
|
1083
|
+
const before = st.adminSandbox;
|
|
1084
|
+
st.adminSandbox = enabled === true;
|
|
1085
|
+
picker.render();
|
|
1086
|
+
try {
|
|
1087
|
+
const view = await setAdminSandbox(st.adminSandbox);
|
|
1088
|
+
if (typeof view?.adminSandbox === 'boolean') st.adminSandbox = view.adminSandbox;
|
|
1089
|
+
picker.render();
|
|
1090
|
+
renderer.appendStatus(st.adminSandbox
|
|
1091
|
+
? tt('assistant.adminSandbox.statusOn', 'Admin sandbox: on. On Windows, Codex may ask for administrator approval once per account to set up its sandbox.')
|
|
1092
|
+
: tt('assistant.adminSandbox.statusOff', 'Admin sandbox: off. No agent SynaBun starts asks for administrator approval for a sandbox.'));
|
|
1093
|
+
} catch (err) {
|
|
1094
|
+
st.adminSandbox = before;
|
|
1095
|
+
picker.render();
|
|
1096
|
+
toast(err?.message || String(err));
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1041
1100
|
/** Fresh tabs inherit the last-used test density (like the route mode). */
|
|
1042
1101
|
function applyLastUsedTestDensity() {
|
|
1043
1102
|
if (saved || st.features.testDensity !== true || st.testDensity != null) return;
|
|
@@ -1468,13 +1527,13 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1468
1527
|
if (meta.silent) return;
|
|
1469
1528
|
if (meta.requiresSwitch) {
|
|
1470
1529
|
const b = st.brain;
|
|
1471
|
-
sock.send({ type: 'switch_brain', brain: b.provider, model: b.model || undefined, effort: b.effort || undefined, accountId: b.accountId || undefined, mcpProfile: b.mcpProfile || undefined, cwd: b.project || undefined, agent: b.agent || undefined, permissionMode: b.permissionMode, planMode: b.planMode === true });
|
|
1530
|
+
if (!meta.restartBrain) sock.send({ type: 'switch_brain', brain: b.provider, model: b.model || undefined, effort: b.effort || undefined, accountId: b.accountId || undefined, mcpProfile: b.mcpProfile || undefined, cwd: b.project || undefined, agent: b.agent || undefined, permissionMode: b.permissionMode, planMode: b.planMode === true });
|
|
1472
1531
|
renderer.appendDivider(`${tt('assistant.status.brain', 'brain')} → ${brainLabel(b)}`);
|
|
1473
1532
|
renderer.forgetSeen(); // the new brain's ids start over
|
|
1474
1533
|
} else if (before.permissionMode !== st.brain.permissionMode || !!before.planMode !== !!st.brain.planMode) {
|
|
1475
1534
|
sock.send({ type: 'set_permission_mode', mode: st.brain.permissionMode, planMode: st.brain.planMode === true });
|
|
1476
1535
|
}
|
|
1477
|
-
patchAssistantSession(sessionId, { brain: st.brain }).catch((err) => {
|
|
1536
|
+
patchAssistantSession(sessionId, { brain: st.brain, ...(meta.restartBrain ? { restartBrain: true } : {}) }).catch((err) => {
|
|
1478
1537
|
// Refused (a model disabled in Assistant → Models): undo, so the refused
|
|
1479
1538
|
// brain is neither shown nor saved as the last used one — the next start
|
|
1480
1539
|
// would ask for it again. A brain switch already got the error on the socket.
|
|
@@ -1653,7 +1712,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1653
1712
|
items.push({ id: 'compact', label: tt('assistant.topbar.compact', 'Compact context'), onSelect: () => compact() });
|
|
1654
1713
|
if (routingAvailable()) items.push({ id: 'routes', label: tt('assistant.routes.editor', 'Model routes…'), icon: ICON_ROUTE, onSelect: () => openRoutes() });
|
|
1655
1714
|
items.push({ id: 'models', label: tt('assistant.models.menu', 'Models…'), onSelect: () => openModels() });
|
|
1656
|
-
items.push({ id: 'budget', label: tt('assistant.budget.menu', 'Budget…'), desc: st.budget
|
|
1715
|
+
items.push({ id: 'budget', label: tt('assistant.budget.menu', 'Budget…'), desc: budgetSpendText(st.budget, { t }), onSelect: () => openBudget() });
|
|
1657
1716
|
items.push({ id: 'clear', label: tt('assistant.topbar.clear', 'Clear transcript'), onSelect: () => clearTranscript() });
|
|
1658
1717
|
items.push({ id: 'help', label: tt('assistant.topbar.helpShortcuts', 'Help & shortcuts'), onSelect: () => openHelp() });
|
|
1659
1718
|
items.push({ kind: 'separator' });
|
|
@@ -1707,12 +1766,12 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1707
1766
|
}
|
|
1708
1767
|
}
|
|
1709
1768
|
|
|
1710
|
-
// Assistant → Budget:
|
|
1769
|
+
// Assistant → Budget: one saved amount plus the current session's live split.
|
|
1711
1770
|
function openBudget() {
|
|
1712
1771
|
openBudgetEditor({
|
|
1713
1772
|
t,
|
|
1714
1773
|
sessionId,
|
|
1715
|
-
|
|
1774
|
+
controller: budgetController,
|
|
1716
1775
|
onToast: toast,
|
|
1717
1776
|
onSaved: (res) => { if (res?.session) { st.budget = res.session; renderCost(); } },
|
|
1718
1777
|
}).catch(() => {});
|
|
@@ -1821,6 +1880,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1821
1880
|
|
|
1822
1881
|
// ── Bus ──
|
|
1823
1882
|
unsubs.push(on('sync:assistant:session-updated', (msg) => { const s = msg?.session || msg; if (s?.id === sessionId) applySessionMeta(s); }));
|
|
1883
|
+
unsubs.push(on('sync:assistant:budget', (msg) => { if ((msg?.assistantSessionId || msg?.sessionId) === sessionId) budgetController.receiveSession(msg.budget); }));
|
|
1884
|
+
unsubs.push(on('sync:assistant:budget-changed', () => budgetController.load()));
|
|
1824
1885
|
unsubs.push(on('sync:assistant:cost', (msg) => { if (!msg?.assistantSessionId || msg.assistantSessionId === sessionId) applyCost(msg); }));
|
|
1825
1886
|
unsubs.push(on('sync:assistant:mailbox', (msg) => { if (msg?.assistantSessionId === sessionId) renderer.appendMailbox(msg.items || msg.events || []); }));
|
|
1826
1887
|
unsubs.push(on('sync:assistant:permission-request', (msg) => {
|
|
@@ -1849,6 +1910,12 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1849
1910
|
st.routing = { ...(st.routing || {}), defaultMode: def, effectiveMode: st.routeMode || def };
|
|
1850
1911
|
routeChip.render();
|
|
1851
1912
|
}));
|
|
1913
|
+
// The global Admin sandbox setting changed in some window (this one included): every panel shows it.
|
|
1914
|
+
unsubs.push(on('sync:agents:admin-sandbox-changed', (msg) => {
|
|
1915
|
+
if (typeof msg?.adminSandbox !== 'boolean' || msg.adminSandbox === st.adminSandbox) return;
|
|
1916
|
+
st.adminSandbox = msg.adminSandbox;
|
|
1917
|
+
picker.render();
|
|
1918
|
+
}));
|
|
1852
1919
|
unsubs.push(on('session:info', () => updateGuest()));
|
|
1853
1920
|
unsubs.push(on('permissions:changed', () => updateGuest()));
|
|
1854
1921
|
|
|
@@ -1891,6 +1958,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1891
1958
|
sock.connect();
|
|
1892
1959
|
});
|
|
1893
1960
|
fetchUsage();
|
|
1961
|
+
budgetController.load();
|
|
1894
1962
|
|
|
1895
1963
|
const controller = {
|
|
1896
1964
|
sessionId,
|
|
@@ -1934,6 +2002,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1934
2002
|
if (persistTimer) { clearTimeout(persistTimer); persistTimer = null; }
|
|
1935
2003
|
clearTimeout(announceTimer);
|
|
1936
2004
|
if (replay) { clearTimeout(replay.timer); replay = null; }
|
|
2005
|
+
closeBudgetEditor(budgetController);
|
|
2006
|
+
emptyBudgetChip?.destroy();
|
|
2007
|
+
budgetController.destroy();
|
|
1937
2008
|
director.destroy();
|
|
1938
2009
|
usage.destroy();
|
|
1939
2010
|
clearTimeout(limitTimer);
|
|
@@ -228,6 +228,7 @@ export function updateRunCard(card, run, opts = {}) {
|
|
|
228
228
|
const r = run.route;
|
|
229
229
|
const routeBits = [r.taskClassLabel || r.taskClass || t('assistant.routes.task', 'task')];
|
|
230
230
|
if (r.status === 'pending') routeBits.push(t('assistant.routes.state.pending', 'waiting for your choice'));
|
|
231
|
+
else if (r.decidedBy === 'budget' || r.source === 'budget') routeBits.push(t('assistant.routes.budgetDecision', 'budget re-route'));
|
|
231
232
|
else if (r.decidedBy === 'user') routeBits.push(t('assistant.routes.youChose', 'you chose'));
|
|
232
233
|
else if (r.remembered || r.decidedBy === 'remembered') routeBits.push(t('assistant.routes.remembered', 'your saved choice'));
|
|
233
234
|
else if (Number.isFinite(Number(r.confidence))) routeBits.push(`${Math.round(Number(r.confidence) <= 1 ? Number(r.confidence) * 100 : Number(r.confidence))}%`);
|
|
@@ -548,8 +549,10 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
548
549
|
|
|
549
550
|
/** A menu opened from the empty state's project picker does not outlive it. */
|
|
550
551
|
function closeEmptyMenu() {
|
|
551
|
-
const btn
|
|
552
|
-
|
|
552
|
+
for (const btn of emptyEl?.querySelectorAll('.asst-empty-project-btn') || []) {
|
|
553
|
+
if (isMenuOpen(btn)) closeMenu();
|
|
554
|
+
}
|
|
555
|
+
hooks.unmountEmptyControls?.();
|
|
553
556
|
}
|
|
554
557
|
|
|
555
558
|
// ── The hero ──
|
|
@@ -2689,7 +2692,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2689
2692
|
|
|
2690
2693
|
function upsertRunCard(run, { result = null } = {}) {
|
|
2691
2694
|
if (!run?.runId) return null;
|
|
2692
|
-
|
|
2695
|
+
const prev = runInfo.get(run.runId);
|
|
2696
|
+
const status = run.status || prev?.state;
|
|
2697
|
+
const tone = status ? runTone({ ...run, status }) : '';
|
|
2698
|
+
const routeId = run.route?.routeId || prev?.routeId || '';
|
|
2699
|
+
runInfo.set(run.runId, { ...prev, title: run.title || run.task || prev?.title, state: status, provider: run.provider, routeId, working: tone === 'running' || tone === 'queued' });
|
|
2700
|
+
if (routeId) syncRouteLive(routeId);
|
|
2693
2701
|
const cardOpts = runCardOpts(result);
|
|
2694
2702
|
let card = runCards.get(run.runId);
|
|
2695
2703
|
if (card && card.isConnected) {
|
|
@@ -2745,21 +2753,42 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2745
2753
|
return { phase, t: hooks.t, providerIcon: hooks.providerIcon, brainLabel: hooks.brainLabel, onChangeRoutes: hooks.onChangeRoutes };
|
|
2746
2754
|
}
|
|
2747
2755
|
|
|
2748
|
-
/** Create or update the line for `route.routeId` in place. */
|
|
2756
|
+
/** Create or update the line for `route.routeId` in place. Only a new line enters (never on replay). */
|
|
2749
2757
|
function upsertRouteLine(route, phase = 'decided') {
|
|
2750
2758
|
const routeId = route?.routeId;
|
|
2751
2759
|
if (!routeId) return null;
|
|
2752
2760
|
const existing = routeLines.get(routeId);
|
|
2753
|
-
if (existing && existing.el.isConnected) { existing.update(route, phase); return existing; }
|
|
2761
|
+
if (existing && existing.el.isConnected) { existing.update(route, phase); syncRouteLive(routeId); return existing; }
|
|
2754
2762
|
const line = createRouteLine(route, routeLineOpts(phase));
|
|
2755
2763
|
routeLines.set(routeId, line);
|
|
2756
2764
|
append(line.el);
|
|
2765
|
+
syncRouteLive(routeId);
|
|
2766
|
+
enterRow(line.el);
|
|
2757
2767
|
return line;
|
|
2758
2768
|
}
|
|
2759
2769
|
|
|
2760
2770
|
/** Adopt a line created by a collapsing route card so later events update it. */
|
|
2761
2771
|
function registerRouteLine(routeId, line) {
|
|
2762
|
-
if (routeId
|
|
2772
|
+
if (!routeId || !line) return;
|
|
2773
|
+
const fresh = routeLines.get(routeId) !== line;
|
|
2774
|
+
routeLines.set(routeId, line);
|
|
2775
|
+
syncRouteLive(routeId);
|
|
2776
|
+
if (fresh && line.el?.isConnected) enterRow(line.el);
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
/** The line is live while a run it dispatched is queued or working (run cards keep runInfo current). */
|
|
2780
|
+
function syncRouteLive(routeId) {
|
|
2781
|
+
const line = routeLines.get(routeId);
|
|
2782
|
+
if (!line?.setRunning) return;
|
|
2783
|
+
let working = false;
|
|
2784
|
+
for (const info of runInfo.values()) if (info.routeId === routeId && info.working) { working = true; break; }
|
|
2785
|
+
line.setRunning(working);
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
/** A new row's entrance: the rack ledger's (4px rise and fade, --asst-dur-fast, --asst-ease-enter); none when calm. */
|
|
2789
|
+
function enterRow(node) {
|
|
2790
|
+
if (calm() || typeof node?.animate !== 'function') return;
|
|
2791
|
+
node.animate([{ opacity: 0, transform: 'translateY(4px)' }, { opacity: 1, transform: 'none' }], { duration: 140, easing: cssEase('--asst-ease-enter', 'cubic-bezier(0,0,.38,.9)') });
|
|
2763
2792
|
}
|
|
2764
2793
|
|
|
2765
2794
|
function removeRouteLine(routeId) {
|
|
@@ -2899,7 +2928,9 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2899
2928
|
const projectSlot = emptyEl.querySelector('.asst-empty-project');
|
|
2900
2929
|
let picker = null;
|
|
2901
2930
|
try { picker = typeof hooks.mountEmptyProject === 'function' ? hooks.mountEmptyProject(projectSlot) : null; } catch { picker = null; }
|
|
2902
|
-
|
|
2931
|
+
let budget = null;
|
|
2932
|
+
try { budget = hooks.mountEmptyBudget?.(projectSlot); } catch { /* unavailable */ }
|
|
2933
|
+
projectSlot.hidden = !picker && !budget;
|
|
2903
2934
|
renderSuggestions();
|
|
2904
2935
|
$msgs.appendChild(emptyEl);
|
|
2905
2936
|
// The host's "Starting…" character flies into place (not on replay or reduced motion).
|
|
@@ -22,6 +22,8 @@ import { catalogModelRow, fmtModelPrice, hiddenModelFor, ICON_CARET, invalidateM
|
|
|
22
22
|
// The Routes editor's model lists (where they come from, how they stay current): asst-route-models.js.
|
|
23
23
|
import { closeMenu, isMenuOpen, openMenu } from './asst-menu.js';
|
|
24
24
|
import { decorateControlHead } from './asst-control.js';
|
|
25
|
+
import { accountSummary } from '../account-view.js';
|
|
26
|
+
import { accountUsageText, autoAccountLabel } from '../account-usage.js';
|
|
25
27
|
import {
|
|
26
28
|
DEFAULT_ROUTE_MODE,
|
|
27
29
|
fmtConfidence,
|
|
@@ -39,6 +41,18 @@ function obj(v) { return v && typeof v === 'object' && !Array.isArray(v) ? v : n
|
|
|
39
41
|
function arr(v) { return Array.isArray(v) ? v : []; }
|
|
40
42
|
function firstStr(...values) { for (const v of values) { const s = str(v); if (s) return s; } return ''; }
|
|
41
43
|
|
|
44
|
+
export function routeDisplayAccount(target, t = null) {
|
|
45
|
+
if (target?.accountId === 'auto') return autoAccountLabel(t);
|
|
46
|
+
if (target?.account) return [accountSummary(target.account, target.provider === 'codex' ? 'codex' : 'claude'), accountUsageText(target.account, t)].join(' · ');
|
|
47
|
+
return target?.accountId ? tf(t)('assistant.routes.account.removed', '{id} · account removed', { id: target.accountId }) : '';
|
|
48
|
+
}
|
|
49
|
+
function routeAccountError(error, t) {
|
|
50
|
+
if (error?.code === 'ACCOUNT_NO_SUBSCRIPTION') return tf(t)('assistant.accountUsage.noSubscription', 'Auto requires a signed-in subscription account.');
|
|
51
|
+
return error?.code === 'ACCOUNT_NOT_FOUND'
|
|
52
|
+
? tf(t)('assistant.routes.account.removedHint', 'Account removed. Save another account choice before running this route.')
|
|
53
|
+
: tf(t)('assistant.routes.account.signedOut', 'Signed out. Sign in from the provider account menu before running this route.');
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
/** Local translator: hooks.t when it resolves, else the fallback with {param} interpolation. */
|
|
43
57
|
function tf(t) {
|
|
44
58
|
return (key, fallback, params) => {
|
|
@@ -93,6 +107,7 @@ function normalizeRouteOption(o) {
|
|
|
93
107
|
provider: normalizeProvider(src.provider, str(src.provider) || null),
|
|
94
108
|
model: str(src.model),
|
|
95
109
|
effort: str(src.effort) || null,
|
|
110
|
+
...(Object.hasOwn(src, 'accountId') ? { accountId: src.accountId, account: obj(src.account), accountError: obj(src.accountError) } : {}),
|
|
96
111
|
label: str(src.label),
|
|
97
112
|
badge: str(src.badge) || null,
|
|
98
113
|
tier: str(src.tier) || null,
|
|
@@ -101,6 +116,7 @@ function normalizeRouteOption(o) {
|
|
|
101
116
|
contextWindow: num(src.contextWindow),
|
|
102
117
|
reason: str(src.reason),
|
|
103
118
|
warning: str(src.warning) || null,
|
|
119
|
+
budget: str(src.budget) || null,
|
|
104
120
|
disabled: src.disabled === true,
|
|
105
121
|
disabledReason: str(src.disabledReason) || null,
|
|
106
122
|
};
|
|
@@ -174,7 +190,7 @@ export function sameRouteTarget(a, b) {
|
|
|
174
190
|
return (str(a.kind) || 'direct') === (str(b.kind) || 'direct')
|
|
175
191
|
&& str(a.provider) === str(b.provider)
|
|
176
192
|
&& modelKey(a.model) === modelKey(b.model)
|
|
177
|
-
&& (str(a.effort) || '') === (str(b.effort) || '');
|
|
193
|
+
&& (str(a.effort) || '') === (str(b.effort) || '') && str(a.accountId) === str(b.accountId);
|
|
178
194
|
}
|
|
179
195
|
|
|
180
196
|
const BADGE_RANK = { suggested: 0, remembered: 1, current: 2, capable: 3, cheaper: 4, stronger: 5 };
|
|
@@ -269,6 +285,7 @@ export function routeOptions(n, { brainLabel = '', t = null } = {}) {
|
|
|
269
285
|
// "here" is the brain's own model: prefer its display label over a raw id.
|
|
270
286
|
title: isHere ? tr('assistant.routes.doHere', 'Do it here with {model}', { model: str(o.label) || hereModel }) : (label || o.id),
|
|
271
287
|
badgeText: badgeText(o, n, tr),
|
|
288
|
+
disabledReason: o.budget === 'share_used_up' ? routeBudgetReason({ provider: o.provider }, t) : o.disabledReason,
|
|
272
289
|
selected: o.id === defaultId,
|
|
273
290
|
};
|
|
274
291
|
});
|
|
@@ -350,6 +367,16 @@ export function routeCatalogRow(pref, catalog) {
|
|
|
350
367
|
return pref.model ? catalogModelRow(pref.provider, pref.model, catalog) || aliasRow(pref.provider, pref.model, rows) : rows.find(m => m.isDefault) || null;
|
|
351
368
|
}
|
|
352
369
|
|
|
370
|
+
/** Budget messages are UI copy; never show the server's English disabledReason. */
|
|
371
|
+
export function routeBudgetReason({ provider, dry } = {}, t) {
|
|
372
|
+
const tr = tf(t), names = { 'claude-code': 'Claude Code', codex: 'Codex', opencode: 'OpenCode' };
|
|
373
|
+
if (provider) return tr('assistant.routes.budgetProvider', "Budget: the {provider} share of this session's budget is used up.", { provider: names[provider] || provider });
|
|
374
|
+
const list = [...new Set(arr(dry))].map(id => names[id] || id);
|
|
375
|
+
if (!list.length) return tr('assistant.routes.budgetSpent', "Budget: this session's budget is spent.");
|
|
376
|
+
return tr(list.length === 1 ? 'assistant.routes.budgetDryOne' : 'assistant.routes.budgetDryMany',
|
|
377
|
+
list.length === 1 ? 'Budget: the {list} share is used up.' : 'Budget: the {list} shares are used up.', { list: list.join(', ') });
|
|
378
|
+
}
|
|
379
|
+
|
|
353
380
|
/** Text parts of a route line (pure). */
|
|
354
381
|
export function routeLineParts(route, phase = 'decided', { brainLabel = '', t = null } = {}) {
|
|
355
382
|
const tr = tf(t);
|
|
@@ -366,10 +393,12 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
366
393
|
else if (phase === 'declined' && str(r.reasonCode) === 'no_capable_model') {
|
|
367
394
|
state = str(r.needs) === 'vision' ? tr('assistant.routes.reason.noSeeingModel', 'no model can see images') : tr('assistant.routes.reason.noCapableModel', 'no model can make this');
|
|
368
395
|
}
|
|
396
|
+
else if (str(r.reasonCode) === 'budget_exhausted') state = routeBudgetReason({ dry: r.dry }, t);
|
|
369
397
|
else if (phase === 'declined') state = tr('assistant.routes.state.declined', 'declined');
|
|
370
398
|
else if (phase === 'cancelled') state = tr('assistant.routes.state.cancelled', 'cancelled');
|
|
371
399
|
let decided = '';
|
|
372
|
-
switch (str(r.decidedBy)) {
|
|
400
|
+
switch (r.source === 'budget' ? 'budget' : str(r.decidedBy)) {
|
|
401
|
+
case 'budget': decided = tr('assistant.routes.budgetDecision', 'budget re-route'); break;
|
|
373
402
|
case 'brain': decided = tr('assistant.routes.decidedBy', 'decided by {model}', { model: brainLabel || tr('assistant.routes.theBrainLower', 'the assistant') }); break;
|
|
374
403
|
case 'user': decided = tr('assistant.routes.youChose', 'you chose'); break;
|
|
375
404
|
case 'remembered': decided = tr('assistant.routes.remembered', 'your saved choice'); break;
|
|
@@ -377,6 +406,8 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
377
406
|
default: decided = r.remembered ? tr('assistant.routes.remembered', 'your saved choice') : '';
|
|
378
407
|
}
|
|
379
408
|
const meta = [];
|
|
409
|
+
const accountLabel = routeDisplayAccount(target, t);
|
|
410
|
+
if (accountLabel) meta.push(accountLabel);
|
|
380
411
|
if (effort && effort !== 'off') meta.push(effort);
|
|
381
412
|
if (confidence) meta.push(confidence);
|
|
382
413
|
return {
|
|
@@ -390,7 +421,7 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
390
421
|
muted: phase === 'expired' || phase === 'declined' || phase === 'cancelled',
|
|
391
422
|
pending: phase === 'pending' || phase === 'card',
|
|
392
423
|
decidedText: decided,
|
|
393
|
-
reason: str(r.reason),
|
|
424
|
+
reason: target?.accountError ? routeAccountError(target.accountError, t) : str(r.reason),
|
|
394
425
|
alternatives: arr(r.alternatives).map(a => routeTargetLabel(a, { effort: true })).filter(Boolean),
|
|
395
426
|
corrections: arr(r.corrections).map(c => correctionText(c, tr)).filter(Boolean),
|
|
396
427
|
};
|
|
@@ -400,9 +431,15 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
400
431
|
function correctionText(c, tr) {
|
|
401
432
|
if (typeof c === 'string') return c;
|
|
402
433
|
if (!c || typeof c !== 'object') return '';
|
|
434
|
+
if (['account_auto', 'account_limited', 'accounts_all_limited'].includes(c.reason) && c.usage) {
|
|
435
|
+
const key = c.reason === 'account_auto' ? 'autoPicked' : c.reason === 'account_limited' ? 'fallbackPicked' : 'allLimitedPicked';
|
|
436
|
+
const fallbacks = { autoPicked: 'Auto chose {to}: {usage}', fallbackPicked: '{from} out of usage → {to}: {usage}', allLimitedPicked: 'All subscriptions limited; soonest reset → {to}: {usage}' };
|
|
437
|
+
const text = tr(`assistant.accountUsage.${key}`, fallbacks[key], { from: str(c.from), to: str(c.to), usage: accountUsageText({ mode: 'subscription', usage: c.usage }, (key, params) => tr(key, key, params)) });
|
|
438
|
+
return text + (c.workerForAccount ? ' · ' + tr('assistant.accountUsage.directWorkerHint', 'Runs on a worker because this conversation belongs to another account.') : '');
|
|
439
|
+
}
|
|
403
440
|
if (c.reason === 'remembered_route') return tr('assistant.routes.correction.remembered', 'your saved route {to} replaced {from}', { to: str(c.to), from: str(c.from) });
|
|
404
441
|
if (c.reason === 'remembered_disabled') return tr('assistant.routes.correction.rememberedDisabled', 'saved route {from} is disabled, kept the pick', { from: str(c.from) });
|
|
405
|
-
const text = firstStr(c.text, c.summary);
|
|
442
|
+
const text = firstStr(c.text, c.summary, c.message);
|
|
406
443
|
if (text) return text;
|
|
407
444
|
if (c.field && (c.from || c.to)) return `${c.field}: ${str(c.from) || '—'} → ${str(c.to) || 'default'}`;
|
|
408
445
|
return str(c.field);
|
|
@@ -416,6 +453,14 @@ export function routeLineText(route, phase, opts) {
|
|
|
416
453
|
return [`⇄ ${p.taskClass} → ${head}`, ...p.meta].join(' · ');
|
|
417
454
|
}
|
|
418
455
|
|
|
456
|
+
/** Everything the one-line row can cut short (meta, state included): its tooltip. */
|
|
457
|
+
export function routeLineTitle(route, phase, opts) {
|
|
458
|
+
const p = routeLineParts(route, phase, opts);
|
|
459
|
+
const tail = [p.kindLabel, p.targetLabel, ...p.meta, p.stateText].filter(Boolean).join(' · ');
|
|
460
|
+
if (!tail) return `⇄ ${p.taskClass}`;
|
|
461
|
+
return `⇄ ${p.taskClass} ${p.kindLabel || p.targetLabel ? '→' : '·'} ${tail}`;
|
|
462
|
+
}
|
|
463
|
+
|
|
419
464
|
// ═══════════════════════════════════════════
|
|
420
465
|
// DOM
|
|
421
466
|
// ═══════════════════════════════════════════
|
|
@@ -449,8 +494,12 @@ function providerColor(provider) {
|
|
|
449
494
|
let _lineUid = 0;
|
|
450
495
|
|
|
451
496
|
/**
|
|
452
|
-
* The compact route line
|
|
453
|
-
*
|
|
497
|
+
* The compact route line, always one line: the glyph, arrow, kind and mark
|
|
498
|
+
* hold; the rest (model · account · usage, or the state) gives way first,
|
|
499
|
+
* then the class; the title has it all. data-live: "waiting" while it waits
|
|
500
|
+
* for a choice, "working" while a run it dispatched is (setRunning).
|
|
501
|
+
* opts: { phase, t, providerIcon, brainLabel() , onChangeRoutes() }
|
|
502
|
+
* Returns { el, routeId, update(route, phase), setRunning(on) }.
|
|
454
503
|
*/
|
|
455
504
|
export function createRouteLine(route, opts = {}) {
|
|
456
505
|
const tr = tf(opts.t);
|
|
@@ -458,20 +507,19 @@ export function createRouteLine(route, opts = {}) {
|
|
|
458
507
|
const detailId = `asst-route-detail-${++_lineUid}`;
|
|
459
508
|
node.innerHTML = `
|
|
460
509
|
<button type="button" class="asst-route-line-head" aria-expanded="false" aria-controls="${detailId}">
|
|
461
|
-
<span class="asst-route-glyph" aria-hidden="true"
|
|
510
|
+
<span class="asst-route-glyph asst-icon" aria-hidden="true">${ICON_ROUTE}</span>
|
|
462
511
|
<span class="asst-route-class"></span>
|
|
463
512
|
<span class="asst-route-arrow" aria-hidden="true">→</span>
|
|
464
513
|
<span class="asst-route-kind"></span>
|
|
465
514
|
<span class="asst-route-icon asst-icon" aria-hidden="true"></span>
|
|
466
|
-
<span class="asst-route-target"></span>
|
|
467
|
-
<span class="asst-route-meta"></span>
|
|
468
|
-
<span class="asst-route-state"></span>
|
|
515
|
+
<span class="asst-route-rest"><span class="asst-route-target"></span> <span class="asst-route-meta"></span> <span class="asst-route-state"></span></span>
|
|
469
516
|
<span class="asst-route-chev" aria-hidden="true">›</span>
|
|
517
|
+
<span class="asst-route-sweep" aria-hidden="true"></span>
|
|
470
518
|
</button>
|
|
471
519
|
<div class="asst-route-detail" id="${detailId}" hidden></div>`;
|
|
472
520
|
const head = node.querySelector('.asst-route-line-head');
|
|
473
521
|
const detail = node.querySelector('.asst-route-detail');
|
|
474
|
-
let current = { route: obj(route) || {}, phase: opts.phase || 'decided' };
|
|
522
|
+
let current = { route: obj(route) || {}, phase: opts.phase || 'decided', running: false };
|
|
475
523
|
|
|
476
524
|
head.addEventListener('click', () => {
|
|
477
525
|
const open = head.getAttribute('aria-expanded') !== 'true';
|
|
@@ -487,6 +535,8 @@ export function createRouteLine(route, opts = {}) {
|
|
|
487
535
|
node.dataset.phase = current.phase;
|
|
488
536
|
node.classList.toggle('muted', p.muted);
|
|
489
537
|
node.classList.toggle('pending', p.pending);
|
|
538
|
+
const live = p.pending ? 'waiting' : current.running && !p.muted && !p.stateText ? 'working' : '';
|
|
539
|
+
if (live) node.dataset.live = live; else delete node.dataset.live;
|
|
490
540
|
node.querySelector('.asst-route-class').textContent = p.taskClass;
|
|
491
541
|
const arrow = node.querySelector('.asst-route-arrow');
|
|
492
542
|
const kindEl = node.querySelector('.asst-route-kind');
|
|
@@ -494,7 +544,7 @@ export function createRouteLine(route, opts = {}) {
|
|
|
494
544
|
const targetEl = node.querySelector('.asst-route-target');
|
|
495
545
|
const metaEl = node.querySelector('.asst-route-meta');
|
|
496
546
|
const stateEl = node.querySelector('.asst-route-state');
|
|
497
|
-
arrow.hidden = !p.targetLabel && !p.pending;
|
|
547
|
+
arrow.hidden = !p.targetLabel && !p.pending && !p.kindLabel;
|
|
498
548
|
kindEl.textContent = p.kindLabel;
|
|
499
549
|
kindEl.hidden = !p.kindLabel;
|
|
500
550
|
iconEl.innerHTML = p.targetLabel ? providerIconHtml(p.provider, opts) : '';
|
|
@@ -506,7 +556,9 @@ export function createRouteLine(route, opts = {}) {
|
|
|
506
556
|
metaEl.hidden = !p.meta.length;
|
|
507
557
|
stateEl.textContent = p.stateText;
|
|
508
558
|
stateEl.hidden = !p.stateText;
|
|
559
|
+
node.querySelector('.asst-route-rest').hidden = targetEl.hidden && metaEl.hidden && stateEl.hidden;
|
|
509
560
|
head.setAttribute('aria-label', routeLineText(current.route, current.phase, { brainLabel, t: opts.t }));
|
|
561
|
+
head.title = routeLineTitle(current.route, current.phase, { brainLabel, t: opts.t });
|
|
510
562
|
|
|
511
563
|
detail.innerHTML = '';
|
|
512
564
|
const rows = [];
|
|
@@ -535,6 +587,12 @@ export function createRouteLine(route, opts = {}) {
|
|
|
535
587
|
if (phase) current.phase = phase;
|
|
536
588
|
render();
|
|
537
589
|
},
|
|
590
|
+
/** A run this route dispatched is queued or working (the renderer knows from its run cards). */
|
|
591
|
+
setRunning(on) {
|
|
592
|
+
if (current.running === !!on) return;
|
|
593
|
+
current.running = !!on;
|
|
594
|
+
render();
|
|
595
|
+
},
|
|
538
596
|
};
|
|
539
597
|
}
|
|
540
598
|
|
|
@@ -705,7 +763,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
705
763
|
row.setAttribute('aria-disabled', o.disabled ? 'true' : 'false');
|
|
706
764
|
row.tabIndex = -1;
|
|
707
765
|
const kindWord = o.kind && o.kind !== 'direct' ? routeKindLabel(o.kind) : '';
|
|
708
|
-
const sub = [kindWord, o.effort && o.effort !== 'off' ? o.effort : '', o.price ? priceText(o.price) : '', o.vision ? tr('assistant.brain.vision', 'vision') : '', o.reason].filter(Boolean).join(' · ');
|
|
766
|
+
const sub = [kindWord, routeDisplayAccount(o, hooks.t), o.effort && o.effort !== 'off' ? o.effort : '', o.price ? priceText(o.price) : '', o.vision ? tr('assistant.brain.vision', 'vision') : '', o.reason].filter(Boolean).join(' · ');
|
|
709
767
|
row.innerHTML = `
|
|
710
768
|
<span class="asst-route-num" aria-hidden="true">${esc(o.number)}</span>
|
|
711
769
|
<span class="asst-route-opt-icon asst-icon" aria-hidden="true"${o.provider ? ` style="color:${esc(providerColor(o.provider))}"` : ''}>${providerIconHtml(o.provider, hooks)}</span>
|
|
@@ -718,7 +776,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
718
776
|
row.querySelector('.asst-route-opt-title').textContent = o.title;
|
|
719
777
|
if (sub) row.querySelector('.asst-route-opt-sub').textContent = sub;
|
|
720
778
|
const warn = row.querySelector('.asst-route-opt-warn');
|
|
721
|
-
if (warn) warn.textContent = o.disabled && o.disabledReason ? o.disabledReason : o.warning;
|
|
779
|
+
if (warn) warn.textContent = o.accountError ? routeAccountError(o.accountError, hooks.t) : o.disabled && o.disabledReason ? o.disabledReason : o.warning;
|
|
722
780
|
if (o.badgeText) row.querySelector('.asst-route-badge').textContent = o.badgeText;
|
|
723
781
|
row.addEventListener('click', () => { if (!o.disabled) select(o.id, { focus: true }); });
|
|
724
782
|
return row;
|