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/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;
|
|
@@ -142,7 +142,10 @@ function featureBoardEvidenceText(timeline, feature, width) {
|
|
|
142
142
|
? `deps ${dependencies.map((item) => safeFeatureBoardText(item.title)).join(", ")}`
|
|
143
143
|
: "independent";
|
|
144
144
|
const assignmentText = feature.actorEngine && feature.criticEngine
|
|
145
|
-
?
|
|
145
|
+
? [
|
|
146
|
+
`actor ${feature.actorEngine}/${feature.actorModel?.trim() || "default"}`,
|
|
147
|
+
`critic ${feature.criticEngine}/${feature.criticModel?.trim() || "default"}`
|
|
148
|
+
].join(" · ")
|
|
146
149
|
: "";
|
|
147
150
|
const evidence = feature.latestFinding
|
|
148
151
|
? `finding · ${safeFeatureBoardText(feature.latestFinding)}`
|
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, featureEditingModel = null, 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,24 @@ 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
|
+
}
|
|
33
|
+
if (mode === "features" && featureEditingModel) {
|
|
34
|
+
const prefix = `${featureEditingModel.role} model > `;
|
|
35
|
+
const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
|
|
36
|
+
const display = chatInputDisplayParts(featureEditingModel.value, featureEditingModel.cursor, valueWidth);
|
|
37
|
+
const textWidth = displayWidth(`${prefix}${display.before}|${display.after}`);
|
|
38
|
+
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 })] }));
|
|
39
|
+
}
|
|
22
40
|
if (mode === "status") {
|
|
23
41
|
const hints = statusDetailInputHints(terminalWidth);
|
|
24
42
|
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 +440,27 @@ function workerInputHints(width) {
|
|
|
422
440
|
}
|
|
423
441
|
function statusDetailInputHints(width) {
|
|
424
442
|
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" },
|
|
443
|
+
{ label: "status", detail: " · ^E roles · ^X diagnostics · ^S/Esc back · ^C exit" },
|
|
444
|
+
{ label: "status", detail: " · ^E roles · ^X diag · ^S/Esc back · ^C exit" },
|
|
427
445
|
{ label: "status", detail: " · ^S back · ^C exit" },
|
|
428
446
|
{ label: "status", detail: " · ^S back" },
|
|
429
447
|
{ label: "status", detail: "" },
|
|
430
448
|
{ label: "st", detail: "" }
|
|
431
449
|
]);
|
|
432
450
|
}
|
|
451
|
+
function roleConfigurationInputHints(width, scope, canApply, saving, hasOverride) {
|
|
452
|
+
const label = saving ? "roles · saving" : `roles · ${scope}`;
|
|
453
|
+
const apply = canApply ? "Enter apply" : "task unavailable";
|
|
454
|
+
const reset = hasOverride ? " · X reset" : "";
|
|
455
|
+
return selectInputHints(width, [
|
|
456
|
+
{ label, detail: ` · Tab scope · Up/Dn role · Left/Right provider · M model · ${apply}${reset} · ^E/Esc back` },
|
|
457
|
+
{ label, detail: ` · Tab scope · Up/Dn · Left/Right provider · M model · ${apply}${reset} · Esc back` },
|
|
458
|
+
{ label, detail: ` · Tab · Up/Dn · Left/Right · M model · ${apply}${reset}` },
|
|
459
|
+
{ label, detail: ` · Tab · arrows · M · ${canApply ? "Enter" : "no task"}` },
|
|
460
|
+
{ label: "roles", detail: " · Tab · arrows · M · Enter" },
|
|
461
|
+
{ label: "roles", detail: "" }
|
|
462
|
+
]);
|
|
463
|
+
}
|
|
433
464
|
function workerOverviewInputHints(width) {
|
|
434
465
|
if (width < 16) {
|
|
435
466
|
return { label: "wrk", detail: "" };
|
|
@@ -457,9 +488,9 @@ function workerOverviewInputHints(width) {
|
|
|
457
488
|
function featureBoardInputHints(width, options) {
|
|
458
489
|
if (options.assignment) {
|
|
459
490
|
return selectInputHints(width, [
|
|
460
|
-
{ label: "assign
|
|
461
|
-
{ label: "assign", detail: " · A
|
|
462
|
-
{ label: "assign", detail: " · A
|
|
491
|
+
{ label: "assign", detail: " · A/C provider · 1/2 model · M/Esc done" },
|
|
492
|
+
{ label: "assign", detail: " · A/C provider · 1/2 model · Esc done" },
|
|
493
|
+
{ label: "assign", detail: " · A/C provider · 1/2 model" },
|
|
463
494
|
{ label: "provider", detail: " · A · C · Esc" },
|
|
464
495
|
{ label: "provider", detail: "" },
|
|
465
496
|
{ label: "M", detail: "" }
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { CONFIGURABLE_ROLES } from "../core/role-configuration.js";
|
|
4
|
+
import { compactEndByDisplayWidth, displayWidth } from "./display-width.js";
|
|
5
|
+
import { roleConfigurationScopeHasOverride, roleConfigurationScopeLabel } from "./role-configuration-state.js";
|
|
6
|
+
import { TUI_THEME } from "./theme.js";
|
|
7
|
+
export function RoleConfigurationView({ height = 20, terminalWidth = process.stdout.columns || 120, ...input }) {
|
|
8
|
+
const viewportHeight = Math.max(1, Math.trunc(height));
|
|
9
|
+
const width = Math.max(1, terminalWidth - 2);
|
|
10
|
+
const lines = roleConfigurationDisplayLines(input, width).slice(0, viewportHeight);
|
|
11
|
+
const blanks = Math.max(0, viewportHeight - lines.length);
|
|
12
|
+
return (_jsxs(Box, { flexDirection: "column", height: viewportHeight, children: [lines.map((line, index) => (_jsx(RoleConfigurationRow, { line: line, width: width }, `${line.text}-${index}`))), Array.from({ length: blanks }, (_, index) => (_jsx(Text, { backgroundColor: TUI_THEME.surface, children: " ".repeat(width) }, `role-config-fill-${index}`)))] }));
|
|
13
|
+
}
|
|
14
|
+
export function roleConfigurationDisplayLines(input, width) {
|
|
15
|
+
const safeWidth = Math.max(1, Math.trunc(width));
|
|
16
|
+
const lines = [
|
|
17
|
+
{ text: "Role & model control", tone: "heading" },
|
|
18
|
+
{
|
|
19
|
+
text: `scope · ${roleConfigurationScopeLabel(input.scope)}`,
|
|
20
|
+
tone: input.scope === "task" && !input.hasActiveTask ? "warning" : "accent"
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
if (input.loading || !input.snapshot || !input.draft) {
|
|
24
|
+
lines.push({ text: "loading role configuration...", tone: "muted" });
|
|
25
|
+
return lines.map((line) => fittedRoleLine(line, safeWidth));
|
|
26
|
+
}
|
|
27
|
+
if (input.scope === "task" && !input.hasActiveTask) {
|
|
28
|
+
lines.push({ text: "No active Task · choose next request or future requests.", tone: "warning" });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
lines.push({
|
|
32
|
+
text: roleConfigurationScopeHasOverride(input.snapshot, input.scope)
|
|
33
|
+
? "saved override · Enter updates it · X resets inheritance"
|
|
34
|
+
: "inheriting defaults · Enter saves this matrix",
|
|
35
|
+
tone: roleConfigurationScopeHasOverride(input.snapshot, input.scope) ? "success" : "muted"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
lines.push({ text: "", tone: "text" });
|
|
39
|
+
for (const [index, role] of CONFIGURABLE_ROLES.entries()) {
|
|
40
|
+
const target = input.draft[role];
|
|
41
|
+
const provider = input.snapshot.providers.find((candidate) => candidate.id === target.engine);
|
|
42
|
+
const remote = provider?.modelProvider.trim();
|
|
43
|
+
const model = target.model.trim() || provider?.model.trim() || "default";
|
|
44
|
+
const prefix = index === input.selectedRoleIndex ? ">" : " ";
|
|
45
|
+
const roleLabel = `${role[0]?.toUpperCase() ?? ""}${role.slice(1)}`.padEnd(7, " ");
|
|
46
|
+
lines.push({
|
|
47
|
+
text: `${prefix} ${roleLabel} ${target.engine} · ${[remote, model].filter(Boolean).join("/")}`,
|
|
48
|
+
tone: index === input.selectedRoleIndex ? "accent" : "text",
|
|
49
|
+
selected: index === input.selectedRoleIndex
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
lines.push({ text: "", tone: "text" });
|
|
53
|
+
const active = input.snapshot.activeTurn;
|
|
54
|
+
if (active) {
|
|
55
|
+
lines.push({
|
|
56
|
+
text: `active turn · ${CONFIGURABLE_ROLES.map((role) => `${role}/${active[role].engine}/${active[role].model || "default"}`).join(" · ")}`,
|
|
57
|
+
tone: "muted"
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (input.saving) {
|
|
61
|
+
lines.push({ text: "saving role configuration...", tone: "warning" });
|
|
62
|
+
}
|
|
63
|
+
else if (input.error?.trim()) {
|
|
64
|
+
lines.push({ text: `error · ${input.error.trim()}`, tone: "danger" });
|
|
65
|
+
}
|
|
66
|
+
else if (input.notice?.trim()) {
|
|
67
|
+
lines.push({ text: input.notice.trim(), tone: "success" });
|
|
68
|
+
}
|
|
69
|
+
return lines.map((line) => fittedRoleLine(line, safeWidth));
|
|
70
|
+
}
|
|
71
|
+
function RoleConfigurationRow({ line, width }) {
|
|
72
|
+
const text = compactEndByDisplayWidth(line.text, width);
|
|
73
|
+
const trailing = Math.max(0, width - displayWidth(text));
|
|
74
|
+
const theme = roleConfigurationLineTheme(line.tone, line.selected);
|
|
75
|
+
return (_jsxs(Text, { children: [_jsx(Text, { ...theme, children: text }), trailing > 0 ? _jsx(Text, { backgroundColor: theme.backgroundColor, children: " ".repeat(trailing) }) : null] }));
|
|
76
|
+
}
|
|
77
|
+
export function roleConfigurationLineTheme(tone, selected = false) {
|
|
78
|
+
return {
|
|
79
|
+
backgroundColor: selected ? TUI_THEME.rail : TUI_THEME.surface,
|
|
80
|
+
color: tone === "heading" || tone === "accent"
|
|
81
|
+
? TUI_THEME.accent
|
|
82
|
+
: tone === "success"
|
|
83
|
+
? TUI_THEME.success
|
|
84
|
+
: tone === "warning"
|
|
85
|
+
? TUI_THEME.warning
|
|
86
|
+
: tone === "danger"
|
|
87
|
+
? TUI_THEME.danger
|
|
88
|
+
: tone === "muted"
|
|
89
|
+
? TUI_THEME.muted
|
|
90
|
+
: TUI_THEME.text,
|
|
91
|
+
...(tone === "heading" || tone === "danger" || selected ? { bold: true } : {})
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function fittedRoleLine(line, width) {
|
|
95
|
+
return { ...line, text: compactEndByDisplayWidth(line.text, width) };
|
|
96
|
+
}
|
|
@@ -24,10 +24,10 @@ export function statusDetailDisplayLines(input, width, height) {
|
|
|
24
24
|
text: fitStatusDetailText(`workspace · ${basename(input.cwd) || input.cwd} · ${input.cwd}`, safeWidth),
|
|
25
25
|
tone: "muted"
|
|
26
26
|
},
|
|
27
|
-
{
|
|
28
|
-
text: fitStatusDetailText(
|
|
29
|
-
tone:
|
|
30
|
-
}
|
|
27
|
+
...roleMatrixDetailLines(input).map((line) => ({
|
|
28
|
+
text: fitStatusDetailText(line.text, safeWidth),
|
|
29
|
+
tone: line.tone
|
|
30
|
+
}))
|
|
31
31
|
];
|
|
32
32
|
if (input.configStatus?.trim()) {
|
|
33
33
|
lines.push({
|
|
@@ -59,15 +59,39 @@ export function statusDetailDisplayLines(input, width, height) {
|
|
|
59
59
|
}
|
|
60
60
|
return lines.slice(0, maxLines);
|
|
61
61
|
}
|
|
62
|
-
function
|
|
62
|
+
function roleMatrixDetail(label, pairing, selection) {
|
|
63
63
|
return [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
label,
|
|
65
|
+
rolePairingEntry("main", selection?.main.engine ?? pairing.main, selection?.main.model),
|
|
66
|
+
rolePairingEntry("judge", selection?.judge.engine ?? pairing.judge, selection?.judge.model),
|
|
67
|
+
rolePairingEntry("actor", selection?.actor.engine ?? pairing.actor, selection?.actor.model),
|
|
68
|
+
rolePairingEntry("critic", selection?.critic.engine ?? pairing.critic, selection?.critic.model)
|
|
69
69
|
].join(" · ");
|
|
70
70
|
}
|
|
71
|
+
function roleMatrixDetailLines(input) {
|
|
72
|
+
const snapshot = input.roleConfigurationSnapshot;
|
|
73
|
+
const lines = [{
|
|
74
|
+
text: roleMatrixDetail("actual roles", input.pairing, input.roleSelection),
|
|
75
|
+
tone: "text"
|
|
76
|
+
}];
|
|
77
|
+
if (!snapshot) {
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
const next = snapshot.next ?? snapshot.task ?? snapshot.future;
|
|
81
|
+
const source = snapshot.next ? "one shot" : snapshot.task ? "task default" : "future default";
|
|
82
|
+
lines.push({
|
|
83
|
+
text: roleMatrixDetail(`next roles (${source})`, input.pairing, next),
|
|
84
|
+
tone: snapshot.next ? "active" : "muted"
|
|
85
|
+
});
|
|
86
|
+
lines.push({
|
|
87
|
+
text: roleMatrixDetail("future roles", input.pairing, snapshot.future),
|
|
88
|
+
tone: "muted"
|
|
89
|
+
});
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
92
|
+
function rolePairingEntry(role, engine, model) {
|
|
93
|
+
return [role, engine, model?.trim()].filter(Boolean).join("/");
|
|
94
|
+
}
|
|
71
95
|
function taskDetail(input) {
|
|
72
96
|
if (!input.taskId) {
|
|
73
97
|
return "task · none";
|
package/dist/tui/keyboard.js
CHANGED
|
@@ -38,6 +38,9 @@ export function isCopyShortcut(input, key) {
|
|
|
38
38
|
export function isDiagnosticsShortcut(input, key) {
|
|
39
39
|
return (key.ctrl === true && input.toLowerCase() === "x") || input === "\u0018";
|
|
40
40
|
}
|
|
41
|
+
export function isRoleConfigurationShortcut(input, key) {
|
|
42
|
+
return (key.ctrl === true && input.toLowerCase() === "e") || input === "\u0005";
|
|
43
|
+
}
|
|
41
44
|
export function workerLogJumpKind(input) {
|
|
42
45
|
if (input === "e" || input === "E") {
|
|
43
46
|
return "error";
|
|
@@ -80,6 +83,14 @@ export function rawPlainArrowDelta(input) {
|
|
|
80
83
|
}
|
|
81
84
|
return delta;
|
|
82
85
|
}
|
|
86
|
+
export function rawHorizontalArrowDelta(input) {
|
|
87
|
+
let delta = 0;
|
|
88
|
+
for (const match of input.matchAll(/\x1b(?:O([CD])|\[[0-9;?]*([CD]))/g)) {
|
|
89
|
+
const direction = match[1] ?? match[2];
|
|
90
|
+
delta += direction === "C" ? 1 : -1;
|
|
91
|
+
}
|
|
92
|
+
return delta;
|
|
93
|
+
}
|
|
83
94
|
export function rawChatScrollArrowDelta(input) {
|
|
84
95
|
const delta = rawPlainArrowDelta(input);
|
|
85
96
|
// Alternate-scroll wheels arrive as arrow bursts; one arrow remains a draft-history keypress.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { CONFIGURABLE_ROLES, cloneRoleSelection } from "../core/role-configuration.js";
|
|
2
|
+
const ROLE_CONFIGURATION_SCOPES = ["next", "task", "future"];
|
|
3
|
+
export function roleConfigurationSelectionForScope(snapshot, scope) {
|
|
4
|
+
if (scope === "future") {
|
|
5
|
+
return cloneRoleSelection(snapshot.future);
|
|
6
|
+
}
|
|
7
|
+
if (scope === "task") {
|
|
8
|
+
return cloneRoleSelection(snapshot.task ?? snapshot.future);
|
|
9
|
+
}
|
|
10
|
+
return cloneRoleSelection(snapshot.next ?? snapshot.future);
|
|
11
|
+
}
|
|
12
|
+
export function roleConfigurationScopeHasOverride(snapshot, scope) {
|
|
13
|
+
if (!snapshot) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
if (scope === "future") {
|
|
17
|
+
return !sameRoleSelection(snapshot.future, snapshot.baseline);
|
|
18
|
+
}
|
|
19
|
+
return scope === "next" ? snapshot.next !== null : snapshot.task !== null;
|
|
20
|
+
}
|
|
21
|
+
export function nextRoleConfigurationScope(scope, delta, hasTask) {
|
|
22
|
+
const available = ROLE_CONFIGURATION_SCOPES.filter((candidate) => candidate !== "task" || hasTask);
|
|
23
|
+
const index = Math.max(0, available.indexOf(scope));
|
|
24
|
+
return available[((index + delta) % available.length + available.length) % available.length] ?? "next";
|
|
25
|
+
}
|
|
26
|
+
export function moveRoleConfigurationSelection(index, delta) {
|
|
27
|
+
const length = CONFIGURABLE_ROLES.length;
|
|
28
|
+
return ((index + delta) % length + length) % length;
|
|
29
|
+
}
|
|
30
|
+
export function selectedConfigurableRole(index) {
|
|
31
|
+
return CONFIGURABLE_ROLES[Math.max(0, Math.min(CONFIGURABLE_ROLES.length - 1, index))] ?? "main";
|
|
32
|
+
}
|
|
33
|
+
export function cycleRoleProvider(roles, role, providers, delta) {
|
|
34
|
+
const currentEngine = roles[role].engine;
|
|
35
|
+
const selectableProviders = providers.filter((provider) => (provider.assignable || provider.id === currentEngine));
|
|
36
|
+
if (selectableProviders.length === 0) {
|
|
37
|
+
return cloneRoleSelection(roles);
|
|
38
|
+
}
|
|
39
|
+
const currentIndex = selectableProviders.findIndex((provider) => provider.id === currentEngine);
|
|
40
|
+
const start = currentIndex >= 0 ? currentIndex : 0;
|
|
41
|
+
const provider = selectableProviders[((start + delta) % selectableProviders.length + selectableProviders.length) % selectableProviders.length] ?? selectableProviders[0];
|
|
42
|
+
if (!provider) {
|
|
43
|
+
return cloneRoleSelection(roles);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
...cloneRoleSelection(roles),
|
|
47
|
+
[role]: {
|
|
48
|
+
engine: provider.id,
|
|
49
|
+
model: provider.model
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function updateRoleModel(roles, role, model) {
|
|
54
|
+
return {
|
|
55
|
+
...cloneRoleSelection(roles),
|
|
56
|
+
[role]: {
|
|
57
|
+
...roles[role],
|
|
58
|
+
model
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function sameRoleSelection(left, right) {
|
|
63
|
+
return CONFIGURABLE_ROLES.every((role) => (left[role].engine === right[role].engine
|
|
64
|
+
&& left[role].model === right[role].model));
|
|
65
|
+
}
|
|
66
|
+
export function roleConfigurationScopeLabel(scope) {
|
|
67
|
+
if (scope === "next") {
|
|
68
|
+
return "next request · one shot";
|
|
69
|
+
}
|
|
70
|
+
if (scope === "task") {
|
|
71
|
+
return "current task · retries and follow-ups";
|
|
72
|
+
}
|
|
73
|
+
return "future requests · persisted default";
|
|
74
|
+
}
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "0.
|
|
1
|
+
export const version = "0.3.1";
|
|
@@ -18,7 +18,11 @@ export async function buildNativeForkLaunch(input) {
|
|
|
18
18
|
async function buildNativeSessionLaunch(input, mode) {
|
|
19
19
|
const workerConfig = workerProvider(input.config, input.worker.engine).config;
|
|
20
20
|
const nativeSession = await readWorkerNativeSession(input.worker, workerConfig.capabilities.profile);
|
|
21
|
-
const modelConfig =
|
|
21
|
+
const modelConfig = {
|
|
22
|
+
...workerConfig.model,
|
|
23
|
+
name: input.worker.runtimeStatus?.model_name?.trim() || workerConfig.model.name,
|
|
24
|
+
provider: input.worker.runtimeStatus?.model_provider?.trim() || workerConfig.model.provider
|
|
25
|
+
};
|
|
22
26
|
const env = modelEnvironment(modelConfig);
|
|
23
27
|
const interactiveArgs = mode === "fork"
|
|
24
28
|
? workerConfig.interactive.forkArgs
|