@namzu/cli 29.0.1 → 30.0.0
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 +194 -0
- package/README.md +20 -0
- package/dist/browser/commands.d.ts +29 -0
- package/dist/browser/commands.d.ts.map +1 -0
- package/dist/browser/commands.js +361 -0
- package/dist/browser/commands.js.map +1 -0
- package/dist/browser/control.d.ts +79 -0
- package/dist/browser/control.d.ts.map +1 -0
- package/dist/browser/control.js +140 -0
- package/dist/browser/control.js.map +1 -0
- package/dist/browser/describe.d.ts +12 -0
- package/dist/browser/describe.d.ts.map +1 -0
- package/dist/browser/describe.js +27 -0
- package/dist/browser/describe.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +1 -0
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/browser.d.ts +7 -0
- package/dist/commands/browser.d.ts.map +1 -0
- package/dist/commands/browser.js +16 -0
- package/dist/commands/browser.js.map +1 -0
- package/dist/commands/drain.d.ts +2 -1
- package/dist/commands/drain.d.ts.map +1 -1
- package/dist/commands/drain.js +6 -3
- package/dist/commands/drain.js.map +1 -1
- package/dist/commands/exec-flags.d.ts +2 -1
- package/dist/commands/exec-flags.d.ts.map +1 -1
- package/dist/commands/exec-flags.js +6 -3
- package/dist/commands/exec-flags.js.map +1 -1
- package/dist/commands/exec-json.d.ts.map +1 -1
- package/dist/commands/exec-json.js +2 -1
- package/dist/commands/exec-json.js.map +1 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +5 -1
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/host-queries.d.ts.map +1 -1
- package/dist/commands/host-queries.js +10 -3
- package/dist/commands/host-queries.js.map +1 -1
- package/dist/commands/schedule.d.ts +9 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/schedule.js +126 -0
- package/dist/commands/schedule.js.map +1 -0
- package/dist/commands/serve.d.ts +3 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +5 -3
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/skills.d.ts +10 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +23 -7
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +11 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/config/load.d.ts +39 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +232 -1
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +78 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/user-config.d.ts +23 -0
- package/dist/config/user-config.d.ts.map +1 -0
- package/dist/config/user-config.js +48 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/context/capabilities.d.ts +1 -1
- package/dist/context/capabilities.d.ts.map +1 -1
- package/dist/context/capabilities.js +1 -0
- package/dist/context/capabilities.js.map +1 -1
- package/dist/context/environment.d.ts +26 -3
- package/dist/context/environment.d.ts.map +1 -1
- package/dist/context/environment.js +68 -9
- package/dist/context/environment.js.map +1 -1
- package/dist/doctor/checks/browser.d.ts +15 -0
- package/dist/doctor/checks/browser.d.ts.map +1 -0
- package/dist/doctor/checks/browser.js +63 -0
- package/dist/doctor/checks/browser.js.map +1 -0
- package/dist/doctor/checks/index.d.ts +4 -1
- package/dist/doctor/checks/index.d.ts.map +1 -1
- package/dist/doctor/checks/index.js +8 -1
- package/dist/doctor/checks/index.js.map +1 -1
- package/dist/doctor/checks/scheduler.d.ts +10 -0
- package/dist/doctor/checks/scheduler.d.ts.map +1 -0
- package/dist/doctor/checks/scheduler.js +74 -0
- package/dist/doctor/checks/scheduler.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/notifications/desktop/freedesktop.d.ts +10 -0
- package/dist/integrations/notifications/desktop/freedesktop.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/freedesktop.js +33 -0
- package/dist/integrations/notifications/desktop/freedesktop.js.map +1 -0
- package/dist/integrations/notifications/desktop/macos.d.ts +6 -0
- package/dist/integrations/notifications/desktop/macos.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/macos.js +17 -0
- package/dist/integrations/notifications/desktop/macos.js.map +1 -0
- package/dist/integrations/notifications/desktop/sanitize.d.ts +16 -0
- package/dist/integrations/notifications/desktop/sanitize.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/sanitize.js +49 -0
- package/dist/integrations/notifications/desktop/sanitize.js.map +1 -0
- package/dist/integrations/notifications/desktop/throttle.d.ts +10 -0
- package/dist/integrations/notifications/desktop/throttle.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/throttle.js +39 -0
- package/dist/integrations/notifications/desktop/throttle.js.map +1 -0
- package/dist/integrations/notifications/desktop/windows-toast.d.ts +23 -0
- package/dist/integrations/notifications/desktop/windows-toast.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/windows-toast.js +56 -0
- package/dist/integrations/notifications/desktop/windows-toast.js.map +1 -0
- package/dist/integrations/notifications/desktop.d.ts +84 -0
- package/dist/integrations/notifications/desktop.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop.js +199 -0
- package/dist/integrations/notifications/desktop.js.map +1 -0
- package/dist/integrations/plugins/runtime.d.ts +8 -2
- package/dist/integrations/plugins/runtime.d.ts.map +1 -1
- package/dist/integrations/plugins/runtime.js +14 -8
- package/dist/integrations/plugins/runtime.js.map +1 -1
- package/dist/integrations/providers/harness-credentials.d.ts +9 -4
- package/dist/integrations/providers/harness-credentials.d.ts.map +1 -1
- package/dist/integrations/providers/harness-credentials.js +16 -8
- package/dist/integrations/providers/harness-credentials.js.map +1 -1
- package/dist/integrations/resident/session-step.d.ts.map +1 -1
- package/dist/integrations/resident/session-step.js +1 -0
- package/dist/integrations/resident/session-step.js.map +1 -1
- package/dist/integrations/subagents/runtime.d.ts +6 -0
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +14 -5
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/integrations/web/open-url.d.ts +24 -0
- package/dist/integrations/web/open-url.d.ts.map +1 -0
- package/dist/integrations/web/open-url.js +73 -0
- package/dist/integrations/web/open-url.js.map +1 -0
- package/dist/permissions/browser-sites.d.ts +95 -0
- package/dist/permissions/browser-sites.d.ts.map +1 -0
- package/dist/permissions/browser-sites.js +270 -0
- package/dist/permissions/browser-sites.js.map +1 -0
- package/dist/schedule/build.d.ts +86 -0
- package/dist/schedule/build.d.ts.map +1 -0
- package/dist/schedule/build.js +267 -0
- package/dist/schedule/build.js.map +1 -0
- package/dist/schedule/changes.d.ts +28 -0
- package/dist/schedule/changes.d.ts.map +1 -0
- package/dist/schedule/changes.js +65 -0
- package/dist/schedule/changes.js.map +1 -0
- package/dist/schedule/commands/add.d.ts +24 -0
- package/dist/schedule/commands/add.d.ts.map +1 -0
- package/dist/schedule/commands/add.js +447 -0
- package/dist/schedule/commands/add.js.map +1 -0
- package/dist/schedule/commands/args.d.ts +22 -0
- package/dist/schedule/commands/args.d.ts.map +1 -0
- package/dist/schedule/commands/args.js +80 -0
- package/dist/schedule/commands/args.js.map +1 -0
- package/dist/schedule/commands/confirm-prompt.d.ts +6 -0
- package/dist/schedule/commands/confirm-prompt.d.ts.map +1 -0
- package/dist/schedule/commands/confirm-prompt.js +21 -0
- package/dist/schedule/commands/confirm-prompt.js.map +1 -0
- package/dist/schedule/commands/lifecycle.d.ts +39 -0
- package/dist/schedule/commands/lifecycle.d.ts.map +1 -0
- package/dist/schedule/commands/lifecycle.js +586 -0
- package/dist/schedule/commands/lifecycle.js.map +1 -0
- package/dist/schedule/commands/list.d.ts +33 -0
- package/dist/schedule/commands/list.d.ts.map +1 -0
- package/dist/schedule/commands/list.js +206 -0
- package/dist/schedule/commands/list.js.map +1 -0
- package/dist/schedule/commands/logs.d.ts +7 -0
- package/dist/schedule/commands/logs.d.ts.map +1 -0
- package/dist/schedule/commands/logs.js +106 -0
- package/dist/schedule/commands/logs.js.map +1 -0
- package/dist/schedule/commands/service.d.ts +16 -0
- package/dist/schedule/commands/service.d.ts.map +1 -0
- package/dist/schedule/commands/service.js +458 -0
- package/dist/schedule/commands/service.js.map +1 -0
- package/dist/schedule/daemon/daemon.d.ts +156 -0
- package/dist/schedule/daemon/daemon.d.ts.map +1 -0
- package/dist/schedule/daemon/daemon.js +1253 -0
- package/dist/schedule/daemon/daemon.js.map +1 -0
- package/dist/schedule/daemon/endpoint.d.ts +38 -0
- package/dist/schedule/daemon/endpoint.d.ts.map +1 -0
- package/dist/schedule/daemon/endpoint.js +158 -0
- package/dist/schedule/daemon/endpoint.js.map +1 -0
- package/dist/schedule/daemon/exit.d.ts +8 -0
- package/dist/schedule/daemon/exit.d.ts.map +1 -0
- package/dist/schedule/daemon/exit.js +8 -0
- package/dist/schedule/daemon/exit.js.map +1 -0
- package/dist/schedule/daemon/log.d.ts +12 -0
- package/dist/schedule/daemon/log.d.ts.map +1 -0
- package/dist/schedule/daemon/log.js +53 -0
- package/dist/schedule/daemon/log.js.map +1 -0
- package/dist/schedule/daemon/notify.d.ts +38 -0
- package/dist/schedule/daemon/notify.d.ts.map +1 -0
- package/dist/schedule/daemon/notify.js +115 -0
- package/dist/schedule/daemon/notify.js.map +1 -0
- package/dist/schedule/daemon/retention.d.ts +16 -0
- package/dist/schedule/daemon/retention.d.ts.map +1 -0
- package/dist/schedule/daemon/retention.js +46 -0
- package/dist/schedule/daemon/retention.js.map +1 -0
- package/dist/schedule/daemon/sessions.d.ts +23 -0
- package/dist/schedule/daemon/sessions.d.ts.map +1 -0
- package/dist/schedule/daemon/sessions.js +52 -0
- package/dist/schedule/daemon/sessions.js.map +1 -0
- package/dist/schedule/env.d.ts +32 -0
- package/dist/schedule/env.d.ts.map +1 -0
- package/dist/schedule/env.js +106 -0
- package/dist/schedule/env.js.map +1 -0
- package/dist/schedule/fire/browser-preflight.d.ts +41 -0
- package/dist/schedule/fire/browser-preflight.d.ts.map +1 -0
- package/dist/schedule/fire/browser-preflight.js +73 -0
- package/dist/schedule/fire/browser-preflight.js.map +1 -0
- package/dist/schedule/fire/calls.d.ts +49 -0
- package/dist/schedule/fire/calls.d.ts.map +1 -0
- package/dist/schedule/fire/calls.js +114 -0
- package/dist/schedule/fire/calls.js.map +1 -0
- package/dist/schedule/fire/fire.d.ts +78 -0
- package/dist/schedule/fire/fire.d.ts.map +1 -0
- package/dist/schedule/fire/fire.js +490 -0
- package/dist/schedule/fire/fire.js.map +1 -0
- package/dist/schedule/fire/result.d.ts +13 -0
- package/dist/schedule/fire/result.d.ts.map +1 -0
- package/dist/schedule/fire/result.js +18 -0
- package/dist/schedule/fire/result.js.map +1 -0
- package/dist/schedule/fire/unattended-note.d.ts +22 -0
- package/dist/schedule/fire/unattended-note.d.ts.map +1 -0
- package/dist/schedule/fire/unattended-note.js +46 -0
- package/dist/schedule/fire/unattended-note.js.map +1 -0
- package/dist/schedule/floor.d.ts +136 -0
- package/dist/schedule/floor.d.ts.map +1 -0
- package/dist/schedule/floor.js +1289 -0
- package/dist/schedule/floor.js.map +1 -0
- package/dist/schedule/folder.d.ts +34 -0
- package/dist/schedule/folder.d.ts.map +1 -0
- package/dist/schedule/folder.js +84 -0
- package/dist/schedule/folder.js.map +1 -0
- package/dist/schedule/paths.d.ts +49 -0
- package/dist/schedule/paths.d.ts.map +1 -0
- package/dist/schedule/paths.js +53 -0
- package/dist/schedule/paths.js.map +1 -0
- package/dist/schedule/policy.d.ts +141 -0
- package/dist/schedule/policy.d.ts.map +1 -0
- package/dist/schedule/policy.js +378 -0
- package/dist/schedule/policy.js.map +1 -0
- package/dist/schedule/resume-command.d.ts +36 -0
- package/dist/schedule/resume-command.d.ts.map +1 -0
- package/dist/schedule/resume-command.js +72 -0
- package/dist/schedule/resume-command.js.map +1 -0
- package/dist/schedule/service/detect.d.ts +22 -0
- package/dist/schedule/service/detect.d.ts.map +1 -0
- package/dist/schedule/service/detect.js +29 -0
- package/dist/schedule/service/detect.js.map +1 -0
- package/dist/schedule/service/index.d.ts +48 -0
- package/dist/schedule/service/index.d.ts.map +1 -0
- package/dist/schedule/service/index.js +361 -0
- package/dist/schedule/service/index.js.map +1 -0
- package/dist/schedule/service/launchd.d.ts +24 -0
- package/dist/schedule/service/launchd.d.ts.map +1 -0
- package/dist/schedule/service/launchd.js +84 -0
- package/dist/schedule/service/launchd.js.map +1 -0
- package/dist/schedule/service/manifest.d.ts +44 -0
- package/dist/schedule/service/manifest.d.ts.map +1 -0
- package/dist/schedule/service/manifest.js +16 -0
- package/dist/schedule/service/manifest.js.map +1 -0
- package/dist/schedule/service/names.d.ts +9 -0
- package/dist/schedule/service/names.d.ts.map +1 -0
- package/dist/schedule/service/names.js +22 -0
- package/dist/schedule/service/names.js.map +1 -0
- package/dist/schedule/service/quote.d.ts +24 -0
- package/dist/schedule/service/quote.d.ts.map +1 -0
- package/dist/schedule/service/quote.js +64 -0
- package/dist/schedule/service/quote.js.map +1 -0
- package/dist/schedule/service/runner.d.ts +17 -0
- package/dist/schedule/service/runner.d.ts.map +1 -0
- package/dist/schedule/service/runner.js +31 -0
- package/dist/schedule/service/runner.js.map +1 -0
- package/dist/schedule/service/systemd.d.ts +37 -0
- package/dist/schedule/service/systemd.d.ts.map +1 -0
- package/dist/schedule/service/systemd.js +96 -0
- package/dist/schedule/service/systemd.js.map +1 -0
- package/dist/schedule/service/windows-task.d.ts +58 -0
- package/dist/schedule/service/windows-task.d.ts.map +1 -0
- package/dist/schedule/service/windows-task.js +199 -0
- package/dist/schedule/service/windows-task.js.map +1 -0
- package/dist/schedule/service/wsl.d.ts +69 -0
- package/dist/schedule/service/wsl.d.ts.map +1 -0
- package/dist/schedule/service/wsl.js +121 -0
- package/dist/schedule/service/wsl.js.map +1 -0
- package/dist/schedule/store/atomic.d.ts +43 -0
- package/dist/schedule/store/atomic.d.ts.map +1 -0
- package/dist/schedule/store/atomic.js +135 -0
- package/dist/schedule/store/atomic.js.map +1 -0
- package/dist/schedule/store/claims.d.ts +26 -0
- package/dist/schedule/store/claims.d.ts.map +1 -0
- package/dist/schedule/store/claims.js +57 -0
- package/dist/schedule/store/claims.js.map +1 -0
- package/dist/schedule/store/digest.d.ts +18 -0
- package/dist/schedule/store/digest.d.ts.map +1 -0
- package/dist/schedule/store/digest.js +139 -0
- package/dist/schedule/store/digest.js.map +1 -0
- package/dist/schedule/store/history.d.ts +21 -0
- package/dist/schedule/store/history.d.ts.map +1 -0
- package/dist/schedule/store/history.js +114 -0
- package/dist/schedule/store/history.js.map +1 -0
- package/dist/schedule/store/jobs.d.ts +61 -0
- package/dist/schedule/store/jobs.d.ts.map +1 -0
- package/dist/schedule/store/jobs.js +210 -0
- package/dist/schedule/store/jobs.js.map +1 -0
- package/dist/schedule/store/state.d.ts +19 -0
- package/dist/schedule/store/state.d.ts.map +1 -0
- package/dist/schedule/store/state.js +38 -0
- package/dist/schedule/store/state.js.map +1 -0
- package/dist/schedule/types.d.ts +283 -0
- package/dist/schedule/types.d.ts.map +1 -0
- package/dist/schedule/types.js +9 -0
- package/dist/schedule/types.js.map +1 -0
- package/dist/skills/catalog.d.ts +96 -0
- package/dist/skills/catalog.d.ts.map +1 -0
- package/dist/skills/catalog.js +226 -0
- package/dist/skills/catalog.js.map +1 -0
- package/dist/skills/directory.d.ts +38 -0
- package/dist/skills/directory.d.ts.map +1 -0
- package/dist/skills/directory.js +51 -0
- package/dist/skills/directory.js.map +1 -0
- package/dist/skills/save.d.ts +148 -0
- package/dist/skills/save.d.ts.map +1 -0
- package/dist/skills/save.js +344 -0
- package/dist/skills/save.js.map +1 -0
- package/dist/skills/store.d.ts +103 -16
- package/dist/skills/store.d.ts.map +1 -1
- package/dist/skills/store.js +227 -30
- package/dist/skills/store.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +609 -74
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/LiveActivity.d.ts +8 -1
- package/dist/tui/LiveActivity.d.ts.map +1 -1
- package/dist/tui/LiveActivity.js +20 -4
- package/dist/tui/LiveActivity.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +36 -4
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +135 -15
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/SaveSkillOverlay.d.ts +33 -0
- package/dist/tui/SaveSkillOverlay.d.ts.map +1 -0
- package/dist/tui/SaveSkillOverlay.js +118 -0
- package/dist/tui/SaveSkillOverlay.js.map +1 -0
- package/dist/tui/agent.d.ts +208 -6
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +371 -58
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/browser-notices.d.ts +33 -0
- package/dist/tui/browser-notices.d.ts.map +1 -0
- package/dist/tui/browser-notices.js +102 -0
- package/dist/tui/browser-notices.js.map +1 -0
- package/dist/tui/open-browser.d.ts +29 -1
- package/dist/tui/open-browser.d.ts.map +1 -1
- package/dist/tui/open-browser.js +83 -11
- package/dist/tui/open-browser.js.map +1 -1
- package/dist/tui/permission-review.d.ts +10 -1
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/permission-review.js +228 -22
- package/dist/tui/permission-review.js.map +1 -1
- package/dist/tui/schedule/host-commands.d.ts +34 -0
- package/dist/tui/schedule/host-commands.d.ts.map +1 -0
- package/dist/tui/schedule/host-commands.js +310 -0
- package/dist/tui/schedule/host-commands.js.map +1 -0
- package/dist/tui/schedule/integration.d.ts +62 -0
- package/dist/tui/schedule/integration.d.ts.map +1 -0
- package/dist/tui/schedule/integration.js +145 -0
- package/dist/tui/schedule/integration.js.map +1 -0
- package/dist/tui/schedule/loop-host.d.ts +42 -0
- package/dist/tui/schedule/loop-host.d.ts.map +1 -0
- package/dist/tui/schedule/loop-host.js +153 -0
- package/dist/tui/schedule/loop-host.js.map +1 -0
- package/dist/tui/schedule/resume.d.ts +122 -0
- package/dist/tui/schedule/resume.d.ts.map +1 -0
- package/dist/tui/schedule/resume.js +324 -0
- package/dist/tui/schedule/resume.js.map +1 -0
- package/dist/tui/schedule/startup.d.ts +11 -0
- package/dist/tui/schedule/startup.d.ts.map +1 -0
- package/dist/tui/schedule/startup.js +121 -0
- package/dist/tui/schedule/startup.js.map +1 -0
- package/dist/tui/schedule/tool-host.d.ts +58 -0
- package/dist/tui/schedule/tool-host.d.ts.map +1 -0
- package/dist/tui/schedule/tool-host.js +479 -0
- package/dist/tui/schedule/tool-host.js.map +1 -0
- package/dist/tui/skills/learning.d.ts +74 -0
- package/dist/tui/skills/learning.d.ts.map +1 -0
- package/dist/tui/skills/learning.js +131 -0
- package/dist/tui/skills/learning.js.map +1 -0
- package/dist/tui/skills/suggest-setting.d.ts +22 -0
- package/dist/tui/skills/suggest-setting.d.ts.map +1 -0
- package/dist/tui/skills/suggest-setting.js +55 -0
- package/dist/tui/skills/suggest-setting.js.map +1 -0
- package/dist/tui/skills/suggestion-ledger.d.ts +35 -0
- package/dist/tui/skills/suggestion-ledger.d.ts.map +1 -0
- package/dist/tui/skills/suggestion-ledger.js +52 -0
- package/dist/tui/skills/suggestion-ledger.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +20 -0
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +76 -2
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/turn-interruption.d.ts +20 -1
- package/dist/tui/turn-interruption.d.ts.map +1 -1
- package/dist/tui/turn-interruption.js +43 -0
- package/dist/tui/turn-interruption.js.map +1 -1
- package/dist/tui/types.d.ts +9 -1
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +7 -5
- package/skills/README.md +20 -0
- package/skills/browser-automation/SKILL.md +84 -0
- package/skills/schedule-task/SKILL.md +146 -0
- package/skills/skill-creator/SKILL.md +101 -0
package/dist/tui/App.js
CHANGED
|
@@ -16,8 +16,8 @@ import { TURN_LIMIT_FIELDS, turnLimitCommands } from './turn-limits-settings.js'
|
|
|
16
16
|
import { discoverProviders } from '../integrations/providers/discover.js';
|
|
17
17
|
import { ProviderSetup } from './ProviderSetup.js';
|
|
18
18
|
import { ToolOutputViewer } from './ToolOutputViewer.js';
|
|
19
|
-
import { relative } from 'node:path';
|
|
20
|
-
import { DEFAULT_MAX_GOAL_ROUNDS, DiskMessageFeedbackStore, GoalRoundLimitError, HostCommandRegistry, SessionGoalActivation, StaleGoalError, TurnCancelled, asSessionId, createAssistantMessage, createUserMessage, generateSessionId, generateTurnId, isCompactionMessage, kernelHostCommands, } from '@namzu/sdk';
|
|
19
|
+
import { join, relative } from 'node:path';
|
|
20
|
+
import { DEFAULT_MAX_GOAL_ROUNDS, DiskMessageFeedbackStore, GoalRoundLimitError, HostCommandRegistry, SessionGoalActivation, StaleGoalError, TurnCancelled, asSessionId, createAssistantMessage, createUserMessage, generateSessionId, generateTurnId, isCompactionMessage, kernelHostCommands, SKILL_TOOL_NAME, } from '@namzu/sdk';
|
|
21
21
|
import { Box, Text, useApp, useInput, useStdout, useWindowSize } from 'ink';
|
|
22
22
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
23
23
|
import { pinTrustedProjectPath, resolveTrustedProjectContext, } from '../config/trusted-project-context.js';
|
|
@@ -35,7 +35,7 @@ import { checkUpdates } from '../integrations/updates.js';
|
|
|
35
35
|
import { renderMemoryReport, renderMemorySaveResult, renderStoredMemorySection, renderTypedNoteResult, } from '../memory/presentation.js';
|
|
36
36
|
import { appendMemoryWithStatus, readMemory } from '../memory/store.js';
|
|
37
37
|
import { effectivePermissionMode, permissionModeDescription, permissionModeLabel, } from '../permissions/mode.js';
|
|
38
|
-
import { composeSkillsPrompt, discoverSkills, loadSkillBody } from '../skills/store.js';
|
|
38
|
+
import { NO_SKILLS_FOUND, composeSkillsPrompt, discoverSkills, loadSkillBody, renderSkillRoster, skillTierLabel, } from '../skills/store.js';
|
|
39
39
|
import { discoverUserCommands } from '../user-commands/store.js';
|
|
40
40
|
import { AgentCockpit, AgentNarrationBand, AgentTaskPanel, AgentTranscript, activeSubagentCohorts, agentCockpitIsWide, agentPhasePageSize, agentPhases, agentPickerPageSize, agentTranscriptPageSize, agentWorkflowPageSize, agentWorkflows, maxAgentTranscriptTailOffset, } from './AgentExplorer.js';
|
|
41
41
|
import { completionRow, launchReceipt, settleLine } from './agent-transcript-rows.js';
|
|
@@ -49,9 +49,16 @@ import { CopyPicker } from './CopyPicker.js';
|
|
|
49
49
|
import { EditPromptPicker } from './EditPromptPicker.js';
|
|
50
50
|
import { LiveActivity, formatElapsed } from './LiveActivity.js';
|
|
51
51
|
import { webCallTitle, webLiveStatus, webSettledLine } from './web-activity.js';
|
|
52
|
-
import { PermissionOverlay } from './PermissionOverlay.js';
|
|
52
|
+
import { PermissionOverlay, permissionAnswers } from './PermissionOverlay.js';
|
|
53
53
|
import { Picker } from './Picker.js';
|
|
54
54
|
import { ResumePicker } from './ResumePicker.js';
|
|
55
|
+
import { resolveNamzuHome } from '../integrations/state/home.js';
|
|
56
|
+
import { createScheduleIntegration } from './schedule/integration.js';
|
|
57
|
+
import { SaveSkillOverlay } from './SaveSkillOverlay.js';
|
|
58
|
+
import { SKILL_CREATOR_SKILL, buildSaveSkillTool, learnSkillPrompt, newSkillPrompt, } from '../skills/save.js';
|
|
59
|
+
import { SUGGESTIONS_STOPPED_NOTICE, createTurnActivity, judgeSkillSuggestion, observeTurnEvent, skillSuggestionNotice, } from './skills/learning.js';
|
|
60
|
+
import { FRESH_LEDGER, admitSuggestion, readSuggestionLedger, writeSuggestionLedger, } from './skills/suggestion-ledger.js';
|
|
61
|
+
import { setSkillSuggestions } from './skills/suggest-setting.js';
|
|
55
62
|
import { StatusBar } from './StatusBar.js';
|
|
56
63
|
import { isRepeatedNotice } from './notices.js';
|
|
57
64
|
import { checklistProgress } from './Checklist.js';
|
|
@@ -81,7 +88,8 @@ import { renderCheckpoints, renderRestore } from '../checkpoints/store.js';
|
|
|
81
88
|
import { expandFileMentions, listMentionableFiles } from './mentions.js';
|
|
82
89
|
import { openInBrowser } from './open-browser.js';
|
|
83
90
|
import { buildPermissionReview, buildPermissionSummary, permissionReviewPageRows, permissionReviewRefusal, permissionReviewRows, releasedByApproveAll, } from './permission-review.js';
|
|
84
|
-
import { describeTurnInterruption, describeTurnStop } from './turn-interruption.js';
|
|
91
|
+
import { PAUSED_TURN_LINES, describeTurnInterruption, describeTurnStop, handoffContinuationNote, } from './turn-interruption.js';
|
|
92
|
+
import { browserSiteNotes, describeBrowserHandoff, runBrowserSlash } from './browser-notices.js';
|
|
85
93
|
import { moveSelection } from './selection-window.js';
|
|
86
94
|
import { describeShellEscape, describeShellEscapeForModel, runShellEscape, shellEscapeCommand, } from './shell-escape.js';
|
|
87
95
|
import { baseBranchReviewPrompt, commitReviewPrompt, hostCommandNames, kernelCommandDescriptors, mergeHostCommands, renderAgents, renderOutcome, reviewPrompt, runSlash, } from './slashCommands.js';
|
|
@@ -314,6 +322,9 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
314
322
|
const initialConversationIdRef = useRef(initialCtx.initialConversationId);
|
|
315
323
|
const [history, setHistory] = useState([]);
|
|
316
324
|
const [state, setState] = useState('idle');
|
|
325
|
+
/** For the scheduler integration's loop timer, which reads it outside render. */
|
|
326
|
+
const stateRef = useRef(state);
|
|
327
|
+
stateRef.current = state;
|
|
317
328
|
const [phase, setPhase] = useState('probing');
|
|
318
329
|
const [session, setSession] = useState(null);
|
|
319
330
|
// Once a usable session publishes the transcript, temporary lifecycle
|
|
@@ -617,6 +628,25 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
617
628
|
}, []);
|
|
618
629
|
const [composerDraft, setComposerDraft] = useState(null);
|
|
619
630
|
const [composerHasDraft, setComposerHasDraft] = useState(false);
|
|
631
|
+
// A turn a tool paused for a person (`ToolResult.handoff`), while its
|
|
632
|
+
// notice is the newest thing on screen: Enter continues it, Esc stops it.
|
|
633
|
+
// The ref is what the key handler reads; the state is what the footer shows.
|
|
634
|
+
/**
|
|
635
|
+
* The stream of the turn running now, so a tool's own screen can close the
|
|
636
|
+
* reply it streamed before the tool ran. The kernel hands the tool's start
|
|
637
|
+
* to this loop only when its batch settles, so a reply streamed with a
|
|
638
|
+
* tool call stays pending while that tool asks the operator something; a
|
|
639
|
+
* row written then (the schedule tool's confirmation) cannot reach
|
|
640
|
+
* scrollback past the pending reply, and the top of a tall one was cut
|
|
641
|
+
* off while the operator was asked about it.
|
|
642
|
+
*/
|
|
643
|
+
const liveStreamRef = useRef(null);
|
|
644
|
+
const [handoffPark, setHandoffParkState] = useState(null);
|
|
645
|
+
const handoffParkRef = useRef(null);
|
|
646
|
+
const setHandoffPark = useCallback((value) => {
|
|
647
|
+
handoffParkRef.current = value;
|
|
648
|
+
setHandoffParkState(value);
|
|
649
|
+
}, []);
|
|
620
650
|
const composerDraftTokenRef = useRef(0);
|
|
621
651
|
/** Bounded child session projection published by the current AgentSession. */
|
|
622
652
|
const [subagents, setSubagentsState] = useState([]);
|
|
@@ -837,6 +867,16 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
837
867
|
* server's child process outlives the object that opened it.
|
|
838
868
|
*/
|
|
839
869
|
const previousSessionRef = useRef(null);
|
|
870
|
+
/** The profile `/browser profile` chose, kept across session rebuilds. */
|
|
871
|
+
const browserProfileRef = useRef(undefined);
|
|
872
|
+
/** The current session's browser, for notices written from event handlers. */
|
|
873
|
+
const browserControlRef = useRef(undefined);
|
|
874
|
+
/** The current session, for the review screen to name a computer-use control by its ref. */
|
|
875
|
+
const reviewSessionRef = useRef(null);
|
|
876
|
+
useEffect(() => {
|
|
877
|
+
browserControlRef.current = session?.browser;
|
|
878
|
+
reviewSessionRef.current = session;
|
|
879
|
+
}, [session]);
|
|
840
880
|
useEffect(() => {
|
|
841
881
|
const source = session?.subagents;
|
|
842
882
|
setAgentSurface(null);
|
|
@@ -910,6 +950,89 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
910
950
|
// turns attribute to the resumed conversation.
|
|
911
951
|
const sessionsRef = useRef(null);
|
|
912
952
|
const scopeRef = useRef(null);
|
|
953
|
+
/**
|
|
954
|
+
* Scheduled jobs and session loops (`./schedule/`). Built once; what it
|
|
955
|
+
* reads later — the transcript writer, the question and permission
|
|
956
|
+
* screens, the submit path, the session's model — is filled in each
|
|
957
|
+
* render through `scheduleLiveRef`.
|
|
958
|
+
*/
|
|
959
|
+
const scheduleLiveRef = useRef(null);
|
|
960
|
+
const scheduleRef = useRef(null);
|
|
961
|
+
if (scheduleRef.current === null) {
|
|
962
|
+
scheduleRef.current = createScheduleIntegration({
|
|
963
|
+
home: () => sessionsRef.current?.root ?? resolveNamzuHome(),
|
|
964
|
+
cwd: () => ctxRef.current.cwd,
|
|
965
|
+
extraRoots: () => ctxRef.current.additionalDirectories ?? [],
|
|
966
|
+
config: () => ctxRef.current,
|
|
967
|
+
model: () => scheduleLiveRef.current?.model,
|
|
968
|
+
sessionId: () => (conversationMaterializedRef.current ? scopeRef.current?.sessionId : undefined),
|
|
969
|
+
loopsFile: () => {
|
|
970
|
+
const sessions = sessionsRef.current;
|
|
971
|
+
const scope = scopeRef.current;
|
|
972
|
+
if (!sessions || !scope || !conversationMaterializedRef.current)
|
|
973
|
+
return undefined;
|
|
974
|
+
try {
|
|
975
|
+
return join(sessions.paths.sessionDir({ sessionId: scope.sessionId }), 'loops.json');
|
|
976
|
+
}
|
|
977
|
+
catch {
|
|
978
|
+
return undefined;
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
isIdle: () => stateRef.current === 'idle' && abortRef.current === null,
|
|
982
|
+
say: (text) => scheduleLiveRef.current?.say(text),
|
|
983
|
+
ask: (question, signal) => scheduleLiveRef.current?.ask(question, signal) ?? Promise.resolve({ kind: 'skip' }),
|
|
984
|
+
askPermission: (request) => scheduleLiveRef.current?.askPermission(request) ??
|
|
985
|
+
Promise.resolve({ kind: 'reject', feedback: 'Nobody can answer yet.' }),
|
|
986
|
+
submit: (text) => scheduleLiveRef.current?.submit(text),
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* `save_skill`'s confirmation. The tool is the TUI's alone (never exec, a
|
|
991
|
+
* scheduled run or a sub-agent), and its question is this screen, not the
|
|
992
|
+
* permission gate, so no permission mode answers it for the operator.
|
|
993
|
+
*/
|
|
994
|
+
const [saveSkillPrompt, setSaveSkillPromptState] = useState(null);
|
|
995
|
+
const saveSkillPromptRef = useRef(null);
|
|
996
|
+
const setSaveSkillPrompt = useCallback((next) => {
|
|
997
|
+
saveSkillPromptRef.current = next;
|
|
998
|
+
setSaveSkillPromptState(next);
|
|
999
|
+
}, []);
|
|
1000
|
+
const saveSkillNotifyRef = useRef(null);
|
|
1001
|
+
const saveSkillToolRef = useRef(null);
|
|
1002
|
+
if (saveSkillToolRef.current === null) {
|
|
1003
|
+
saveSkillToolRef.current = buildSaveSkillTool({
|
|
1004
|
+
cwd: () => ctxRef.current.cwd,
|
|
1005
|
+
config: () => ctxRef.current.skills,
|
|
1006
|
+
sessionId: () => conversationMaterializedRef.current ? scopeRef.current?.sessionId : undefined,
|
|
1007
|
+
confirm: (request, signal) => new Promise((resolve) => {
|
|
1008
|
+
if (signal?.aborted) {
|
|
1009
|
+
resolve('cancel');
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const entry = {
|
|
1013
|
+
request,
|
|
1014
|
+
resolve: (answer) => {
|
|
1015
|
+
signal?.removeEventListener('abort', onAbort);
|
|
1016
|
+
// The screen answers from its own key handler, which Ink calls
|
|
1017
|
+
// before App's (a child subscribes first). Clearing the ref now
|
|
1018
|
+
// would hand the same Esc or Ctrl+C to App, which reads it as
|
|
1019
|
+
// an interrupt of the turn this answer is for. Let the key's
|
|
1020
|
+
// dispatch finish first.
|
|
1021
|
+
queueMicrotask(() => {
|
|
1022
|
+
if (saveSkillPromptRef.current === entry)
|
|
1023
|
+
setSaveSkillPrompt(null);
|
|
1024
|
+
});
|
|
1025
|
+
resolve(answer);
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
const onAbort = () => entry.resolve('cancel');
|
|
1029
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
1030
|
+
setSaveSkillPrompt(entry);
|
|
1031
|
+
saveSkillNotifyRef.current?.();
|
|
1032
|
+
}),
|
|
1033
|
+
saved: ({ name, path }) => scheduleLiveRef.current?.say(`✎ Saved skill ${name} to ${path}. The model is offered it from the next turn; /skills ${name} activates it now.`),
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
913
1036
|
/**
|
|
914
1037
|
* A provider session can be ready before a person has started a conversation.
|
|
915
1038
|
* The scope still needs a mutable session cursor, but its generated id is
|
|
@@ -948,6 +1071,12 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
948
1071
|
const [goalDriveVersion, setGoalDriveVersion] = useState(0);
|
|
949
1072
|
const [goalStatus, setGoalStatus] = useState(null);
|
|
950
1073
|
const pendingGoalResumeRef = useRef(null);
|
|
1074
|
+
/**
|
|
1075
|
+
* The conversation `namzu resume <id>` opened, until the screen is ready to
|
|
1076
|
+
* look at it once: a scheduled run parked on a decision there is continued
|
|
1077
|
+
* then, so its permission prompt is on screen without typing `/resume`.
|
|
1078
|
+
*/
|
|
1079
|
+
const pendingScheduledResumeRef = useRef(null);
|
|
951
1080
|
const wakeGoalDriver = useCallback(() => setGoalDriveVersion((version) => version + 1), []);
|
|
952
1081
|
/**
|
|
953
1082
|
* Conversation writes in the order the operator produced them.
|
|
@@ -1160,6 +1289,56 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1160
1289
|
]);
|
|
1161
1290
|
return id;
|
|
1162
1291
|
}, [nextId]);
|
|
1292
|
+
/**
|
|
1293
|
+
* The skill proposal's per-conversation facts: whether this conversation
|
|
1294
|
+
* has proposed one, and whether it has used a skill. Keyed by the
|
|
1295
|
+
* conversation generation, so `/clear`, `/resume` and a fork start over.
|
|
1296
|
+
*/
|
|
1297
|
+
const learningRef = useRef({ generation: -1, proposed: false, skillUsed: false });
|
|
1298
|
+
/** `/skills save off|on` in this session, over whatever the config files say. */
|
|
1299
|
+
const skillSuggestOverrideRef = useRef(undefined);
|
|
1300
|
+
const conversationLearning = useCallback(() => {
|
|
1301
|
+
if (learningRef.current.generation !== conversationGenRef.current) {
|
|
1302
|
+
learningRef.current = {
|
|
1303
|
+
generation: conversationGenRef.current,
|
|
1304
|
+
proposed: false,
|
|
1305
|
+
skillUsed: false,
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
return learningRef.current;
|
|
1309
|
+
}, []);
|
|
1310
|
+
/**
|
|
1311
|
+
* After a turn settles: one dim line proposing to save it as a skill, when
|
|
1312
|
+
* the turn earned it (`judgeSkillSuggestion`) and the anti-nag ledger lets
|
|
1313
|
+
* it through. Costs nothing and saves nothing; `/skills save` does that.
|
|
1314
|
+
*/
|
|
1315
|
+
const offerSkillSuggestion = useCallback((st) => {
|
|
1316
|
+
if (!st.learning)
|
|
1317
|
+
return;
|
|
1318
|
+
const conversation = conversationLearning();
|
|
1319
|
+
const config = ctxRef.current.skills;
|
|
1320
|
+
const verdict = judgeSkillSuggestion(st.learning, {
|
|
1321
|
+
suggest: skillSuggestOverrideRef.current ?? config?.suggest,
|
|
1322
|
+
...(config?.suggestMinToolCalls !== undefined
|
|
1323
|
+
? { minToolCalls: config.suggestMinToolCalls }
|
|
1324
|
+
: {}),
|
|
1325
|
+
planMode: st.startedInPlanMode === true || permissionModeRef.current === 'plan',
|
|
1326
|
+
skillUsedInConversation: conversation.skillUsed,
|
|
1327
|
+
proposedInConversation: conversation.proposed,
|
|
1328
|
+
skillFlowTurn: st.skillFlow === true,
|
|
1329
|
+
});
|
|
1330
|
+
if (!verdict.suggest)
|
|
1331
|
+
return;
|
|
1332
|
+
const home = resolveNamzuHome();
|
|
1333
|
+
const admission = admitSuggestion(readSuggestionLedger(home));
|
|
1334
|
+
if (admission.show === 'nothing')
|
|
1335
|
+
return;
|
|
1336
|
+
conversation.proposed = true;
|
|
1337
|
+
writeSuggestionLedger(home, admission.next);
|
|
1338
|
+
pushMessage('system', admission.show === 'proposal'
|
|
1339
|
+
? skillSuggestionNotice(verdict.steps, verdict.tools)
|
|
1340
|
+
: SUGGESTIONS_STOPPED_NOTICE, false, '✻', undefined, theme.text.muted);
|
|
1341
|
+
}, [conversationLearning, pushMessage]);
|
|
1163
1342
|
/**
|
|
1164
1343
|
* `/agents [running|available|batches]`, answered by the delegation UI's
|
|
1165
1344
|
* `agentsSlashCommand` over this session's live monitor and the batches the
|
|
@@ -1532,7 +1711,10 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1532
1711
|
})();
|
|
1533
1712
|
}, [pushMessage]);
|
|
1534
1713
|
const activateSkill = useCallback((name) => {
|
|
1535
|
-
const info = discoverSkills({
|
|
1714
|
+
const info = discoverSkills({
|
|
1715
|
+
cwd: ctx.cwd,
|
|
1716
|
+
...(ctx.skills ? { config: ctx.skills } : {}),
|
|
1717
|
+
}).find((skill) => skill.name === name);
|
|
1536
1718
|
if (!info) {
|
|
1537
1719
|
pushMessage('system', `No skill named "${name}". See /skills.`);
|
|
1538
1720
|
return;
|
|
@@ -1543,12 +1725,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
1543
1725
|
...previous.filter((skill) => skill.name !== info.name),
|
|
1544
1726
|
{ name: info.name, body },
|
|
1545
1727
|
]);
|
|
1728
|
+
conversationLearning().skillUsed = true;
|
|
1546
1729
|
pushMessage('system', `Activated skill: ${info.name}`);
|
|
1547
1730
|
}
|
|
1548
1731
|
catch (error) {
|
|
1549
1732
|
pushMessage('system', `Could not load skill "${name}": ${error instanceof Error ? error.message : String(error)}`);
|
|
1550
1733
|
}
|
|
1551
|
-
}, [ctx.cwd, pushMessage]);
|
|
1734
|
+
}, [conversationLearning, ctx.cwd, ctx.skills, pushMessage]);
|
|
1552
1735
|
const removeStoredCredential = useCallback((target) => {
|
|
1553
1736
|
const path = credentialsPath();
|
|
1554
1737
|
const hadClaude = readStoredSubscriptionCredential() !== null;
|
|
@@ -2151,6 +2334,23 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2151
2334
|
const finalizeMessage = useCallback((id, finalContent) => {
|
|
2152
2335
|
setMessages((prev) => prev.map((m) => m.id === id ? { ...m, content: finalContent ?? m.content, pending: false } : m));
|
|
2153
2336
|
}, []);
|
|
2337
|
+
/**
|
|
2338
|
+
* Close the reply the running turn streamed before a tool took over, as a
|
|
2339
|
+
* tool's start does. For a tool that shows the operator something of its
|
|
2340
|
+
* own while it runs: the reply is complete by then (tools run after the
|
|
2341
|
+
* model's response ends), and left pending it holds every later row out
|
|
2342
|
+
* of scrollback. Text that arrives later starts a new reply.
|
|
2343
|
+
*/
|
|
2344
|
+
const closeLiveReply = useCallback(() => {
|
|
2345
|
+
const st = liveStreamRef.current;
|
|
2346
|
+
if (!st)
|
|
2347
|
+
return;
|
|
2348
|
+
flushStream(st);
|
|
2349
|
+
if (st.assistantId) {
|
|
2350
|
+
finalizeMessage(st.assistantId);
|
|
2351
|
+
st.assistantId = null;
|
|
2352
|
+
}
|
|
2353
|
+
}, [finalizeMessage, flushStream]);
|
|
2154
2354
|
// Open the SDK session store and select the durable conversation once.
|
|
2155
2355
|
// This is an admission gate for every startup, not optional persistence: a
|
|
2156
2356
|
// corrupt or split estate must not silently widen into cwd-local state and
|
|
@@ -2172,6 +2372,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2172
2372
|
? { text: persistedOutput, provenance: 'persisted' }
|
|
2173
2373
|
: null;
|
|
2174
2374
|
pendingGoalResumeRef.current = requestedConversationId;
|
|
2375
|
+
pendingScheduledResumeRef.current = requestedConversationId;
|
|
2175
2376
|
initialConversationIdRef.current = undefined;
|
|
2176
2377
|
conversationMaterializedRef.current = true;
|
|
2177
2378
|
}
|
|
@@ -2243,21 +2444,50 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2243
2444
|
if (signal?.aborted)
|
|
2244
2445
|
return;
|
|
2245
2446
|
const activeCtx = ctxRef.current;
|
|
2447
|
+
const primary = prefs.providers[0];
|
|
2448
|
+
if (primary && scheduleLiveRef.current) {
|
|
2449
|
+
scheduleLiveRef.current.model = {
|
|
2450
|
+
provider: primary.id,
|
|
2451
|
+
...(primary.model ? { model: primary.model } : {}),
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
const browserConfig = activeCtx.browser;
|
|
2455
|
+
const browserProfile = browserProfileRef.current ?? browserConfig?.defaultProfile;
|
|
2456
|
+
const browserOptions = browserConfig
|
|
2457
|
+
? {
|
|
2458
|
+
...(browserProfile ? { profile: browserProfile } : {}),
|
|
2459
|
+
...(browserConfig.engine ? { engine: browserConfig.engine } : {}),
|
|
2460
|
+
...(browserConfig.headless ? { headless: browserConfig.headless } : {}),
|
|
2461
|
+
...(browserConfig.sites ? { sites: browserConfig.sites } : {}),
|
|
2462
|
+
...(browserConfig.keepOpen ? { keepOpen: true } : {}),
|
|
2463
|
+
...(sessionsRef.current ? { home: sessionsRef.current.root } : {}),
|
|
2464
|
+
}
|
|
2465
|
+
: undefined;
|
|
2246
2466
|
const s = await createAgentSession(prefs, detectedNow, {
|
|
2247
2467
|
scope,
|
|
2248
2468
|
cwd: activeCtx.cwd,
|
|
2469
|
+
// A person is here to confirm: the model may propose scheduled jobs
|
|
2470
|
+
// and session loops (never in exec, a scheduled run or a sub-agent).
|
|
2471
|
+
extraTools: [
|
|
2472
|
+
...(scheduleRef.current?.tools() ?? []),
|
|
2473
|
+
...(saveSkillToolRef.current ? [saveSkillToolRef.current] : []),
|
|
2474
|
+
],
|
|
2249
2475
|
...(activeCtx.structuredOutput ? { structuredOutput: activeCtx.structuredOutput } : {}),
|
|
2250
2476
|
...(activeCtx.additionalDirectories
|
|
2251
2477
|
? { additionalDirectories: activeCtx.additionalDirectories }
|
|
2252
2478
|
: {}),
|
|
2253
2479
|
...(sessionsRef.current ? { stateRoot: sessionsRef.current.root } : {}),
|
|
2254
2480
|
enableComputerUse: true,
|
|
2481
|
+
// The browser tools, when the config did not switch them off. The
|
|
2482
|
+
// profile is the one `/browser profile` chose, else the config's.
|
|
2483
|
+
...(browserOptions ? { browser: browserOptions } : {}),
|
|
2255
2484
|
rules: activeCtx.rules,
|
|
2256
2485
|
...(sessionsRef.current
|
|
2257
2486
|
? { sessionGoals: sessionsRef.current.goals, conversationSessions: sessionsRef.current }
|
|
2258
2487
|
: {}),
|
|
2259
2488
|
...(activeCtx.mcpServers ? { mcpServers: activeCtx.mcpServers } : {}),
|
|
2260
2489
|
...(activeCtx.plugins ? { plugins: activeCtx.plugins } : {}),
|
|
2490
|
+
...(activeCtx.skills ? { skills: activeCtx.skills } : {}),
|
|
2261
2491
|
...(activeCtx.web ? { web: activeCtx.web } : {}),
|
|
2262
2492
|
...(activeCtx.hooks ? { hooks: activeCtx.hooks } : {}),
|
|
2263
2493
|
...(activeCtx.compaction ? { compaction: activeCtx.compaction } : {}),
|
|
@@ -2276,6 +2506,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2276
2506
|
// question when a decision is genuinely theirs.
|
|
2277
2507
|
askUser: true,
|
|
2278
2508
|
allowModelSwitch: true,
|
|
2509
|
+
// The user is at this desktop, so the model may open a page for them.
|
|
2510
|
+
openUrl: true,
|
|
2279
2511
|
});
|
|
2280
2512
|
if (signal?.aborted) {
|
|
2281
2513
|
void s.close();
|
|
@@ -2862,7 +3094,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2862
3094
|
return;
|
|
2863
3095
|
}
|
|
2864
3096
|
await session.abandonTurn(active.turnId, reason);
|
|
2865
|
-
pushMessage('system',
|
|
3097
|
+
pushMessage('system', PAUSED_TURN_LINES.abandoned);
|
|
2866
3098
|
}
|
|
2867
3099
|
catch (err) {
|
|
2868
3100
|
pushMessage('system', `Could not abandon the turn: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2875,7 +3107,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2875
3107
|
*/
|
|
2876
3108
|
// `applyEvent` is declared further down; the resume path reads it at call time.
|
|
2877
3109
|
const applyEventRef = useRef(null);
|
|
2878
|
-
const resumeActiveTurn = useCallback(async () => {
|
|
3110
|
+
const resumeActiveTurn = useCallback(async (resume) => {
|
|
2879
3111
|
const sessions = sessionsRef.current;
|
|
2880
3112
|
const scope = scopeRef.current;
|
|
2881
3113
|
if (!sessions || !scope || !conversationMaterializedRef.current || !session?.hasProvider)
|
|
@@ -2887,6 +3119,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2887
3119
|
pushMessage('system', 'Wait for the running turn to finish before resuming the parked one.');
|
|
2888
3120
|
return true;
|
|
2889
3121
|
}
|
|
3122
|
+
setHandoffPark(null);
|
|
2890
3123
|
const ac = new AbortController();
|
|
2891
3124
|
abortRef.current = ac;
|
|
2892
3125
|
setState('thinking');
|
|
@@ -2900,15 +3133,58 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2900
3133
|
sessionId: scope.sessionId,
|
|
2901
3134
|
notification: null,
|
|
2902
3135
|
};
|
|
2903
|
-
|
|
3136
|
+
liveStreamRef.current = st;
|
|
3137
|
+
let scheduledRun = false;
|
|
3138
|
+
let restoreBrowser;
|
|
2904
3139
|
try {
|
|
3140
|
+
// A scheduled run parked on a decision: the operator answers the
|
|
3141
|
+
// parked batch here, and the turn continues under the job's rules.
|
|
3142
|
+
const scheduled = active.paused
|
|
3143
|
+
? await scheduleRef.current?.prepareResume(permissionModeRef.current, {
|
|
3144
|
+
cwd: ctxRef.current.cwd,
|
|
3145
|
+
roots: session.directories?.list() ?? [],
|
|
3146
|
+
sandboxed: session.sandbox.workspace !== 'host',
|
|
3147
|
+
// The resumed turn runs on the job's provider: one this
|
|
3148
|
+
// session has no credential for is refused before asking.
|
|
3149
|
+
providers: detected.map((item) => item.entry.id),
|
|
3150
|
+
browser: session.browser !== undefined,
|
|
3151
|
+
})
|
|
3152
|
+
: undefined;
|
|
3153
|
+
if (scheduled && 'leave' in scheduled) {
|
|
3154
|
+
pushMessage('system', 'The scheduled run stays waiting. /resume asks again.');
|
|
3155
|
+
return true;
|
|
3156
|
+
}
|
|
3157
|
+
if (scheduled && 'abandon' in scheduled) {
|
|
3158
|
+
scheduledRun = true;
|
|
3159
|
+
if (!session.abandonTurn)
|
|
3160
|
+
throw new Error('this session cannot abandon turns');
|
|
3161
|
+
await session.abandonTurn(active.turnId, scheduled.abandon);
|
|
3162
|
+
pushMessage('system', PAUSED_TURN_LINES.abandonedScheduled);
|
|
3163
|
+
return true;
|
|
3164
|
+
}
|
|
3165
|
+
scheduledRun = scheduled !== undefined;
|
|
3166
|
+
const { browser: jobBrowser, ...scheduledParams } = scheduled ?? {};
|
|
3167
|
+
// A job's browser grant: this turn drives the job's profile, held
|
|
3168
|
+
// to its sites, and the session's own comes back when it ends.
|
|
3169
|
+
if (jobBrowser && session.browser) {
|
|
3170
|
+
restoreBrowser = await session.browser.runAs(jobBrowser);
|
|
3171
|
+
pushMessage('system', `The browser uses the job’s profile ${jobBrowser.profile} for this run, on its sites only.`);
|
|
3172
|
+
}
|
|
3173
|
+
// Said once the turn really continues: a scheduled park can still
|
|
3174
|
+
// be left waiting or abandoned above.
|
|
3175
|
+
pushMessage('system', PAUSED_TURN_LINES.resuming, false, '▶');
|
|
2905
3176
|
for await (const event of session.resumePaused({
|
|
2906
3177
|
turnId: active.turnId,
|
|
2907
3178
|
signal: ac.signal,
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
3179
|
+
...(scheduled ? scheduledParams : {
|
|
3180
|
+
// The operator's mode, read at every decision like a new turn's:
|
|
3181
|
+
// `/resume` in plan mode stays read-only, and so do its children.
|
|
3182
|
+
permissionMode: permissionModeRef.current,
|
|
3183
|
+
currentPermissionMode: () => permissionModeRef.current,
|
|
3184
|
+
...(resume?.handoffReason
|
|
3185
|
+
? { systemNote: handoffContinuationNote(resume.handoffReason) }
|
|
3186
|
+
: {}),
|
|
3187
|
+
}),
|
|
2912
3188
|
})) {
|
|
2913
3189
|
applyEventRef.current?.(event, st);
|
|
2914
3190
|
}
|
|
@@ -2926,9 +3202,38 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2926
3202
|
if (abortRef.current === ac)
|
|
2927
3203
|
abortRef.current = null;
|
|
2928
3204
|
setState('idle');
|
|
3205
|
+
// A scheduled run answered here records its end in its job now —
|
|
3206
|
+
// completed, failed or cancelled — not at a scheduler's next tick,
|
|
3207
|
+
// which may never come. One that parked again stays waiting.
|
|
3208
|
+
if (scheduledRun)
|
|
3209
|
+
await scheduleRef.current?.settleAnswered();
|
|
3210
|
+
await restoreBrowser?.().catch(() => undefined);
|
|
2929
3211
|
}
|
|
2930
3212
|
return true;
|
|
2931
|
-
}, [finalizeMessage, flushStream, pushMessage, session, state]);
|
|
3213
|
+
}, [detected, finalizeMessage, flushStream, pushMessage, session, setHandoffPark, state]);
|
|
3214
|
+
// `namzu resume <id>` of a scheduled run parked on a decision: what the
|
|
3215
|
+
// notification and `/schedule` tell the operator to run. Continue it once
|
|
3216
|
+
// the screen is ready, exactly as `/resume` would, so the parked call is on
|
|
3217
|
+
// the permission screen; the job and session checks in `prepareResume`
|
|
3218
|
+
// still refuse a session that does not run as the job does. Any other
|
|
3219
|
+
// parked or interrupted turn is left for the operator's own `/resume`.
|
|
3220
|
+
useEffect(() => {
|
|
3221
|
+
const requested = pendingScheduledResumeRef.current;
|
|
3222
|
+
if (!requested || phase !== 'ready' || state !== 'idle' || !session?.hasProvider)
|
|
3223
|
+
return;
|
|
3224
|
+
const sessions = sessionsRef.current;
|
|
3225
|
+
if (!sessions || scopeRef.current?.sessionId !== requested || abortRef.current)
|
|
3226
|
+
return;
|
|
3227
|
+
pendingScheduledResumeRef.current = null;
|
|
3228
|
+
void (async () => {
|
|
3229
|
+
const active = await activeConversationTurn(sessions, asSessionId(requested)).catch(() => undefined);
|
|
3230
|
+
if (!active?.paused || !(await scheduleRef.current?.isParked()))
|
|
3231
|
+
return;
|
|
3232
|
+
if (scopeRef.current?.sessionId !== requested)
|
|
3233
|
+
return;
|
|
3234
|
+
await resumeActiveTurn();
|
|
3235
|
+
})();
|
|
3236
|
+
}, [phase, resumeActiveTurn, session, state]);
|
|
2932
3237
|
const doResume = useCallback(async () => {
|
|
2933
3238
|
const sessions = sessionsRef.current ?? (await ensureSessions(), sessionsRef.current);
|
|
2934
3239
|
if (!sessions) {
|
|
@@ -2979,7 +3284,10 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
2979
3284
|
// only on its own.
|
|
2980
3285
|
const kept = [];
|
|
2981
3286
|
for (const pending of permissionQueueRef.current.splice(0)) {
|
|
2982
|
-
|
|
3287
|
+
// Nor a question about sharing the screen, which only its own
|
|
3288
|
+
// answer settles.
|
|
3289
|
+
if (pending.permission.screenConsent !== true &&
|
|
3290
|
+
releasedByApproveAll(pending.permission.toolCalls))
|
|
2983
3291
|
pending.resolve(decision);
|
|
2984
3292
|
else
|
|
2985
3293
|
kept.push(pending);
|
|
@@ -3595,11 +3903,14 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3595
3903
|
feedback: permissionReviewRefusal(review.reason),
|
|
3596
3904
|
});
|
|
3597
3905
|
}
|
|
3598
|
-
const summary = buildPermissionSummary(review.text
|
|
3906
|
+
const summary = buildPermissionSummary(review.text, {
|
|
3907
|
+
describeUiRef: (ref) => reviewSessionRef.current?.describeComputerUseRef?.(ref),
|
|
3908
|
+
});
|
|
3909
|
+
const siteNotes = browserSiteNotes(req.toolCalls, browserControlRef.current?.status());
|
|
3599
3910
|
return new Promise((resolve) => {
|
|
3600
3911
|
if (permissionResolveRef.current) {
|
|
3601
3912
|
permissionQueueRef.current.push({
|
|
3602
|
-
permission: { ...req, review: review.text, summary },
|
|
3913
|
+
permission: { ...req, review: review.text, summary, siteNotes },
|
|
3603
3914
|
resolve,
|
|
3604
3915
|
});
|
|
3605
3916
|
setQueuedPermissionCount(permissionQueueRef.current.length);
|
|
@@ -3609,7 +3920,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3609
3920
|
permissionOpenedAtRef.current = Date.now();
|
|
3610
3921
|
setPermissionReviewOffset(0);
|
|
3611
3922
|
setPermissionDetailsOpen(!summary.complete);
|
|
3612
|
-
setPermission({ ...req, review: review.text, summary });
|
|
3923
|
+
setPermission({ ...req, review: review.text, summary, siteNotes });
|
|
3613
3924
|
setState('awaiting-permission');
|
|
3614
3925
|
sendTerminalNotification({ kind: 'approval-required' });
|
|
3615
3926
|
});
|
|
@@ -3620,15 +3931,26 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3620
3931
|
* and the tool's promise waits on the row the operator picks. One question
|
|
3621
3932
|
* at a time: the tool is not concurrency-safe, so a second cannot arrive
|
|
3622
3933
|
* while the first is up.
|
|
3934
|
+
*
|
|
3935
|
+
* `signal`, when the caller has one (the `schedule` tool's confirmation,
|
|
3936
|
+
* which waits on a person and so is given the tool's own abort signal),
|
|
3937
|
+
* is honoured the way `save_skill`'s screen honours the same signal: an
|
|
3938
|
+
* abort — the tool's own deadline elapsing, or the turn stopping —
|
|
3939
|
+
* answers as `abort` and takes the picker off screen, rather than
|
|
3940
|
+
* leaving it live after the tool has already given up on the answer.
|
|
3623
3941
|
*/
|
|
3624
|
-
const askQuestion = useCallback((question) => new Promise((resolve) => {
|
|
3942
|
+
const askQuestion = useCallback((question, signal) => new Promise((resolve) => {
|
|
3943
|
+
if (signal?.aborted) {
|
|
3944
|
+
resolve({ kind: 'abort' });
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3947
|
+
closeLiveReply();
|
|
3625
3948
|
questionRef.current = { question, resolve };
|
|
3626
3949
|
const values = [
|
|
3627
3950
|
...question.options.map((option) => option.id),
|
|
3628
3951
|
...(question.allowFreeText ? [FREE_TEXT_ANSWER] : []),
|
|
3629
3952
|
];
|
|
3630
|
-
|
|
3631
|
-
setChoicePicker({
|
|
3953
|
+
const picker = {
|
|
3632
3954
|
kind: 'user-question',
|
|
3633
3955
|
title: question.question,
|
|
3634
3956
|
notice: question.header
|
|
@@ -3644,9 +3966,17 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3644
3966
|
? [{ label: 'Something else…', description: 'Answer in your own words' }]
|
|
3645
3967
|
: []),
|
|
3646
3968
|
],
|
|
3647
|
-
}
|
|
3969
|
+
};
|
|
3970
|
+
const onAbort = () => {
|
|
3971
|
+
if (choicePickerRef.current === picker)
|
|
3972
|
+
setChoicePicker(null);
|
|
3973
|
+
resolveQuestion({ kind: 'abort' });
|
|
3974
|
+
};
|
|
3975
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
3976
|
+
setSelectedChoice(0);
|
|
3977
|
+
setChoicePicker(picker);
|
|
3648
3978
|
sendTerminalNotification({ kind: 'approval-required' });
|
|
3649
|
-
}), [sendTerminalNotification, setChoicePicker, setSelectedChoice]);
|
|
3979
|
+
}), [closeLiveReply, sendTerminalNotification, setChoicePicker, setSelectedChoice, resolveQuestion]);
|
|
3650
3980
|
/**
|
|
3651
3981
|
* Grow this turn's open task block, or open one. The whole plan rides along
|
|
3652
3982
|
* so the block shows the checklist as it stood after the operation; a
|
|
@@ -3670,6 +4000,11 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3670
4000
|
// `st` carries the streaming-assistant bubble id + accumulated text across
|
|
3671
4001
|
// events within a turn/stream.
|
|
3672
4002
|
const applyEvent = useCallback((event, st) => {
|
|
4003
|
+
if (st.learning)
|
|
4004
|
+
observeTurnEvent(st.learning, event);
|
|
4005
|
+
if (event.kind === 'tool-start' && event.toolName === SKILL_TOOL_NAME) {
|
|
4006
|
+
conversationLearning().skillUsed = true;
|
|
4007
|
+
}
|
|
3673
4008
|
const ensureAssistant = () => {
|
|
3674
4009
|
if (!st.assistantId)
|
|
3675
4010
|
st.assistantId = pushMessage('assistant', '', true);
|
|
@@ -3935,6 +4270,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
3935
4270
|
setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
|
|
3936
4271
|
break;
|
|
3937
4272
|
}
|
|
4273
|
+
if (event.cancelled) {
|
|
4274
|
+
// A No on the tool's own screen: the person's answer, not a failure.
|
|
4275
|
+
pushMessage('tool', done?.label ?? formatToolCall(event.toolName, event.summary), false, '○', undefined, theme.text.muted, event.durationMs !== undefined ? formatElapsed(event.durationMs) : undefined);
|
|
4276
|
+
pushMessage('tool', event.resultLabel ?? 'Cancelled', false, '⎿');
|
|
4277
|
+
setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
|
|
4278
|
+
break;
|
|
4279
|
+
}
|
|
3938
4280
|
const catalogue = !event.isError && event.toolName === 'agent_models' && event.output !== undefined
|
|
3939
4281
|
? modelCatalogueView(event.output) : undefined;
|
|
3940
4282
|
if (catalogue !== undefined) {
|
|
@@ -4091,6 +4433,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4091
4433
|
if (closing)
|
|
4092
4434
|
pushMessage('system', closing, false, '✻', undefined, theme.text.muted);
|
|
4093
4435
|
}
|
|
4436
|
+
offerSkillSuggestion(st);
|
|
4094
4437
|
break;
|
|
4095
4438
|
}
|
|
4096
4439
|
case 'paused':
|
|
@@ -4102,7 +4445,22 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4102
4445
|
st.outcome = 'stopped';
|
|
4103
4446
|
st.queuePauseOutcome = 'paused';
|
|
4104
4447
|
st.notification = { kind: 'turn-settled', outcome: 'stopped' };
|
|
4105
|
-
|
|
4448
|
+
if (event.handoff) {
|
|
4449
|
+
// Nothing failed and nothing needs approving: a tool needs the
|
|
4450
|
+
// operator to do something first. Continuing is one key.
|
|
4451
|
+
const browser = browserControlRef.current;
|
|
4452
|
+
const browserStatus = browser?.status();
|
|
4453
|
+
const browserNotice = describeBrowserHandoff(event.handoff, browserStatus);
|
|
4454
|
+
// Without a window the sign-in happens in `namzu browser
|
|
4455
|
+
// login`, which needs the profile this session holds.
|
|
4456
|
+
if (browserNotice && browser && browserStatus?.headless)
|
|
4457
|
+
void browser.release();
|
|
4458
|
+
pushMessage('system', browserNotice ??
|
|
4459
|
+
`${describeTurnInterruption(event)}\nWhen that is done, press Enter to continue · Esc to stop.`, false, '‖');
|
|
4460
|
+
setHandoffPark({ turnId: event.turnId, reason: event.handoff.reason });
|
|
4461
|
+
}
|
|
4462
|
+
else
|
|
4463
|
+
pushMessage('system', describeTurnInterruption(event), false, '‖');
|
|
4106
4464
|
break;
|
|
4107
4465
|
case 'error':
|
|
4108
4466
|
closeAssistant();
|
|
@@ -4115,7 +4473,16 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4115
4473
|
st.notification = null;
|
|
4116
4474
|
break;
|
|
4117
4475
|
}
|
|
4118
|
-
}, [
|
|
4476
|
+
}, [
|
|
4477
|
+
appendToMessage,
|
|
4478
|
+
conversationLearning,
|
|
4479
|
+
finalizeMessage,
|
|
4480
|
+
flushStream,
|
|
4481
|
+
offerSkillSuggestion,
|
|
4482
|
+
pushMessage,
|
|
4483
|
+
setHandoffPark,
|
|
4484
|
+
writeTaskBlock,
|
|
4485
|
+
]);
|
|
4119
4486
|
applyEventRef.current = applyEvent;
|
|
4120
4487
|
const runTurn = useCallback(async (prompt) => {
|
|
4121
4488
|
// A passive queue effect can still hold the previous render's session
|
|
@@ -4243,7 +4610,19 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4243
4610
|
outcome: null,
|
|
4244
4611
|
sessionId: destination,
|
|
4245
4612
|
notification: null,
|
|
4613
|
+
...(prompt.kind === 'human'
|
|
4614
|
+
? {
|
|
4615
|
+
learning: createTurnActivity(),
|
|
4616
|
+
...(prompt.skillFlow ? { skillFlow: true } : {}),
|
|
4617
|
+
startedInPlanMode: permissionModeRef.current === 'plan',
|
|
4618
|
+
}
|
|
4619
|
+
: {}),
|
|
4246
4620
|
};
|
|
4621
|
+
liveStreamRef.current = st;
|
|
4622
|
+
// A skill activated with /skills <name> stays in the prompt of every
|
|
4623
|
+
// later conversation too; a turn run under one already used a skill.
|
|
4624
|
+
if (st.learning && activeSkills.length > 0)
|
|
4625
|
+
st.learning.skillUsed = true;
|
|
4247
4626
|
const ac = new AbortController();
|
|
4248
4627
|
const turnToken = {};
|
|
4249
4628
|
let abnormalTerminal;
|
|
@@ -4819,6 +5198,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4819
5198
|
pushMessage('system', `Conversation history is being ${operation}. Wait for it to finish before sending another command or prompt.`);
|
|
4820
5199
|
return;
|
|
4821
5200
|
}
|
|
5201
|
+
// The operator moved on; a parked turn is still there for /resume.
|
|
5202
|
+
setHandoffPark(null);
|
|
4822
5203
|
setHistory((prev) => [...prev, value]);
|
|
4823
5204
|
const selectionIntent = !attachments?.length ? parseModelSelectionIntent(value) : undefined;
|
|
4824
5205
|
if (selectionIntent) {
|
|
@@ -4859,6 +5240,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
4859
5240
|
// including the queue — so a command-driven turn is not a second way
|
|
4860
5241
|
// to run one.
|
|
4861
5242
|
let outgoing = value;
|
|
5243
|
+
let skillFlow = false;
|
|
4862
5244
|
const slash = runSlash(value, slashCtx, hostCommands);
|
|
4863
5245
|
if (slash) {
|
|
4864
5246
|
switch (slash.kind) {
|
|
@@ -5330,20 +5712,16 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5330
5712
|
// distinction the headless commands get from `--cwd`. They are
|
|
5331
5713
|
// the same value today, and were the same value in `exec --json`
|
|
5332
5714
|
// too until they were not.
|
|
5333
|
-
const skills = discoverSkills({
|
|
5715
|
+
const skills = discoverSkills({
|
|
5716
|
+
cwd: ctx.cwd,
|
|
5717
|
+
...(ctx.skills ? { config: ctx.skills } : {}),
|
|
5718
|
+
});
|
|
5334
5719
|
if (skills.length === 0) {
|
|
5335
|
-
pushMessage('system',
|
|
5720
|
+
pushMessage('system', NO_SKILLS_FOUND);
|
|
5336
5721
|
return;
|
|
5337
5722
|
}
|
|
5338
5723
|
const activeNames = new Set(activeSkills.map((s) => s.name));
|
|
5339
|
-
|
|
5340
|
-
// A refused skill is shown with its reason rather than hidden.
|
|
5341
|
-
// Dropping it silently would leave someone wondering where a
|
|
5342
|
-
// file they can see on disk went.
|
|
5343
|
-
s.problem
|
|
5344
|
-
? `! ${s.name} — ${s.problem}`
|
|
5345
|
-
: `${activeNames.has(s.name) ? '● ' : '○ '}${s.name} — ${s.description}`);
|
|
5346
|
-
pushMessage('system', `Skills (● active):\n ${lines.join('\n ')}`);
|
|
5724
|
+
pushMessage('system', renderSkillRoster(skills, activeNames));
|
|
5347
5725
|
return;
|
|
5348
5726
|
}
|
|
5349
5727
|
case 'plugins': {
|
|
@@ -5372,9 +5750,12 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5372
5750
|
return;
|
|
5373
5751
|
}
|
|
5374
5752
|
case 'skill-picker': {
|
|
5375
|
-
const skills = discoverSkills({
|
|
5753
|
+
const skills = discoverSkills({
|
|
5754
|
+
cwd: ctx.cwd,
|
|
5755
|
+
...(ctx.skills ? { config: ctx.skills } : {}),
|
|
5756
|
+
});
|
|
5376
5757
|
if (skills.length === 0) {
|
|
5377
|
-
pushMessage('system',
|
|
5758
|
+
pushMessage('system', NO_SKILLS_FOUND);
|
|
5378
5759
|
return;
|
|
5379
5760
|
}
|
|
5380
5761
|
const activeNames = new Set(activeSkills.map((skill) => skill.name));
|
|
@@ -5387,7 +5768,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5387
5768
|
label: skill.name,
|
|
5388
5769
|
description: skill.problem
|
|
5389
5770
|
? `Unavailable: ${skill.problem}`
|
|
5390
|
-
: `${skill.description} · ${skill.source}`,
|
|
5771
|
+
: `${skill.description} · ${skill.source} (${skillTierLabel(skill.tier)})`,
|
|
5391
5772
|
current: activeNames.has(skill.name),
|
|
5392
5773
|
})),
|
|
5393
5774
|
});
|
|
@@ -5397,6 +5778,46 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5397
5778
|
activateSkill(slash.name);
|
|
5398
5779
|
return;
|
|
5399
5780
|
}
|
|
5781
|
+
case 'new-skill':
|
|
5782
|
+
case 'save-skill': {
|
|
5783
|
+
const creator = discoverSkills({
|
|
5784
|
+
cwd: ctx.cwd,
|
|
5785
|
+
...(ctx.skills ? { config: ctx.skills } : {}),
|
|
5786
|
+
}).find((skill) => skill.name === SKILL_CREATOR_SKILL);
|
|
5787
|
+
if (!creator || creator.problem || creator.invocation === 'operator') {
|
|
5788
|
+
pushMessage('system', `The ${SKILL_CREATOR_SKILL} skill is not available to the model here${creator?.problem ? ` (${creator.problem})` : ''}. Turn the built-in skills back on (skills.builtin) or write a SKILL.md by hand under ~/.namzu/skills/<name>/.`);
|
|
5789
|
+
return;
|
|
5790
|
+
}
|
|
5791
|
+
// Falls through to the queue-or-send a typed message takes, as
|
|
5792
|
+
// `prompt` does below.
|
|
5793
|
+
skillFlow = true;
|
|
5794
|
+
if (slash.kind === 'new-skill')
|
|
5795
|
+
outgoing = newSkillPrompt(slash.idea);
|
|
5796
|
+
else {
|
|
5797
|
+
outgoing = learnSkillPrompt(slash.name);
|
|
5798
|
+
// The proposal was taken up: this conversation proposes no
|
|
5799
|
+
// more, and the ignored-in-a-row count starts again.
|
|
5800
|
+
conversationLearning().proposed = true;
|
|
5801
|
+
const home = resolveNamzuHome();
|
|
5802
|
+
const ledger = readSuggestionLedger(home);
|
|
5803
|
+
if (ledger.unanswered > 0)
|
|
5804
|
+
writeSuggestionLedger(home, { ...ledger, unanswered: 0 });
|
|
5805
|
+
}
|
|
5806
|
+
break;
|
|
5807
|
+
}
|
|
5808
|
+
case 'skill-suggestions': {
|
|
5809
|
+
// The session follows the choice whatever the files say; the
|
|
5810
|
+
// message says whether a later start will too.
|
|
5811
|
+
skillSuggestOverrideRef.current = slash.on;
|
|
5812
|
+
if (slash.on)
|
|
5813
|
+
writeSuggestionLedger(resolveNamzuHome(), FRESH_LEDGER);
|
|
5814
|
+
const profile = ctx.configDebug?.selectedProfile?.name;
|
|
5815
|
+
pushMessage('system', setSkillSuggestions(slash.on, {
|
|
5816
|
+
cwd: ctx.cwd,
|
|
5817
|
+
...(profile ? { profile } : {}),
|
|
5818
|
+
}).message);
|
|
5819
|
+
return;
|
|
5820
|
+
}
|
|
5400
5821
|
case 'resume':
|
|
5401
5822
|
void doResume();
|
|
5402
5823
|
return;
|
|
@@ -5457,6 +5878,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5457
5878
|
case 'none':
|
|
5458
5879
|
return;
|
|
5459
5880
|
case 'host-command': {
|
|
5881
|
+
if (scheduleRef.current?.handleSlash(slash.name, slash.args))
|
|
5882
|
+
return;
|
|
5460
5883
|
// Dispatched through the kernel's registry, built with what
|
|
5461
5884
|
// THIS session can answer from. The descriptors used for
|
|
5462
5885
|
// the merge above carry no store — they are names — so the
|
|
@@ -5761,6 +6184,21 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5761
6184
|
runConversationExport({ kind: 'file', path: slash.path });
|
|
5762
6185
|
return;
|
|
5763
6186
|
}
|
|
6187
|
+
case 'browser': {
|
|
6188
|
+
const generation = conversationGenRef.current;
|
|
6189
|
+
void runBrowserSlash(session?.browser, slash.args, {
|
|
6190
|
+
busy: state !== 'idle' || abortRef.current !== null,
|
|
6191
|
+
}).then((answer) => {
|
|
6192
|
+
// A later session keeps the operator's choice: `/model`
|
|
6193
|
+
// rebuilds the session, and the profile goes with it.
|
|
6194
|
+
if (answer.profile)
|
|
6195
|
+
browserProfileRef.current = answer.profile;
|
|
6196
|
+
if (conversationGenRef.current !== generation)
|
|
6197
|
+
return;
|
|
6198
|
+
pushMessage('system', answer.text);
|
|
6199
|
+
});
|
|
6200
|
+
return;
|
|
6201
|
+
}
|
|
5764
6202
|
default: {
|
|
5765
6203
|
// Exhaustive on purpose. Without it a `SlashAction` kind added
|
|
5766
6204
|
// and not handled here falls out of the switch into the send
|
|
@@ -5786,8 +6224,16 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5786
6224
|
kind: 'human',
|
|
5787
6225
|
text: outgoing,
|
|
5788
6226
|
...(attachments && attachments.length > 0 ? { attachments: [...attachments] } : {}),
|
|
6227
|
+
...(skillFlow ? { skillFlow: true } : {}),
|
|
5789
6228
|
};
|
|
5790
|
-
|
|
6229
|
+
// `/skills save` and `/skills new` are turns of their own: steered into
|
|
6230
|
+
// a running turn, they would lose the flag that keeps the skill-making
|
|
6231
|
+
// turn from being proposed as a skill, and the model would read them
|
|
6232
|
+
// mid-task. They wait in the queue for the turn to end.
|
|
6233
|
+
if (skillFlow && activeTurnInboxRef.current) {
|
|
6234
|
+
pushMessage('system', 'Queued: this runs when the current turn ends.');
|
|
6235
|
+
}
|
|
6236
|
+
if (mode === 'submit' && !skillFlow) {
|
|
5791
6237
|
const inbox = activeTurnInboxRef.current;
|
|
5792
6238
|
if (inbox) {
|
|
5793
6239
|
const expanded = expandFileMentions(outgoing, ctx.cwd);
|
|
@@ -5805,6 +6251,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5805
6251
|
activeSkills,
|
|
5806
6252
|
advanceQueueContinuation,
|
|
5807
6253
|
applyPermissionMode,
|
|
6254
|
+
conversationLearning,
|
|
5808
6255
|
applyReasoningEffort,
|
|
5809
6256
|
appLifetime,
|
|
5810
6257
|
ctx.cwd,
|
|
@@ -5841,6 +6288,40 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
5841
6288
|
// a ref so selecting a help row re-enters the one ordinary slash-command
|
|
5842
6289
|
// path instead of growing a second command executor.
|
|
5843
6290
|
commandPickerSubmitRef.current = handleSubmit;
|
|
6291
|
+
saveSkillNotifyRef.current = () => sendTerminalNotification({ kind: 'approval-required' });
|
|
6292
|
+
scheduleLiveRef.current = {
|
|
6293
|
+
...(scheduleLiveRef.current?.model ? { model: scheduleLiveRef.current.model } : {}),
|
|
6294
|
+
say: (text) => {
|
|
6295
|
+
closeLiveReply();
|
|
6296
|
+
pushMessage('system', text);
|
|
6297
|
+
},
|
|
6298
|
+
ask: askQuestion,
|
|
6299
|
+
askPermission: onPermission,
|
|
6300
|
+
submit: (text) => handleSubmit(text),
|
|
6301
|
+
};
|
|
6302
|
+
// Scheduled work since the TUI last looked: one line, after the first
|
|
6303
|
+
// paint, never blocking it. Loops fire when a turn ends, and on their timer.
|
|
6304
|
+
useEffect(() => {
|
|
6305
|
+
const timer = setTimeout(() => {
|
|
6306
|
+
const line = scheduleRef.current?.startupLine();
|
|
6307
|
+
if (line)
|
|
6308
|
+
scheduleLiveRef.current?.say(line);
|
|
6309
|
+
}, 250);
|
|
6310
|
+
return () => {
|
|
6311
|
+
clearTimeout(timer);
|
|
6312
|
+
scheduleRef.current?.dispose();
|
|
6313
|
+
};
|
|
6314
|
+
}, []);
|
|
6315
|
+
useEffect(() => {
|
|
6316
|
+
if (state !== 'idle')
|
|
6317
|
+
return;
|
|
6318
|
+
try {
|
|
6319
|
+
scheduleRef.current?.loops.tick();
|
|
6320
|
+
}
|
|
6321
|
+
catch {
|
|
6322
|
+
// A loop that cannot be read never takes the screen down with it.
|
|
6323
|
+
}
|
|
6324
|
+
}, [state]);
|
|
5844
6325
|
// Keep the footer on the same durable goal record the driver mutates. A ref
|
|
5845
6326
|
// alone cannot repaint React, so every goal mutation bumps goalDriveVersion;
|
|
5846
6327
|
// generation/session checks stop a late disk read labelling a resumed chat.
|
|
@@ -6149,6 +6630,11 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6149
6630
|
useInput((input, key) => {
|
|
6150
6631
|
if (outputViewer !== null)
|
|
6151
6632
|
return;
|
|
6633
|
+
// The save-skill screen owns its keys, Esc and Ctrl+C included: neither
|
|
6634
|
+
// may interrupt the turn that is waiting on its answer. A permission
|
|
6635
|
+
// request (an agent's, say) takes the screen over it and gets the keys.
|
|
6636
|
+
if (saveSkillPromptRef.current && permission === null)
|
|
6637
|
+
return;
|
|
6152
6638
|
// Startup has refused admission, so there is no draft or active turn to
|
|
6153
6639
|
// protect with the ready screen's two-press exit ladder.
|
|
6154
6640
|
if (phase === 'unhealthy') {
|
|
@@ -6262,7 +6748,11 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6262
6748
|
}
|
|
6263
6749
|
if (permission && (key.upArrow || key.downArrow)) {
|
|
6264
6750
|
const current = permissionChoiceRef.current;
|
|
6265
|
-
const
|
|
6751
|
+
const last = permissionAnswers(permission.toolCalls, {
|
|
6752
|
+
batchOnly: permission.batchOnly === true,
|
|
6753
|
+
screenConsent: permission.screenConsent === true,
|
|
6754
|
+
}).length - 1;
|
|
6755
|
+
const next = key.upArrow ? Math.max(0, current - 1) : Math.min(last, current + 1);
|
|
6266
6756
|
setPermissionChoice(next);
|
|
6267
6757
|
return;
|
|
6268
6758
|
}
|
|
@@ -6301,14 +6791,26 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6301
6791
|
// the operator never looked at. Refusals above never wait.
|
|
6302
6792
|
if (!approvalIsDeliberate(permissionOpenedAtRef.current, Date.now()))
|
|
6303
6793
|
return;
|
|
6304
|
-
|
|
6794
|
+
// The answers on screen, so a digit or Enter means what it is next
|
|
6795
|
+
// to: a batch-only prompt (a scheduled run) has no "allow all", and
|
|
6796
|
+
// neither its `a` nor a `3` answers it.
|
|
6797
|
+
const answers = permissionAnswers(permission?.toolCalls ?? [], {
|
|
6798
|
+
batchOnly: permission?.batchOnly === true,
|
|
6799
|
+
screenConsent: permission?.screenConsent === true,
|
|
6800
|
+
});
|
|
6801
|
+
const numbered = /^[1-9]$/.test(ch) ? Number(ch) - 1 : null;
|
|
6305
6802
|
const chosen = key.return ? permissionChoiceRef.current : numbered;
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6803
|
+
const kind = ch === 'y'
|
|
6804
|
+
? 'approve'
|
|
6805
|
+
: ch === 'a'
|
|
6806
|
+
? answers.some((answer) => answer.kind === 'approve-all')
|
|
6807
|
+
? 'approve-all'
|
|
6808
|
+
: undefined
|
|
6809
|
+
: chosen !== null
|
|
6810
|
+
? answers[chosen]?.kind
|
|
6811
|
+
: undefined;
|
|
6812
|
+
if (kind)
|
|
6813
|
+
resolvePermission({ kind });
|
|
6312
6814
|
return;
|
|
6313
6815
|
}
|
|
6314
6816
|
// The active child count already advertises this key beside Working.
|
|
@@ -6614,6 +7116,21 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6614
7116
|
resetTranscript();
|
|
6615
7117
|
return;
|
|
6616
7118
|
}
|
|
7119
|
+
// A turn a tool paused for a person: Enter on an empty composer
|
|
7120
|
+
// continues it, Esc stops it (the turn is abandoned, like /abandon).
|
|
7121
|
+
if (handoffParkRef.current &&
|
|
7122
|
+
phase === 'ready' &&
|
|
7123
|
+
state === 'idle' &&
|
|
7124
|
+
!abortRef.current &&
|
|
7125
|
+
((key.return && !composerHasDraft) || key.escape)) {
|
|
7126
|
+
const parked = handoffParkRef.current;
|
|
7127
|
+
setHandoffPark(null);
|
|
7128
|
+
if (key.return)
|
|
7129
|
+
void resumeActiveTurn({ handoffReason: parked.reason });
|
|
7130
|
+
else
|
|
7131
|
+
void abandonActiveTurn('The operator stopped the turn a tool paused for them.');
|
|
7132
|
+
return;
|
|
7133
|
+
}
|
|
6617
7134
|
// Esc interrupts a running turn (Ctrl+C stays reserved for exit). Mirrors
|
|
6618
7135
|
// the Ctrl+C interrupt path: abort, drop the queue, one "Interrupted." line.
|
|
6619
7136
|
if (key.escape && (abortRef.current || pendingModelSwitchRef.current)) {
|
|
@@ -6738,29 +7255,33 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6738
7255
|
? 'starting a fresh conversation — input is paused'
|
|
6739
7256
|
: compacting
|
|
6740
7257
|
? 'compacting conversation — input is paused'
|
|
6741
|
-
:
|
|
6742
|
-
?
|
|
6743
|
-
:
|
|
6744
|
-
?
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
? '
|
|
6748
|
-
:
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
:
|
|
6752
|
-
? '
|
|
6753
|
-
:
|
|
6754
|
-
?
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
? '
|
|
6758
|
-
:
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
:
|
|
6762
|
-
?
|
|
6763
|
-
:
|
|
7258
|
+
: saveSkillPrompt
|
|
7259
|
+
? 'save skill — ←/→ choose · enter apply · esc cancel'
|
|
7260
|
+
: permission
|
|
7261
|
+
? hintForPhase(phase, state, session?.hasProvider === true)
|
|
7262
|
+
: agentSurface?.kind === 'cockpit'
|
|
7263
|
+
? agentSurface.focus === 'workflows'
|
|
7264
|
+
? 'workflows — enter select · esc return'
|
|
7265
|
+
: agentSurface.focus === 'phases'
|
|
7266
|
+
? 'agent phases — enter agents · esc return'
|
|
7267
|
+
: 'agents — enter inspect · left phases · esc return'
|
|
7268
|
+
: agentSurface?.kind === 'transcript'
|
|
7269
|
+
? 'observing agent — esc agents · q parent'
|
|
7270
|
+
: textPrompt
|
|
7271
|
+
? 'editor open · enter apply · esc cancel'
|
|
7272
|
+
: choicePicker
|
|
7273
|
+
? choicePickerSearchable(choicePicker)
|
|
7274
|
+
? 'type to filter · ↑↓ select · enter apply · esc back'
|
|
7275
|
+
: effortSlider
|
|
7276
|
+
? '←/→ adjust · 1–9 select · enter apply · esc back'
|
|
7277
|
+
: '↑↓ / 1–9 select · enter apply · esc back'
|
|
7278
|
+
: copyPicker
|
|
7279
|
+
? 'copy target open — ↑↓ / 1–9 select · esc cancel'
|
|
7280
|
+
: handoffPark && phase === 'ready' && state === 'idle' && !composerHasDraft
|
|
7281
|
+
? 'enter continue · esc stop'
|
|
7282
|
+
: goalStatus && phase === 'ready' && state === 'idle'
|
|
7283
|
+
? undefined
|
|
7284
|
+
: hintForPhase(phase, state, session?.hasProvider === true, composerHasDraft);
|
|
6764
7285
|
const permissionSource = reviewSource(permission, scopeRef.current?.sessionId);
|
|
6765
7286
|
const permissionOwner = permissionSource
|
|
6766
7287
|
? subagents.find((agent) => agent.sessionId === permissionSource)
|
|
@@ -6799,7 +7320,15 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6799
7320
|
// where it is decided rather than guessed on the screen: with no
|
|
6800
7321
|
// session behind the picker, esc exits namzu and the footer says
|
|
6801
7322
|
// so instead of offering a cancel nobody will see.
|
|
6802
|
-
cancelExits: !session?.hasProvider })) : (_jsxs(_Fragment, { children: [agentSurface === null && outputViewer === null ? (_jsx(LiveActivity, { compact: compactWork, activeTools: visibleActiveTools, working: state === 'thinking' || state === 'tool', interruptible: abortRef.current !== null, animate: stdout.isTTY === true &&
|
|
7323
|
+
cancelExits: !session?.hasProvider })) : (_jsxs(_Fragment, { children: [agentSurface === null && outputViewer === null ? (_jsx(LiveActivity, { compact: compactWork, activeTools: visibleActiveTools, working: state === 'thinking' || state === 'tool', interruptible: abortRef.current !== null, animate: stdout.isTTY === true &&
|
|
7324
|
+
permission === null &&
|
|
7325
|
+
textPrompt === null &&
|
|
7326
|
+
saveSkillPrompt === null, thinking: thinking, tokens: turnTokens, waitingForYou: choicePicker !== null ||
|
|
7327
|
+
permission !== null ||
|
|
7328
|
+
textPrompt !== null ||
|
|
7329
|
+
saveSkillPrompt !== null })) : null, agentSurface === null && outputViewer === null && permission === null ? (_jsx(TaskList, { tasks: liveTasks })) : null, permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls, review: permission.review, summary: permission.summary, detailsOpen: permissionDetailsOpen, reviewOffset: permissionReviewOffset, choice: permissionChoice, queuedCount: queuedPermissionCount, sourceLabel: permissionSourceLabel, columns: terminal.columns, rows: terminal.rows, batchOnly: permission.batchOnly === true, siteNotes: permission.siteNotes, ...(permission.screenConsent === true
|
|
7330
|
+
? { screenConsent: { provider: session?.providerSummary ?? null } }
|
|
7331
|
+
: {}) })) : null, saveSkillPrompt && permission === null ? (_jsx(SaveSkillOverlay, { request: saveSkillPrompt.request, cwd: ctx.cwd, columns: Math.max(1, (terminal.columns ?? 80) - 2), rows: terminal.rows, onAnswer: saveSkillPrompt.resolve }, saveSkillPrompt.request.markdown)) : null, textPrompt ? (_jsx(TextPrompt, { columns: Math.max(1, (terminal.columns ?? 80) - 2), title: textPrompt.title, placeholder: textPrompt.placeholder, initialValue: textPrompt.initialValue, emptyNotice: textPrompt.emptyNotice, hidden: permission !== null || (agentSurface !== null || outputViewer !== null), onSubmit: submitTextPrompt, onCancel: cancelTextPrompt }, textPrompt.token)) : permission === null &&
|
|
6803
7332
|
agentSurface === null &&
|
|
6804
7333
|
outputViewer === null &&
|
|
6805
7334
|
choicePicker?.kind === 'reasoning-effort' &&
|
|
@@ -6811,7 +7340,9 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6811
7340
|
textPrompt === null &&
|
|
6812
7341
|
choicePicker === null &&
|
|
6813
7342
|
copyPicker === null &&
|
|
7343
|
+
saveSkillPrompt === null &&
|
|
6814
7344
|
agentSurface === null && outputViewer === null, hidden: permission !== null ||
|
|
7345
|
+
saveSkillPrompt !== null ||
|
|
6815
7346
|
textPrompt !== null ||
|
|
6816
7347
|
choicePicker !== null ||
|
|
6817
7348
|
copyPicker !== null ||
|
|
@@ -6836,14 +7367,18 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
|
|
|
6836
7367
|
conversationMutation !== null ||
|
|
6837
7368
|
externalEditorRequest !== null ||
|
|
6838
7369
|
(agentSurface !== null || outputViewer !== null), hidden: permission !== null ||
|
|
7370
|
+
saveSkillPrompt !== null ||
|
|
6839
7371
|
textPrompt !== null ||
|
|
6840
7372
|
choicePicker !== null ||
|
|
6841
7373
|
copyPicker !== null ||
|
|
6842
7374
|
(agentSurface !== null || outputViewer !== null),
|
|
6843
7375
|
// A turn is running, so Esc is the interrupt and not
|
|
6844
7376
|
// the composer's clear.
|
|
6845
|
-
escapeInterrupts: !compacting &&
|
|
6846
|
-
(state === 'thinking' ||
|
|
7377
|
+
escapeInterrupts: (!compacting &&
|
|
7378
|
+
(state === 'thinking' ||
|
|
7379
|
+
state === 'tool' ||
|
|
7380
|
+
pendingModelSwitchRef.current !== null)) ||
|
|
7381
|
+
(handoffPark !== null && state === 'idle'), onSubmit: handleSubmit, onNotice: (text) => pushMessage('system', text), onStepReasoningEffort: stepReasoningEffort, onExternalEdit: requestExternalEditor, onEditPrevious: openPromptEditor, draftToRestore: composerDraft, onDraftRestored: (token) => setComposerDraft((draft) => (draft?.token === token ? null : draft)), onDraftPresenceChange: setComposerHasDraft, onOpenAgentPanel: openAgentCockpit, userCommands: userCommands, builtins: hostCommands.map((command) => ({
|
|
6847
7382
|
...command,
|
|
6848
7383
|
description: command.unavailable?.(slashCtx) ?? command.description,
|
|
6849
7384
|
})), mentionCandidates: mentionCandidates, history: history })] })] })), _jsx(StatusBar, { cwd: ctx.cwd, provider: session?.providerSummary ?? null, model: session?.modelSummary ?? null, effort: reasoningEffort, orchestrate: orchestrateMode, goal: statusGoal, state: state, hint: statusHint, permissionMode: displayedPermissionMode, canCycleMode: true }), showComposerSurface &&
|