lsd-pi 1.1.3 → 1.1.5
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 +2 -1
- package/dist/cli.js +14 -0
- package/dist/headless.d.ts +2 -0
- package/dist/headless.js +23 -0
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/await-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/cancel-job-tool.js +7 -0
- package/dist/resources/extensions/cache-timer/index.js +4 -0
- package/dist/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/dist/resources/extensions/codex-rotate/README.md +9 -3
- package/dist/resources/extensions/codex-rotate/commands.js +19 -9
- package/dist/resources/extensions/codex-rotate/index.js +22 -37
- package/dist/resources/extensions/codex-rotate/oauth.js +70 -13
- package/dist/resources/extensions/codex-rotate/quota.js +7 -6
- package/dist/resources/extensions/codex-rotate/sync.js +6 -0
- package/dist/resources/extensions/memory/auto-extract.js +160 -77
- package/dist/resources/extensions/shared/interview-ui.js +2 -1
- package/dist/resources/extensions/shared/rtk.js +89 -87
- package/dist/resources/extensions/slash-commands/plan.js +12 -0
- package/dist/resources/extensions/subagent/background-job-manager.js +170 -0
- package/dist/resources/extensions/subagent/background-runner.js +36 -0
- package/dist/resources/extensions/subagent/background-types.js +8 -0
- package/dist/resources/extensions/subagent/index.js +459 -13
- package/dist/startup-model-validation.js +12 -2
- package/dist/update-check.js +2 -2
- package/dist/update-cmd.js +3 -3
- package/package.json +2 -1
- package/packages/native/dist/stream-process/index.js +110 -1
- package/packages/native/src/stream-process/index.ts +115 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +9 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
- package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
- package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +66 -0
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +9 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +36 -2
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +38 -5
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js +131 -101
- package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/footer-data-provider.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/footer-data-provider.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/footer-data-provider.js +7 -0
- package/packages/pi-coding-agent/dist/core/footer-data-provider.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +2 -0
- package/packages/pi-coding-agent/dist/core/messages.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts +48 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js +173 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +24 -6
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sandbox/index.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/sandbox/index.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/index.js +5 -0
- package/packages/pi-coding-agent/dist/core/sandbox/index.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/linux-sandbox.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/sandbox/linux-sandbox.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/linux-sandbox.js +73 -0
- package/packages/pi-coding-agent/dist/core/sandbox/linux-sandbox.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/macos-sandbox.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/sandbox/macos-sandbox.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/macos-sandbox.js +73 -0
- package/packages/pi-coding-agent/dist/core/sandbox/macos-sandbox.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-manager.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-manager.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-manager.js +200 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-manager.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-policy.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-policy.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-policy.js +19 -0
- package/packages/pi-coding-agent/dist/core/sandbox/sandbox-policy.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +18 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +18 -0
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +20 -0
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +33 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/tool-approval.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.js +12 -2
- package/packages/pi-coding-agent/dist/core/tool-approval.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/bash.js +100 -65
- package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/grep.js +1 -1
- package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +23 -2
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js +289 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +2 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +8 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +5 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +31 -56
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts +39 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +182 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +7 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +45 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +2 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +4 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +6 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +196 -98
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +8 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +52 -11
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +30 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +362 -37
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +5 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +22 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +8 -8
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/utils/editor-link.d.ts +28 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/editor-link.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/editor-link.js +78 -0
- package/packages/pi-coding-agent/dist/modes/interactive/utils/editor-link.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts +10 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts +7 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js.map +1 -0
- package/packages/pi-coding-agent/package.json +9 -4
- package/packages/pi-coding-agent/src/core/agent-session.ts +48 -2
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +33 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +36 -5
- package/packages/pi-coding-agent/src/core/bash-executor.ts +142 -106
- package/packages/pi-coding-agent/src/core/extensions/types.ts +1 -1
- package/packages/pi-coding-agent/src/core/footer-data-provider.ts +10 -1
- package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +2 -0
- package/packages/pi-coding-agent/src/core/messages.ts +2 -1
- package/packages/pi-coding-agent/src/core/pty-executor.ts +229 -0
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +73 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +25 -6
- package/packages/pi-coding-agent/src/core/sandbox/index.ts +10 -0
- package/packages/pi-coding-agent/src/core/sandbox/linux-sandbox.ts +96 -0
- package/packages/pi-coding-agent/src/core/sandbox/macos-sandbox.ts +83 -0
- package/packages/pi-coding-agent/src/core/sandbox/sandbox-manager.ts +240 -0
- package/packages/pi-coding-agent/src/core/sandbox/sandbox-policy.ts +39 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +18 -3
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +22 -0
- package/packages/pi-coding-agent/src/core/session-manager.ts +20 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +53 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/tool-approval.ts +22 -2
- package/packages/pi-coding-agent/src/core/tools/bash.ts +107 -65
- package/packages/pi-coding-agent/src/core/tools/grep.ts +1 -1
- package/packages/pi-coding-agent/src/core/tools/index.ts +35 -2
- package/packages/pi-coding-agent/src/core/tools/pty.ts +354 -0
- package/packages/pi-coding-agent/src/index.ts +9 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +8 -8
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -4
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +43 -62
- package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +224 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +7 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +57 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +4 -14
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +197 -101
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +8 -8
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +57 -10
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +410 -42
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +6 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +24 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +8 -8
- package/packages/pi-coding-agent/src/modes/interactive/utils/editor-link.ts +101 -0
- package/packages/pi-coding-agent/src/utils/terminal-screen.ts +77 -0
- package/packages/pi-coding-agent/src/utils/terminal-serializer.ts +72 -0
- package/packages/pi-tui/dist/components/animated-status.d.ts +66 -0
- package/packages/pi-tui/dist/components/animated-status.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/animated-status.js +168 -0
- package/packages/pi-tui/dist/components/animated-status.js.map +1 -0
- package/packages/pi-tui/dist/components/loader.d.ts +26 -6
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +178 -18
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +7 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +11 -4
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/loader.ts +196 -19
- package/packages/pi-tui/src/components/markdown.ts +19 -4
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +22 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +8 -8
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/cancel-job-tool.ts +8 -0
- package/src/resources/extensions/cache-timer/index.ts +101 -96
- package/src/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/src/resources/extensions/codex-rotate/README.md +9 -3
- package/src/resources/extensions/codex-rotate/commands.ts +335 -322
- package/src/resources/extensions/codex-rotate/index.ts +94 -107
- package/src/resources/extensions/codex-rotate/oauth.ts +76 -12
- package/src/resources/extensions/codex-rotate/quota.ts +8 -7
- package/src/resources/extensions/codex-rotate/sync.ts +5 -0
- package/src/resources/extensions/memory/auto-extract.ts +297 -203
- package/src/resources/extensions/memory/tests/auto-extract.test.ts +191 -144
- package/src/resources/extensions/shared/interview-ui.ts +2 -1
- package/src/resources/extensions/shared/rtk.js +112 -0
- package/src/resources/extensions/slash-commands/plan.ts +11 -0
- package/src/resources/extensions/subagent/background-job-manager.ts +217 -0
- package/src/resources/extensions/subagent/background-runner.ts +69 -0
- package/src/resources/extensions/subagent/background-types.ts +54 -0
- package/src/resources/extensions/subagent/index.ts +1653 -1123
package/README.md
CHANGED
|
@@ -242,8 +242,9 @@ A few quality-of-life touches in the TUI:
|
|
|
242
242
|
|
|
243
243
|
- the footer can show a live cache timer for the current prompt-cache window
|
|
244
244
|
- `/hotkeys` gives you a full shortcut reference on demand
|
|
245
|
-
- `/settings` now includes toggles for Codex rotate, the cache timer, RTK shell-command compression, and a configurable **Main accent** preset
|
|
245
|
+
- `/settings` now includes toggles for Codex rotate, the cache timer, **Pin last prompt**, RTK shell-command compression, and a configurable **Main accent** preset
|
|
246
246
|
- changing the main accent also updates accent-driven UI elements and the text input border across thinking levels
|
|
247
|
+
- when **Pin last prompt** is enabled, LSD keeps your most recent non-command prompt visible above the editor as a lightweight reminder
|
|
247
248
|
|
|
248
249
|
Some workflow/automation commands still use the legacy namespace:
|
|
249
250
|
|
package/dist/cli.js
CHANGED
|
@@ -58,6 +58,14 @@ function parseCliArgs(argv) {
|
|
|
58
58
|
else if (arg === '--model' && i + 1 < args.length) {
|
|
59
59
|
flags.model = args[++i];
|
|
60
60
|
}
|
|
61
|
+
else if (arg === '--sandbox' && i + 1 < args.length) {
|
|
62
|
+
const value = args[++i];
|
|
63
|
+
if (value === 'none' || value === 'workspace-write' || value === 'auto')
|
|
64
|
+
flags.sandbox = value;
|
|
65
|
+
}
|
|
66
|
+
else if (arg === '--no-sandbox') {
|
|
67
|
+
flags.noSandbox = true;
|
|
68
|
+
}
|
|
61
69
|
else if (arg === '--extension' && i + 1 < args.length) {
|
|
62
70
|
flags.extensions.push(args[++i]);
|
|
63
71
|
}
|
|
@@ -249,6 +257,12 @@ const modelRegistry = new ModelRegistry(authStorage, modelsJsonPath);
|
|
|
249
257
|
markStartup('ModelRegistry');
|
|
250
258
|
const settingsManager = SettingsManager.create(agentDir);
|
|
251
259
|
markStartup('SettingsManager.create');
|
|
260
|
+
if (cliFlags.noSandbox) {
|
|
261
|
+
process.env.PI_NO_SANDBOX = '1';
|
|
262
|
+
}
|
|
263
|
+
else if (cliFlags.sandbox) {
|
|
264
|
+
process.env.PI_SANDBOX = cliFlags.sandbox;
|
|
265
|
+
}
|
|
252
266
|
process.env.LUCENT_CODE_PERMISSION_MODE = settingsManager.getPermissionMode();
|
|
253
267
|
const configuredClassifierModel = settingsManager.getClassifierModel();
|
|
254
268
|
if (configuredClassifierModel) {
|
package/dist/headless.d.ts
CHANGED
package/dist/headless.js
CHANGED
|
@@ -86,6 +86,15 @@ export function parseHeadlessArgs(argv) {
|
|
|
86
86
|
// --model can also be passed from the main CLI; headless-specific takes precedence
|
|
87
87
|
options.model = args[++i];
|
|
88
88
|
}
|
|
89
|
+
else if (arg === '--sandbox' && i + 1 < args.length) {
|
|
90
|
+
const value = args[++i];
|
|
91
|
+
if (value === 'none' || value === 'workspace-write' || value === 'auto') {
|
|
92
|
+
options.sandbox = value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (arg === '--no-sandbox') {
|
|
96
|
+
options.noSandbox = true;
|
|
97
|
+
}
|
|
89
98
|
else if (arg === '--context' && i + 1 < args.length) {
|
|
90
99
|
options.context = args[++i];
|
|
91
100
|
}
|
|
@@ -189,6 +198,14 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
189
198
|
if (isAutoMode && options.timeout === 300_000) {
|
|
190
199
|
options.timeout = 0;
|
|
191
200
|
}
|
|
201
|
+
if (!options.json) {
|
|
202
|
+
if (options.noSandbox) {
|
|
203
|
+
process.stderr.write('[headless] Sandbox: disabled by flag\n');
|
|
204
|
+
}
|
|
205
|
+
else if (options.sandbox) {
|
|
206
|
+
process.stderr.write(`[headless] Sandbox: ${options.sandbox}\n`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
192
209
|
// Supervised mode cannot share stdin with --context -
|
|
193
210
|
if (options.supervised && options.context === '-') {
|
|
194
211
|
process.stderr.write('[headless] Error: --supervised cannot be used with --context - (both require stdin)\n');
|
|
@@ -271,6 +288,12 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
271
288
|
if (injector) {
|
|
272
289
|
clientOptions.env = injector.getSecretEnvVars();
|
|
273
290
|
}
|
|
291
|
+
if (options.noSandbox) {
|
|
292
|
+
clientOptions.env = { ...(clientOptions.env || {}), PI_NO_SANDBOX: '1' };
|
|
293
|
+
}
|
|
294
|
+
else if (options.sandbox) {
|
|
295
|
+
clientOptions.env = { ...(clientOptions.env || {}), PI_SANDBOX: options.sandbox };
|
|
296
|
+
}
|
|
274
297
|
// Signal headless mode to the GSD extension (skips UAT human pause, etc.)
|
|
275
298
|
clientOptions.env = { ...(clientOptions.env || {}), GSD_HEADLESS: '1' };
|
|
276
299
|
// Propagate --bare to the child process
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* with await_job.
|
|
7
7
|
*/
|
|
8
8
|
import { getShellConfig, sanitizeCommand, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, } from "@gsd/pi-coding-agent";
|
|
9
|
+
import { Text } from "@gsd/pi-tui";
|
|
9
10
|
import { Type } from "@sinclair/typebox";
|
|
10
11
|
import { spawn, spawnSync } from "node:child_process";
|
|
11
12
|
import { createWriteStream } from "node:fs";
|
|
@@ -70,6 +71,19 @@ export function createAsyncBashTool(getManager, getCwd) {
|
|
|
70
71
|
"Check /jobs to see all running and recent background jobs.",
|
|
71
72
|
],
|
|
72
73
|
parameters: schema,
|
|
74
|
+
renderCall(args, theme, options) {
|
|
75
|
+
const cmd = args.command ?? "";
|
|
76
|
+
const display = cmd.length > 80 ? cmd.slice(0, 77) + "..." : cmd;
|
|
77
|
+
const indicator = options?.statusIndicator ? `${options.statusIndicator} ` : "";
|
|
78
|
+
let text = indicator + theme.fg("toolTitle", theme.bold("async_bash "));
|
|
79
|
+
text += theme.fg("bashMode", "$ ");
|
|
80
|
+
text += theme.fg("accent", display || theme.fg("muted", "..."));
|
|
81
|
+
if (args.label)
|
|
82
|
+
text += theme.fg("muted", ` (${args.label})`);
|
|
83
|
+
if (args.timeout)
|
|
84
|
+
text += theme.fg("dim", ` timeout:${args.timeout}s`);
|
|
85
|
+
return new Text(text, 0, 0);
|
|
86
|
+
},
|
|
73
87
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
74
88
|
const manager = getManager();
|
|
75
89
|
const cwd = getCwd();
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* If specific job IDs are provided, waits for those jobs.
|
|
5
5
|
* If omitted, waits for any running job to complete.
|
|
6
6
|
*/
|
|
7
|
+
import { Text } from "@gsd/pi-tui";
|
|
7
8
|
import { Type } from "@sinclair/typebox";
|
|
8
9
|
const DEFAULT_TIMEOUT_SECONDS = 120;
|
|
9
10
|
const schema = Type.Object({
|
|
@@ -21,6 +22,19 @@ export function createAwaitTool(getManager) {
|
|
|
21
22
|
label: "Await Background Job",
|
|
22
23
|
description: "Wait for background jobs to complete. Provide specific job IDs or omit to wait for the next job that finishes. Returns results of completed jobs.",
|
|
23
24
|
parameters: schema,
|
|
25
|
+
renderCall(args, theme, options) {
|
|
26
|
+
const indicator = options?.statusIndicator ? `${options.statusIndicator} ` : "";
|
|
27
|
+
let text = indicator + theme.fg("toolTitle", theme.bold("await_job"));
|
|
28
|
+
if (args.jobs && args.jobs.length > 0) {
|
|
29
|
+
text += " " + theme.fg("accent", args.jobs.join(", "));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
text += theme.fg("muted", " (any)");
|
|
33
|
+
}
|
|
34
|
+
if (args.timeout)
|
|
35
|
+
text += theme.fg("dim", ` timeout:${args.timeout}s`);
|
|
36
|
+
return new Text(text, 0, 0);
|
|
37
|
+
},
|
|
24
38
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
25
39
|
const manager = getManager();
|
|
26
40
|
const { jobs: jobIds, timeout } = params;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* cancel_job tool — cancel a running background job.
|
|
3
3
|
*/
|
|
4
|
+
import { Text } from "@gsd/pi-tui";
|
|
4
5
|
import { Type } from "@sinclair/typebox";
|
|
5
6
|
const schema = Type.Object({
|
|
6
7
|
job_id: Type.String({ description: "The background job ID to cancel (e.g. bg_a1b2c3d4)" }),
|
|
@@ -11,6 +12,12 @@ export function createCancelJobTool(getManager) {
|
|
|
11
12
|
label: "Cancel Background Job",
|
|
12
13
|
description: "Cancel a running background job by its ID.",
|
|
13
14
|
parameters: schema,
|
|
15
|
+
renderCall(args, theme, options) {
|
|
16
|
+
const indicator = options?.statusIndicator ? `${options.statusIndicator} ` : "";
|
|
17
|
+
let text = indicator + theme.fg("toolTitle", theme.bold("cancel_job "));
|
|
18
|
+
text += theme.fg("accent", args.job_id);
|
|
19
|
+
return new Text(text, 0, 0);
|
|
20
|
+
},
|
|
14
21
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
15
22
|
const manager = getManager();
|
|
16
23
|
const result = manager.cancel(params.job_id);
|
|
@@ -14,6 +14,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { getAgentDir } from "@gsd/pi-coding-agent";
|
|
16
16
|
const STATUS_KEY = "cache-timer";
|
|
17
|
+
const IS_MEMORY_MAINTENANCE_WORKER = process.env.LSD_MEMORY_EXTRACT === "1" || process.env.LSD_MEMORY_DREAM === "1";
|
|
17
18
|
// ANSI color codes for timer display
|
|
18
19
|
const ANSI_RESET = "\x1b[0m";
|
|
19
20
|
const ANSI_YELLOW = "\x1b[33m";
|
|
@@ -65,6 +66,9 @@ function formatElapsed(ms) {
|
|
|
65
66
|
return `⏱ ${time}`;
|
|
66
67
|
}
|
|
67
68
|
export default function cacheTimerExtension(pi) {
|
|
69
|
+
if (IS_MEMORY_MAINTENANCE_WORKER) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
68
72
|
let timer = null;
|
|
69
73
|
let startTime = null;
|
|
70
74
|
let enabled = readEnabled();
|
|
@@ -18,6 +18,7 @@ Implemented a Codex OAuth rotation extension for LSD that manages multiple ChatG
|
|
|
18
18
|
- Writes `openai-codex` credential array with `api_key` type
|
|
19
19
|
- Avoids the `set()` method (which appends duplicates)
|
|
20
20
|
- Maintains stable credential order for index-based backoff
|
|
21
|
+
- Followed by a live `AuthStorage.reload()` in the extension so the running process sees account changes immediately
|
|
21
22
|
|
|
22
23
|
3. **Background Refresh Timer** (`index.ts`)
|
|
23
24
|
- Runs every 10 minutes
|
|
@@ -42,11 +43,10 @@ Implemented a Codex OAuth rotation extension for LSD that manages multiple ChatG
|
|
|
42
43
|
- `/codex import-cockpit` - Import from Cockpit
|
|
43
44
|
- `/codex sync` - Force refresh all tokens
|
|
44
45
|
|
|
45
|
-
6. **Error Detection**
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
- Integrates with `AuthStorage.markUsageLimitReached()`
|
|
46
|
+
6. **Error Detection / Rotation Path**
|
|
47
|
+
- Quota/rate-limit/auth classification lives in `quota.ts`
|
|
48
|
+
- Same-turn retry and per-credential backoff are performed by core `RetryHandler`
|
|
49
|
+
- The extension's responsibility is to keep `auth.json` and the live in-memory auth state in sync
|
|
50
50
|
|
|
51
51
|
## File Structure
|
|
52
52
|
|
|
@@ -87,6 +87,7 @@ Using `FileAuthStorageBackend.withLockAsync()`:
|
|
|
87
87
|
- Prevents race conditions with multiple LSD instances
|
|
88
88
|
- Replaces entire credential array (not just updating keys)
|
|
89
89
|
- Maintains stable order for index-based backoff
|
|
90
|
+
- Requires a follow-up live auth reload in the current process so retry rotation uses the updated credential set
|
|
90
91
|
|
|
91
92
|
### 4. Session-Sticky Credential Selection
|
|
92
93
|
|
|
@@ -105,15 +106,15 @@ LSD already uses session-sticky selection by default:
|
|
|
105
106
|
|
|
106
107
|
## Next Steps: Phase 2 (Resilience)
|
|
107
108
|
|
|
108
|
-
|
|
109
|
+
Remaining improvements that could still be added:
|
|
109
110
|
|
|
110
|
-
1. **
|
|
111
|
-
-
|
|
112
|
-
-
|
|
111
|
+
1. **Richer `/codex status` diagnostics**
|
|
112
|
+
- Show live backoff / credential-availability state from `AuthStorage`
|
|
113
|
+
- Make it easier to verify when an account was rotated away after a usage-limit hit
|
|
113
114
|
|
|
114
|
-
2. **
|
|
115
|
-
- Could listen for external auth
|
|
116
|
-
- Would re-sync on change detection
|
|
115
|
+
2. **External change detection**
|
|
116
|
+
- Could listen for external auth/account file modifications
|
|
117
|
+
- Would re-sync / reload on change detection
|
|
117
118
|
|
|
118
119
|
3. **Migration helpers**
|
|
119
120
|
- Could add `/codex migrate` to assist users moving from Cockpit
|
|
@@ -121,8 +122,12 @@ The following features were planned but not yet implemented:
|
|
|
121
122
|
|
|
122
123
|
## Testing
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
Implemented regression coverage now includes:
|
|
126
|
+
- `src/tests/codex-rotate-auth-reload.test.ts`
|
|
127
|
+
- Verifies that codex-rotate syncs require a live auth reload for the running process to see the latest credentials
|
|
128
|
+
- Verifies `quota_exhausted` backoff on one `openai-codex` credential causes the next retry to select the next credential
|
|
125
129
|
|
|
130
|
+
Manual smoke test:
|
|
126
131
|
1. Add an account: `/codex add`
|
|
127
132
|
2. Check status: `/codex status`
|
|
128
133
|
3. Add another account: `/codex add`
|
|
@@ -97,14 +97,20 @@ Codex access tokens work identically to API keys:
|
|
|
97
97
|
- Background timer runs every 10 minutes
|
|
98
98
|
- Tokens are refreshed when expiring within 5 minutes
|
|
99
99
|
- Refreshed tokens are atomically synced to auth.json
|
|
100
|
+
- After every successful sync, the extension reloads live `AuthStorage` so retries and rotation see the latest credentials immediately
|
|
100
101
|
- Failed refreshes disable the account with a reason
|
|
101
102
|
|
|
102
103
|
### Error Handling
|
|
103
104
|
|
|
104
|
-
The extension
|
|
105
|
+
The extension now relies on LSD core retry/backoff handling rather than its own `agent_end` hook:
|
|
106
|
+
- The Codex provider surfaces friendly usage-limit / rate-limit / auth errors
|
|
107
|
+
- Core `RetryHandler` classifies those failures and calls `markUsageLimitReached(...)`
|
|
108
|
+
- If another `openai-codex` credential is available, LSD automatically rotates and retries the same prompt
|
|
105
109
|
- Rate limit errors (429) → 30s backoff
|
|
106
|
-
- Quota exhausted errors → 30min backoff
|
|
107
|
-
- Auth errors (401) → treated as
|
|
110
|
+
- Quota exhausted / usage limit errors → 30min backoff
|
|
111
|
+
- Auth errors (401) → treated as immediate credential-rotation failures
|
|
112
|
+
|
|
113
|
+
To make that work reliably, the extension reloads the live auth state after every successful sync so the retry handler sees the current credential pool immediately.
|
|
108
114
|
|
|
109
115
|
## Security
|
|
110
116
|
|
|
@@ -63,6 +63,13 @@ function displayAccounts(ctx, accounts) {
|
|
|
63
63
|
});
|
|
64
64
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
65
65
|
}
|
|
66
|
+
async function syncAccountsToAuthAndReload(ctx) {
|
|
67
|
+
const synced = await syncAccountsToAuth(getAllAccounts());
|
|
68
|
+
if (synced) {
|
|
69
|
+
ctx.modelRegistry.authStorage.reload();
|
|
70
|
+
}
|
|
71
|
+
return synced;
|
|
72
|
+
}
|
|
66
73
|
/**
|
|
67
74
|
* Register the /codex command
|
|
68
75
|
*/
|
|
@@ -104,7 +111,10 @@ export function registerCodexCommand(pi) {
|
|
|
104
111
|
switch (sub) {
|
|
105
112
|
case "add": {
|
|
106
113
|
ctx.ui.notify("Starting OAuth login flow...", "info");
|
|
107
|
-
const accountData = await performOAuthLogin(
|
|
114
|
+
const accountData = await performOAuthLogin(undefined, {
|
|
115
|
+
onStatus: (msg) => ctx.ui.notify(msg, "info"),
|
|
116
|
+
onManualCodeInput: async () => (await ctx.ui.input("Paste the redirect URL from your browser:", "http://localhost:...")) ?? "",
|
|
117
|
+
});
|
|
108
118
|
// Prompt for email (optional)
|
|
109
119
|
const emailInput = await ctx.ui.input("Email for this account (optional, press Enter to skip)", "");
|
|
110
120
|
const email = emailInput || undefined;
|
|
@@ -115,7 +125,7 @@ export function registerCodexCommand(pi) {
|
|
|
115
125
|
disabled: false,
|
|
116
126
|
});
|
|
117
127
|
// Sync to auth.json
|
|
118
|
-
const success = await
|
|
128
|
+
const success = await syncAccountsToAuthAndReload(ctx);
|
|
119
129
|
if (success) {
|
|
120
130
|
ctx.ui.notify(`Added account: ${email || account.accountId}. Synced to auth.json.`, "success");
|
|
121
131
|
}
|
|
@@ -173,7 +183,7 @@ export function registerCodexCommand(pi) {
|
|
|
173
183
|
const confirmed = await ctx.ui.select(`Remove account: ${account.email || account.accountId}?`, ["Yes, remove", "Cancel"], { signal: AbortSignal.timeout(30000) });
|
|
174
184
|
if (confirmed === "Yes, remove") {
|
|
175
185
|
removeAccount(account.id);
|
|
176
|
-
await
|
|
186
|
+
await syncAccountsToAuthAndReload(ctx);
|
|
177
187
|
ctx.ui.notify(`Removed account: ${account.email || account.accountId}`, "success");
|
|
178
188
|
}
|
|
179
189
|
return;
|
|
@@ -197,7 +207,7 @@ export function registerCodexCommand(pi) {
|
|
|
197
207
|
return;
|
|
198
208
|
}
|
|
199
209
|
updateAccount(account.id, { disabled: false, disabledReason: undefined });
|
|
200
|
-
await
|
|
210
|
+
await syncAccountsToAuthAndReload(ctx);
|
|
201
211
|
ctx.ui.notify(`Enabled account: ${account.email || account.accountId}`, "success");
|
|
202
212
|
return;
|
|
203
213
|
}
|
|
@@ -220,7 +230,7 @@ export function registerCodexCommand(pi) {
|
|
|
220
230
|
return;
|
|
221
231
|
}
|
|
222
232
|
updateAccount(account.id, { disabled: true, disabledReason: "manually disabled" });
|
|
223
|
-
await
|
|
233
|
+
await syncAccountsToAuthAndReload(ctx);
|
|
224
234
|
ctx.ui.notify(`Disabled account: ${account.email || account.accountId}`, "success");
|
|
225
235
|
return;
|
|
226
236
|
}
|
|
@@ -231,7 +241,7 @@ export function registerCodexCommand(pi) {
|
|
|
231
241
|
ctx.ui.notify("No account found to import.", "warning");
|
|
232
242
|
return;
|
|
233
243
|
}
|
|
234
|
-
|
|
244
|
+
addAccount({
|
|
235
245
|
email: imported.email,
|
|
236
246
|
accountId: imported.accountId,
|
|
237
247
|
refreshToken: imported.refreshToken,
|
|
@@ -240,7 +250,7 @@ export function registerCodexCommand(pi) {
|
|
|
240
250
|
lastUsed: undefined,
|
|
241
251
|
disabled: false,
|
|
242
252
|
});
|
|
243
|
-
await
|
|
253
|
+
await syncAccountsToAuthAndReload(ctx);
|
|
244
254
|
ctx.ui.notify(`Imported account: ${imported.email || imported.accountId}`, "success");
|
|
245
255
|
return;
|
|
246
256
|
}
|
|
@@ -254,7 +264,7 @@ export function registerCodexCommand(pi) {
|
|
|
254
264
|
for (const acc of imported) {
|
|
255
265
|
addAccount(acc);
|
|
256
266
|
}
|
|
257
|
-
await
|
|
267
|
+
await syncAccountsToAuthAndReload(ctx);
|
|
258
268
|
ctx.ui.notify(`Imported ${imported.length} account(s) from Cockpit Tools`, "success");
|
|
259
269
|
return;
|
|
260
270
|
}
|
|
@@ -275,7 +285,7 @@ export function registerCodexCommand(pi) {
|
|
|
275
285
|
results.failed++;
|
|
276
286
|
}
|
|
277
287
|
}
|
|
278
|
-
await
|
|
288
|
+
await syncAccountsToAuthAndReload(ctx);
|
|
279
289
|
if (results.failed === 0) {
|
|
280
290
|
ctx.ui.notify(`Synced ${results.success} account(s) to auth.json`, "success");
|
|
281
291
|
}
|
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
import { getAllAccounts, updateAccount, getAccountsNeedingRefresh } from "./accounts.js";
|
|
8
8
|
import { syncAccountsToAuth } from "./sync.js";
|
|
9
9
|
import { registerCodexCommand } from "./commands.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { logCodexRotateError, logCodexRotateSwitch } from "./logger.js";
|
|
10
|
+
import { REFRESH_INTERVAL_MS } from "./config.js";
|
|
11
|
+
import { logCodexRotateError } from "./logger.js";
|
|
13
12
|
let refreshTimer = null;
|
|
13
|
+
async function reloadLiveAuthState(ctx) {
|
|
14
|
+
ctx.modelRegistry.authStorage.reload();
|
|
15
|
+
}
|
|
14
16
|
/**
|
|
15
17
|
* Refresh all accounts that need it
|
|
16
18
|
*/
|
|
17
|
-
async function refreshExpiringAccounts() {
|
|
19
|
+
async function refreshExpiringAccounts(ctx) {
|
|
18
20
|
try {
|
|
19
21
|
const accountsNeedingRefresh = getAccountsNeedingRefresh();
|
|
20
22
|
if (accountsNeedingRefresh.length === 0) {
|
|
@@ -42,7 +44,10 @@ async function refreshExpiringAccounts() {
|
|
|
42
44
|
if (successCount > 0) {
|
|
43
45
|
// Sync refreshed accounts to auth.json
|
|
44
46
|
const allAccounts = getAllAccounts();
|
|
45
|
-
await syncAccountsToAuth(allAccounts);
|
|
47
|
+
const synced = await syncAccountsToAuth(allAccounts);
|
|
48
|
+
if (synced) {
|
|
49
|
+
await reloadLiveAuthState(ctx);
|
|
50
|
+
}
|
|
46
51
|
}
|
|
47
52
|
if (failCount > 0 && successCount === 0) {
|
|
48
53
|
logCodexRotateError(`Failed to refresh ${failCount} account(s)`);
|
|
@@ -55,12 +60,12 @@ async function refreshExpiringAccounts() {
|
|
|
55
60
|
/**
|
|
56
61
|
* Start the background refresh timer
|
|
57
62
|
*/
|
|
58
|
-
function startRefreshTimer() {
|
|
63
|
+
function startRefreshTimer(ctx) {
|
|
59
64
|
if (refreshTimer) {
|
|
60
65
|
clearInterval(refreshTimer);
|
|
61
66
|
}
|
|
62
67
|
refreshTimer = setInterval(() => {
|
|
63
|
-
void refreshExpiringAccounts();
|
|
68
|
+
void refreshExpiringAccounts(ctx);
|
|
64
69
|
}, REFRESH_INTERVAL_MS);
|
|
65
70
|
}
|
|
66
71
|
/**
|
|
@@ -79,46 +84,26 @@ export default function CodexRotateExtension(pi) {
|
|
|
79
84
|
// Register commands
|
|
80
85
|
registerCodexCommand(pi);
|
|
81
86
|
// Session start hook
|
|
82
|
-
pi.on("session_start", async (_event) => {
|
|
87
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
83
88
|
const accounts = getAllAccounts();
|
|
84
89
|
if (accounts.length === 0) {
|
|
85
90
|
return;
|
|
86
91
|
}
|
|
87
92
|
// Refresh any expiring accounts immediately
|
|
88
|
-
await refreshExpiringAccounts();
|
|
93
|
+
await refreshExpiringAccounts(ctx);
|
|
89
94
|
// Sync to auth.json
|
|
90
|
-
await syncAccountsToAuth(getAllAccounts());
|
|
95
|
+
const synced = await syncAccountsToAuth(getAllAccounts());
|
|
96
|
+
if (synced) {
|
|
97
|
+
await reloadLiveAuthState(ctx);
|
|
98
|
+
}
|
|
91
99
|
// Start background refresh timer
|
|
92
|
-
startRefreshTimer();
|
|
100
|
+
startRefreshTimer(ctx);
|
|
93
101
|
});
|
|
94
102
|
// Session shutdown hook
|
|
95
103
|
pi.on("session_shutdown", () => {
|
|
96
104
|
stopRefreshTimer();
|
|
97
105
|
});
|
|
98
|
-
// Agent end hook
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const messages = event.messages;
|
|
102
|
-
const lastAssistant = [...messages].reverse().find((message) => message.role === "assistant");
|
|
103
|
-
if (!lastAssistant || !("errorMessage" in lastAssistant) || !lastAssistant.errorMessage)
|
|
104
|
-
return;
|
|
105
|
-
const errorMessage = lastAssistant.errorMessage;
|
|
106
|
-
if (!shouldBackoffCredential(errorMessage))
|
|
107
|
-
return;
|
|
108
|
-
const errorType = classifyError(errorMessage);
|
|
109
|
-
const sessionId = ctx.sessionManager.getSessionId();
|
|
110
|
-
const anotherAvailable = markCredentialBackoff(PROVIDER_NAME, sessionId, errorType);
|
|
111
|
-
if (anotherAvailable) {
|
|
112
|
-
logCodexRotateSwitch(`Auto-switched account after ${errorType} error`);
|
|
113
|
-
ctx.ui.notify("Codex credential backed off, rotating to next account", "info");
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
logCodexRotateError(`All accounts backed off after ${errorType} error`);
|
|
117
|
-
ctx.ui.notify("All Codex credentials are backed off. Please wait before retrying.", "warning");
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
logCodexRotateError("Error in agent_end handler:", error);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
106
|
+
// Agent end hook intentionally omitted.
|
|
107
|
+
// Credential backoff + same-turn retry now lives in the core RetryHandler,
|
|
108
|
+
// which knows the actual credential index used for the current session.
|
|
124
109
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OAuth flow wrapper for Codex account management
|
|
2
|
+
* OAuth flow wrapper for Codex account management.
|
|
3
|
+
*
|
|
4
|
+
* Uses authStorage.login('openai-codex', callbacks) — the exact same code
|
|
5
|
+
* path as the onboarding wizard — so the browser auto-redirect flow, PKCE
|
|
6
|
+
* exchange, and manual-paste fallback all work identically.
|
|
3
7
|
*/
|
|
4
|
-
import {
|
|
8
|
+
import { refreshOpenAICodexToken } from "@gsd/pi-ai/oauth";
|
|
5
9
|
import { logCodexRotateError } from "./logger.js";
|
|
6
10
|
function getAccountId(credentials) {
|
|
7
11
|
if (typeof credentials.accountId !== "string" || credentials.accountId.length === 0) {
|
|
@@ -9,6 +13,14 @@ function getAccountId(credentials) {
|
|
|
9
13
|
}
|
|
10
14
|
return credentials.accountId;
|
|
11
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Get the AuthStorage instance (same pattern as quota.ts)
|
|
18
|
+
*/
|
|
19
|
+
async function getAuthStorage() {
|
|
20
|
+
const specifier = "@gsd/pi-coding-agent/dist/core/auth-storage.js";
|
|
21
|
+
const mod = await import(/* webpackIgnore: true */ specifier);
|
|
22
|
+
return new mod.AuthStorage();
|
|
23
|
+
}
|
|
12
24
|
function asObject(value) {
|
|
13
25
|
return value !== null && typeof value === "object" ? value : null;
|
|
14
26
|
}
|
|
@@ -19,23 +31,68 @@ function getRequiredRefreshToken(data) {
|
|
|
19
31
|
const refreshToken = data.refreshToken ?? data.refresh_token;
|
|
20
32
|
return typeof refreshToken === "string" && refreshToken.length > 0 ? refreshToken : null;
|
|
21
33
|
}
|
|
34
|
+
/** Open a URL in the system browser (best-effort, non-blocking) */
|
|
35
|
+
async function openBrowser(url) {
|
|
36
|
+
try {
|
|
37
|
+
const { execFile } = await import("node:child_process");
|
|
38
|
+
if (process.platform === "win32") {
|
|
39
|
+
execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => { });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
43
|
+
execFile(cmd, [url], () => { });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Browser open failed — URL still shown via onStatus
|
|
48
|
+
}
|
|
49
|
+
}
|
|
22
50
|
/**
|
|
23
|
-
* Perform OAuth login and return a new Codex account
|
|
51
|
+
* Perform OAuth login and return a new Codex account.
|
|
52
|
+
*
|
|
53
|
+
* Delegates to authStorage.login('openai-codex', callbacks) — the exact same
|
|
54
|
+
* code path used by the onboarding wizard. After login completes, reads back
|
|
55
|
+
* the stored OAuth credential to extract the tokens for the account store.
|
|
24
56
|
*/
|
|
25
|
-
export async function performOAuthLogin(email) {
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
57
|
+
export async function performOAuthLogin(email, callbacks) {
|
|
58
|
+
const { onStatus, onManualCodeInput } = callbacks ?? {};
|
|
59
|
+
const authStorage = await getAuthStorage();
|
|
60
|
+
// Use authStorage.login() — the exact same path as onboarding
|
|
61
|
+
await authStorage.login("openai-codex", {
|
|
62
|
+
onAuth: (info) => {
|
|
63
|
+
onStatus?.(`Opening browser for Codex OAuth...\nURL: ${info.url}`);
|
|
64
|
+
openBrowser(info.url);
|
|
65
|
+
if (info.instructions) {
|
|
66
|
+
onStatus?.(info.instructions);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
onPrompt: async (prompt) => {
|
|
70
|
+
// Fallback: if onManualCodeInput is available, use it for the prompt too
|
|
71
|
+
if (onManualCodeInput) {
|
|
72
|
+
return onManualCodeInput();
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`OAuth browser flow failed: ${prompt.message}`);
|
|
30
75
|
},
|
|
31
|
-
onProgress: () => {
|
|
76
|
+
onProgress: (message) => {
|
|
77
|
+
onStatus?.(message);
|
|
78
|
+
},
|
|
79
|
+
onManualCodeInput,
|
|
32
80
|
});
|
|
81
|
+
// Read back the stored credential
|
|
82
|
+
const credential = authStorage.get("openai-codex");
|
|
83
|
+
if (!credential || credential.type !== "oauth") {
|
|
84
|
+
throw new Error("OAuth login succeeded but no credential was stored");
|
|
85
|
+
}
|
|
86
|
+
const { access, refresh, expires, accountId } = credential;
|
|
87
|
+
if (!access || !refresh || !accountId) {
|
|
88
|
+
throw new Error("Stored OAuth credential is missing required fields");
|
|
89
|
+
}
|
|
33
90
|
return {
|
|
34
91
|
email,
|
|
35
|
-
accountId:
|
|
36
|
-
refreshToken:
|
|
37
|
-
accessToken:
|
|
38
|
-
expiresAt:
|
|
92
|
+
accountId: accountId,
|
|
93
|
+
refreshToken: refresh,
|
|
94
|
+
accessToken: access,
|
|
95
|
+
expiresAt: expires,
|
|
39
96
|
};
|
|
40
97
|
}
|
|
41
98
|
/**
|
|
@@ -66,10 +66,11 @@ export function extractErrorFromResponse(response) {
|
|
|
66
66
|
/**
|
|
67
67
|
* Get the AuthStorage instance for marking usage limits
|
|
68
68
|
*/
|
|
69
|
-
function getAuthStorage() {
|
|
70
|
-
// Dynamic import to avoid top-level dependencies
|
|
71
|
-
const
|
|
72
|
-
|
|
69
|
+
async function getAuthStorage() {
|
|
70
|
+
// Dynamic import to avoid top-level dependencies (require not available in ESM context)
|
|
71
|
+
const specifier = "@gsd/pi-coding-agent/dist/core/auth-storage.js";
|
|
72
|
+
const mod = await import(/* webpackIgnore: true */ specifier);
|
|
73
|
+
return new mod.AuthStorage();
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
76
|
* Mark a credential as rate-limited/quota exhausted
|
|
@@ -77,9 +78,9 @@ function getAuthStorage() {
|
|
|
77
78
|
* This should be called when we detect a quota/rate limit error in the agent response.
|
|
78
79
|
* It will back off the credential and LSD will automatically rotate to the next one.
|
|
79
80
|
*/
|
|
80
|
-
export function markCredentialBackoff(provider, sessionId, errorType) {
|
|
81
|
+
export async function markCredentialBackoff(provider, sessionId, errorType) {
|
|
81
82
|
try {
|
|
82
|
-
const authStorage = getAuthStorage();
|
|
83
|
+
const authStorage = await getAuthStorage();
|
|
83
84
|
// Map CodexErrorType to AuthStorage error type
|
|
84
85
|
const authErrorType = errorType === "rate_limit"
|
|
85
86
|
? "rate_limit"
|
|
@@ -30,6 +30,12 @@ export async function syncAccountsToAuth(accounts) {
|
|
|
30
30
|
}
|
|
31
31
|
const credentials = accounts
|
|
32
32
|
.filter((acc) => !acc.disabled)
|
|
33
|
+
.sort((a, b) => {
|
|
34
|
+
const lastUsedDiff = (b.lastUsed ?? 0) - (a.lastUsed ?? 0);
|
|
35
|
+
if (lastUsedDiff !== 0)
|
|
36
|
+
return lastUsedDiff;
|
|
37
|
+
return b.addedAt - a.addedAt;
|
|
38
|
+
})
|
|
33
39
|
.map((acc) => ({
|
|
34
40
|
type: "api_key",
|
|
35
41
|
key: acc.accessToken,
|