parallel-codex-tui 0.2.10 → 0.3.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 +21 -6
- package/dist/bootstrap.js +8 -1
- package/dist/cli.js +10 -1
- package/dist/core/role-configuration.js +238 -0
- package/dist/orchestrator/orchestrator.js +135 -11
- package/dist/tui/App.js +397 -4
- package/dist/tui/AppShell.js +7 -1
- package/dist/tui/InputBar.js +27 -3
- package/dist/tui/RoleConfigurationView.js +96 -0
- package/dist/tui/StatusDetailView.js +9 -6
- package/dist/tui/keyboard.js +11 -0
- package/dist/tui/role-configuration-state.js +74 -0
- package/dist/version.js +1 -1
- package/dist/workers/native-attach.js +5 -1
- package/package.json +1 -1
package/dist/tui/App.js
CHANGED
|
@@ -5,6 +5,7 @@ import { Box, Text, useApp, useInput, useStdin } from "ink";
|
|
|
5
5
|
import { Lexer } from "marked";
|
|
6
6
|
import { copyTextToClipboard } from "../core/clipboard.js";
|
|
7
7
|
import { readJson } from "../core/file-store.js";
|
|
8
|
+
import { CONFIGURABLE_ROLES, configuredRoleSelection } from "../core/role-configuration.js";
|
|
8
9
|
import { WorkerStatusSchema } from "../domain/schemas.js";
|
|
9
10
|
import { effectiveWorkerWatchdog, formatRoutePendingStatus, formatRoutePendingSummaryStatus, formatRouteStatus, formatRouteSummaryStatus, formatStatusLine, formatWorkerRuntimeStatus } from "./status-line.js";
|
|
10
11
|
import { applyChatInputChunk, insertChatPaste } from "./chat-input.js";
|
|
@@ -19,7 +20,7 @@ import { TerminalOutput } from "./TerminalOutput.js";
|
|
|
19
20
|
import { NativeTerminalScreen } from "./terminal-screen.js";
|
|
20
21
|
import { WorkerOutputView } from "./WorkerOutputView.js";
|
|
21
22
|
import { compactEndByDisplayWidth, displayWidth, wrapByDisplayWidth } from "./display-width.js";
|
|
22
|
-
import { isAttachShortcut, isCopyShortcut, isDiagnosticsShortcut, isExitShortcut, isLogsShortcut, isNewConversationShortcut, isRouterDiagnosticsShortcut, isStatusDetailsShortcut, isTaskResultShortcut, isTaskSessionsShortcut, isWorkerOverviewShortcut, isWorkerSearchShortcut, isWorkspaceShortcut, mouseScrollDelta, rawChatScrollArrowDelta, rawHistoryDelta, rawPageScrollDelta, rawPlainArrowDelta, scrollDelta, workerLogJumpKind } from "./keyboard.js";
|
|
23
|
+
import { isAttachShortcut, isCopyShortcut, isDiagnosticsShortcut, isExitShortcut, isLogsShortcut, isNewConversationShortcut, isRoleConfigurationShortcut, isRouterDiagnosticsShortcut, isStatusDetailsShortcut, isTaskResultShortcut, isTaskSessionsShortcut, isWorkerOverviewShortcut, isWorkerSearchShortcut, isWorkspaceShortcut, mouseScrollDelta, rawChatScrollArrowDelta, rawHistoryDelta, rawHorizontalArrowDelta, rawPageScrollDelta, rawPlainArrowDelta, scrollDelta, workerLogJumpKind } from "./keyboard.js";
|
|
23
24
|
import { createRawInputDecoder, tokenizeRawInput } from "./raw-input-decoder.js";
|
|
24
25
|
import { decodeHtmlEntities } from "./markdown-text.js";
|
|
25
26
|
import { configureTuiTheme, TUI_THEME } from "./theme.js";
|
|
@@ -35,6 +36,8 @@ import { latestTaskResultMessageIndex, parseTaskResultSummary } from "./task-res
|
|
|
35
36
|
import { buildNativeAttachLaunch, buildNativeForkLaunch, startNativeAttachProcess } from "../workers/native-attach.js";
|
|
36
37
|
import { assignableWorkerProviderIds, workerProviderLabel, workerProviders } from "../workers/provider.js";
|
|
37
38
|
import { StatusDetailView } from "./StatusDetailView.js";
|
|
39
|
+
import { RoleConfigurationView } from "./RoleConfigurationView.js";
|
|
40
|
+
import { cycleRoleProvider, moveRoleConfigurationSelection, nextRoleConfigurationScope, roleConfigurationScopeHasOverride, roleConfigurationSelectionForScope, selectedConfigurableRole, updateRoleModel } from "./role-configuration-state.js";
|
|
38
41
|
import { runtimeConfigChange } from "./runtime-config-state.js";
|
|
39
42
|
export function routeDecisionChatMessage(route) {
|
|
40
43
|
return `route · ${route.mode} · ${route.source ?? "router"}\n${route.reason.trim()}`;
|
|
@@ -77,6 +80,15 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
77
80
|
const [canRetryTask, setCanRetryTask] = useState(initialCanRetryTask);
|
|
78
81
|
const [attachError, setAttachError] = useState(null);
|
|
79
82
|
const [configChange, setConfigChange] = useState(null);
|
|
83
|
+
const [roleConfigurationSnapshot, setRoleConfigurationSnapshot] = useState(null);
|
|
84
|
+
const [roleConfigurationDraft, setRoleConfigurationDraft] = useState(null);
|
|
85
|
+
const [roleConfigurationScope, setRoleConfigurationScope] = useState("next");
|
|
86
|
+
const [roleConfigurationSelectedIndex, setRoleConfigurationSelectedIndex] = useState(0);
|
|
87
|
+
const [roleConfigurationLoading, setRoleConfigurationLoading] = useState(false);
|
|
88
|
+
const [roleConfigurationSaving, setRoleConfigurationSaving] = useState(false);
|
|
89
|
+
const [roleConfigurationNotice, setRoleConfigurationNotice] = useState(null);
|
|
90
|
+
const [roleConfigurationError, setRoleConfigurationError] = useState(null);
|
|
91
|
+
const [roleModelEdit, setRoleModelEdit] = useState(null);
|
|
80
92
|
const [clipboardNotice, setClipboardNotice] = useState(null);
|
|
81
93
|
const [nativeInput, setNativeInput] = useState("");
|
|
82
94
|
const [workerScrollOffset, setWorkerScrollOffset] = useState(0);
|
|
@@ -169,6 +181,13 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
169
181
|
const featureBoardSelectedIndexRef = useRef(0);
|
|
170
182
|
const featureCancelPromptRef = useRef(null);
|
|
171
183
|
const featureAssignmentPromptRef = useRef(null);
|
|
184
|
+
const roleConfigurationSnapshotRef = useRef(null);
|
|
185
|
+
const roleConfigurationDraftRef = useRef(null);
|
|
186
|
+
const roleConfigurationScopeRef = useRef("next");
|
|
187
|
+
const roleConfigurationSelectedIndexRef = useRef(0);
|
|
188
|
+
const roleConfigurationLoadingRef = useRef(false);
|
|
189
|
+
const roleConfigurationSavingRef = useRef(false);
|
|
190
|
+
const roleModelEditRef = useRef(null);
|
|
172
191
|
const collaborationFeatureIndexRef = useRef(-1);
|
|
173
192
|
const collaborationSelectedEventIdRef = useRef(null);
|
|
174
193
|
const collaborationDetailOpenRef = useRef(false);
|
|
@@ -188,6 +207,9 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
188
207
|
const confirmFeatureCancellationRef = useRef(confirmFeatureCancellation);
|
|
189
208
|
const reassignSelectedFeatureRef = useRef(reassignSelectedFeature);
|
|
190
209
|
const openCollaborationTimelineRef = useRef(openCollaborationTimeline);
|
|
210
|
+
const openRoleConfigurationRef = useRef(openRoleConfiguration);
|
|
211
|
+
const applyRoleConfigurationRef = useRef(applyRoleConfiguration);
|
|
212
|
+
const clearRoleConfigurationRef = useRef(clearRoleConfiguration);
|
|
191
213
|
const refreshCollaborationTimelineRef = useRef(refreshCollaborationTimeline);
|
|
192
214
|
const activateSelectedTaskSessionRef = useRef(activateSelectedTaskSession);
|
|
193
215
|
const refreshTaskSessionsRef = useRef(refreshTaskSessions);
|
|
@@ -213,6 +235,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
213
235
|
const taskSessionsReturnViewRef = useRef("chat");
|
|
214
236
|
const collaborationReturnViewRef = useRef("workers");
|
|
215
237
|
const statusReturnViewRef = useRef("chat");
|
|
238
|
+
const roleConfigurationReturnViewRef = useRef("chat");
|
|
239
|
+
const roleConfigurationLoadSequenceRef = useRef(0);
|
|
216
240
|
const collaborationLoadSequenceRef = useRef(0);
|
|
217
241
|
const routerLoadSequenceRef = useRef(0);
|
|
218
242
|
const taskSessionsLoadSequenceRef = useRef(0);
|
|
@@ -256,6 +280,14 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
256
280
|
const taskResultMessageIndex = useMemo(() => activeTaskId ? latestTaskResultMessageIndex(messages, activeTaskId) : -1, [activeTaskId, messages]);
|
|
257
281
|
const visibleChatMessages = useMemo(() => routeAnnouncement ? [...messages, routeAnnouncement] : messages, [messages, routeAnnouncement]);
|
|
258
282
|
const hasTaskResult = taskResultMessageIndex >= 0;
|
|
283
|
+
const activeRoleSelection = roleConfigurationSnapshot
|
|
284
|
+
? activeTaskId
|
|
285
|
+
? roleConfigurationSnapshot.activeTurn
|
|
286
|
+
?? roleConfigurationSnapshot.task
|
|
287
|
+
?? roleConfigurationSnapshot.future
|
|
288
|
+
: roleConfigurationSnapshot.future
|
|
289
|
+
: configuredRoleSelection(config);
|
|
290
|
+
const roleConfigurationHasOverride = roleConfigurationScopeHasOverride(roleConfigurationSnapshot, roleConfigurationScope);
|
|
259
291
|
useEffect(() => {
|
|
260
292
|
inputRef.current = input;
|
|
261
293
|
}, [input]);
|
|
@@ -325,6 +357,54 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
325
357
|
useEffect(() => {
|
|
326
358
|
canRetryTaskRef.current = canRetryTask;
|
|
327
359
|
}, [canRetryTask]);
|
|
360
|
+
useEffect(() => {
|
|
361
|
+
roleConfigurationSnapshotRef.current = roleConfigurationSnapshot;
|
|
362
|
+
}, [roleConfigurationSnapshot]);
|
|
363
|
+
useEffect(() => {
|
|
364
|
+
roleConfigurationDraftRef.current = roleConfigurationDraft;
|
|
365
|
+
}, [roleConfigurationDraft]);
|
|
366
|
+
useEffect(() => {
|
|
367
|
+
roleConfigurationScopeRef.current = roleConfigurationScope;
|
|
368
|
+
}, [roleConfigurationScope]);
|
|
369
|
+
useEffect(() => {
|
|
370
|
+
roleConfigurationSelectedIndexRef.current = roleConfigurationSelectedIndex;
|
|
371
|
+
}, [roleConfigurationSelectedIndex]);
|
|
372
|
+
useEffect(() => {
|
|
373
|
+
roleConfigurationLoadingRef.current = roleConfigurationLoading;
|
|
374
|
+
}, [roleConfigurationLoading]);
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
roleConfigurationSavingRef.current = roleConfigurationSaving;
|
|
377
|
+
}, [roleConfigurationSaving]);
|
|
378
|
+
useEffect(() => {
|
|
379
|
+
roleModelEditRef.current = roleModelEdit;
|
|
380
|
+
}, [roleModelEdit]);
|
|
381
|
+
useEffect(() => {
|
|
382
|
+
let active = true;
|
|
383
|
+
const sequence = roleConfigurationLoadSequenceRef.current + 1;
|
|
384
|
+
roleConfigurationLoadSequenceRef.current = sequence;
|
|
385
|
+
const snapshotPromise = typeof orchestrator.roleConfigurationSnapshot === "function"
|
|
386
|
+
? orchestrator.roleConfigurationSnapshot(activeTaskId)
|
|
387
|
+
: Promise.resolve(fallbackRoleConfigurationSnapshot(config));
|
|
388
|
+
void snapshotPromise.then((snapshot) => {
|
|
389
|
+
if (!active || roleConfigurationLoadSequenceRef.current !== sequence) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
393
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
394
|
+
if (viewRef.current === "roles") {
|
|
395
|
+
const draft = roleConfigurationSelectionForScope(snapshot, roleConfigurationScopeRef.current);
|
|
396
|
+
roleConfigurationDraftRef.current = draft;
|
|
397
|
+
setRoleConfigurationDraft(draft);
|
|
398
|
+
}
|
|
399
|
+
}).catch((error) => {
|
|
400
|
+
if (active && roleConfigurationLoadSequenceRef.current === sequence) {
|
|
401
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
return () => {
|
|
405
|
+
active = false;
|
|
406
|
+
};
|
|
407
|
+
}, [activeTaskId, orchestrator]);
|
|
328
408
|
useEffect(() => {
|
|
329
409
|
taskResultExpandedRef.current = taskResultExpanded;
|
|
330
410
|
}, [taskResultExpanded]);
|
|
@@ -433,6 +513,9 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
433
513
|
openWorkerOverviewRef.current = openWorkerOverview;
|
|
434
514
|
openTaskSessionsRef.current = openTaskSessions;
|
|
435
515
|
openFeatureBoardRef.current = openFeatureBoard;
|
|
516
|
+
openRoleConfigurationRef.current = openRoleConfiguration;
|
|
517
|
+
applyRoleConfigurationRef.current = applyRoleConfiguration;
|
|
518
|
+
clearRoleConfigurationRef.current = clearRoleConfiguration;
|
|
436
519
|
confirmFeatureCancellationRef.current = confirmFeatureCancellation;
|
|
437
520
|
openCollaborationTimelineRef.current = openCollaborationTimeline;
|
|
438
521
|
refreshCollaborationTimelineRef.current = refreshCollaborationTimeline;
|
|
@@ -828,6 +911,137 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
828
911
|
if (currentView === "workspace") {
|
|
829
912
|
return;
|
|
830
913
|
}
|
|
914
|
+
if (currentView !== "native"
|
|
915
|
+
&& tokenizeRawInput(chunk).some((inputChunk) => isRoleConfigurationShortcut(inputChunk, {}))
|
|
916
|
+
&& !busyRef.current) {
|
|
917
|
+
if (currentView === "roles") {
|
|
918
|
+
roleModelEditRef.current = null;
|
|
919
|
+
setRoleModelEdit(null);
|
|
920
|
+
viewRef.current = roleConfigurationReturnViewRef.current;
|
|
921
|
+
setView(roleConfigurationReturnViewRef.current);
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
void openRoleConfigurationRef.current();
|
|
925
|
+
}
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (currentView === "roles") {
|
|
929
|
+
const roleChunks = tokenizeRawInput(chunk);
|
|
930
|
+
if (roleChunks.some((roleChunk) => isExitShortcut(roleChunk, {}))) {
|
|
931
|
+
activeRunControllerRef.current?.abort();
|
|
932
|
+
exitRef.current();
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (roleChunks.some((roleChunk) => isStatusDetailsShortcut(roleChunk, {}))) {
|
|
936
|
+
statusReturnViewRef.current = "roles";
|
|
937
|
+
viewRef.current = "status";
|
|
938
|
+
setView("status");
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
for (const roleChunk of roleChunks) {
|
|
942
|
+
const edit = roleModelEditRef.current;
|
|
943
|
+
if (edit) {
|
|
944
|
+
if (roleChunk === "\x1b") {
|
|
945
|
+
roleModelEditRef.current = null;
|
|
946
|
+
setRoleModelEdit(null);
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const update = applyChatInputChunk(edit.value, roleChunk, edit.cursor);
|
|
950
|
+
if (update.submit !== null) {
|
|
951
|
+
const draft = roleConfigurationDraftRef.current;
|
|
952
|
+
if (draft) {
|
|
953
|
+
const nextDraft = updateRoleModel(draft, edit.role, update.submit);
|
|
954
|
+
roleConfigurationDraftRef.current = nextDraft;
|
|
955
|
+
setRoleConfigurationDraft(nextDraft);
|
|
956
|
+
}
|
|
957
|
+
roleModelEditRef.current = null;
|
|
958
|
+
setRoleModelEdit(null);
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
const nextEdit = { ...edit, value: update.value, cursor: update.cursor };
|
|
962
|
+
roleModelEditRef.current = nextEdit;
|
|
963
|
+
setRoleModelEdit(nextEdit);
|
|
964
|
+
}
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
if (roleChunk === "\x1b") {
|
|
968
|
+
viewRef.current = roleConfigurationReturnViewRef.current;
|
|
969
|
+
setView(roleConfigurationReturnViewRef.current);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
if (roleConfigurationLoadingRef.current || roleConfigurationSavingRef.current) {
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
if (roleChunk === "\t") {
|
|
976
|
+
const nextScope = nextRoleConfigurationScope(roleConfigurationScopeRef.current, 1, Boolean(activeTaskIdRef.current));
|
|
977
|
+
roleConfigurationScopeRef.current = nextScope;
|
|
978
|
+
setRoleConfigurationScope(nextScope);
|
|
979
|
+
const snapshot = roleConfigurationSnapshotRef.current;
|
|
980
|
+
if (snapshot) {
|
|
981
|
+
const draft = roleConfigurationSelectionForScope(snapshot, nextScope);
|
|
982
|
+
roleConfigurationDraftRef.current = draft;
|
|
983
|
+
setRoleConfigurationDraft(draft);
|
|
984
|
+
}
|
|
985
|
+
setRoleConfigurationNotice(null);
|
|
986
|
+
setRoleConfigurationError(null);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
const selectionDelta = -(rawHistoryDelta(roleChunk)
|
|
990
|
+
+ rawPageScrollDelta(roleChunk, CONFIGURABLE_ROLES.length)
|
|
991
|
+
+ mouseScrollDelta(roleChunk, 1));
|
|
992
|
+
if (selectionDelta !== 0) {
|
|
993
|
+
const nextIndex = moveRoleConfigurationSelection(roleConfigurationSelectedIndexRef.current, selectionDelta);
|
|
994
|
+
roleConfigurationSelectedIndexRef.current = nextIndex;
|
|
995
|
+
setRoleConfigurationSelectedIndex(nextIndex);
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
const providerDelta = rawHorizontalArrowDelta(roleChunk)
|
|
999
|
+
|| (roleChunk === "]" ? 1 : roleChunk === "[" ? -1 : 0);
|
|
1000
|
+
if (providerDelta !== 0) {
|
|
1001
|
+
const snapshot = roleConfigurationSnapshotRef.current;
|
|
1002
|
+
const draft = roleConfigurationDraftRef.current;
|
|
1003
|
+
if (snapshot && draft) {
|
|
1004
|
+
const nextDraft = cycleRoleProvider(draft, selectedConfigurableRole(roleConfigurationSelectedIndexRef.current), snapshot.providers, providerDelta);
|
|
1005
|
+
roleConfigurationDraftRef.current = nextDraft;
|
|
1006
|
+
setRoleConfigurationDraft(nextDraft);
|
|
1007
|
+
setRoleConfigurationNotice(null);
|
|
1008
|
+
setRoleConfigurationError(null);
|
|
1009
|
+
}
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (roleChunk === "m" || roleChunk === "M") {
|
|
1013
|
+
const role = selectedConfigurableRole(roleConfigurationSelectedIndexRef.current);
|
|
1014
|
+
const value = roleConfigurationDraftRef.current?.[role].model ?? "";
|
|
1015
|
+
const editState = {
|
|
1016
|
+
role,
|
|
1017
|
+
value,
|
|
1018
|
+
cursor: Array.from(value).length
|
|
1019
|
+
};
|
|
1020
|
+
roleModelEditRef.current = editState;
|
|
1021
|
+
setRoleModelEdit(editState);
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
if (roleChunk === "\r" || roleChunk === "\n") {
|
|
1025
|
+
void applyRoleConfigurationRef.current();
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (roleChunk === "x" || roleChunk === "X") {
|
|
1029
|
+
void clearRoleConfigurationRef.current();
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (roleChunk === "r" || roleChunk === "R") {
|
|
1033
|
+
const snapshot = roleConfigurationSnapshotRef.current;
|
|
1034
|
+
if (snapshot) {
|
|
1035
|
+
const draft = roleConfigurationSelectionForScope(snapshot, roleConfigurationScopeRef.current);
|
|
1036
|
+
roleConfigurationDraftRef.current = draft;
|
|
1037
|
+
setRoleConfigurationDraft(draft);
|
|
1038
|
+
setRoleConfigurationNotice("Draft restored from saved configuration");
|
|
1039
|
+
setRoleConfigurationError(null);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
831
1045
|
if (currentView === "status") {
|
|
832
1046
|
if (isExitShortcut(chunk, {})) {
|
|
833
1047
|
activeRunControllerRef.current?.abort();
|
|
@@ -1841,6 +2055,15 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1841
2055
|
.join("\n")
|
|
1842
2056
|
};
|
|
1843
2057
|
}
|
|
2058
|
+
if (currentView === "roles") {
|
|
2059
|
+
const roles = roleConfigurationDraftRef.current;
|
|
2060
|
+
return {
|
|
2061
|
+
label: "role configuration",
|
|
2062
|
+
text: roles
|
|
2063
|
+
? CONFIGURABLE_ROLES.map((role) => `${role} · ${roles[role].engine} · ${roles[role].model || "default"}`).join("\n")
|
|
2064
|
+
: ""
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
1844
2067
|
if (currentView === "workers") {
|
|
1845
2068
|
return {
|
|
1846
2069
|
label: "workers",
|
|
@@ -2187,6 +2410,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2187
2410
|
cwd,
|
|
2188
2411
|
taskId: target.taskId,
|
|
2189
2412
|
route: followUpRoute?.route,
|
|
2413
|
+
roleSelection: followUpRoute?.roleSelection,
|
|
2190
2414
|
...callbacks
|
|
2191
2415
|
})
|
|
2192
2416
|
: target.kind === "task-question"
|
|
@@ -2195,6 +2419,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2195
2419
|
cwd,
|
|
2196
2420
|
taskId: target.taskId,
|
|
2197
2421
|
route: followUpRoute?.route,
|
|
2422
|
+
roleSelection: followUpRoute?.roleSelection,
|
|
2198
2423
|
...callbacks
|
|
2199
2424
|
})
|
|
2200
2425
|
: await orchestrator.handleRequest({
|
|
@@ -2209,6 +2434,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2209
2434
|
setCanRetryTask(nextMemory.activeTaskId
|
|
2210
2435
|
? await orchestrator.canRetryTask(nextMemory.activeTaskId)
|
|
2211
2436
|
: false);
|
|
2437
|
+
await refreshRoleConfigurationState(nextMemory.activeTaskId);
|
|
2212
2438
|
setRouteAnnouncement(null);
|
|
2213
2439
|
await appendVisibleMessage({ from: "system", text: result.summary }, nextMemory.activeTaskId ?? undefined);
|
|
2214
2440
|
if (result.mode === "complex" && parseTaskResultSummary(result.summary)) {
|
|
@@ -2221,6 +2447,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2221
2447
|
if (retryTaskId) {
|
|
2222
2448
|
setCanRetryTask(await orchestrator.canRetryTask(retryTaskId));
|
|
2223
2449
|
}
|
|
2450
|
+
await refreshRoleConfigurationState(retryTaskId);
|
|
2224
2451
|
await appendVisibleMessage({
|
|
2225
2452
|
from: "system",
|
|
2226
2453
|
text: isAbortError(error) ? "cancelled · request stopped" : error instanceof Error ? error.message : String(error)
|
|
@@ -2268,6 +2495,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2268
2495
|
setActiveTaskId(taskId);
|
|
2269
2496
|
setActiveMode("complex");
|
|
2270
2497
|
setCanRetryTask(false);
|
|
2498
|
+
await refreshRoleConfigurationState(taskId);
|
|
2271
2499
|
setRouteAnnouncement(null);
|
|
2272
2500
|
await appendVisibleMessage({ from: "system", text: result.summary }, taskId);
|
|
2273
2501
|
if (parseTaskResultSummary(result.summary)) {
|
|
@@ -2277,6 +2505,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2277
2505
|
catch (error) {
|
|
2278
2506
|
setRouteAnnouncement(null);
|
|
2279
2507
|
setCanRetryTask(await orchestrator.canRetryTask(taskId));
|
|
2508
|
+
await refreshRoleConfigurationState(taskId);
|
|
2280
2509
|
await appendVisibleMessage({
|
|
2281
2510
|
from: "system",
|
|
2282
2511
|
text: isAbortError(error) ? "cancelled · retry stopped" : error instanceof Error ? error.message : String(error)
|
|
@@ -2305,6 +2534,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2305
2534
|
return;
|
|
2306
2535
|
}
|
|
2307
2536
|
resetActiveTaskUiForMainConversation();
|
|
2537
|
+
await refreshRoleConfigurationState(null);
|
|
2308
2538
|
await appendVisibleMessage({ from: "system", text: "new conversation · ready" });
|
|
2309
2539
|
}
|
|
2310
2540
|
function resetActiveTaskUiForMainConversation() {
|
|
@@ -3070,6 +3300,153 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3070
3300
|
setAttachError(null);
|
|
3071
3301
|
setView("workers");
|
|
3072
3302
|
}
|
|
3303
|
+
async function refreshRoleConfigurationState(taskId) {
|
|
3304
|
+
try {
|
|
3305
|
+
const snapshot = typeof orchestrator.roleConfigurationSnapshot === "function"
|
|
3306
|
+
? await orchestrator.roleConfigurationSnapshot(taskId)
|
|
3307
|
+
: fallbackRoleConfigurationSnapshot(config);
|
|
3308
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3309
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3310
|
+
if (viewRef.current === "roles") {
|
|
3311
|
+
const draft = roleConfigurationSelectionForScope(snapshot, roleConfigurationScopeRef.current);
|
|
3312
|
+
roleConfigurationDraftRef.current = draft;
|
|
3313
|
+
setRoleConfigurationDraft(draft);
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
catch (error) {
|
|
3317
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
async function openRoleConfiguration() {
|
|
3321
|
+
const currentView = viewRef.current;
|
|
3322
|
+
if (busyRef.current || currentView === "native" || currentView === "workspace") {
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3325
|
+
const returnView = currentView === "roles" ? roleConfigurationReturnViewRef.current : currentView;
|
|
3326
|
+
roleConfigurationReturnViewRef.current = returnView;
|
|
3327
|
+
const scope = roleConfigurationScopeRef.current === "task" && !activeTaskIdRef.current
|
|
3328
|
+
? "next"
|
|
3329
|
+
: roleConfigurationScopeRef.current;
|
|
3330
|
+
roleConfigurationScopeRef.current = scope;
|
|
3331
|
+
setRoleConfigurationScope(scope);
|
|
3332
|
+
roleModelEditRef.current = null;
|
|
3333
|
+
setRoleModelEdit(null);
|
|
3334
|
+
setRoleConfigurationNotice(null);
|
|
3335
|
+
setRoleConfigurationError(null);
|
|
3336
|
+
roleConfigurationLoadingRef.current = true;
|
|
3337
|
+
setRoleConfigurationLoading(true);
|
|
3338
|
+
viewRef.current = "roles";
|
|
3339
|
+
setView("roles");
|
|
3340
|
+
const sequence = roleConfigurationLoadSequenceRef.current + 1;
|
|
3341
|
+
roleConfigurationLoadSequenceRef.current = sequence;
|
|
3342
|
+
try {
|
|
3343
|
+
const snapshot = typeof orchestrator.roleConfigurationSnapshot === "function"
|
|
3344
|
+
? await orchestrator.roleConfigurationSnapshot(activeTaskIdRef.current)
|
|
3345
|
+
: fallbackRoleConfigurationSnapshot(config);
|
|
3346
|
+
if (!mountedRef.current || roleConfigurationLoadSequenceRef.current !== sequence) {
|
|
3347
|
+
return;
|
|
3348
|
+
}
|
|
3349
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3350
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3351
|
+
const draft = roleConfigurationSelectionForScope(snapshot, scope);
|
|
3352
|
+
roleConfigurationDraftRef.current = draft;
|
|
3353
|
+
setRoleConfigurationDraft(draft);
|
|
3354
|
+
}
|
|
3355
|
+
catch (error) {
|
|
3356
|
+
if (mountedRef.current && roleConfigurationLoadSequenceRef.current === sequence) {
|
|
3357
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
finally {
|
|
3361
|
+
if (mountedRef.current && roleConfigurationLoadSequenceRef.current === sequence) {
|
|
3362
|
+
roleConfigurationLoadingRef.current = false;
|
|
3363
|
+
setRoleConfigurationLoading(false);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
async function applyRoleConfiguration() {
|
|
3368
|
+
const draft = roleConfigurationDraftRef.current;
|
|
3369
|
+
if (!draft || roleConfigurationSavingRef.current || roleConfigurationLoadingRef.current) {
|
|
3370
|
+
return;
|
|
3371
|
+
}
|
|
3372
|
+
const scope = roleConfigurationScopeRef.current;
|
|
3373
|
+
const taskId = activeTaskIdRef.current;
|
|
3374
|
+
if (scope === "task" && !taskId) {
|
|
3375
|
+
setRoleConfigurationError("No active Task · choose next request or future requests");
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3378
|
+
roleConfigurationSavingRef.current = true;
|
|
3379
|
+
setRoleConfigurationSaving(true);
|
|
3380
|
+
setRoleConfigurationNotice(null);
|
|
3381
|
+
setRoleConfigurationError(null);
|
|
3382
|
+
try {
|
|
3383
|
+
if (typeof orchestrator.updateRoleConfiguration !== "function") {
|
|
3384
|
+
throw new Error("Role configuration is unavailable in this runtime");
|
|
3385
|
+
}
|
|
3386
|
+
const snapshot = await orchestrator.updateRoleConfiguration({
|
|
3387
|
+
scope,
|
|
3388
|
+
roles: draft,
|
|
3389
|
+
taskId
|
|
3390
|
+
});
|
|
3391
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3392
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3393
|
+
const saved = roleConfigurationSelectionForScope(snapshot, scope);
|
|
3394
|
+
roleConfigurationDraftRef.current = saved;
|
|
3395
|
+
setRoleConfigurationDraft(saved);
|
|
3396
|
+
setRoleConfigurationNotice(scope === "next"
|
|
3397
|
+
? "Saved · the next request will consume this matrix once"
|
|
3398
|
+
: scope === "task"
|
|
3399
|
+
? "Saved · current Task retries and follow-ups will use this matrix"
|
|
3400
|
+
: "Saved · future requests now use this default matrix");
|
|
3401
|
+
if (scope === "task" && taskId) {
|
|
3402
|
+
void refreshCollaborationTimelineRef.current(false);
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
catch (error) {
|
|
3406
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3407
|
+
}
|
|
3408
|
+
finally {
|
|
3409
|
+
roleConfigurationSavingRef.current = false;
|
|
3410
|
+
setRoleConfigurationSaving(false);
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
async function clearRoleConfiguration() {
|
|
3414
|
+
if (roleConfigurationSavingRef.current || roleConfigurationLoadingRef.current) {
|
|
3415
|
+
return;
|
|
3416
|
+
}
|
|
3417
|
+
const scope = roleConfigurationScopeRef.current;
|
|
3418
|
+
const taskId = activeTaskIdRef.current;
|
|
3419
|
+
if (scope === "task" && !taskId) {
|
|
3420
|
+
setRoleConfigurationError("No active Task · choose next request or future requests");
|
|
3421
|
+
return;
|
|
3422
|
+
}
|
|
3423
|
+
roleConfigurationSavingRef.current = true;
|
|
3424
|
+
setRoleConfigurationSaving(true);
|
|
3425
|
+
setRoleConfigurationNotice(null);
|
|
3426
|
+
setRoleConfigurationError(null);
|
|
3427
|
+
try {
|
|
3428
|
+
if (typeof orchestrator.clearRoleConfiguration !== "function") {
|
|
3429
|
+
throw new Error("Role configuration is unavailable in this runtime");
|
|
3430
|
+
}
|
|
3431
|
+
const snapshot = await orchestrator.clearRoleConfiguration(scope, taskId);
|
|
3432
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3433
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3434
|
+
const inherited = roleConfigurationSelectionForScope(snapshot, scope);
|
|
3435
|
+
roleConfigurationDraftRef.current = inherited;
|
|
3436
|
+
setRoleConfigurationDraft(inherited);
|
|
3437
|
+
setRoleConfigurationNotice(scope === "future" ? "Reset · future requests inherit config.toml" : "Reset · scope now inherits future defaults");
|
|
3438
|
+
if (scope === "task" && taskId) {
|
|
3439
|
+
void refreshCollaborationTimelineRef.current(false);
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
catch (error) {
|
|
3443
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3444
|
+
}
|
|
3445
|
+
finally {
|
|
3446
|
+
roleConfigurationSavingRef.current = false;
|
|
3447
|
+
setRoleConfigurationSaving(false);
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3073
3450
|
function openWorkspacePicker() {
|
|
3074
3451
|
const currentView = viewRef.current;
|
|
3075
3452
|
if (busyRef.current || !switchWorkspace || currentView === "native" || currentView === "workspace") {
|
|
@@ -3113,11 +3490,11 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3113
3490
|
? { type: "delete", title: taskSessionAction.title }
|
|
3114
3491
|
: null, taskSessionsIncludeArchived: sessionCenterMode === "conversations"
|
|
3115
3492
|
? mainConversationsIncludeArchived
|
|
3116
|
-
: taskSessionsIncludeArchived, mainConversationSessions: sessionCenterMode === "conversations" && !taskSessionDetails, taskSessionDetail: Boolean(taskSessionDetails), taskSessionDetailHasNative: taskSessionDetailHasNative, taskSessionDetailCanFork: taskSessionDetailCanFork, canRetry: canRetryTask, hasWorkers: workers.length > 0, hasActiveTask: Boolean(activeTaskId), hasTaskResult: hasTaskResult, taskResultExpanded: taskResultExpanded, chatScrollOffset: chatScrollOffset, chatMaxScrollOffset: chatMaxScrollOffset, nativeClosed: view === "native" && nativeAttach?.closedCode !== null, searchMatchIndex: workerSearch.matchIndex, searchMatchCount: workerNavigationTargets.searchOffsets.length, clipboardNotice: clipboardNotice, value: workerSearch.open && view === "worker"
|
|
3493
|
+
: taskSessionsIncludeArchived, mainConversationSessions: sessionCenterMode === "conversations" && !taskSessionDetails, taskSessionDetail: Boolean(taskSessionDetails), taskSessionDetailHasNative: taskSessionDetailHasNative, taskSessionDetailCanFork: taskSessionDetailCanFork, canRetry: canRetryTask, hasWorkers: workers.length > 0, hasActiveTask: Boolean(activeTaskId), hasTaskResult: hasTaskResult, taskResultExpanded: taskResultExpanded, chatScrollOffset: chatScrollOffset, chatMaxScrollOffset: chatMaxScrollOffset, nativeClosed: view === "native" && nativeAttach?.closedCode !== null, searchMatchIndex: workerSearch.matchIndex, searchMatchCount: workerNavigationTargets.searchOffsets.length, clipboardNotice: clipboardNotice, roleScope: roleConfigurationScope, roleEditingModel: roleModelEdit, roleCanApply: roleConfigurationScope !== "task" || Boolean(activeTaskId), roleSaving: roleConfigurationSaving, roleHasOverride: roleConfigurationHasOverride, value: workerSearch.open && view === "worker"
|
|
3117
3494
|
? workerSearch.query
|
|
3118
|
-
: view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" ? "" : input, cursor: workerSearch.open && view === "worker"
|
|
3495
|
+
: view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" || view === "roles" ? "" : input, cursor: workerSearch.open && view === "worker"
|
|
3119
3496
|
? workerSearch.cursor
|
|
3120
|
-
: view === "chat" ? inputCursor : undefined, terminalWidth: terminalWidth, onChange: view === "native" ? setNativeInput : setInput, onSubmit: view === "native" ? undefined : submit }), error: attachError, children: view === "status" ? (_jsx(StatusDetailView, { cwd: cwd, taskId: activeTaskId, mode: activeMode, busy: busy, canRetry: canRetryTask, taskStatus: visibleTaskStatus, routeStatus: visibleRouteStatus, routeReason: routePending ? undefined : lastRoute?.reason, pairing: config.pairing, configStatus: configChange?.detail, configRestartRequired: configChange?.kind === "restart", workers: workers, selectedWorkerIndex: selectedWorkerIndex, height: contentHeight, terminalWidth: terminalWidth })) : view === "native" ? (_jsx(NativeAttachView, { attach: nativeAttach, viewportHeight: contentHeight })) : view === "sessions" ? (taskSessionDetails ? (_jsx(TaskSessionDetailView, { details: taskSessionDetails, selectedWorkerIndex: taskSessionDetailSelectedWorkerIndex, loading: taskSessionsLoading, error: taskSessionsError, notice: taskSessionDetailNotice, height: contentHeight, terminalWidth: terminalWidth })) : sessionCenterMode === "conversations" ? (_jsx(MainConversationsView, { conversations: mainConversations, selectedIndex: selectedMainConversationIndex, includeArchived: mainConversationsIncludeArchived, notice: taskSessionsNotice, action: mainConversationAction?.type === "rename"
|
|
3497
|
+
: view === "chat" ? inputCursor : undefined, terminalWidth: terminalWidth, onChange: view === "native" ? setNativeInput : setInput, onSubmit: view === "native" ? undefined : submit }), error: attachError, children: view === "roles" ? (_jsx(RoleConfigurationView, { snapshot: roleConfigurationSnapshot, draft: roleConfigurationDraft, scope: roleConfigurationScope, selectedRoleIndex: roleConfigurationSelectedIndex, loading: roleConfigurationLoading, saving: roleConfigurationSaving, notice: roleConfigurationNotice, error: roleConfigurationError, hasActiveTask: Boolean(activeTaskId), height: contentHeight, terminalWidth: terminalWidth })) : view === "status" ? (_jsx(StatusDetailView, { cwd: cwd, taskId: activeTaskId, mode: activeMode, busy: busy, canRetry: canRetryTask, taskStatus: visibleTaskStatus, routeStatus: visibleRouteStatus, routeReason: routePending ? undefined : lastRoute?.reason, pairing: config.pairing, roleSelection: activeRoleSelection, configStatus: configChange?.detail, configRestartRequired: configChange?.kind === "restart", workers: workers, selectedWorkerIndex: selectedWorkerIndex, height: contentHeight, terminalWidth: terminalWidth })) : view === "native" ? (_jsx(NativeAttachView, { attach: nativeAttach, viewportHeight: contentHeight })) : view === "sessions" ? (taskSessionDetails ? (_jsx(TaskSessionDetailView, { details: taskSessionDetails, selectedWorkerIndex: taskSessionDetailSelectedWorkerIndex, loading: taskSessionsLoading, error: taskSessionsError, notice: taskSessionDetailNotice, height: contentHeight, terminalWidth: terminalWidth })) : sessionCenterMode === "conversations" ? (_jsx(MainConversationsView, { conversations: mainConversations, selectedIndex: selectedMainConversationIndex, includeArchived: mainConversationsIncludeArchived, notice: taskSessionsNotice, action: mainConversationAction?.type === "rename"
|
|
3121
3498
|
? { type: "rename", title: mainConversationAction.title }
|
|
3122
3499
|
: mainConversationAction?.type === "delete"
|
|
3123
3500
|
? { type: "delete", title: mainConversationAction.title }
|
|
@@ -3154,6 +3531,22 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3154
3531
|
}
|
|
3155
3532
|
} })) }));
|
|
3156
3533
|
}
|
|
3534
|
+
function fallbackRoleConfigurationSnapshot(config) {
|
|
3535
|
+
const roles = configuredRoleSelection(config);
|
|
3536
|
+
return {
|
|
3537
|
+
baseline: roles,
|
|
3538
|
+
future: configuredRoleSelection(config),
|
|
3539
|
+
next: null,
|
|
3540
|
+
task: null,
|
|
3541
|
+
activeTurn: null,
|
|
3542
|
+
providers: workerProviders(config).map(({ id, config: provider }) => ({
|
|
3543
|
+
id,
|
|
3544
|
+
model: provider.model.name,
|
|
3545
|
+
modelProvider: provider.model.provider,
|
|
3546
|
+
assignable: provider.assignable
|
|
3547
|
+
}))
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3157
3550
|
function readTerminalSize() {
|
|
3158
3551
|
return {
|
|
3159
3552
|
columns: Math.max(1, Math.trunc(process.stdout.columns || 120)),
|
package/dist/tui/AppShell.js
CHANGED
|
@@ -236,6 +236,9 @@ function viewLabel(view) {
|
|
|
236
236
|
return shortViewLabel(view);
|
|
237
237
|
}
|
|
238
238
|
function shortcutHint(view) {
|
|
239
|
+
if (view === "roles") {
|
|
240
|
+
return "^E back";
|
|
241
|
+
}
|
|
239
242
|
if (view === "status") {
|
|
240
243
|
return "^S back";
|
|
241
244
|
}
|
|
@@ -245,6 +248,9 @@ function shortcutHint(view) {
|
|
|
245
248
|
return "^S status · ^C exit";
|
|
246
249
|
}
|
|
247
250
|
function shortViewLabel(view) {
|
|
251
|
+
if (view === "roles") {
|
|
252
|
+
return "roles";
|
|
253
|
+
}
|
|
248
254
|
if (view === "status") {
|
|
249
255
|
return "status";
|
|
250
256
|
}
|
|
@@ -272,7 +278,7 @@ function shortViewLabel(view) {
|
|
|
272
278
|
return "chat";
|
|
273
279
|
}
|
|
274
280
|
function shortShortcutHint(view) {
|
|
275
|
-
return view === "native" ? "^]" : view === "status" ? "^S" : "^C";
|
|
281
|
+
return view === "native" ? "^]" : view === "status" ? "^S" : view === "roles" ? "^E" : "^C";
|
|
276
282
|
}
|
|
277
283
|
function compactHeaderProject(cwd, maxLength) {
|
|
278
284
|
const project = basename(cwd) || cwd;
|
package/dist/tui/InputBar.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { compactEndByDisplayWidth, compactTailByDisplayWidth, displayWidth } from "./display-width.js";
|
|
4
4
|
import { TUI_THEME } from "./theme.js";
|
|
5
|
-
export function InputBar({ mode, ready = true, busy = false, routeFallback = false, collaborationDetail = false, collaborationUnresolved = false, collaborationBack = "workers", featureCanCancel = false, featureCanPause = false, featureCanReassign = false, featureCancelConfirm = false, featurePauseConfirm = false, featureAssignment = false, taskSessionAction = null, taskSessionsIncludeArchived = false, mainConversationSessions = false, taskSessionDetail = false, taskSessionDetailHasNative = false, taskSessionDetailCanFork = false, canRetry = false, hasWorkers = false, hasActiveTask = false, hasTaskResult = false, taskResultExpanded = false, chatScrollOffset = 0, chatMaxScrollOffset = 0, nativeClosed = false, searchMatchIndex = 0, searchMatchCount = 0, clipboardNotice = null, value, cursor, terminalWidth: providedTerminalWidth, onChange, onSubmit }) {
|
|
5
|
+
export function InputBar({ mode, ready = true, busy = false, routeFallback = false, collaborationDetail = false, collaborationUnresolved = false, collaborationBack = "workers", featureCanCancel = false, featureCanPause = false, featureCanReassign = false, featureCancelConfirm = false, featurePauseConfirm = false, featureAssignment = false, taskSessionAction = null, taskSessionsIncludeArchived = false, mainConversationSessions = false, taskSessionDetail = false, taskSessionDetailHasNative = false, taskSessionDetailCanFork = false, canRetry = false, hasWorkers = false, hasActiveTask = false, hasTaskResult = false, taskResultExpanded = false, chatScrollOffset = 0, chatMaxScrollOffset = 0, nativeClosed = false, searchMatchIndex = 0, searchMatchCount = 0, clipboardNotice = null, roleScope = "next", roleEditingModel = null, roleCanApply = true, roleSaving = false, roleHasOverride = false, value, cursor, terminalWidth: providedTerminalWidth, onChange, onSubmit }) {
|
|
6
6
|
const terminalWidth = providedTerminalWidth ?? process.stdout.columns ?? 120;
|
|
7
7
|
const fillRail = providedTerminalWidth !== undefined || typeof process.stdout.columns === "number";
|
|
8
8
|
if (clipboardNotice) {
|
|
@@ -19,6 +19,17 @@ export function InputBar({ mode, ready = true, busy = false, routeFallback = fal
|
|
|
19
19
|
const textWidth = displayWidth(`${prefix}${display.before}|${display.after}${suffix}`);
|
|
20
20
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: textWidth, fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: prefix }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.before }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: "|" }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.after }), suffix ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: suffix }) : null] }));
|
|
21
21
|
}
|
|
22
|
+
if (mode === "roles") {
|
|
23
|
+
if (roleEditingModel) {
|
|
24
|
+
const prefix = `${roleEditingModel.role} model > `;
|
|
25
|
+
const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
|
|
26
|
+
const display = chatInputDisplayParts(roleEditingModel.value, roleEditingModel.cursor, valueWidth);
|
|
27
|
+
const textWidth = displayWidth(`${prefix}${display.before}|${display.after}`);
|
|
28
|
+
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: textWidth, fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: prefix }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.before }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: "|" }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.after })] }));
|
|
29
|
+
}
|
|
30
|
+
const hints = roleConfigurationInputHints(terminalWidth, roleScope, roleCanApply, roleSaving, roleHasOverride);
|
|
31
|
+
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: roleSaving ? TUI_THEME.warning : TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
32
|
+
}
|
|
22
33
|
if (mode === "status") {
|
|
23
34
|
const hints = statusDetailInputHints(terminalWidth);
|
|
24
35
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
@@ -422,14 +433,27 @@ function workerInputHints(width) {
|
|
|
422
433
|
}
|
|
423
434
|
function statusDetailInputHints(width) {
|
|
424
435
|
return selectInputHints(width, [
|
|
425
|
-
{ label: "status", detail: " · ^X diagnostics · ^S/Esc back · ^C exit" },
|
|
426
|
-
{ label: "status", detail: " · ^X diag · ^S/Esc back · ^C exit" },
|
|
436
|
+
{ label: "status", detail: " · ^E roles · ^X diagnostics · ^S/Esc back · ^C exit" },
|
|
437
|
+
{ label: "status", detail: " · ^E roles · ^X diag · ^S/Esc back · ^C exit" },
|
|
427
438
|
{ label: "status", detail: " · ^S back · ^C exit" },
|
|
428
439
|
{ label: "status", detail: " · ^S back" },
|
|
429
440
|
{ label: "status", detail: "" },
|
|
430
441
|
{ label: "st", detail: "" }
|
|
431
442
|
]);
|
|
432
443
|
}
|
|
444
|
+
function roleConfigurationInputHints(width, scope, canApply, saving, hasOverride) {
|
|
445
|
+
const label = saving ? "roles · saving" : `roles · ${scope}`;
|
|
446
|
+
const apply = canApply ? "Enter apply" : "task unavailable";
|
|
447
|
+
const reset = hasOverride ? " · X reset" : "";
|
|
448
|
+
return selectInputHints(width, [
|
|
449
|
+
{ label, detail: ` · Tab scope · Up/Dn role · Left/Right provider · M model · ${apply}${reset} · ^E/Esc back` },
|
|
450
|
+
{ label, detail: ` · Tab scope · Up/Dn · Left/Right provider · M model · ${apply}${reset} · Esc back` },
|
|
451
|
+
{ label, detail: ` · Tab · Up/Dn · Left/Right · M model · ${apply}${reset}` },
|
|
452
|
+
{ label, detail: ` · Tab · arrows · M · ${canApply ? "Enter" : "no task"}` },
|
|
453
|
+
{ label: "roles", detail: " · Tab · arrows · M · Enter" },
|
|
454
|
+
{ label: "roles", detail: "" }
|
|
455
|
+
]);
|
|
456
|
+
}
|
|
433
457
|
function workerOverviewInputHints(width) {
|
|
434
458
|
if (width < 16) {
|
|
435
459
|
return { label: "wrk", detail: "" };
|