@robota-sdk/agent-ui-terminal 3.0.0-beta.81
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/LICENSE +661 -0
- package/README.md +27 -0
- package/dist/node/index.cjs +32 -0
- package/dist/node/index.d.cts +1066 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +1066 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +33 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +93 -0
- package/src/App.tsx +146 -0
- package/src/AppPresentation.tsx +190 -0
- package/src/AppView.tsx +26 -0
- package/src/BackgroundTaskPanel.tsx +73 -0
- package/src/CjkTextInput.tsx +336 -0
- package/src/ConfirmPrompt.tsx +140 -0
- package/src/ContextWarningBanner.tsx +45 -0
- package/src/ExecutionWorkspaceDetailPane.tsx +85 -0
- package/src/ExecutionWorkspaceSwitcher.tsx +213 -0
- package/src/HistorySearchOverlay.tsx +150 -0
- package/src/InputArea.tsx +338 -0
- package/src/ListPicker.tsx +158 -0
- package/src/MenuSelect.tsx +159 -0
- package/src/MessageList.tsx +308 -0
- package/src/MultiSelectList.tsx +294 -0
- package/src/PendingActionPrompt.tsx +121 -0
- package/src/PermissionPrompt.tsx +186 -0
- package/src/PluginTUI.tsx +258 -0
- package/src/RoleLabel.tsx +72 -0
- package/src/SafeText.tsx +66 -0
- package/src/SessionEventNotices.tsx +27 -0
- package/src/SessionPicker.tsx +72 -0
- package/src/SessionStatusBar.tsx +76 -0
- package/src/SlashAutocomplete.tsx +154 -0
- package/src/StatusBar.tsx +284 -0
- package/src/StreamingIndicator.tsx +145 -0
- package/src/SupervisedSessionView.tsx +455 -0
- package/src/TextPrompt.tsx +112 -0
- package/src/ThemePicker.tsx +366 -0
- package/src/ToolCommandOutput.tsx +48 -0
- package/src/ToolDiffBlock.tsx +44 -0
- package/src/TransportTUI.tsx +166 -0
- package/src/TuiInteractionChannel.ts +567 -0
- package/src/UpdateNotice.tsx +18 -0
- package/src/UsageSummaryEntry.tsx +49 -0
- package/src/WaveText.tsx +70 -0
- package/src/__tests__/ListPicker.test.tsx +188 -0
- package/src/__tests__/MenuSelect.test.tsx +127 -0
- package/src/__tests__/PendingActionPrompt.test.tsx +193 -0
- package/src/__tests__/PluginTUI.test.tsx +167 -0
- package/src/__tests__/SlashAutocomplete.test.tsx +162 -0
- package/src/__tests__/SupervisedSessionView-render.test.tsx +24 -0
- package/src/__tests__/SupervisedSessionView.test.tsx +629 -0
- package/src/__tests__/TextPrompt.test.tsx +112 -0
- package/src/__tests__/TransportTUI.test.tsx +124 -0
- package/src/__tests__/TuiInteractionChannel.askUser.test.ts +124 -0
- package/src/__tests__/TuiInteractionChannel.display-contract.test.ts +284 -0
- package/src/__tests__/TuiInteractionChannel.lifecycle.test.ts +642 -0
- package/src/__tests__/UpdateNotice.test.tsx +15 -0
- package/src/__tests__/abort-after-permission.test.tsx +160 -0
- package/src/__tests__/abort-streaming-e2e.test.tsx +183 -0
- package/src/__tests__/attention-bell-and-marks.test.ts +144 -0
- package/src/__tests__/autocomplete-editing-keys.test.tsx +127 -0
- package/src/__tests__/background-task-panel-countdown.test.tsx +75 -0
- package/src/__tests__/background-task-panel.test.tsx +98 -0
- package/src/__tests__/background-task-row-format.test.ts +152 -0
- package/src/__tests__/channel-factory-integration.test.ts +150 -0
- package/src/__tests__/cjk-defer-submit.test.tsx +67 -0
- package/src/__tests__/cjk-fallback-render.test.tsx +78 -0
- package/src/__tests__/cjk-text-input-flow.test.ts +186 -0
- package/src/__tests__/cjk-text-input.test.ts +191 -0
- package/src/__tests__/command-handoff-pty-e2e.test.ts +107 -0
- package/src/__tests__/command-output-summary.test.ts +95 -0
- package/src/__tests__/command-result-handler.test.ts +171 -0
- package/src/__tests__/compact-event-bridge.test.ts +20 -0
- package/src/__tests__/confirm-permission-flow.test.ts +149 -0
- package/src/__tests__/confirm-prompt.test.tsx +103 -0
- package/src/__tests__/contextual-keybindings-input.test.tsx +158 -0
- package/src/__tests__/default-tui-cli-adapter-host-settings.test.ts +45 -0
- package/src/__tests__/effort-reaches-the-session.test.ts +41 -0
- package/src/__tests__/execution-workspace-switcher.test.tsx +147 -0
- package/src/__tests__/execution-workspace-view-model.test.ts +94 -0
- package/src/__tests__/external-prompt-notice.test.tsx +100 -0
- package/src/__tests__/fixtures/command-handoff-driver.tsx +111 -0
- package/src/__tests__/fixtures/fake-editor.sh +7 -0
- package/src/__tests__/fixtures/terminal-handoff-driver.tsx +90 -0
- package/src/__tests__/fork-attach.test.tsx +235 -0
- package/src/__tests__/helpers/terminal-profiles.ts +152 -0
- package/src/__tests__/helpers/vt-cursor-interpreter.ts +175 -0
- package/src/__tests__/history-clear-broadcast.test.ts +66 -0
- package/src/__tests__/history-search-overlay.test.tsx +329 -0
- package/src/__tests__/humanize-tool-name.test.ts +30 -0
- package/src/__tests__/input-area-bottom-border.test.tsx +45 -0
- package/src/__tests__/input-area-flow.test.ts +199 -0
- package/src/__tests__/input-area-focus-handoff.test.tsx +77 -0
- package/src/__tests__/interaction-channel-charter.test.ts +13 -0
- package/src/__tests__/key-hint-consistency.test.tsx +249 -0
- package/src/__tests__/key-hint-footer.test.tsx +69 -0
- package/src/__tests__/live-trace-reaches-session.test.ts +14 -0
- package/src/__tests__/message-list-rendering.test.tsx +473 -0
- package/src/__tests__/org-policy-reaches-the-channel.test.ts +77 -0
- package/src/__tests__/output-style-reaches-session.test.ts +35 -0
- package/src/__tests__/palette-consistency.test.ts +330 -0
- package/src/__tests__/permission-prompt-availability.test.tsx +23 -0
- package/src/__tests__/permission-prompt-peer.test.tsx +34 -0
- package/src/__tests__/product-display-name.test.tsx +79 -0
- package/src/__tests__/prompt-queue.test.tsx +260 -0
- package/src/__tests__/provider-definitions-reach-the-session.test.ts +75 -0
- package/src/__tests__/pty/ask-user-question.ptytest.ts +72 -0
- package/src/__tests__/pty/background-work-switcher.ptytest.ts +53 -0
- package/src/__tests__/pty/behavior-2437-git.ptytest.ts +342 -0
- package/src/__tests__/pty/fixtures/ask-user-question.jsonl +3 -0
- package/src/__tests__/pty/fixtures/replay-conversation.jsonl +4 -0
- package/src/__tests__/pty/fixtures/screen-005-ask-shapes.jsonl +5 -0
- package/src/__tests__/pty/fixtures/screen-005-permission.jsonl +3 -0
- package/src/__tests__/pty/fixtures/screen-006-colors.jsonl +3 -0
- package/src/__tests__/pty/fixtures/screen-2002-themes.jsonl +5 -0
- package/src/__tests__/pty/flag-tui.ptytest.ts +56 -0
- package/src/__tests__/pty/flow-2006-deep-link.ptytest.ts +291 -0
- package/src/__tests__/pty/ime-cursor-tmux.ptytest.ts +165 -0
- package/src/__tests__/pty/ime-cursor.ptytest.ts +186 -0
- package/src/__tests__/pty/isolated-home.ts +64 -0
- package/src/__tests__/pty/org-policy.ptytest.ts +71 -0
- package/src/__tests__/pty/provider-setup.ptytest.ts +61 -0
- package/src/__tests__/pty/pty-driver.ts +153 -0
- package/src/__tests__/pty/replay-conversation.ptytest.ts +68 -0
- package/src/__tests__/pty/screen-005-prompt-footers.ptytest.ts +134 -0
- package/src/__tests__/pty/screen-006-no-color.ptytest.ts +138 -0
- package/src/__tests__/pty/screen-010-scrollback.ptytest.ts +123 -0
- package/src/__tests__/pty/screen-1993-scrollback.ptytest.ts +124 -0
- package/src/__tests__/pty/screen-2002-themes.ptytest.ts +292 -0
- package/src/__tests__/pty/screen-2670-prepark.ptytest.ts +228 -0
- package/src/__tests__/pty/screen-reader-mode.ptytest.ts +228 -0
- package/src/__tests__/pty/settings-screen.ptytest.ts +49 -0
- package/src/__tests__/pty/spawn-pty.test.ts +120 -0
- package/src/__tests__/pty/spawn-pty.ts +229 -0
- package/src/__tests__/pty/terminal-handoff.ptytest.ts +79 -0
- package/src/__tests__/pty/tui-pty.ptytest.ts +65 -0
- package/src/__tests__/pty-driver-dispose.test.ts +43 -0
- package/src/__tests__/real-cursor-positioning.test.tsx +263 -0
- package/src/__tests__/rename-broadcast-persistence.test.tsx +140 -0
- package/src/__tests__/render-channel-options.test.ts +201 -0
- package/src/__tests__/render-lifecycle.test.ts +36 -0
- package/src/__tests__/render-markdown.test.ts +164 -0
- package/src/__tests__/rendered-markdown-styling.test.tsx +98 -0
- package/src/__tests__/safe-text-boundary.test.tsx +75 -0
- package/src/__tests__/screen-reader-input.test.tsx +106 -0
- package/src/__tests__/screen-reader-labels.test.ts +91 -0
- package/src/__tests__/screen-reader-menus.test.tsx +200 -0
- package/src/__tests__/screen-reader-pacing.test.ts +230 -0
- package/src/__tests__/screen-reader-prepark-echo.test.tsx +200 -0
- package/src/__tests__/screen-reader-render-options.test.ts +262 -0
- package/src/__tests__/screen-reader-stdout.test.ts +322 -0
- package/src/__tests__/screen-reader-turn-marks-port.test.tsx +85 -0
- package/src/__tests__/screen-reader-turn-signals.test.tsx +125 -0
- package/src/__tests__/sec-019-terminal-sanitizer.test.ts +276 -0
- package/src/__tests__/sec-019-tool-label-render.test.tsx +207 -0
- package/src/__tests__/selection-flow.test.ts +61 -0
- package/src/__tests__/session-event-notices.test.tsx +31 -0
- package/src/__tests__/session-init-poller.test.ts +102 -0
- package/src/__tests__/session-switch-channel.test.tsx +470 -0
- package/src/__tests__/shutdown-signal.test.ts +34 -0
- package/src/__tests__/status-activity.test.ts +71 -0
- package/src/__tests__/status-bar.test.tsx +282 -0
- package/src/__tests__/status-glyph.test.ts +30 -0
- package/src/__tests__/streaming-indicator.test.tsx +177 -0
- package/src/__tests__/terminal-capabilities.test.ts +153 -0
- package/src/__tests__/terminal-handoff-controller.test.ts +173 -0
- package/src/__tests__/terminal-handoff-pty-e2e.test.ts +77 -0
- package/src/__tests__/text-prompt-flow.test.ts +77 -0
- package/src/__tests__/theme-picker.test.tsx +345 -0
- package/src/__tests__/transport-boundary.test.ts +11 -0
- package/src/__tests__/tui-app-channel-port.test.ts +31 -0
- package/src/__tests__/tui-channel-init-failure.test.ts +62 -0
- package/src/__tests__/tui-channel-lifecycle-coordinator.test.ts +258 -0
- package/src/__tests__/tui-interaction-queues.test.ts +139 -0
- package/src/__tests__/tui-preset-capabilities.test.ts +33 -0
- package/src/__tests__/tui-state-manager.error-stall.test.ts +169 -0
- package/src/__tests__/tui-state-manager.test.ts +451 -0
- package/src/__tests__/ui-intent-rendering.test.tsx +291 -0
- package/src/__tests__/wave-text.test.tsx +133 -0
- package/src/app-banner.tsx +42 -0
- package/src/app-static-items.ts +35 -0
- package/src/app-view-model.ts +136 -0
- package/src/attention/__tests__/attention-coordinator.test.ts +166 -0
- package/src/attention/__tests__/attention-tracker.test.ts +103 -0
- package/src/attention/__tests__/countdown.test.ts +15 -0
- package/src/attention/__tests__/focus-input-filter.test.ts +92 -0
- package/src/attention/__tests__/interval-recap.test.ts +49 -0
- package/src/attention/attention-coordinator.ts +71 -0
- package/src/attention/attention-tracker.ts +124 -0
- package/src/attention/countdown.ts +24 -0
- package/src/attention/focus-input-filter.ts +131 -0
- package/src/attention/interval-recap.ts +148 -0
- package/src/attention/time-units.ts +6 -0
- package/src/attention-bell.ts +79 -0
- package/src/attributed-user-echo.ts +30 -0
- package/src/background-task-row-format.ts +99 -0
- package/src/command-interaction.ts +8 -0
- package/src/command-output-summary.ts +129 -0
- package/src/create-default-tui-cli-adapter.ts +45 -0
- package/src/execution-workspace-switcher-selection.ts +118 -0
- package/src/execution-workspace-view-model.ts +139 -0
- package/src/external-prompt-notice.tsx +38 -0
- package/src/flows/__tests__/background-focus-flow.test.ts +48 -0
- package/src/flows/__tests__/defer-submit.test.ts +87 -0
- package/src/flows/__tests__/real-cursor-flow.test.ts +124 -0
- package/src/flows/background-focus-flow.ts +44 -0
- package/src/flows/cjk-cursor-motion.ts +75 -0
- package/src/flows/cjk-text-deletion.ts +53 -0
- package/src/flows/cjk-text-input-flow.ts +248 -0
- package/src/flows/cjk-text-input-types.ts +24 -0
- package/src/flows/confirm-prompt-flow.ts +90 -0
- package/src/flows/defer-submit.ts +67 -0
- package/src/flows/fork-attach-flow.ts +62 -0
- package/src/flows/input-area-flow.ts +188 -0
- package/src/flows/permission-prompt-flow.ts +108 -0
- package/src/flows/real-cursor-flow.ts +83 -0
- package/src/flows/selection-flow.ts +205 -0
- package/src/flows/session-init-poller.ts +76 -0
- package/src/flows/text-prompt-flow.ts +93 -0
- package/src/history-search/__tests__/history-search-flow.test.ts +86 -0
- package/src/history-search/history-search-flow.ts +94 -0
- package/src/history-search/useHistorySearch.ts +336 -0
- package/src/hooks/__tests__/use-app-loop-escape.test.tsx +74 -0
- package/src/hooks/__tests__/use-plugin-callbacks.test.ts +18 -0
- package/src/hooks/__tests__/use-tui-channel-stability.test.tsx +100 -0
- package/src/hooks/__tests__/useAppThemeState.test.tsx +130 -0
- package/src/hooks/command-result-handler.ts +44 -0
- package/src/hooks/side-effects-types.ts +45 -0
- package/src/hooks/useAppController.ts +267 -0
- package/src/hooks/useAppInputBindings.ts +145 -0
- package/src/hooks/useAppInteractionState.ts +81 -0
- package/src/hooks/useAppLifecycleState.ts +81 -0
- package/src/hooks/useAppOverlays.ts +74 -0
- package/src/hooks/useAppScreenState.ts +81 -0
- package/src/hooks/useAppSubmissionState.ts +45 -0
- package/src/hooks/useAppThemeState.ts +157 -0
- package/src/hooks/useAppWorkspaceState.ts +149 -0
- package/src/hooks/useAppearanceSettings.ts +30 -0
- package/src/hooks/useAutocomplete.ts +101 -0
- package/src/hooks/useBackgroundPanel.ts +58 -0
- package/src/hooks/useCountdownTick.ts +24 -0
- package/src/hooks/useExecutionDetailPage.ts +69 -0
- package/src/hooks/useForkAttach.ts +60 -0
- package/src/hooks/useInputAreaHistorySearch.ts +72 -0
- package/src/hooks/useInputAreaKeys.ts +114 -0
- package/src/hooks/useNumberedSelection.ts +76 -0
- package/src/hooks/usePluginCallbacks.ts +38 -0
- package/src/hooks/usePluginScreenData.ts +85 -0
- package/src/hooks/useRealCursorPosition.ts +125 -0
- package/src/hooks/useRenderMarkdown.ts +38 -0
- package/src/hooks/useScreenReaderTurnSignals.ts +113 -0
- package/src/hooks/useSideEffects.ts +144 -0
- package/src/hooks/useStatusLineSettings.ts +50 -0
- package/src/hooks/useTerminalHandoffSuspension.ts +30 -0
- package/src/hooks/useTuiChannel.ts +129 -0
- package/src/humanize-tool-name.ts +51 -0
- package/src/index.ts +56 -0
- package/src/input-area-rules.tsx +71 -0
- package/src/json-value.ts +18 -0
- package/src/key-hint-footer.tsx +54 -0
- package/src/keybindings/__tests__/keybinding-registry.test.ts +228 -0
- package/src/keybindings/__tests__/node-keybindings-source.test.ts +111 -0
- package/src/keybindings/keybinding-catalogue.ts +171 -0
- package/src/keybindings/keybinding-registry.ts +303 -0
- package/src/keybindings/keybinding-syntax.ts +155 -0
- package/src/keybindings/keybindings-context.tsx +124 -0
- package/src/keybindings/node-keybindings-source.ts +189 -0
- package/src/numbered-list.tsx +113 -0
- package/src/plugin-tui-handlers.ts +163 -0
- package/src/product-display-name-context.tsx +30 -0
- package/src/render-markdown.ts +209 -0
- package/src/render.tsx +544 -0
- package/src/sanitize-terminal-text.ts +171 -0
- package/src/screen-reader-announcement.ts +52 -0
- package/src/screen-reader-context.tsx +32 -0
- package/src/screen-reader-labels.ts +64 -0
- package/src/screen-reader-pacing-context.tsx +47 -0
- package/src/screen-reader-pacing.ts +178 -0
- package/src/screen-reader-stdout.ts +328 -0
- package/src/shutdown-signal.ts +34 -0
- package/src/status-activity.ts +70 -0
- package/src/status-glyph.ts +78 -0
- package/src/terminal-capabilities-context.tsx +27 -0
- package/src/terminal-capabilities.ts +77 -0
- package/src/terminal-focus-reporting.ts +50 -0
- package/src/terminal-handoff-controller.ts +98 -0
- package/src/terminal-marks.ts +63 -0
- package/src/theme/__tests__/built-in-themes.test.ts +117 -0
- package/src/theme/__tests__/color-vision.test.ts +82 -0
- package/src/theme/__tests__/theme-context.test.tsx +146 -0
- package/src/theme/__tests__/theme-document.test.ts +222 -0
- package/src/theme/__tests__/theme-registry.test.ts +138 -0
- package/src/theme/__tests__/theme-styles.test.ts +233 -0
- package/src/theme/built-in-themes.ts +342 -0
- package/src/theme/color-vision.ts +171 -0
- package/src/theme/index.ts +45 -0
- package/src/theme/theme-context.tsx +81 -0
- package/src/theme/theme-contracts.ts +139 -0
- package/src/theme/theme-document.ts +278 -0
- package/src/theme/theme-registry.ts +127 -0
- package/src/theme/theme-styles.ts +177 -0
- package/src/tool-summary-status.ts +59 -0
- package/src/tui-app-channel-port.ts +83 -0
- package/src/tui-channel-lifecycle-coordinator.ts +149 -0
- package/src/tui-channel-options.ts +172 -0
- package/src/tui-cli-adapter-context.tsx +13 -0
- package/src/tui-cli-adapter.ts +24 -0
- package/src/tui-interaction-queues.ts +163 -0
- package/src/tui-session-binding.ts +52 -0
- package/src/tui-session-event-projector.ts +140 -0
- package/src/tui-session-events.ts +82 -0
- package/src/tui-session-notice-store.ts +43 -0
- package/src/tui-session-options.ts +118 -0
- package/src/tui-state-manager.ts +327 -0
- package/src/types.ts +19 -0
- package/src/use-terminal-title.ts +29 -0
- package/src/utils/__tests__/edit-diff.test.ts +426 -0
- package/src/utils/__tests__/paste-detection.test.ts +116 -0
- package/src/utils/__tests__/paste-labels.test.ts +46 -0
- package/src/utils/__tests__/tool-call-extractor.test.ts +227 -0
- package/src/utils/__tests__/tool-diff-summary.test.ts +104 -0
- package/src/utils/edit-diff.ts +151 -0
- package/src/utils/input-top-border.ts +19 -0
- package/src/utils/paste-labels.ts +9 -0
- package/src/utils/tool-call-extractor.ts +92 -0
- package/src/utils/tool-diff-summary.ts +75 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { IPendingPermissionRequest } from './types.js';
|
|
2
|
+
import type { IActionRequest, TActionResponse, TToolArgs } from '@robota-sdk/agent-core';
|
|
3
|
+
import type { TPermissionResultValue } from '@robota-sdk/agent-interface-session';
|
|
4
|
+
|
|
5
|
+
interface IUserActionQueueEntry {
|
|
6
|
+
presentedRequest: IActionRequest;
|
|
7
|
+
resolve: (response: TActionResponse) => void;
|
|
8
|
+
id?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface IPermissionQueueEntry {
|
|
12
|
+
toolName: string;
|
|
13
|
+
toolArgs: TToolArgs;
|
|
14
|
+
canPersistProjectPermission?: boolean;
|
|
15
|
+
requestedByPeer?: string;
|
|
16
|
+
resolve: (result: TPermissionResultValue) => void;
|
|
17
|
+
id?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Serializes action prompts and guarantees every pending promise is settled during teardown. */
|
|
21
|
+
export class TuiUserActionQueue {
|
|
22
|
+
private entries: IUserActionQueueEntry[] = [];
|
|
23
|
+
private processing = false;
|
|
24
|
+
|
|
25
|
+
constructor(private readonly onChange: () => void) {}
|
|
26
|
+
|
|
27
|
+
get current(): IActionRequest | null {
|
|
28
|
+
return this.processing ? (this.entries[0]?.presentedRequest ?? null) : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
enqueue(request: IActionRequest, id?: string): Promise<TActionResponse> {
|
|
32
|
+
return new Promise<TActionResponse>((resolve) => {
|
|
33
|
+
this.entries.push({
|
|
34
|
+
presentedRequest: { ...request },
|
|
35
|
+
resolve,
|
|
36
|
+
...(id !== undefined ? { id } : {}),
|
|
37
|
+
});
|
|
38
|
+
this.processNext();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
resolveCurrent(expectedRequest: IActionRequest, response: TActionResponse): void {
|
|
43
|
+
if (this.entries[0]?.presentedRequest !== expectedRequest) return;
|
|
44
|
+
const pending = this.entries.shift();
|
|
45
|
+
if (!pending) return;
|
|
46
|
+
this.processing = false;
|
|
47
|
+
this.onChange();
|
|
48
|
+
pending.resolve(response);
|
|
49
|
+
this.processNext();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
dismissById(id: string): boolean {
|
|
53
|
+
const dismissed = this.entries.filter((entry) => entry.id === id);
|
|
54
|
+
if (dismissed.length === 0) return false;
|
|
55
|
+
this.entries = this.entries.filter((entry) => entry.id !== id);
|
|
56
|
+
this.processing = false;
|
|
57
|
+
for (const entry of dismissed) entry.resolve({ type: 'cancelled' });
|
|
58
|
+
this.processNext();
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
cancelAll(): void {
|
|
63
|
+
const queued = this.entries;
|
|
64
|
+
this.entries = [];
|
|
65
|
+
this.processing = false;
|
|
66
|
+
for (const pending of queued) pending.resolve({ type: 'cancelled' });
|
|
67
|
+
this.onChange();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private processNext(): void {
|
|
71
|
+
if (this.processing) return;
|
|
72
|
+
if (!this.entries[0]) {
|
|
73
|
+
this.onChange();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
this.processing = true;
|
|
77
|
+
this.onChange();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Serializes permission prompts and denies every pending request during teardown. */
|
|
82
|
+
export class TuiPermissionQueue {
|
|
83
|
+
private entries: IPermissionQueueEntry[] = [];
|
|
84
|
+
private processing = false;
|
|
85
|
+
private currentRequest: IPendingPermissionRequest | null = null;
|
|
86
|
+
|
|
87
|
+
constructor(private readonly onChange: () => void) {}
|
|
88
|
+
|
|
89
|
+
get current(): IPendingPermissionRequest | null {
|
|
90
|
+
return this.currentRequest;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
enqueue(
|
|
94
|
+
toolName: string,
|
|
95
|
+
toolArgs: TToolArgs,
|
|
96
|
+
id?: string,
|
|
97
|
+
canPersistProjectPermission?: boolean,
|
|
98
|
+
requestedByPeer?: string,
|
|
99
|
+
): Promise<TPermissionResultValue> {
|
|
100
|
+
return new Promise<TPermissionResultValue>((resolve) => {
|
|
101
|
+
this.entries.push({
|
|
102
|
+
toolName,
|
|
103
|
+
toolArgs,
|
|
104
|
+
resolve,
|
|
105
|
+
...(id !== undefined ? { id } : {}),
|
|
106
|
+
...(canPersistProjectPermission !== undefined ? { canPersistProjectPermission } : {}),
|
|
107
|
+
...(requestedByPeer !== undefined ? { requestedByPeer } : {}),
|
|
108
|
+
});
|
|
109
|
+
this.processNext();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
dismissById(id: string): boolean {
|
|
114
|
+
const dismissed = this.entries.filter((entry) => entry.id === id);
|
|
115
|
+
if (dismissed.length === 0) return false;
|
|
116
|
+
const dismissedCurrent = this.entries[0]?.id === id;
|
|
117
|
+
this.entries = this.entries.filter((entry) => entry.id !== id);
|
|
118
|
+
for (const entry of dismissed) entry.resolve(false);
|
|
119
|
+
if (dismissedCurrent) {
|
|
120
|
+
this.processing = false;
|
|
121
|
+
this.currentRequest = null;
|
|
122
|
+
this.processNext();
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
cancelAll(): void {
|
|
128
|
+
const queued = this.entries;
|
|
129
|
+
this.entries = [];
|
|
130
|
+
this.processing = false;
|
|
131
|
+
this.currentRequest = null;
|
|
132
|
+
for (const pending of queued) pending.resolve(false);
|
|
133
|
+
this.onChange();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private processNext(): void {
|
|
137
|
+
if (this.processing) return;
|
|
138
|
+
const next = this.entries[0];
|
|
139
|
+
if (!next) {
|
|
140
|
+
this.currentRequest = null;
|
|
141
|
+
this.onChange();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.processing = true;
|
|
145
|
+
this.currentRequest = {
|
|
146
|
+
toolName: next.toolName,
|
|
147
|
+
toolArgs: next.toolArgs,
|
|
148
|
+
...(next.canPersistProjectPermission !== undefined
|
|
149
|
+
? { canPersistProjectPermission: next.canPersistProjectPermission }
|
|
150
|
+
: {}),
|
|
151
|
+
...(next.requestedByPeer !== undefined ? { requestedByPeer: next.requestedByPeer } : {}),
|
|
152
|
+
resolve: (result) => {
|
|
153
|
+
if (this.entries[0] !== next) return;
|
|
154
|
+
this.entries.shift();
|
|
155
|
+
this.processing = false;
|
|
156
|
+
this.currentRequest = null;
|
|
157
|
+
next.resolve(result);
|
|
158
|
+
setTimeout(() => this.processNext(), 0);
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
this.onChange();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { TuiStateManager } from './tui-state-manager.js';
|
|
2
|
+
import type {
|
|
3
|
+
IInteractiveSession,
|
|
4
|
+
IInteractiveSessionEvents,
|
|
5
|
+
TInteractiveEventName,
|
|
6
|
+
} from '@robota-sdk/agent-interface-session';
|
|
7
|
+
|
|
8
|
+
export interface ITuiSessionEventBinding {
|
|
9
|
+
event: TInteractiveEventName;
|
|
10
|
+
handler: (...args: never[]) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ITuiSessionBinder {
|
|
14
|
+
<E extends TInteractiveEventName>(event: E, handler: IInteractiveSessionEvents[E]): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function bindTuiSessionNoticeEvents(
|
|
18
|
+
bind: ITuiSessionBinder,
|
|
19
|
+
manager: TuiStateManager,
|
|
20
|
+
): void {
|
|
21
|
+
bind('plan_event', (payload) => manager.addSessionEventNotice({ event: 'plan_event', payload }));
|
|
22
|
+
bind('context_file_refreshed', (payload) =>
|
|
23
|
+
manager.addSessionEventNotice({ event: 'context_file_refreshed', payload }),
|
|
24
|
+
);
|
|
25
|
+
bind('branch_event', (payload) =>
|
|
26
|
+
manager.addSessionEventNotice({ event: 'branch_event', payload }),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Isolate a TUI-owned projection failure from the session operation that already committed. */
|
|
31
|
+
export function bindTuiSessionEvent<E extends TInteractiveEventName>(
|
|
32
|
+
session: Pick<IInteractiveSession, 'on'>,
|
|
33
|
+
event: E,
|
|
34
|
+
handler: IInteractiveSessionEvents[E],
|
|
35
|
+
onDeliveryError: (error: Error, event: E) => void,
|
|
36
|
+
bindings: ITuiSessionEventBinding[],
|
|
37
|
+
): void {
|
|
38
|
+
const ownedHandler = ((...args: never[]): void => {
|
|
39
|
+
try {
|
|
40
|
+
(handler as (...values: never[]) => void)(...args);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
43
|
+
try {
|
|
44
|
+
onDeliveryError(normalized, event);
|
|
45
|
+
} catch {
|
|
46
|
+
// Diagnostics cannot make a committed session operation fail.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}) as IInteractiveSessionEvents[E];
|
|
50
|
+
session.on(event, ownedHandler);
|
|
51
|
+
bindings.push({ event, handler: ownedHandler as (...args: never[]) => void });
|
|
52
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { printablePeerDriver } from '@robota-sdk/agent-core';
|
|
2
|
+
|
|
3
|
+
import { attributedUserEcho } from './attributed-user-echo.js';
|
|
4
|
+
import { bindTuiSessionEvent, bindTuiSessionNoticeEvents } from './tui-session-binding.js';
|
|
5
|
+
|
|
6
|
+
import type { AttentionCoordinator } from './attention/attention-coordinator.js';
|
|
7
|
+
import type { ITuiSessionEventBinding } from './tui-session-binding.js';
|
|
8
|
+
import type { TuiStateManager } from './tui-state-manager.js';
|
|
9
|
+
import type { IActionRequest, TActionResponse, TToolArgs } from '@robota-sdk/agent-core';
|
|
10
|
+
import type { InteractiveSession } from '@robota-sdk/agent-framework';
|
|
11
|
+
import type { IExecutionWorkspaceEvent } from '@robota-sdk/agent-interface-execution';
|
|
12
|
+
import type {
|
|
13
|
+
IExecutionResult,
|
|
14
|
+
IInteractiveSessionEvents,
|
|
15
|
+
TInteractiveEventName,
|
|
16
|
+
TPermissionResultValue,
|
|
17
|
+
} from '@robota-sdk/agent-interface-session';
|
|
18
|
+
|
|
19
|
+
export interface ITuiSessionEventProjectorOptions {
|
|
20
|
+
session: InteractiveSession;
|
|
21
|
+
manager: TuiStateManager;
|
|
22
|
+
onUserMessage: (content: string) => void;
|
|
23
|
+
requestPermission: (
|
|
24
|
+
toolName: string,
|
|
25
|
+
toolArgs: TToolArgs,
|
|
26
|
+
id: string,
|
|
27
|
+
canPersistProjectPermission?: boolean,
|
|
28
|
+
requestedByPeer?: string,
|
|
29
|
+
) => Promise<TPermissionResultValue>;
|
|
30
|
+
askUser: (request: IActionRequest, id: string) => Promise<TActionResponse>;
|
|
31
|
+
dismissPrompt: (id: string) => void;
|
|
32
|
+
onDeliveryError?: (error: Error, event: TInteractiveEventName) => void;
|
|
33
|
+
/** SCREEN-1992: fed the events the interval recap counts; absent ⇒ nothing is counted. */
|
|
34
|
+
attention?: AttentionCoordinator;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Projects runtime session events into TUI state and owns the complete listener lifecycle. */
|
|
38
|
+
export class TuiSessionEventProjector {
|
|
39
|
+
private readonly bindings: ITuiSessionEventBinding[] = [];
|
|
40
|
+
|
|
41
|
+
constructor(private readonly options: ITuiSessionEventProjectorOptions) {}
|
|
42
|
+
|
|
43
|
+
wire(): void {
|
|
44
|
+
if (this.bindings.length > 0) return;
|
|
45
|
+
const { session, manager, attention } = this.options;
|
|
46
|
+
|
|
47
|
+
const onUserMessage = (content: string): void => {
|
|
48
|
+
this.options.onUserMessage(content);
|
|
49
|
+
manager.addUserEcho(attributedUserEcho(content, session));
|
|
50
|
+
};
|
|
51
|
+
const syncHistory = (): void => manager.syncHistory(session.getFullHistory());
|
|
52
|
+
const onComplete = (result: IExecutionResult): void => {
|
|
53
|
+
manager.onComplete(result);
|
|
54
|
+
attention?.onComplete();
|
|
55
|
+
syncHistory();
|
|
56
|
+
};
|
|
57
|
+
const onError = (error: Error): void => {
|
|
58
|
+
manager.onError(error);
|
|
59
|
+
attention?.onError();
|
|
60
|
+
syncHistory();
|
|
61
|
+
};
|
|
62
|
+
const onExecutionWorkspaceEvent = (event: IExecutionWorkspaceEvent): void => {
|
|
63
|
+
manager.syncExecutionWorkspaceSnapshot(event.snapshot);
|
|
64
|
+
attention?.onWorkspaceSnapshot(event.snapshot);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
this.bind('user_message', onUserMessage);
|
|
68
|
+
this.bind('text_delta', manager.onTextDelta);
|
|
69
|
+
this.bind('tool_start', manager.onToolStart);
|
|
70
|
+
this.bind('tool_end', manager.onToolEnd);
|
|
71
|
+
this.bind('thinking', manager.onThinking);
|
|
72
|
+
this.bind('complete', onComplete);
|
|
73
|
+
this.bind('interrupted', manager.onInterrupted);
|
|
74
|
+
this.bind('error', onError);
|
|
75
|
+
this.bind('context_update', manager.onContextUpdate);
|
|
76
|
+
this.bind('compact', syncHistory);
|
|
77
|
+
this.bind('skill_activation', syncHistory);
|
|
78
|
+
this.bind('memory_event', syncHistory);
|
|
79
|
+
this.bind('execution_workspace_event', onExecutionWorkspaceEvent);
|
|
80
|
+
this.bind('history_cleared', () => manager.clearHistory());
|
|
81
|
+
// SCREEN-1992: subscribed regardless, so the channel's subscribed set equals the classification.
|
|
82
|
+
this.bind('turn_source', (source) => attention?.onTurnSource(source));
|
|
83
|
+
bindTuiSessionNoticeEvents(this.bind.bind(this), manager);
|
|
84
|
+
|
|
85
|
+
const onPermissionRequest: IInteractiveSessionEvents['permission_request'] = ({
|
|
86
|
+
id,
|
|
87
|
+
toolName,
|
|
88
|
+
toolArgs,
|
|
89
|
+
canPersistProjectPermission,
|
|
90
|
+
requesterDriverId,
|
|
91
|
+
}) => {
|
|
92
|
+
attention?.onNeedsInput();
|
|
93
|
+
// A peer turn's ask names the peer, printed only as a plain identifier.
|
|
94
|
+
const requestedByPeer = requesterDriverId?.startsWith('peer:')
|
|
95
|
+
? printablePeerDriver(requesterDriverId)
|
|
96
|
+
: undefined;
|
|
97
|
+
void this.options
|
|
98
|
+
.requestPermission(toolName, toolArgs, id, canPersistProjectPermission, requestedByPeer)
|
|
99
|
+
.then((result) => session.resolvePermission(id, result))
|
|
100
|
+
.catch(() => session.resolvePermission(id, false));
|
|
101
|
+
};
|
|
102
|
+
const onAskRequest: IInteractiveSessionEvents['ask_request'] = ({ id, request }) => {
|
|
103
|
+
attention?.onNeedsInput();
|
|
104
|
+
void this.options
|
|
105
|
+
.askUser(request, id)
|
|
106
|
+
.then((response) => session.resolveAsk(id, response))
|
|
107
|
+
.catch(() => session.resolveAsk(id, { type: 'cancelled' }));
|
|
108
|
+
};
|
|
109
|
+
this.bind('permission_request', onPermissionRequest);
|
|
110
|
+
this.bind('ask_request', onAskRequest);
|
|
111
|
+
this.bind('prompt_resolved', ({ id }) => this.options.dismissPrompt(id));
|
|
112
|
+
attention?.wire();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
unwire(): void {
|
|
116
|
+
const { session } = this.options;
|
|
117
|
+
this.options.attention?.unwire();
|
|
118
|
+
for (const { event, handler } of this.bindings) {
|
|
119
|
+
session.off(event, handler as IInteractiveSessionEvents[typeof event]);
|
|
120
|
+
}
|
|
121
|
+
this.bindings.length = 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private bind<E extends TInteractiveEventName>(
|
|
125
|
+
event: E,
|
|
126
|
+
handler: IInteractiveSessionEvents[E],
|
|
127
|
+
): void {
|
|
128
|
+
bindTuiSessionEvent(
|
|
129
|
+
this.options.session,
|
|
130
|
+
event,
|
|
131
|
+
handler,
|
|
132
|
+
(error) => {
|
|
133
|
+
const report = this.options.onDeliveryError;
|
|
134
|
+
if (report) report(error, event);
|
|
135
|
+
else this.options.manager.addSessionEventDeliveryError(error, event);
|
|
136
|
+
},
|
|
137
|
+
this.bindings,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IBranchEvent,
|
|
3
|
+
IContextFileRefreshedEvent,
|
|
4
|
+
IPlanApprovalEvent,
|
|
5
|
+
TInteractiveEventName,
|
|
6
|
+
} from '@robota-sdk/agent-interface-session';
|
|
7
|
+
|
|
8
|
+
export type TTuiSessionEventClassification = 'channel' | 'react-side-effect' | 'non-surface';
|
|
9
|
+
|
|
10
|
+
/** Exhaustive TUI-owner policy over the shared session-event vocabulary. */
|
|
11
|
+
export const TUI_SESSION_EVENT_CLASSIFICATION = {
|
|
12
|
+
text_delta: 'channel',
|
|
13
|
+
tool_start: 'channel',
|
|
14
|
+
tool_end: 'channel',
|
|
15
|
+
thinking: 'channel',
|
|
16
|
+
complete: 'channel',
|
|
17
|
+
error: 'channel',
|
|
18
|
+
context_update: 'channel',
|
|
19
|
+
compact: 'channel',
|
|
20
|
+
interrupted: 'channel',
|
|
21
|
+
skill_activation: 'channel',
|
|
22
|
+
background_task_event: 'non-surface',
|
|
23
|
+
background_job_group_event: 'non-surface',
|
|
24
|
+
execution_workspace_event: 'channel',
|
|
25
|
+
user_message: 'channel',
|
|
26
|
+
turn_source: 'channel',
|
|
27
|
+
context_file_refreshed: 'channel',
|
|
28
|
+
memory_event: 'channel',
|
|
29
|
+
goal_event: 'non-surface',
|
|
30
|
+
plan_event: 'channel',
|
|
31
|
+
branch_event: 'channel',
|
|
32
|
+
permission_request: 'channel',
|
|
33
|
+
ask_request: 'channel',
|
|
34
|
+
prompt_resolved: 'channel',
|
|
35
|
+
ui_intent: 'react-side-effect',
|
|
36
|
+
session_renamed: 'react-side-effect',
|
|
37
|
+
history_cleared: 'channel',
|
|
38
|
+
} as const satisfies Record<TInteractiveEventName, TTuiSessionEventClassification>;
|
|
39
|
+
|
|
40
|
+
export interface ITuiSessionEventNotice {
|
|
41
|
+
id: string;
|
|
42
|
+
event:
|
|
43
|
+
| 'plan_event'
|
|
44
|
+
| 'context_file_refreshed'
|
|
45
|
+
| 'branch_event'
|
|
46
|
+
| 'delivery-error'
|
|
47
|
+
/** SCREEN-1992: TUI-originated — the interval recap on return, not a session event. */
|
|
48
|
+
| 'attention-recap';
|
|
49
|
+
message: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type TTuiNoticeInput =
|
|
53
|
+
| { event: 'plan_event'; payload: IPlanApprovalEvent }
|
|
54
|
+
| { event: 'context_file_refreshed'; payload: IContextFileRefreshedEvent }
|
|
55
|
+
| { event: 'branch_event'; payload: IBranchEvent };
|
|
56
|
+
|
|
57
|
+
/** Pure deterministic projection from domain event to TUI render state. */
|
|
58
|
+
export function createTuiSessionEventNotice(
|
|
59
|
+
id: string,
|
|
60
|
+
input: TTuiNoticeInput,
|
|
61
|
+
): ITuiSessionEventNotice {
|
|
62
|
+
switch (input.event) {
|
|
63
|
+
case 'plan_event':
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
event: input.event,
|
|
67
|
+
message: `Plan ${input.payload.type.replaceAll('_', ' ')}`,
|
|
68
|
+
};
|
|
69
|
+
case 'context_file_refreshed':
|
|
70
|
+
return {
|
|
71
|
+
id,
|
|
72
|
+
event: input.event,
|
|
73
|
+
message: `Context refreshed: ${input.payload.filePath}`,
|
|
74
|
+
};
|
|
75
|
+
case 'branch_event':
|
|
76
|
+
return {
|
|
77
|
+
id,
|
|
78
|
+
event: input.event,
|
|
79
|
+
message: `Branch ${input.payload.kind.replaceAll('_', ' ')}: ${input.payload.branchId} @ ${input.payload.checkpointId}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTuiSessionEventNotice,
|
|
3
|
+
type ITuiSessionEventNotice,
|
|
4
|
+
type TTuiNoticeInput,
|
|
5
|
+
} from './tui-session-events.js';
|
|
6
|
+
|
|
7
|
+
import type { TInteractiveEventName } from '@robota-sdk/agent-interface-session';
|
|
8
|
+
|
|
9
|
+
const NOTICE_LIMIT = 50;
|
|
10
|
+
|
|
11
|
+
/** Bounded append-only render projection, deliberately separate from canonical session history. */
|
|
12
|
+
export class TuiSessionNoticeStore {
|
|
13
|
+
private sequence = 0;
|
|
14
|
+
readonly notices: ITuiSessionEventNotice[] = [];
|
|
15
|
+
|
|
16
|
+
add(input: TTuiNoticeInput): void {
|
|
17
|
+
this.append(createTuiSessionEventNotice(this.nextId(), input));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
addDeliveryError(error: Error, event: TInteractiveEventName): void {
|
|
21
|
+
this.append({
|
|
22
|
+
id: this.nextId(),
|
|
23
|
+
event: 'delivery-error',
|
|
24
|
+
message: `Session event delivery failed (${event}): ${error.message}`,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** SCREEN-1992: one line for the unattended interval that just ended. */
|
|
29
|
+
addAttentionRecap(line: string): void {
|
|
30
|
+
this.append({ id: this.nextId(), event: 'attention-recap', message: line });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private nextId(): string {
|
|
34
|
+
this.sequence += 1;
|
|
35
|
+
return `session-event-${this.sequence}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private append(notice: ITuiSessionEventNotice): void {
|
|
39
|
+
this.notices.push(notice);
|
|
40
|
+
const overflow = this.notices.length - NOTICE_LIMIT;
|
|
41
|
+
if (overflow > 0) this.notices.splice(0, overflow);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARCH-005 — the TUI's session-option mapping, split out of `TuiInteractionChannel`.
|
|
3
|
+
*
|
|
4
|
+
* Pure: it maps the channel's already-resolved options onto the framework's `TInteractiveSessionOptions`.
|
|
5
|
+
* RUNTIME-001 — the channel then builds through the shared `buildRuntimeSession` seam (agent-framework),
|
|
6
|
+
* never a private `new InteractiveSession`: one session-construction SSOT across the TUI, print, and
|
|
7
|
+
* `--serve`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ITuiInteractionChannelOptions } from './tui-channel-options.js';
|
|
11
|
+
import type { TInteractiveSessionOptions } from '@robota-sdk/agent-framework';
|
|
12
|
+
|
|
13
|
+
/** Map the channel's resolved options onto the framework session-construction options. */
|
|
14
|
+
export function buildTuiSessionOptions(
|
|
15
|
+
opts: ITuiInteractionChannelOptions,
|
|
16
|
+
): TInteractiveSessionOptions {
|
|
17
|
+
return {
|
|
18
|
+
cwd: opts.cwd,
|
|
19
|
+
...(opts.livePromptTrace ? { livePromptTrace: opts.livePromptTrace } : {}),
|
|
20
|
+
provider: opts.provider,
|
|
21
|
+
...(opts.providerErrorGuidance !== undefined
|
|
22
|
+
? { providerErrorGuidance: opts.providerErrorGuidance }
|
|
23
|
+
: {}),
|
|
24
|
+
...(opts.promptFileReferenceTag !== undefined
|
|
25
|
+
? { promptFileReferenceTag: opts.promptFileReferenceTag }
|
|
26
|
+
: {}),
|
|
27
|
+
...(opts.modelCommandToolPrefix !== undefined
|
|
28
|
+
? { modelCommandToolPrefix: opts.modelCommandToolPrefix }
|
|
29
|
+
: {}),
|
|
30
|
+
...(opts.subagentHookEnvironmentNames !== undefined
|
|
31
|
+
? { subagentHookEnvironmentNames: opts.subagentHookEnvironmentNames }
|
|
32
|
+
: {}),
|
|
33
|
+
...(opts.observerFailureWarningCode !== undefined
|
|
34
|
+
? { observerFailureWarningCode: opts.observerFailureWarningCode }
|
|
35
|
+
: {}),
|
|
36
|
+
...(opts.commandHookShell !== undefined ? { commandHookShell: opts.commandHookShell } : {}),
|
|
37
|
+
...(opts.bare === true ? { bare: true } : {}),
|
|
38
|
+
...(opts.skipConfiguredHooks === true ? { skipConfiguredHooks: true } : {}),
|
|
39
|
+
...(opts.orgPolicy !== undefined ? { orgPolicy: opts.orgPolicy } : {}),
|
|
40
|
+
...(opts.projectAccess !== undefined ? { projectAccess: opts.projectAccess } : {}),
|
|
41
|
+
...(opts.projectSettingsPaths !== undefined
|
|
42
|
+
? { projectSettingsPaths: opts.projectSettingsPaths }
|
|
43
|
+
: {}),
|
|
44
|
+
...(opts.baselinePermissionAllow !== undefined
|
|
45
|
+
? { baselinePermissionAllow: opts.baselinePermissionAllow }
|
|
46
|
+
: {}),
|
|
47
|
+
...(opts.taskContext !== undefined ? { taskContext: opts.taskContext } : {}),
|
|
48
|
+
...(opts.contributionSources !== undefined
|
|
49
|
+
? { contributionSources: opts.contributionSources }
|
|
50
|
+
: {}),
|
|
51
|
+
...(opts.skillRoots !== undefined ? { skillRoots: opts.skillRoots } : {}),
|
|
52
|
+
...(opts.userSettingsSources !== undefined
|
|
53
|
+
? { userSettingsSources: opts.userSettingsSources }
|
|
54
|
+
: {}),
|
|
55
|
+
...(opts.editCheckpointStore !== undefined
|
|
56
|
+
? { editCheckpointStore: opts.editCheckpointStore }
|
|
57
|
+
: {}),
|
|
58
|
+
// CLI-076: forward the resolved model so `--model` takes effect rather than falling through to the
|
|
59
|
+
// session's config/default model.
|
|
60
|
+
...(opts.model !== undefined ? { model: opts.model } : {}),
|
|
61
|
+
...(opts.effort !== undefined ? { effort: opts.effort } : {}),
|
|
62
|
+
...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),
|
|
63
|
+
...(opts.maxOutputTokens !== undefined ? { maxOutputTokens: opts.maxOutputTokens } : {}),
|
|
64
|
+
...(opts.presetSystemPrompt !== undefined
|
|
65
|
+
? { presetSystemPrompt: opts.presetSystemPrompt }
|
|
66
|
+
: {}),
|
|
67
|
+
...(opts.responseFormat !== undefined ? { responseFormat: opts.responseFormat } : {}),
|
|
68
|
+
...(opts.outputStyle !== undefined ? { outputStyle: opts.outputStyle } : {}),
|
|
69
|
+
permissionMode: opts.permissionMode,
|
|
70
|
+
maxTurns: opts.maxTurns,
|
|
71
|
+
// REMOTE-007: no injected permission/ask handlers — the TUI subscribes to the session's
|
|
72
|
+
// transport-neutral `permission_request`/`ask_request` events (wireSessionEvents) and answers via
|
|
73
|
+
// `resolvePermission`/`resolveAsk`. The local Ink queues + rendering are unchanged.
|
|
74
|
+
sessionStore: opts.sessionStore,
|
|
75
|
+
disableSessionLoops: opts.disableSessionLoops,
|
|
76
|
+
resolveDefaultLoopPrompt: opts.resolveDefaultLoopPrompt,
|
|
77
|
+
resumeSessionId: opts.resumeSessionId,
|
|
78
|
+
forkSession: opts.forkSession,
|
|
79
|
+
...(opts.workspaceMovedFrom !== undefined
|
|
80
|
+
? { workspaceMovedFrom: opts.workspaceMovedFrom }
|
|
81
|
+
: {}),
|
|
82
|
+
sessionName: opts.sessionName,
|
|
83
|
+
backgroundTaskRunners: opts.backgroundTaskRunners,
|
|
84
|
+
...(opts.toolCallHandoff !== undefined ? { toolCallHandoff: opts.toolCallHandoff } : {}),
|
|
85
|
+
subagentRunnerFactory: opts.subagentRunnerFactory,
|
|
86
|
+
...(opts.agentDefinitions !== undefined ? { agentDefinitions: opts.agentDefinitions } : {}),
|
|
87
|
+
...(opts.agentDefinitionRoots !== undefined
|
|
88
|
+
? { agentDefinitionRoots: opts.agentDefinitionRoots }
|
|
89
|
+
: {}),
|
|
90
|
+
...(opts.pluginDirectories !== undefined ? { pluginDirectories: opts.pluginDirectories } : {}),
|
|
91
|
+
...(opts.additionalTools !== undefined ? { additionalTools: opts.additionalTools } : {}),
|
|
92
|
+
...(opts.defaultTools !== undefined ? { defaultTools: opts.defaultTools } : {}),
|
|
93
|
+
commandModules: opts.commandModules,
|
|
94
|
+
commandHostAdapters: opts.commandHostAdapters,
|
|
95
|
+
shellExec: opts.shellExec,
|
|
96
|
+
...(opts.remoteCommandPolicy ? { remoteCommandPolicy: opts.remoteCommandPolicy } : {}),
|
|
97
|
+
language: opts.language,
|
|
98
|
+
agentName: opts.agentName,
|
|
99
|
+
activePresetId: opts.activePresetId,
|
|
100
|
+
persona: opts.persona,
|
|
101
|
+
systemPrompt: opts.systemPrompt,
|
|
102
|
+
appendSystemPrompt: opts.appendSystemPrompt,
|
|
103
|
+
allowedTools: opts.allowedTools,
|
|
104
|
+
deniedTools: opts.deniedTools,
|
|
105
|
+
enableParallelSubagents: opts.enableParallelSubagents,
|
|
106
|
+
selfVerification: opts.selfVerification,
|
|
107
|
+
terminalHandoff: opts.terminalHandoff,
|
|
108
|
+
// #1844: the session reads these when `/provider switch` hot-swaps. Absent, the switch throws
|
|
109
|
+
// "Unknown provider: <name>. Currently supported: " with an EMPTY list — measured, not inferred.
|
|
110
|
+
...(opts.providerDefinitions ? { providerDefinitions: opts.providerDefinitions } : {}),
|
|
111
|
+
// SELFHOST-008 P6: forward the surface-resolved memory fields only when present (absent ⇒ OFF).
|
|
112
|
+
...(opts.memoryStore ? { memoryStore: opts.memoryStore } : {}),
|
|
113
|
+
...(opts.automaticMemory ? { automaticMemory: opts.automaticMemory } : {}),
|
|
114
|
+
...(opts.recallMemory ? { recallMemory: opts.recallMemory } : {}),
|
|
115
|
+
// SCREEN-1993: the owner-prompt append (absent ⇒ the controller records nothing).
|
|
116
|
+
...(opts.promptHistory ? { promptHistory: opts.promptHistory } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|