parallel-codex-tui 0.2.10 → 0.3.1
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 +25 -8
- package/dist/bootstrap.js +8 -1
- package/dist/cli.js +10 -1
- package/dist/core/collaboration-timeline.js +3 -1
- package/dist/core/role-configuration.js +277 -0
- package/dist/domain/schemas.js +9 -0
- package/dist/orchestrator/collaboration-channel.js +11 -3
- package/dist/orchestrator/orchestrator.js +249 -46
- package/dist/tui/App.js +459 -12
- package/dist/tui/AppShell.js +7 -1
- package/dist/tui/FeatureBoardView.js +4 -1
- package/dist/tui/InputBar.js +37 -6
- package/dist/tui/RoleConfigurationView.js +96 -0
- package/dist/tui/StatusDetailView.js +34 -10
- 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);
|
|
@@ -240,7 +264,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
240
264
|
&& (config.workers[selectedTaskSessionDetailWorker.engine]?.interactive.forkArgs.length ?? 0) > 0);
|
|
241
265
|
const featureCanCancel = busy && (selectedBoardFeature?.state === "actor_running"
|
|
242
266
|
|| selectedBoardFeature?.state === "critic_running");
|
|
243
|
-
const featureCanReassign = assignableProviderIds.length >
|
|
267
|
+
const featureCanReassign = assignableProviderIds.length > 0
|
|
244
268
|
&& !busy
|
|
245
269
|
&& canRetryTask
|
|
246
270
|
&& selectedBoardFeature?.state !== "approved";
|
|
@@ -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();
|
|
@@ -1182,6 +1396,24 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1182
1396
|
const pendingAssignment = featureAssignmentPromptRef.current;
|
|
1183
1397
|
if (pendingAssignment) {
|
|
1184
1398
|
for (const featureChunk of featureChunks) {
|
|
1399
|
+
const modelEdit = featureAssignmentPromptRef.current?.modelEdit;
|
|
1400
|
+
if (modelEdit) {
|
|
1401
|
+
if (featureChunk === "\x1b") {
|
|
1402
|
+
updateFeatureAssignmentPrompt({ ...pendingAssignment, modelEdit: undefined });
|
|
1403
|
+
continue;
|
|
1404
|
+
}
|
|
1405
|
+
const update = applyChatInputChunk(modelEdit.value, featureChunk, modelEdit.cursor);
|
|
1406
|
+
if (update.submit !== null) {
|
|
1407
|
+
void reassignSelectedFeatureRef.current(pendingAssignment, modelEdit.role, update.submit);
|
|
1408
|
+
}
|
|
1409
|
+
else {
|
|
1410
|
+
updateFeatureAssignmentPrompt({
|
|
1411
|
+
...pendingAssignment,
|
|
1412
|
+
modelEdit: { ...modelEdit, value: update.value, cursor: update.cursor }
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1185
1417
|
if (featureChunk === "\x1b" || featureChunk === "m" || featureChunk === "M") {
|
|
1186
1418
|
updateFeatureAssignmentPrompt(null);
|
|
1187
1419
|
setFeatureBoardNotice(null);
|
|
@@ -1195,6 +1427,16 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1195
1427
|
void reassignSelectedFeatureRef.current(pendingAssignment, "critic");
|
|
1196
1428
|
return;
|
|
1197
1429
|
}
|
|
1430
|
+
if (featureChunk === "1" || featureChunk === "2") {
|
|
1431
|
+
const feature = collaborationTimelineRef.current?.features.find((item) => item.id === pendingAssignment.featureId);
|
|
1432
|
+
const role = featureChunk === "1" ? "actor" : "critic";
|
|
1433
|
+
const value = role === "actor" ? feature?.actorModel ?? "" : feature?.criticModel ?? "";
|
|
1434
|
+
updateFeatureAssignmentPrompt({
|
|
1435
|
+
...pendingAssignment,
|
|
1436
|
+
modelEdit: { role, value, cursor: Array.from(value).length }
|
|
1437
|
+
});
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1198
1440
|
}
|
|
1199
1441
|
return;
|
|
1200
1442
|
}
|
|
@@ -1254,7 +1496,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1254
1496
|
featureId: feature.id,
|
|
1255
1497
|
title: feature.title
|
|
1256
1498
|
});
|
|
1257
|
-
setFeatureBoardNotice(`Reassign ${feature.title} · A Actor (${feature.actorEngine ?? config.pairing.actor}) · C Critic (${feature.criticEngine ?? config.pairing.critic})`);
|
|
1499
|
+
setFeatureBoardNotice(`Reassign ${feature.title} · A Actor (${featureTargetDisplay(config, feature.actorEngine ?? config.pairing.actor, feature.actorModel)}) · C Critic (${featureTargetDisplay(config, feature.criticEngine ?? config.pairing.critic, feature.criticModel)})`);
|
|
1258
1500
|
return;
|
|
1259
1501
|
}
|
|
1260
1502
|
if (featureChunk === "r" || featureChunk === "R") {
|
|
@@ -1841,6 +2083,15 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1841
2083
|
.join("\n")
|
|
1842
2084
|
};
|
|
1843
2085
|
}
|
|
2086
|
+
if (currentView === "roles") {
|
|
2087
|
+
const roles = roleConfigurationDraftRef.current;
|
|
2088
|
+
return {
|
|
2089
|
+
label: "role configuration",
|
|
2090
|
+
text: roles
|
|
2091
|
+
? CONFIGURABLE_ROLES.map((role) => `${role} · ${roles[role].engine} · ${roles[role].model || "default"}`).join("\n")
|
|
2092
|
+
: ""
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
1844
2095
|
if (currentView === "workers") {
|
|
1845
2096
|
return {
|
|
1846
2097
|
label: "workers",
|
|
@@ -2187,6 +2438,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2187
2438
|
cwd,
|
|
2188
2439
|
taskId: target.taskId,
|
|
2189
2440
|
route: followUpRoute?.route,
|
|
2441
|
+
roleSelection: followUpRoute?.roleSelection,
|
|
2190
2442
|
...callbacks
|
|
2191
2443
|
})
|
|
2192
2444
|
: target.kind === "task-question"
|
|
@@ -2195,6 +2447,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2195
2447
|
cwd,
|
|
2196
2448
|
taskId: target.taskId,
|
|
2197
2449
|
route: followUpRoute?.route,
|
|
2450
|
+
roleSelection: followUpRoute?.roleSelection,
|
|
2198
2451
|
...callbacks
|
|
2199
2452
|
})
|
|
2200
2453
|
: await orchestrator.handleRequest({
|
|
@@ -2209,6 +2462,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2209
2462
|
setCanRetryTask(nextMemory.activeTaskId
|
|
2210
2463
|
? await orchestrator.canRetryTask(nextMemory.activeTaskId)
|
|
2211
2464
|
: false);
|
|
2465
|
+
await refreshRoleConfigurationState(nextMemory.activeTaskId);
|
|
2212
2466
|
setRouteAnnouncement(null);
|
|
2213
2467
|
await appendVisibleMessage({ from: "system", text: result.summary }, nextMemory.activeTaskId ?? undefined);
|
|
2214
2468
|
if (result.mode === "complex" && parseTaskResultSummary(result.summary)) {
|
|
@@ -2221,6 +2475,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2221
2475
|
if (retryTaskId) {
|
|
2222
2476
|
setCanRetryTask(await orchestrator.canRetryTask(retryTaskId));
|
|
2223
2477
|
}
|
|
2478
|
+
await refreshRoleConfigurationState(retryTaskId);
|
|
2224
2479
|
await appendVisibleMessage({
|
|
2225
2480
|
from: "system",
|
|
2226
2481
|
text: isAbortError(error) ? "cancelled · request stopped" : error instanceof Error ? error.message : String(error)
|
|
@@ -2268,6 +2523,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2268
2523
|
setActiveTaskId(taskId);
|
|
2269
2524
|
setActiveMode("complex");
|
|
2270
2525
|
setCanRetryTask(false);
|
|
2526
|
+
await refreshRoleConfigurationState(taskId);
|
|
2271
2527
|
setRouteAnnouncement(null);
|
|
2272
2528
|
await appendVisibleMessage({ from: "system", text: result.summary }, taskId);
|
|
2273
2529
|
if (parseTaskResultSummary(result.summary)) {
|
|
@@ -2277,6 +2533,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2277
2533
|
catch (error) {
|
|
2278
2534
|
setRouteAnnouncement(null);
|
|
2279
2535
|
setCanRetryTask(await orchestrator.canRetryTask(taskId));
|
|
2536
|
+
await refreshRoleConfigurationState(taskId);
|
|
2280
2537
|
await appendVisibleMessage({
|
|
2281
2538
|
from: "system",
|
|
2282
2539
|
text: isAbortError(error) ? "cancelled · retry stopped" : error instanceof Error ? error.message : String(error)
|
|
@@ -2305,6 +2562,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2305
2562
|
return;
|
|
2306
2563
|
}
|
|
2307
2564
|
resetActiveTaskUiForMainConversation();
|
|
2565
|
+
await refreshRoleConfigurationState(null);
|
|
2308
2566
|
await appendVisibleMessage({ from: "system", text: "new conversation · ready" });
|
|
2309
2567
|
}
|
|
2310
2568
|
function resetActiveTaskUiForMainConversation() {
|
|
@@ -2857,7 +3115,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2857
3115
|
featureAssignmentPromptRef.current = next;
|
|
2858
3116
|
setFeatureAssignmentPrompt(next);
|
|
2859
3117
|
}
|
|
2860
|
-
async function reassignSelectedFeature(prompt, role) {
|
|
3118
|
+
async function reassignSelectedFeature(prompt, role, model) {
|
|
2861
3119
|
const feature = collaborationTimelineRef.current?.features.find((item) => item.id === prompt.featureId);
|
|
2862
3120
|
if (!feature) {
|
|
2863
3121
|
setFeatureBoardNotice(`Feature no longer exists: ${prompt.featureId}`);
|
|
@@ -2867,15 +3125,22 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2867
3125
|
const current = role === "actor"
|
|
2868
3126
|
? feature.actorEngine ?? config.pairing.actor
|
|
2869
3127
|
: feature.criticEngine ?? config.pairing.critic;
|
|
2870
|
-
const engine =
|
|
3128
|
+
const engine = model === undefined
|
|
3129
|
+
? nextAssignableFeatureEngine(current, assignableProviderIds)
|
|
3130
|
+
: current;
|
|
2871
3131
|
try {
|
|
2872
|
-
await orchestrator.reassignFeature({
|
|
3132
|
+
const result = await orchestrator.reassignFeature({
|
|
2873
3133
|
taskId: prompt.taskId,
|
|
2874
3134
|
featureId: prompt.featureId,
|
|
2875
3135
|
role,
|
|
2876
|
-
engine
|
|
3136
|
+
engine,
|
|
3137
|
+
...(model === undefined ? {} : { model })
|
|
2877
3138
|
});
|
|
2878
|
-
|
|
3139
|
+
updateFeatureAssignmentPrompt({ ...prompt, modelEdit: undefined });
|
|
3140
|
+
const assignedModel = role === "actor"
|
|
3141
|
+
? result.assignment.actor_model
|
|
3142
|
+
: result.assignment.critic_model;
|
|
3143
|
+
setFeatureBoardNotice(`${role === "actor" ? "Actor" : "Critic"} reassigned to ${featureTargetDisplay(config, engine, assignedModel)} · Ctrl+R resumes the task.`);
|
|
2879
3144
|
await refreshCollaborationTimeline(false);
|
|
2880
3145
|
}
|
|
2881
3146
|
catch (error) {
|
|
@@ -3070,6 +3335,159 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3070
3335
|
setAttachError(null);
|
|
3071
3336
|
setView("workers");
|
|
3072
3337
|
}
|
|
3338
|
+
async function refreshRoleConfigurationState(taskId) {
|
|
3339
|
+
try {
|
|
3340
|
+
const snapshot = typeof orchestrator.roleConfigurationSnapshot === "function"
|
|
3341
|
+
? await orchestrator.roleConfigurationSnapshot(taskId)
|
|
3342
|
+
: fallbackRoleConfigurationSnapshot(config);
|
|
3343
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3344
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3345
|
+
if (viewRef.current === "roles") {
|
|
3346
|
+
const draft = roleConfigurationSelectionForScope(snapshot, roleConfigurationScopeRef.current);
|
|
3347
|
+
roleConfigurationDraftRef.current = draft;
|
|
3348
|
+
setRoleConfigurationDraft(draft);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
catch (error) {
|
|
3352
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
async function openRoleConfiguration() {
|
|
3356
|
+
const currentView = viewRef.current;
|
|
3357
|
+
if (busyRef.current || currentView === "native" || currentView === "workspace") {
|
|
3358
|
+
return;
|
|
3359
|
+
}
|
|
3360
|
+
const returnView = currentView === "roles" ? roleConfigurationReturnViewRef.current : currentView;
|
|
3361
|
+
roleConfigurationReturnViewRef.current = returnView;
|
|
3362
|
+
const scope = roleConfigurationScopeRef.current === "task" && !activeTaskIdRef.current
|
|
3363
|
+
? "next"
|
|
3364
|
+
: roleConfigurationScopeRef.current;
|
|
3365
|
+
roleConfigurationScopeRef.current = scope;
|
|
3366
|
+
setRoleConfigurationScope(scope);
|
|
3367
|
+
roleModelEditRef.current = null;
|
|
3368
|
+
setRoleModelEdit(null);
|
|
3369
|
+
setRoleConfigurationNotice(null);
|
|
3370
|
+
setRoleConfigurationError(null);
|
|
3371
|
+
roleConfigurationLoadingRef.current = true;
|
|
3372
|
+
setRoleConfigurationLoading(true);
|
|
3373
|
+
viewRef.current = "roles";
|
|
3374
|
+
setView("roles");
|
|
3375
|
+
const sequence = roleConfigurationLoadSequenceRef.current + 1;
|
|
3376
|
+
roleConfigurationLoadSequenceRef.current = sequence;
|
|
3377
|
+
try {
|
|
3378
|
+
const snapshot = typeof orchestrator.roleConfigurationSnapshot === "function"
|
|
3379
|
+
? await orchestrator.roleConfigurationSnapshot(activeTaskIdRef.current)
|
|
3380
|
+
: fallbackRoleConfigurationSnapshot(config);
|
|
3381
|
+
if (!mountedRef.current || roleConfigurationLoadSequenceRef.current !== sequence) {
|
|
3382
|
+
return;
|
|
3383
|
+
}
|
|
3384
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3385
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3386
|
+
const draft = roleConfigurationSelectionForScope(snapshot, scope);
|
|
3387
|
+
roleConfigurationDraftRef.current = draft;
|
|
3388
|
+
setRoleConfigurationDraft(draft);
|
|
3389
|
+
}
|
|
3390
|
+
catch (error) {
|
|
3391
|
+
if (mountedRef.current && roleConfigurationLoadSequenceRef.current === sequence) {
|
|
3392
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
finally {
|
|
3396
|
+
if (mountedRef.current && roleConfigurationLoadSequenceRef.current === sequence) {
|
|
3397
|
+
roleConfigurationLoadingRef.current = false;
|
|
3398
|
+
setRoleConfigurationLoading(false);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
async function applyRoleConfiguration() {
|
|
3403
|
+
const draft = roleConfigurationDraftRef.current;
|
|
3404
|
+
if (!draft || roleConfigurationSavingRef.current || roleConfigurationLoadingRef.current) {
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
const scope = roleConfigurationScopeRef.current;
|
|
3408
|
+
const taskId = activeTaskIdRef.current;
|
|
3409
|
+
if (scope === "task" && !taskId) {
|
|
3410
|
+
setRoleConfigurationError("No active Task · choose next request or future requests");
|
|
3411
|
+
return;
|
|
3412
|
+
}
|
|
3413
|
+
roleConfigurationSavingRef.current = true;
|
|
3414
|
+
setRoleConfigurationSaving(true);
|
|
3415
|
+
setRoleConfigurationNotice(null);
|
|
3416
|
+
setRoleConfigurationError(null);
|
|
3417
|
+
try {
|
|
3418
|
+
if (typeof orchestrator.updateRoleConfiguration !== "function") {
|
|
3419
|
+
throw new Error("Role configuration is unavailable in this runtime");
|
|
3420
|
+
}
|
|
3421
|
+
if (typeof orchestrator.validateRoleConfiguration === "function") {
|
|
3422
|
+
const validation = await orchestrator.validateRoleConfiguration(draft);
|
|
3423
|
+
if (!validation.ok) {
|
|
3424
|
+
throw new Error(formatRoleConfigurationPreflightFailure(validation.lines));
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
const snapshot = await orchestrator.updateRoleConfiguration({
|
|
3428
|
+
scope,
|
|
3429
|
+
roles: draft,
|
|
3430
|
+
taskId
|
|
3431
|
+
});
|
|
3432
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3433
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3434
|
+
const saved = roleConfigurationSelectionForScope(snapshot, scope);
|
|
3435
|
+
roleConfigurationDraftRef.current = saved;
|
|
3436
|
+
setRoleConfigurationDraft(saved);
|
|
3437
|
+
setRoleConfigurationNotice(scope === "next"
|
|
3438
|
+
? "Saved · the next request will consume this matrix once"
|
|
3439
|
+
: scope === "task"
|
|
3440
|
+
? "Saved · current Task retries and follow-ups will use this matrix"
|
|
3441
|
+
: "Saved · future requests now use this default matrix");
|
|
3442
|
+
if (scope === "task" && taskId) {
|
|
3443
|
+
void refreshCollaborationTimelineRef.current(false);
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
catch (error) {
|
|
3447
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3448
|
+
}
|
|
3449
|
+
finally {
|
|
3450
|
+
roleConfigurationSavingRef.current = false;
|
|
3451
|
+
setRoleConfigurationSaving(false);
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
async function clearRoleConfiguration() {
|
|
3455
|
+
if (roleConfigurationSavingRef.current || roleConfigurationLoadingRef.current) {
|
|
3456
|
+
return;
|
|
3457
|
+
}
|
|
3458
|
+
const scope = roleConfigurationScopeRef.current;
|
|
3459
|
+
const taskId = activeTaskIdRef.current;
|
|
3460
|
+
if (scope === "task" && !taskId) {
|
|
3461
|
+
setRoleConfigurationError("No active Task · choose next request or future requests");
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
roleConfigurationSavingRef.current = true;
|
|
3465
|
+
setRoleConfigurationSaving(true);
|
|
3466
|
+
setRoleConfigurationNotice(null);
|
|
3467
|
+
setRoleConfigurationError(null);
|
|
3468
|
+
try {
|
|
3469
|
+
if (typeof orchestrator.clearRoleConfiguration !== "function") {
|
|
3470
|
+
throw new Error("Role configuration is unavailable in this runtime");
|
|
3471
|
+
}
|
|
3472
|
+
const snapshot = await orchestrator.clearRoleConfiguration(scope, taskId);
|
|
3473
|
+
roleConfigurationSnapshotRef.current = snapshot;
|
|
3474
|
+
setRoleConfigurationSnapshot(snapshot);
|
|
3475
|
+
const inherited = roleConfigurationSelectionForScope(snapshot, scope);
|
|
3476
|
+
roleConfigurationDraftRef.current = inherited;
|
|
3477
|
+
setRoleConfigurationDraft(inherited);
|
|
3478
|
+
setRoleConfigurationNotice(scope === "future" ? "Reset · future requests inherit config.toml" : "Reset · scope now inherits future defaults");
|
|
3479
|
+
if (scope === "task" && taskId) {
|
|
3480
|
+
void refreshCollaborationTimelineRef.current(false);
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
catch (error) {
|
|
3484
|
+
setRoleConfigurationError(error instanceof Error ? error.message : String(error));
|
|
3485
|
+
}
|
|
3486
|
+
finally {
|
|
3487
|
+
roleConfigurationSavingRef.current = false;
|
|
3488
|
+
setRoleConfigurationSaving(false);
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3073
3491
|
function openWorkspacePicker() {
|
|
3074
3492
|
const currentView = viewRef.current;
|
|
3075
3493
|
if (busyRef.current || !switchWorkspace || currentView === "native" || currentView === "workspace") {
|
|
@@ -3101,7 +3519,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3101
3519
|
if (view === "workspace") {
|
|
3102
3520
|
return (_jsx(Box, { flexDirection: "column", height: terminalSize.rows, children: _jsx(WorkspacePicker, { cwd: cwd, choices: workspaceChoices, terminalHeight: terminalSize.rows, terminalWidth: terminalWidth, onCancel: closeWorkspacePicker, onSelect: (workspace) => void selectWorkspace(workspace) }) }));
|
|
3103
3521
|
}
|
|
3104
|
-
return (_jsx(AppShell, { view: view, cwd: cwd, taskId: activeTaskId, statusText: [visibleTaskStatus, visibleRouteSummary, configChange?.compact].filter(Boolean).join(" | "), contentHeight: contentHeight, showStatusBar: config.ui.showStatusBar, input: _jsx(InputBar, { mode: view === "worker" && workerSearch.open ? "worker-search" : view, ready: inputReady, busy: busy, routeFallback: Boolean(routeFallbackPrompt), collaborationDetail: collaborationDetailOpen, collaborationUnresolved: collaborationUnresolvedOnly, collaborationBack: collaborationReturnViewRef.current, featureCanCancel: featureCanCancel, featureCanPause: featureCanCancel, featureCanReassign: featureCanReassign, featureCancelConfirm: featureCancelPrompt?.action === "cancel", featurePauseConfirm: featureCancelPrompt?.action === "pause", featureAssignment: Boolean(featureAssignmentPrompt), taskSessionAction: sessionCenterMode === "conversations"
|
|
3522
|
+
return (_jsx(AppShell, { view: view, cwd: cwd, taskId: activeTaskId, statusText: [visibleTaskStatus, visibleRouteSummary, configChange?.compact].filter(Boolean).join(" | "), contentHeight: contentHeight, showStatusBar: config.ui.showStatusBar, input: _jsx(InputBar, { mode: view === "worker" && workerSearch.open ? "worker-search" : view, ready: inputReady, busy: busy, routeFallback: Boolean(routeFallbackPrompt), collaborationDetail: collaborationDetailOpen, collaborationUnresolved: collaborationUnresolvedOnly, collaborationBack: collaborationReturnViewRef.current, featureCanCancel: featureCanCancel, featureCanPause: featureCanCancel, featureCanReassign: featureCanReassign, featureCancelConfirm: featureCancelPrompt?.action === "cancel", featurePauseConfirm: featureCancelPrompt?.action === "pause", featureAssignment: Boolean(featureAssignmentPrompt), featureEditingModel: featureAssignmentPrompt?.modelEdit ?? null, taskSessionAction: sessionCenterMode === "conversations"
|
|
3105
3523
|
? mainConversationAction?.type === "rename"
|
|
3106
3524
|
? { type: "rename", value: mainConversationAction.value, cursor: mainConversationAction.cursor }
|
|
3107
3525
|
: mainConversationAction?.type === "delete"
|
|
@@ -3113,11 +3531,11 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3113
3531
|
? { type: "delete", title: taskSessionAction.title }
|
|
3114
3532
|
: null, taskSessionsIncludeArchived: sessionCenterMode === "conversations"
|
|
3115
3533
|
? 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"
|
|
3534
|
+
: 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
3535
|
? workerSearch.query
|
|
3118
|
-
: view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" ? "" : input, cursor: workerSearch.open && view === "worker"
|
|
3536
|
+
: view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" || view === "roles" ? "" : input, cursor: workerSearch.open && view === "worker"
|
|
3119
3537
|
? 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"
|
|
3538
|
+
: 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, roleConfigurationSnapshot: roleConfigurationSnapshot, 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
3539
|
? { type: "rename", title: mainConversationAction.title }
|
|
3122
3540
|
: mainConversationAction?.type === "delete"
|
|
3123
3541
|
? { type: "delete", title: mainConversationAction.title }
|
|
@@ -3154,6 +3572,22 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3154
3572
|
}
|
|
3155
3573
|
} })) }));
|
|
3156
3574
|
}
|
|
3575
|
+
function fallbackRoleConfigurationSnapshot(config) {
|
|
3576
|
+
const roles = configuredRoleSelection(config);
|
|
3577
|
+
return {
|
|
3578
|
+
baseline: roles,
|
|
3579
|
+
future: configuredRoleSelection(config),
|
|
3580
|
+
next: null,
|
|
3581
|
+
task: null,
|
|
3582
|
+
activeTurn: null,
|
|
3583
|
+
providers: workerProviders(config).map(({ id, config: provider }) => ({
|
|
3584
|
+
id,
|
|
3585
|
+
model: provider.model.name,
|
|
3586
|
+
modelProvider: provider.model.provider,
|
|
3587
|
+
assignable: provider.assignable
|
|
3588
|
+
}))
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3157
3591
|
function readTerminalSize() {
|
|
3158
3592
|
return {
|
|
3159
3593
|
columns: Math.max(1, Math.trunc(process.stdout.columns || 120)),
|
|
@@ -3963,6 +4397,10 @@ function sessionDetailWorkerLabel(worker) {
|
|
|
3963
4397
|
function featureEngineDisplay(config, engine) {
|
|
3964
4398
|
return workerProviderLabel(config, engine);
|
|
3965
4399
|
}
|
|
4400
|
+
function featureTargetDisplay(config, engine, model) {
|
|
4401
|
+
const effectiveModel = model?.trim() || config.workers[engine]?.model.name || "default";
|
|
4402
|
+
return `${featureEngineDisplay(config, engine)}/${effectiveModel}`;
|
|
4403
|
+
}
|
|
3966
4404
|
function compactChatText(text, maxLength) {
|
|
3967
4405
|
if (maxLength <= 5) {
|
|
3968
4406
|
return takeChatTextStartByDisplayWidth(text, maxLength);
|
|
@@ -4352,6 +4790,15 @@ function compactNativeAttachRole(label) {
|
|
|
4352
4790
|
function compactNativeSessionForTitle(sessionId, maxLength) {
|
|
4353
4791
|
return compactEndByDisplayWidth(sessionId, Math.min(maxLength, 16));
|
|
4354
4792
|
}
|
|
4793
|
+
export function formatRoleConfigurationPreflightFailure(lines) {
|
|
4794
|
+
const clean = lines
|
|
4795
|
+
.map((line) => line.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim())
|
|
4796
|
+
.filter(Boolean);
|
|
4797
|
+
const actionable = clean.filter((line) => (/\b(?:denied|error|failed|invalid|missing|unavailable|unsupported)\b/i.test(line)
|
|
4798
|
+
|| /\bnot (?:found|supported|available)\b/i.test(line)));
|
|
4799
|
+
const detail = (actionable.length > 0 ? actionable : clean).slice(0, 4);
|
|
4800
|
+
return ["Provider preflight failed", ...detail].join(" · ");
|
|
4801
|
+
}
|
|
4355
4802
|
function isAbortError(error) {
|
|
4356
4803
|
return error instanceof Error && error.name === "AbortError";
|
|
4357
4804
|
}
|