@aragon-agent/cli 0.6.44 → 0.6.46
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/CHANGELOG.md +44 -0
- package/README.md +120 -2
- package/dist/agent/controller.d.ts +69 -0
- package/dist/agent/controller.js +224 -11
- package/dist/agent/governance-view.d.ts +40 -0
- package/dist/agent/governance-view.js +40 -0
- package/dist/agent/governance-wiring.d.ts +162 -0
- package/dist/agent/governance-wiring.js +470 -0
- package/dist/agent/invocation-card.d.ts +77 -0
- package/dist/agent/invocation-card.js +169 -0
- package/dist/agent/plan-prompt.d.ts +7 -0
- package/dist/agent/plan-prompt.js +2 -1
- package/dist/agent/prompt-options.d.ts +6 -0
- package/dist/agent/queued-messages.d.ts +3 -0
- package/dist/agent/queued-messages.js +4 -1
- package/dist/agent/reducer.d.ts +38 -1
- package/dist/agent/reducer.js +57 -12
- package/dist/agent/system-prompt.d.ts +19 -0
- package/dist/agent/system-prompt.js +9 -1
- package/dist/cli.js +45 -1
- package/dist/commands/builtins.js +22 -4
- package/dist/commands/governance-command.d.ts +35 -0
- package/dist/commands/governance-command.js +304 -0
- package/dist/commands/governance-runs-table.d.ts +40 -0
- package/dist/commands/governance-runs-table.js +130 -0
- package/dist/commands/index-build.d.ts +8 -0
- package/dist/commands/index-build.js +11 -0
- package/dist/commands/registry.d.ts +35 -0
- package/dist/commands/registry.js +20 -7
- package/dist/commands/skills.js +36 -2
- package/dist/commands/subagent-command.js +3 -1
- package/dist/commands/team-command.d.ts +6 -0
- package/dist/commands/team-command.js +40 -3
- package/dist/config/app-paths.d.ts +10 -0
- package/dist/config/app-paths.js +12 -0
- package/dist/config/cli-commands.d.ts +18 -0
- package/dist/config/cli-commands.js +47 -1
- package/dist/config/env.d.ts +2 -1
- package/dist/config/env.js +10 -1
- package/dist/config/governance-config.d.ts +125 -0
- package/dist/config/governance-config.js +164 -0
- package/dist/config/governance-launch.d.ts +71 -0
- package/dist/config/governance-launch.js +58 -0
- package/dist/config/load.d.ts +15 -0
- package/dist/config/load.js +26 -1
- package/dist/config/model-profile-store.d.ts +4 -2
- package/dist/config/schema.d.ts +22 -0
- package/dist/config/schema.js +6 -0
- package/dist/config/store.js +14 -1
- package/dist/config/ui-state.d.ts +11 -0
- package/dist/config/ui-state.js +11 -0
- package/dist/diagnostics/doctor.js +20 -0
- package/dist/governance/card-normalize.d.ts +24 -0
- package/dist/governance/card-normalize.js +76 -0
- package/dist/governance/copy.d.ts +109 -0
- package/dist/governance/copy.js +223 -0
- package/dist/governance/engine/aftermath.d.ts +49 -0
- package/dist/governance/engine/aftermath.js +134 -0
- package/dist/governance/engine/artifact-persist.d.ts +35 -0
- package/dist/governance/engine/artifact-persist.js +86 -0
- package/dist/governance/engine/budget.d.ts +52 -0
- package/dist/governance/engine/budget.js +112 -0
- package/dist/governance/engine/cards.d.ts +18 -0
- package/dist/governance/engine/cards.js +186 -0
- package/dist/governance/engine/digest.d.ts +32 -0
- package/dist/governance/engine/digest.js +63 -0
- package/dist/governance/engine/engine-core.d.ts +219 -0
- package/dist/governance/engine/engine-core.js +562 -0
- package/dist/governance/engine/engine.d.ts +68 -0
- package/dist/governance/engine/engine.js +314 -0
- package/dist/governance/engine/estimate.d.ts +38 -0
- package/dist/governance/engine/estimate.js +46 -0
- package/dist/governance/engine/inspector.d.ts +16 -0
- package/dist/governance/engine/inspector.js +396 -0
- package/dist/governance/engine/integration.d.ts +35 -0
- package/dist/governance/engine/integration.js +220 -0
- package/dist/governance/engine/lifecycle.d.ts +29 -0
- package/dist/governance/engine/lifecycle.js +656 -0
- package/dist/governance/engine/pause.d.ts +56 -0
- package/dist/governance/engine/pause.js +107 -0
- package/dist/governance/engine/phase-members.d.ts +15 -0
- package/dist/governance/engine/phase-members.js +34 -0
- package/dist/governance/engine/phase-planner.d.ts +53 -0
- package/dist/governance/engine/phase-planner.js +282 -0
- package/dist/governance/engine/rebuild.d.ts +27 -0
- package/dist/governance/engine/rebuild.js +57 -0
- package/dist/governance/engine/report.d.ts +66 -0
- package/dist/governance/engine/report.js +220 -0
- package/dist/governance/engine/run-digest.d.ts +20 -0
- package/dist/governance/engine/run-digest.js +42 -0
- package/dist/governance/engine/spec-builder.d.ts +117 -0
- package/dist/governance/engine/spec-builder.js +321 -0
- package/dist/governance/engine/stage-runner.d.ts +22 -0
- package/dist/governance/engine/stage-runner.js +664 -0
- package/dist/governance/engine/start-input.d.ts +19 -0
- package/dist/governance/engine/start-input.js +111 -0
- package/dist/governance/engine/state-machine.d.ts +101 -0
- package/dist/governance/engine/state-machine.js +586 -0
- package/dist/governance/engine/submission-queries.d.ts +42 -0
- package/dist/governance/engine/submission-queries.js +97 -0
- package/dist/governance/engine/submission-render.d.ts +12 -0
- package/dist/governance/engine/submission-render.js +92 -0
- package/dist/governance/engine/submissions.d.ts +27 -0
- package/dist/governance/engine/submissions.js +455 -0
- package/dist/governance/engine/visibility.d.ts +104 -0
- package/dist/governance/engine/visibility.js +223 -0
- package/dist/governance/governed-dispatch.d.ts +19 -0
- package/dist/governance/governed-dispatch.js +17 -0
- package/dist/governance/limits.d.ts +107 -0
- package/dist/governance/limits.js +131 -0
- package/dist/governance/posture-transition.d.ts +38 -0
- package/dist/governance/posture-transition.js +44 -0
- package/dist/governance/posture.d.ts +35 -0
- package/dist/governance/posture.js +59 -0
- package/dist/governance/profiles.d.ts +48 -0
- package/dist/governance/profiles.js +140 -0
- package/dist/governance/prompts/orchestrator-block.d.ts +40 -0
- package/dist/governance/prompts/orchestrator-block.js +128 -0
- package/dist/governance/prompts/plan-addendum.d.ts +22 -0
- package/dist/governance/prompts/plan-addendum.js +60 -0
- package/dist/governance/prompts/refusals.d.ts +48 -0
- package/dist/governance/prompts/refusals.js +82 -0
- package/dist/governance/prompts/role-blocks.d.ts +40 -0
- package/dist/governance/prompts/role-blocks.js +255 -0
- package/dist/governance/report-reader.d.ts +31 -0
- package/dist/governance/report-reader.js +57 -0
- package/dist/governance/roles.d.ts +83 -0
- package/dist/governance/roles.js +142 -0
- package/dist/governance/rules/claims.d.ts +83 -0
- package/dist/governance/rules/claims.js +250 -0
- package/dist/governance/rules/elimination.d.ts +66 -0
- package/dist/governance/rules/elimination.js +105 -0
- package/dist/governance/rules/events-table.d.ts +46 -0
- package/dist/governance/rules/events-table.js +94 -0
- package/dist/governance/rules/failure-analysis.d.ts +28 -0
- package/dist/governance/rules/failure-analysis.js +93 -0
- package/dist/governance/rules/ledger-replay.d.ts +53 -0
- package/dist/governance/rules/ledger-replay.js +53 -0
- package/dist/governance/rules/ledger-state.d.ts +85 -0
- package/dist/governance/rules/ledger-state.js +175 -0
- package/dist/governance/rules/ledger.d.ts +228 -0
- package/dist/governance/rules/ledger.js +560 -0
- package/dist/governance/rules/rca.d.ts +68 -0
- package/dist/governance/rules/rca.js +220 -0
- package/dist/governance/rules/settlement.d.ts +129 -0
- package/dist/governance/rules/settlement.js +363 -0
- package/dist/governance/rules/suspicion.d.ts +66 -0
- package/dist/governance/rules/suspicion.js +169 -0
- package/dist/governance/store/artifacts.d.ts +81 -0
- package/dist/governance/store/artifacts.js +173 -0
- package/dist/governance/store/atomic.d.ts +79 -0
- package/dist/governance/store/atomic.js +119 -0
- package/dist/governance/store/events-log.d.ts +35 -0
- package/dist/governance/store/events-log.js +86 -0
- package/dist/governance/store/run-lock.d.ts +51 -0
- package/dist/governance/store/run-lock.js +122 -0
- package/dist/governance/store/run-store.d.ts +229 -0
- package/dist/governance/store/run-store.js +644 -0
- package/dist/governance/store/scoreboard-export.d.ts +53 -0
- package/dist/governance/store/scoreboard-export.js +65 -0
- package/dist/governance/tools/child-tools.d.ts +31 -0
- package/dist/governance/tools/child-tools.js +65 -0
- package/dist/governance/tools/guard.d.ts +71 -0
- package/dist/governance/tools/guard.js +194 -0
- package/dist/governance/tools/inspector-tools.d.ts +63 -0
- package/dist/governance/tools/inspector-tools.js +424 -0
- package/dist/governance/tools/lead-tools.d.ts +42 -0
- package/dist/governance/tools/lead-tools.js +226 -0
- package/dist/governance/tools/normalize-util.d.ts +101 -0
- package/dist/governance/tools/normalize-util.js +230 -0
- package/dist/governance/tools/scan-incomplete.d.ts +79 -0
- package/dist/governance/tools/scan-incomplete.js +486 -0
- package/dist/governance/tools/shared-tools.d.ts +44 -0
- package/dist/governance/tools/shared-tools.js +125 -0
- package/dist/governance/tools/submit-normalizers.d.ts +28 -0
- package/dist/governance/tools/submit-normalizers.js +474 -0
- package/dist/governance/tools/submit-tools.d.ts +26 -0
- package/dist/governance/tools/submit-tools.js +171 -0
- package/dist/governance/types.d.ts +661 -0
- package/dist/governance/types.js +27 -0
- package/dist/session/persist.js +10 -0
- package/dist/session/validate-session.js +36 -3
- package/dist/team/forced-mode.d.ts +19 -0
- package/dist/team/forced-mode.js +42 -20
- package/dist/team/runtime.d.ts +8 -0
- package/dist/team/runtime.js +9 -0
- package/dist/team/subagent.d.ts +23 -0
- package/dist/team/subagent.js +14 -3
- package/dist/team/types.d.ts +6 -0
- package/dist/tools/index.d.ts +30 -7
- package/dist/tools/index.js +14 -0
- package/dist/tools/plan-tools.d.ts +14 -2
- package/dist/tools/plan-tools.js +5 -2
- package/dist/ui/App.js +170 -31
- package/dist/ui/BottomStatusRow.js +1 -1
- package/dist/ui/Composer.d.ts +7 -1
- package/dist/ui/Composer.js +9 -6
- package/dist/ui/GovernancePanel.d.ts +50 -0
- package/dist/ui/GovernancePanel.js +77 -0
- package/dist/ui/GovernanceRun.d.ts +18 -0
- package/dist/ui/GovernanceRun.js +11 -0
- package/dist/ui/StatusBar.d.ts +15 -2
- package/dist/ui/StatusBar.js +10 -4
- package/dist/ui/Transcript.js +7 -1
- package/dist/ui/entries/GovernanceCard.d.ts +30 -0
- package/dist/ui/entries/GovernanceCard.js +32 -0
- package/dist/ui/entries/InvocationCard.d.ts +20 -0
- package/dist/ui/entries/InvocationCard.js +32 -0
- package/dist/ui/governance-copy.d.ts +69 -0
- package/dist/ui/governance-copy.js +104 -0
- package/dist/ui/governance-status.d.ts +145 -0
- package/dist/ui/governance-status.js +326 -0
- package/dist/ui/interaction-copy.d.ts +4 -0
- package/dist/ui/interaction-copy.js +5 -0
- package/dist/ui/interaction-hints.d.ts +17 -0
- package/dist/ui/interaction-hints.js +14 -2
- package/dist/ui/layout/governance-board.d.ts +121 -0
- package/dist/ui/layout/governance-board.js +382 -0
- package/dist/ui/layout/governance-card.d.ts +53 -0
- package/dist/ui/layout/governance-card.js +134 -0
- package/dist/ui/layout/governance-chip.d.ts +38 -0
- package/dist/ui/layout/governance-chip.js +64 -0
- package/dist/ui/layout/governance-panel.d.ts +85 -0
- package/dist/ui/layout/governance-panel.js +358 -0
- package/dist/ui/layout/governance-roster.d.ts +65 -0
- package/dist/ui/layout/governance-roster.js +275 -0
- package/dist/ui/layout/invocation-card.d.ts +41 -0
- package/dist/ui/layout/invocation-card.js +91 -0
- package/dist/ui/layout/status-detail-layout.js +3 -3
- package/dist/ui/layout/status-layout.d.ts +5 -2
- package/dist/ui/layout/status-layout.js +10 -6
- package/dist/ui/layout/virtual-window.js +22 -1
- package/dist/ui/overlays/GovernanceBoardOverlay.d.ts +41 -0
- package/dist/ui/overlays/GovernanceBoardOverlay.js +76 -0
- package/dist/ui/overlays/GovernanceSettingsPage.d.ts +48 -0
- package/dist/ui/overlays/GovernanceSettingsPage.js +93 -0
- package/dist/ui/overlays/HelpOverlay.js +4 -3
- package/dist/ui/overlays/QuestionOverlay.d.ts +9 -1
- package/dist/ui/overlays/QuestionOverlay.js +4 -2
- package/dist/ui/overlays/SettingsScreen.d.ts +7 -0
- package/dist/ui/overlays/SettingsScreen.js +21 -1
- package/dist/ui/overlays/governance-settings.d.ts +79 -0
- package/dist/ui/overlays/governance-settings.js +140 -0
- package/dist/ui/overlays/model-profile-state.d.ts +6 -1
- package/dist/ui/overlays/model-profile-state.js +10 -0
- package/dist/ui/palettes.d.ts +17 -0
- package/dist/ui/palettes.js +9 -0
- package/dist/ui/scrollback/live-preview.js +2 -0
- package/dist/ui/scrollback/settled-boundary.js +3 -0
- package/dist/ui/shortcuts.js +2 -2
- package/dist/ui/theme.d.ts +7 -0
- package/dist/ui/theme.js +1 -0
- package/dist/ui/transcript-text.js +13 -1
- package/dist/ui/use-wheel-routing.js +3 -1
- package/package.json +2 -2
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* GovernanceBoardOverlay - the read-only `/gov board` overview
|
|
4
|
+
* (governance-mode section 10.5).
|
|
5
|
+
*
|
|
6
|
+
* ASCII ONLY: every symbol comes through `glyphs`.
|
|
7
|
+
*
|
|
8
|
+
* READ-ONLY IS THE SAFETY BOUNDARY, as for the Agents board: nothing here scores, rules
|
|
9
|
+
* or resolves anything. The only side effect is `e`, which asks the owner to copy the
|
|
10
|
+
* `board.md` path.
|
|
11
|
+
*
|
|
12
|
+
* `OverlayFrame` SELF-MANAGED MODE, with this file owning the window. The tab strip must
|
|
13
|
+
* stay put while the rows scroll, which the frame's controlled mode (it windows EVERY row
|
|
14
|
+
* it is given) cannot do; so the body is "one strip row + a window of rows" and the
|
|
15
|
+
* windowing is `sliceWindow` through `boardWindow`, the arithmetic the frame itself uses.
|
|
16
|
+
* `PgUp` / `PgDn` / the wheel arrive through `App`'s `controlled` overlay list as
|
|
17
|
+
* `scrollOffset`; Up/Down and Left/Right are this component's own keys, the same split the
|
|
18
|
+
* Agents board makes.
|
|
19
|
+
*
|
|
20
|
+
* NO `ink-spinner` and no motion prop (spinner-census): this is a reading surface.
|
|
21
|
+
*/
|
|
22
|
+
import { useEffect, useRef, useState } from 'react';
|
|
23
|
+
import { Box, Text, useInput } from 'ink';
|
|
24
|
+
import { pickGlyphs } from '../glyphs.js';
|
|
25
|
+
import { OverlayFrame, overlayBodyRows } from '../layout/OverlayFrame.js';
|
|
26
|
+
import { boardBodyRows, boardHint, boardPosition, boardWindow, buildGovernanceBoardLayout, stepTab, } from '../layout/governance-board.js';
|
|
27
|
+
import { cellWidth } from '../layout/cells.js';
|
|
28
|
+
import { GovernanceLine } from '../GovernanceRun.js';
|
|
29
|
+
import { BOARD_COPIED, BOARD_NO_PATH, BOARD_TITLE } from '../governance-copy.js';
|
|
30
|
+
/** `OverlayFrame`'s own breakpoint, mirrored so the chrome estimate cannot drift. */
|
|
31
|
+
const FRAME_BORDERLESS_COLS = 48;
|
|
32
|
+
export function GovernanceBoardOverlay({ data, cols, maxRows, scrollOffset, onScrollClamp, isActive, theme, caps, onCopyPath, onNotice, initialTab = 'roster', }) {
|
|
33
|
+
const glyphs = pickGlyphs(caps);
|
|
34
|
+
const [tab, setTab] = useState(initialTab);
|
|
35
|
+
// The frame spends 4 of `cols` on its border + padding (2 in the narrow rail form), so a
|
|
36
|
+
// board planned against the frame width would lose its last four columns to truncation.
|
|
37
|
+
const contentCols = Math.max(1, cols - (cols >= FRAME_BORDERLESS_COLS ? 4 : 2));
|
|
38
|
+
const layout = buildGovernanceBoardLayout({ cols: contentCols, tab, data, glyphs });
|
|
39
|
+
const height = boardBodyRows(overlayBodyRows(maxRows, cols));
|
|
40
|
+
const window = boardWindow(layout.lines.length, scrollOffset, height);
|
|
41
|
+
const position = boardPosition(window, glyphs);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (window.clamped !== scrollOffset)
|
|
44
|
+
onScrollClamp(window.clamped);
|
|
45
|
+
}, [window.clamped, scrollOffset, onScrollClamp]);
|
|
46
|
+
// The offset the NEXT key press starts from. A held Down key delivers several presses
|
|
47
|
+
// before React re-renders; stepping from the rendered value would turn three presses
|
|
48
|
+
// into one row of movement.
|
|
49
|
+
const offsetRef = useRef(window.clamped);
|
|
50
|
+
offsetRef.current = window.clamped;
|
|
51
|
+
useInput((input, key) => {
|
|
52
|
+
if (!isActive)
|
|
53
|
+
return;
|
|
54
|
+
if (key.leftArrow || key.rightArrow) {
|
|
55
|
+
setTab((current) => stepTab(current, key.rightArrow ? 1 : -1));
|
|
56
|
+
offsetRef.current = 0;
|
|
57
|
+
onScrollClamp(0);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (key.upArrow || key.downArrow) {
|
|
61
|
+
offsetRef.current = Math.max(0, offsetRef.current + (key.upArrow ? -1 : 1));
|
|
62
|
+
onScrollClamp(offsetRef.current);
|
|
63
|
+
}
|
|
64
|
+
else if (input === 'e' || input === 'E') {
|
|
65
|
+
const path = data?.boardPath ?? null;
|
|
66
|
+
if (path === null || onCopyPath === undefined) {
|
|
67
|
+
onNotice?.('warn', BOARD_NO_PATH);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
onCopyPath(path);
|
|
71
|
+
onNotice?.('info', BOARD_COPIED);
|
|
72
|
+
}
|
|
73
|
+
}, { isActive });
|
|
74
|
+
const visible = layout.lines.slice(window.start, window.start + window.visible);
|
|
75
|
+
return (_jsxs(OverlayFrame, { title: BOARD_TITLE, hint: boardHint(contentCols, glyphs), maxRows: maxRows, cols: cols, theme: theme, caps: caps, children: [_jsxs(Box, { flexDirection: "row", height: 1, flexShrink: 0, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: _jsx(GovernanceLine, { segments: layout.tabStrip.segments, theme: theme }) }), position !== '' && (_jsx(Box, { width: cellWidth(position) + 1, flexShrink: 0, justifyContent: "flex-end", children: _jsx(Text, { color: theme.muted, children: position }) }))] }), visible.map((line) => (_jsx(GovernanceLine, { segments: line.segments, theme: theme }, line.key)))] }));
|
|
76
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings > Governance: eleven arrows-adjustable rows (governance-mode-v1-1 5, closing
|
|
3
|
+
* governance-mode IF-13).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY - `src/ui/**` is inside `glyphs.test.ts::inScope`.
|
|
6
|
+
*
|
|
7
|
+
* THIS IS `TeamFanoutPage` WITH A DIFFERENT TABLE, ON PURPOSE. Every defence that page
|
|
8
|
+
* carries was paid for by a real defect, so each one is inherited verbatim:
|
|
9
|
+
*
|
|
10
|
+
* - THE PAGE OWNS ONE PIECE OF STATE, its cursor. Every value comes from `props.view.rows`,
|
|
11
|
+
* which `SettingsScreen` derives from the reducer; a second copy of the draft here could
|
|
12
|
+
* disagree with the root page about what Ctrl+S is about to save.
|
|
13
|
+
* - `Enter` TOGGLES THE SWITCH ROWS AND DOES NOTHING ON A NUMBER (R-P1-12). A stray Return
|
|
14
|
+
* that silently changed a number is indistinguishable from a bug, and the arrow keys are
|
|
15
|
+
* already the obvious control there.
|
|
16
|
+
* - IT DOES NOT LISTEN FOR Ctrl+S, AND THE FOOTER SAYS SO (R-P1-13). `useRootSettingsInput`
|
|
17
|
+
* gates its `useInput` on `state.page === 'root'`, so a subpage has no save listener at
|
|
18
|
+
* all; a footer promising "Ctrl+S saves here" would be a lie the user acts on.
|
|
19
|
+
*
|
|
20
|
+
* THE RESTART NOTE IS THE ONE THING THIS PAGE ADDS. `enabled` is a REGISTRATION key: the tool
|
|
21
|
+
* array is fixed when the session is built, so flipping it here changes the file and the
|
|
22
|
+
* next launch, not this session. The note sits under that row so the consequence is read
|
|
23
|
+
* where the choice is made.
|
|
24
|
+
*/
|
|
25
|
+
import React from 'react';
|
|
26
|
+
import { type ProfileFrameProps } from './ModelProfilePicker.js';
|
|
27
|
+
import type { GovernanceSettingKey, GovernanceSettingRow } from './governance-settings.js';
|
|
28
|
+
/** What the page may ask the screen to change. */
|
|
29
|
+
export interface GovernanceSettingsAction {
|
|
30
|
+
type: 'governance-step';
|
|
31
|
+
key: GovernanceSettingKey;
|
|
32
|
+
delta: -1 | 1;
|
|
33
|
+
}
|
|
34
|
+
export interface GovernanceSettingsView {
|
|
35
|
+
/** Exactly eleven, in the fixed order the cursor indexes. */
|
|
36
|
+
rows: GovernanceSettingRow[];
|
|
37
|
+
error?: string;
|
|
38
|
+
/** Shown under the first row; `null` when the draft agrees with this session. */
|
|
39
|
+
restartNote: string | null;
|
|
40
|
+
}
|
|
41
|
+
interface GovernanceSettingsPageProps extends ProfileFrameProps {
|
|
42
|
+
view: GovernanceSettingsView;
|
|
43
|
+
dispatch: (action: GovernanceSettingsAction) => void;
|
|
44
|
+
/** Esc: back to the settings root. The draft is NOT discarded here. */
|
|
45
|
+
onBack: () => void;
|
|
46
|
+
}
|
|
47
|
+
export declare function GovernanceSettingsPage(props: GovernanceSettingsPageProps): React.ReactElement;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Settings > Governance: eleven arrows-adjustable rows (governance-mode-v1-1 5, closing
|
|
4
|
+
* governance-mode IF-13).
|
|
5
|
+
*
|
|
6
|
+
* ASCII ONLY - `src/ui/**` is inside `glyphs.test.ts::inScope`.
|
|
7
|
+
*
|
|
8
|
+
* THIS IS `TeamFanoutPage` WITH A DIFFERENT TABLE, ON PURPOSE. Every defence that page
|
|
9
|
+
* carries was paid for by a real defect, so each one is inherited verbatim:
|
|
10
|
+
*
|
|
11
|
+
* - THE PAGE OWNS ONE PIECE OF STATE, its cursor. Every value comes from `props.view.rows`,
|
|
12
|
+
* which `SettingsScreen` derives from the reducer; a second copy of the draft here could
|
|
13
|
+
* disagree with the root page about what Ctrl+S is about to save.
|
|
14
|
+
* - `Enter` TOGGLES THE SWITCH ROWS AND DOES NOTHING ON A NUMBER (R-P1-12). A stray Return
|
|
15
|
+
* that silently changed a number is indistinguishable from a bug, and the arrow keys are
|
|
16
|
+
* already the obvious control there.
|
|
17
|
+
* - IT DOES NOT LISTEN FOR Ctrl+S, AND THE FOOTER SAYS SO (R-P1-13). `useRootSettingsInput`
|
|
18
|
+
* gates its `useInput` on `state.page === 'root'`, so a subpage has no save listener at
|
|
19
|
+
* all; a footer promising "Ctrl+S saves here" would be a lie the user acts on.
|
|
20
|
+
*
|
|
21
|
+
* THE RESTART NOTE IS THE ONE THING THIS PAGE ADDS. `enabled` is a REGISTRATION key: the tool
|
|
22
|
+
* array is fixed when the session is built, so flipping it here changes the file and the
|
|
23
|
+
* next launch, not this session. The note sits under that row so the consequence is read
|
|
24
|
+
* where the choice is made.
|
|
25
|
+
*/
|
|
26
|
+
import { useState } from 'react';
|
|
27
|
+
import { Text, useInput } from 'ink';
|
|
28
|
+
import { pickGlyphs } from '../glyphs.js';
|
|
29
|
+
import { OverlayFrame } from '../layout/OverlayFrame.js';
|
|
30
|
+
import { clipProfileText, useProfileScroll } from './ModelProfilePicker.js';
|
|
31
|
+
/** Matches the root page's `padEnd(18)` rows so the value column lines up (measured). */
|
|
32
|
+
const LABEL_PAD = 18;
|
|
33
|
+
/** The rows `Enter` flips, and nothing else (R-P1-12). */
|
|
34
|
+
const TOGGLE_KEYS = new Set([
|
|
35
|
+
'enabled',
|
|
36
|
+
'shiftTab',
|
|
37
|
+
'panel',
|
|
38
|
+
'confirmCharter',
|
|
39
|
+
]);
|
|
40
|
+
export function GovernanceSettingsPage(props) {
|
|
41
|
+
const [index, setIndex] = useState(0);
|
|
42
|
+
const rows = props.view.rows;
|
|
43
|
+
const count = Math.max(1, rows.length);
|
|
44
|
+
const cursor = Math.min(index, count - 1);
|
|
45
|
+
const focused = rows[cursor];
|
|
46
|
+
const note = props.view.restartNote;
|
|
47
|
+
// The note is one extra LINE after row 0, so every later row sits one line lower.
|
|
48
|
+
const offset = useProfileScroll(props, cursor + (note !== null && cursor > 0 ? 1 : 0));
|
|
49
|
+
const nudge = (delta) => {
|
|
50
|
+
if (focused)
|
|
51
|
+
props.dispatch({ type: 'governance-step', key: focused.key, delta });
|
|
52
|
+
};
|
|
53
|
+
const handleKey = (key) => {
|
|
54
|
+
if (key.escape) {
|
|
55
|
+
props.onBack();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (key.upArrow || (key.tab && key.shift)) {
|
|
59
|
+
setIndex((cursor - 1 + count) % count);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (key.downArrow || key.tab) {
|
|
63
|
+
setIndex((cursor + 1) % count);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (key.leftArrow || key.rightArrow) {
|
|
67
|
+
nudge(key.leftArrow ? -1 : 1);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
// `Enter` toggles the switch rows and does NOTHING on a numeric or choice row.
|
|
71
|
+
if (key.return && focused && TOGGLE_KEYS.has(focused.key))
|
|
72
|
+
nudge(1);
|
|
73
|
+
};
|
|
74
|
+
useInput((_input, key) => {
|
|
75
|
+
if (props.isActive === false)
|
|
76
|
+
return;
|
|
77
|
+
handleKey(key);
|
|
78
|
+
});
|
|
79
|
+
const glyphs = pickGlyphs(props.caps);
|
|
80
|
+
const body = [];
|
|
81
|
+
rows.forEach((row, at) => {
|
|
82
|
+
const line = `${row.label.padEnd(LABEL_PAD)}${row.value}`;
|
|
83
|
+
body.push(_jsx(Text, { wrap: "truncate", color: at === cursor ? props.theme.accent : props.theme.muted, children: `${at === cursor ? glyphs.caret : ' '} ${clipProfileText(line, props.cols - 8)}` }, row.key));
|
|
84
|
+
if (at === 0 && note !== null) {
|
|
85
|
+
body.push(_jsx(Text, { wrap: "truncate", color: props.theme.muted, children: ` ${clipProfileText(note, props.cols - 8)}` }, "restart-note"));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
// The hint explains the FOCUSED row: the range and the reason are the point of the page.
|
|
89
|
+
if (focused) {
|
|
90
|
+
body.push(_jsx(Text, { wrap: "truncate", color: props.theme.muted, children: clipProfileText(focused.hint, props.cols - 8) }, "hint"));
|
|
91
|
+
}
|
|
92
|
+
return _jsx(OverlayFrame, { ...props, rows: body, scrollOffset: offset, title: props.view.error ?? 'Governance', hint: "Arrows adjust. Esc goes back; Ctrl+S on the main page saves." });
|
|
93
|
+
}
|
|
@@ -47,7 +47,7 @@ export function allKeyRows(times) {
|
|
|
47
47
|
// product's word for transcript line offset (the header chip and the row below),
|
|
48
48
|
// so abbreviating the mode fallback would put two meanings of `^` on one screen
|
|
49
49
|
// (quick-prompt-library D-1 / P1-7).
|
|
50
|
-
['Shift+Tab / Ctrl+N', '
|
|
50
|
+
['Shift+Tab / Ctrl+N', 'Cycle BUILD / PLAN / GOVERN / PLAN+GOVERN (/plan and /gov pick one)'],
|
|
51
51
|
['Ctrl+P', 'Quick prompt library: manage, create, reorder, pick'],
|
|
52
52
|
// The ladder clears a draft first (ctrl-c-clear-draft-exit):
|
|
53
53
|
// with a draft x2 clears the input and x3 exits; an empty input
|
|
@@ -117,7 +117,8 @@ const COMMANDS = [
|
|
|
117
117
|
['/task refresh', 'Regenerate the header task summary from the last prompt'],
|
|
118
118
|
['/model', 'Open the model picker'],
|
|
119
119
|
['/settings', 'Open the settings screen'],
|
|
120
|
-
['/plan [on|off|status]', 'Toggle plan mode (
|
|
120
|
+
['/plan [on|off|status]', 'Toggle plan mode (leaves GOVERN)'],
|
|
121
|
+
['/gov [on|plan|off|board|status]', 'Governed team: enter, leave, the board, the run status'],
|
|
121
122
|
['/theme <name>', 'auto | warm | cool | light'],
|
|
122
123
|
['/thinking <level>', 'Set thinking level'],
|
|
123
124
|
['/max-tokens [n|auto]', 'Output token cap; no argument reports the effective one'],
|
|
@@ -165,7 +166,7 @@ const SKILL_ROWS = [
|
|
|
165
166
|
['skill / skill_install', 'Tools the model uses to load and install skills'],
|
|
166
167
|
];
|
|
167
168
|
const PLAN_ROWS = [
|
|
168
|
-
['Shift+Tab / /plan', 'Switch
|
|
169
|
+
['Shift+Tab / /plan', 'Switch BUILD (do it now) and PLAN (research first); Shift+Tab also cycles GOVERN'],
|
|
169
170
|
// A ROW OF ITS OWN, not a third name on the line above: the reader who needs
|
|
170
171
|
// this one is the reader whose `Shift+Tab` does nothing, and what they need is
|
|
171
172
|
// the reason, not another synonym. Listing the key in both places instead just
|
|
@@ -26,6 +26,14 @@ interface QuestionOverlayProps {
|
|
|
26
26
|
theme: Theme;
|
|
27
27
|
caps: TermCapabilities;
|
|
28
28
|
onSubmit: (answers: Answer[]) => void;
|
|
29
|
+
/**
|
|
30
|
+
* PLAN+GOVERN interview progress (governance-mode 9 / 10.6). Present only in that
|
|
31
|
+
* posture; absent leaves the title byte-identical to the pre-governance overlay.
|
|
32
|
+
*/
|
|
33
|
+
interview?: {
|
|
34
|
+
round: number;
|
|
35
|
+
required: number;
|
|
36
|
+
};
|
|
29
37
|
}
|
|
30
|
-
export declare function QuestionOverlay({ isActive, questions, maxRows, cols, theme, caps, onSubmit, }: QuestionOverlayProps): React.ReactElement;
|
|
38
|
+
export declare function QuestionOverlay({ isActive, questions, maxRows, cols, theme, caps, onSubmit, interview, }: QuestionOverlayProps): React.ReactElement;
|
|
31
39
|
export {};
|
|
@@ -22,6 +22,7 @@ import { wrapToRows } from '../layout/wrap-rows.js';
|
|
|
22
22
|
import { OverlayFrame, overlayBodyRows } from '../layout/OverlayFrame.js';
|
|
23
23
|
import { stripPasteFrames } from '../paste-frames.js';
|
|
24
24
|
import { stripEnterFrames } from '../enter-frames.js';
|
|
25
|
+
import { interviewChipText } from '../governance-status.js';
|
|
25
26
|
/** Which option index each question starts on: the recommended one. */
|
|
26
27
|
function initialCursors(questions) {
|
|
27
28
|
return questions.map((q) => Math.max(0, q.options.findIndex((o) => o.recommended)));
|
|
@@ -32,7 +33,7 @@ function initialSelections(questions) {
|
|
|
32
33
|
return new Set(recommended >= 0 ? [recommended] : [0]);
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
|
-
export function QuestionOverlay({ isActive = true, questions, maxRows, cols, theme, caps, onSubmit, }) {
|
|
36
|
+
export function QuestionOverlay({ isActive = true, questions, maxRows, cols, theme, caps, onSubmit, interview, }) {
|
|
36
37
|
const glyphs = pickGlyphs(caps);
|
|
37
38
|
const [step, setStep] = useState(0);
|
|
38
39
|
const [phase, setPhase] = useState('choosing');
|
|
@@ -215,6 +216,7 @@ export function QuestionOverlay({ isActive = true, questions, maxRows, cols, the
|
|
|
215
216
|
});
|
|
216
217
|
const title = phase === 'review' ? 'Review your answers' : 'Clarify the request';
|
|
217
218
|
const position = phase === 'review' ? '' : ` ${step + 1}/${questions.length}`;
|
|
219
|
+
const interviewChip = interview ? ` ${interviewChipText(interview.round, interview.required)}` : '';
|
|
218
220
|
const hint = phase === 'typingOther'
|
|
219
221
|
? `type your answer ${glyphs.midDot} ${glyphs.enterKey} commit ${glyphs.midDot} ${glyphs.arrowLeft} back ${glyphs.midDot} esc cancel`
|
|
220
222
|
: phase === 'review'
|
|
@@ -252,5 +254,5 @@ export function QuestionOverlay({ isActive = true, questions, maxRows, cols, the
|
|
|
252
254
|
const shortHint = phase === 'typingOther' ? 'Enter commit | Left back | Esc cancel'
|
|
253
255
|
: phase === 'review' ? 'Enter submit | Left back | Esc cancel'
|
|
254
256
|
: '^v choose Space select Enter next Esc';
|
|
255
|
-
return _jsx(OverlayFrame, { title: `${title}${position}`, hint: cols <= 48 ? shortHint : hint, maxRows: maxRows, cols: cols, theme: theme, caps: caps, rows: rowTexts.map((line, index) => _jsx(Text, { wrap: "truncate", children: line || ' ' }, index)), scrollOffset: offset, onScrollClamp: setOffset });
|
|
257
|
+
return _jsx(OverlayFrame, { title: `${title}${position}${interviewChip}`, hint: cols <= 48 ? shortHint : hint, maxRows: maxRows, cols: cols, theme: theme, caps: caps, rows: rowTexts.map((line, index) => _jsx(Text, { wrap: "truncate", children: line || ' ' }, index)), scrollOffset: offset, onScrollClamp: setOffset });
|
|
256
258
|
}
|
|
@@ -146,6 +146,13 @@ interface SettingsScreenProps {
|
|
|
146
146
|
* literal, and absent honestly means "unknown, so do not disclose".
|
|
147
147
|
*/
|
|
148
148
|
teamRegistered?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Whether the governance tools were registered when this session started
|
|
151
|
+
* (governance-mode-v1-1 5.2). Same contract as `teamRegistered`: a CONSTRUCTION-TIME
|
|
152
|
+
* snapshot, OPTIONAL, and `undefined` means "not disclosed" so no note is shown. It is
|
|
153
|
+
* what lets the page say `applies next launch` when the draft's `enabled` disagrees.
|
|
154
|
+
*/
|
|
155
|
+
governanceRegistered?: boolean;
|
|
149
156
|
}
|
|
150
157
|
/**
|
|
151
158
|
* The derived, non-editable line under the five fast rows — the sibling of
|
|
@@ -29,6 +29,8 @@ import { KitEditor } from './KitEditor.js';
|
|
|
29
29
|
import { KitManager } from './KitManager.js';
|
|
30
30
|
import { TeamFanoutPage } from './TeamFanoutPage.js';
|
|
31
31
|
import { fanoutRows, teamFanoutValues, teamSummary } from './team-fanout-state.js';
|
|
32
|
+
import { GovernanceSettingsPage } from './GovernanceSettingsPage.js';
|
|
33
|
+
import { draftGovernance, governanceRestartNote, governanceSettingRows, governanceSummary, } from './governance-settings.js';
|
|
32
34
|
import { heldSlots, kitManagerRows, kitPackRow, kitReducer } from './kit-settings-state.js';
|
|
33
35
|
/**
|
|
34
36
|
* THE ROOT PAGE'S ROW INDEXES, AS NAMES (subagent-model V-5).
|
|
@@ -42,7 +44,7 @@ import { heldSlots, kitManagerRows, kitPackRow, kitReducer } from './kit-setting
|
|
|
42
44
|
* fifth of these and the "five offsets agreed by accident" defect does not need
|
|
43
45
|
* a sequel. Order here IS the render order.
|
|
44
46
|
*/
|
|
45
|
-
const ROOT_ROWS = ['main', 'fast', 'subagent', 'team', 'kit', 'manage'];
|
|
47
|
+
const ROOT_ROWS = ['main', 'fast', 'subagent', 'team', 'governance', 'kit', 'manage'];
|
|
46
48
|
const ROOT_MAIN = ROOT_ROWS.indexOf('main');
|
|
47
49
|
const ROOT_FAST = ROOT_ROWS.indexOf('fast');
|
|
48
50
|
const ROOT_SUBAGENT = ROOT_ROWS.indexOf('subagent');
|
|
@@ -51,6 +53,9 @@ const ROOT_SUBAGENT = ROOT_ROWS.indexOf('subagent');
|
|
|
51
53
|
// one decision, and splitting them across the page would make the pair harder to
|
|
52
54
|
// keep consistent than the pair of numbers inside it.
|
|
53
55
|
const ROOT_TEAM = ROOT_ROWS.indexOf('team');
|
|
56
|
+
// Governance (governance-mode-v1-1 5.2) sits right under Team: both decide the SHAPE of the
|
|
57
|
+
// team, and the row is an index into this list like every other, never a typed number.
|
|
58
|
+
const ROOT_GOVERNANCE = ROOT_ROWS.indexOf('governance');
|
|
54
59
|
const ROOT_KIT = ROOT_ROWS.indexOf('kit');
|
|
55
60
|
const ROOT_MANAGE = ROOT_ROWS.indexOf('manage');
|
|
56
61
|
const ROOT_FIRST_FIELD = ROOT_ROWS.length;
|
|
@@ -693,6 +698,12 @@ function activateRootSetting(props, view) {
|
|
|
693
698
|
// `team-fanout-page.test.tsx` asserts that Enter on this row saves nothing.
|
|
694
699
|
dispatch({ type: 'page', page: 'team' });
|
|
695
700
|
}
|
|
701
|
+
else if (index === ROOT_GOVERNANCE) {
|
|
702
|
+
// SAME RULE AS THE TEAM BRANCH ABOVE, and the same cost of getting it wrong: this MUST
|
|
703
|
+
// stay above the fallback `else`, which is `saveProfileSettings`. A misplaced branch
|
|
704
|
+
// compiles and silently commits the whole draft when Enter opens the page.
|
|
705
|
+
dispatch({ type: 'page', page: 'governance' });
|
|
706
|
+
}
|
|
696
707
|
else if (index === ROOT_KIT) {
|
|
697
708
|
// The fifth root row opens the manager, not a picker: kits are data the user
|
|
698
709
|
// owns here, and `ctrl+K` is where they are CONSUMED (agent-model-kits §4.5).
|
|
@@ -858,6 +869,14 @@ function renderProfileSubpage(props, view) {
|
|
|
858
869
|
rows: fanoutRows(teamFanoutValues(state.draft), props.teamRegistered),
|
|
859
870
|
error: state.error,
|
|
860
871
|
}, dispatch: dispatch, onBack: () => dispatch({ type: 'page', page: 'root' }) });
|
|
872
|
+
if (state.page === 'governance') {
|
|
873
|
+
const config = draftGovernance(state.draft);
|
|
874
|
+
return _jsx(GovernanceSettingsPage, { ...props, view: {
|
|
875
|
+
rows: governanceSettingRows(config),
|
|
876
|
+
error: state.error,
|
|
877
|
+
restartNote: governanceRestartNote(config.enabled, props.governanceRegistered),
|
|
878
|
+
}, dispatch: dispatch, onBack: () => dispatch({ type: 'page', page: 'root' }) });
|
|
879
|
+
}
|
|
861
880
|
// THE KIT PAGES (agent-model-kits §4.5). They are dispatched BEFORE the profile
|
|
862
881
|
// pages because `'kit-slot'` RENDERS a profile picker while belonging to the
|
|
863
882
|
// kit editor - the page name, not the component, decides where the user is.
|
|
@@ -982,6 +1001,7 @@ function buildRootSettingsRows(props, view) {
|
|
|
982
1001
|
// four values, only two of which are about fan-out width. `padEnd(18)` is
|
|
983
1002
|
// unchanged, so the value column still lines up with `Subagent model`.
|
|
984
1003
|
`Team ${summary}`,
|
|
1004
|
+
`Governance ${governanceSummary(draftGovernance(state.draft))}`,
|
|
985
1005
|
`Kit pack ${kitPackRow(state)}`,
|
|
986
1006
|
`Manage profiles ${library.entries.length} saved`,
|
|
987
1007
|
...fields.map((item) => `${item.label.padEnd(18)}${item.key === 'apiKey'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings > Governance: the pure half of the page (governance-mode 11.1).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY - `src/ui/**` is inside `glyphs.test.ts::inScope`.
|
|
5
|
+
*
|
|
6
|
+
* FIELD DEFINITIONS AND THE ARROW-KEY STEP, NOTHING ELSE. `SettingsScreen.tsx` is far past
|
|
7
|
+
* the size limit, so the page lists its rows from here and adds wiring only, mirroring
|
|
8
|
+
* `team-fanout-state.ts`. Every value a step produces goes back through
|
|
9
|
+
* `clampGovernanceConfig`, so the page, `config set` and a hand-edited `config.json` can
|
|
10
|
+
* never disagree about a range.
|
|
11
|
+
*
|
|
12
|
+
* THE PAGE MAY WRITE THE ELEVEN POLICY KEYS AND NOTHING ELSE. The structural bounds
|
|
13
|
+
* (`GOV_LIMITS`) are not settings: a number a user can tune past what the store and the
|
|
14
|
+
* panel can carry is not a preference, it is a crash.
|
|
15
|
+
*/
|
|
16
|
+
import { type GovernanceConfig } from '../../config/governance-config.js';
|
|
17
|
+
import type { ModelSettingsDraft } from '../../config/model-profile-store.js';
|
|
18
|
+
export type GovernanceSettingKey = keyof GovernanceConfig;
|
|
19
|
+
export type GovernanceSettingKind = 'toggle' | 'number' | 'choice';
|
|
20
|
+
export interface GovernanceSettingDef {
|
|
21
|
+
key: GovernanceSettingKey;
|
|
22
|
+
/** At most 17 columns: the page pads labels to 18, like the Team page. */
|
|
23
|
+
label: string;
|
|
24
|
+
/** One line, at most 60 columns: WHY this setting, not a restatement of the label. */
|
|
25
|
+
description: string;
|
|
26
|
+
kind: GovernanceSettingKind;
|
|
27
|
+
/** Inclusive; numbers only. Read from the clamp, never repeated here. */
|
|
28
|
+
range?: {
|
|
29
|
+
min: number;
|
|
30
|
+
max: number;
|
|
31
|
+
};
|
|
32
|
+
/** Arrow-key increment; numbers only. */
|
|
33
|
+
step?: number;
|
|
34
|
+
/** Cycle order; choices only. */
|
|
35
|
+
choices?: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
/** The rows in cursor order. The switch is first: the rest describe what it turns on. */
|
|
38
|
+
export declare const GOVERNANCE_SETTING_DEFS: readonly GovernanceSettingDef[];
|
|
39
|
+
export interface GovernanceSettingRow {
|
|
40
|
+
key: GovernanceSettingKey;
|
|
41
|
+
label: string;
|
|
42
|
+
/** The value as the page prints it (`'on'` / `'3'` / `'unlimited'`). */
|
|
43
|
+
value: string;
|
|
44
|
+
/** Description plus the range, one line. */
|
|
45
|
+
hint: string;
|
|
46
|
+
}
|
|
47
|
+
/** The page's rows for the CLAMPED section, so the screen shows what would be saved. */
|
|
48
|
+
export declare function governanceSettingRows(values: unknown): GovernanceSettingRow[];
|
|
49
|
+
/**
|
|
50
|
+
* The PATCH FRAGMENT for one arrow press, for a section the screen already shows
|
|
51
|
+
* (`current`). Exactly one key, so a step can never carry a second one into the draft.
|
|
52
|
+
* Toggles flip on either arrow, as on the Team page: a direction means nothing for two
|
|
53
|
+
* states. An unknown key yields an empty patch rather than throwing.
|
|
54
|
+
*/
|
|
55
|
+
export declare function stepGovernanceSetting(current: unknown, key: GovernanceSettingKey, delta: -1 | 1): Partial<GovernanceConfig>;
|
|
56
|
+
/**
|
|
57
|
+
* The governance section this draft would save: the patch over the disk baseline, then the
|
|
58
|
+
* clamp (governance-mode-v1-1 5.2). Same order as `teamFanoutValues`, and the clamp is not
|
|
59
|
+
* cosmetic: it is what makes the number on screen the number `Ctrl+S` writes, whatever a
|
|
60
|
+
* hand-edited `config.json` or an old patch carried.
|
|
61
|
+
*/
|
|
62
|
+
export declare function draftGovernance(draft: ModelSettingsDraft): GovernanceConfig;
|
|
63
|
+
/**
|
|
64
|
+
* The one-line summary the settings root row prints. A DERIVED row, never a control: the
|
|
65
|
+
* values change on the page Enter opens, so the root row cannot be moved by a stray arrow.
|
|
66
|
+
* `off` alone when the switch is off, because the numbers then describe a capability that
|
|
67
|
+
* is not there (the `teamSummary` rule).
|
|
68
|
+
*/
|
|
69
|
+
export declare function governanceSummary(config: GovernanceConfig): string;
|
|
70
|
+
export type GovernanceRestartNote = string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Whether saving the `enabled` switch would not take effect in THIS session. The tool array
|
|
73
|
+
* is immutable after construction (v1 D-22), so a draft that disagrees with what was
|
|
74
|
+
* registered at launch only applies next launch, and the page says so rather than letting a
|
|
75
|
+
* user believe a toggle summoned `gov_run` into the running session. `undefined` means the
|
|
76
|
+
* host did not say, and the page then discloses nothing (every fixture builds props by
|
|
77
|
+
* literal).
|
|
78
|
+
*/
|
|
79
|
+
export declare function governanceRestartNote(enabledInDraft: boolean, registeredAtLaunch: boolean | undefined): GovernanceRestartNote;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings > Governance: the pure half of the page (governance-mode 11.1).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY - `src/ui/**` is inside `glyphs.test.ts::inScope`.
|
|
5
|
+
*
|
|
6
|
+
* FIELD DEFINITIONS AND THE ARROW-KEY STEP, NOTHING ELSE. `SettingsScreen.tsx` is far past
|
|
7
|
+
* the size limit, so the page lists its rows from here and adds wiring only, mirroring
|
|
8
|
+
* `team-fanout-state.ts`. Every value a step produces goes back through
|
|
9
|
+
* `clampGovernanceConfig`, so the page, `config set` and a hand-edited `config.json` can
|
|
10
|
+
* never disagree about a range.
|
|
11
|
+
*
|
|
12
|
+
* THE PAGE MAY WRITE THE ELEVEN POLICY KEYS AND NOTHING ELSE. The structural bounds
|
|
13
|
+
* (`GOV_LIMITS`) are not settings: a number a user can tune past what the store and the
|
|
14
|
+
* panel can carry is not a preference, it is a crash.
|
|
15
|
+
*/
|
|
16
|
+
import { clampGovernanceConfig, governanceRangeOf, } from '../../config/governance-config.js';
|
|
17
|
+
import { PROFILE_IDS } from '../../governance/types.js';
|
|
18
|
+
function numeric(key, label, description, step = 1) {
|
|
19
|
+
return { key, label, description, kind: 'number', range: governanceRangeOf(key), step };
|
|
20
|
+
}
|
|
21
|
+
/** The rows in cursor order. The switch is first: the rest describe what it turns on. */
|
|
22
|
+
export const GOVERNANCE_SETTING_DEFS = [
|
|
23
|
+
{ key: 'enabled', label: 'Governance', kind: 'toggle',
|
|
24
|
+
description: 'Off removes it entirely. Turning it on needs a restart.' },
|
|
25
|
+
{ key: 'shiftTab', label: 'Shift+Tab cycle', kind: 'toggle',
|
|
26
|
+
description: 'Off keeps Shift+Tab on BUILD/PLAN; /gov still works.' },
|
|
27
|
+
{ key: 'panel', label: 'Legion panel', kind: 'toggle',
|
|
28
|
+
description: 'Off shows the status chip only, no panel.' },
|
|
29
|
+
{ key: 'profile', label: 'Profile', kind: 'choice', choices: PROFILE_IDS,
|
|
30
|
+
description: 'Domain wording for the roles and their checklists.' },
|
|
31
|
+
numeric('maxWorkPackages', 'Work packages', 'Most work packages one run may define.'),
|
|
32
|
+
numeric('maxPhases', 'Phases', 'Most dev/review/test rounds one run may take.'),
|
|
33
|
+
numeric('maxRework', 'Rework rounds', 'Rework rounds per work package before escalation.'),
|
|
34
|
+
numeric('maxFixCycles', 'Fix cycles', 'Fix cycles per phase after integration fails.'),
|
|
35
|
+
numeric('minAskRounds', 'Ask rounds', 'Questions PLAN+GOVERN asks before a plan is allowed.'),
|
|
36
|
+
numeric('budgetTokens', 'Token budget', 'Per-run token ceiling; 0 means unlimited.', 100_000),
|
|
37
|
+
{ key: 'confirmCharter', label: 'Confirm charter', kind: 'toggle',
|
|
38
|
+
description: 'On asks you to approve the run plan before any dispatch.' },
|
|
39
|
+
];
|
|
40
|
+
function formatValue(def, config) {
|
|
41
|
+
const value = config[def.key];
|
|
42
|
+
if (def.kind === 'toggle')
|
|
43
|
+
return value ? 'on' : 'off';
|
|
44
|
+
if (def.key === 'budgetTokens')
|
|
45
|
+
return value === 0 ? 'unlimited' : String(value);
|
|
46
|
+
return String(value);
|
|
47
|
+
}
|
|
48
|
+
function formatHint(def) {
|
|
49
|
+
if (def.range === undefined)
|
|
50
|
+
return def.description;
|
|
51
|
+
return `${def.description} (${def.range.min}-${def.range.max})`;
|
|
52
|
+
}
|
|
53
|
+
/** The page's rows for the CLAMPED section, so the screen shows what would be saved. */
|
|
54
|
+
export function governanceSettingRows(values) {
|
|
55
|
+
const config = clampGovernanceConfig(values);
|
|
56
|
+
return GOVERNANCE_SETTING_DEFS.map((def) => ({
|
|
57
|
+
key: def.key,
|
|
58
|
+
label: def.label,
|
|
59
|
+
value: formatValue(def, config),
|
|
60
|
+
hint: formatHint(def),
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
function stepNumber(config, def, delta) {
|
|
64
|
+
const key = def.key;
|
|
65
|
+
// NO WRAPAROUND: a form control is not a ring, and Left at the floor jumping to the
|
|
66
|
+
// ceiling would be a way to raise a limit while trying to lower it. The clamp does it.
|
|
67
|
+
const next = clampGovernanceConfig({ ...config, [key]: config[key] + delta * (def.step ?? 1) });
|
|
68
|
+
return { [key]: next[key] };
|
|
69
|
+
}
|
|
70
|
+
function stepChoice(config, def, delta) {
|
|
71
|
+
const choices = def.choices ?? [];
|
|
72
|
+
const at = choices.indexOf(String(config[def.key]));
|
|
73
|
+
const next = choices[(at + delta + choices.length) % choices.length];
|
|
74
|
+
// Through the clamp, so a choice list that ever drifts from the clamp's vocabulary cannot
|
|
75
|
+
// write a value the read path would silently repair.
|
|
76
|
+
const stored = clampGovernanceConfig({ ...config, [def.key]: next })[def.key];
|
|
77
|
+
return { [def.key]: stored };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The PATCH FRAGMENT for one arrow press, for a section the screen already shows
|
|
81
|
+
* (`current`). Exactly one key, so a step can never carry a second one into the draft.
|
|
82
|
+
* Toggles flip on either arrow, as on the Team page: a direction means nothing for two
|
|
83
|
+
* states. An unknown key yields an empty patch rather than throwing.
|
|
84
|
+
*/
|
|
85
|
+
export function stepGovernanceSetting(current, key, delta) {
|
|
86
|
+
const config = clampGovernanceConfig(current);
|
|
87
|
+
const def = GOVERNANCE_SETTING_DEFS.find((candidate) => candidate.key === key);
|
|
88
|
+
if (def === undefined)
|
|
89
|
+
return {};
|
|
90
|
+
if (def.kind === 'toggle')
|
|
91
|
+
return { [key]: !config[key] };
|
|
92
|
+
return def.kind === 'number' ? stepNumber(config, def, delta) : stepChoice(config, def, delta);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The governance section this draft would save: the patch over the disk baseline, then the
|
|
96
|
+
* clamp (governance-mode-v1-1 5.2). Same order as `teamFanoutValues`, and the clamp is not
|
|
97
|
+
* cosmetic: it is what makes the number on screen the number `Ctrl+S` writes, whatever a
|
|
98
|
+
* hand-edited `config.json` or an old patch carried.
|
|
99
|
+
*/
|
|
100
|
+
export function draftGovernance(draft) {
|
|
101
|
+
return clampGovernanceConfig({ ...draft.baseline.governance, ...draft.patch.governance });
|
|
102
|
+
}
|
|
103
|
+
/** `0` is unlimited; otherwise a compact figure (`500k`, `1.2M`). */
|
|
104
|
+
function budgetText(tokens) {
|
|
105
|
+
if (tokens <= 0)
|
|
106
|
+
return 'unlimited';
|
|
107
|
+
if (tokens >= 1_000_000)
|
|
108
|
+
return `${Number((tokens / 1_000_000).toFixed(1))}M`;
|
|
109
|
+
return `${Math.round(tokens / 1_000)}k`;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The one-line summary the settings root row prints. A DERIVED row, never a control: the
|
|
113
|
+
* values change on the page Enter opens, so the root row cannot be moved by a stray arrow.
|
|
114
|
+
* `off` alone when the switch is off, because the numbers then describe a capability that
|
|
115
|
+
* is not there (the `teamSummary` rule).
|
|
116
|
+
*/
|
|
117
|
+
export function governanceSummary(config) {
|
|
118
|
+
if (!config.enabled)
|
|
119
|
+
return 'off';
|
|
120
|
+
return [
|
|
121
|
+
'on',
|
|
122
|
+
`${config.maxWorkPackages} WP`,
|
|
123
|
+
`${config.maxPhases} phases`,
|
|
124
|
+
`rework ${config.maxRework}`,
|
|
125
|
+
`budget ${budgetText(config.budgetTokens)}`,
|
|
126
|
+
].join(' | ');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Whether saving the `enabled` switch would not take effect in THIS session. The tool array
|
|
130
|
+
* is immutable after construction (v1 D-22), so a draft that disagrees with what was
|
|
131
|
+
* registered at launch only applies next launch, and the page says so rather than letting a
|
|
132
|
+
* user believe a toggle summoned `gov_run` into the running session. `undefined` means the
|
|
133
|
+
* host did not say, and the page then discloses nothing (every fixture builds props by
|
|
134
|
+
* literal).
|
|
135
|
+
*/
|
|
136
|
+
export function governanceRestartNote(enabledInDraft, registeredAtLaunch) {
|
|
137
|
+
if (registeredAtLaunch === undefined || enabledInDraft === registeredAtLaunch)
|
|
138
|
+
return null;
|
|
139
|
+
return 'Governance is registered at launch; this change applies next launch.';
|
|
140
|
+
}
|
|
@@ -4,7 +4,8 @@ import type { ModelSettingsDraft } from '../../config/model-profile-store.js';
|
|
|
4
4
|
import type { SubagentModelMode } from '../../team/model.js';
|
|
5
5
|
import type { KitDraft } from './kit-settings-state.js';
|
|
6
6
|
import { type TeamFanoutField } from './team-fanout-state.js';
|
|
7
|
-
|
|
7
|
+
import { type GovernanceSettingKey } from './governance-settings.js';
|
|
8
|
+
export type ProfilePage = 'root' | 'picker' | 'manager' | 'editor' | 'models' | 'delete-confirm' | 'discard-confirm' | 'kits' | 'kit-edit' | 'kit-slot' | 'kit-delete' | 'team' | 'governance';
|
|
8
9
|
/**
|
|
9
10
|
* `'team'` is the fan-out page (subagent-fanout D-5), and it is a PAGE rather
|
|
10
11
|
* than three more fields on the root page for a measurable reason: the root page
|
|
@@ -118,6 +119,10 @@ export type ModelProfileAction = {
|
|
|
118
119
|
type: 'team-fanout';
|
|
119
120
|
field: TeamFanoutField;
|
|
120
121
|
delta: -1 | 1;
|
|
122
|
+
} | {
|
|
123
|
+
type: 'governance-step';
|
|
124
|
+
key: GovernanceSettingKey;
|
|
125
|
+
delta: -1 | 1;
|
|
121
126
|
};
|
|
122
127
|
/** Keep the original secret separate from the empty replacement field. */
|
|
123
128
|
export declare function createProfileEditor(profile: ModelProfile): ProfileEditorDraft;
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { emptyModelProfiles, duplicateModelProfile, normalizeModelProfiles, validateModelProfiles } from '../../config/model-profiles.js';
|
|
3
3
|
import { parseContextWindowInput } from '../../config/schema.js';
|
|
4
4
|
import { stepFanoutValue, teamFanoutValues } from './team-fanout-state.js';
|
|
5
|
+
import { draftGovernance, stepGovernanceSetting } from './governance-settings.js';
|
|
5
6
|
/** The picker's built-in `Fast tier model` row; never a profile id on disk. */
|
|
6
7
|
export const SUBAGENT_FAST_ROW_ID = '__fast__';
|
|
7
8
|
/** Keep the original secret separate from the empty replacement field. */
|
|
@@ -233,6 +234,15 @@ export function modelProfileReducer(state, action) {
|
|
|
233
234
|
draft: { ...state.draft,
|
|
234
235
|
patch: { ...state.draft.patch, team: { ...state.draft.patch.team, ...step } } } };
|
|
235
236
|
}
|
|
237
|
+
case 'governance-step': {
|
|
238
|
+
// THE FRAGMENT IS THE ONLY THING WRITTEN, as for `team-fanout`: `stepGovernanceSetting`
|
|
239
|
+
// returns exactly one key, clamped, from the value the screen is showing
|
|
240
|
+
// (`draftGovernance`), so a step can never drag a second key into the patch.
|
|
241
|
+
const step = stepGovernanceSetting(draftGovernance(state.draft), action.key, action.delta);
|
|
242
|
+
return { ...state, dirty: true, error: undefined,
|
|
243
|
+
draft: { ...state.draft, patch: { ...state.draft.patch,
|
|
244
|
+
governance: { ...state.draft.patch.governance, ...step } } } };
|
|
245
|
+
}
|
|
236
246
|
case 'duplicate': {
|
|
237
247
|
if (library.entries.length >= 200)
|
|
238
248
|
return { ...state,
|