@qduc/term2 0.15.0 → 0.17.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/README.md +324 -316
- package/dist/agent.d.ts +5 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +16 -64
- package/dist/agent.js.map +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +129 -19
- package/dist/app.js.map +1 -1
- package/dist/cli.js +52 -17
- package/dist/cli.js.map +1 -1
- package/dist/commands/auto-approve-command.d.ts +2 -1
- package/dist/commands/auto-approve-command.d.ts.map +1 -1
- package/dist/commands/auto-approve-command.js +16 -27
- package/dist/commands/auto-approve-command.js.map +1 -1
- package/dist/commands/compact-command.d.ts.map +1 -1
- package/dist/commands/compact-command.js +4 -0
- package/dist/commands/compact-command.js.map +1 -1
- package/dist/commands/effort-command.js +1 -1
- package/dist/commands/effort-command.js.map +1 -1
- package/dist/commands/guarded-settings-command.d.ts.map +1 -1
- package/dist/commands/guarded-settings-command.js +4 -8
- package/dist/commands/guarded-settings-command.js.map +1 -1
- package/dist/commands/model-command.js +1 -1
- package/dist/commands/model-command.js.map +1 -1
- package/dist/commands/resume-command.d.ts +11 -0
- package/dist/commands/resume-command.d.ts.map +1 -0
- package/dist/commands/resume-command.js +41 -0
- package/dist/commands/resume-command.js.map +1 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/ErrorBoundary.js +5 -4
- package/dist/components/ErrorBoundary.js.map +1 -1
- package/dist/components/InputBox.d.ts +2 -0
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +56 -18
- package/dist/components/InputBox.js.map +1 -1
- package/dist/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/components/MarkdownRenderer.js +16 -15
- package/dist/components/MarkdownRenderer.js.map +1 -1
- package/dist/components/common/MenuContainer.d.ts +25 -2
- package/dist/components/common/MenuContainer.d.ts.map +1 -1
- package/dist/components/common/MenuContainer.js +35 -11
- package/dist/components/common/MenuContainer.js.map +1 -1
- package/dist/components/common/ScrollableTabBar.d.ts.map +1 -1
- package/dist/components/common/ScrollableTabBar.js +5 -4
- package/dist/components/common/ScrollableTabBar.js.map +1 -1
- package/dist/components/input/ApplicationInputSurface.d.ts +7 -5
- package/dist/components/input/ApplicationInputSurface.d.ts.map +1 -1
- package/dist/components/input/ApplicationInputSurface.js +7 -0
- package/dist/components/input/ApplicationInputSurface.js.map +1 -1
- package/dist/components/input/FirstRunSetupPrompt.d.ts.map +1 -1
- package/dist/components/input/FirstRunSetupPrompt.js +5 -4
- package/dist/components/input/FirstRunSetupPrompt.js.map +1 -1
- package/dist/components/input/MenuSurface.d.ts.map +1 -1
- package/dist/components/input/MenuSurface.js +10 -3
- package/dist/components/input/MenuSurface.js.map +1 -1
- package/dist/components/input/ModelMenuSession.d.ts.map +1 -1
- package/dist/components/input/ModelMenuSession.js +2 -1
- package/dist/components/input/ModelMenuSession.js.map +1 -1
- package/dist/components/input/PendingQueueList.d.ts +2 -0
- package/dist/components/input/PendingQueueList.d.ts.map +1 -1
- package/dist/components/input/PendingQueueList.js +17 -14
- package/dist/components/input/PendingQueueList.js.map +1 -1
- package/dist/components/input/ResumeMenuSession.d.ts +17 -0
- package/dist/components/input/ResumeMenuSession.d.ts.map +1 -0
- package/dist/components/input/ResumeMenuSession.js +57 -0
- package/dist/components/input/ResumeMenuSession.js.map +1 -0
- package/dist/components/input/SettingsValueMenuSession.d.ts.map +1 -1
- package/dist/components/input/SettingsValueMenuSession.js +2 -1
- package/dist/components/input/SettingsValueMenuSession.js.map +1 -1
- package/dist/components/input/determine-active-menu.d.ts +3 -0
- package/dist/components/input/determine-active-menu.d.ts.map +1 -1
- package/dist/components/input/determine-active-menu.js +10 -5
- package/dist/components/input/determine-active-menu.js.map +1 -1
- package/dist/components/input/menu-registry.d.ts.map +1 -1
- package/dist/components/input/menu-registry.js +2 -0
- package/dist/components/input/menu-registry.js.map +1 -1
- package/dist/components/input/menu-types.d.ts +4 -0
- package/dist/components/input/menu-types.d.ts.map +1 -1
- package/dist/components/input/triggers.d.ts +1 -0
- package/dist/components/input/triggers.d.ts.map +1 -1
- package/dist/components/input/triggers.js +26 -0
- package/dist/components/input/triggers.js.map +1 -1
- package/dist/components/layout/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/components/layout/BackgroundTaskManager.js +12 -11
- package/dist/components/layout/BackgroundTaskManager.js.map +1 -1
- package/dist/components/layout/BackgroundTasksPanel.d.ts +2 -2
- package/dist/components/layout/BackgroundTasksPanel.d.ts.map +1 -1
- package/dist/components/layout/BackgroundTasksPanel.js +10 -9
- package/dist/components/layout/BackgroundTasksPanel.js.map +1 -1
- package/dist/components/layout/Banner.d.ts +0 -1
- package/dist/components/layout/Banner.d.ts.map +1 -1
- package/dist/components/layout/Banner.js +43 -73
- package/dist/components/layout/Banner.js.map +1 -1
- package/dist/components/layout/BottomArea.d.ts +8 -5
- package/dist/components/layout/BottomArea.d.ts.map +1 -1
- package/dist/components/layout/BottomArea.js +17 -9
- package/dist/components/layout/BottomArea.js.map +1 -1
- package/dist/components/layout/DiffView.js +5 -5
- package/dist/components/layout/DiffView.js.map +1 -1
- package/dist/components/layout/StatusBar.d.ts +4 -1
- package/dist/components/layout/StatusBar.d.ts.map +1 -1
- package/dist/components/layout/StatusBar.js +305 -95
- package/dist/components/layout/StatusBar.js.map +1 -1
- package/dist/components/layout/background-task-clock.d.ts +43 -0
- package/dist/components/layout/background-task-clock.d.ts.map +1 -0
- package/dist/components/layout/background-task-clock.js +46 -0
- package/dist/components/layout/background-task-clock.js.map +1 -0
- package/dist/components/menu/CopyMenu.d.ts.map +1 -1
- package/dist/components/menu/CopyMenu.js +4 -3
- package/dist/components/menu/CopyMenu.js.map +1 -1
- package/dist/components/menu/MentorPoolSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/MentorPoolSelectionMenu.js +28 -21
- package/dist/components/menu/MentorPoolSelectionMenu.js.map +1 -1
- package/dist/components/menu/ModelSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/ModelSelectionMenu.js +26 -13
- package/dist/components/menu/ModelSelectionMenu.js.map +1 -1
- package/dist/components/menu/PathSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/PathSelectionMenu.js +4 -3
- package/dist/components/menu/PathSelectionMenu.js.map +1 -1
- package/dist/components/menu/ProviderSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/ProviderSelectionMenu.js +46 -26
- package/dist/components/menu/ProviderSelectionMenu.js.map +1 -1
- package/dist/components/menu/ResumeSelectionMenu.d.ts +11 -0
- package/dist/components/menu/ResumeSelectionMenu.d.ts.map +1 -0
- package/dist/components/menu/ResumeSelectionMenu.js +91 -0
- package/dist/components/menu/ResumeSelectionMenu.js.map +1 -0
- package/dist/components/menu/RewindMenu.d.ts.map +1 -1
- package/dist/components/menu/RewindMenu.js +4 -3
- package/dist/components/menu/RewindMenu.js.map +1 -1
- package/dist/components/menu/SettingsSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/SettingsSelectionMenu.js +26 -26
- package/dist/components/menu/SettingsSelectionMenu.js.map +1 -1
- package/dist/components/menu/SettingsValueSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/SettingsValueSelectionMenu.js +22 -20
- package/dist/components/menu/SettingsValueSelectionMenu.js.map +1 -1
- package/dist/components/menu/SkillSelectionMenu.d.ts.map +1 -1
- package/dist/components/menu/SkillSelectionMenu.js +33 -23
- package/dist/components/menu/SkillSelectionMenu.js.map +1 -1
- package/dist/components/menu/SlashCommandMenu.d.ts.map +1 -1
- package/dist/components/menu/SlashCommandMenu.js +11 -6
- package/dist/components/menu/SlashCommandMenu.js.map +1 -1
- package/dist/components/message/ChatMessage.js +2 -2
- package/dist/components/message/ChatMessage.js.map +1 -1
- package/dist/components/message/CodeContextSearchRenderer.d.ts.map +1 -1
- package/dist/components/message/CodeContextSearchRenderer.js +8 -11
- package/dist/components/message/CodeContextSearchRenderer.js.map +1 -1
- package/dist/components/message/CommandGroupSummary.d.ts.map +1 -1
- package/dist/components/message/CommandGroupSummary.js +21 -9
- package/dist/components/message/CommandGroupSummary.js.map +1 -1
- package/dist/components/message/CommandMessage.d.ts.map +1 -1
- package/dist/components/message/CommandMessage.js +111 -91
- package/dist/components/message/CommandMessage.js.map +1 -1
- package/dist/components/message/GrepRenderer.d.ts.map +1 -1
- package/dist/components/message/GrepRenderer.js +5 -8
- package/dist/components/message/GrepRenderer.js.map +1 -1
- package/dist/components/message/MemoryRenderer.d.ts +1 -0
- package/dist/components/message/MemoryRenderer.d.ts.map +1 -1
- package/dist/components/message/MemoryRenderer.js +19 -17
- package/dist/components/message/MemoryRenderer.js.map +1 -1
- package/dist/components/message/MessageList.d.ts +1 -2
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +12 -11
- package/dist/components/message/MessageList.js.map +1 -1
- package/dist/components/message/ReadFileRenderer.d.ts.map +1 -1
- package/dist/components/message/ReadFileRenderer.js +5 -6
- package/dist/components/message/ReadFileRenderer.js.map +1 -1
- package/dist/components/message/SubagentActivityMessage.d.ts.map +1 -1
- package/dist/components/message/SubagentActivityMessage.js +17 -13
- package/dist/components/message/SubagentActivityMessage.js.map +1 -1
- package/dist/components/message/WebFetchRenderer.d.ts.map +1 -1
- package/dist/components/message/WebFetchRenderer.js +7 -11
- package/dist/components/message/WebFetchRenderer.js.map +1 -1
- package/dist/components/message/WebSearchRenderer.d.ts.map +1 -1
- package/dist/components/message/WebSearchRenderer.js +5 -10
- package/dist/components/message/WebSearchRenderer.js.map +1 -1
- package/dist/components/message/command-grouping.d.ts +29 -6
- package/dist/components/message/command-grouping.d.ts.map +1 -1
- package/dist/components/message/command-grouping.js +62 -20
- package/dist/components/message/command-grouping.js.map +1 -1
- package/dist/components/message/command-message-helpers.d.ts +10 -37
- package/dist/components/message/command-message-helpers.d.ts.map +1 -1
- package/dist/components/message/command-message-helpers.js +26 -8
- package/dist/components/message/command-message-helpers.js.map +1 -1
- package/dist/components/message/useCommandVisibility.d.ts.map +1 -1
- package/dist/components/message/useCommandVisibility.js +5 -4
- package/dist/components/message/useCommandVisibility.js.map +1 -1
- package/dist/components/prompt/ApprovalPrompt.d.ts.map +1 -1
- package/dist/components/prompt/ApprovalPrompt.js +232 -106
- package/dist/components/prompt/ApprovalPrompt.js.map +1 -1
- package/dist/components/prompt/HandoffConfirmationPrompt.d.ts.map +1 -1
- package/dist/components/prompt/HandoffConfirmationPrompt.js +3 -2
- package/dist/components/prompt/HandoffConfirmationPrompt.js.map +1 -1
- package/dist/components/prompt/InputSurgeConfirmationPrompt.d.ts.map +1 -1
- package/dist/components/prompt/InputSurgeConfirmationPrompt.js +5 -4
- package/dist/components/prompt/InputSurgeConfirmationPrompt.js.map +1 -1
- package/dist/components/prompt/LargeUncachedConfirmationPrompt.d.ts.map +1 -1
- package/dist/components/prompt/LargeUncachedConfirmationPrompt.js +4 -3
- package/dist/components/prompt/LargeUncachedConfirmationPrompt.js.map +1 -1
- package/dist/components/prompt/ModeSwitchConfirmationPrompt.d.ts.map +1 -1
- package/dist/components/prompt/ModeSwitchConfirmationPrompt.js +4 -3
- package/dist/components/prompt/ModeSwitchConfirmationPrompt.js.map +1 -1
- package/dist/components/prompt/QueuePausedPrompt.d.ts.map +1 -1
- package/dist/components/prompt/QueuePausedPrompt.js +6 -5
- package/dist/components/prompt/QueuePausedPrompt.js.map +1 -1
- package/dist/components/prompt/StandardModeConfirmationPrompt.d.ts.map +1 -1
- package/dist/components/prompt/StandardModeConfirmationPrompt.js +3 -2
- package/dist/components/prompt/StandardModeConfirmationPrompt.js.map +1 -1
- package/dist/components/theme.d.ts +58 -3
- package/dist/components/theme.d.ts.map +1 -1
- package/dist/components/theme.js +84 -17
- package/dist/components/theme.js.map +1 -1
- package/dist/context/InputContext.d.ts +1 -1
- package/dist/context/InputContext.d.ts.map +1 -1
- package/dist/context/InputContext.js +2 -0
- package/dist/context/InputContext.js.map +1 -1
- package/dist/contracts/application-stream.d.ts +14 -0
- package/dist/contracts/application-stream.d.ts.map +1 -1
- package/dist/contracts/streamed-model-turn.d.ts +13 -1
- package/dist/contracts/streamed-model-turn.d.ts.map +1 -1
- package/dist/gateway/assertion.d.ts +4 -1
- package/dist/gateway/assertion.d.ts.map +1 -1
- package/dist/gateway/assertion.js +24 -2
- package/dist/gateway/assertion.js.map +1 -1
- package/dist/gateway/contracts.d.ts +21 -4
- package/dist/gateway/contracts.d.ts.map +1 -1
- package/dist/gateway/contracts.js +11 -0
- package/dist/gateway/contracts.js.map +1 -1
- package/dist/gateway/dynamic-workspace-registry.d.ts +76 -0
- package/dist/gateway/dynamic-workspace-registry.d.ts.map +1 -0
- package/dist/gateway/dynamic-workspace-registry.js +403 -0
- package/dist/gateway/dynamic-workspace-registry.js.map +1 -0
- package/dist/gateway/gateway.d.ts +24 -1
- package/dist/gateway/gateway.d.ts.map +1 -1
- package/dist/gateway/gateway.js +704 -21
- package/dist/gateway/gateway.js.map +1 -1
- package/dist/gateway/launcher-seam.d.ts +18 -0
- package/dist/gateway/launcher-seam.d.ts.map +1 -0
- package/dist/gateway/launcher-seam.js +43 -0
- package/dist/gateway/launcher-seam.js.map +1 -0
- package/dist/gateway/pairing.d.ts +25 -0
- package/dist/gateway/pairing.d.ts.map +1 -0
- package/dist/gateway/pairing.js +107 -0
- package/dist/gateway/pairing.js.map +1 -0
- package/dist/gateway/persistence/coordinator.d.ts +2 -0
- package/dist/gateway/persistence/coordinator.d.ts.map +1 -1
- package/dist/gateway/persistence/coordinator.js +13 -0
- package/dist/gateway/persistence/coordinator.js.map +1 -1
- package/dist/gateway/runtime-factory.d.ts +15 -6
- package/dist/gateway/runtime-factory.d.ts.map +1 -1
- package/dist/gateway/runtime-factory.js +38 -9
- package/dist/gateway/runtime-factory.js.map +1 -1
- package/dist/gateway/safe-log.d.ts.map +1 -1
- package/dist/gateway/safe-log.js +19 -0
- package/dist/gateway/safe-log.js.map +1 -1
- package/dist/gateway/server-session.d.ts.map +1 -1
- package/dist/gateway/server-session.js +36 -4
- package/dist/gateway/server-session.js.map +1 -1
- package/dist/gateway/server.d.ts +26 -3
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +105 -17
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/settings-rpc.d.ts +95 -0
- package/dist/gateway/settings-rpc.d.ts.map +1 -0
- package/dist/gateway/settings-rpc.js +227 -0
- package/dist/gateway/settings-rpc.js.map +1 -0
- package/dist/gateway/trusted-clients.d.ts +27 -0
- package/dist/gateway/trusted-clients.d.ts.map +1 -0
- package/dist/gateway/trusted-clients.js +157 -0
- package/dist/gateway/trusted-clients.js.map +1 -0
- package/dist/gateway/worker-boundary.d.ts +6 -5
- package/dist/gateway/worker-boundary.d.ts.map +1 -1
- package/dist/gateway/worker-boundary.js +20 -10
- package/dist/gateway/worker-boundary.js.map +1 -1
- package/dist/gateway/workspace-admission.d.ts +11 -0
- package/dist/gateway/workspace-admission.d.ts.map +1 -1
- package/dist/gateway/workspace-admission.js +89 -16
- package/dist/gateway/workspace-admission.js.map +1 -1
- package/dist/gateway/workspace-boundary-probe.d.ts +18 -0
- package/dist/gateway/workspace-boundary-probe.d.ts.map +1 -0
- package/dist/gateway/workspace-boundary-probe.js +37 -0
- package/dist/gateway/workspace-boundary-probe.js.map +1 -0
- package/dist/hooks/conversation-ui-reducer.d.ts +6 -0
- package/dist/hooks/conversation-ui-reducer.d.ts.map +1 -1
- package/dist/hooks/conversation-ui-reducer.js +9 -1
- package/dist/hooks/conversation-ui-reducer.js.map +1 -1
- package/dist/hooks/settings-completion-config.d.ts.map +1 -1
- package/dist/hooks/settings-completion-config.js +11 -3
- package/dist/hooks/settings-completion-config.js.map +1 -1
- package/dist/hooks/use-app-commands.d.ts +6 -1
- package/dist/hooks/use-app-commands.d.ts.map +1 -1
- package/dist/hooks/use-app-commands.js +23 -14
- package/dist/hooks/use-app-commands.js.map +1 -1
- package/dist/hooks/use-app-keyboard-shortcuts.d.ts +1 -3
- package/dist/hooks/use-app-keyboard-shortcuts.d.ts.map +1 -1
- package/dist/hooks/use-app-keyboard-shortcuts.js +11 -9
- package/dist/hooks/use-app-keyboard-shortcuts.js.map +1 -1
- package/dist/hooks/use-conversation.d.ts +8 -0
- package/dist/hooks/use-conversation.d.ts.map +1 -1
- package/dist/hooks/use-conversation.js +27 -18
- package/dist/hooks/use-conversation.js.map +1 -1
- package/dist/hooks/use-opencode-go-usage.d.ts +8 -0
- package/dist/hooks/use-opencode-go-usage.d.ts.map +1 -0
- package/dist/hooks/use-opencode-go-usage.js +45 -0
- package/dist/hooks/use-opencode-go-usage.js.map +1 -0
- package/dist/hooks/use-resume-selection.d.ts +22 -0
- package/dist/hooks/use-resume-selection.d.ts.map +1 -0
- package/dist/hooks/use-resume-selection.js +76 -0
- package/dist/hooks/use-resume-selection.js.map +1 -0
- package/dist/hooks/use-shell-mode.d.ts +3 -4
- package/dist/hooks/use-shell-mode.d.ts.map +1 -1
- package/dist/hooks/use-shell-mode.js +14 -12
- package/dist/hooks/use-shell-mode.js.map +1 -1
- package/dist/hooks/use-terminal-columns.d.ts +15 -0
- package/dist/hooks/use-terminal-columns.d.ts.map +1 -0
- package/dist/hooks/use-terminal-columns.js +45 -0
- package/dist/hooks/use-terminal-columns.js.map +1 -0
- package/dist/lib/agent-client.d.ts +14 -1
- package/dist/lib/agent-client.d.ts.map +1 -1
- package/dist/lib/agent-client.js +112 -37
- package/dist/lib/agent-client.js.map +1 -1
- package/dist/lib/agent-configuration.d.ts +5 -0
- package/dist/lib/agent-configuration.d.ts.map +1 -1
- package/dist/lib/agent-configuration.js +14 -5
- package/dist/lib/agent-configuration.js.map +1 -1
- package/dist/lib/agent-factory.d.ts +7 -2
- package/dist/lib/agent-factory.d.ts.map +1 -1
- package/dist/lib/agent-factory.js +22 -11
- package/dist/lib/agent-factory.js.map +1 -1
- package/dist/lib/harness-input-idle.d.ts.map +1 -1
- package/dist/lib/harness-input-idle.js +1 -1
- package/dist/lib/harness-input-idle.js.map +1 -1
- package/dist/non-interactive.d.ts +3 -0
- package/dist/non-interactive.d.ts.map +1 -1
- package/dist/non-interactive.js +106 -22
- package/dist/non-interactive.js.map +1 -1
- package/dist/prompts/approval-model.md +22 -0
- package/dist/prompts/gpt-5.6.md +3 -2
- package/dist/prompts/mentor-mode-stub.md +3 -0
- package/dist/prompts/orchestrator-mode-stub.md +3 -0
- package/dist/prompts/prefix-quality.test.ts +10 -0
- package/dist/prompts/prompt-constructor.d.ts +2 -0
- package/dist/prompts/prompt-constructor.d.ts.map +1 -1
- package/dist/prompts/prompt-constructor.js +23 -13
- package/dist/prompts/prompt-constructor.js.map +1 -1
- package/dist/prompts/prompt-constructor.test.ts +39 -5
- package/dist/prompts/prompt-constructor.ts +27 -15
- package/dist/prompts/prompt-profiles.d.ts +1 -3
- package/dist/prompts/prompt-profiles.d.ts.map +1 -1
- package/dist/prompts/prompt-profiles.js +2 -7
- package/dist/prompts/prompt-profiles.js.map +1 -1
- package/dist/prompts/prompt-profiles.ts +3 -16
- package/dist/prompts/session-browser.md +3 -0
- package/dist/prompts/subagent-delegation.d.ts +4 -3
- package/dist/prompts/subagent-delegation.d.ts.map +1 -1
- package/dist/prompts/subagent-delegation.js +14 -18
- package/dist/prompts/subagent-delegation.js.map +1 -1
- package/dist/prompts/subagent-delegation.test.ts +2 -2
- package/dist/prompts/subagent-delegation.ts +13 -20
- package/dist/providers/chained-wire-state.d.ts +7 -2
- package/dist/providers/chained-wire-state.d.ts.map +1 -1
- package/dist/providers/chained-wire-state.js +16 -7
- package/dist/providers/chained-wire-state.js.map +1 -1
- package/dist/providers/codex-auth.d.ts +2 -5
- package/dist/providers/codex-auth.d.ts.map +1 -1
- package/dist/providers/codex-auth.js +2 -0
- package/dist/providers/codex-auth.js.map +1 -1
- package/dist/providers/codex-compact.d.ts +9 -0
- package/dist/providers/codex-compact.d.ts.map +1 -0
- package/dist/providers/codex-compact.js +23 -0
- package/dist/providers/codex-compact.js.map +1 -0
- package/dist/providers/codex-responses-model.d.ts +17 -1
- package/dist/providers/codex-responses-model.d.ts.map +1 -1
- package/dist/providers/codex-responses-model.js +75 -8
- package/dist/providers/codex-responses-model.js.map +1 -1
- package/dist/providers/codex-turn-converter.d.ts.map +1 -1
- package/dist/providers/codex-turn-converter.js +8 -4
- package/dist/providers/codex-turn-converter.js.map +1 -1
- package/dist/providers/codex.provider.js +2 -2
- package/dist/providers/codex.provider.js.map +1 -1
- package/dist/providers/grok-auth.d.ts +2 -5
- package/dist/providers/grok-auth.d.ts.map +1 -1
- package/dist/providers/grok-auth.js +2 -0
- package/dist/providers/grok-auth.js.map +1 -1
- package/dist/providers/oauth-account-store.d.ts +5 -0
- package/dist/providers/oauth-account-store.d.ts.map +1 -1
- package/dist/providers/oauth-account-store.js +56 -5
- package/dist/providers/oauth-account-store.js.map +1 -1
- package/dist/providers/oauth-pkce.d.ts +8 -0
- package/dist/providers/oauth-pkce.d.ts.map +1 -1
- package/dist/providers/oauth-pkce.js +91 -19
- package/dist/providers/oauth-pkce.js.map +1 -1
- package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
- package/dist/providers/openai-compatible.provider.js +8 -1
- package/dist/providers/openai-compatible.provider.js.map +1 -1
- package/dist/providers/openai-responses-model.d.ts.map +1 -1
- package/dist/providers/openai-responses-model.js +2 -0
- package/dist/providers/openai-responses-model.js.map +1 -1
- package/dist/providers/opencode-go-usage.d.ts +24 -0
- package/dist/providers/opencode-go-usage.d.ts.map +1 -0
- package/dist/providers/opencode-go-usage.js +37 -0
- package/dist/providers/opencode-go-usage.js.map +1 -0
- package/dist/providers/openrouter.provider.d.ts.map +1 -1
- package/dist/providers/openrouter.provider.js +8 -1
- package/dist/providers/openrouter.provider.js.map +1 -1
- package/dist/providers/responses-websocket-sessions.d.ts +1 -0
- package/dist/providers/responses-websocket-sessions.d.ts.map +1 -1
- package/dist/providers/responses-websocket-sessions.js +16 -2
- package/dist/providers/responses-websocket-sessions.js.map +1 -1
- package/dist/providers/retrying-model.d.ts +1 -0
- package/dist/providers/retrying-model.d.ts.map +1 -1
- package/dist/providers/retrying-model.js +37 -3
- package/dist/providers/retrying-model.js.map +1 -1
- package/dist/services/agent-runtime/application-run-loop.d.ts +5 -1
- package/dist/services/agent-runtime/application-run-loop.d.ts.map +1 -1
- package/dist/services/agent-runtime/application-run-loop.js +231 -149
- package/dist/services/agent-runtime/application-run-loop.js.map +1 -1
- package/dist/services/agent-runtime/generation-guard.d.ts +39 -25
- package/dist/services/agent-runtime/generation-guard.d.ts.map +1 -1
- package/dist/services/agent-runtime/generation-guard.js +98 -100
- package/dist/services/agent-runtime/generation-guard.js.map +1 -1
- package/dist/services/agent-runtime/run-budget.d.ts +1 -1
- package/dist/services/agent-runtime/run-budget.d.ts.map +1 -1
- package/dist/services/agent-runtime/run-budget.js +4 -0
- package/dist/services/agent-runtime/run-budget.js.map +1 -1
- package/dist/services/agent-stream.d.ts.map +1 -1
- package/dist/services/agent-stream.js +1 -0
- package/dist/services/agent-stream.js.map +1 -1
- package/dist/services/approval/shell-auto-approval-evaluator.d.ts +3 -1
- package/dist/services/approval/shell-auto-approval-evaluator.d.ts.map +1 -1
- package/dist/services/approval/shell-auto-approval-evaluator.js +4 -2
- package/dist/services/approval/shell-auto-approval-evaluator.js.map +1 -1
- package/dist/services/approval/shell-auto-approval-resolver.d.ts +2 -0
- package/dist/services/approval/shell-auto-approval-resolver.d.ts.map +1 -1
- package/dist/services/approval/shell-auto-approval-resolver.js +2 -0
- package/dist/services/approval/shell-auto-approval-resolver.js.map +1 -1
- package/dist/services/conversation/conversation-adapter.d.ts +13 -2
- package/dist/services/conversation/conversation-adapter.d.ts.map +1 -1
- package/dist/services/conversation/conversation-adapter.js +35 -7
- package/dist/services/conversation/conversation-adapter.js.map +1 -1
- package/dist/services/conversation/conversation-admission-workflow.d.ts +3 -2
- package/dist/services/conversation/conversation-admission-workflow.d.ts.map +1 -1
- package/dist/services/conversation/conversation-admission-workflow.js +6 -1
- package/dist/services/conversation/conversation-admission-workflow.js.map +1 -1
- package/dist/services/conversation/conversation-events.d.ts +43 -2
- package/dist/services/conversation/conversation-events.d.ts.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.d.ts +14 -1
- package/dist/services/conversation/conversation-orchestrator.d.ts.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.js +142 -11
- package/dist/services/conversation/conversation-orchestrator.js.map +1 -1
- package/dist/services/conversation/conversation-orchestrator.types.d.ts +3 -1
- package/dist/services/conversation/conversation-orchestrator.types.d.ts.map +1 -1
- package/dist/services/conversation/conversation-persistence.d.ts +22 -1
- package/dist/services/conversation/conversation-persistence.d.ts.map +1 -1
- package/dist/services/conversation/conversation-persistence.js +61 -0
- package/dist/services/conversation/conversation-persistence.js.map +1 -1
- package/dist/services/conversation/conversation-service.d.ts.map +1 -1
- package/dist/services/conversation/conversation-service.js +45 -29
- package/dist/services/conversation/conversation-service.js.map +1 -1
- package/dist/services/conversation/session-audit.d.ts +70 -0
- package/dist/services/conversation/session-audit.d.ts.map +1 -0
- package/dist/services/conversation/session-audit.js +164 -0
- package/dist/services/conversation/session-audit.js.map +1 -0
- package/dist/services/conversation/session-browser.d.ts +49 -0
- package/dist/services/conversation/session-browser.d.ts.map +1 -0
- package/dist/services/conversation/session-browser.js +411 -0
- package/dist/services/conversation/session-browser.js.map +1 -0
- package/dist/services/input-surge-approval.d.ts +21 -0
- package/dist/services/input-surge-approval.d.ts.map +1 -0
- package/dist/services/input-surge-approval.js +42 -0
- package/dist/services/input-surge-approval.js.map +1 -0
- package/dist/services/large-uncached-input-guard.js +4 -4
- package/dist/services/large-uncached-input-guard.js.map +1 -1
- package/dist/services/logging/assistant-turn-journal.d.ts +15 -0
- package/dist/services/logging/assistant-turn-journal.d.ts.map +1 -1
- package/dist/services/logging/assistant-turn-journal.js +27 -0
- package/dist/services/logging/assistant-turn-journal.js.map +1 -1
- package/dist/services/memory/memory-capabilities.d.ts.map +1 -1
- package/dist/services/memory/memory-capabilities.js +7 -4
- package/dist/services/memory/memory-capabilities.js.map +1 -1
- package/dist/services/memory/memory-search.d.ts +16 -0
- package/dist/services/memory/memory-search.d.ts.map +1 -0
- package/dist/services/memory/memory-search.js +55 -0
- package/dist/services/memory/memory-search.js.map +1 -0
- package/dist/services/memory/memory-store.d.ts +12 -0
- package/dist/services/memory/memory-store.d.ts.map +1 -1
- package/dist/services/memory/memory-store.js +14 -35
- package/dist/services/memory/memory-store.js.map +1 -1
- package/dist/services/mode-notices.d.ts +11 -0
- package/dist/services/mode-notices.d.ts.map +1 -1
- package/dist/services/mode-notices.js +37 -3
- package/dist/services/mode-notices.js.map +1 -1
- package/dist/services/queue/queue-controller.d.ts +13 -0
- package/dist/services/queue/queue-controller.d.ts.map +1 -1
- package/dist/services/queue/queue-controller.js +32 -1
- package/dist/services/queue/queue-controller.js.map +1 -1
- package/dist/services/retry/in-loop-model-retry.d.ts +16 -0
- package/dist/services/retry/in-loop-model-retry.d.ts.map +1 -0
- package/dist/services/retry/in-loop-model-retry.js +125 -0
- package/dist/services/retry/in-loop-model-retry.js.map +1 -0
- package/dist/services/retry/retry-classifier.d.ts.map +1 -1
- package/dist/services/retry/retry-classifier.js +18 -12
- package/dist/services/retry/retry-classifier.js.map +1 -1
- package/dist/services/retry/retry-error-classification.d.ts.map +1 -1
- package/dist/services/retry/retry-error-classification.js +10 -6
- package/dist/services/retry/retry-error-classification.js.map +1 -1
- package/dist/services/retry/upstream-retry-policy.d.ts.map +1 -1
- package/dist/services/retry/upstream-retry-policy.js +40 -7
- package/dist/services/retry/upstream-retry-policy.js.map +1 -1
- package/dist/services/runtime-setting-router.d.ts.map +1 -1
- package/dist/services/runtime-setting-router.js +7 -2
- package/dist/services/runtime-setting-router.js.map +1 -1
- package/dist/services/session/background-check-in-scheduler.d.ts +44 -0
- package/dist/services/session/background-check-in-scheduler.d.ts.map +1 -0
- package/dist/services/session/background-check-in-scheduler.js +88 -0
- package/dist/services/session/background-check-in-scheduler.js.map +1 -0
- package/dist/services/session/background-task-control.js +4 -1
- package/dist/services/session/background-task-control.js.map +1 -1
- package/dist/services/session/initial-input-preparer.d.ts +2 -1
- package/dist/services/session/initial-input-preparer.d.ts.map +1 -1
- package/dist/services/session/initial-input-preparer.js +4 -1
- package/dist/services/session/initial-input-preparer.js.map +1 -1
- package/dist/services/session/repetition-detector.d.ts +0 -4
- package/dist/services/session/repetition-detector.d.ts.map +1 -1
- package/dist/services/session/repetition-detector.js +0 -7
- package/dist/services/session/repetition-detector.js.map +1 -1
- package/dist/services/session/session-access-state.d.ts +3 -0
- package/dist/services/session/session-access-state.d.ts.map +1 -1
- package/dist/services/session/session-access-state.js +12 -0
- package/dist/services/session/session-access-state.js.map +1 -1
- package/dist/services/session/session-client-factory.d.ts +3 -1
- package/dist/services/session/session-client-factory.d.ts.map +1 -1
- package/dist/services/session/session-client-factory.js +2 -1
- package/dist/services/session/session-client-factory.js.map +1 -1
- package/dist/services/session/session-composition.d.ts +6 -2
- package/dist/services/session/session-composition.d.ts.map +1 -1
- package/dist/services/session/session-composition.js +148 -29
- package/dist/services/session/session-composition.js.map +1 -1
- package/dist/services/session/session-input-planner.d.ts +9 -0
- package/dist/services/session/session-input-planner.d.ts.map +1 -1
- package/dist/services/session/session-input-planner.js +23 -0
- package/dist/services/session/session-input-planner.js.map +1 -1
- package/dist/services/session/session-lifecycle.d.ts +8 -0
- package/dist/services/session/session-lifecycle.d.ts.map +1 -1
- package/dist/services/session/session-lifecycle.js +4 -0
- package/dist/services/session/session-lifecycle.js.map +1 -1
- package/dist/services/session/session-stream-processor.d.ts.map +1 -1
- package/dist/services/session/session-stream-processor.js +5 -15
- package/dist/services/session/session-stream-processor.js.map +1 -1
- package/dist/services/session/turn-attempt-factory.d.ts +2 -1
- package/dist/services/session/turn-attempt-factory.d.ts.map +1 -1
- package/dist/services/session/turn-attempt-factory.js +1 -0
- package/dist/services/session/turn-attempt-factory.js.map +1 -1
- package/dist/services/session/turn-attempt.d.ts +3 -0
- package/dist/services/session/turn-attempt.d.ts.map +1 -1
- package/dist/services/session/turn-attempt.js +5 -0
- package/dist/services/session/turn-attempt.js.map +1 -1
- package/dist/services/session/turn-coordinator.d.ts +1 -1
- package/dist/services/session/turn-coordinator.d.ts.map +1 -1
- package/dist/services/session/turn-workflow.d.ts.map +1 -1
- package/dist/services/session/turn-workflow.js +4 -1
- package/dist/services/session/turn-workflow.js.map +1 -1
- package/dist/services/settings/settings-schema.d.ts +27 -3
- package/dist/services/settings/settings-schema.d.ts.map +1 -1
- package/dist/services/settings/settings-schema.js +35 -11
- package/dist/services/settings/settings-schema.js.map +1 -1
- package/dist/services/settings/settings-service.d.ts +9 -0
- package/dist/services/settings/settings-service.d.ts.map +1 -1
- package/dist/services/settings/settings-service.js +40 -0
- package/dist/services/settings/settings-service.js.map +1 -1
- package/dist/services/settings/settings-sources.d.ts.map +1 -1
- package/dist/services/settings/settings-sources.js +5 -0
- package/dist/services/settings/settings-sources.js.map +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.d.ts +2 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.d.ts.map +1 -1
- package/dist/services/settings/test-helpers/settings-consumer-inventory.js +6 -0
- package/dist/services/settings/test-helpers/settings-consumer-inventory.js.map +1 -1
- package/dist/services/shell/shell-interaction-session.d.ts +4 -5
- package/dist/services/shell/shell-interaction-session.d.ts.map +1 -1
- package/dist/services/shell/shell-interaction-session.js +8 -20
- package/dist/services/shell/shell-interaction-session.js.map +1 -1
- package/dist/services/stream-event-processor.d.ts.map +1 -1
- package/dist/services/stream-event-processor.js +15 -0
- package/dist/services/stream-event-processor.js.map +1 -1
- package/dist/services/subagents/mentor-runner.d.ts.map +1 -1
- package/dist/services/subagents/mentor-runner.js +2 -0
- package/dist/services/subagents/mentor-runner.js.map +1 -1
- package/dist/services/subagents/nested-runner.d.ts.map +1 -1
- package/dist/services/subagents/nested-runner.js +34 -1
- package/dist/services/subagents/nested-runner.js.map +1 -1
- package/dist/services/subagents/subagent-async-registry.js +1 -1
- package/dist/services/subagents/subagent-async-registry.js.map +1 -1
- package/dist/services/subagents/subagent-notification-store.d.ts +28 -1
- package/dist/services/subagents/subagent-notification-store.d.ts.map +1 -1
- package/dist/services/subagents/subagent-notification-store.js +11 -0
- package/dist/services/subagents/subagent-notification-store.js.map +1 -1
- package/dist/services/subagents/tool-policy.d.ts.map +1 -1
- package/dist/services/subagents/tool-policy.js +10 -5
- package/dist/services/subagents/tool-policy.js.map +1 -1
- package/dist/services/subagents/utils.d.ts +2 -0
- package/dist/services/subagents/utils.d.ts.map +1 -1
- package/dist/services/subagents/utils.js +29 -0
- package/dist/services/subagents/utils.js.map +1 -1
- package/dist/services/tool-execution-ledger.d.ts.map +1 -1
- package/dist/services/tool-execution-ledger.js +5 -1
- package/dist/services/tool-execution-ledger.js.map +1 -1
- package/dist/slash-commands.d.ts +3 -0
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js.map +1 -1
- package/dist/tools/agent/ask-user.d.ts.map +1 -1
- package/dist/tools/agent/ask-user.js +2 -0
- package/dist/tools/agent/ask-user.js.map +1 -1
- package/dist/tools/agent/run-subagent-async.d.ts +0 -1
- package/dist/tools/agent/run-subagent-async.d.ts.map +1 -1
- package/dist/tools/agent/run-subagent-async.js +3 -3
- package/dist/tools/agent/run-subagent-async.js.map +1 -1
- package/dist/tools/agent/run-subagent.d.ts +2 -2
- package/dist/tools/agent/run-subagent.d.ts.map +1 -1
- package/dist/tools/agent/run-subagent.js +44 -28
- package/dist/tools/agent/run-subagent.js.map +1 -1
- package/dist/tools/file/apply-patch.d.ts.map +1 -1
- package/dist/tools/file/apply-patch.js +11 -3
- package/dist/tools/file/apply-patch.js.map +1 -1
- package/dist/tools/file/create-file.d.ts.map +1 -1
- package/dist/tools/file/create-file.js +12 -4
- package/dist/tools/file/create-file.js.map +1 -1
- package/dist/tools/file/search-replace.d.ts.map +1 -1
- package/dist/tools/file/search-replace.js +10 -3
- package/dist/tools/file/search-replace.js.map +1 -1
- package/dist/tools/memory/memory-tools.d.ts +5 -1
- package/dist/tools/memory/memory-tools.d.ts.map +1 -1
- package/dist/tools/memory/memory-tools.js +445 -62
- package/dist/tools/memory/memory-tools.js.map +1 -1
- package/dist/tools/session-browser/session-browser-tools.d.ts +4 -0
- package/dist/tools/session-browser/session-browser-tools.d.ts.map +1 -0
- package/dist/tools/session-browser/session-browser-tools.js +47 -0
- package/dist/tools/session-browser/session-browser-tools.js.map +1 -0
- package/dist/tools/system/shell.d.ts.map +1 -1
- package/dist/tools/system/shell.js +7 -0
- package/dist/tools/system/shell.js.map +1 -1
- package/dist/tools/types.d.ts +4 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/utils/busy-turn-guard.d.ts +17 -0
- package/dist/utils/busy-turn-guard.d.ts.map +1 -0
- package/dist/utils/busy-turn-guard.js +24 -0
- package/dist/utils/busy-turn-guard.js.map +1 -0
- package/dist/utils/conversation/conversation-event-handler.d.ts.map +1 -1
- package/dist/utils/conversation/conversation-event-handler.js +28 -7
- package/dist/utils/conversation/conversation-event-handler.js.map +1 -1
- package/dist/utils/output/bounded-json.d.ts +33 -0
- package/dist/utils/output/bounded-json.d.ts.map +1 -0
- package/dist/utils/output/bounded-json.js +59 -0
- package/dist/utils/output/bounded-json.js.map +1 -0
- package/dist/utils/output/output-trim.d.ts.map +1 -1
- package/dist/utils/output/output-trim.js +7 -7
- package/dist/utils/output/output-trim.js.map +1 -1
- package/dist/utils/output/text-snippet.d.ts +6 -0
- package/dist/utils/output/text-snippet.d.ts.map +1 -0
- package/dist/utils/output/text-snippet.js +106 -0
- package/dist/utils/output/text-snippet.js.map +1 -0
- package/dist/utils/resume-list.d.ts +3 -1
- package/dist/utils/resume-list.d.ts.map +1 -1
- package/dist/utils/resume-list.js +3 -2
- package/dist/utils/resume-list.js.map +1 -1
- package/dist/utils/settings-command.d.ts.map +1 -1
- package/dist/utils/settings-command.js +15 -0
- package/dist/utils/settings-command.js.map +1 -1
- package/dist/utils/shell/command-safety/handlers/index.d.ts.map +1 -1
- package/dist/utils/shell/command-safety/handlers/index.js +3 -0
- package/dist/utils/shell/command-safety/handlers/index.js.map +1 -1
- package/dist/utils/shell/command-safety/handlers/rm-handler.d.ts +3 -0
- package/dist/utils/shell/command-safety/handlers/rm-handler.d.ts.map +1 -0
- package/dist/utils/shell/command-safety/handlers/rm-handler.js +97 -0
- package/dist/utils/shell/command-safety/handlers/rm-handler.js.map +1 -0
- package/dist/utils/shell/command-safety/handlers/types.d.ts +1 -0
- package/dist/utils/shell/command-safety/handlers/types.d.ts.map +1 -1
- package/dist/utils/shell/command-safety/index.d.ts +6 -2
- package/dist/utils/shell/command-safety/index.d.ts.map +1 -1
- package/dist/utils/shell/command-safety/index.js +13 -12
- package/dist/utils/shell/command-safety/index.js.map +1 -1
- package/dist/utils/shell/sandbox/sandbox-policy.d.ts +2 -0
- package/dist/utils/shell/sandbox/sandbox-policy.d.ts.map +1 -1
- package/dist/utils/shell/sandbox/sandbox-policy.js.map +1 -1
- package/dist/utils/shell/sandbox/shell-sandbox-runner.d.ts +1 -0
- package/dist/utils/shell/sandbox/shell-sandbox-runner.d.ts.map +1 -1
- package/dist/utils/shell/sandbox/shell-sandbox-runner.js +20 -1
- package/dist/utils/shell/sandbox/shell-sandbox-runner.js.map +1 -1
- package/dist/utils/shell/temp-dir.d.ts +1 -0
- package/dist/utils/shell/temp-dir.d.ts.map +1 -1
- package/dist/utils/shell/temp-dir.js +12 -4
- package/dist/utils/shell/temp-dir.js.map +1 -1
- package/dist/utils/value-suggestions.d.ts.map +1 -1
- package/dist/utils/value-suggestions.js +1 -0
- package/dist/utils/value-suggestions.js.map +1 -1
- package/package.json +4 -2
package/dist/gateway/gateway.js
CHANGED
|
@@ -14,6 +14,18 @@ import { GatewayAdmissionPersistence } from './persistence/admission-persistence
|
|
|
14
14
|
import { GATEWAY_EVENT_HEARTBEAT_INTERVAL_MS, GATEWAY_EVENT_STREAM_CONTENT_TYPE, GATEWAY_EVENT_STREAM_HEARTBEAT, } from './server.js';
|
|
15
15
|
import { ServerSession } from './server-session.js';
|
|
16
16
|
import { ServerSessionError } from './server-session.js';
|
|
17
|
+
import { DynamicWorkspaceRegistry, DynamicWorkspaceRegistryError } from './dynamic-workspace-registry.js';
|
|
18
|
+
import { accessSync, constants as fsConstants, realpathSync, statSync } from 'node:fs';
|
|
19
|
+
import { ModelCatalogSession } from '../services/models/model-catalog-session.js';
|
|
20
|
+
import { getAvailableProviderIds } from '../utils/ai/provider-credentials.js';
|
|
21
|
+
import { getProviderIds } from '../providers/index.js';
|
|
22
|
+
import { LoggingService } from '../services/logging/logging-service.js';
|
|
23
|
+
import { applySettingsChanges, buildSettingsProjection, deleteCredential, setCredential, SettingsRpcError, } from './settings-rpc.js';
|
|
24
|
+
import { GatewayPairing, PairingError } from './pairing.js';
|
|
25
|
+
import { TrustedClientsStore } from './trusted-clients.js';
|
|
26
|
+
import { isOAuthAccountProvider, listOAuthAccounts, removeOAuthAccount, setActiveOAuthAccount, } from '../providers/oauth-accounts.js';
|
|
27
|
+
import { loginToCodex } from '../providers/codex-auth.js';
|
|
28
|
+
import { loginToGrok } from '../providers/grok-auth.js';
|
|
17
29
|
const MAX_BUFFERED_SSE_EVENTS = 256;
|
|
18
30
|
const NONCRITICAL_RUNTIME_EVENT_TYPES = new Set(['text_delta', 'reasoning_delta']);
|
|
19
31
|
const isCriticalRuntimeEvent = (event) => !NONCRITICAL_RUNTIME_EVENT_TYPES.has(event.type);
|
|
@@ -80,6 +92,15 @@ const PUBLIC_EVENT_PAYLOAD_KEYS = new Set([
|
|
|
80
92
|
'deniedRead',
|
|
81
93
|
'runBudgetEvidence',
|
|
82
94
|
]);
|
|
95
|
+
const LOCAL_OWNER_PURPOSES = new Set([
|
|
96
|
+
'settings_read',
|
|
97
|
+
'settings_write',
|
|
98
|
+
'credential_write',
|
|
99
|
+
'credential_delete',
|
|
100
|
+
'oauth_login',
|
|
101
|
+
'oauth_select',
|
|
102
|
+
'oauth_delete',
|
|
103
|
+
]);
|
|
83
104
|
export class GatewayStartupError extends Error {
|
|
84
105
|
constructor() {
|
|
85
106
|
super('gateway prerequisites unavailable');
|
|
@@ -87,16 +108,61 @@ export class GatewayStartupError extends Error {
|
|
|
87
108
|
}
|
|
88
109
|
}
|
|
89
110
|
export function assertGatewayStartup(config, manifest) {
|
|
90
|
-
|
|
111
|
+
const socketTransport = config.socketPath !== undefined;
|
|
112
|
+
const networkTransport = config.host !== undefined || config.port !== undefined || config.tls !== undefined;
|
|
113
|
+
if (socketTransport === networkTransport)
|
|
114
|
+
throw new GatewayStartupError();
|
|
115
|
+
if (socketTransport) {
|
|
116
|
+
if (!config.socketPath.startsWith('/'))
|
|
117
|
+
throw new GatewayStartupError();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
if ((config.host !== undefined && !config.host.trim()) ||
|
|
121
|
+
!Number.isSafeInteger(config.port) ||
|
|
122
|
+
config.port < 1 ||
|
|
123
|
+
config.port > 65_535 ||
|
|
124
|
+
!config.tls ||
|
|
125
|
+
!config.tls.certPath ||
|
|
126
|
+
!config.tls.keyPath ||
|
|
127
|
+
(config.tls.caPath !== undefined && !config.tls.caPath) ||
|
|
128
|
+
typeof config.tls.requireClientCert !== 'boolean')
|
|
129
|
+
throw new GatewayStartupError();
|
|
130
|
+
for (const filePath of [
|
|
131
|
+
config.tls.certPath,
|
|
132
|
+
config.tls.keyPath,
|
|
133
|
+
...(config.tls.caPath ? [config.tls.caPath] : []),
|
|
134
|
+
]) {
|
|
135
|
+
try {
|
|
136
|
+
accessSync(filePath, fsConstants.R_OK);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
throw new GatewayStartupError();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!config.issuer || !config.audience || !config.replayDbPath)
|
|
91
144
|
throw new GatewayStartupError();
|
|
92
|
-
if (
|
|
93
|
-
(config.
|
|
145
|
+
if (config.pairing?.enabled) {
|
|
146
|
+
if (!config.pairing.trustFilePath.startsWith('/') || config.pairing.trustFilePath.includes('\u0000'))
|
|
147
|
+
throw new GatewayStartupError();
|
|
148
|
+
if ((config.pairing.otpTtlMs !== undefined &&
|
|
149
|
+
(!Number.isSafeInteger(config.pairing.otpTtlMs) || config.pairing.otpTtlMs <= 0)) ||
|
|
150
|
+
(config.pairing.maxAttempts !== undefined &&
|
|
151
|
+
(!Number.isSafeInteger(config.pairing.maxAttempts) || config.pairing.maxAttempts <= 0)))
|
|
152
|
+
throw new GatewayStartupError();
|
|
153
|
+
}
|
|
154
|
+
const configuredKeyCount = config.publicKeys instanceof Map ? config.publicKeys.size : Object.keys(config.publicKeys ?? {}).length;
|
|
155
|
+
if (configuredKeyCount === 0 && !config.pairing?.enabled)
|
|
94
156
|
throw new GatewayStartupError();
|
|
95
157
|
if (!manifest)
|
|
96
158
|
throw new GatewayStartupError();
|
|
97
159
|
if (!config.manifestSha256 || !/^[a-f0-9]{64}$/i.test(config.manifestSha256))
|
|
98
160
|
throw new GatewayStartupError();
|
|
99
|
-
|
|
161
|
+
const realProviderStack = config.runtimeFactory?.usesRealProviderStack === true;
|
|
162
|
+
if (!realProviderStack &&
|
|
163
|
+
(!config.providerBroker || !config.providerProbe?.available || !config.providerProbe.secretFree))
|
|
164
|
+
throw new GatewayStartupError();
|
|
165
|
+
if (realProviderStack && !config.runtimeFactory?.settingsAuthority)
|
|
100
166
|
throw new GatewayStartupError();
|
|
101
167
|
if (config.workerSandboxAvailable !== true || typeof config.workspaceBoundaryProbe !== 'function')
|
|
102
168
|
throw new GatewayStartupError();
|
|
@@ -104,7 +170,8 @@ export function assertGatewayStartup(config, manifest) {
|
|
|
104
170
|
throw new GatewayStartupError();
|
|
105
171
|
if (typeof config.auditWriter !== 'function')
|
|
106
172
|
throw new GatewayStartupError();
|
|
107
|
-
|
|
173
|
+
if (!realProviderStack)
|
|
174
|
+
assertProviderBrokerReady(config.providerBroker, config.providerProbe);
|
|
108
175
|
if (!config.tmpDir?.startsWith('/'))
|
|
109
176
|
throw new GatewayStartupError();
|
|
110
177
|
return manifest;
|
|
@@ -112,7 +179,6 @@ export function assertGatewayStartup(config, manifest) {
|
|
|
112
179
|
/** Gateway control plane. Runtime/session protocols remain private RPC consumers. */
|
|
113
180
|
export class Term2Gateway {
|
|
114
181
|
#config;
|
|
115
|
-
#manifest;
|
|
116
182
|
#replay;
|
|
117
183
|
#admission;
|
|
118
184
|
#verifier;
|
|
@@ -125,27 +191,94 @@ export class Term2Gateway {
|
|
|
125
191
|
#interactionCounters = new Map();
|
|
126
192
|
#eventPersistenceTails = new Map();
|
|
127
193
|
#shutdownInProgress = false;
|
|
194
|
+
#shutdownPromise;
|
|
128
195
|
#server;
|
|
196
|
+
#modelCatalog;
|
|
197
|
+
#workspaceRegistry;
|
|
198
|
+
#pairing;
|
|
199
|
+
#oauthMutation = Promise.resolve();
|
|
200
|
+
#settingsMutation = Promise.resolve();
|
|
129
201
|
constructor(config, manifest) {
|
|
130
202
|
this.#config = config;
|
|
131
|
-
this.#manifest = manifest;
|
|
132
203
|
this.#replay = new SqliteReplayLedger(config.replayDbPath);
|
|
133
|
-
this.#admission =
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
204
|
+
this.#admission =
|
|
205
|
+
config.workspaceRegistry?.admission ??
|
|
206
|
+
new WorkspaceAdmission(manifest, {
|
|
207
|
+
allowWrite: config.allowWrite === true,
|
|
208
|
+
boundaryProbe: config.workspaceBoundaryProbe,
|
|
209
|
+
});
|
|
210
|
+
this.#workspaceRegistry =
|
|
211
|
+
config.workspaceRegistry ??
|
|
212
|
+
new DynamicWorkspaceRegistry({
|
|
213
|
+
admission: this.#admission,
|
|
214
|
+
allowedRoots: config.workspaceAllowedRoots ?? deriveDynamicWorkspaceRoots(manifest),
|
|
215
|
+
});
|
|
216
|
+
const trustedStore = config.pairing?.enabled ? new TrustedClientsStore(config.pairing.trustFilePath) : undefined;
|
|
217
|
+
const assertionKeys = new Map();
|
|
218
|
+
for (const [kid, key] of config.publicKeys instanceof Map
|
|
219
|
+
? config.publicKeys.entries()
|
|
220
|
+
: Object.entries(config.publicKeys))
|
|
221
|
+
assertionKeys.set(kid, key);
|
|
222
|
+
for (const trusted of trustedStore?.entries() ?? [])
|
|
223
|
+
assertionKeys.set(trusted.kid, trusted.publicKeyPem);
|
|
137
224
|
this.#verifier = new AssertionVerifier({
|
|
138
225
|
issuer: config.issuer,
|
|
139
226
|
audience: config.audience,
|
|
140
|
-
publicKeys:
|
|
227
|
+
publicKeys: assertionKeys,
|
|
228
|
+
allowEmptyKeys: config.pairing?.enabled === true,
|
|
141
229
|
replayLedger: this.#replay,
|
|
142
230
|
});
|
|
231
|
+
this.#pairing = trustedStore
|
|
232
|
+
? new GatewayPairing({
|
|
233
|
+
enabled: config.pairing.enabled,
|
|
234
|
+
otpTtlMs: config.pairing.otpTtlMs,
|
|
235
|
+
maxAttempts: config.pairing.maxAttempts,
|
|
236
|
+
trustStore: trustedStore,
|
|
237
|
+
})
|
|
238
|
+
: undefined;
|
|
143
239
|
this.#audit = new GatewayAuditLog(config.auditWriter);
|
|
144
240
|
this.#admissions = config.persistence ? new GatewayAdmissionPersistence(config.persistence.index) : null;
|
|
241
|
+
const settingsAuthority = config.runtimeFactory?.settingsAuthority;
|
|
242
|
+
if (settingsAuthority) {
|
|
243
|
+
this.#modelCatalog = new ModelCatalogSession({
|
|
244
|
+
settingsService: settingsAuthority,
|
|
245
|
+
loggingService: config.runtimeFactory?.modelCatalogLogger ??
|
|
246
|
+
new LoggingService({ disableLogging: true, suppressConsoleOutput: true }),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
const transport = config.socketPath
|
|
250
|
+
? { socketPath: config.socketPath }
|
|
251
|
+
: { host: config.host, port: config.port, tls: config.tls };
|
|
145
252
|
this.#server = new GatewayServer({
|
|
146
|
-
|
|
253
|
+
...transport,
|
|
147
254
|
verifier: this.#verifier,
|
|
148
255
|
lifecycle: this.#lifecycle,
|
|
256
|
+
...(this.#pairing
|
|
257
|
+
? {
|
|
258
|
+
pairingHandler: async ({ body }) => {
|
|
259
|
+
if (!isPairingRegisterBody(body))
|
|
260
|
+
return publicError(401, 'pairing_invalid', 'pairing request is invalid');
|
|
261
|
+
try {
|
|
262
|
+
const result = await this.#pairing.register(body.publicKeyPem, body.otp);
|
|
263
|
+
const publicKeyPem = trustedStore.entries().find((entry) => entry.kid === result.kid)?.publicKeyPem;
|
|
264
|
+
if (!publicKeyPem)
|
|
265
|
+
return publicError(503, 'pairing_unavailable', 'pairing is unavailable', true);
|
|
266
|
+
this.#verifier.addTrustedKey(result.kid, publicKeyPem);
|
|
267
|
+
return { status: 200, body: result };
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (error instanceof PairingError) {
|
|
271
|
+
if (error.code === 'pairing_unavailable')
|
|
272
|
+
return publicError(503, 'pairing_unavailable', 'pairing is unavailable', true);
|
|
273
|
+
return publicError(401, error.code === 'pairing_required' || error.code === 'pairing_not_allowed'
|
|
274
|
+
? 'pairing_required'
|
|
275
|
+
: 'pairing_invalid', 'pairing rejected');
|
|
276
|
+
}
|
|
277
|
+
throw error;
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
: {}),
|
|
149
282
|
handler: (input) => this.#handleRpc(input),
|
|
150
283
|
});
|
|
151
284
|
}
|
|
@@ -159,6 +292,8 @@ export class Term2Gateway {
|
|
|
159
292
|
return publicError(400, 'protocol_conflict', 'gateway path and assertion session do not match');
|
|
160
293
|
if (!route.legacy && route.purpose !== claims.purpose)
|
|
161
294
|
return publicError(400, 'protocol_conflict', 'gateway purpose does not match route');
|
|
295
|
+
if (LOCAL_OWNER_PURPOSES.has(claims.purpose) && claims.sub !== this.#config.localOwnerUserId)
|
|
296
|
+
return publicError(403, 'settings_forbidden', 'local owner authorization is required');
|
|
162
297
|
try {
|
|
163
298
|
if (claims.purpose === 'workspace_list') {
|
|
164
299
|
await this.#audit.write(createSafeLogMetadata({
|
|
@@ -204,8 +339,32 @@ export class Term2Gateway {
|
|
|
204
339
|
throw error;
|
|
205
340
|
}
|
|
206
341
|
}
|
|
342
|
+
if (claims.purpose === 'model_list')
|
|
343
|
+
return await this.#modelList(input.correlationId);
|
|
344
|
+
if (claims.purpose === 'workspace_candidate_validate')
|
|
345
|
+
return await this.#workspaceCandidateValidate(claims, input.body, input.correlationId);
|
|
346
|
+
if (claims.purpose === 'workspace_candidate_browse')
|
|
347
|
+
return await this.#workspaceCandidateBrowse(claims, input.body, input.correlationId);
|
|
348
|
+
if (claims.purpose === 'workspace_candidate_select')
|
|
349
|
+
return await this.#workspaceCandidateSelect(claims, input.body, input.correlationId);
|
|
350
|
+
if (claims.purpose === 'settings_read')
|
|
351
|
+
return await this.#settingsRead(input.correlationId);
|
|
352
|
+
if (claims.purpose === 'settings_write')
|
|
353
|
+
return await this.#settingsWrite(input.body, input.correlationId);
|
|
354
|
+
if (claims.purpose === 'credential_write')
|
|
355
|
+
return await this.#credentialWrite(route.interactionId, input.body, input.correlationId);
|
|
356
|
+
if (claims.purpose === 'credential_delete')
|
|
357
|
+
return await this.#credentialDelete(route.interactionId, input.correlationId);
|
|
358
|
+
if (claims.purpose === 'oauth_login')
|
|
359
|
+
return await this.#oauthLogin(route.interactionId, input.correlationId);
|
|
360
|
+
if (claims.purpose === 'oauth_select')
|
|
361
|
+
return await this.#oauthSelect(route.interactionId, input.body, input.correlationId);
|
|
362
|
+
if (claims.purpose === 'oauth_delete')
|
|
363
|
+
return await this.#oauthDelete(route.interactionId, input.correlationId);
|
|
207
364
|
if (!claims.sessionId)
|
|
208
365
|
return publicError(400, 'protocol_conflict', 'session assertion is incomplete');
|
|
366
|
+
if (claims.purpose === 'session_update')
|
|
367
|
+
return await this.#sessionUpdate(claims, input.body, input.correlationId);
|
|
209
368
|
if (claims.purpose === 'session_read') {
|
|
210
369
|
const projection = await this.#projectionFor(claims.sub, claims.sessionId);
|
|
211
370
|
return { status: 200, body: { session: toSessionProjection(projection) } };
|
|
@@ -224,6 +383,287 @@ export class Term2Gateway {
|
|
|
224
383
|
return this.#mapError(error);
|
|
225
384
|
}
|
|
226
385
|
}
|
|
386
|
+
async #modelList(correlationId) {
|
|
387
|
+
const catalog = this.#modelCatalog;
|
|
388
|
+
const settings = this.#config.runtimeFactory?.settingsAuthority;
|
|
389
|
+
if (!catalog || !settings)
|
|
390
|
+
return publicError(503, 'model_catalog_unavailable', 'model catalog unavailable', true);
|
|
391
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
392
|
+
operation: 'model_list',
|
|
393
|
+
outcome: 'allowed',
|
|
394
|
+
reasonCode: 'accepted',
|
|
395
|
+
...(correlationId ? { correlationId } : {}),
|
|
396
|
+
}));
|
|
397
|
+
const models = [];
|
|
398
|
+
for (const provider of getAvailableProviderIds(settings, getProviderIds())) {
|
|
399
|
+
try {
|
|
400
|
+
const result = await catalog.load(provider);
|
|
401
|
+
for (const model of result.models) {
|
|
402
|
+
models.push({
|
|
403
|
+
provider,
|
|
404
|
+
id: model.id,
|
|
405
|
+
...(model.name === undefined ? {} : { name: model.name }),
|
|
406
|
+
...(model.default_reasoning_level === undefined
|
|
407
|
+
? {}
|
|
408
|
+
: { default_reasoning_level: model.default_reasoning_level }),
|
|
409
|
+
...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
// Provider errors can include credential values or request headers.
|
|
415
|
+
// The redacted model surface simply omits that provider.
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return { status: 200, body: { models } };
|
|
419
|
+
}
|
|
420
|
+
async #workspaceCandidateValidate(claims, body, correlationId) {
|
|
421
|
+
if (!isWorkspaceCandidateValidateBody(body))
|
|
422
|
+
return publicError(400, 'validation_error', 'workspace request is invalid');
|
|
423
|
+
const result = this.#workspaceRegistry.validateCandidate(body.absolutePath, claims.sub);
|
|
424
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
425
|
+
operation: 'workspace_candidate_validate',
|
|
426
|
+
outcome: result.valid ? 'allowed' : 'denied',
|
|
427
|
+
reasonCode: result.valid ? 'accepted' : result.reasonCode,
|
|
428
|
+
...(correlationId ? { correlationId } : {}),
|
|
429
|
+
}));
|
|
430
|
+
return { status: 200, body: result };
|
|
431
|
+
}
|
|
432
|
+
async #workspaceCandidateBrowse(claims, body, correlationId) {
|
|
433
|
+
if (!isWorkspaceCandidateBrowseBody(body))
|
|
434
|
+
return publicError(400, 'validation_error', 'workspace request is invalid');
|
|
435
|
+
const result = this.#workspaceRegistry.browse(body.candidateId, body.child, claims.sub);
|
|
436
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
437
|
+
operation: 'workspace_candidate_browse',
|
|
438
|
+
outcome: 'allowed',
|
|
439
|
+
reasonCode: 'accepted',
|
|
440
|
+
...(correlationId ? { correlationId } : {}),
|
|
441
|
+
}));
|
|
442
|
+
return { status: 200, body: result };
|
|
443
|
+
}
|
|
444
|
+
async #workspaceCandidateSelect(claims, body, correlationId) {
|
|
445
|
+
if (!isWorkspaceCandidateSelectBody(body))
|
|
446
|
+
return publicError(400, 'validation_error', 'workspace request is invalid');
|
|
447
|
+
const result = this.#workspaceRegistry.select(body.candidateId, body.access, claims.sub);
|
|
448
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
449
|
+
operation: 'workspace_candidate_select',
|
|
450
|
+
outcome: 'allowed',
|
|
451
|
+
reasonCode: 'accepted',
|
|
452
|
+
workspaceId: result.workspaceId,
|
|
453
|
+
grantVersion: result.binding.grantVersion,
|
|
454
|
+
access: result.binding.access,
|
|
455
|
+
...(correlationId ? { correlationId } : {}),
|
|
456
|
+
}));
|
|
457
|
+
return {
|
|
458
|
+
status: 200,
|
|
459
|
+
body: {
|
|
460
|
+
workspaceId: result.workspaceId,
|
|
461
|
+
displayName: result.displayName,
|
|
462
|
+
binding: {
|
|
463
|
+
sessionId: result.binding.sessionId,
|
|
464
|
+
ownerUserId: result.binding.ownerUserId,
|
|
465
|
+
workspaceId: result.binding.workspaceId,
|
|
466
|
+
grantVersion: result.binding.grantVersion,
|
|
467
|
+
canonicalRoot: result.binding.canonicalRoot,
|
|
468
|
+
access: result.binding.access,
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
#settingsAuthority() {
|
|
474
|
+
const settings = this.#config.runtimeFactory?.settingsAuthority;
|
|
475
|
+
return settings ? settings : undefined;
|
|
476
|
+
}
|
|
477
|
+
async #settingsRead(correlationId) {
|
|
478
|
+
const settings = this.#settingsAuthority();
|
|
479
|
+
if (!settings)
|
|
480
|
+
return publicError(503, 'settings_unavailable', 'settings unavailable', true);
|
|
481
|
+
const projection = buildSettingsProjection(settings, this.#config.allowWrite ? 'read_write' : 'read');
|
|
482
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
483
|
+
operation: 'settings_read',
|
|
484
|
+
outcome: 'allowed',
|
|
485
|
+
reasonCode: 'accepted',
|
|
486
|
+
...(correlationId ? { correlationId } : {}),
|
|
487
|
+
}));
|
|
488
|
+
return { status: 200, body: projection };
|
|
489
|
+
}
|
|
490
|
+
async #settingsWrite(body, correlationId) {
|
|
491
|
+
const settings = this.#settingsAuthority();
|
|
492
|
+
if (!settings)
|
|
493
|
+
return publicError(503, 'settings_unavailable', 'settings unavailable', true);
|
|
494
|
+
if (!isSettingsWriteBody(body))
|
|
495
|
+
return publicError(400, 'validation_error', 'settings request is invalid');
|
|
496
|
+
return await this.#withSettingsMutation(async () => {
|
|
497
|
+
const projection = applySettingsChanges(settings, body.expectedRevision, body.changes);
|
|
498
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
499
|
+
operation: 'settings_write',
|
|
500
|
+
outcome: 'allowed',
|
|
501
|
+
reasonCode: 'accepted',
|
|
502
|
+
...(correlationId ? { correlationId } : {}),
|
|
503
|
+
}));
|
|
504
|
+
return { status: 200, body: { committed: true, revision: projection.revision, projection } };
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
async #credentialWrite(credentialId, body, correlationId) {
|
|
508
|
+
const settings = this.#settingsAuthority();
|
|
509
|
+
if (!settings)
|
|
510
|
+
return publicError(503, 'settings_unavailable', 'settings unavailable', true);
|
|
511
|
+
if (!isCredentialWriteBody(body))
|
|
512
|
+
return publicError(400, 'validation_error', 'credential request is invalid');
|
|
513
|
+
return await this.#withSettingsMutation(async () => {
|
|
514
|
+
const result = setCredential(settings, credentialId, body.value);
|
|
515
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
516
|
+
operation: 'credential_write',
|
|
517
|
+
outcome: 'allowed',
|
|
518
|
+
reasonCode: 'accepted',
|
|
519
|
+
...(correlationId ? { correlationId } : {}),
|
|
520
|
+
}));
|
|
521
|
+
return { status: 200, body: result };
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
async #credentialDelete(credentialId, correlationId) {
|
|
525
|
+
const settings = this.#settingsAuthority();
|
|
526
|
+
if (!settings)
|
|
527
|
+
return publicError(503, 'settings_unavailable', 'settings unavailable', true);
|
|
528
|
+
return await this.#withSettingsMutation(async () => {
|
|
529
|
+
const result = deleteCredential(settings, credentialId);
|
|
530
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
531
|
+
operation: 'credential_delete',
|
|
532
|
+
outcome: 'allowed',
|
|
533
|
+
reasonCode: 'accepted',
|
|
534
|
+
...(correlationId ? { correlationId } : {}),
|
|
535
|
+
}));
|
|
536
|
+
return { status: 200, body: result };
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
async #withSettingsMutation(operation) {
|
|
540
|
+
const previous = this.#settingsMutation;
|
|
541
|
+
let release;
|
|
542
|
+
this.#settingsMutation = new Promise((resolve) => {
|
|
543
|
+
release = resolve;
|
|
544
|
+
});
|
|
545
|
+
await previous;
|
|
546
|
+
try {
|
|
547
|
+
return await operation();
|
|
548
|
+
}
|
|
549
|
+
finally {
|
|
550
|
+
release();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async #oauthLogin(resource, correlationId) {
|
|
554
|
+
const [provider] = resource.split('\u0000');
|
|
555
|
+
if (!isOAuthAccountProvider(provider))
|
|
556
|
+
return publicError(400, 'validation_error', 'OAuth provider is invalid');
|
|
557
|
+
return await this.#withOAuthMutation(async () => {
|
|
558
|
+
try {
|
|
559
|
+
if (this.#config.oauthLogin)
|
|
560
|
+
await this.#config.oauthLogin(provider);
|
|
561
|
+
else if (provider === 'codex')
|
|
562
|
+
await loginToCodex();
|
|
563
|
+
else
|
|
564
|
+
await loginToGrok();
|
|
565
|
+
return {
|
|
566
|
+
status: 200,
|
|
567
|
+
body: { status: 'completed', configured: listOAuthAccounts(provider).length > 0 },
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
return { status: 200, body: { status: 'not_completed' } };
|
|
572
|
+
}
|
|
573
|
+
}, 'oauth_login', correlationId);
|
|
574
|
+
}
|
|
575
|
+
async #oauthSelect(resource, body, correlationId) {
|
|
576
|
+
const [provider] = resource.split('\u0000');
|
|
577
|
+
if (!isOAuthAccountProvider(provider) || !isOAuthSelectBody(body))
|
|
578
|
+
return publicError(400, 'validation_error', 'OAuth request is invalid');
|
|
579
|
+
return await this.#withOAuthMutation(async () => {
|
|
580
|
+
const ok = setActiveOAuthAccount(provider, body.accountId);
|
|
581
|
+
const account = listOAuthAccounts(provider).find((item) => item.id === body.accountId);
|
|
582
|
+
return {
|
|
583
|
+
status: 200,
|
|
584
|
+
body: { ok, isSelected: account?.isSelected === true, isInUse: account?.isInUse === true },
|
|
585
|
+
};
|
|
586
|
+
}, 'oauth_select', correlationId);
|
|
587
|
+
}
|
|
588
|
+
async #oauthDelete(resource, correlationId) {
|
|
589
|
+
const [provider, accountId] = resource.split('\u0000');
|
|
590
|
+
if (!isOAuthAccountProvider(provider) || !accountId)
|
|
591
|
+
return publicError(400, 'validation_error', 'OAuth request is invalid');
|
|
592
|
+
return await this.#withOAuthMutation(async () => {
|
|
593
|
+
const ok = removeOAuthAccount(provider, accountId);
|
|
594
|
+
return { status: 200, body: { ok } };
|
|
595
|
+
}, 'oauth_delete', correlationId);
|
|
596
|
+
}
|
|
597
|
+
async #withOAuthMutation(operation, auditOperation, correlationId) {
|
|
598
|
+
const previous = this.#oauthMutation;
|
|
599
|
+
let release;
|
|
600
|
+
this.#oauthMutation = new Promise((resolve) => {
|
|
601
|
+
release = resolve;
|
|
602
|
+
});
|
|
603
|
+
await previous;
|
|
604
|
+
try {
|
|
605
|
+
const result = await operation();
|
|
606
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
607
|
+
operation: auditOperation,
|
|
608
|
+
outcome: 'allowed',
|
|
609
|
+
reasonCode: 'accepted',
|
|
610
|
+
...(correlationId ? { correlationId } : {}),
|
|
611
|
+
}));
|
|
612
|
+
return result;
|
|
613
|
+
}
|
|
614
|
+
finally {
|
|
615
|
+
release();
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async #sessionUpdate(claims, body, correlationId) {
|
|
619
|
+
if (!isSessionUpdateBody(body))
|
|
620
|
+
return publicError(400, 'validation_error', 'session config is invalid');
|
|
621
|
+
const session = this.#sessions.get(claims.sessionId);
|
|
622
|
+
if (!(session instanceof ServerSession) || session.binding.ownerUserId !== claims.sub)
|
|
623
|
+
return publicError(404, 'not_found', 'session not found');
|
|
624
|
+
if (session.status === 'running' || session.status === 'awaiting_interaction')
|
|
625
|
+
return publicError(409, 'session_busy', 'session is busy');
|
|
626
|
+
const settings = session.resources.sessionSettingsService;
|
|
627
|
+
if (!settings)
|
|
628
|
+
return publicError(503, 'settings_unavailable', 'session settings unavailable', true);
|
|
629
|
+
if (body.model !== undefined) {
|
|
630
|
+
if (!this.#modelCatalog)
|
|
631
|
+
return publicError(503, 'settings_unavailable', 'model catalog unavailable', true);
|
|
632
|
+
const provider = String(settings.getDynamic('agent.provider') ?? '');
|
|
633
|
+
const models = await this.#modelCatalog.load(provider);
|
|
634
|
+
if (!models.models.some((model) => model.id === body.model))
|
|
635
|
+
return publicError(422, 'validation_error', 'model is unavailable');
|
|
636
|
+
session.service.setModel(body.model);
|
|
637
|
+
}
|
|
638
|
+
if (body.reasoningEffort !== undefined)
|
|
639
|
+
session.service.setReasoningEffort(body.reasoningEffort);
|
|
640
|
+
if (body.mode !== undefined) {
|
|
641
|
+
const modes = { mentorMode: false, liteMode: false, planMode: false, orchestratorMode: false };
|
|
642
|
+
const key = body.mode === 'mentor'
|
|
643
|
+
? 'mentorMode'
|
|
644
|
+
: body.mode === 'lite'
|
|
645
|
+
? 'liteMode'
|
|
646
|
+
: body.mode === 'plan'
|
|
647
|
+
? 'planMode'
|
|
648
|
+
: body.mode === 'orchestrator'
|
|
649
|
+
? 'orchestratorMode'
|
|
650
|
+
: undefined;
|
|
651
|
+
if (key)
|
|
652
|
+
modes[key] = true;
|
|
653
|
+
for (const [modeKey, value] of Object.entries(modes))
|
|
654
|
+
settings.setDynamic(`app.${modeKey}`, value, { persist: false });
|
|
655
|
+
}
|
|
656
|
+
const result = sessionConfigProjection(session);
|
|
657
|
+
await this.#audit.write(createSafeLogMetadata({
|
|
658
|
+
operation: 'session_update',
|
|
659
|
+
outcome: 'allowed',
|
|
660
|
+
reasonCode: 'accepted',
|
|
661
|
+
sessionId: session.sessionId,
|
|
662
|
+
workspaceId: session.binding.workspaceId,
|
|
663
|
+
...(correlationId ? { correlationId } : {}),
|
|
664
|
+
}));
|
|
665
|
+
return { status: 200, body: result };
|
|
666
|
+
}
|
|
227
667
|
#interactionBindingFor(sessionId, snapshot, turnId) {
|
|
228
668
|
const current = this.#interactionBindings.get(sessionId);
|
|
229
669
|
if (current && current.expectedInteractionId === snapshot.interactionId)
|
|
@@ -744,6 +1184,14 @@ export class Term2Gateway {
|
|
|
744
1184
|
await persisted.persistence.journal.append(mapped, {
|
|
745
1185
|
durability: mapped.type === 'text_delta' || mapped.type === 'reasoning_delta' ? 'stream' : 'critical',
|
|
746
1186
|
});
|
|
1187
|
+
// Keep the sessions-list sequence counter live: stream deltas stay
|
|
1188
|
+
// non-blocking, but every critical event refreshes the index row so the
|
|
1189
|
+
// sidebar never reports a stale "1 events" for a running session.
|
|
1190
|
+
if (mapped.type !== 'text_delta' && mapped.type !== 'reasoning_delta') {
|
|
1191
|
+
this.#config.persistence?.index.update(sessionId, {
|
|
1192
|
+
lastPublishedSequence: persisted.persistence.journal.highWater().lastPublishedSequence,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
747
1195
|
if (event.type === 'final' || event.type === 'error') {
|
|
748
1196
|
const admission = admissions.find((candidate) => candidate.turnId === context.turnId);
|
|
749
1197
|
if (admission && (admission.state === 'accepted' || admission.state === 'committed')) {
|
|
@@ -759,8 +1207,34 @@ export class Term2Gateway {
|
|
|
759
1207
|
});
|
|
760
1208
|
}
|
|
761
1209
|
#mapError(error) {
|
|
1210
|
+
if (error instanceof SettingsRpcError) {
|
|
1211
|
+
if (error.code === 'settings_conflict')
|
|
1212
|
+
return publicError(409, 'settings_conflict', 'settings changed; reload before retrying', false, {
|
|
1213
|
+
currentRevision: error.details?.currentRevision,
|
|
1214
|
+
...(error.details?.projection ? { projection: error.details.projection } : {}),
|
|
1215
|
+
});
|
|
1216
|
+
if (error.code === 'settings_not_allowed')
|
|
1217
|
+
return publicError(403, 'settings_forbidden', 'settings change is not allowed');
|
|
1218
|
+
if (error.code === 'not_persisted')
|
|
1219
|
+
return publicError(503, 'settings_unavailable', 'settings could not be saved', true);
|
|
1220
|
+
return publicError(503, 'settings_unavailable', 'settings unavailable', true);
|
|
1221
|
+
}
|
|
1222
|
+
if (error instanceof DynamicWorkspaceRegistryError) {
|
|
1223
|
+
if (error.code === 'candidate_not_found' || error.code === 'candidate_expired')
|
|
1224
|
+
return publicError(404, 'not_found', 'workspace candidate not found');
|
|
1225
|
+
if (error.code === 'workspace_owner_mismatch' || error.code === 'workspace_path_escape')
|
|
1226
|
+
return publicError(403, 'workspace_forbidden', 'workspace access denied');
|
|
1227
|
+
if (error.code === 'candidate_registry_full')
|
|
1228
|
+
return publicError(429, 'candidate_registry_full', 'workspace candidate capacity is full', true);
|
|
1229
|
+
return publicError(422, 'validation_error', 'workspace candidate is unavailable');
|
|
1230
|
+
}
|
|
762
1231
|
if (error instanceof WorkspaceAdmissionError) {
|
|
763
|
-
|
|
1232
|
+
// One active session per workspace is a deliberate constraint; surface it
|
|
1233
|
+
// with a distinct code so clients can recover (e.g. select the existing
|
|
1234
|
+
// session) instead of seeing a generic protocol_conflict.
|
|
1235
|
+
if (error.code === 'workspace_session_exists')
|
|
1236
|
+
return publicError(409, 'session_busy', 'a session is already active for this workspace');
|
|
1237
|
+
return publicError(403, 'workspace_forbidden', 'workspace access denied');
|
|
764
1238
|
}
|
|
765
1239
|
if (error instanceof GatewayPersistenceError) {
|
|
766
1240
|
if (error.code === 'not_found' || error.code === 'owner_mismatch')
|
|
@@ -799,7 +1273,7 @@ export class Term2Gateway {
|
|
|
799
1273
|
return this.#lifecycle;
|
|
800
1274
|
}
|
|
801
1275
|
get manifestVersion() {
|
|
802
|
-
return this.#
|
|
1276
|
+
return this.#admission.manifestVersion;
|
|
803
1277
|
}
|
|
804
1278
|
get interactionMetrics() {
|
|
805
1279
|
return Object.freeze(Object.fromEntries(INTERACTION_METRICS.map((metric) => [metric, this.#interactionCounters.get(metric) ?? 0])));
|
|
@@ -911,12 +1385,50 @@ export class Term2Gateway {
|
|
|
911
1385
|
return session;
|
|
912
1386
|
}
|
|
913
1387
|
async shutdown(graceMs = 5_000) {
|
|
1388
|
+
if (this.#shutdownPromise)
|
|
1389
|
+
return this.#shutdownPromise;
|
|
914
1390
|
this.#shutdownInProgress = true;
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1391
|
+
const hardDeadlineMs = Math.max(1, graceMs);
|
|
1392
|
+
this.#shutdownPromise = (async () => {
|
|
1393
|
+
const deadline = Date.now() + hardDeadlineMs;
|
|
1394
|
+
let forced = false;
|
|
1395
|
+
const remaining = () => Math.max(1, deadline - Date.now());
|
|
1396
|
+
const waitBounded = async (work) => {
|
|
1397
|
+
let timer;
|
|
1398
|
+
const completed = await Promise.race([
|
|
1399
|
+
work.then(() => true, () => true),
|
|
1400
|
+
new Promise((resolve) => {
|
|
1401
|
+
timer = setTimeout(() => resolve(false), remaining());
|
|
1402
|
+
}),
|
|
1403
|
+
]);
|
|
1404
|
+
if (timer)
|
|
1405
|
+
clearTimeout(timer);
|
|
1406
|
+
return completed;
|
|
1407
|
+
};
|
|
1408
|
+
const markForced = () => {
|
|
1409
|
+
if (forced)
|
|
1410
|
+
return;
|
|
1411
|
+
forced = true;
|
|
1412
|
+
for (const sessionId of this.#persisted.keys())
|
|
1413
|
+
this.#config.persistence?.markForcedShutdown(sessionId);
|
|
1414
|
+
};
|
|
1415
|
+
if (!(await waitBounded(this.#server.shutdown(remaining()))))
|
|
1416
|
+
markForced();
|
|
1417
|
+
if (!(await waitBounded(Promise.all([...this.#sessions.values()].map((session) => Promise.resolve(session.dispose()))))))
|
|
1418
|
+
markForced();
|
|
1419
|
+
if (this.#config.runtimeFactory && !(await waitBounded(this.#config.runtimeFactory.shutdown(remaining()))))
|
|
1420
|
+
markForced();
|
|
1421
|
+
this.#sessions.clear();
|
|
1422
|
+
this.#replay.close();
|
|
1423
|
+
// The audit record is bounded by the same deadline. A broken audit sink
|
|
1424
|
+
// must not keep the local owner process alive indefinitely.
|
|
1425
|
+
await waitBounded(this.#audit.write(createSafeLogMetadata({
|
|
1426
|
+
operation: 'shutdown',
|
|
1427
|
+
outcome: forced ? 'interrupted' : 'allowed',
|
|
1428
|
+
reasonCode: forced ? 'forced_shutdown' : 'shutdown',
|
|
1429
|
+
})));
|
|
1430
|
+
})();
|
|
1431
|
+
return this.#shutdownPromise;
|
|
920
1432
|
}
|
|
921
1433
|
}
|
|
922
1434
|
function matchPrivateRoute(method, pathname) {
|
|
@@ -925,11 +1437,37 @@ function matchPrivateRoute(method, pathname) {
|
|
|
925
1437
|
// GET is canonical for workspace_list; POST is a tested compatibility alias for the pinned BFF wire.
|
|
926
1438
|
if ((method === 'GET' || method === 'POST') && pathname === '/private/agent/v1/workspaces')
|
|
927
1439
|
return { purpose: 'workspace_list' };
|
|
1440
|
+
if (method === 'POST' && pathname === '/private/agent/v1/workspace/candidates/validate')
|
|
1441
|
+
return { purpose: 'workspace_candidate_validate' };
|
|
1442
|
+
if (method === 'POST' && pathname === '/private/agent/v1/workspace/candidates/browse')
|
|
1443
|
+
return { purpose: 'workspace_candidate_browse' };
|
|
1444
|
+
if (method === 'POST' && pathname === '/private/agent/v1/workspace/candidates/select')
|
|
1445
|
+
return { purpose: 'workspace_candidate_select' };
|
|
1446
|
+
if (method === 'GET' && pathname === '/private/agent/v1/models')
|
|
1447
|
+
return { purpose: 'model_list' };
|
|
1448
|
+
if (method === 'GET' && pathname === '/private/agent/v1/settings')
|
|
1449
|
+
return { purpose: 'settings_read' };
|
|
1450
|
+
if (method === 'PUT' && pathname === '/private/agent/v1/settings')
|
|
1451
|
+
return { purpose: 'settings_write' };
|
|
1452
|
+
const credential = pathname.match(/^\/private\/agent\/v1\/credentials\/([A-Za-z0-9_-]{1,256})$/);
|
|
1453
|
+
if (credential && method === 'POST')
|
|
1454
|
+
return { purpose: 'credential_write', interactionId: credential[1] };
|
|
1455
|
+
if (credential && method === 'DELETE')
|
|
1456
|
+
return { purpose: 'credential_delete', interactionId: credential[1] };
|
|
1457
|
+
const oauth = pathname.match(/^\/private\/agent\/v1\/oauth\/([A-Za-z0-9_-]{1,256})\/(login|select)$/);
|
|
1458
|
+
if (oauth && method === 'POST')
|
|
1459
|
+
return {
|
|
1460
|
+
purpose: oauth[2] === 'login' ? 'oauth_login' : 'oauth_select',
|
|
1461
|
+
interactionId: `${oauth[1]}\u0000${oauth[2]}`,
|
|
1462
|
+
};
|
|
1463
|
+
const oauthDelete = pathname.match(/^\/private\/agent\/v1\/oauth\/([A-Za-z0-9_-]{1,256})\/accounts\/([A-Za-z0-9_-]{1,256})$/);
|
|
1464
|
+
if (oauthDelete && method === 'DELETE')
|
|
1465
|
+
return { purpose: 'oauth_delete', interactionId: `${oauthDelete[1]}\u0000${oauthDelete[2]}` };
|
|
928
1466
|
if (method === 'POST' && pathname === '/private/agent/v1/sessions')
|
|
929
1467
|
return { purpose: 'session_create' };
|
|
930
1468
|
if (method === 'GET' && pathname === '/private/agent/v1/sessions')
|
|
931
1469
|
return { purpose: 'session_list' };
|
|
932
|
-
const match = pathname.match(/^\/private\/agent\/v1\/sessions\/([A-Za-z0-9_-]{1,256})(?:\/(messages|abort|events|interactions\/([A-Za-z0-9_-]{1,256})))?$/);
|
|
1470
|
+
const match = pathname.match(/^\/private\/agent\/v1\/sessions\/([A-Za-z0-9_-]{1,256})(?:\/(messages|abort|events|config|interactions\/([A-Za-z0-9_-]{1,256})))?$/);
|
|
933
1471
|
if (!match)
|
|
934
1472
|
return null;
|
|
935
1473
|
const sessionId = match[1];
|
|
@@ -941,10 +1479,29 @@ function matchPrivateRoute(method, pathname) {
|
|
|
941
1479
|
return { purpose: 'abort', sessionId };
|
|
942
1480
|
if (method === 'GET' && match[2] === 'events')
|
|
943
1481
|
return { purpose: 'events_connect', sessionId };
|
|
1482
|
+
if (method === 'POST' && match[2] === 'config')
|
|
1483
|
+
return { purpose: 'session_update', sessionId };
|
|
944
1484
|
if (method === 'POST' && match[2]?.startsWith('interactions/'))
|
|
945
1485
|
return { purpose: 'interaction_resolve', sessionId, interactionId: match[3] };
|
|
946
1486
|
return null;
|
|
947
1487
|
}
|
|
1488
|
+
function deriveDynamicWorkspaceRoots(manifest) {
|
|
1489
|
+
const roots = new Set();
|
|
1490
|
+
for (const grant of manifest.grants) {
|
|
1491
|
+
if (grant.kind !== 'local' || !grant.localRoot)
|
|
1492
|
+
continue;
|
|
1493
|
+
try {
|
|
1494
|
+
const canonical = realpathSync(grant.localRoot);
|
|
1495
|
+
if (statSync(canonical).isDirectory())
|
|
1496
|
+
roots.add(canonical);
|
|
1497
|
+
}
|
|
1498
|
+
catch {
|
|
1499
|
+
// A stale manifest root remains a v1 admission error; it must not make
|
|
1500
|
+
// the otherwise-compatible gateway fail before the candidate route is used.
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return [...roots];
|
|
1504
|
+
}
|
|
948
1505
|
export function classifyInteractionResolution(interaction, hasLiveSnapshot) {
|
|
949
1506
|
if (hasLiveSnapshot)
|
|
950
1507
|
return null;
|
|
@@ -968,6 +1525,132 @@ function isSessionCreateBody(body, workspaceId, legacy = false) {
|
|
|
968
1525
|
const requested = body.workspaceId;
|
|
969
1526
|
return typeof requested === 'string' && requested === workspaceId;
|
|
970
1527
|
}
|
|
1528
|
+
function isWorkspaceCandidateValidateBody(body) {
|
|
1529
|
+
return (!!body &&
|
|
1530
|
+
typeof body === 'object' &&
|
|
1531
|
+
!Array.isArray(body) &&
|
|
1532
|
+
Object.keys(body).length === 1 &&
|
|
1533
|
+
typeof body.absolutePath === 'string');
|
|
1534
|
+
}
|
|
1535
|
+
function isWorkspaceCandidateBrowseBody(body) {
|
|
1536
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
1537
|
+
return false;
|
|
1538
|
+
const value = body;
|
|
1539
|
+
return (Object.keys(value).every((key) => key === 'candidateId' || key === 'child') &&
|
|
1540
|
+
isOpaqueId(value.candidateId) &&
|
|
1541
|
+
(value.child === undefined || isOpaqueId(value.child)));
|
|
1542
|
+
}
|
|
1543
|
+
function isWorkspaceCandidateSelectBody(body) {
|
|
1544
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
1545
|
+
return false;
|
|
1546
|
+
const value = body;
|
|
1547
|
+
return (Object.keys(value).length === 2 &&
|
|
1548
|
+
Object.keys(value).every((key) => key === 'candidateId' || key === 'access') &&
|
|
1549
|
+
isOpaqueId(value.candidateId) &&
|
|
1550
|
+
(value.access === 'read' || value.access === 'read_write'));
|
|
1551
|
+
}
|
|
1552
|
+
function isSettingsWriteBody(body) {
|
|
1553
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
1554
|
+
return false;
|
|
1555
|
+
const value = body;
|
|
1556
|
+
if (typeof value.expectedRevision !== 'string' || !Array.isArray(value.changes) || value.changes.length > 100)
|
|
1557
|
+
return false;
|
|
1558
|
+
return value.changes.every((change) => {
|
|
1559
|
+
if (!change || typeof change !== 'object' || Array.isArray(change))
|
|
1560
|
+
return false;
|
|
1561
|
+
const item = change;
|
|
1562
|
+
return (Object.keys(item).length === 2 &&
|
|
1563
|
+
typeof item.key === 'string' &&
|
|
1564
|
+
Object.prototype.hasOwnProperty.call(item, 'value') &&
|
|
1565
|
+
isSafeSettingsValue(item.value));
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
function isPairingRegisterBody(body) {
|
|
1569
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
1570
|
+
return false;
|
|
1571
|
+
const value = body;
|
|
1572
|
+
return (Object.keys(value).length === 2 &&
|
|
1573
|
+
typeof value.publicKeyPem === 'string' &&
|
|
1574
|
+
value.publicKeyPem.length > 0 &&
|
|
1575
|
+
value.publicKeyPem.length <= 16_384 &&
|
|
1576
|
+
typeof value.otp === 'string' &&
|
|
1577
|
+
/^\d{6}$/.test(value.otp));
|
|
1578
|
+
}
|
|
1579
|
+
function isSafeSettingsValue(value) {
|
|
1580
|
+
return (value === null ||
|
|
1581
|
+
typeof value === 'string' ||
|
|
1582
|
+
typeof value === 'number' ||
|
|
1583
|
+
typeof value === 'boolean' ||
|
|
1584
|
+
(Array.isArray(value) && value.every((item) => typeof item === 'string')));
|
|
1585
|
+
}
|
|
1586
|
+
function isCredentialWriteBody(body) {
|
|
1587
|
+
return (!!body &&
|
|
1588
|
+
typeof body === 'object' &&
|
|
1589
|
+
!Array.isArray(body) &&
|
|
1590
|
+
Object.keys(body).length === 1 &&
|
|
1591
|
+
typeof body.value === 'string' &&
|
|
1592
|
+
body.value.length > 0);
|
|
1593
|
+
}
|
|
1594
|
+
function isOAuthSelectBody(body) {
|
|
1595
|
+
return (!!body &&
|
|
1596
|
+
typeof body === 'object' &&
|
|
1597
|
+
!Array.isArray(body) &&
|
|
1598
|
+
Object.keys(body).length === 1 &&
|
|
1599
|
+
isOpaqueId(body.accountId));
|
|
1600
|
+
}
|
|
1601
|
+
function isSessionUpdateBody(body) {
|
|
1602
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
1603
|
+
return false;
|
|
1604
|
+
const value = body;
|
|
1605
|
+
if (Object.keys(value).some((key) => !['model', 'reasoningEffort', 'mode'].includes(key)))
|
|
1606
|
+
return false;
|
|
1607
|
+
if (value.model !== undefined &&
|
|
1608
|
+
(typeof value.model !== 'string' || value.model.length === 0 || value.model.length > 512))
|
|
1609
|
+
return false;
|
|
1610
|
+
if (value.reasoningEffort !== undefined &&
|
|
1611
|
+
!['default', 'none', 'minimal', 'low', 'medium', 'high', 'xhigh'].includes(String(value.reasoningEffort)))
|
|
1612
|
+
return false;
|
|
1613
|
+
return (value.mode === undefined || ['standard', 'lite', 'plan', 'mentor', 'orchestrator'].includes(String(value.mode)));
|
|
1614
|
+
}
|
|
1615
|
+
export function sessionConfigProjection(session) {
|
|
1616
|
+
const snapshot = session.resources.settings;
|
|
1617
|
+
const service = session.resources.sessionSettingsService;
|
|
1618
|
+
const get = (key, fallback) => service?.getDynamic(key) ?? fallback;
|
|
1619
|
+
const snapshotPolicy = snapshot.toolPolicy ?? {};
|
|
1620
|
+
const sessionPolicy = session.resources.settings.toolPolicy ?? {};
|
|
1621
|
+
const toolPolicy = {
|
|
1622
|
+
allowWrite: sessionPolicy.allowWrite ?? snapshotPolicy.allowWrite ?? false,
|
|
1623
|
+
autoApprove: sessionPolicy.autoApprove ?? snapshotPolicy.autoApprove ?? false,
|
|
1624
|
+
allowUnsandboxed: sessionPolicy.allowUnsandboxed ?? snapshotPolicy.allowUnsandboxed ?? false,
|
|
1625
|
+
sshEnabled: sessionPolicy.sshEnabled ?? snapshotPolicy.sshEnabled ?? false,
|
|
1626
|
+
};
|
|
1627
|
+
const settings = {
|
|
1628
|
+
providerId: get('agent.provider', snapshot.providerId),
|
|
1629
|
+
modelId: get('agent.model', snapshot.modelId),
|
|
1630
|
+
reasoningEffort: get('agent.reasoningEffort', snapshot.reasoningEffort ?? 'default'),
|
|
1631
|
+
mode: get('app.orchestratorMode', false)
|
|
1632
|
+
? 'orchestrator'
|
|
1633
|
+
: get('app.liteMode', false)
|
|
1634
|
+
? 'lite'
|
|
1635
|
+
: get('app.planMode', false)
|
|
1636
|
+
? 'plan'
|
|
1637
|
+
: get('app.mentorMode', false)
|
|
1638
|
+
? 'mentor'
|
|
1639
|
+
: snapshot.mode ?? 'standard',
|
|
1640
|
+
toolPolicy,
|
|
1641
|
+
defaultsRevision: snapshot.defaultsRevision,
|
|
1642
|
+
};
|
|
1643
|
+
return {
|
|
1644
|
+
sessionId: session.sessionId,
|
|
1645
|
+
providerId: settings.providerId ?? 'unknown',
|
|
1646
|
+
modelId: settings.modelId ?? 'unknown',
|
|
1647
|
+
reasoningEffort: settings.reasoningEffort ?? 'default',
|
|
1648
|
+
mode: settings.mode ?? 'standard',
|
|
1649
|
+
toolPolicy,
|
|
1650
|
+
configRevision: crypto.createHash('sha256').update(JSON.stringify(settings)).digest('hex'),
|
|
1651
|
+
defaultsRevision: settings.defaultsRevision ?? 'unknown',
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
971
1654
|
function isOpaqueId(value) {
|
|
972
1655
|
return typeof value === 'string' && /^[A-Za-z0-9_-]{1,256}$/.test(value);
|
|
973
1656
|
}
|