@songsid/agend 2.2.0-beta.1 → 2.2.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-TW.md +1 -1
- package/dist/agent-cli-instructions.md +16 -9
- package/dist/agent-cli.js +15 -7
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +21 -9
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +47 -0
- package/dist/agent-whats-new.js +147 -0
- package/dist/agent-whats-new.js.map +1 -0
- package/dist/auth-api.d.ts +2 -2
- package/dist/auth-api.js +4 -2
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/antigravity.d.ts +3 -0
- package/dist/backend/antigravity.js +9 -0
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-carry.d.ts +22 -0
- package/dist/backend/claude-carry.js +170 -0
- package/dist/backend/claude-carry.js.map +1 -0
- package/dist/backend/claude-code.d.ts +65 -2
- package/dist/backend/claude-code.js +226 -33
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-metadata.d.ts +33 -0
- package/dist/backend/codex-metadata.js +122 -18
- package/dist/backend/codex-metadata.js.map +1 -1
- package/dist/backend/codex.d.ts +3 -0
- package/dist/backend/codex.js +17 -1
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/credential-profile.d.ts +40 -3
- package/dist/backend/credential-profile.js +113 -5
- package/dist/backend/credential-profile.js.map +1 -1
- package/dist/backend/effort-metadata.d.ts +1 -1
- package/dist/backend/effort-metadata.js +3 -2
- package/dist/backend/effort-metadata.js.map +1 -1
- package/dist/backend/grok.d.ts +4 -0
- package/dist/backend/grok.js +14 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro-engine-ledger.d.ts +5 -0
- package/dist/backend/kiro-engine-ledger.js +20 -0
- package/dist/backend/kiro-engine-ledger.js.map +1 -1
- package/dist/backend/kiro-identity.d.ts +6 -0
- package/dist/backend/kiro-identity.js +19 -5
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro.d.ts +39 -3
- package/dist/backend/kiro.js +143 -19
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/launch-effort.d.ts +33 -0
- package/dist/backend/launch-effort.js +123 -0
- package/dist/backend/launch-effort.js.map +1 -0
- package/dist/backend/muse.d.ts +84 -0
- package/dist/backend/muse.js +449 -53
- package/dist/backend/muse.js.map +1 -1
- package/dist/backend/types.d.ts +33 -4
- package/dist/backend/types.js +2 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/backend-command.d.ts +91 -0
- package/dist/backend-command.js +122 -0
- package/dist/backend-command.js.map +1 -0
- package/dist/background-work-ended.d.ts +18 -0
- package/dist/background-work-ended.js +30 -0
- package/dist/background-work-ended.js.map +1 -0
- package/dist/cache-service.d.ts +2 -0
- package/dist/cache-service.js +2 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +24 -1
- package/dist/channel/adapters/discord.js +87 -2
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +24 -2
- package/dist/channel/adapters/telegram.js +118 -20
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-server.js +9 -1
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +11 -0
- package/dist/channel/mcp-tools.js +24 -13
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-edit-errors.d.ts +5 -0
- package/dist/channel/message-edit-errors.js +24 -0
- package/dist/channel/message-edit-errors.js.map +1 -0
- package/dist/channel/telegram-markdown.d.ts +6 -0
- package/dist/channel/telegram-markdown.js +104 -0
- package/dist/channel/telegram-markdown.js.map +1 -0
- package/dist/channel/tool-router.js +9 -3
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/channel/types.d.ts +39 -3
- package/dist/cli.js +133 -28
- package/dist/cli.js.map +1 -1
- package/dist/command-table.d.ts +2 -2
- package/dist/command-table.js +6 -3
- package/dist/command-table.js.map +1 -1
- package/dist/config-validator.js +59 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +15 -2
- package/dist/cross-instance-envelope.js +29 -5
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +156 -1
- package/dist/daemon.js +764 -89
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +62 -7
- package/dist/delivery-outbox.js +309 -22
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +18 -1
- package/dist/delivery-reconciliation.js +67 -2
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/fleet-context.d.ts +10 -1
- package/dist/fleet-manager.d.ts +345 -10
- package/dist/fleet-manager.js +1868 -174
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/backend-providers/SKILL.md +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +92 -21
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +27 -3
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +8 -8
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -5
- package/dist/general-knowledge/skills/fleet-health/SKILL.md +4 -4
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +13 -13
- package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -2
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +6 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +9 -9
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +8 -2
- package/dist/general-knowledge/skills/scheduling/SKILL.md +5 -3
- package/dist/general-knowledge/skills/session-management/SKILL.md +6 -5
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +2 -2
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +8 -3
- package/dist/general-knowledge/steering/core-rules.md +8 -7
- package/dist/host-memory.d.ts +10 -0
- package/dist/host-memory.js +33 -1
- package/dist/host-memory.js.map +1 -1
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/instance-avatar.d.ts +36 -0
- package/dist/instance-avatar.js +57 -0
- package/dist/instance-avatar.js.map +1 -0
- package/dist/instance-config-impact.js +6 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +29 -6
- package/dist/instance-lifecycle.js +79 -28
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +3 -1
- package/dist/instructions.js +11 -8
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-log-gate.d.ts +42 -0
- package/dist/kiro-log-gate.js +39 -0
- package/dist/kiro-log-gate.js.map +1 -0
- package/dist/locale.js +200 -44
- package/dist/locale.js.map +1 -1
- package/dist/log-service.d.ts +99 -0
- package/dist/log-service.js +243 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +169 -0
- package/dist/log-steps.js +858 -0
- package/dist/log-steps.js.map +1 -0
- package/dist/login-controller.d.ts +20 -3
- package/dist/login-controller.js +71 -19
- package/dist/login-controller.js.map +1 -1
- package/dist/login-flows.d.ts +5 -2
- package/dist/login-flows.js +5 -4
- package/dist/login-flows.js.map +1 -1
- package/dist/memory-pressure.d.ts +13 -1
- package/dist/memory-pressure.js +50 -10
- package/dist/memory-pressure.js.map +1 -1
- package/dist/needs-you-hub.js +2 -0
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you-live.d.ts +6 -0
- package/dist/needs-you-live.js +23 -3
- package/dist/needs-you-live.js.map +1 -1
- package/dist/next-steps.js +1 -1
- package/dist/next-steps.js.map +1 -1
- package/dist/outbound-handlers.d.ts +100 -0
- package/dist/outbound-handlers.js +263 -96
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +26 -15
- package/dist/outbound-schemas.js +24 -7
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +101 -0
- package/dist/public-preview.js +236 -0
- package/dist/public-preview.js.map +1 -0
- package/dist/public-web-gateway.js +11 -4
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +24 -1
- package/dist/public-web-link.js +63 -9
- package/dist/public-web-link.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +3 -0
- package/dist/reply-buttons-controller.js +3 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/restart-guard.d.ts +6 -3
- package/dist/restart-guard.js +6 -3
- package/dist/restart-guard.js.map +1 -1
- package/dist/restart-progress.d.ts +33 -0
- package/dist/restart-progress.js +30 -0
- package/dist/restart-progress.js.map +1 -1
- package/dist/service-activation.d.ts +15 -7
- package/dist/service-activation.js +46 -29
- package/dist/service-activation.js.map +1 -1
- package/dist/service-installer.d.ts +31 -0
- package/dist/service-installer.js +41 -2
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.js +85 -25
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +72 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +28 -4
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +7 -0
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/setup-form.d.ts +1 -1
- package/dist/setup-form.js +1 -1
- package/dist/spawn-gate.js +5 -1
- package/dist/spawn-gate.js.map +1 -1
- package/dist/spawn-hold-reply.d.ts +12 -0
- package/dist/spawn-hold-reply.js +39 -0
- package/dist/spawn-hold-reply.js.map +1 -0
- package/dist/status-emojis.d.ts +10 -3
- package/dist/status-emojis.js +11 -2
- package/dist/status-emojis.js.map +1 -1
- package/dist/systemd-peer-bus.d.ts +95 -0
- package/dist/systemd-peer-bus.js +430 -0
- package/dist/systemd-peer-bus.js.map +1 -0
- package/dist/systemd-runtime.d.ts +27 -1
- package/dist/systemd-runtime.js +69 -7
- package/dist/systemd-runtime.js.map +1 -1
- package/dist/tips.js +4 -4
- package/dist/tips.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.js.map +1 -1
- package/dist/topic-commands.d.ts +42 -2
- package/dist/topic-commands.js +92 -7
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.d.ts +3 -3
- package/dist/transcript-delta-reader.js +8 -8
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-jsonl.d.ts +80 -3
- package/dist/transcript-jsonl.js +147 -10
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +19 -0
- package/dist/transcript-monitor.js +87 -15
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +78 -0
- package/dist/transcript-sources.js +375 -64
- package/dist/transcript-sources.js.map +1 -1
- package/dist/transcript-turns.d.ts +15 -2
- package/dist/transcript-turns.js +15 -2
- package/dist/transcript-turns.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/purpose-lane.d.ts +5 -1
- package/dist/tunnel/purpose-lane.js +14 -7
- package/dist/tunnel/purpose-lane.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/turn-resume.d.ts +2 -0
- package/dist/turn-resume.js +1 -1
- package/dist/turn-resume.js.map +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/ui/chat-commands.js +1 -0
- package/dist/ui/chat-emojis.js +33 -0
- package/dist/ui/chat-mentions.js +63 -0
- package/dist/ui/chat-render.js +42 -2
- package/dist/ui/chat-store.js +2 -2
- package/dist/ui/chat-strings.js +38 -20
- package/dist/ui/chat-thread.js +69 -12
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/panel-chat.js +39 -10
- package/dist/ui/panel-details.js +2 -0
- package/dist/ui/panel-fleet.js +199 -57
- package/dist/ui/panel-log.js +428 -0
- package/dist/ui/panel-needs.js +6 -2
- package/dist/ui/panel-settings.js +137 -32
- package/dist/ui/preview-panel.js +55 -15
- package/dist/ui/preview-status.js +28 -0
- package/dist/ui/preview.js +44 -3
- package/dist/ui/settings-dialogs.js +7 -2
- package/dist/ui/settings-model.js +2 -1
- package/dist/ui/settings-strings.js +46 -4
- package/dist/ui/settings-token.js +1 -1
- package/dist/ui/shared/app-i18n.js +43 -17
- package/dist/ui/shared/app-prefs.js +250 -0
- package/dist/ui/shared/app-route.js +7 -2
- package/dist/ui/shared/app-shell.js +165 -8
- package/dist/ui/shared/app-store.js +5 -1
- package/dist/ui/shared/app-stream.js +23 -5
- package/dist/ui/shared/app.css +155 -22
- package/dist/ui/shared/app.js +11 -1
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/header-tools.js +55 -5
- package/dist/ui/shared/instance-nav.js +241 -45
- package/dist/ui/shared/instance-switch.js +4 -4
- package/dist/ui/shared/panel-view.js +6 -0
- package/dist/ui/shared/signin.js +7 -7
- package/dist/ui/shared/theme.js +39 -7
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/ui/shared/ui-icons.js +1 -0
- package/dist/ui/shared/usage-dialog.js +23 -5
- package/dist/ui/shared/view-strings.js +32 -4
- package/dist/ui/signin.html +1 -1
- package/dist/unrecognised-prompt.d.ts +23 -0
- package/dist/unrecognised-prompt.js +74 -0
- package/dist/unrecognised-prompt.js.map +1 -0
- package/dist/update-install.d.ts +28 -2
- package/dist/update-install.js +22 -4
- package/dist/update-install.js.map +1 -1
- package/dist/update-marker.d.ts +51 -0
- package/dist/update-marker.js +61 -0
- package/dist/update-marker.js.map +1 -1
- package/dist/update-preflight.d.ts +100 -0
- package/dist/update-preflight.js +264 -0
- package/dist/update-preflight.js.map +1 -0
- package/dist/updater-caller.d.ts +31 -0
- package/dist/updater-caller.js +109 -0
- package/dist/updater-caller.js.map +1 -0
- package/dist/usage/i18n-keys.d.ts +1 -1
- package/dist/usage/i18n-keys.js +1 -1
- package/dist/usage/i18n-keys.js.map +1 -1
- package/dist/usage/providers.d.ts +34 -5
- package/dist/usage/providers.js +160 -27
- package/dist/usage/providers.js.map +1 -1
- package/dist/usage/statusline-usage.d.ts +6 -2
- package/dist/usage/statusline-usage.js +10 -4
- package/dist/usage/statusline-usage.js.map +1 -1
- package/dist/view-api.d.ts +1 -1
- package/dist/view-api.js +38 -8
- package/dist/view-api.js.map +1 -1
- package/dist/wake-coordinator.d.ts +11 -0
- package/dist/wake-coordinator.js +38 -4
- package/dist/wake-coordinator.js.map +1 -1
- package/dist/web-api.d.ts +49 -2
- package/dist/web-api.js +421 -31
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.d.ts +2 -2
- package/dist/web-auth.js +9 -4
- package/dist/web-auth.js.map +1 -1
- package/dist/web-commands.d.ts +27 -4
- package/dist/web-commands.js +19 -4
- package/dist/web-commands.js.map +1 -1
- package/dist/web-host-guard.d.ts +3 -11
- package/dist/web-host-guard.js +3 -12
- package/dist/web-host-guard.js.map +1 -1
- package/dist/web-images.d.ts +47 -0
- package/dist/web-images.js +169 -0
- package/dist/web-images.js.map +1 -0
- package/dist/web-mentions.d.ts +44 -0
- package/dist/web-mentions.js +75 -0
- package/dist/web-mentions.js.map +1 -0
- package/dist/web-prefs.d.ts +63 -0
- package/dist/web-prefs.js +265 -0
- package/dist/web-prefs.js.map +1 -0
- package/dist/web-preview.d.ts +14 -1
- package/dist/web-preview.js +35 -13
- package/dist/web-preview.js.map +1 -1
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-schedules.d.ts +12 -0
- package/dist/web-schedules.js +34 -0
- package/dist/web-schedules.js.map +1 -0
- package/dist/web-server-drain.d.ts +10 -0
- package/dist/web-server-drain.js +50 -0
- package/dist/web-server-drain.js.map +1 -0
- package/dist/web-session.d.ts +36 -1
- package/dist/web-session.js +53 -6
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +23 -0
- package/dist/web-settings-patch.js +77 -0
- package/dist/web-settings-patch.js.map +1 -0
- package/dist/web-shell-routes.d.ts +5 -1
- package/dist/web-shell-routes.js +10 -1
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/web-upload.d.ts +17 -4
- package/dist/web-upload.js +64 -13
- package/dist/web-upload.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +1 -1
- package/templates/launchd.plist.ejs +3 -1
- package/templates/systemd.service.ejs +2 -1
package/dist/backend/muse.js
CHANGED
|
@@ -90,6 +90,27 @@ const MUSE_STATUS_BAR = /^\s{1,4}[^\s·]+(?:\s+·\s+[^·\s][^·]*)+$/;
|
|
|
90
90
|
const MUSE_DIALOG_CHROME = /Allow this (?:tool|command)|Approve this (?:tool|command)|^\s*[>❯]?\s*1\s+(?:Allow|Approve|Trust)\b|Use Up\/Down|Esc (?:quits|to cancel)|↓↑ to select|·\s*Enter to choose\b/i;
|
|
91
91
|
/** The status bar is one row; a long cwd in a narrow pane wraps it onto a few more. */
|
|
92
92
|
const MUSE_MAX_STATUS_ROWS = 4;
|
|
93
|
+
/**
|
|
94
|
+
* #1717: extended bottom-region depth, but ONLY for a terminals-panel-headed
|
|
95
|
+
* block (header + running tasks + bar). A long wrapped bar still fails closed
|
|
96
|
+
* past MUSE_MAX_STATUS_ROWS (#958): the extension below accepts a deeper
|
|
97
|
+
* separator only when the row right after it is the panel header.
|
|
98
|
+
*/
|
|
99
|
+
const MUSE_MAX_PANEL_DEPTH = 8;
|
|
100
|
+
/**
|
|
101
|
+
* #1717: while a background task runs, muse draws a terminals panel between
|
|
102
|
+
* the input box's bottom separator and the status bar (live 1.4.6,
|
|
103
|
+
* tests/fixtures/muse-1.4.6/prod-background-work.pane.txt):
|
|
104
|
+
* main · ↓ to select
|
|
105
|
+
* └ ◆ Wait in the background running 11s
|
|
106
|
+
* The header ends in `↓ to select` — note the single arrow, which is NOT the
|
|
107
|
+
* dialog chrome's `↓↑ to select`. The timer redraws every second, which is
|
|
108
|
+
* exactly the periodic-redraw case. The composer above stays free, so the
|
|
109
|
+
* panel must not fail the idle proof — but only this shape passes: anything
|
|
110
|
+
* else under the separator still fails closed.
|
|
111
|
+
*/
|
|
112
|
+
const MUSE_TERMINALS_HEADER = /^.+· ↓ to select\s*$/;
|
|
113
|
+
const MUSE_TERMINAL_TASK_ROW = /^[└├]\s+.*\brunning\b\s+(?:\d+(?:\.\d+)?[hms]\s*)+\s*$/;
|
|
93
114
|
/** The working line's timer: `◇ Thinking (2s · esc to interrupt)`. */
|
|
94
115
|
const MUSE_BUSY = /\(\s*(?:\d+(?:\.\d+)?[hms]\s*)+·\s*esc to interrupt\s*\)/;
|
|
95
116
|
const MUSE_MAX_INPUT_ROWS = 200;
|
|
@@ -98,10 +119,30 @@ const MUSE_MAX_INPUT_ROWS = 200;
|
|
|
98
119
|
* first row in the bar's grammar, the rest a wrapped continuation of it. A
|
|
99
120
|
* picker, a dialog, a busy line or a second separator there means the box above
|
|
100
121
|
* is not the live input (#829 review: a chooser under an old box).
|
|
122
|
+
*
|
|
123
|
+
* #1717: a terminals panel may sit between the separator and the bar (a
|
|
124
|
+
* `· ↓ to select` header plus `running` task rows). It is stripped first and
|
|
125
|
+
* each of its rows must match the panel grammar — a header with no task row,
|
|
126
|
+
* or any other shape, still fails closed.
|
|
101
127
|
*/
|
|
102
128
|
function museStatusFooter(rows, bottomSeparator, busy) {
|
|
103
|
-
|
|
104
|
-
if (status.length === 0
|
|
129
|
+
let status = rows.slice(bottomSeparator + 1);
|
|
130
|
+
if (status.length === 0)
|
|
131
|
+
return false;
|
|
132
|
+
if (MUSE_TERMINALS_HEADER.test(status[0])) {
|
|
133
|
+
let end = 1;
|
|
134
|
+
while (end < status.length && MUSE_TERMINAL_TASK_ROW.test(status[end]))
|
|
135
|
+
end++;
|
|
136
|
+
if (end === 1)
|
|
137
|
+
return false;
|
|
138
|
+
const panel = status.slice(0, end);
|
|
139
|
+
if (!panel.every(row => !MUSE_SEPARATOR.test(row.trim()) && !busy.test(row) && !MUSE_DIALOG_CHROME.test(row)))
|
|
140
|
+
return false;
|
|
141
|
+
status = status.slice(end);
|
|
142
|
+
if (status.length === 0)
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
if (!MUSE_STATUS_BAR.test(status[0]))
|
|
105
146
|
return false;
|
|
106
147
|
if (!status.every(row => /^\s+\S/.test(row) && !MUSE_SEPARATOR.test(row.trim()) && !busy.test(row) && !MUSE_DIALOG_CHROME.test(row)))
|
|
107
148
|
return false;
|
|
@@ -109,6 +150,85 @@ function museStatusFooter(rows, bottomSeparator, busy) {
|
|
|
109
150
|
// selection cursor under the bar is a picker, not the bar.
|
|
110
151
|
return !status.slice(1).some(row => /^\s*[>❯›]?\s*\d+[.)]?\s+\S/.test(row) || /^\s*[>›]/.test(row));
|
|
111
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The structural core of the idle proof, shared with the quit-menu matcher
|
|
155
|
+
* (#1730 P2-1): true when the CURRENT screen — anchored to the bottom
|
|
156
|
+
* separator — shows the live, empty input box. A transcript quoting an older
|
|
157
|
+
* screen above never satisfies it.
|
|
158
|
+
*/
|
|
159
|
+
function museFreeComposerLayout(rows, busyPattern) {
|
|
160
|
+
// The approval prompt is auto-answered rather than input-blocking, so the
|
|
161
|
+
// idle proof itself must refuse a frame that shows dialog chrome.
|
|
162
|
+
const dialogChrome = MUSE_DIALOG_CHROME;
|
|
163
|
+
// Everything after the bottom separator must be the status bar (#1717:
|
|
164
|
+
// optionally a terminals panel first).
|
|
165
|
+
const bottomSeparator = museBottomSeparator(rows);
|
|
166
|
+
if (bottomSeparator < 2)
|
|
167
|
+
return false;
|
|
168
|
+
if (!museStatusFooter(rows, bottomSeparator, busyPattern))
|
|
169
|
+
return false;
|
|
170
|
+
// Directly above it, the EMPTY live prompt. A `❯` with text is a draft
|
|
171
|
+
// (or, higher up, a transcript echo) and never proves idle.
|
|
172
|
+
if (!/^❯\s*$/.test(rows[bottomSeparator - 1]))
|
|
173
|
+
return false;
|
|
174
|
+
const topSeparator = bottomSeparator - 2;
|
|
175
|
+
if (!MUSE_SEPARATOR.test(rows[topSeparator]))
|
|
176
|
+
return false;
|
|
177
|
+
// No busy indicator and no dialog just above the input box. The working
|
|
178
|
+
// line (`◇ Thinking (2s · esc to interrupt)`) sits immediately above the
|
|
179
|
+
// top separator; check 8 rows up to be safe.
|
|
180
|
+
const above = rows.slice(Math.max(0, topSeparator - 8), topSeparator);
|
|
181
|
+
if (above.some(r => busyPattern.test(r) || dialogChrome.test(r)))
|
|
182
|
+
return false;
|
|
183
|
+
// Arrow-key pickers (`/model`) are refused by their component chrome
|
|
184
|
+
// above, not by guessing from answer text that happens to list model ids.
|
|
185
|
+
// A `›` selection cursor is refused too; `❯` is not a cursor here — muse
|
|
186
|
+
// echoes submitted messages with it.
|
|
187
|
+
return !above.some(r => /^\s*›\s+\S/.test(r));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Nearest bottom separator, with the #1717 panel extension: the plain search
|
|
191
|
+
* stays capped at MUSE_MAX_STATUS_ROWS (a 5-row wrapped bar still fails
|
|
192
|
+
* closed, #958); a deeper separator is accepted only when the row right
|
|
193
|
+
* after it is the terminals-panel header. Nearest-first, so a stale
|
|
194
|
+
* separator in scrollback can never win over the live one.
|
|
195
|
+
*/
|
|
196
|
+
function museBottomSeparator(rows) {
|
|
197
|
+
for (let i = rows.length - 2; i >= Math.max(0, rows.length - 1 - MUSE_MAX_STATUS_ROWS); i--) {
|
|
198
|
+
if (MUSE_SEPARATOR.test(rows[i]))
|
|
199
|
+
return i;
|
|
200
|
+
}
|
|
201
|
+
for (let i = Math.max(0, rows.length - 1 - MUSE_MAX_STATUS_ROWS) - 1; i >= Math.max(0, rows.length - 1 - MUSE_MAX_PANEL_DEPTH); i--) {
|
|
202
|
+
if (MUSE_SEPARATOR.test(rows[i])) {
|
|
203
|
+
return MUSE_TERMINALS_HEADER.test(rows[i + 1] ?? "") ? i : -1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return -1;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* #1717: true when the pane shows muse's terminals panel with at least one
|
|
210
|
+
* running task — the "local work still active" state. Anchored to the last
|
|
211
|
+
* separator, where the live panel sits under the input box: a transcript
|
|
212
|
+
* quoting an old panel above a separator never counts. The idle proof checks
|
|
213
|
+
* the same shape positionally via museStatusFooter; this boolean feeds the
|
|
214
|
+
* pause gate below.
|
|
215
|
+
*/
|
|
216
|
+
export function museBackgroundWorkActive(pane) {
|
|
217
|
+
const rows = pane.replace(/\r/g, "").split("\n");
|
|
218
|
+
while (rows.length && !rows[rows.length - 1].trim())
|
|
219
|
+
rows.pop();
|
|
220
|
+
let sep = -1;
|
|
221
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
222
|
+
if (MUSE_SEPARATOR.test(rows[i])) {
|
|
223
|
+
sep = i;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (sep < 0)
|
|
228
|
+
return false;
|
|
229
|
+
const after = rows.slice(sep + 1);
|
|
230
|
+
return after.length > 1 && MUSE_TERMINALS_HEADER.test(after[0]) && MUSE_TERMINAL_TASK_ROW.test(after[1]);
|
|
231
|
+
}
|
|
112
232
|
/**
|
|
113
233
|
* The rows of muse's live input box, or null when the bottom of the pane is not
|
|
114
234
|
* muse's layout (#829). The box is the LAST thing above the status bar:
|
|
@@ -127,13 +247,7 @@ export function museInputBox(pane, busy = MUSE_BUSY) {
|
|
|
127
247
|
const rows = pane.replace(/\r/g, "").split("\n");
|
|
128
248
|
while (rows.length && !rows[rows.length - 1].trim())
|
|
129
249
|
rows.pop();
|
|
130
|
-
|
|
131
|
-
for (let i = rows.length - 2; i >= Math.max(0, rows.length - 1 - MUSE_MAX_STATUS_ROWS); i--) {
|
|
132
|
-
if (MUSE_SEPARATOR.test(rows[i])) {
|
|
133
|
-
bottom = i;
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
250
|
+
const bottom = museBottomSeparator(rows);
|
|
137
251
|
if (bottom < 2 || !museStatusFooter(rows, bottom, busy))
|
|
138
252
|
return null;
|
|
139
253
|
for (let top = bottom - 1, n = 0; top >= 1 && n < MUSE_MAX_INPUT_ROWS; top--, n++) {
|
|
@@ -234,6 +348,288 @@ const MUSE_LOGIN_MENU = {
|
|
|
234
348
|
inputBlocked: true,
|
|
235
349
|
description: "Muse login menu — waiting for a human to log in (never answered)",
|
|
236
350
|
};
|
|
351
|
+
/**
|
|
352
|
+
* #1716: `/quit` while a background task runs is answered with a confirmation
|
|
353
|
+
* instead of an exit (live 1.4.6,
|
|
354
|
+
* tests/fixtures/muse-1.4.6/prod-quit-with-local-work.pane.txt):
|
|
355
|
+
* ❯
|
|
356
|
+
* Local work is still active
|
|
357
|
+
* The following will stop when you exit:
|
|
358
|
+
* background task · tool.bash
|
|
359
|
+
* ❯ 1. Exit anyway
|
|
360
|
+
* 2. Stay
|
|
361
|
+
* Enter to confirm · Esc to cancel
|
|
362
|
+
* ──────────
|
|
363
|
+
* main · ↓ to select
|
|
364
|
+
* └ ◇ Wait in the background running 15s
|
|
365
|
+
* mock-model · high · ~/work · Launch overrides
|
|
366
|
+
* Unlike Claude's prompt this menu is NOT bottom-anchored — the terminals
|
|
367
|
+
* panel sits under it — so the match is anchored to the hint row instead:
|
|
368
|
+
* the header must be within 12 rows above it with both options in between.
|
|
369
|
+
* Enter would take the selected option ("Exit anyway"), so like #1217 this is
|
|
370
|
+
* recognised whole and never answered here: the stop flow cancels it.
|
|
371
|
+
*/
|
|
372
|
+
/**
|
|
373
|
+
* #1715: with `skip_permissions: false` muse asks before running a command
|
|
374
|
+
* needing approval (live 1.4.6,
|
|
375
|
+
* tests/fixtures/muse-1.4.6/approval-escalated.pane.txt and
|
|
376
|
+
* approval-untrusted-staged.pane.txt):
|
|
377
|
+
* Would you like to run the following command?
|
|
378
|
+
* $ echo perm-check
|
|
379
|
+
* › 1. Yes, proceed (y)
|
|
380
|
+
* 2. No, and tell Muse Code what to do differently (esc)
|
|
381
|
+
* Change approval defaults in /permissions
|
|
382
|
+
* `--approval-mode untrusted` (new in 1.4.6) stages the command first
|
|
383
|
+
* (`Stage 1/1`, `Current argv: [...]`) and offers three options (allow once /
|
|
384
|
+
* always allow in this workspace / abort). 1.4.4 shows the same prompt with a
|
|
385
|
+
* `›` marker and no footer line. Enter would take the preselected allow
|
|
386
|
+
* option, so like #1716 this is recognised whole and never answered here.
|
|
387
|
+
* 1.4.7 draws the selection cursor as `⟩` on this prompt and on the #1716
|
|
388
|
+
* quit menu (tests/fixtures/muse-1.4.7); the other rows of those blocks are
|
|
389
|
+
* unchanged.
|
|
390
|
+
*/
|
|
391
|
+
export const MUSE_APPROVAL_PROMPT = /^[ \t]*Would you like to run the following command\?[ \t]*$/m;
|
|
392
|
+
const MUSE_APPROVAL_STAGE = /^[ \t]*Stage \d+\/\d+[ \t]*$/;
|
|
393
|
+
const MUSE_APPROVAL_ARGV = /^[ \t]*Current argv: \[.*\][ \t]*$/;
|
|
394
|
+
const MUSE_APPROVAL_OPT = /^[ \t]*[❯›⟩]?[ \t]*\d\.[ \t]+/;
|
|
395
|
+
const MUSE_APPROVAL_FOOTER = /^[ \t]*Change approval defaults in \/permissions[ \t]*$/;
|
|
396
|
+
/**
|
|
397
|
+
* #1715: which request the approval prompt is showing — its `$ command` row.
|
|
398
|
+
* Stable across the `Calling tools (Ns)` timer redraws, so the episode can
|
|
399
|
+
* confirm; a whole-pane hash never would. Derived from the SAME selected
|
|
400
|
+
* block as the predicate (#1715 r2): a scrollback quote's command must never
|
|
401
|
+
* become the current request's identity.
|
|
402
|
+
*/
|
|
403
|
+
export function museApprovalRequestIdentity(pane) {
|
|
404
|
+
const s = museApprovalScreen(pane);
|
|
405
|
+
if (s === null)
|
|
406
|
+
return null;
|
|
407
|
+
return museApprovalBlock(s.screen, s.top, s.bottom)?.command ?? null;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* #1715 r2: the selected live approval block — its header row index and `$`
|
|
411
|
+
* command text. Headers are tried newest-first inside the live input block so
|
|
412
|
+
* a scrollback quote never shadows the current prompt, and the identity comes
|
|
413
|
+
* from the SAME block the predicate accepts (never a separate global search).
|
|
414
|
+
*/
|
|
415
|
+
function museApprovalBlock(screen, top, bottom) {
|
|
416
|
+
const window = [];
|
|
417
|
+
for (let i = bottom - 1; i > top; i--) {
|
|
418
|
+
if (screen[i].trim())
|
|
419
|
+
window.unshift({ row: screen[i], index: i });
|
|
420
|
+
}
|
|
421
|
+
for (let h = window.length - 1; h >= 0; h--) {
|
|
422
|
+
if (!MUSE_APPROVAL_PROMPT.test(window[h].row))
|
|
423
|
+
continue;
|
|
424
|
+
let k = h + 1;
|
|
425
|
+
const commandRow = k < window.length ? window[k].row : "";
|
|
426
|
+
const command = /^[ \t]*\$ (.+?)[ \t]*$/.exec(commandRow)?.[1]?.trim();
|
|
427
|
+
if (!command)
|
|
428
|
+
continue;
|
|
429
|
+
k++;
|
|
430
|
+
if (k < window.length && MUSE_APPROVAL_STAGE.test(window[k].row))
|
|
431
|
+
k++;
|
|
432
|
+
if (k < window.length && MUSE_APPROVAL_ARGV.test(window[k].row))
|
|
433
|
+
k++;
|
|
434
|
+
const last = window[window.length - 1]?.row;
|
|
435
|
+
const options = (last !== undefined && MUSE_APPROVAL_FOOTER.test(last) ? window.slice(k, -1) : window.slice(k))
|
|
436
|
+
.map(entry => entry.row);
|
|
437
|
+
if (options.length !== 2 && options.length !== 3)
|
|
438
|
+
continue;
|
|
439
|
+
if (!options.every(row => MUSE_APPROVAL_OPT.test(row)))
|
|
440
|
+
continue;
|
|
441
|
+
const first = (options[0] ?? "").replace(/^[ \t]*[❯›⟩]?[ \t]*/, "");
|
|
442
|
+
const second = options[1] ?? "";
|
|
443
|
+
if (options.length === 2) {
|
|
444
|
+
if (!/^1\. Yes, proceed \(y\)[ \t]*$/.test(first))
|
|
445
|
+
continue;
|
|
446
|
+
if (!/^[ \t]*[❯›⟩]?[ \t]*2\. No, and tell Muse Code what to do differently \(esc\)[ \t]*$/.test(second))
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
if (!/^1\. Allow this stage once \(y\)[ \t]*$/.test(first))
|
|
451
|
+
continue;
|
|
452
|
+
if (!/^[ \t]*[❯›⟩]?[ \t]*2\. Always allow in this workspace: .+ \(p\)[ \t]*$/.test(second))
|
|
453
|
+
continue;
|
|
454
|
+
if (!/^[ \t]*[❯›⟩]?[ \t]*3\. Abort the entire command \(esc\)[ \t]*$/.test(options[2] ?? ""))
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
return { head: window[h].index, command };
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
function museApprovalScreen(pane) {
|
|
462
|
+
const screen = pane.replace(/\r/g, "").split("\n");
|
|
463
|
+
while (screen.length && !screen[screen.length - 1].trim())
|
|
464
|
+
screen.pop();
|
|
465
|
+
const bottom = museBottomSeparator(screen);
|
|
466
|
+
if (bottom < 0)
|
|
467
|
+
return null;
|
|
468
|
+
// Same ownership as the quit menu (#1730 P2-1): the prompt must live in the
|
|
469
|
+
// current input block, and a free composer means any prompt text is history.
|
|
470
|
+
let top = -1;
|
|
471
|
+
for (let i = bottom - 1; i >= 0; i--) {
|
|
472
|
+
if (MUSE_SEPARATOR.test(screen[i])) {
|
|
473
|
+
top = i;
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (museFreeComposerLayout(screen, MUSE_BUSY))
|
|
478
|
+
return null;
|
|
479
|
+
// #1715 r3: any readable composer means prompt text is a quote, not the
|
|
480
|
+
// live block — the live approval panes carry no ❯ composer row at all
|
|
481
|
+
// (inputDraft null), while a quoting draft does, even one starting with a
|
|
482
|
+
// blank first row. A null draft (no readable box) carries nothing to judge,
|
|
483
|
+
// so it still proceeds.
|
|
484
|
+
if (museInputBox(pane, MUSE_BUSY) !== null)
|
|
485
|
+
return null;
|
|
486
|
+
return { screen, top, bottom };
|
|
487
|
+
}
|
|
488
|
+
export function museApprovalPromptActive(pane) {
|
|
489
|
+
const s = museApprovalScreen(pane);
|
|
490
|
+
return s !== null && museApprovalBlock(s.screen, s.top, s.bottom) !== null;
|
|
491
|
+
}
|
|
492
|
+
export const MUSE_LOCAL_WORK_QUIT = /^[ \t]*Local work is still active[ \t]*$/m;
|
|
493
|
+
const MUSE_QUIT_HINT = /^[ \t]*Enter to confirm · Esc to cancel[ \t]*$/;
|
|
494
|
+
const MUSE_QUIT_OPT_1 = /^[ \t]*[❯›⟩]?[ \t]*1\.[ \t]+Exit anyway[ \t]*$/m;
|
|
495
|
+
const MUSE_QUIT_OPT_2 = /^[ \t]*[❯›⟩]?[ \t]*2\.[ \t]+Stay[ \t]*$/m;
|
|
496
|
+
export function museLocalWorkQuitActive(pane) {
|
|
497
|
+
const screen = pane.replace(/\r/g, "").split("\n");
|
|
498
|
+
while (screen.length && !screen[screen.length - 1].trim())
|
|
499
|
+
screen.pop();
|
|
500
|
+
const bottom = museBottomSeparator(screen);
|
|
501
|
+
if (bottom < 0)
|
|
502
|
+
return false;
|
|
503
|
+
// #1730 P2-1 (refined): the menu must belong to the CURRENT input block —
|
|
504
|
+
// between the bottom separator and the nearest separator above it. Menu
|
|
505
|
+
// text quoted in an older screen is history even when the current composer
|
|
506
|
+
// is busy with a draft, not just when it is free.
|
|
507
|
+
let top = -1;
|
|
508
|
+
for (let i = bottom - 1; i >= 0; i--) {
|
|
509
|
+
if (MUSE_SEPARATOR.test(screen[i])) {
|
|
510
|
+
top = i;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// A free composer on the current screen means any menu text is history.
|
|
515
|
+
if (museFreeComposerLayout(screen, MUSE_BUSY))
|
|
516
|
+
return false;
|
|
517
|
+
// #1730 P2-1 r3: a populated ❯ line means any menu text is a quote, not the
|
|
518
|
+
// live chrome. The menu only ever answers a submitted /quit, which clears
|
|
519
|
+
// the box — the live capture's ❯ row is empty with the menu as chrome under
|
|
520
|
+
// it — while every quoting draft (r2's "cancel me", r3a's hint-carrying
|
|
521
|
+
// draft, r3b's menu-quoting draft) holds text on the ❯ line itself. A null
|
|
522
|
+
// draft (no readable box) carries nothing to judge, so it still proceeds.
|
|
523
|
+
// Residual: an empty ❯ followed by a verbatim menu paste is byte-identical
|
|
524
|
+
// to the live chrome and cannot be separated here.
|
|
525
|
+
const draft = museInputBox(pane, MUSE_BUSY);
|
|
526
|
+
if (draft !== null && draft.rows.length > 0 && draft.rows[0].trim() !== "")
|
|
527
|
+
return false;
|
|
528
|
+
// Candidates newest-first, so a stale quote above never shadows the live
|
|
529
|
+
// block below (or passes as live itself).
|
|
530
|
+
for (let h = screen.length - 1; h >= 0; h--) {
|
|
531
|
+
if (!MUSE_QUIT_HINT.test(screen[h]))
|
|
532
|
+
continue;
|
|
533
|
+
if (!(h > top && h < bottom))
|
|
534
|
+
continue;
|
|
535
|
+
// The block above, skipping padding blanks (the same 12-non-blank
|
|
536
|
+
// window as before — the live menu fits in 9), with screen indices so the
|
|
537
|
+
// WHOLE block is fenced to the live input box, not just the hint: the
|
|
538
|
+
// backward scan must not borrow the header or options from above top.
|
|
539
|
+
const window = [];
|
|
540
|
+
for (let i = h - 1; i >= 0 && window.length < 12; i--) {
|
|
541
|
+
if (screen[i].trim())
|
|
542
|
+
window.unshift({ row: screen[i], index: i });
|
|
543
|
+
}
|
|
544
|
+
if (window.length < 4)
|
|
545
|
+
continue;
|
|
546
|
+
// #1730 P2-1 r4: headers newest-first, eligible only — a scrollback header
|
|
547
|
+
// above top must not shadow the live header inside the box. The first
|
|
548
|
+
// (oldest) match can sit above top while the current menu is intact below.
|
|
549
|
+
for (let head = window.length - 1; head >= 0; head--) {
|
|
550
|
+
if (!MUSE_LOCAL_WORK_QUIT.test(window[head].row))
|
|
551
|
+
continue;
|
|
552
|
+
if (window[head].index <= top)
|
|
553
|
+
continue;
|
|
554
|
+
const block = window.slice(head).map(entry => entry.row).join("\n");
|
|
555
|
+
if (!MUSE_QUIT_OPT_1.test(block) || !MUSE_QUIT_OPT_2.test(block))
|
|
556
|
+
continue;
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* #1796: the fallback `/model` set, used only when muse's on-disk provider
|
|
564
|
+
* catalog is missing or unreadable. It mirrors what the 1.4.7 picker shows
|
|
565
|
+
* (visible rows only — no `-contributor` ids); the live catalog remains the
|
|
566
|
+
* source of truth whenever it can be read.
|
|
567
|
+
*/
|
|
568
|
+
const MUSE_MODEL_FALLBACK = [
|
|
569
|
+
{ id: "muse-wonder-1.0", label: "muse-wonder-1.0" },
|
|
570
|
+
{ id: "muse-spark-1.3", label: "muse-spark-1.3" },
|
|
571
|
+
{ id: "muse-spark-1.2", label: "muse-spark-1.2" },
|
|
572
|
+
];
|
|
573
|
+
/**
|
|
574
|
+
* #1796: read the provider catalog muse caches on disk
|
|
575
|
+
* (`<state>/model-catalog/<provider>__<profile>.json`). Bounded (at most 16
|
|
576
|
+
* files, 256 KiB each) and best-effort — anything unexpected yields the
|
|
577
|
+
* fallback. Listed rows only: `visibility: "visible"` is what the 1.4.7
|
|
578
|
+
* picker shows (the `-contributor` siblings and the image model are hidden),
|
|
579
|
+
* and a null `context_limit` additionally marks a non-chat model. File order
|
|
580
|
+
* is kept and ids deduped.
|
|
581
|
+
*
|
|
582
|
+
* `stateDir` is injectable for tests; production passes nothing and reads the
|
|
583
|
+
* host state dir (the fleet never isolates XDG_DATA_HOME, mirroring the
|
|
584
|
+
* session lookup).
|
|
585
|
+
*/
|
|
586
|
+
export function readMuseModelCatalog(stateDir) {
|
|
587
|
+
// Root resolution mirrors the binary, not XDG_STATE_HOME: 1.4.7 builds its
|
|
588
|
+
// state dir as `$XDG_DATA_HOME/muse` or `$HOME/.local/share/muse`
|
|
589
|
+
// (MUSE_SESSIONS uses the same root).
|
|
590
|
+
const base = process.env.XDG_DATA_HOME
|
|
591
|
+
? join(process.env.XDG_DATA_HOME, "muse")
|
|
592
|
+
: join(homedir(), ".local", "share", "muse");
|
|
593
|
+
const dir = join(stateDir ?? base, "model-catalog");
|
|
594
|
+
try {
|
|
595
|
+
const files = readdirSync(dir).filter(f => f.endsWith(".json")).sort().slice(0, 16);
|
|
596
|
+
if (files.length === 0)
|
|
597
|
+
return [...MUSE_MODEL_FALLBACK];
|
|
598
|
+
const seen = new Set();
|
|
599
|
+
const out = [];
|
|
600
|
+
for (const file of files) {
|
|
601
|
+
const path = join(dir, file);
|
|
602
|
+
try {
|
|
603
|
+
if (statSync(path).size > 262_144)
|
|
604
|
+
continue;
|
|
605
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
606
|
+
const rows = parsed.rows;
|
|
607
|
+
if (!Array.isArray(rows))
|
|
608
|
+
continue;
|
|
609
|
+
for (const row of rows.slice(0, 256)) {
|
|
610
|
+
const r = row;
|
|
611
|
+
if (typeof r.model_id !== "string" || r.model_id === "" || r.visibility !== "visible")
|
|
612
|
+
continue;
|
|
613
|
+
// A `-contributor` suffix is a legacy spelling of contributor mode,
|
|
614
|
+
// not a model — excluded even if some profile ever marks it visible.
|
|
615
|
+
if (r.model_id.endsWith("-contributor"))
|
|
616
|
+
continue;
|
|
617
|
+
if (r.context_limit == null)
|
|
618
|
+
continue;
|
|
619
|
+
if (seen.has(r.model_id))
|
|
620
|
+
continue;
|
|
621
|
+
seen.add(r.model_id);
|
|
622
|
+
out.push({ id: r.model_id, label: typeof r.display_label === "string" && r.display_label !== "" ? r.display_label : r.model_id });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
catch { /* one bad file never hides the rest */ }
|
|
626
|
+
}
|
|
627
|
+
return out.length > 0 ? out : [...MUSE_MODEL_FALLBACK];
|
|
628
|
+
}
|
|
629
|
+
catch {
|
|
630
|
+
return [...MUSE_MODEL_FALLBACK];
|
|
631
|
+
}
|
|
632
|
+
}
|
|
237
633
|
/**
|
|
238
634
|
* Meta Muse Code — the `muse` CLI (v1.3.0 verified).
|
|
239
635
|
*
|
|
@@ -559,41 +955,7 @@ export class MuseBackend {
|
|
|
559
955
|
const rows = pane.replace(/\r/g, "").split("\n");
|
|
560
956
|
while (rows.length && !rows[rows.length - 1].trim())
|
|
561
957
|
rows.pop();
|
|
562
|
-
|
|
563
|
-
const busyPattern = this.getBusyPattern();
|
|
564
|
-
// The approval prompt is auto-answered rather than input-blocking, so the
|
|
565
|
-
// idle proof itself must refuse a frame that shows dialog chrome.
|
|
566
|
-
const dialogChrome = MUSE_DIALOG_CHROME;
|
|
567
|
-
// Everything after the bottom separator must be the status bar.
|
|
568
|
-
let bottomSeparator = -1;
|
|
569
|
-
for (let i = rows.length - 2; i >= Math.max(0, rows.length - 1 - MUSE_MAX_STATUS_ROWS); i--) {
|
|
570
|
-
if (separator.test(rows[i])) {
|
|
571
|
-
bottomSeparator = i;
|
|
572
|
-
break;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (bottomSeparator < 2)
|
|
576
|
-
return false;
|
|
577
|
-
if (!museStatusFooter(rows, bottomSeparator, busyPattern))
|
|
578
|
-
return false;
|
|
579
|
-
// Directly above it, the EMPTY live prompt. A `❯` with text is a draft
|
|
580
|
-
// (or, higher up, a transcript echo) and never proves idle.
|
|
581
|
-
if (!/^❯\s*$/.test(rows[bottomSeparator - 1]))
|
|
582
|
-
return false;
|
|
583
|
-
const topSeparator = bottomSeparator - 2;
|
|
584
|
-
if (!separator.test(rows[topSeparator]))
|
|
585
|
-
return false;
|
|
586
|
-
// No busy indicator and no dialog just above the input box. The working
|
|
587
|
-
// line (`◇ Thinking (2s · esc to interrupt)`) sits immediately above the
|
|
588
|
-
// top separator; check 8 rows up to be safe.
|
|
589
|
-
const above = rows.slice(Math.max(0, topSeparator - 8), topSeparator);
|
|
590
|
-
if (above.some(r => busyPattern.test(r) || dialogChrome.test(r)))
|
|
591
|
-
return false;
|
|
592
|
-
// Arrow-key pickers (`/model`) are refused by their component chrome
|
|
593
|
-
// above, not by guessing from answer text that happens to list model ids.
|
|
594
|
-
// A `›` selection cursor is refused too; `❯` is not a cursor here — muse
|
|
595
|
-
// echoes submitted messages with it.
|
|
596
|
-
return !above.some(r => /^\s*›\s+\S/.test(r));
|
|
958
|
+
return museFreeComposerLayout(rows, this.getBusyPattern());
|
|
597
959
|
}
|
|
598
960
|
getErrorPatterns() {
|
|
599
961
|
// NOTE: an Escape-interrupted run prints "interrupting run" in the status
|
|
@@ -659,6 +1021,31 @@ export class MuseBackend {
|
|
|
659
1021
|
keys: ["1", "Enter"],
|
|
660
1022
|
description: "Muse tool approval — allow",
|
|
661
1023
|
},
|
|
1024
|
+
{
|
|
1025
|
+
// #1715: never answered here. Enter would take the preselected allow
|
|
1026
|
+
// option, so the human must choose. The prompt replaces the input
|
|
1027
|
+
// box (no draft to clear, unlike #829); deliveries simply hold.
|
|
1028
|
+
pattern: MUSE_APPROVAL_PROMPT,
|
|
1029
|
+
isActive: museApprovalPromptActive,
|
|
1030
|
+
requestIdentity: museApprovalRequestIdentity,
|
|
1031
|
+
interactionKind: "permission",
|
|
1032
|
+
keys: [],
|
|
1033
|
+
holdOnly: true,
|
|
1034
|
+
blocksDelivery: true,
|
|
1035
|
+
inputBlocked: true,
|
|
1036
|
+
description: "Muse approval prompt — waiting for a human choice, never auto-approving",
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
// #1716: never answered here. A delivery's Enter would pick
|
|
1040
|
+
// "Exit anyway"; the stop flow cancels it (Escape, then SIGTERM).
|
|
1041
|
+
pattern: MUSE_LOCAL_WORK_QUIT,
|
|
1042
|
+
isActive: museLocalWorkQuitActive,
|
|
1043
|
+
keys: [],
|
|
1044
|
+
holdOnly: true,
|
|
1045
|
+
blocksDelivery: true,
|
|
1046
|
+
inputBlocked: true,
|
|
1047
|
+
description: "Muse \"Local work is still active\" quit menu — holding, never answering with Enter",
|
|
1048
|
+
},
|
|
662
1049
|
];
|
|
663
1050
|
}
|
|
664
1051
|
getContextUsage() {
|
|
@@ -726,6 +1113,19 @@ export class MuseBackend {
|
|
|
726
1113
|
getQuitCommand() { return "/quit"; }
|
|
727
1114
|
getQuitKey() { return "C-c"; }
|
|
728
1115
|
getQuitKeyPresses() { return 2; }
|
|
1116
|
+
/** #1716: `/quit` with a background task running stops at the "Local work is still active" menu. */
|
|
1117
|
+
quitBlockedByDialog(pane) {
|
|
1118
|
+
return museLocalWorkQuitActive(pane);
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* #1717: the terminals panel with a `running` task — the pause flow defers
|
|
1122
|
+
* an idle pause while this holds, so an AgEnD-initiated silent pause never
|
|
1123
|
+
* kills the user's running background work. Explicit stop/restart still
|
|
1124
|
+
* proceed via quitBlockedByDialog above.
|
|
1125
|
+
*/
|
|
1126
|
+
backgroundWorkRunning(pane) {
|
|
1127
|
+
return museBackgroundWorkActive(pane);
|
|
1128
|
+
}
|
|
729
1129
|
getCompactCommand() { return "/compact"; }
|
|
730
1130
|
getClearCommand() { return "/clear"; }
|
|
731
1131
|
/**
|
|
@@ -773,15 +1173,11 @@ export class MuseBackend {
|
|
|
773
1173
|
async listModels() {
|
|
774
1174
|
// muse has no models subcommand (the command table is: resume, exec,
|
|
775
1175
|
// config, export, trace, skills, plugins, sandbox, schema, serve,
|
|
776
|
-
// session-message, mcp, auth, login, logout, init).
|
|
777
|
-
// the
|
|
778
|
-
//
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
{ id: "muse-spark-1.3-contributor", label: "muse-spark-1.3-contributor (shares content)" },
|
|
782
|
-
{ id: "muse-spark-1.2", label: "muse-spark-1.2" },
|
|
783
|
-
{ id: "muse-spark-1.2-contributor", label: "muse-spark-1.2-contributor (shares content)" },
|
|
784
|
-
];
|
|
1176
|
+
// session-message, mcp, auth, login, logout, init). The live `/model`
|
|
1177
|
+
// picker reads the provider catalog muse caches on disk (#1796), so this
|
|
1178
|
+
// reads the same files — bounded and best-effort, falling back to the
|
|
1179
|
+
// last known set when the cache is missing or unreadable.
|
|
1180
|
+
return readMuseModelCatalog();
|
|
785
1181
|
}
|
|
786
1182
|
async probeCLIEnv() {
|
|
787
1183
|
const { probeCliVersion } = await import("./types.js");
|